mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Restore rodiojack support
Probably more simple than the previous approach which doubles the code: Instead of implementing the `Open` trait, we simply use custom SinkBuilder, one for the default host, and one for the "jack" host.
This commit is contained in:
parent
678d1777fd
commit
c0942f14e8
8 changed files with 65 additions and 44 deletions
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -78,6 +78,7 @@ jobs:
|
||||||
- run: cargo build --locked --no-default-features --features "portaudio-backend"
|
- run: cargo build --locked --no-default-features --features "portaudio-backend"
|
||||||
- run: cargo build --locked --no-default-features --features "pulseaudio-backend"
|
- run: cargo build --locked --no-default-features --features "pulseaudio-backend"
|
||||||
- run: cargo build --locked --no-default-features --features "jackaudio-backend"
|
- run: cargo build --locked --no-default-features --features "jackaudio-backend"
|
||||||
|
- run: cargo build --locked --no-default-features --features "rodiojack-backend"
|
||||||
- run: cargo build --locked --no-default-features --features "rodio-backend"
|
- run: cargo build --locked --no-default-features --features "rodio-backend"
|
||||||
- run: cargo build --locked --no-default-features --features "sdl-backend"
|
- run: cargo build --locked --no-default-features --features "sdl-backend"
|
||||||
- run: cargo build --locked --no-default-features --features "gstreamer-backend"
|
- run: cargo build --locked --no-default-features --features "gstreamer-backend"
|
||||||
|
|
|
@ -46,6 +46,7 @@ Depending on the chosen backend, specific development libraries are required.
|
||||||
|PortAudio | `portaudio19-dev` | `portaudio-devel` | `portaudio` |
|
|PortAudio | `portaudio19-dev` | `portaudio-devel` | `portaudio` |
|
||||||
|PulseAudio | `libpulse-dev` | `pulseaudio-libs-devel` | |
|
|PulseAudio | `libpulse-dev` | `pulseaudio-libs-devel` | |
|
||||||
|JACK | `libjack-dev` | `jack-audio-connection-kit-devel` | |
|
|JACK | `libjack-dev` | `jack-audio-connection-kit-devel` | |
|
||||||
|
|JACK over Rodio | `libjack-dev` | `jack-audio-connection-kit-devel` | - |
|
||||||
|SDL | `libsdl2-dev` | `SDL2-devel` | |
|
|SDL | `libsdl2-dev` | `SDL2-devel` | |
|
||||||
|Pipe | - | - | - |
|
|Pipe | - | - | - |
|
||||||
|
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -433,6 +433,7 @@ dependencies = [
|
||||||
"alsa",
|
"alsa",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"coreaudio-rs",
|
"coreaudio-rs",
|
||||||
|
"jack",
|
||||||
"jni 0.17.0",
|
"jni 0.17.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
|
|
@ -66,6 +66,7 @@ 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"]
|
||||||
rodio-backend = ["librespot-playback/rodio-backend"]
|
rodio-backend = ["librespot-playback/rodio-backend"]
|
||||||
|
rodiojack-backend = ["librespot-playback/rodiojack-backend"]
|
||||||
sdl-backend = ["librespot-playback/sdl-backend"]
|
sdl-backend = ["librespot-playback/sdl-backend"]
|
||||||
gstreamer-backend = ["librespot-playback/gstreamer-backend"]
|
gstreamer-backend = ["librespot-playback/gstreamer-backend"]
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ ALSA
|
||||||
PortAudio
|
PortAudio
|
||||||
PulseAudio
|
PulseAudio
|
||||||
JACK
|
JACK
|
||||||
|
JACK over Rodio
|
||||||
SDL
|
SDL
|
||||||
Pipe
|
Pipe
|
||||||
```
|
```
|
||||||
|
|
|
@ -46,5 +46,6 @@ portaudio-backend = ["portaudio-rs"]
|
||||||
pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"]
|
pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"]
|
||||||
jackaudio-backend = ["jack"]
|
jackaudio-backend = ["jack"]
|
||||||
rodio-backend = ["rodio", "cpal", "thiserror"]
|
rodio-backend = ["rodio", "cpal", "thiserror"]
|
||||||
|
rodiojack-backend = ["rodio", "cpal/jack", "thiserror"]
|
||||||
sdl-backend = ["sdl2"]
|
sdl-backend = ["sdl2"]
|
||||||
gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"]
|
gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"]
|
||||||
|
|
|
@ -42,10 +42,9 @@ mod gstreamer;
|
||||||
#[cfg(feature = "gstreamer-backend")]
|
#[cfg(feature = "gstreamer-backend")]
|
||||||
use self::gstreamer::GstreamerSink;
|
use self::gstreamer::GstreamerSink;
|
||||||
|
|
||||||
#[cfg(feature = "rodio-backend")]
|
#[cfg(any(feature = "rodio-backend", feature = "rodiojack-backend"))]
|
||||||
mod rodio;
|
mod rodio;
|
||||||
#[cfg(feature = "rodio-backend")]
|
|
||||||
use self::rodio::RodioSink;
|
|
||||||
#[cfg(feature = "sdl-backend")]
|
#[cfg(feature = "sdl-backend")]
|
||||||
mod sdl;
|
mod sdl;
|
||||||
#[cfg(feature = "sdl-backend")]
|
#[cfg(feature = "sdl-backend")]
|
||||||
|
@ -69,7 +68,9 @@ pub const BACKENDS: &'static [(&'static str, SinkBuilder)] = &[
|
||||||
#[cfg(feature = "gstreamer-backend")]
|
#[cfg(feature = "gstreamer-backend")]
|
||||||
("gstreamer", mk_sink::<GstreamerSink>),
|
("gstreamer", mk_sink::<GstreamerSink>),
|
||||||
#[cfg(feature = "rodio-backend")]
|
#[cfg(feature = "rodio-backend")]
|
||||||
("rodio", mk_sink::<RodioSink>),
|
("rodio", rodio::mk_rodio),
|
||||||
|
#[cfg(feature = "rodiojack-backend")]
|
||||||
|
("rodiojack", rodio::mk_rodiojack),
|
||||||
#[cfg(feature = "sdl-backend")]
|
#[cfg(feature = "sdl-backend")]
|
||||||
("sdl", mk_sink::<SdlSink>),
|
("sdl", mk_sink::<SdlSink>),
|
||||||
("pipe", mk_sink::<StdoutSink>),
|
("pipe", mk_sink::<StdoutSink>),
|
||||||
|
|
|
@ -5,9 +5,28 @@ use std::{io, thread, time};
|
||||||
use cpal::traits::{DeviceTrait, HostTrait};
|
use cpal::traits::{DeviceTrait, HostTrait};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::{Open, Sink};
|
use super::Sink;
|
||||||
use crate::audio::AudioPacket;
|
use crate::audio::AudioPacket;
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "rodiojack-backend",
|
||||||
|
not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))
|
||||||
|
))]
|
||||||
|
compile_error!("Rodio JACK backend is currently only supported on linux.");
|
||||||
|
|
||||||
|
#[cfg(feature = "rodio-backend")]
|
||||||
|
pub fn mk_rodio(device: Option<String>) -> Box<dyn Sink + Send> {
|
||||||
|
Box::new(open(cpal::default_host(), device))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rodiojack-backend")]
|
||||||
|
pub fn mk_rodiojack(device: Option<String>) -> Box<dyn Sink + Send> {
|
||||||
|
Box::new(open(
|
||||||
|
cpal::host_from_id(cpal::HostId::Jack).unwrap(),
|
||||||
|
device,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RodioError {
|
pub enum RodioError {
|
||||||
#[error("Rodio: no device available")]
|
#[error("Rodio: no device available")]
|
||||||
|
@ -60,10 +79,10 @@ fn list_formats(device: &rodio::Device) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_outputs() -> Result<(), cpal::DevicesError> {
|
fn list_outputs(host: &cpal::Host) -> Result<(), cpal::DevicesError> {
|
||||||
let mut default_device_name = None;
|
let mut default_device_name = None;
|
||||||
|
|
||||||
if let Some(default_device) = get_default_device() {
|
if let Some(default_device) = host.default_output_device() {
|
||||||
default_device_name = default_device.name().ok();
|
default_device_name = default_device.name().ok();
|
||||||
println!(
|
println!(
|
||||||
"Default Audio Device:\n {}",
|
"Default Audio Device:\n {}",
|
||||||
|
@ -77,7 +96,7 @@ fn list_outputs() -> Result<(), cpal::DevicesError> {
|
||||||
warn!("No default device was found");
|
warn!("No default device was found");
|
||||||
}
|
}
|
||||||
|
|
||||||
for device in cpal::default_host().output_devices()? {
|
for device in host.output_devices()? {
|
||||||
match device.name() {
|
match device.name() {
|
||||||
Ok(name) if Some(&name) == default_device_name.as_ref() => (),
|
Ok(name) if Some(&name) == default_device_name.as_ref() => (),
|
||||||
Ok(name) => {
|
Ok(name) => {
|
||||||
|
@ -95,14 +114,13 @@ fn list_outputs() -> Result<(), cpal::DevicesError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_default_device() -> Option<rodio::Device> {
|
fn create_sink(
|
||||||
cpal::default_host().default_output_device()
|
host: &cpal::Host,
|
||||||
}
|
device: Option<String>,
|
||||||
|
) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
|
||||||
fn create_sink(device: Option<String>) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
|
|
||||||
let rodio_device = match device {
|
let rodio_device = match device {
|
||||||
Some(ask) if &ask == "?" => {
|
Some(ask) if &ask == "?" => {
|
||||||
let exit_code = match list_outputs() {
|
let exit_code = match list_outputs(host) {
|
||||||
Ok(()) => 0,
|
Ok(()) => 0,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{}", e);
|
error!("{}", e);
|
||||||
|
@ -112,12 +130,13 @@ fn create_sink(device: Option<String>) -> Result<(rodio::Sink, rodio::OutputStre
|
||||||
exit(exit_code)
|
exit(exit_code)
|
||||||
}
|
}
|
||||||
Some(device_name) => {
|
Some(device_name) => {
|
||||||
cpal::default_host()
|
host.output_devices()?
|
||||||
.output_devices()?
|
|
||||||
.find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
|
.find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
|
||||||
.ok_or(RodioError::DeviceNotAvailable(device_name))?
|
.ok_or(RodioError::DeviceNotAvailable(device_name))?
|
||||||
}
|
}
|
||||||
None => get_default_device().ok_or(RodioError::NoDeviceAvailable)?,
|
None => host
|
||||||
|
.default_output_device()
|
||||||
|
.ok_or(RodioError::NoDeviceAvailable)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = rodio_device.name().ok();
|
let name = rodio_device.name().ok();
|
||||||
|
@ -131,37 +150,32 @@ fn create_sink(device: Option<String>) -> Result<(rodio::Sink, rodio::OutputStre
|
||||||
Ok((sink, stream))
|
Ok((sink, stream))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open for RodioSink {
|
pub fn open(host: cpal::Host, device: Option<String>) -> RodioSink {
|
||||||
fn open(device: Option<String>) -> RodioSink {
|
debug!("Using rodio sink with cpal host: {}", host.id().name());
|
||||||
debug!(
|
|
||||||
"Using rodio sink with cpal host: {:?}",
|
|
||||||
cpal::default_host().id().name()
|
|
||||||
);
|
|
||||||
|
|
||||||
let (sink_tx, sink_rx) = mpsc::sync_channel(1);
|
let (sink_tx, sink_rx) = mpsc::sync_channel(1);
|
||||||
let (close_tx, close_rx) = mpsc::sync_channel(1);
|
let (close_tx, close_rx) = mpsc::sync_channel(1);
|
||||||
|
|
||||||
std::thread::spawn(move || match create_sink(device) {
|
std::thread::spawn(move || match create_sink(&host, device) {
|
||||||
Ok((sink, stream)) => {
|
Ok((sink, stream)) => {
|
||||||
sink_tx.send(Ok(sink)).unwrap();
|
sink_tx.send(Ok(sink)).unwrap();
|
||||||
|
|
||||||
close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped
|
close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped
|
||||||
debug!("drop rodio::OutputStream");
|
debug!("drop rodio::OutputStream");
|
||||||
drop(stream);
|
drop(stream);
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
sink_tx.send(Err(e)).unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Instead of the second `unwrap`, better error handling could be introduced
|
|
||||||
let sink = sink_rx.recv().unwrap().unwrap();
|
|
||||||
|
|
||||||
debug!("Rodio sink was created");
|
|
||||||
RodioSink {
|
|
||||||
rodio_sink: sink,
|
|
||||||
_close_tx: close_tx,
|
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
sink_tx.send(Err(e)).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Instead of the second `unwrap`, better error handling could be introduced
|
||||||
|
let sink = sink_rx.recv().unwrap().unwrap();
|
||||||
|
|
||||||
|
debug!("Rodio sink was created");
|
||||||
|
RodioSink {
|
||||||
|
rodio_sink: sink,
|
||||||
|
_close_tx: close_tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue