From 891298171c5afb13c7be169918915679b6102912 Mon Sep 17 00:00:00 2001 From: Will Stott Date: Thu, 15 Nov 2018 19:34:13 +0000 Subject: [PATCH] Initial untested VecDeque concept. --- Cargo.toml | 1 + playback/Cargo.toml | 2 + playback/src/audio_backend/cpal.rs | 92 ++++++++++++++++++++++++++++++ playback/src/audio_backend/mod.rs | 7 +++ 4 files changed, 102 insertions(+) create mode 100644 playback/src/audio_backend/cpal.rs diff --git a/Cargo.toml b/Cargo.toml index 8231759a..5b6765a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] jackaudio-backend = ["librespot-playback/jackaudio-backend"] +cpal-backend = ["librespot-playback/cpal-backend"] with-tremor = ["librespot-audio/with-tremor"] with-vorbis = ["librespot-audio/with-vorbis"] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 653cbe6c..32be6d1f 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -20,9 +20,11 @@ portaudio-rs = { version = "0.3.0", optional = true } libpulse-sys = { version = "0.0.0", optional = true } jack = { version = "0.5.3", optional = true } libc = { version = "0.2", optional = true } +cpal = { version = "0.8.2", optional = true } [features] alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-sys", "libc"] jackaudio-backend = ["jack"] +cpal-backend = ["cpal"] diff --git a/playback/src/audio_backend/cpal.rs b/playback/src/audio_backend/cpal.rs new file mode 100644 index 00000000..e493c8ac --- /dev/null +++ b/playback/src/audio_backend/cpal.rs @@ -0,0 +1,92 @@ +use super::{Open, Sink}; +extern crate cpal; +use std::io; +use std::thread; +use std::collections::VecDeque; + +pub struct CpalSink { + event_loop: cpal::EventLoop, + buffer: mut VecDeque, + stream_id: Option, +} + +impl Open for CpalSink { + fn open(device: Option) -> CpalSink { + info!("Using cpal sink"); + + if device.is_some() { + // N.B. This is perfectly possible to support. + // TODO: First need to enable listing of devices. + // Remember to filter to those which support Stereo 16bit 44100Hz + // TODO: Choose cpal sink by name. + panic!("cpal sink does not support specifying a device name"); + } + + let event_loop = cpal::EventLoop::new(); + + CpalSink { + // Allow an (arbitrary) 2 second buffer before resizing. + buffer: VecDeque::with_capacity(44100 * 2 * 2), + event_loop: event_loop, + } + } +} + +impl Sink for CpalSink { + fn start(&mut self) -> io::Result<()> { + + if self.stream_id.is_none() { + + let device = cpal::default_output_device().expect("no output device available"); + // TODO: Support more formats. + let format = cpal::Format(2, 44100, cpal::SampleFormat::I16); + + self.stream_id = self.event_loop.build_output_stream(&device, &format)?; + + self.event_loop.play_stream(self.stream_id.clone()); + } + + if self.thread.is_none() { + let event_loop = self.event_loop; + let source = self.buffer; + thread::spawn(move |event_loop, source| { + event_loop.run(move |_stream_id, mut stream_data| { + match data { + cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { + let sl = source.len(); + if (sl > buffer.len()) { + sl = buffer.len(); + } + // let u: Vec<_> = source.drain(..sl).collect(); + // buffer[..s1].copy_from_slice(u[..s1]); + + for (sample, data) in buffer.iter_mut().zip(source.drain(..sl)) { + *sample = data; + } + }, + _ => (), + } + }); + }) + } + + Ok(()) + } + + fn stop(&mut self) -> io::Result<()> { + if !self.stream_id.is_none() { + self.event_loop.destroy_stream(self.stream_id); + self.stream_id = None; + self.buffer.clear(); + } + Ok(()) + } + + fn write(&mut self, data: &[i16]) -> io::Result<()> { + // self.0.as_mut().unwrap().write_interleaved(&data).unwrap(); + // self.buffer.reserve(data.len()); // Unneccessary? + // self.buffer.extend_from_slice(data); + self.buffer.extend(data); + Ok(()) + } +} diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 895b0100..077cdf3a 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -34,6 +34,11 @@ mod jackaudio; #[cfg(feature = "jackaudio-backend")] use self::jackaudio::JackSink; +#[cfg(feature = "cpal-backend")] +mod cpal; +#[cfg(feature = "cpal-backend")] +use self::cpal::CpalSink; + mod pipe; use self::pipe::StdoutSink; @@ -46,6 +51,8 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = ("pulseaudio", mk_sink::), #[cfg(feature = "jackaudio-backend")] ("jackaudio", mk_sink::), + #[cfg(feature = "cpal-backend")] + ("cpal", mk_sink::), ("pipe", mk_sink::), ];