librespot/src/audio_backend/portaudio.rs

103 lines
2.7 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 portaudio;
2016-07-06 09:54:46 +00:00
use portaudio::device::{DeviceIndex, DeviceInfo, get_default_output_index};
pub struct PortAudioSink<'a>(portaudio::stream::Stream<'a, i16, i16>);
2016-07-06 09:54:46 +00:00
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> {
2016-07-06 09:54:46 +00:00
fn open(device: Option<&str>) -> PortAudioSink<'a> {
use portaudio::stream::*;
debug!("Using PortAudio sink");
portaudio::initialize().unwrap();
2016-07-06 09:54:46 +00:00
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)
}
}
impl <'a> Sink for PortAudioSink<'a> {
fn start(&self) -> io::Result<()> {
self.0.start().unwrap();
Ok(())
}
fn stop(&self) -> io::Result<()> {
self.0.stop().unwrap();
Ok(())
}
fn write(&self, data: &[i16]) -> io::Result<()> {
match self.0.write(&data) {
Ok(_) => (),
2016-07-06 09:54:46 +00:00
Err(portaudio::PaError::OutputUnderflowed) =>
error!("PortAudio write underflow"),
Err(e) => panic!("PA Error {}", e),
};
Ok(())
}
}
impl <'a> Drop for PortAudioSink<'a> {
fn drop(&mut self) {
portaudio::terminate().unwrap();
}
}