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