|
@@ -1,17 +1,73 @@
|
|
|
use super::{Open, Sink};
|
|
|
use std::io;
|
|
|
+use std::process::exit;
|
|
|
use portaudio;
|
|
|
+use portaudio::device::{DeviceIndex, DeviceInfo, get_default_output_index};
|
|
|
|
|
|
pub struct PortAudioSink<'a>(portaudio::stream::Stream<'a, i16, i16>);
|
|
|
|
|
|
+fn output_devices() -> Box<Iterator<Item=(DeviceIndex, DeviceInfo)>> {
|
|
|
+ let count = portaudio::device::get_count().unwrap();
|
|
|
+ let devices = (0..count)
|
|
|
+ .filter_map(|idx| {
|
|
|
+ portaudio::device::get_info(idx).map(|info| (idx, info))
|
|
|
+ }).filter(|&(_, ref info)| {
|
|
|
+ info.max_output_channels > 0
|
|
|
+ });
|
|
|
+
|
|
|
+ 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)
|
|
|
+}
|
|
|
+
|
|
|
impl <'a> Open for PortAudioSink<'a> {
|
|
|
- fn open() -> PortAudioSink<'a> {
|
|
|
+ fn open(device: Option<&str>) -> PortAudioSink<'a> {
|
|
|
+ use portaudio::stream::*;
|
|
|
+
|
|
|
+ debug!("Using PortAudio sink");
|
|
|
+
|
|
|
portaudio::initialize().unwrap();
|
|
|
|
|
|
- let stream = portaudio::stream::Stream::open_default(
|
|
|
- 0, 2, 44100.0,
|
|
|
- portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
|
|
|
- None
|
|
|
+ let device_idx = match device {
|
|
|
+ Some("?") => {
|
|
|
+ list_outputs();
|
|
|
+ exit(0)
|
|
|
+ }
|
|
|
+ Some(device) => find_output(device),
|
|
|
+ None => get_default_output_index(),
|
|
|
+ }.expect("Could not find device");
|
|
|
+
|
|
|
+ let params = StreamParameters {
|
|
|
+ device: device_idx,
|
|
|
+ channel_count: 2,
|
|
|
+ // Super hacky workaround the fact that Duration is private
|
|
|
+ // in portaudio
|
|
|
+ suggested_latency: unsafe { ::std::mem::transmute(0i64) },
|
|
|
+ data: 0i16,
|
|
|
+ };
|
|
|
+
|
|
|
+ let stream = Stream::open(
|
|
|
+ None, Some(params),
|
|
|
+ 44100.0,
|
|
|
+ FRAMES_PER_BUFFER_UNSPECIFIED,
|
|
|
+ StreamFlags::empty(),
|
|
|
+ None
|
|
|
).unwrap();
|
|
|
|
|
|
PortAudioSink(stream)
|
|
@@ -30,7 +86,8 @@ impl <'a> Sink for PortAudioSink<'a> {
|
|
|
fn write(&self, data: &[i16]) -> io::Result<()> {
|
|
|
match self.0.write(&data) {
|
|
|
Ok(_) => (),
|
|
|
- Err(portaudio::PaError::OutputUnderflowed) => error!("PortAudio write underflow"),
|
|
|
+ Err(portaudio::PaError::OutputUnderflowed) =>
|
|
|
+ error!("PortAudio write underflow"),
|
|
|
Err(e) => panic!("PA Error {}", e),
|
|
|
};
|
|
|
|