librespot/playback/src/audio_backend/portaudio.rs

111 lines
3 KiB
Rust
Raw Normal View History

use super::{Open, Sink};
use std::io;
2016-07-06 09:54:46 +00:00
use std::process::exit;
use std::time::Duration;
use portaudio_rs;
use portaudio_rs::stream::*;
use portaudio_rs::device::{DeviceIndex, DeviceInfo, get_default_output_index};
pub struct PortAudioSink<'a>(Option<portaudio_rs::stream::Stream<'a, i16, i16>>, StreamParameters<i16>);
2016-07-06 09:54:46 +00:00
fn output_devices() -> Box<Iterator<Item=(DeviceIndex, DeviceInfo)>> {
let count = portaudio_rs::device::get_count().unwrap();
2016-07-06 09:54:46 +00:00
let devices = (0..count)
.filter_map(|idx| {
portaudio_rs::device::get_info(idx).map(|info| (idx, info))
2016-07-06 09:54:46 +00:00
}).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> {
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");
portaudio_rs::initialize().unwrap();
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");
let info = portaudio_rs::device::get_info(device_idx);
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,
suggested_latency: latency,
2016-07-06 09:54:46 +00:00
data: 0i16,
};
2016-08-01 19:20:17 +00:00
PortAudioSink(None, params)
}
}
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() {
self.0 = Some(Stream::open(
None, Some(self.1),
44100.0,
FRAMES_PER_BUFFER_UNSPECIFIED,
StreamFlags::empty(),
None
).unwrap());;
}
self.0.as_mut().unwrap().start().unwrap();
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;
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) {
Ok(_) => (),
Err(portaudio_rs::PaError::OutputUnderflowed) =>
2016-07-06 09:54:46 +00:00
error!("PortAudio write underflow"),
Err(e) => panic!("PA Error {}", e),
};
Ok(())
}
}
impl <'a> Drop for PortAudioSink<'a> {
fn drop(&mut self) {
portaudio_rs::terminate().unwrap();
}
}