Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonLG1979 committed Mar 20, 2022
1 parent dd8155b commit dc9f822
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [main] Add a `-q`, `--quiet` option that changes the logging level to warn.
- [main] Add a short name for every flag and option.
- [main] Add the ability to parse environment variables.
- [playback] `pulseaudio`: set the PulseAudio name to match librespot's device name via `PULSE_PROP_application.name` environment variable (user set env var value takes precedence). (breaking)
- [playback] `pulseaudio`: set icon to `audio-x-generic` so we get an icon instead of a placeholder via `PULSE_PROP_application.icon_name` environment variable (user set env var value takes precedence). (breaking)
- [playback] `pulseaudio`: set values to: `PULSE_PROP_application.version`, `PULSE_PROP_application.process.binary`, `PULSE_PROP_stream.description`, `PULSE_PROP_media.software` and `PULSE_PROP_media.role` environment variables (user set env var values take precedence). (breaking)

### Fixed
- [main] Prevent hang when discovery is disabled and there are no credentials or when bad credentials are given.
Expand Down
41 changes: 22 additions & 19 deletions playback/src/audio_backend/pulseaudio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ use crate::decoder::AudioPacket;
use crate::{NUM_CHANNELS, SAMPLE_RATE};
use libpulse_binding::{self as pulse, error::PAErr, stream::Direction};
use libpulse_simple_binding::Simple;
use std::env;
use thiserror::Error;

const APP_NAME: &str = "librespot";
const STREAM_NAME: &str = "Spotify endpoint";

#[derive(Debug, Error)]
enum PulseError {
#[error("<PulseAudioSink> Unsupported Pulseaudio Sample Spec, Format {pulse_format:?} ({format:?}), Channels {channels}, Rate {rate}")]
Expand Down Expand Up @@ -47,13 +45,18 @@ impl From<PulseError> for SinkError {
}

pub struct PulseAudioSink {
s: Option<Simple>,
sink: Option<Simple>,
device: Option<String>,
app_name: String,
stream_desc: String,
format: AudioFormat,
}

impl Open for PulseAudioSink {
fn open(device: Option<String>, format: AudioFormat) -> Self {
let app_name = env::var("PULSE_PROP_application.name").unwrap_or_default();
let stream_desc = env::var("PULSE_PROP_stream.description").unwrap_or_default();

let mut actual_format = format;

if actual_format == AudioFormat::F64 {
Expand All @@ -64,16 +67,18 @@ impl Open for PulseAudioSink {
info!("Using PulseAudioSink with format: {:?}", actual_format);

Self {
s: None,
sink: None,
device,
app_name,
stream_desc,
format: actual_format,
}
}
}

impl Sink for PulseAudioSink {
fn start(&mut self) -> SinkResult<()> {
if self.s.is_none() {
if self.sink.is_none() {
// PulseAudio calls S24 and S24_3 different from the rest of the world
let pulse_format = match self.format {
AudioFormat::F32 => pulse::sample::Format::FLOAT32NE,
Expand All @@ -84,13 +89,13 @@ impl Sink for PulseAudioSink {
_ => unreachable!(),
};

let ss = pulse::sample::Spec {
let sample_spec = pulse::sample::Spec {
format: pulse_format,
channels: NUM_CHANNELS,
rate: SAMPLE_RATE,
};

if !ss.is_valid() {
if !sample_spec.is_valid() {
let pulse_error = PulseError::InvalidSampleSpec {
pulse_format,
format: self.format,
Expand All @@ -101,30 +106,28 @@ impl Sink for PulseAudioSink {
return Err(SinkError::from(pulse_error));
}

let s = Simple::new(
let sink = Simple::new(
None, // Use the default server.
APP_NAME, // Our application's name.
&self.app_name, // Our application's name.
Direction::Playback, // Direction.
self.device.as_deref(), // Our device (sink) name.
STREAM_NAME, // Description of our stream.
&ss, // Our sample format.
&self.stream_desc, // Description of our stream.
&sample_spec, // Our sample format.
None, // Use default channel map.
None, // Use default buffering attributes.
)
.map_err(PulseError::ConnectionRefused)?;

self.s = Some(s);
self.sink = Some(sink);
}

Ok(())
}

fn stop(&mut self) -> SinkResult<()> {
let s = self.s.as_mut().ok_or(PulseError::NotConnected)?;

s.drain().map_err(PulseError::DrainFailure)?;
let sink = self.sink.take().ok_or(PulseError::NotConnected)?;

self.s = None;
sink.drain().map_err(PulseError::DrainFailure)?;
Ok(())
}

Expand All @@ -133,9 +136,9 @@ impl Sink for PulseAudioSink {

impl SinkAsBytes for PulseAudioSink {
fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> {
let s = self.s.as_mut().ok_or(PulseError::NotConnected)?;
let sink = self.sink.as_mut().ok_or(PulseError::NotConnected)?;

s.write(data).map_err(PulseError::OnWrite)?;
sink.write(data).map_err(PulseError::OnWrite)?;

Ok(())
}
Expand Down
37 changes: 37 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,43 @@ fn get_setup() -> Setup {
exit(1);
}

#[cfg(feature = "pulseaudio-backend")]
{
if env::var("PULSE_PROP_application.name").is_err() {
let pulseaudio_name = if name != connect_default_config.name {
format!("{} - {}", connect_default_config.name, name)
} else {
name.clone()
};

env::set_var("PULSE_PROP_application.name", pulseaudio_name);
}

if env::var("PULSE_PROP_application.version").is_err() {
env::set_var("PULSE_PROP_application.version", version::SEMVER);
}

if env::var("PULSE_PROP_application.icon_name").is_err() {
env::set_var("PULSE_PROP_application.icon_name", "audio-x-generic");
}

if env::var("PULSE_PROP_application.process.binary").is_err() {
env::set_var("PULSE_PROP_application.process.binary", "librespot");
}

if env::var("PULSE_PROP_stream.description").is_err() {
env::set_var("PULSE_PROP_stream.description", "Spotify Connect endpoint");
}

if env::var("PULSE_PROP_media.software").is_err() {
env::set_var("PULSE_PROP_media.software", "Spotify");
}

if env::var("PULSE_PROP_media.role").is_err() {
env::set_var("PULSE_PROP_media.role", "music");
}
}

let initial_volume = opt_str(INITIAL_VOLUME)
.map(|initial_volume| {
let volume = match initial_volume.parse::<u16>() {
Expand Down

0 comments on commit dc9f822

Please sign in to comment.