diff --git a/config/presets/loudgain.ini b/config/presets/loudgain.ini index c3de119..23a0ca3 100644 --- a/config/presets/loudgain.ini +++ b/config/presets/loudgain.ini @@ -1,6 +1,7 @@ [Global] TagMode=i Album=true +AlbumAsAES77=false TargetLoudness=-18 ClipMode=a MaxPeakLevel=-1.0 diff --git a/src/easymode.cpp b/src/easymode.cpp index 3fcc8d6..e3bf16c 100644 --- a/src/easymode.cpp +++ b/src/easymode.cpp @@ -48,6 +48,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -68,6 +69,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -88,6 +90,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -108,6 +111,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -128,6 +132,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -148,6 +153,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -168,6 +174,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -188,6 +195,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -208,6 +216,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -228,6 +237,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -248,6 +258,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -268,6 +279,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -288,6 +300,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -308,6 +321,7 @@ static Config configs[] = { .true_peak = false, .clip_mode = 'p', .do_album = true, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -468,6 +482,15 @@ int global_handler([[maybe_unused]] void *user, const char *section, const char else quit(EXIT_FAILURE); } + else if (MATCH(name, "AlbumAsAES77")) { + bool as_aes77; + if (convert_bool(value, as_aes77)) { + for (Config &config : configs) + config.album_as_aes77 = as_aes77; + } + else + quit(EXIT_FAILURE); + } else if (MATCH(name, "TagMode")) { char tag_mode; if (parse_tag_mode_easy(value, tag_mode)) { @@ -570,6 +593,8 @@ int format_handler([[maybe_unused]] void *user, const char *section, const char // Parse setting keys if (MATCH(name, "Album")) convert_bool(value, configs[static_cast(file_type)].do_album); + else if (MATCH(name, "AlbumAsAES77")) + convert_bool(value, configs[static_cast(file_type)].album_as_aes77); else if (MATCH(name, "TagMode")) parse_tag_mode_easy(value, configs[static_cast(file_type)].tag_mode); else if (MATCH(name, "ClipMode")) diff --git a/src/rsgain.cpp b/src/rsgain.cpp index c80da64..c851012 100644 --- a/src/rsgain.cpp +++ b/src/rsgain.cpp @@ -182,9 +182,10 @@ static void custom_mode(int argc, char *argv[]) unsigned int nb_files = 0; opterr = 0; - const char *short_opts = "+ac:m:tdl:O::qps:LSI:o:h?"; + const char *short_opts = "+aec:m:tdl:O::qps:LSI:o:h?"; static struct option long_opts[] = { { "album", no_argument, nullptr, 'a' }, + { "album-aes77", no_argument, nullptr, 'e' }, { "skip-existing", no_argument, nullptr, 'S' }, { "clip-mode", required_argument, nullptr, 'c' }, @@ -214,6 +215,7 @@ static void custom_mode(int argc, char *argv[]) .true_peak = false, .clip_mode = 'n', .do_album = false, + .album_as_aes77 = false, .tab_output = OutputType::NONE, .sep_header = false, .sort_alphanum = false, @@ -231,6 +233,11 @@ static void custom_mode(int argc, char *argv[]) config.do_album = true; break; + case 'e': + config.do_album = true; + config.album_as_aes77 = true; + break; + case 'S': config.skip_existing = true; break; @@ -430,6 +437,7 @@ static inline void help_custom() { rsgain::print("\n"); CMD_HELP("--album", "-a", "Calculate album gain and peak"); + CMD_HELP("--album-aes77", "-e", "Use the loudest track as the album loudness as recommended in AES77"); CMD_HELP("--skip-existing", "-S", "Don't scan files with existing ReplayGain information"); rsgain::print("\n"); diff --git a/src/rsgain.hpp b/src/rsgain.hpp index 9f5e4f9..c4ac844 100644 --- a/src/rsgain.hpp +++ b/src/rsgain.hpp @@ -27,6 +27,7 @@ struct Config { bool true_peak; char clip_mode; bool do_album; + bool album_as_aes77; OutputType tab_output; bool sep_header; bool sort_alphanum; diff --git a/src/scan.cpp b/src/scan.cpp index eaae7b3..bf792eb 100644 --- a/src/scan.cpp +++ b/src/scan.cpp @@ -672,20 +672,35 @@ void ScanJob::Track::calculate_loudness(const Config &config) void ScanJob::calculate_album_loudness() { double album_loudness, album_peak; - size_t nb_states = tracks.size(); - std::vector states(nb_states); - for (const Track &track : tracks) - if (track.result.track_loudness != -HUGE_VAL) - states.emplace_back(track.ebur128.get()); - - if (ebur128_loudness_global_multiple(states.data(), states.size(), &album_loudness) != EBUR128_SUCCESS) - album_loudness = config.target_loudness; - - album_peak = std::max_element(tracks.begin(), - tracks.end(), - [](const auto &a, const auto &b) { return a.result.track_peak < b.result.track_peak; } - )->result.track_peak; - + if (config.album_as_aes77) { + album_loudness = -HUGE_VAL; + album_peak = 0.0; + for (const Track &track : tracks) { + if (album_loudness < track.result.track_loudness) { + album_loudness = track.result.track_loudness; + } + if (album_peak < track.result.track_peak) { + album_peak = track.result.track_peak; + } + } + } + + else { + size_t nb_states = tracks.size(); + std::vector states(nb_states); + for (const Track &track : tracks) + if (track.result.track_loudness != -HUGE_VAL) + states.emplace_back(track.ebur128.get()); + + if (ebur128_loudness_global_multiple(states.data(), states.size(), &album_loudness) != EBUR128_SUCCESS) + album_loudness = config.target_loudness; + + album_peak = std::max_element(tracks.begin(), + tracks.end(), + [](const auto &a, const auto &b) { return a.result.track_peak < b.result.track_peak; } + )->result.track_peak; + } + double album_gain = (type == FileType::OPUS && config.opus_mode == 's' ? -23.0 : config.target_loudness) - album_loudness; for (Track &track : tracks) {