Skip to content

Commit 03e71f6

Browse files
authored
simplify get_factor (#942)
Simplify `get_factor`
1 parent 55ced49 commit 03e71f6

2 files changed

Lines changed: 46 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929
- [main] Don't panic when parsing options. Instead list valid values and exit.
3030
- [main] `--alsa-mixer-device` and `--alsa-mixer-index` now fallback to the card and index specified in `--device`.
3131
- [core] Removed unsafe code (breaking)
32+
- [playback] Adhere to ReplayGain spec when calculating gain normalisation factor.
3233

3334
### Removed
3435
- [playback] `alsamixer`: previously deprecated option `mixer-card` has been removed.

playback/src/player.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::{MS_PER_PAGE, NUM_CHANNELS, PAGES_PER_MS, SAMPLES_PER_SECOND};
3131

3232
const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000;
3333
pub const DB_VOLTAGE_RATIO: f64 = 20.0;
34+
pub const PCM_AT_0DBFS: f64 = 1.0;
3435

3536
pub struct Player {
3637
commands: Option<mpsc::UnboundedSender<PlayerCommand>>,
@@ -251,26 +252,57 @@ impl NormalisationData {
251252
(data.track_gain_db, data.track_peak)
252253
};
253254

254-
let normalisation_power = gain_db + config.normalisation_pregain_db;
255-
let mut normalisation_factor = db_to_ratio(normalisation_power);
255+
// As per the ReplayGain 1.0 & 2.0 (proposed) spec:
256+
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Clipping_prevention
257+
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Clipping_prevention
258+
let normalisation_factor = if config.normalisation_method == NormalisationMethod::Basic {
259+
// For Basic Normalisation, factor = min(ratio of (ReplayGain + PreGain), 1.0 / peak level).
260+
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification#Peak_amplitude
261+
// https://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification#Peak_amplitude
262+
// We then limit that to 1.0 as not to exceed dBFS (0.0 dB).
263+
let factor = f64::min(
264+
db_to_ratio(gain_db + config.normalisation_pregain_db),
265+
PCM_AT_0DBFS / gain_peak,
266+
);
256267

257-
if normalisation_power + ratio_to_db(gain_peak) > config.normalisation_threshold_dbfs {
258-
let limited_normalisation_factor =
259-
db_to_ratio(config.normalisation_threshold_dbfs) / gain_peak;
260-
let limited_normalisation_power = ratio_to_db(limited_normalisation_factor);
268+
if factor > PCM_AT_0DBFS {
269+
info!(
270+
"Lowering gain by {:.2} dB for the duration of this track to avoid potentially exceeding dBFS.",
271+
ratio_to_db(factor)
272+
);
261273

262-
if config.normalisation_method == NormalisationMethod::Basic {
263-
warn!("Limiting gain to {:.2} dB for the duration of this track to stay under normalisation threshold.", limited_normalisation_power);
264-
normalisation_factor = limited_normalisation_factor;
274+
PCM_AT_0DBFS
265275
} else {
276+
factor
277+
}
278+
} else {
279+
// For Dynamic Normalisation it's up to the player to decide,
280+
// factor = ratio of (ReplayGain + PreGain).
281+
// We then let the dynamic limiter handle gain reduction.
282+
let factor = db_to_ratio(gain_db + config.normalisation_pregain_db);
283+
let threshold_ratio = db_to_ratio(config.normalisation_threshold_dbfs);
284+
285+
if factor > PCM_AT_0DBFS {
286+
let factor_db = gain_db + config.normalisation_pregain_db;
287+
let limiting_db = factor_db + config.normalisation_threshold_dbfs.abs();
288+
266289
warn!(
267-
"This track will at its peak be subject to {:.2} dB of dynamic limiting.",
268-
normalisation_power - limited_normalisation_power
290+
"This track may exceed dBFS by {:.2} dB and be subject to {:.2} dB of dynamic limiting at it's peak.",
291+
factor_db, limiting_db
292+
);
293+
} else if factor > threshold_ratio {
294+
let limiting_db = gain_db
295+
+ config.normalisation_pregain_db
296+
+ config.normalisation_threshold_dbfs.abs();
297+
298+
info!(
299+
"This track may be subject to {:.2} dB of dynamic limiting at it's peak.",
300+
limiting_db
269301
);
270302
}
271303

272-
warn!("Please lower pregain to avoid.");
273-
}
304+
factor
305+
};
274306

275307
debug!("Normalisation Data: {:?}", data);
276308
debug!(

0 commit comments

Comments
 (0)