Skip to content

Commit 650d41b

Browse files
authored
Merge pull request #976 from JasonLG1979/pulseaudio-name
Set the PulseAudio name to match librespot's device name
2 parents 616809b + e0e23c9 commit 650d41b

3 files changed

Lines changed: 62 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- [main] Add a `-q`, `--quiet` option that changes the logging level to warn.
2424
- [main] Add a short name for every flag and option.
2525
- [main] Add the ability to parse environment variables.
26+
- [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)
27+
- [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)
28+
- [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)
2629

2730
### Fixed
2831
- [main] Prevent hang when discovery is disabled and there are no credentials or when bad credentials are given.

playback/src/audio_backend/pulseaudio.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ use crate::decoder::AudioPacket;
55
use crate::{NUM_CHANNELS, SAMPLE_RATE};
66
use libpulse_binding::{self as pulse, error::PAErr, stream::Direction};
77
use libpulse_simple_binding::Simple;
8+
use std::env;
89
use thiserror::Error;
910

10-
const APP_NAME: &str = "librespot";
11-
const STREAM_NAME: &str = "Spotify endpoint";
12-
1311
#[derive(Debug, Error)]
1412
enum PulseError {
1513
#[error("<PulseAudioSink> Unsupported Pulseaudio Sample Spec, Format {pulse_format:?} ({format:?}), Channels {channels}, Rate {rate}")]
@@ -47,13 +45,18 @@ impl From<PulseError> for SinkError {
4745
}
4846

4947
pub struct PulseAudioSink {
50-
s: Option<Simple>,
48+
sink: Option<Simple>,
5149
device: Option<String>,
50+
app_name: String,
51+
stream_desc: String,
5252
format: AudioFormat,
5353
}
5454

5555
impl Open for PulseAudioSink {
5656
fn open(device: Option<String>, format: AudioFormat) -> Self {
57+
let app_name = env::var("PULSE_PROP_application.name").unwrap_or_default();
58+
let stream_desc = env::var("PULSE_PROP_stream.description").unwrap_or_default();
59+
5760
let mut actual_format = format;
5861

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

6669
Self {
67-
s: None,
70+
sink: None,
6871
device,
72+
app_name,
73+
stream_desc,
6974
format: actual_format,
7075
}
7176
}
7277
}
7378

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

87-
let ss = pulse::sample::Spec {
92+
let sample_spec = pulse::sample::Spec {
8893
format: pulse_format,
8994
channels: NUM_CHANNELS,
9095
rate: SAMPLE_RATE,
9196
};
9297

93-
if !ss.is_valid() {
98+
if !sample_spec.is_valid() {
9499
let pulse_error = PulseError::InvalidSampleSpec {
95100
pulse_format,
96101
format: self.format,
@@ -101,30 +106,28 @@ impl Sink for PulseAudioSink {
101106
return Err(SinkError::from(pulse_error));
102107
}
103108

104-
let s = Simple::new(
109+
let sink = Simple::new(
105110
None, // Use the default server.
106-
APP_NAME, // Our application's name.
111+
&self.app_name, // Our application's name.
107112
Direction::Playback, // Direction.
108113
self.device.as_deref(), // Our device (sink) name.
109-
STREAM_NAME, // Description of our stream.
110-
&ss, // Our sample format.
114+
&self.stream_desc, // Description of our stream.
115+
&sample_spec, // Our sample format.
111116
None, // Use default channel map.
112117
None, // Use default buffering attributes.
113118
)
114119
.map_err(PulseError::ConnectionRefused)?;
115120

116-
self.s = Some(s);
121+
self.sink = Some(sink);
117122
}
118123

119124
Ok(())
120125
}
121126

122127
fn stop(&mut self) -> SinkResult<()> {
123-
let s = self.s.as_mut().ok_or(PulseError::NotConnected)?;
124-
125-
s.drain().map_err(PulseError::DrainFailure)?;
128+
let sink = self.sink.take().ok_or(PulseError::NotConnected)?;
126129

127-
self.s = None;
130+
sink.drain().map_err(PulseError::DrainFailure)?;
128131
Ok(())
129132
}
130133

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

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

138-
s.write(data).map_err(PulseError::OnWrite)?;
141+
sink.write(data).map_err(PulseError::OnWrite)?;
139142

140143
Ok(())
141144
}

src/main.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,43 @@ fn get_setup() -> Setup {
11241124
exit(1);
11251125
}
11261126

1127+
#[cfg(feature = "pulseaudio-backend")]
1128+
{
1129+
if env::var("PULSE_PROP_application.name").is_err() {
1130+
let pulseaudio_name = if name != connect_default_config.name {
1131+
format!("{} - {}", connect_default_config.name, name)
1132+
} else {
1133+
name.clone()
1134+
};
1135+
1136+
env::set_var("PULSE_PROP_application.name", pulseaudio_name);
1137+
}
1138+
1139+
if env::var("PULSE_PROP_application.version").is_err() {
1140+
env::set_var("PULSE_PROP_application.version", version::SEMVER);
1141+
}
1142+
1143+
if env::var("PULSE_PROP_application.icon_name").is_err() {
1144+
env::set_var("PULSE_PROP_application.icon_name", "audio-x-generic");
1145+
}
1146+
1147+
if env::var("PULSE_PROP_application.process.binary").is_err() {
1148+
env::set_var("PULSE_PROP_application.process.binary", "librespot");
1149+
}
1150+
1151+
if env::var("PULSE_PROP_stream.description").is_err() {
1152+
env::set_var("PULSE_PROP_stream.description", "Spotify Connect endpoint");
1153+
}
1154+
1155+
if env::var("PULSE_PROP_media.software").is_err() {
1156+
env::set_var("PULSE_PROP_media.software", "Spotify");
1157+
}
1158+
1159+
if env::var("PULSE_PROP_media.role").is_err() {
1160+
env::set_var("PULSE_PROP_media.role", "music");
1161+
}
1162+
}
1163+
11271164
let initial_volume = opt_str(INITIAL_VOLUME)
11281165
.map(|initial_volume| {
11291166
let volume = match initial_volume.parse::<u16>() {

0 commit comments

Comments
 (0)