mirror of
https://github.com/librespot-org/librespot.git
synced 2025-10-05 02:39:53 +02:00

* Remove deprecated use of std::u16::MAX * Use `FromStr` for fallible `&str` conversions * DRY up strings into constants * Change `as_ref().map()` into `as_deref()` * Use `Duration` for time constants and functions * Optimize `Vec` with response times * Move comments for `rustdoc` to parse
119 lines
3.6 KiB
Rust
119 lines
3.6 KiB
Rust
use super::{Open, Sink};
|
|
use crate::config::AudioFormat;
|
|
use crate::convert::Converter;
|
|
use crate::decoder::AudioPacket;
|
|
use crate::{NUM_CHANNELS, SAMPLE_RATE};
|
|
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
|
use std::time::Duration;
|
|
use std::{io, thread};
|
|
|
|
pub enum SdlSink {
|
|
F32(AudioQueue<f32>),
|
|
S32(AudioQueue<i32>),
|
|
S16(AudioQueue<i16>),
|
|
}
|
|
|
|
impl Open for SdlSink {
|
|
fn open(device: Option<String>, format: AudioFormat) -> Self {
|
|
info!("Using SDL sink with format: {:?}", format);
|
|
|
|
if device.is_some() {
|
|
warn!("SDL sink does not support specifying a device name");
|
|
}
|
|
|
|
let ctx = sdl2::init().expect("could not initialize SDL");
|
|
let audio = ctx
|
|
.audio()
|
|
.expect("could not initialize SDL audio subsystem");
|
|
|
|
let desired_spec = AudioSpecDesired {
|
|
freq: Some(SAMPLE_RATE as i32),
|
|
channels: Some(NUM_CHANNELS),
|
|
samples: None,
|
|
};
|
|
|
|
macro_rules! open_sink {
|
|
($sink: expr, $type: ty) => {{
|
|
let queue: AudioQueue<$type> = audio
|
|
.open_queue(None, &desired_spec)
|
|
.expect("could not open SDL audio device");
|
|
$sink(queue)
|
|
}};
|
|
}
|
|
match format {
|
|
AudioFormat::F32 => open_sink!(Self::F32, f32),
|
|
AudioFormat::S32 => open_sink!(Self::S32, i32),
|
|
AudioFormat::S16 => open_sink!(Self::S16, i16),
|
|
_ => {
|
|
unimplemented!("SDL currently does not support {:?} output", format)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sink for SdlSink {
|
|
fn start(&mut self) -> io::Result<()> {
|
|
macro_rules! start_sink {
|
|
($queue: expr) => {{
|
|
$queue.clear();
|
|
$queue.resume();
|
|
}};
|
|
}
|
|
match self {
|
|
Self::F32(queue) => start_sink!(queue),
|
|
Self::S32(queue) => start_sink!(queue),
|
|
Self::S16(queue) => start_sink!(queue),
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn stop(&mut self) -> io::Result<()> {
|
|
macro_rules! stop_sink {
|
|
($queue: expr) => {{
|
|
$queue.pause();
|
|
$queue.clear();
|
|
}};
|
|
}
|
|
match self {
|
|
Self::F32(queue) => stop_sink!(queue),
|
|
Self::S32(queue) => stop_sink!(queue),
|
|
Self::S16(queue) => stop_sink!(queue),
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn write(&mut self, packet: &AudioPacket, converter: &mut Converter) -> io::Result<()> {
|
|
macro_rules! drain_sink {
|
|
($queue: expr, $size: expr) => {{
|
|
// sleep and wait for sdl thread to drain the queue a bit
|
|
while $queue.size() > (NUM_CHANNELS as u32 * $size as u32 * SAMPLE_RATE) {
|
|
thread::sleep(Duration::from_millis(10));
|
|
}
|
|
}};
|
|
}
|
|
|
|
let samples = packet.samples();
|
|
match self {
|
|
Self::F32(queue) => {
|
|
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
|
|
drain_sink!(queue, AudioFormat::F32.size());
|
|
queue.queue(samples_f32)
|
|
}
|
|
Self::S32(queue) => {
|
|
let samples_s32: &[i32] = &converter.f64_to_s32(samples);
|
|
drain_sink!(queue, AudioFormat::S32.size());
|
|
queue.queue(samples_s32)
|
|
}
|
|
Self::S16(queue) => {
|
|
let samples_s16: &[i16] = &converter.f64_to_s16(samples);
|
|
drain_sink!(queue, AudioFormat::S16.size());
|
|
queue.queue(samples_s16)
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl SdlSink {
|
|
pub const NAME: &'static str = "sdl";
|
|
}
|