librespot/playback/src/audio_backend/portaudio.rs

178 lines
5.5 KiB
Rust
Raw Normal View History

use super::{Open, Sink};
2021-01-07 06:42:38 +00:00
use crate::audio::AudioPacket;
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLE_RATE};
2018-02-26 01:50:41 +00:00
use portaudio_rs;
use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo};
use portaudio_rs::stream::*;
use std::io;
2016-07-06 09:54:46 +00:00
use std::process::exit;
use std::time::Duration;
pub enum PortAudioSink<'a> {
F32(
Option<portaudio_rs::stream::Stream<'a, f32, f32>>,
StreamParameters<f32>,
),
S32(
Option<portaudio_rs::stream::Stream<'a, i32, i32>>,
StreamParameters<i32>,
),
S16(
Option<portaudio_rs::stream::Stream<'a, i16, i16>>,
StreamParameters<i16>,
),
}
fn output_devices() -> Box<dyn Iterator<Item = (DeviceIndex, DeviceInfo)>> {
let count = portaudio_rs::device::get_count().unwrap();
2016-07-06 09:54:46 +00:00
let devices = (0..count)
2018-02-26 01:50:41 +00:00
.filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info)))
.filter(|&(_, ref info)| info.max_output_channels > 0);
2016-07-06 09:54:46 +00:00
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)
}
2018-02-26 01:50:41 +00:00
impl<'a> Open for PortAudioSink<'a> {
fn open(device: Option<String>, format: AudioFormat) -> PortAudioSink<'a> {
info!("Using PortAudio sink with format: {:?}", format);
2016-07-06 09:54:46 +00:00
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");
2016-07-06 09:54:46 +00:00
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),
};
macro_rules! open_sink {
($sink: expr, $type: ty) => {{
let params = StreamParameters {
device: device_idx,
channel_count: NUM_CHANNELS as u32,
suggested_latency: latency,
data: 0.0 as $type,
};
$sink(None, params)
}};
}
match format {
AudioFormat::F32 => open_sink!(Self::F32, f32),
AudioFormat::S32 => open_sink!(Self::S32, i32),
AudioFormat::S16 => open_sink!(Self::S16, i16),
_ => {
unimplemented!("PortAudio currently does not support {:?} output", format)
}
}
}
}
2018-02-26 01:50:41 +00:00
impl<'a> Sink for PortAudioSink<'a> {
2016-08-01 19:20:17 +00:00
fn start(&mut self) -> io::Result<()> {
macro_rules! start_sink {
($stream: ident, $parameters: ident) => {{
if $stream.is_none() {
*$stream = Some(
Stream::open(
None,
Some(*$parameters),
SAMPLE_RATE as f64,
FRAMES_PER_BUFFER_UNSPECIFIED,
StreamFlags::empty(),
None,
)
.unwrap(),
);
}
$stream.as_mut().unwrap().start().unwrap()
}};
2016-08-01 19:20:17 +00:00
}
match self {
Self::F32(stream, parameters) => start_sink!(stream, parameters),
Self::S32(stream, parameters) => start_sink!(stream, parameters),
Self::S16(stream, parameters) => start_sink!(stream, parameters),
};
2016-08-01 19:20:17 +00:00
Ok(())
}
2016-08-01 19:20:17 +00:00
fn stop(&mut self) -> io::Result<()> {
macro_rules! stop_sink {
($stream: expr) => {{
$stream.as_mut().unwrap().stop().unwrap();
*$stream = None;
}};
}
match self {
Self::F32(stream, _parameters) => stop_sink!(stream),
Self::S32(stream, _parameters) => stop_sink!(stream),
Self::S16(stream, _parameters) => stop_sink!(stream),
};
Ok(())
}
2021-01-07 06:42:38 +00:00
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
macro_rules! write_sink {
($stream: expr, $samples: expr) => {
$stream.as_mut().unwrap().write($samples)
};
}
let result = match self {
Self::F32(stream, _parameters) => {
let samples = packet.samples();
write_sink!(stream, &samples)
}
Self::S32(stream, _parameters) => {
let samples_s32: Vec<i32> = AudioPacket::f32_to_s32(packet.samples());
write_sink!(stream, &samples_s32)
}
Self::S16(stream, _parameters) => {
let samples_s16: Vec<i16> = AudioPacket::f32_to_s16(packet.samples());
write_sink!(stream, &samples_s16)
}
};
match result {
Ok(_) => (),
2018-02-26 01:50:41 +00:00
Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"),
Err(e) => panic!("PortAudio error {}", e),
};
Ok(())
}
}
2018-02-26 01:50:41 +00:00
impl<'a> Drop for PortAudioSink<'a> {
fn drop(&mut self) {
portaudio_rs::terminate().unwrap();
}
}