2016-03-20 16:16:11 +00:00
|
|
|
use super::{Open, Sink};
|
2018-02-26 01:50:41 +00:00
|
|
|
use portaudio_rs;
|
|
|
|
use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo};
|
|
|
|
use portaudio_rs::stream::*;
|
2016-03-20 16:16:11 +00:00
|
|
|
use std::io;
|
2016-07-06 09:54:46 +00:00
|
|
|
use std::process::exit;
|
2016-07-22 22:39:41 +00:00
|
|
|
use std::time::Duration;
|
2016-03-20 16:16:11 +00:00
|
|
|
|
2018-02-26 01:50:41 +00:00
|
|
|
pub struct PortAudioSink<'a>(
|
|
|
|
Option<portaudio_rs::stream::Stream<'a, i16, i16>>,
|
|
|
|
StreamParameters<i16>,
|
|
|
|
);
|
2016-03-20 16:16:11 +00:00
|
|
|
|
2018-02-26 01:50:41 +00:00
|
|
|
fn output_devices() -> Box<Iterator<Item = (DeviceIndex, DeviceInfo)>> {
|
2017-04-29 10:41:48 +00:00
|
|
|
let count = portaudio_rs::device::get_count().unwrap();
|
2016-07-06 09:54:46 +00:00
|
|
|
let devices = (0..count)
|
2018-02-26 01:50:41 +00:00
|
|
|
.filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info)))
|
|
|
|
.filter(|&(_, ref info)| info.max_output_channels > 0);
|
2016-07-06 09:54:46 +00:00
|
|
|
|
|
|
|
Box::new(devices)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn list_outputs() {
|
|
|
|
let default = get_default_output_index();
|
|
|
|
|
|
|
|
for (idx, info) in output_devices() {
|
|
|
|
if Some(idx) == default {
|
|
|
|
println!("- {} (default)", info.name);
|
|
|
|
} else {
|
|
|
|
println!("- {}", info.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_output(device: &str) -> Option<DeviceIndex> {
|
|
|
|
output_devices()
|
|
|
|
.find(|&(_, ref info)| info.name == device)
|
|
|
|
.map(|(idx, _)| idx)
|
|
|
|
}
|
|
|
|
|
2018-02-26 01:50:41 +00:00
|
|
|
impl<'a> Open for PortAudioSink<'a> {
|
2017-01-18 18:41:22 +00:00
|
|
|
fn open(device: Option<String>) -> PortAudioSink<'a> {
|
2016-07-06 09:54:46 +00:00
|
|
|
debug!("Using PortAudio sink");
|
|
|
|
|
2017-04-29 10:41:48 +00:00
|
|
|
portaudio_rs::initialize().unwrap();
|
2016-03-20 16:16:11 +00:00
|
|
|
|
2017-01-18 18:41:22 +00:00
|
|
|
let device_idx = match device.as_ref().map(AsRef::as_ref) {
|
2016-07-06 09:54:46 +00:00
|
|
|
Some("?") => {
|
|
|
|
list_outputs();
|
|
|
|
exit(0)
|
|
|
|
}
|
|
|
|
Some(device) => find_output(device),
|
|
|
|
None => get_default_output_index(),
|
|
|
|
}.expect("Could not find device");
|
|
|
|
|
2017-04-29 10:41:48 +00:00
|
|
|
let info = portaudio_rs::device::get_info(device_idx);
|
2016-07-22 22:39:41 +00:00
|
|
|
let latency = match info {
|
|
|
|
Some(info) => info.default_high_output_latency,
|
|
|
|
None => Duration::new(0, 0),
|
|
|
|
};
|
|
|
|
|
2016-07-06 09:54:46 +00:00
|
|
|
let params = StreamParameters {
|
|
|
|
device: device_idx,
|
|
|
|
channel_count: 2,
|
2016-07-22 22:39:41 +00:00
|
|
|
suggested_latency: latency,
|
2016-07-06 09:54:46 +00:00
|
|
|
data: 0i16,
|
|
|
|
};
|
|
|
|
|
2016-08-01 19:20:17 +00:00
|
|
|
PortAudioSink(None, params)
|
2016-03-20 16:16:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 01:50:41 +00:00
|
|
|
impl<'a> Sink for PortAudioSink<'a> {
|
2016-08-01 19:20:17 +00:00
|
|
|
fn start(&mut self) -> io::Result<()> {
|
|
|
|
if self.0.is_none() {
|
2018-02-26 01:50:41 +00:00
|
|
|
self.0 = Some(
|
|
|
|
Stream::open(
|
|
|
|
None,
|
|
|
|
Some(self.1),
|
|
|
|
44100.0,
|
|
|
|
FRAMES_PER_BUFFER_UNSPECIFIED,
|
|
|
|
StreamFlags::empty(),
|
|
|
|
None,
|
|
|
|
).unwrap(),
|
|
|
|
);;
|
2016-08-01 19:20:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.0.as_mut().unwrap().start().unwrap();
|
2016-03-20 16:16:11 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-08-01 19:20:17 +00:00
|
|
|
fn stop(&mut self) -> io::Result<()> {
|
|
|
|
self.0.as_mut().unwrap().stop().unwrap();
|
|
|
|
self.0 = None;
|
2016-03-20 16:16:11 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-08-01 19:20:17 +00:00
|
|
|
fn write(&mut self, data: &[i16]) -> io::Result<()> {
|
2017-01-29 16:25:09 +00:00
|
|
|
match self.0.as_mut().unwrap().write(data) {
|
2016-03-20 16:16:11 +00:00
|
|
|
Ok(_) => (),
|
2018-02-26 01:50:41 +00:00
|
|
|
Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"),
|
2016-03-20 16:16:11 +00:00
|
|
|
Err(e) => panic!("PA Error {}", e),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 01:50:41 +00:00
|
|
|
impl<'a> Drop for PortAudioSink<'a> {
|
2016-03-20 16:16:11 +00:00
|
|
|
fn drop(&mut self) {
|
2017-04-29 10:41:48 +00:00
|
|
|
portaudio_rs::terminate().unwrap();
|
2016-03-20 16:16:11 +00:00
|
|
|
}
|
|
|
|
}
|