Add and default to "auto" normalisation type (#844)

This commit is contained in:
Roderick van Domburg 2021-09-20 19:22:02 +02:00 committed by GitHub
parent 7401d6a96e
commit 949ca4fded
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 25 deletions

View file

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [playback] Add `--volume-range` option to set dB range and control `log` and `cubic` volume control curves
- [playback] `alsamixer`: support for querying dB range from Alsa softvol
- [playback] Add `--format F64` (supported by Alsa and GStreamer only)
- [playback] Add `--normalisation-type auto` that switches between album and track automatically
### Changed
- [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
- [playback] `alsamixer`: use `--device` name for `--mixer-card` unless specified otherwise
- [playback] `player`: consider errors in `sink.start`, `sink.stop` and `sink.write` fatal and `exit(1)` (breaking)
- [playback] `player`: make `convert` and `decoder` public so you can implement your own `Sink`
- [playback] Updated default normalisation threshold to -2 dBFS
- [playback] `player`: update default normalisation threshold to -2 dBFS
- [playback] `player`: default normalisation type is now `auto`
### Deprecated
- [connect] The `discovery` module was deprecated in favor of the `librespot-discovery` crate

View file

@ -266,7 +266,8 @@ impl AudioFileFetch {
fn handle_file_data(&mut self, data: ReceivedData) -> ControlFlow {
match data {
ReceivedData::ResponseTime(response_time) => {
trace!("Ping time estimated as: {}ms", response_time.as_millis());
// chatty
// trace!("Ping time estimated as: {}ms", response_time.as_millis());
// prune old response times. Keep at most two so we can push a third.
while self.network_response_times.len() >= 3 {

View file

@ -902,7 +902,8 @@ impl SpircTask {
self.context_fut = self.resolve_station(&context_uri);
self.update_tracks_from_context();
}
if self.config.autoplay && new_index == tracks_len - 1 {
let last_track = new_index == tracks_len - 1;
if self.config.autoplay && last_track {
// Extend the playlist
// Note: This doesn't seem to reflect in the UI
// the additional tracks in the frame don't show up as with station view
@ -917,6 +918,11 @@ impl SpircTask {
if tracks_len > 0 {
self.state.set_playing_track_index(new_index);
self.load_track(continue_playing, 0);
if self.config.autoplay && last_track {
// If we're now playing the last track of an album, then
// switch to track normalisation mode for the autoplay to come.
self.player.set_auto_normalise_as_album(false);
}
} else {
info!("Not playing next track because there are no more tracks left in queue.");
self.state.set_playing_track_index(0);
@ -1084,6 +1090,9 @@ impl SpircTask {
self.autoplay_fut = self.resolve_autoplay_uri(&context_uri);
}
self.player
.set_auto_normalise_as_album(context_uri.starts_with("spotify:album:"));
self.state.set_playing_track_index(index);
self.state.set_track(tracks.iter().cloned().collect());
self.state.set_context_uri(context_uri);

View file

@ -76,10 +76,11 @@ impl AudioFormat {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum NormalisationType {
Album,
Track,
Auto,
}
impl FromStr for NormalisationType {
@ -88,6 +89,7 @@ impl FromStr for NormalisationType {
match s.to_lowercase().as_ref() {
"album" => Ok(Self::Album),
"track" => Ok(Self::Track),
"auto" => Ok(Self::Auto),
_ => Err(()),
}
}
@ -95,7 +97,7 @@ impl FromStr for NormalisationType {
impl Default for NormalisationType {
fn default() -> Self {
Self::Album
Self::Auto
}
}

View file

@ -67,6 +67,8 @@ struct PlayerInternal {
limiter_peak_sample: f64,
limiter_factor: f64,
limiter_strength: f64,
auto_normalise_as_album: bool,
}
enum PlayerCommand {
@ -86,6 +88,7 @@ enum PlayerCommand {
AddEventSender(mpsc::UnboundedSender<PlayerEvent>),
SetSinkEventCallback(Option<SinkEventCallback>),
EmitVolumeSetEvent(u16),
SetAutoNormaliseAsAlbum(bool),
}
#[derive(Debug, Clone)]
@ -238,9 +241,10 @@ impl NormalisationData {
return 1.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 [gain_db, gain_peak] = if config.normalisation_type == NormalisationType::Album {
[data.album_gain_db, data.album_peak]
} else {
[data.track_gain_db, data.track_peak]
};
let normalisation_power = gain_db as f64 + config.normalisation_pregain;
@ -264,7 +268,11 @@ impl NormalisationData {
}
debug!("Normalisation Data: {:?}", data);
debug!("Normalisation Factor: {:.2}%", normalisation_factor * 100.0);
debug!(
"Calculated Normalisation Factor for {:?}: {:.2}%",
config.normalisation_type,
normalisation_factor * 100.0
);
normalisation_factor as f64
}
@ -327,6 +335,8 @@ impl Player {
limiter_peak_sample: 0.0,
limiter_factor: 1.0,
limiter_strength: 0.0,
auto_normalise_as_album: false,
};
// While PlayerInternal is written as a future, it still contains blocking code.
@ -406,6 +416,10 @@ impl Player {
pub fn emit_volume_set_event(&self, volume: u16) {
self.command(PlayerCommand::EmitVolumeSetEvent(volume));
}
pub fn set_auto_normalise_as_album(&self, setting: bool) {
self.command(PlayerCommand::SetAutoNormaliseAsAlbum(setting));
}
}
impl Drop for Player {
@ -423,7 +437,7 @@ impl Drop for Player {
struct PlayerLoadedTrackData {
decoder: Decoder,
normalisation_factor: f64,
normalisation_data: NormalisationData,
stream_loader_controller: StreamLoaderController,
bytes_per_second: usize,
duration_ms: u32,
@ -456,6 +470,7 @@ enum PlayerState {
track_id: SpotifyId,
play_request_id: u64,
decoder: Decoder,
normalisation_data: NormalisationData,
normalisation_factor: f64,
stream_loader_controller: StreamLoaderController,
bytes_per_second: usize,
@ -467,6 +482,7 @@ enum PlayerState {
track_id: SpotifyId,
play_request_id: u64,
decoder: Decoder,
normalisation_data: NormalisationData,
normalisation_factor: f64,
stream_loader_controller: StreamLoaderController,
bytes_per_second: usize,
@ -543,7 +559,7 @@ impl PlayerState {
decoder,
duration_ms,
bytes_per_second,
normalisation_factor,
normalisation_data,
stream_loader_controller,
stream_position_pcm,
..
@ -553,7 +569,7 @@ impl PlayerState {
play_request_id,
loaded_track: PlayerLoadedTrackData {
decoder,
normalisation_factor,
normalisation_data,
stream_loader_controller,
bytes_per_second,
duration_ms,
@ -572,6 +588,7 @@ impl PlayerState {
track_id,
play_request_id,
decoder,
normalisation_data,
normalisation_factor,
stream_loader_controller,
duration_ms,
@ -583,6 +600,7 @@ impl PlayerState {
track_id,
play_request_id,
decoder,
normalisation_data,
normalisation_factor,
stream_loader_controller,
duration_ms,
@ -603,6 +621,7 @@ impl PlayerState {
track_id,
play_request_id,
decoder,
normalisation_data,
normalisation_factor,
stream_loader_controller,
duration_ms,
@ -615,6 +634,7 @@ impl PlayerState {
track_id,
play_request_id,
decoder,
normalisation_data,
normalisation_factor,
stream_loader_controller,
duration_ms,
@ -775,14 +795,16 @@ impl PlayerTrackLoader {
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file)
{
Ok(normalisation_data) => {
NormalisationData::get_factor(&self.config, normalisation_data)
}
let normalisation_data = match NormalisationData::parse_from_file(&mut decrypted_file) {
Ok(data) => data,
Err(_) => {
warn!("Unable to extract normalisation data, using default value.");
1.0
NormalisationData {
track_gain_db: 0.0,
track_peak: 1.0,
album_gain_db: 0.0,
album_peak: 1.0,
}
}
};
@ -838,7 +860,7 @@ impl PlayerTrackLoader {
return Some(PlayerLoadedTrackData {
decoder,
normalisation_factor,
normalisation_data,
stream_loader_controller,
bytes_per_second,
duration_ms,
@ -1339,6 +1361,17 @@ impl PlayerInternal {
) {
let position_ms = Self::position_pcm_to_ms(loaded_track.stream_position_pcm);
let mut config = self.config.clone();
if config.normalisation_type == NormalisationType::Auto {
if self.auto_normalise_as_album {
config.normalisation_type = NormalisationType::Album;
} else {
config.normalisation_type = NormalisationType::Track;
}
};
let normalisation_factor =
NormalisationData::get_factor(&config, loaded_track.normalisation_data);
if start_playback {
self.ensure_sink_running();
@ -1353,7 +1386,8 @@ impl PlayerInternal {
track_id,
play_request_id,
decoder: loaded_track.decoder,
normalisation_factor: loaded_track.normalisation_factor,
normalisation_data: loaded_track.normalisation_data,
normalisation_factor,
stream_loader_controller: loaded_track.stream_loader_controller,
duration_ms: loaded_track.duration_ms,
bytes_per_second: loaded_track.bytes_per_second,
@ -1370,7 +1404,8 @@ impl PlayerInternal {
track_id,
play_request_id,
decoder: loaded_track.decoder,
normalisation_factor: loaded_track.normalisation_factor,
normalisation_data: loaded_track.normalisation_data,
normalisation_factor,
stream_loader_controller: loaded_track.stream_loader_controller,
duration_ms: loaded_track.duration_ms,
bytes_per_second: loaded_track.bytes_per_second,
@ -1497,7 +1532,7 @@ impl PlayerInternal {
stream_loader_controller,
bytes_per_second,
duration_ms,
normalisation_factor,
normalisation_data,
..
}
| PlayerState::Paused {
@ -1506,13 +1541,13 @@ impl PlayerInternal {
stream_loader_controller,
bytes_per_second,
duration_ms,
normalisation_factor,
normalisation_data,
..
} = old_state
{
let loaded_track = PlayerLoadedTrackData {
decoder,
normalisation_factor,
normalisation_data,
stream_loader_controller,
bytes_per_second,
duration_ms,
@ -1750,6 +1785,10 @@ impl PlayerInternal {
PlayerCommand::EmitVolumeSetEvent(volume) => {
self.send_event(PlayerEvent::VolumeSet { volume })
}
PlayerCommand::SetAutoNormaliseAsAlbum(setting) => {
self.auto_normalise_as_album = setting
}
}
}
@ -1855,6 +1894,10 @@ impl ::std::fmt::Debug for PlayerCommand {
PlayerCommand::EmitVolumeSetEvent(volume) => {
f.debug_tuple("VolumeSet").field(&volume).finish()
}
PlayerCommand::SetAutoNormaliseAsAlbum(setting) => f
.debug_tuple("SetAutoNormaliseAsAlbum")
.field(&setting)
.finish(),
}
}
}

View file

@ -359,7 +359,7 @@ fn get_setup(args: &[String]) -> Setup {
.optopt(
"",
NORMALISATION_GAIN_TYPE,
"Specify the normalisation gain type to use {track|album}. Defaults to album.",
"Specify the normalisation gain type to use {track|album|auto}. Defaults to auto.",
"TYPE",
)
.optopt(