From 2c2bfc52acd8d24af04870f4a657d32a5a12eb9f Mon Sep 17 00:00:00 2001 From: Will Stott Date: Wed, 20 Mar 2019 13:24:03 +0000 Subject: [PATCH] Cpal -> Rodio Doesn't work that well. --- Cargo.toml | 2 +- playback/Cargo.toml | 4 +- playback/src/audio_backend/cpal.rs | 103 ---------------------------- playback/src/audio_backend/mod.rs | 12 ++-- playback/src/audio_backend/rodio.rs | 85 +++++++++++++++++++++++ 5 files changed, 94 insertions(+), 112 deletions(-) delete mode 100644 playback/src/audio_backend/cpal.rs create mode 100644 playback/src/audio_backend/rodio.rs diff --git a/Cargo.toml b/Cargo.toml index 5b6765a2..761b23d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +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"] +rodio-backend = ["librespot-playback/rodio-backend"] with-tremor = ["librespot-audio/with-tremor"] with-vorbis = ["librespot-audio/with-vorbis"] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 32be6d1f..6b4e7756 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -20,11 +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 } +rodio = { version = "0.8.1", optional = true, default-features = false } [features] alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-sys", "libc"] jackaudio-backend = ["jack"] -cpal-backend = ["cpal"] +rodio-backend = ["rodio"] diff --git a/playback/src/audio_backend/cpal.rs b/playback/src/audio_backend/cpal.rs deleted file mode 100644 index 8a71b9e4..00000000 --- a/playback/src/audio_backend/cpal.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::{Open, Sink}; -extern crate cpal; -use std::io; -use std::thread; -use std::sync::mpsc::{sync_channel, SyncSender}; -use std::process::exit; - -pub struct CpalSink { - // event_loop: cpal::EventLoop, - send: SyncSender, -} - -fn list_outputs() { - println!("Default Audio Device:\n {:?}", cpal::default_output_device().map(|e| e.name())); - - println!("Available Audio Devices:"); - for device in cpal::output_devices() { - println!("- {}", device.name()); - // Output formats - if let Ok(fmt) = device.default_output_format() { - println!(" Default format:\n {:?}", fmt); - } - let mut output_formats = match device.supported_output_formats() { - Ok(f) => f.peekable(), - Err(e) => { - println!("Error: {:?}", e); - continue; - }, - }; - if output_formats.peek().is_some() { - println!(" All formats:"); - for format in output_formats { - println!(" {:?}", format); - } - } - } -} - -impl Open for CpalSink { - fn open(device: Option) -> CpalSink { - info!("Using cpal sink"); - - // buffer for samples from librespot (~10ms) - let (tx, rx) = sync_channel::(2 * 1024 * 4); - let event_loop = cpal::EventLoop::new(); - - if device.is_some() { - if device == Some("?".to_string()) { - list_outputs(); - exit(0) - } - // 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 cpal_device = cpal::default_output_device().expect("no output device available"); - // TODO: Support more formats? Surely cpal will handle that. - let format = cpal::Format{channels: 2, sample_rate: cpal::SampleRate(44100), data_type: cpal::SampleFormat::I16}; - - let stream_id = event_loop.build_output_stream(&cpal_device, &format).expect("could not build output stream"); - event_loop.play_stream(stream_id); - - thread::spawn(move |/*event_loop, rx*/| { - event_loop.run(move |_stream_id, stream_data| { - match stream_data { - cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { - for (sample, recv) in buffer.iter_mut().zip(rx.try_iter()) { - *sample = recv; - } - }, - _ => (), - } - }); - }); - - CpalSink { - send: tx, - // event_loop: event_loop, - } - } -} - -impl Sink for CpalSink { - fn start(&mut self) -> io::Result<()> { - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, data: &[i16]) -> io::Result<()> { - for s in data.iter() { - let res = self.send.send(*s); - if res.is_err() { - error!("jackaudio: cannot write to channel"); - } - } - Ok(()) - } -} diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 077cdf3a..85e05d55 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -34,10 +34,10 @@ mod jackaudio; #[cfg(feature = "jackaudio-backend")] use self::jackaudio::JackSink; -#[cfg(feature = "cpal-backend")] -mod cpal; -#[cfg(feature = "cpal-backend")] -use self::cpal::CpalSink; +#[cfg(feature = "rodio-backend")] +mod rodio; +#[cfg(feature = "rodio-backend")] +use self::rodio::RodioSink; mod pipe; use self::pipe::StdoutSink; @@ -51,8 +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::), + #[cfg(feature = "rodio-backend")] + ("rodio", mk_sink::), ("pipe", mk_sink::), ]; diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs new file mode 100644 index 00000000..1e6fbb04 --- /dev/null +++ b/playback/src/audio_backend/rodio.rs @@ -0,0 +1,85 @@ +use super::{Open, Sink}; +extern crate rodio; +use std::io; +use std::process::exit; + +pub struct RodioSink { + rodio_sink: rodio::Sink, +} + +fn list_outputs() { + println!("Default Audio Device:\n {:?}", rodio::default_output_device().map(|e| e.name())); + + println!("Available Audio Devices:"); + for device in rodio::output_devices() { + println!("- {}", device.name()); + // Output formats + if let Ok(fmt) = device.default_output_format() { + println!(" Default format:\n {:?}", fmt); + } + let mut output_formats = match device.supported_output_formats() { + Ok(f) => f.peekable(), + Err(e) => { + println!("Error: {:?}", e); + continue; + }, + }; + if output_formats.peek().is_some() { + println!(" All formats:"); + for format in output_formats { + println!(" {:?}", format); + } + } + } +} + +impl Open for RodioSink { + fn open(device: Option) -> RodioSink { + info!("Using rodio sink"); + + let mut rodio_device = rodio::default_output_device().expect("no output device available"); + if device.is_some() { + let device_name = device.unwrap(); + + if device_name == "?".to_string() { + list_outputs(); + exit(0) + } + let mut found = false; + for d in rodio::output_devices() { + if d.name() == device_name { + rodio_device = d; + found = true; + break; + } + } + if !found { + println!("No output sink matching '{}' found.", device_name); + exit(0) + } + } + let sink = rodio::Sink::new(&rodio_device); + + RodioSink { + rodio_sink: sink, + } + } +} + +impl Sink for RodioSink { + fn start(&mut self) -> io::Result<()> { + self.rodio_sink.play(); + Ok(()) + } + + fn stop(&mut self) -> io::Result<()> { + self.rodio_sink.stop(); + Ok(()) + } + + fn write(&mut self, data: &[i16]) -> io::Result<()> { + let source = rodio::buffer::SamplesBuffer::new(2, 44100, data); + self.rodio_sink.append(source); + Ok(()) + } +}