Skip to content

Commit 949ca4f

Browse files
authored
Add and default to "auto" normalisation type (#844)
1 parent 7401d6a commit 949ca4f

6 files changed

Lines changed: 82 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- [playback] Add `--volume-range` option to set dB range and control `log` and `cubic` volume control curves
1313
- [playback] `alsamixer`: support for querying dB range from Alsa softvol
1414
- [playback] Add `--format F64` (supported by Alsa and GStreamer only)
15+
- [playback] Add `--normalisation-type auto` that switches between album and track automatically
1516

1617
### Changed
1718
- [audio, playback] Moved `VorbisDecoder`, `VorbisError`, `AudioPacket`, `PassthroughDecoder`, `PassthroughError`, `AudioError`, `AudioDecoder` and the `convert` module from `librespot-audio` to `librespot-playback`. The underlying crates `vorbis`, `librespot-tremor`, `lewton` and `ogg` should be used directly. (breaking)
@@ -26,7 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2627
- [playback] `alsamixer`: use `--device` name for `--mixer-card` unless specified otherwise
2728
- [playback] `player`: consider errors in `sink.start`, `sink.stop` and `sink.write` fatal and `exit(1)` (breaking)
2829
- [playback] `player`: make `convert` and `decoder` public so you can implement your own `Sink`
29-
- [playback] Updated default normalisation threshold to -2 dBFS
30+
- [playback] `player`: update default normalisation threshold to -2 dBFS
31+
- [playback] `player`: default normalisation type is now `auto`
3032

3133
### Deprecated
3234
- [connect] The `discovery` module was deprecated in favor of the `librespot-discovery` crate

audio/src/fetch/receive.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ impl AudioFileFetch {
266266
fn handle_file_data(&mut self, data: ReceivedData) -> ControlFlow {
267267
match data {
268268
ReceivedData::ResponseTime(response_time) => {
269-
trace!("Ping time estimated as: {}ms", response_time.as_millis());
269+
// chatty
270+
// trace!("Ping time estimated as: {}ms", response_time.as_millis());
270271

271272
// prune old response times. Keep at most two so we can push a third.
272273
while self.network_response_times.len() >= 3 {

connect/src/spirc.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,8 @@ impl SpircTask {
902902
self.context_fut = self.resolve_station(&context_uri);
903903
self.update_tracks_from_context();
904904
}
905-
if self.config.autoplay && new_index == tracks_len - 1 {
905+
let last_track = new_index == tracks_len - 1;
906+
if self.config.autoplay && last_track {
906907
// Extend the playlist
907908
// Note: This doesn't seem to reflect in the UI
908909
// the additional tracks in the frame don't show up as with station view
@@ -917,6 +918,11 @@ impl SpircTask {
917918
if tracks_len > 0 {
918919
self.state.set_playing_track_index(new_index);
919920
self.load_track(continue_playing, 0);
921+
if self.config.autoplay && last_track {
922+
// If we're now playing the last track of an album, then
923+
// switch to track normalisation mode for the autoplay to come.
924+
self.player.set_auto_normalise_as_album(false);
925+
}
920926
} else {
921927
info!("Not playing next track because there are no more tracks left in queue.");
922928
self.state.set_playing_track_index(0);
@@ -1084,6 +1090,9 @@ impl SpircTask {
10841090
self.autoplay_fut = self.resolve_autoplay_uri(&context_uri);
10851091
}
10861092

1093+
self.player
1094+
.set_auto_normalise_as_album(context_uri.starts_with("spotify:album:"));
1095+
10871096
self.state.set_playing_track_index(index);
10881097
self.state.set_track(tracks.iter().cloned().collect());
10891098
self.state.set_context_uri(context_uri);

playback/src/config.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ impl AudioFormat {
7676
}
7777
}
7878

79-
#[derive(Clone, Debug)]
79+
#[derive(Clone, Debug, PartialEq)]
8080
pub enum NormalisationType {
8181
Album,
8282
Track,
83+
Auto,
8384
}
8485

8586
impl FromStr for NormalisationType {
@@ -88,14 +89,15 @@ impl FromStr for NormalisationType {
8889
match s.to_lowercase().as_ref() {
8990
"album" => Ok(Self::Album),
9091
"track" => Ok(Self::Track),
92+
"auto" => Ok(Self::Auto),
9193
_ => Err(()),
9294
}
9395
}
9496
}
9597

9698
impl Default for NormalisationType {
9799
fn default() -> Self {
98-
Self::Album
100+
Self::Auto
99101
}
100102
}
101103

playback/src/player.rs

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ struct PlayerInternal {
6767
limiter_peak_sample: f64,
6868
limiter_factor: f64,
6969
limiter_strength: f64,
70+
71+
auto_normalise_as_album: bool,
7072
}
7173

7274
enum PlayerCommand {
@@ -86,6 +88,7 @@ enum PlayerCommand {
8688
AddEventSender(mpsc::UnboundedSender<PlayerEvent>),
8789
SetSinkEventCallback(Option<SinkEventCallback>),
8890
EmitVolumeSetEvent(u16),
91+
SetAutoNormaliseAsAlbum(bool),
8992
}
9093

9194
#[derive(Debug, Clone)]
@@ -238,9 +241,10 @@ impl NormalisationData {
238241
return 1.0;
239242
}
240243

241-
let [gain_db, gain_peak] = match config.normalisation_type {
242-
NormalisationType::Album => [data.album_gain_db, data.album_peak],
243-
NormalisationType::Track => [data.track_gain_db, data.track_peak],
244+
let [gain_db, gain_peak] = if config.normalisation_type == NormalisationType::Album {
245+
[data.album_gain_db, data.album_peak]
246+
} else {
247+
[data.track_gain_db, data.track_peak]
244248
};
245249

246250
let normalisation_power = gain_db as f64 + config.normalisation_pregain;
@@ -264,7 +268,11 @@ impl NormalisationData {
264268
}
265269

266270
debug!("Normalisation Data: {:?}", data);
267-
debug!("Normalisation Factor: {:.2}%", normalisation_factor * 100.0);
271+
debug!(
272+
"Calculated Normalisation Factor for {:?}: {:.2}%",
273+
config.normalisation_type,
274+
normalisation_factor * 100.0
275+
);
268276

269277
normalisation_factor as f64
270278
}
@@ -327,6 +335,8 @@ impl Player {
327335
limiter_peak_sample: 0.0,
328336
limiter_factor: 1.0,
329337
limiter_strength: 0.0,
338+
339+
auto_normalise_as_album: false,
330340
};
331341

332342
// While PlayerInternal is written as a future, it still contains blocking code.
@@ -406,6 +416,10 @@ impl Player {
406416
pub fn emit_volume_set_event(&self, volume: u16) {
407417
self.command(PlayerCommand::EmitVolumeSetEvent(volume));
408418
}
419+
420+
pub fn set_auto_normalise_as_album(&self, setting: bool) {
421+
self.command(PlayerCommand::SetAutoNormaliseAsAlbum(setting));
422+
}
409423
}
410424

411425
impl Drop for Player {
@@ -423,7 +437,7 @@ impl Drop for Player {
423437

424438
struct PlayerLoadedTrackData {
425439
decoder: Decoder,
426-
normalisation_factor: f64,
440+
normalisation_data: NormalisationData,
427441
stream_loader_controller: StreamLoaderController,
428442
bytes_per_second: usize,
429443
duration_ms: u32,
@@ -456,6 +470,7 @@ enum PlayerState {
456470
track_id: SpotifyId,
457471
play_request_id: u64,
458472
decoder: Decoder,
473+
normalisation_data: NormalisationData,
459474
normalisation_factor: f64,
460475
stream_loader_controller: StreamLoaderController,
461476
bytes_per_second: usize,
@@ -467,6 +482,7 @@ enum PlayerState {
467482
track_id: SpotifyId,
468483
play_request_id: u64,
469484
decoder: Decoder,
485+
normalisation_data: NormalisationData,
470486
normalisation_factor: f64,
471487
stream_loader_controller: StreamLoaderController,
472488
bytes_per_second: usize,
@@ -543,7 +559,7 @@ impl PlayerState {
543559
decoder,
544560
duration_ms,
545561
bytes_per_second,
546-
normalisation_factor,
562+
normalisation_data,
547563
stream_loader_controller,
548564
stream_position_pcm,
549565
..
@@ -553,7 +569,7 @@ impl PlayerState {
553569
play_request_id,
554570
loaded_track: PlayerLoadedTrackData {
555571
decoder,
556-
normalisation_factor,
572+
normalisation_data,
557573
stream_loader_controller,
558574
bytes_per_second,
559575
duration_ms,
@@ -572,6 +588,7 @@ impl PlayerState {
572588
track_id,
573589
play_request_id,
574590
decoder,
591+
normalisation_data,
575592
normalisation_factor,
576593
stream_loader_controller,
577594
duration_ms,
@@ -583,6 +600,7 @@ impl PlayerState {
583600
track_id,
584601
play_request_id,
585602
decoder,
603+
normalisation_data,
586604
normalisation_factor,
587605
stream_loader_controller,
588606
duration_ms,
@@ -603,6 +621,7 @@ impl PlayerState {
603621
track_id,
604622
play_request_id,
605623
decoder,
624+
normalisation_data,
606625
normalisation_factor,
607626
stream_loader_controller,
608627
duration_ms,
@@ -615,6 +634,7 @@ impl PlayerState {
615634
track_id,
616635
play_request_id,
617636
decoder,
637+
normalisation_data,
618638
normalisation_factor,
619639
stream_loader_controller,
620640
duration_ms,
@@ -775,14 +795,16 @@ impl PlayerTrackLoader {
775795

776796
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
777797

778-
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file)
779-
{
780-
Ok(normalisation_data) => {
781-
NormalisationData::get_factor(&self.config, normalisation_data)
782-
}
798+
let normalisation_data = match NormalisationData::parse_from_file(&mut decrypted_file) {
799+
Ok(data) => data,
783800
Err(_) => {
784801
warn!("Unable to extract normalisation data, using default value.");
785-
1.0
802+
NormalisationData {
803+
track_gain_db: 0.0,
804+
track_peak: 1.0,
805+
album_gain_db: 0.0,
806+
album_peak: 1.0,
807+
}
786808
}
787809
};
788810

@@ -838,7 +860,7 @@ impl PlayerTrackLoader {
838860

839861
return Some(PlayerLoadedTrackData {
840862
decoder,
841-
normalisation_factor,
863+
normalisation_data,
842864
stream_loader_controller,
843865
bytes_per_second,
844866
duration_ms,
@@ -1339,6 +1361,17 @@ impl PlayerInternal {
13391361
) {
13401362
let position_ms = Self::position_pcm_to_ms(loaded_track.stream_position_pcm);
13411363

1364+
let mut config = self.config.clone();
1365+
if config.normalisation_type == NormalisationType::Auto {
1366+
if self.auto_normalise_as_album {
1367+
config.normalisation_type = NormalisationType::Album;
1368+
} else {
1369+
config.normalisation_type = NormalisationType::Track;
1370+
}
1371+
};
1372+
let normalisation_factor =
1373+
NormalisationData::get_factor(&config, loaded_track.normalisation_data);
1374+
13421375
if start_playback {
13431376
self.ensure_sink_running();
13441377

@@ -1353,7 +1386,8 @@ impl PlayerInternal {
13531386
track_id,
13541387
play_request_id,
13551388
decoder: loaded_track.decoder,
1356-
normalisation_factor: loaded_track.normalisation_factor,
1389+
normalisation_data: loaded_track.normalisation_data,
1390+
normalisation_factor,
13571391
stream_loader_controller: loaded_track.stream_loader_controller,
13581392
duration_ms: loaded_track.duration_ms,
13591393
bytes_per_second: loaded_track.bytes_per_second,
@@ -1370,7 +1404,8 @@ impl PlayerInternal {
13701404
track_id,
13711405
play_request_id,
13721406
decoder: loaded_track.decoder,
1373-
normalisation_factor: loaded_track.normalisation_factor,
1407+
normalisation_data: loaded_track.normalisation_data,
1408+
normalisation_factor,
13741409
stream_loader_controller: loaded_track.stream_loader_controller,
13751410
duration_ms: loaded_track.duration_ms,
13761411
bytes_per_second: loaded_track.bytes_per_second,
@@ -1497,7 +1532,7 @@ impl PlayerInternal {
14971532
stream_loader_controller,
14981533
bytes_per_second,
14991534
duration_ms,
1500-
normalisation_factor,
1535+
normalisation_data,
15011536
..
15021537
}
15031538
| PlayerState::Paused {
@@ -1506,13 +1541,13 @@ impl PlayerInternal {
15061541
stream_loader_controller,
15071542
bytes_per_second,
15081543
duration_ms,
1509-
normalisation_factor,
1544+
normalisation_data,
15101545
..
15111546
} = old_state
15121547
{
15131548
let loaded_track = PlayerLoadedTrackData {
15141549
decoder,
1515-
normalisation_factor,
1550+
normalisation_data,
15161551
stream_loader_controller,
15171552
bytes_per_second,
15181553
duration_ms,
@@ -1750,6 +1785,10 @@ impl PlayerInternal {
17501785
PlayerCommand::EmitVolumeSetEvent(volume) => {
17511786
self.send_event(PlayerEvent::VolumeSet { volume })
17521787
}
1788+
1789+
PlayerCommand::SetAutoNormaliseAsAlbum(setting) => {
1790+
self.auto_normalise_as_album = setting
1791+
}
17531792
}
17541793
}
17551794

@@ -1855,6 +1894,10 @@ impl ::std::fmt::Debug for PlayerCommand {
18551894
PlayerCommand::EmitVolumeSetEvent(volume) => {
18561895
f.debug_tuple("VolumeSet").field(&volume).finish()
18571896
}
1897+
PlayerCommand::SetAutoNormaliseAsAlbum(setting) => f
1898+
.debug_tuple("SetAutoNormaliseAsAlbum")
1899+
.field(&setting)
1900+
.finish(),
18581901
}
18591902
}
18601903
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ fn get_setup(args: &[String]) -> Setup {
359359
.optopt(
360360
"",
361361
NORMALISATION_GAIN_TYPE,
362-
"Specify the normalisation gain type to use {track|album}. Defaults to album.",
362+
"Specify the normalisation gain type to use {track|album|auto}. Defaults to auto.",
363363
"TYPE",
364364
)
365365
.optopt(

0 commit comments

Comments
 (0)