Skip to content

Commit 43975a5

Browse files
Mathias MilletMathias Millet
Mathias Millet
authored and
Mathias Millet
committed
feat: option for audio format
1 parent ae6dac7 commit 43975a5

File tree

4 files changed

+78
-3
lines changed

4 files changed

+78
-3
lines changed

docs/src/config/File.md

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ backend = "alsa" # use portaudio for macOS [homebrew]
4242
# list of valid devices, run `aplay -L`,
4343
device = "alsa_audio_device" # omit for macOS
4444

45+
# The PCM sample format to use. Possible values
46+
# are F32, S32, S24, S24_3, S16.
47+
# Change this value if you encounter errors like
48+
# "Alsa error PCM open ALSA function 'snd_pcm_hw_params_set_format' failed with error 'EINVAL: Invalid argument'"
49+
audio_format = "S16"
50+
4551
# The alsa control device. By default this is the same
4652
# name as the `device` field.
4753
control = "alsa_audio_device" # omit for macOS

src/config.rs

+68-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use gethostname::gethostname;
88
use librespot_core::{
99
cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig, version,
1010
};
11-
use librespot_playback::config::{Bitrate as LSBitrate, PlayerConfig};
11+
use librespot_playback::config::{
12+
AudioFormat as LSAudioFormat, Bitrate as LSBitrate, PlayerConfig,
13+
};
1214
use log::{error, info, warn};
1315
use reqwest::Url;
1416
use serde::{de::Error, de::Unexpected, Deserialize, Deserializer};
@@ -221,6 +223,56 @@ impl From<Bitrate> for LSBitrate {
221223
}
222224
}
223225

226+
/// Libspotify audio formats
227+
static AUDIO_FORMAT_VALUES: &[&str] = &["F32", "S32", "S24", "S24_3", "S16"];
228+
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, StructOpt)]
229+
pub enum AudioFormat {
230+
F32,
231+
S32,
232+
S24,
233+
S24_3,
234+
S16,
235+
}
236+
237+
impl FromStr for AudioFormat {
238+
type Err = ParseError;
239+
240+
fn from_str(s: &str) -> Result<Self, Self::Err> {
241+
match s {
242+
"F32" => Ok(AudioFormat::F32),
243+
"S32" => Ok(AudioFormat::S32),
244+
"S24" => Ok(AudioFormat::S24),
245+
"S24_3" => Ok(AudioFormat::S24_3),
246+
"S16" => Ok(AudioFormat::S16),
247+
_ => unreachable!(),
248+
}
249+
}
250+
}
251+
252+
impl ToString for AudioFormat {
253+
fn to_string(&self) -> String {
254+
match self {
255+
AudioFormat::F32 => "F32".to_string(),
256+
AudioFormat::S32 => "S32".to_string(),
257+
AudioFormat::S24 => "S24".to_string(),
258+
AudioFormat::S24_3 => "S24_3".to_string(),
259+
AudioFormat::S16 => "S16".to_string(),
260+
}
261+
}
262+
}
263+
264+
impl From<AudioFormat> for LSAudioFormat {
265+
fn from(audio_format: AudioFormat) -> Self {
266+
match audio_format {
267+
AudioFormat::F32 => LSAudioFormat::F32,
268+
AudioFormat::S32 => LSAudioFormat::S32,
269+
AudioFormat::S24 => LSAudioFormat::S24,
270+
AudioFormat::S24_3 => LSAudioFormat::S24_3,
271+
AudioFormat::S16 => LSAudioFormat::S16,
272+
}
273+
}
274+
}
275+
224276
#[derive(Debug, Default, StructOpt)]
225277
#[structopt(
226278
about = "A Spotify daemon",
@@ -346,6 +398,10 @@ pub struct SharedConfigValues {
346398
#[structopt(long, short = "B", possible_values = &BITRATE_VALUES, value_name = "number")]
347399
bitrate: Option<Bitrate>,
348400

401+
/// The audio format of the streamed audio data
402+
#[structopt(long, possible_values = &AUDIO_FORMAT_VALUES, value_name = "string")]
403+
audio_format: Option<AudioFormat>,
404+
349405
/// Initial volume between 0 and 100
350406
#[structopt(long, value_name = "initial_volume")]
351407
initial_volume: Option<String>,
@@ -452,6 +508,7 @@ impl fmt::Debug for SharedConfigValues {
452508
.field("mixer", &self.mixer)
453509
.field("device_name", &self.device_name)
454510
.field("bitrate", &self.bitrate)
511+
.field("audio_format", &self.audio_format)
455512
.field("initial_volume", &self.initial_volume)
456513
.field("volume_normalisation", &self.volume_normalisation)
457514
.field("normalisation_pregain", &self.normalisation_pregain)
@@ -521,7 +578,8 @@ impl SharedConfigValues {
521578
zeroconf_port,
522579
proxy,
523580
device_type,
524-
use_mpris
581+
use_mpris,
582+
audio_format
525583
);
526584

527585
// Handles boolean merging.
@@ -559,6 +617,7 @@ pub(crate) struct SpotifydConfig {
559617
pub(crate) cache: Option<Cache>,
560618
pub(crate) backend: Option<String>,
561619
pub(crate) audio_device: Option<String>,
620+
pub(crate) audio_format: LSAudioFormat,
562621
#[allow(unused)]
563622
pub(crate) control_device: Option<String>,
564623
#[allow(unused)]
@@ -601,6 +660,12 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
601660
.unwrap_or(Bitrate::Bitrate160)
602661
.into();
603662

663+
let audio_format: LSAudioFormat = config
664+
.shared_config
665+
.audio_format
666+
.unwrap_or(AudioFormat::S16)
667+
.into();
668+
604669
let backend = config
605670
.shared_config
606671
.backend
@@ -715,6 +780,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
715780
cache,
716781
backend: Some(backend),
717782
audio_device: config.shared_config.device,
783+
audio_format,
718784
control_device: config.shared_config.control,
719785
mixer: config.shared_config.mixer,
720786
volume_controller,

src/main_loop.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub struct AudioSetup {
5050
pub mixer: Box<dyn FnMut() -> Box<dyn Mixer>>,
5151
pub backend: fn(Option<String>, AudioFormat) -> Box<dyn Sink>,
5252
pub audio_device: Option<String>,
53+
pub audio_format: AudioFormat,
5354
}
5455

5556
pub struct SpotifydState {
@@ -158,13 +159,14 @@ impl Future for MainLoopState {
158159
self.librespot_connection.connection = Box::pin(futures::future::pending());
159160
let backend = self.audio_setup.backend;
160161
let audio_device = self.audio_setup.audio_device.clone();
162+
let audio_format = self.audio_setup.audio_format;
161163
let (player, event_channel) = Player::new(
162164
self.player_config.clone(),
163165
session.clone(),
164166
audio_filter,
165167
// TODO: dunno how to work with AudioFormat yet, maybe dig further if this
166168
// doesn't work for all configurations
167-
move || (backend)(audio_device, AudioFormat::default()),
169+
move || (backend)(audio_device, audio_format),
168170
);
169171

170172
self.spotifyd_state.player_event_channel =

src/setup.rs

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
129129
mixer,
130130
backend,
131131
audio_device: config.audio_device.clone(),
132+
audio_format: config.audio_format,
132133
},
133134
spotifyd_state: main_loop::SpotifydState {
134135
ctrl_c_stream: Box::pin(ctrl_c()),

0 commit comments

Comments
 (0)