1 use super::{Open, Sink};
2 use portaudio_rs;
3 use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo};
4 use portaudio_rs::stream::*;
5 use std::io;
6 use std::process::exit;
7 use std::time::Duration;
8
9 pub struct PortAudioSink<'a>(
10 Option<portaudio_rs::stream::Stream<'a, i16, i16>>,
11 StreamParameters<i16>,
12 );
13
output_devices() -> Box<dyn Iterator<Item = (DeviceIndex, DeviceInfo)>>14 fn output_devices() -> Box<dyn Iterator<Item = (DeviceIndex, DeviceInfo)>> {
15 let count = portaudio_rs::device::get_count().unwrap();
16 let devices = (0..count)
17 .filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info)))
18 .filter(|&(_, ref info)| info.max_output_channels > 0);
19
20 Box::new(devices)
21 }
22
list_outputs()23 fn list_outputs() {
24 let default = get_default_output_index();
25
26 for (idx, info) in output_devices() {
27 if Some(idx) == default {
28 println!("- {} (default)", info.name);
29 } else {
30 println!("- {}", info.name)
31 }
32 }
33 }
34
find_output(device: &str) -> Option<DeviceIndex>35 fn find_output(device: &str) -> Option<DeviceIndex> {
36 output_devices()
37 .find(|&(_, ref info)| info.name == device)
38 .map(|(idx, _)| idx)
39 }
40
41 impl<'a> Open for PortAudioSink<'a> {
open(device: Option<String>) -> PortAudioSink<'a>42 fn open(device: Option<String>) -> PortAudioSink<'a> {
43 debug!("Using PortAudio sink");
44
45 portaudio_rs::initialize().unwrap();
46
47 let device_idx = match device.as_ref().map(AsRef::as_ref) {
48 Some("?") => {
49 list_outputs();
50 exit(0)
51 }
52 Some(device) => find_output(device),
53 None => get_default_output_index(),
54 }
55 .expect("Could not find device");
56
57 let info = portaudio_rs::device::get_info(device_idx);
58 let latency = match info {
59 Some(info) => info.default_high_output_latency,
60 None => Duration::new(0, 0),
61 };
62
63 let params = StreamParameters {
64 device: device_idx,
65 channel_count: 2,
66 suggested_latency: latency,
67 data: 0i16,
68 };
69
70 PortAudioSink(None, params)
71 }
72 }
73
74 impl<'a> Sink for PortAudioSink<'a> {
start(&mut self) -> io::Result<()>75 fn start(&mut self) -> io::Result<()> {
76 if self.0.is_none() {
77 self.0 = Some(
78 Stream::open(
79 None,
80 Some(self.1),
81 44100.0,
82 FRAMES_PER_BUFFER_UNSPECIFIED,
83 StreamFlags::empty(),
84 None,
85 )
86 .unwrap(),
87 );
88 }
89
90 self.0.as_mut().unwrap().start().unwrap();
91 Ok(())
92 }
stop(&mut self) -> io::Result<()>93 fn stop(&mut self) -> io::Result<()> {
94 self.0.as_mut().unwrap().stop().unwrap();
95 self.0 = None;
96 Ok(())
97 }
write(&mut self, data: &[i16]) -> io::Result<()>98 fn write(&mut self, data: &[i16]) -> io::Result<()> {
99 match self.0.as_mut().unwrap().write(data) {
100 Ok(_) => (),
101 Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"),
102 Err(e) => panic!("PA Error {}", e),
103 };
104
105 Ok(())
106 }
107 }
108
109 impl<'a> Drop for PortAudioSink<'a> {
drop(&mut self)110 fn drop(&mut self) {
111 portaudio_rs::terminate().unwrap();
112 }
113 }
114