Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: option for audio format #1082

Merged
merged 2 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/src/config/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ backend = "alsa" # use portaudio for macOS [homebrew]
# list of valid devices, run `aplay -L`,
device = "alsa_audio_device" # omit for macOS

# The PCM sample format to use. Possible values
# are F32, S32, S24, S24_3, S16.
# Change this value if you encounter errors like
# "Alsa error PCM open ALSA function 'snd_pcm_hw_params_set_format' failed with error 'EINVAL: Invalid argument'"
audio_format = "S16"

# The alsa control device. By default this is the same
# name as the `device` field.
control = "alsa_audio_device" # omit for macOS
Expand Down
71 changes: 69 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use gethostname::gethostname;
use librespot_core::{
cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig, version,
};
use librespot_playback::config::{Bitrate as LSBitrate, PlayerConfig};
use librespot_playback::config::{
AudioFormat as LSAudioFormat, Bitrate as LSBitrate, PlayerConfig,
};
use log::{error, info, warn};
use serde::{de::Error, de::Unexpected, Deserialize, Deserializer};
use sha1::{Digest, Sha1};
Expand Down Expand Up @@ -266,6 +268,57 @@ impl ToString for DBusType {
}
}

/// LibreSpot supported audio formats
static AUDIO_FORMAT_VALUES: &[&str] = &["F32", "S32", "S24", "S24_3", "S16"];

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, StructOpt)]
eladyn marked this conversation as resolved.
Show resolved Hide resolved
pub enum AudioFormat {
F32,
S32,
S24,
S24_3,
S16,
}

impl FromStr for AudioFormat {
type Err = ParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"F32" => Ok(AudioFormat::F32),
"S32" => Ok(AudioFormat::S32),
"S24" => Ok(AudioFormat::S24),
"S24_3" => Ok(AudioFormat::S24_3),
"S16" => Ok(AudioFormat::S16),
_ => unreachable!(),
}
}
}

impl ToString for AudioFormat {
fn to_string(&self) -> String {
match self {
AudioFormat::F32 => "F32".to_string(),
AudioFormat::S32 => "S32".to_string(),
AudioFormat::S24 => "S24".to_string(),
AudioFormat::S24_3 => "S24_3".to_string(),
AudioFormat::S16 => "S16".to_string(),
}
}
}

impl From<AudioFormat> for LSAudioFormat {
fn from(audio_format: AudioFormat) -> Self {
match audio_format {
AudioFormat::F32 => LSAudioFormat::F32,
AudioFormat::S32 => LSAudioFormat::S32,
AudioFormat::S24 => LSAudioFormat::S24,
AudioFormat::S24_3 => LSAudioFormat::S24_3,
AudioFormat::S16 => LSAudioFormat::S16,
}
}
}

#[derive(Debug, Default, StructOpt)]
#[structopt(
about = "A Spotify daemon",
Expand Down Expand Up @@ -404,6 +457,10 @@ pub struct SharedConfigValues {
#[structopt(long, short = "B", possible_values = &BITRATE_VALUES, value_name = "number")]
bitrate: Option<Bitrate>,

/// The audio format of the streamed audio data
#[structopt(long, possible_values = &AUDIO_FORMAT_VALUES, value_name = "string")]
audio_format: Option<AudioFormat>,

/// Initial volume between 0 and 100
#[structopt(long, value_name = "initial_volume")]
initial_volume: Option<String>,
Expand Down Expand Up @@ -511,6 +568,7 @@ impl fmt::Debug for SharedConfigValues {
.field("mixer", &self.mixer)
.field("device_name", &self.device_name)
.field("bitrate", &self.bitrate)
.field("audio_format", &self.audio_format)
.field("initial_volume", &self.initial_volume)
.field("volume_normalisation", &self.volume_normalisation)
.field("normalisation_pregain", &self.normalisation_pregain)
Expand Down Expand Up @@ -583,7 +641,8 @@ impl SharedConfigValues {
device_type,
use_mpris,
max_cache_size,
dbus_type
dbus_type,
audio_format
);

// Handles boolean merging.
Expand Down Expand Up @@ -622,6 +681,7 @@ pub(crate) struct SpotifydConfig {
pub(crate) cache: Option<Cache>,
pub(crate) backend: Option<String>,
pub(crate) audio_device: Option<String>,
pub(crate) audio_format: LSAudioFormat,
#[allow(unused)]
pub(crate) control_device: Option<String>,
#[allow(unused)]
Expand Down Expand Up @@ -667,6 +727,12 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
.unwrap_or(Bitrate::Bitrate160)
.into();

let audio_format: LSAudioFormat = config
.shared_config
.audio_format
.unwrap_or(AudioFormat::S16)
.into();

let backend = config
.shared_config
.backend
Expand Down Expand Up @@ -780,6 +846,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
cache,
backend: Some(backend),
audio_device: config.shared_config.device,
audio_format,
control_device: config.shared_config.control,
mixer: config.shared_config.mixer,
volume_controller,
Expand Down
6 changes: 3 additions & 3 deletions src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct AudioSetup {
pub mixer: Box<dyn FnMut() -> Box<dyn Mixer>>,
pub backend: fn(Option<String>, AudioFormat) -> Box<dyn Sink>,
pub audio_device: Option<String>,
pub audio_format: AudioFormat,
}

pub struct SpotifydState {
Expand Down Expand Up @@ -124,13 +125,12 @@ impl MainLoop {
let mixer = (self.audio_setup.mixer)();
let backend = self.audio_setup.backend;
let audio_device = self.audio_setup.audio_device.clone();
let audio_format = self.audio_setup.audio_format;
let (player, mut event_channel) = Player::new(
self.player_config.clone(),
session.clone(),
mixer.get_soft_volume(),
// TODO: dunno how to work with AudioFormat yet, maybe dig further if this
// doesn't work for all configurations
move || (backend)(audio_device, AudioFormat::default()),
move || (backend)(audio_device, audio_format),
);

let (spirc, spirc_task) = Spirc::new(
Expand Down
1 change: 1 addition & 0 deletions src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
mixer,
backend,
audio_device: config.audio_device,
audio_format: config.audio_format,
},
spotifyd_state: main_loop::SpotifydState {
cache,
Expand Down