2019-03-20 13:24:03 +00:00
|
|
|
use super::{Open, Sink};
|
|
|
|
extern crate rodio;
|
2019-03-20 13:21:50 +00:00
|
|
|
extern crate cpal;
|
2019-03-20 13:23:20 +00:00
|
|
|
use std::{io, thread, time};
|
2019-03-20 13:24:03 +00:00
|
|
|
use std::process::exit;
|
|
|
|
|
|
|
|
pub struct RodioSink {
|
|
|
|
rodio_sink: rodio::Sink,
|
|
|
|
}
|
|
|
|
|
2019-03-20 13:21:50 +00:00
|
|
|
fn list_formats(ref device: &rodio::Device) {
|
|
|
|
let default_fmt = match device.default_output_format() {
|
|
|
|
Ok(fmt) => cpal::SupportedFormat::from(fmt),
|
|
|
|
Err(e) => {
|
2019-03-17 15:30:32 +00:00
|
|
|
warn!("Error getting default rodio::Sink format: {:?}", e);
|
2019-03-20 13:21:50 +00:00
|
|
|
return;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut output_formats = match device.supported_output_formats() {
|
|
|
|
Ok(f) => f.peekable(),
|
|
|
|
Err(e) => {
|
2019-03-17 15:30:32 +00:00
|
|
|
warn!("Error getting supported rodio::Sink formats: {:?}", e);
|
2019-03-20 13:21:50 +00:00
|
|
|
return;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
if output_formats.peek().is_some() {
|
|
|
|
debug!(" Available formats:");
|
|
|
|
for format in output_formats {
|
|
|
|
let s = format!("{}ch, {:?}, min {:?}, max {:?}", format.channels, format.data_type, format.min_sample_rate, format.max_sample_rate);
|
|
|
|
if format == default_fmt {
|
|
|
|
debug!(" (default) {}", s);
|
|
|
|
} else {
|
|
|
|
debug!(" {:?}", format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-20 13:24:03 +00:00
|
|
|
fn list_outputs() {
|
2019-03-20 13:21:50 +00:00
|
|
|
let default_device = rodio::default_output_device().unwrap();
|
|
|
|
println!("Default Audio Device:\n {}", default_device.name());
|
|
|
|
list_formats(&default_device);
|
2019-03-20 13:24:03 +00:00
|
|
|
|
2019-03-20 13:21:50 +00:00
|
|
|
println!("Other Available Audio Devices:");
|
2019-03-20 13:24:03 +00:00
|
|
|
for device in rodio::output_devices() {
|
2019-03-20 13:21:50 +00:00
|
|
|
if device.name() != default_device.name() {
|
|
|
|
println!(" {}", device.name());
|
|
|
|
list_formats(&device);
|
2019-03-20 13:24:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Open for RodioSink {
|
|
|
|
fn open(device: Option<String>) -> RodioSink {
|
2019-03-20 13:21:50 +00:00
|
|
|
debug!("Using rodio sink");
|
2019-03-20 13:24:03 +00:00
|
|
|
|
|
|
|
let mut rodio_device = rodio::default_output_device().expect("no output device available");
|
|
|
|
if device.is_some() {
|
|
|
|
let device_name = device.unwrap();
|
|
|
|
|
|
|
|
if device_name == "?".to_string() {
|
|
|
|
list_outputs();
|
|
|
|
exit(0)
|
|
|
|
}
|
|
|
|
let mut found = false;
|
|
|
|
for d in rodio::output_devices() {
|
|
|
|
if d.name() == device_name {
|
|
|
|
rodio_device = d;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
println!("No output sink matching '{}' found.", device_name);
|
|
|
|
exit(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let sink = rodio::Sink::new(&rodio_device);
|
|
|
|
|
|
|
|
RodioSink {
|
|
|
|
rodio_sink: sink,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sink for RodioSink {
|
|
|
|
fn start(&mut self) -> io::Result<()> {
|
2019-03-01 15:43:16 +00:00
|
|
|
// More similar to an "unpause" than "play". Doesn't undo "stop".
|
|
|
|
// self.rodio_sink.play();
|
2019-03-20 13:24:03 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stop(&mut self) -> io::Result<()> {
|
2019-03-01 15:43:16 +00:00
|
|
|
// This will immediately stop playback, but the sink is then unusable.
|
|
|
|
// We just have to let the current buffer play till the end.
|
|
|
|
// self.rodio_sink.stop();
|
2019-03-20 13:24:03 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write(&mut self, data: &[i16]) -> io::Result<()> {
|
|
|
|
let source = rodio::buffer::SamplesBuffer::new(2, 44100, data);
|
|
|
|
self.rodio_sink.append(source);
|
2019-03-20 13:23:20 +00:00
|
|
|
|
|
|
|
// Chunk sizes seem to be about 256 to 3000 ish items long.
|
|
|
|
// Assuming they're on average 1628 then a half second buffer is:
|
|
|
|
// 44100 elements --> about 27 chunks
|
|
|
|
while self.rodio_sink.len() > 26 {
|
|
|
|
// sleep and wait for rodio to drain a bit
|
|
|
|
thread::sleep(time::Duration::from_millis(10));
|
|
|
|
}
|
2019-03-20 13:24:03 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|