mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Cpal -> Rodio
Doesn't work that well.
This commit is contained in:
parent
1eb5b7d127
commit
2c2bfc52ac
5 changed files with 94 additions and 112 deletions
|
@ -63,7 +63,7 @@ alsa-backend = ["librespot-playback/alsa-backend"]
|
||||||
portaudio-backend = ["librespot-playback/portaudio-backend"]
|
portaudio-backend = ["librespot-playback/portaudio-backend"]
|
||||||
pulseaudio-backend = ["librespot-playback/pulseaudio-backend"]
|
pulseaudio-backend = ["librespot-playback/pulseaudio-backend"]
|
||||||
jackaudio-backend = ["librespot-playback/jackaudio-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-tremor = ["librespot-audio/with-tremor"]
|
||||||
with-vorbis = ["librespot-audio/with-vorbis"]
|
with-vorbis = ["librespot-audio/with-vorbis"]
|
||||||
|
|
|
@ -20,11 +20,11 @@ portaudio-rs = { version = "0.3.0", optional = true }
|
||||||
libpulse-sys = { version = "0.0.0", optional = true }
|
libpulse-sys = { version = "0.0.0", optional = true }
|
||||||
jack = { version = "0.5.3", optional = true }
|
jack = { version = "0.5.3", optional = true }
|
||||||
libc = { version = "0.2", 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]
|
[features]
|
||||||
alsa-backend = ["alsa"]
|
alsa-backend = ["alsa"]
|
||||||
portaudio-backend = ["portaudio-rs"]
|
portaudio-backend = ["portaudio-rs"]
|
||||||
pulseaudio-backend = ["libpulse-sys", "libc"]
|
pulseaudio-backend = ["libpulse-sys", "libc"]
|
||||||
jackaudio-backend = ["jack"]
|
jackaudio-backend = ["jack"]
|
||||||
cpal-backend = ["cpal"]
|
rodio-backend = ["rodio"]
|
||||||
|
|
|
@ -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<i16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<String>) -> CpalSink {
|
|
||||||
info!("Using cpal sink");
|
|
||||||
|
|
||||||
// buffer for samples from librespot (~10ms)
|
|
||||||
let (tx, rx) = sync_channel::<i16>(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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -34,10 +34,10 @@ mod jackaudio;
|
||||||
#[cfg(feature = "jackaudio-backend")]
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
use self::jackaudio::JackSink;
|
use self::jackaudio::JackSink;
|
||||||
|
|
||||||
#[cfg(feature = "cpal-backend")]
|
#[cfg(feature = "rodio-backend")]
|
||||||
mod cpal;
|
mod rodio;
|
||||||
#[cfg(feature = "cpal-backend")]
|
#[cfg(feature = "rodio-backend")]
|
||||||
use self::cpal::CpalSink;
|
use self::rodio::RodioSink;
|
||||||
|
|
||||||
mod pipe;
|
mod pipe;
|
||||||
use self::pipe::StdoutSink;
|
use self::pipe::StdoutSink;
|
||||||
|
@ -51,8 +51,8 @@ pub const BACKENDS: &'static [(&'static str, fn(Option<String>) -> Box<Sink>)] =
|
||||||
("pulseaudio", mk_sink::<PulseAudioSink>),
|
("pulseaudio", mk_sink::<PulseAudioSink>),
|
||||||
#[cfg(feature = "jackaudio-backend")]
|
#[cfg(feature = "jackaudio-backend")]
|
||||||
("jackaudio", mk_sink::<JackSink>),
|
("jackaudio", mk_sink::<JackSink>),
|
||||||
#[cfg(feature = "cpal-backend")]
|
#[cfg(feature = "rodio-backend")]
|
||||||
("cpal", mk_sink::<CpalSink>),
|
("rodio", mk_sink::<RodioSink>),
|
||||||
("pipe", mk_sink::<StdoutSink>),
|
("pipe", mk_sink::<StdoutSink>),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
85
playback/src/audio_backend/rodio.rs
Normal file
85
playback/src/audio_backend/rodio.rs
Normal file
|
@ -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<String>) -> 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(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue