rodio.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. use super::{Open, Sink};
  2. extern crate cpal;
  3. extern crate rodio;
  4. use cpal::traits::{DeviceTrait, HostTrait};
  5. use std::process::exit;
  6. use std::{io, thread, time};
  7. pub struct RodioSink {
  8. rodio_sink: rodio::Sink,
  9. // We have to keep hold of this object, or the Sink can't play...
  10. #[allow(dead_code)]
  11. stream: rodio::OutputStream,
  12. }
  13. fn list_formats(ref device: &rodio::Device) {
  14. let default_fmt = match device.default_output_config() {
  15. Ok(fmt) => cpal::SupportedStreamConfig::from(fmt),
  16. Err(e) => {
  17. warn!("Error getting default rodio::Sink config: {}", e);
  18. return;
  19. }
  20. };
  21. debug!(" Default config:");
  22. debug!(" {:?}", default_fmt);
  23. let mut output_configs = match device.supported_output_configs() {
  24. Ok(f) => f.peekable(),
  25. Err(e) => {
  26. warn!("Error getting supported rodio::Sink configs: {}", e);
  27. return;
  28. }
  29. };
  30. if output_configs.peek().is_some() {
  31. debug!(" Available configs:");
  32. for format in output_configs {
  33. debug!(" {:?}", format);
  34. }
  35. }
  36. }
  37. fn list_outputs() {
  38. let default_device = get_default_device();
  39. let default_device_name = default_device.name().expect("cannot get output name");
  40. println!("Default Audio Device:\n {}", default_device_name);
  41. list_formats(&default_device);
  42. println!("Other Available Audio Devices:");
  43. for device in cpal::default_host()
  44. .output_devices()
  45. .expect("cannot get list of output devices")
  46. {
  47. let device_name = device.name().expect("cannot get output name");
  48. if device_name != default_device_name {
  49. println!(" {}", device_name);
  50. list_formats(&device);
  51. }
  52. }
  53. }
  54. fn get_default_device() -> rodio::Device {
  55. cpal::default_host()
  56. .default_output_device()
  57. .expect("no default output device available")
  58. }
  59. fn match_device(device: Option<String>) -> rodio::Device {
  60. match device {
  61. Some(device_name) => {
  62. if device_name == "?".to_string() {
  63. list_outputs();
  64. exit(0)
  65. }
  66. for d in cpal::default_host()
  67. .output_devices()
  68. .expect("cannot get list of output devices")
  69. {
  70. if d.name().expect("cannot get output name") == device_name {
  71. return d;
  72. }
  73. }
  74. println!("No output sink matching '{}' found.", device_name);
  75. exit(0)
  76. }
  77. None => return get_default_device(),
  78. }
  79. }
  80. impl Open for RodioSink {
  81. fn open(device: Option<String>) -> RodioSink {
  82. debug!(
  83. "Using rodio sink with cpal host: {:?}",
  84. cpal::default_host().id()
  85. );
  86. let rodio_device = match_device(device);
  87. debug!("Using cpal device");
  88. let stream = rodio::OutputStream::try_from_device(&rodio_device)
  89. .expect("Couldn't open output stream.");
  90. debug!("Using rodio stream");
  91. let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink.");
  92. debug!("Using rodio sink");
  93. RodioSink {
  94. rodio_sink: sink,
  95. stream: stream.0,
  96. }
  97. }
  98. }
  99. impl Sink for RodioSink {
  100. fn start(&mut self) -> io::Result<()> {
  101. // More similar to an "unpause" than "play". Doesn't undo "stop".
  102. // self.rodio_sink.play();
  103. Ok(())
  104. }
  105. fn stop(&mut self) -> io::Result<()> {
  106. // This will immediately stop playback, but the sink is then unusable.
  107. // We just have to let the current buffer play till the end.
  108. // self.rodio_sink.stop();
  109. Ok(())
  110. }
  111. fn write(&mut self, data: &[i16]) -> io::Result<()> {
  112. let source = rodio::buffer::SamplesBuffer::new(2, 44100, data);
  113. self.rodio_sink.append(source);
  114. // Chunk sizes seem to be about 256 to 3000 ish items long.
  115. // Assuming they're on average 1628 then a half second buffer is:
  116. // 44100 elements --> about 27 chunks
  117. while self.rodio_sink.len() > 26 {
  118. // sleep and wait for rodio to drain a bit
  119. thread::sleep(time::Duration::from_millis(10));
  120. }
  121. Ok(())
  122. }
  123. }