alsa.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. use super::{Open, Sink};
  2. use alsa::device_name::HintIter;
  3. use alsa::pcm::{Access, Format, HwParams, PCM};
  4. use alsa::{Direction, Error, ValueOr};
  5. use std::ffi::CString;
  6. use std::io;
  7. use std::process::exit;
  8. pub struct AlsaSink(Option<PCM>, String);
  9. fn list_outputs() {
  10. for t in &["pcm", "ctl", "hwdep"] {
  11. println!("{} devices:", t);
  12. let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
  13. for a in i {
  14. if let Some(Direction::Playback) = a.direction {
  15. // mimic aplay -L
  16. println!(
  17. "{}\n\t{}\n",
  18. a.name.unwrap(),
  19. a.desc.unwrap().replace("\n", "\n\t")
  20. );
  21. }
  22. }
  23. }
  24. }
  25. fn open_device(dev_name: &str) -> Result<(PCM), Box<Error>> {
  26. let pcm = PCM::new(dev_name, Direction::Playback, false)?;
  27. // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
  28. // latency = period_size * periods / (rate * bytes_per_frame)
  29. // For 16 Bit stereo data, one frame has a length of four bytes.
  30. // 500ms = buffer_size / (44100 * 4)
  31. // buffer_size_bytes = 0.5 * 44100 / 4
  32. // buffer_size_frames = 0.5 * 44100 = 22050
  33. {
  34. // Set hardware parameters: 44100 Hz / Stereo / 16 bit
  35. let hwp = HwParams::any(&pcm)?;
  36. hwp.set_access(Access::RWInterleaved)?;
  37. hwp.set_format(Format::s16())?;
  38. hwp.set_rate(44100, ValueOr::Nearest)?;
  39. hwp.set_channels(2)?;
  40. hwp.set_buffer_size_near(22050)?; // ~ 0.5s latency
  41. pcm.hw_params(&hwp)?;
  42. let swp = pcm.sw_params_current()?;
  43. swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
  44. pcm.sw_params(&swp)?;
  45. }
  46. Ok(pcm)
  47. }
  48. impl Open for AlsaSink {
  49. fn open(device: Option<String>) -> AlsaSink {
  50. info!("Using alsa sink");
  51. let name = match device.as_ref().map(AsRef::as_ref) {
  52. Some("?") => {
  53. println!("Listing available alsa outputs");
  54. list_outputs();
  55. exit(0)
  56. }
  57. Some(device) => device,
  58. None => "default",
  59. }
  60. .to_string();
  61. AlsaSink(None, name)
  62. }
  63. }
  64. impl Sink for AlsaSink {
  65. fn start(&mut self) -> io::Result<()> {
  66. if self.0.is_none() {
  67. let pcm = open_device(&self.1);
  68. match pcm {
  69. Ok(p) => self.0 = Some(p),
  70. Err(e) => {
  71. error!("Alsa error PCM open {}", e);
  72. return Err(io::Error::new(
  73. io::ErrorKind::Other,
  74. "Alsa error: PCM open failed",
  75. ));
  76. }
  77. }
  78. }
  79. Ok(())
  80. }
  81. fn stop(&mut self) -> io::Result<()> {
  82. {
  83. let pcm = self.0.as_ref().unwrap();
  84. pcm.drain().unwrap();
  85. }
  86. self.0 = None;
  87. Ok(())
  88. }
  89. fn write(&mut self, data: &[i16]) -> io::Result<()> {
  90. let pcm = self.0.as_mut().unwrap();
  91. let io = pcm.io_i16().unwrap();
  92. match io.writei(&data) {
  93. Ok(_) => (),
  94. Err(err) => pcm.try_recover(err, false).unwrap(),
  95. }
  96. Ok(())
  97. }
  98. }