alsa.rs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. use super::{Open, Sink};
  2. use alsa::device_name::HintIter;
  3. use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
  4. use alsa::{Direction, Error, ValueOr};
  5. use std::cmp::min;
  6. use std::ffi::CString;
  7. use std::io;
  8. use std::process::exit;
  9. const PREFERED_PERIOD_SIZE: Frames = 5512; // Period of roughly 125ms
  10. const BUFFERED_PERIODS: Frames = 4;
  11. pub struct AlsaSink {
  12. pcm: Option<PCM>,
  13. device: String,
  14. buffer: Vec<i16>,
  15. }
  16. fn list_outputs() {
  17. for t in &["pcm", "ctl", "hwdep"] {
  18. println!("{} devices:", t);
  19. let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
  20. for a in i {
  21. if let Some(Direction::Playback) = a.direction {
  22. // mimic aplay -L
  23. println!(
  24. "{}\n\t{}\n",
  25. a.name.unwrap(),
  26. a.desc.unwrap().replace("\n", "\n\t")
  27. );
  28. }
  29. }
  30. }
  31. }
  32. fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box<Error>> {
  33. let pcm = PCM::new(dev_name, Direction::Playback, false)?;
  34. let mut period_size = PREFERED_PERIOD_SIZE;
  35. // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
  36. // latency = period_size * periods / (rate * bytes_per_frame)
  37. // For 16 Bit stereo data, one frame has a length of four bytes.
  38. // 500ms = buffer_size / (44100 * 4)
  39. // buffer_size_bytes = 0.5 * 44100 / 4
  40. // buffer_size_frames = 0.5 * 44100 = 22050
  41. {
  42. // Set hardware parameters: 44100 Hz / Stereo / 16 bit
  43. let hwp = HwParams::any(&pcm)?;
  44. hwp.set_access(Access::RWInterleaved)?;
  45. hwp.set_format(Format::s16())?;
  46. hwp.set_rate(44100, ValueOr::Nearest)?;
  47. hwp.set_channels(2)?;
  48. period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?;
  49. hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS)?;
  50. pcm.hw_params(&hwp)?;
  51. let swp = pcm.sw_params_current()?;
  52. swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
  53. pcm.sw_params(&swp)?;
  54. }
  55. Ok((pcm, period_size))
  56. }
  57. impl Open for AlsaSink {
  58. fn open(device: Option<String>) -> AlsaSink {
  59. info!("Using alsa sink");
  60. let name = match device.as_ref().map(AsRef::as_ref) {
  61. Some("?") => {
  62. println!("Listing available alsa outputs");
  63. list_outputs();
  64. exit(0)
  65. }
  66. Some(device) => device,
  67. None => "default",
  68. }
  69. .to_string();
  70. AlsaSink {
  71. pcm: None,
  72. device: name,
  73. buffer: vec![],
  74. }
  75. }
  76. }
  77. impl Sink for AlsaSink {
  78. fn start(&mut self) -> io::Result<()> {
  79. if self.pcm.is_none() {
  80. let pcm = open_device(&self.device);
  81. match pcm {
  82. Ok((p, period_size)) => {
  83. self.pcm = Some(p);
  84. // Create a buffer for all samples for a full period
  85. self.buffer = Vec::with_capacity((period_size * 2) as usize);
  86. }
  87. Err(e) => {
  88. error!("Alsa error PCM open {}", e);
  89. return Err(io::Error::new(
  90. io::ErrorKind::Other,
  91. "Alsa error: PCM open failed",
  92. ));
  93. }
  94. }
  95. }
  96. Ok(())
  97. }
  98. fn stop(&mut self) -> io::Result<()> {
  99. {
  100. let pcm = self.pcm.as_mut().unwrap();
  101. // Write any leftover data in the period buffer
  102. // before draining the actual buffer
  103. let io = pcm.io_i16().unwrap();
  104. match io.writei(&self.buffer[..]) {
  105. Ok(_) => (),
  106. Err(err) => pcm.try_recover(err, false).unwrap(),
  107. }
  108. pcm.drain().unwrap();
  109. }
  110. self.pcm = None;
  111. Ok(())
  112. }
  113. fn write(&mut self, data: &[i16]) -> io::Result<()> {
  114. let mut processed_data = 0;
  115. while processed_data < data.len() {
  116. let data_to_buffer = min(
  117. self.buffer.capacity() - self.buffer.len(),
  118. data.len() - processed_data,
  119. );
  120. self.buffer
  121. .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
  122. processed_data += data_to_buffer;
  123. if self.buffer.len() == self.buffer.capacity() {
  124. let pcm = self.pcm.as_mut().unwrap();
  125. let io = pcm.io_i16().unwrap();
  126. match io.writei(&self.buffer) {
  127. Ok(_) => (),
  128. Err(err) => pcm.try_recover(err, false).unwrap(),
  129. }
  130. self.buffer.clear();
  131. }
  132. }
  133. Ok(())
  134. }
  135. }