pulseaudio.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. use super::{Open, Sink};
  2. use libc;
  3. use libpulse_sys::*;
  4. use std::ffi::CStr;
  5. use std::ffi::CString;
  6. use std::io;
  7. use std::mem;
  8. use std::ptr::{null, null_mut};
  9. pub struct PulseAudioSink {
  10. s: *mut pa_simple,
  11. ss: pa_sample_spec,
  12. name: CString,
  13. desc: CString,
  14. }
  15. fn call_pulseaudio<T, F, FailCheck>(
  16. f: F,
  17. fail_check: FailCheck,
  18. kind: io::ErrorKind,
  19. ) -> io::Result<T>
  20. where
  21. T: Copy,
  22. F: Fn(*mut libc::c_int) -> T,
  23. FailCheck: Fn(T) -> bool,
  24. {
  25. let mut error: libc::c_int = 0;
  26. let ret = f(&mut error);
  27. if fail_check(ret) {
  28. let err_cstr = unsafe { CStr::from_ptr(pa_strerror(error)) };
  29. let errstr = err_cstr.to_string_lossy().into_owned();
  30. Err(io::Error::new(kind, errstr))
  31. } else {
  32. Ok(ret)
  33. }
  34. }
  35. impl PulseAudioSink {
  36. fn free_connection(&mut self) {
  37. if self.s != null_mut() {
  38. unsafe {
  39. pa_simple_free(self.s);
  40. }
  41. self.s = null_mut();
  42. }
  43. }
  44. }
  45. impl Drop for PulseAudioSink {
  46. fn drop(&mut self) {
  47. self.free_connection();
  48. }
  49. }
  50. impl Open for PulseAudioSink {
  51. fn open(device: Option<String>) -> PulseAudioSink {
  52. debug!("Using PulseAudio sink");
  53. if device.is_some() {
  54. panic!("pulseaudio sink does not support specifying a device name");
  55. }
  56. let ss = pa_sample_spec {
  57. format: PA_SAMPLE_S16LE,
  58. channels: 2, // stereo
  59. rate: 44100,
  60. };
  61. let name = CString::new("librespot").unwrap();
  62. let description = CString::new("Spotify endpoint").unwrap();
  63. PulseAudioSink {
  64. s: null_mut(),
  65. ss: ss,
  66. name: name,
  67. desc: description,
  68. }
  69. }
  70. }
  71. impl Sink for PulseAudioSink {
  72. fn start(&mut self) -> io::Result<()> {
  73. if self.s == null_mut() {
  74. self.s = call_pulseaudio(
  75. |err| unsafe {
  76. pa_simple_new(
  77. null(), // Use the default server.
  78. self.name.as_ptr(), // Our application's name.
  79. PA_STREAM_PLAYBACK,
  80. null(), // Use the default device.
  81. self.desc.as_ptr(), // desc of our stream.
  82. &self.ss, // Our sample format.
  83. null(), // Use default channel map
  84. null(), // Use default buffering attributes.
  85. err,
  86. )
  87. },
  88. |ptr| ptr == null_mut(),
  89. io::ErrorKind::ConnectionRefused,
  90. )?;
  91. }
  92. Ok(())
  93. }
  94. fn stop(&mut self) -> io::Result<()> {
  95. self.free_connection();
  96. Ok(())
  97. }
  98. fn write(&mut self, data: &[i16]) -> io::Result<()> {
  99. if self.s == null_mut() {
  100. Err(io::Error::new(
  101. io::ErrorKind::NotConnected,
  102. "Not connected to pulseaudio",
  103. ))
  104. } else {
  105. let ptr = data.as_ptr() as *const libc::c_void;
  106. let len = data.len() as usize * mem::size_of::<i16>();
  107. assert!(len > 0);
  108. call_pulseaudio(
  109. |err| unsafe { pa_simple_write(self.s, ptr, len, err) },
  110. |ret| ret < 0,
  111. io::ErrorKind::BrokenPipe,
  112. )?;
  113. Ok(())
  114. }
  115. }
  116. }