Merge pull request #573 from librespot-org/album-normalisation

Add option to choose between track or album normalisation gain
This commit is contained in:
Sasha Hilton 2021-02-05 04:19:09 +00:00 committed by GitHub
commit 24486c8c83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 8 deletions

View file

@ -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<Self, Self::Err> {
match s {
"album" => Ok(NormalisationType::Album),
"track" => Ok(NormalisationType::Track),
_ => Err(()),
}
}
}
impl Default for NormalisationType {
fn default() -> NormalisationType {
NormalisationType::Album
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PlayerConfig { pub struct PlayerConfig {
pub bitrate: Bitrate, pub bitrate: Bitrate,
pub normalisation: bool, pub normalisation: bool,
pub normalisation_type: NormalisationType,
pub normalisation_pregain: f32, pub normalisation_pregain: f32,
pub gapless: bool, pub gapless: bool,
} }
@ -38,6 +62,7 @@ impl Default for PlayerConfig {
PlayerConfig { PlayerConfig {
bitrate: Bitrate::default(), bitrate: Bitrate::default(),
normalisation: false, normalisation: false,
normalisation_type: NormalisationType::default(),
normalisation_pregain: 0.0, normalisation_pregain: 0.0,
gapless: true, gapless: true,
} }

View file

@ -9,7 +9,7 @@ use std::mem;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::config::{Bitrate, PlayerConfig}; use crate::config::{Bitrate, NormalisationType, PlayerConfig};
use librespot_core::session::Session; use librespot_core::session::Session;
use librespot_core::spotify_id::SpotifyId; use librespot_core::spotify_id::SpotifyId;
@ -214,17 +214,20 @@ impl NormalisationData {
} }
fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 { fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 {
let mut normalisation_factor = f32::powf( let [gain_db, gain_peak] = match config.normalisation_type {
10.0, NormalisationType::Album => [data.album_gain_db, data.album_peak],
(data.track_gain_db + config.normalisation_pregain) / 20.0, 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."); 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 Data: {:?}", data);
debug!("Normalisation Type: {:?}", config.normalisation_type);
debug!("Applied normalisation factor: {}", normalisation_factor); debug!("Applied normalisation factor: {}", normalisation_factor);
normalisation_factor normalisation_factor

View file

@ -22,7 +22,7 @@ use librespot::core::version;
use librespot::connect::discovery::{discovery, DiscoveryStream}; use librespot::connect::discovery::{discovery, DiscoveryStream};
use librespot::connect::spirc::{Spirc, SpircTask}; use librespot::connect::spirc::{Spirc, SpircTask};
use librespot::playback::audio_backend::{self, Sink, BACKENDS}; 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::mixer::{self, Mixer, MixerConfig};
use librespot::playback::player::{Player, PlayerEvent}; use librespot::playback::player::{Player, PlayerEvent};
@ -177,6 +177,12 @@ fn setup(args: &[String]) -> Setup {
"enable-volume-normalisation", "enable-volume-normalisation",
"Play all tracks at the same volume", "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( .optopt(
"", "",
"normalisation-pregain", "normalisation-pregain",
@ -354,10 +360,18 @@ fn setup(args: &[String]) -> Setup {
.as_ref() .as_ref()
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
.unwrap_or(Bitrate::default()); .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 { PlayerConfig {
bitrate: bitrate, bitrate: bitrate,
gapless: !matches.opt_present("disable-gapless"), gapless: !matches.opt_present("disable-gapless"),
normalisation: matches.opt_present("enable-volume-normalisation"), normalisation: matches.opt_present("enable-volume-normalisation"),
normalisation_type: gain_type,
normalisation_pregain: matches normalisation_pregain: matches
.opt_str("normalisation-pregain") .opt_str("normalisation-pregain")
.map(|pregain| pregain.parse::<f32>().expect("Invalid pregain float value")) .map(|pregain| pregain.parse::<f32>().expect("Invalid pregain float value"))