diff --git a/playback/src/config.rs b/playback/src/config.rs index 9d65042c..0a9bb47d 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -25,10 +25,34 @@ impl Default for Bitrate { } } +#[derive(Clone, Debug)] +pub enum NormalisationType { + Album, + Track, +} + +impl FromStr for NormalisationType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "album" => Ok(NormalisationType::Album), + "track" => Ok(NormalisationType::Track), + _ => Err(()), + } + } +} + +impl Default for NormalisationType { + fn default() -> NormalisationType { + NormalisationType::Album + } +} + #[derive(Clone, Debug)] pub struct PlayerConfig { pub bitrate: Bitrate, pub normalisation: bool, + pub normalisation_type: NormalisationType, pub normalisation_pregain: f32, pub gapless: bool, } @@ -38,6 +62,7 @@ impl Default for PlayerConfig { PlayerConfig { bitrate: Bitrate::default(), normalisation: false, + normalisation_type: NormalisationType::default(), normalisation_pregain: 0.0, gapless: true, } diff --git a/playback/src/player.rs b/playback/src/player.rs index 125184a0..d8350bb6 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -9,7 +9,7 @@ use std::mem; use std::thread; use std::time::{Duration, Instant}; -use crate::config::{Bitrate, PlayerConfig}; +use crate::config::{Bitrate, NormalisationType, PlayerConfig}; use librespot_core::session::Session; use librespot_core::spotify_id::SpotifyId; @@ -214,17 +214,20 @@ impl NormalisationData { } fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 { - let mut normalisation_factor = f32::powf( - 10.0, - (data.track_gain_db + config.normalisation_pregain) / 20.0, - ); + let [gain_db, gain_peak] = match config.normalisation_type { + NormalisationType::Album => [data.album_gain_db, data.album_peak], + NormalisationType::Track => [data.track_gain_db, data.track_peak], + }; + let mut normalisation_factor = + f32::powf(10.0, (gain_db + config.normalisation_pregain) / 20.0); - if normalisation_factor * data.track_peak > 1.0 { + if normalisation_factor * gain_peak > 1.0 { warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); - normalisation_factor = 1.0 / data.track_peak; + normalisation_factor = 1.0 / gain_peak; } debug!("Normalisation Data: {:?}", data); + debug!("Normalisation Type: {:?}", config.normalisation_type); debug!("Applied normalisation factor: {}", normalisation_factor); normalisation_factor diff --git a/src/main.rs b/src/main.rs index f900f681..8444803e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ use librespot::core::version; use librespot::connect::discovery::{discovery, DiscoveryStream}; use librespot::connect::spirc::{Spirc, SpircTask}; use librespot::playback::audio_backend::{self, Sink, BACKENDS}; -use librespot::playback::config::{Bitrate, PlayerConfig}; +use librespot::playback::config::{Bitrate, NormalisationType, PlayerConfig}; use librespot::playback::mixer::{self, Mixer, MixerConfig}; use librespot::playback::player::{Player, PlayerEvent}; @@ -177,6 +177,12 @@ fn setup(args: &[String]) -> Setup { "enable-volume-normalisation", "Play all tracks at the same volume", ) + .optopt( + "", + "normalisation-gain-type", + "Specify the normalisation gain type to use - [track, album]. Default is album.", + "GAIN_TYPE", + ) .optopt( "", "normalisation-pregain", @@ -354,10 +360,18 @@ fn setup(args: &[String]) -> Setup { .as_ref() .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) .unwrap_or(Bitrate::default()); + let gain_type = matches + .opt_str("normalisation-gain-type") + .as_ref() + .map(|gain_type| { + NormalisationType::from_str(gain_type).expect("Invalid normalisation type") + }) + .unwrap_or(NormalisationType::default()); PlayerConfig { bitrate: bitrate, gapless: !matches.opt_present("disable-gapless"), normalisation: matches.opt_present("enable-volume-normalisation"), + normalisation_type: gain_type, normalisation_pregain: matches .opt_str("normalisation-pregain") .map(|pregain| pregain.parse::().expect("Invalid pregain float value"))