1
0
Fork 0
mirror of https://github.com/librespot-org/librespot.git synced 2025-10-05 10:49:40 +02:00
librespot/playback/src/audio_backend/mod.rs
Roderick van Domburg ad19b69bfb
Various code improvements (#777)
* 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
2021-05-31 22:32:39 +02:00

142 lines
4.5 KiB
Rust

use crate::config::AudioFormat;
use crate::convert::Converter;
use crate::decoder::AudioPacket;
use std::io;
pub trait Open {
fn open(_: Option<String>, format: AudioFormat) -> Self;
}
pub trait Sink {
fn start(&mut self) -> io::Result<()> {
Ok(())
}
fn stop(&mut self) -> io::Result<()> {
Ok(())
}
fn write(&mut self, packet: &AudioPacket, converter: &mut Converter) -> io::Result<()>;
}
pub type SinkBuilder = fn(Option<String>, AudioFormat) -> Box<dyn Sink>;
pub trait SinkAsBytes {
fn write_bytes(&mut self, data: &[u8]) -> io::Result<()>;
}
fn mk_sink<S: Sink + Open + 'static>(device: Option<String>, format: AudioFormat) -> Box<dyn Sink> {
Box::new(S::open(device, format))
}
// reuse code for various backends
macro_rules! sink_as_bytes {
() => {
fn write(&mut self, packet: &AudioPacket, converter: &mut Converter) -> io::Result<()> {
use crate::convert::i24;
use zerocopy::AsBytes;
match packet {
AudioPacket::Samples(samples) => match self.format {
AudioFormat::F64 => self.write_bytes(samples.as_bytes()),
AudioFormat::F32 => {
let samples_f32: &[f32] = &converter.f64_to_f32(samples);
self.write_bytes(samples_f32.as_bytes())
}
AudioFormat::S32 => {
let samples_s32: &[i32] = &converter.f64_to_s32(samples);
self.write_bytes(samples_s32.as_bytes())
}
AudioFormat::S24 => {
let samples_s24: &[i32] = &converter.f64_to_s24(samples);
self.write_bytes(samples_s24.as_bytes())
}
AudioFormat::S24_3 => {
let samples_s24_3: &[i24] = &converter.f64_to_s24_3(samples);
self.write_bytes(samples_s24_3.as_bytes())
}
AudioFormat::S16 => {
let samples_s16: &[i16] = &converter.f64_to_s16(samples);
self.write_bytes(samples_s16.as_bytes())
}
},
AudioPacket::OggData(samples) => self.write_bytes(samples),
}
}
};
}
#[cfg(feature = "alsa-backend")]
mod alsa;
#[cfg(feature = "alsa-backend")]
use self::alsa::AlsaSink;
#[cfg(feature = "portaudio-backend")]
mod portaudio;
#[cfg(feature = "portaudio-backend")]
use self::portaudio::PortAudioSink;
#[cfg(feature = "pulseaudio-backend")]
mod pulseaudio;
#[cfg(feature = "pulseaudio-backend")]
use self::pulseaudio::PulseAudioSink;
#[cfg(feature = "jackaudio-backend")]
mod jackaudio;
#[cfg(feature = "jackaudio-backend")]
use self::jackaudio::JackSink;
#[cfg(feature = "gstreamer-backend")]
mod gstreamer;
#[cfg(feature = "gstreamer-backend")]
use self::gstreamer::GstreamerSink;
#[cfg(any(feature = "rodio-backend", feature = "rodiojack-backend"))]
mod rodio;
#[cfg(any(feature = "rodio-backend", feature = "rodiojack-backend"))]
use self::rodio::RodioSink;
#[cfg(feature = "sdl-backend")]
mod sdl;
#[cfg(feature = "sdl-backend")]
use self::sdl::SdlSink;
mod pipe;
use self::pipe::StdoutSink;
mod subprocess;
use self::subprocess::SubprocessSink;
pub const BACKENDS: &[(&str, SinkBuilder)] = &[
#[cfg(feature = "rodio-backend")]
(RodioSink::NAME, rodio::mk_rodio), // default goes first
#[cfg(feature = "alsa-backend")]
(AlsaSink::NAME, mk_sink::<AlsaSink>),
#[cfg(feature = "portaudio-backend")]
(PortAudioSink::NAME, mk_sink::<PortAudioSink>),
#[cfg(feature = "pulseaudio-backend")]
(PulseAudioSink::NAME, mk_sink::<PulseAudioSink>),
#[cfg(feature = "jackaudio-backend")]
(JackSink::NAME, mk_sink::<JackSink>),
#[cfg(feature = "gstreamer-backend")]
(GstreamerSink::NAME, mk_sink::<GstreamerSink>),
#[cfg(feature = "rodiojack-backend")]
("rodiojack", rodio::mk_rodiojack),
#[cfg(feature = "sdl-backend")]
(SdlSink::NAME, mk_sink::<SdlSink>),
(StdoutSink::NAME, mk_sink::<StdoutSink>),
(SubprocessSink::NAME, mk_sink::<SubprocessSink>),
];
pub fn find(name: Option<String>) -> Option<SinkBuilder> {
if let Some(name) = name {
BACKENDS
.iter()
.find(|backend| name == backend.0)
.map(|backend| backend.1)
} else {
Some(
BACKENDS
.first()
.expect("No backends were enabled at build time")
.1,
)
}
}