diff --git a/include/meson.build b/include/meson.build index a534bcd119..99465ea5ac 100644 --- a/include/meson.build +++ b/include/meson.build @@ -92,6 +92,7 @@ install_headers( 'vlc_probe.h', 'vlc_rand.h', 'vlc_renderer_discovery.h', + 'vlc_replay_gain.h', 'vlc_services_discovery.h', 'vlc_sort.h', 'vlc_sout.h', diff --git a/include/vlc_es.h b/include/vlc_es.h index 94abe903f9..ce6c0828bb 100644 --- a/include/vlc_es.h +++ b/include/vlc_es.h @@ -26,6 +26,7 @@ #include #include #include +#include /** * \file @@ -46,26 +47,6 @@ struct video_palette_t uint8_t palette[VIDEO_PALETTE_COLORS_MAX][4]; /**< 4-byte RGBA/YUVA palette */ }; -/** - * audio replay gain description - */ -#define AUDIO_REPLAY_GAIN_MAX (2) -#define AUDIO_REPLAY_GAIN_TRACK (0) -#define AUDIO_REPLAY_GAIN_ALBUM (1) -typedef struct -{ - /* true if we have the peak value */ - bool pb_peak[AUDIO_REPLAY_GAIN_MAX]; - /* peak value where 1.0 means full sample value */ - float pf_peak[AUDIO_REPLAY_GAIN_MAX]; - - /* true if we have the gain value */ - bool pb_gain[AUDIO_REPLAY_GAIN_MAX]; - /* gain value in dB */ - float pf_gain[AUDIO_REPLAY_GAIN_MAX]; -} audio_replay_gain_t; - - /** * Audio channel type */ diff --git a/include/vlc_replay_gain.h b/include/vlc_replay_gain.h new file mode 100644 index 0000000000..4b1d3e89e8 --- /dev/null +++ b/include/vlc_replay_gain.h @@ -0,0 +1,166 @@ +/***************************************************************************** + * vlc_replay_gain.h : common replay gain code + ***************************************************************************** + * Copyright © 2002-2004 VLC authors and VideoLAN + * Copyright © 2011-2012 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_REPLAY_GAIN_H +#define VLC_REPLAY_GAIN_H 1 + +#include + +/** + * \file vlc_replay_gain.h + * \defgroup replay_gain Replay Gain + * \ingroup input + * Functions to read replay gain tags. + * + * @{ + */ + +/** Index for track values */ +#define AUDIO_REPLAY_GAIN_TRACK (0) +/** Index for album values */ +#define AUDIO_REPLAY_GAIN_ALBUM (1) +/** Number of replay gain types */ +#define AUDIO_REPLAY_GAIN_MAX (2) + +/** + * Audio replay gain + */ +typedef struct +{ + bool pb_reference_loudness; /**< true if we have the reference loudness */ + float pf_reference_loudness; /**< reference loudness in LUFS */ + bool pb_gain[AUDIO_REPLAY_GAIN_MAX]; /**< true if we have the gain value */ + float pf_gain[AUDIO_REPLAY_GAIN_MAX]; /**< gain value in dB */ + bool pb_peak[AUDIO_REPLAY_GAIN_MAX]; /**< true if we have the peak value */ + float pf_peak[AUDIO_REPLAY_GAIN_MAX]; /**< peak value where 1.0 means full sample value */ +} audio_replay_gain_t; + +/** + * Extracts replay gain info from metadata and copies it into a replay gain structure. + * Supports both capitalized and lowercase metadata tags. + * + * \param p_dst Destination replay gain structure to fill + * \param p_meta Metadata structure to extract values from + * \return VLC_SUCCESS if either an album or track gain was found, + * VLC_EGENERIC if no gain was found, + * VLC_EINVAL if either argument is null + */ +VLC_API int vlc_replay_gain_CopyFromMeta( audio_replay_gain_t *p_dst, const vlc_meta_t *p_meta ); + +/** + * Calculates the replay gain multiplier according to the Replay Gain 2.0 Specification. + * User preferences control mode, pre-amp, default gain, and peak protection. + * + * \param obj calling vlc object + * \param p_rg replay gain structure + * \return linear gain multiplier + */ +float replay_gain_CalcMultiplier( vlc_object_t *obj, const audio_replay_gain_t *p_rg ); + +/** + * Merges replay gain structures + * + * Only copies gain/peak/reference loudness values that are: + * - Set in the source + * - Not set in the destination + * + * \param p_dst Destination replay gain structure + * \param p_src Source replay gain structure + */ +static inline void replay_gain_Merge( audio_replay_gain_t *p_dst, const audio_replay_gain_t *p_src ) +{ + if( !p_dst || !p_src ) + return; + + if( !p_dst->pb_reference_loudness && p_src->pb_reference_loudness ) + { + p_dst->pb_reference_loudness = p_src->pb_reference_loudness; + p_dst->pf_reference_loudness = p_src->pf_reference_loudness; + } + + for( size_t i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + if( !p_dst->pb_gain[i] && p_src->pb_gain[i] ) + { + p_dst->pb_gain[i] = p_src->pb_gain[i]; + p_dst->pf_gain[i] = p_src->pf_gain[i]; + } + if( !p_dst->pb_peak[i] && p_src->pb_peak[i] ) + { + p_dst->pb_peak[i] = p_src->pb_peak[i]; + p_dst->pf_peak[i] = p_src->pf_peak[i]; + } + } +} + +/** + * Compares two replay gain structures + * + * \param p_a First replay gain structure + * \param p_b Second replay gain structure + * \return true if any gain/peak/reference loudness values or their validity flags differ + */ +static inline bool replay_gain_Compare( const audio_replay_gain_t *p_a, const audio_replay_gain_t *p_b ) +{ + if( !p_a || !p_b ) + return true; + + if( p_a->pb_reference_loudness != p_b->pb_reference_loudness || + p_a->pf_reference_loudness != p_b->pf_reference_loudness ) + return true; + + for( size_t i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + if( p_a->pb_gain[i] != p_b->pb_gain[i] || + p_a->pb_peak[i] != p_b->pb_peak[i] ) + return true; + + if( ( p_a->pb_gain[i] && p_a->pf_gain[i] != p_b->pf_gain[i] ) || + ( p_a->pb_peak[i] && p_a->pf_peak[i] != p_b->pf_peak[i] ) ) + return true; + } + return false; +} + +/** + * Reset replay gain structure values + * + * \param p_dst Replay gain structure + */ +static inline void replay_gain_Reset( audio_replay_gain_t *p_rg ) +{ + if( !p_rg ) + return; + + p_rg->pb_reference_loudness = false; + p_rg->pf_reference_loudness = 0.f; + + for( size_t i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + p_rg->pb_gain[i] = false; + p_rg->pf_gain[i] = 0.f; + + p_rg->pb_peak[i] = false; + p_rg->pf_peak[i] = 0.f; + } +} +/** @} */ +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 94f0d6e96b..bf19d3d4c6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,6 +97,7 @@ pluginsinclude_HEADERS.h = \ ../include/vlc_queue.h \ ../include/vlc_rand.h \ ../include/vlc_renderer_discovery.h \ + ../include/vlc_replay_gain.h \ ../include/vlc_services_discovery.h \ ../include/vlc_sort.h \ ../include/vlc_sout.h \ @@ -290,6 +291,7 @@ libvlccore_la_SOURCES = \ input/meta.c \ input/attachment.c \ input/parse.c \ + input/replay_gain.c \ player/player.c \ player/player.h \ player/input.c \ diff --git a/src/audio_output/volume.c b/src/audio_output/volume.c index dce18d6b22..8a3e3205a5 100644 --- a/src/audio_output/volume.c +++ b/src/audio_output/volume.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "aout_internal.h" struct aout_volume @@ -134,63 +135,12 @@ int aout_volume_Amplify(aout_volume_t *vol, block_t *block) return 0; } -/*** Replay gain ***/ -static float aout_ReplayGainSelect(vlc_object_t *obj, const char *str, - const audio_replay_gain_t *replay_gain) -{ - unsigned mode = AUDIO_REPLAY_GAIN_MAX; - - if (likely(str != NULL)) - { /* Find selectrf mode */ - if (!strcmp (str, "track")) - mode = AUDIO_REPLAY_GAIN_TRACK; - else - if (!strcmp (str, "album")) - mode = AUDIO_REPLAY_GAIN_ALBUM; - } - - /* */ - float multiplier; - - if (mode == AUDIO_REPLAY_GAIN_MAX) - { - multiplier = 1.f; - } - else - { - float gain; - - /* If the selectrf mode is not available, prefer the other one */ - if (!replay_gain->pb_gain[mode] && replay_gain->pb_gain[!mode]) - mode = !mode; - - if (replay_gain->pb_gain[mode]) - gain = replay_gain->pf_gain[mode] - + var_InheritFloat (obj, "audio-replay-gain-preamp"); - else - gain = var_InheritFloat (obj, "audio-replay-gain-default"); - - multiplier = powf (10.f, gain / 20.f); - - if (var_InheritBool (obj, "audio-replay-gain-peak-protection")) - multiplier = fminf (multiplier, replay_gain->pb_peak[mode] - ? 1.f / replay_gain->pf_peak[mode] - : 1.f); - } - - /* Command line / configuration gain */ - multiplier *= var_InheritFloat (obj, "gain"); - - return multiplier; -} - static int ReplayGainCallback (vlc_object_t *obj, char const *var, vlc_value_t oldval, vlc_value_t val, void *data) { aout_volume_t *vol = data; - float multiplier = aout_ReplayGainSelect(obj, val.psz_string, - &vol->replay_gain); + float multiplier = replay_gain_CalcMultiplier(obj, &vol->replay_gain); atomic_store_explicit(&vol->gain_factor, multiplier, memory_order_relaxed); - VLC_UNUSED(var); VLC_UNUSED(oldval); + VLC_UNUSED(var); VLC_UNUSED(oldval); VLC_UNUSED(val); return VLC_SUCCESS; } diff --git a/src/input/decoder.c b/src/input/decoder.c index 95dab75046..9ba55bed3f 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "audio_output/aout_internal.h" #include "stream_output/stream_output.h" @@ -495,20 +496,6 @@ static void MouseEvent( const vlc_mouse_t *newmouse, void *user_data ) /***************************************************************************** * Buffers allocation callbacks for the decoders *****************************************************************************/ -static bool aout_replaygain_changed( const audio_replay_gain_t *a, - const audio_replay_gain_t *b ) -{ - for( size_t i=0; ipb_gain[i] != b->pb_gain[i] || - a->pb_peak[i] != b->pb_peak[i] || - (a->pb_gain[i] && a->pf_gain[i] != b->pf_gain[i]) || - (a->pb_peak[i] && a->pf_peak[i] != b->pf_peak[i]) ) - return true; - } - return false; -} - static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec ) { vlc_input_decoder_t *p_owner = dec_get_owner( p_dec ); @@ -532,8 +519,8 @@ static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec ) } /* Check if only replay gain has changed */ - if( aout_replaygain_changed( &p_dec->fmt_in->audio_replay_gain, - &p_owner->fmt.audio_replay_gain ) ) + if( replay_gain_Compare( &p_dec->fmt_in->audio_replay_gain, + &p_owner->fmt.audio_replay_gain ) ) { p_dec->fmt_out.audio_replay_gain = p_dec->fmt_in->audio_replay_gain; if( p_owner->p_aout ) @@ -2065,19 +2052,7 @@ CreateDecoder( vlc_object_t *p_parent, const struct vlc_input_decoder_cfg *cfg ) /* Copy ourself the input replay gain */ if( fmt->i_cat == AUDIO_ES ) { - for( unsigned i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) - { - if( !p_dec->fmt_out.audio_replay_gain.pb_peak[i] ) - { - p_dec->fmt_out.audio_replay_gain.pb_peak[i] = fmt->audio_replay_gain.pb_peak[i]; - p_dec->fmt_out.audio_replay_gain.pf_peak[i] = fmt->audio_replay_gain.pf_peak[i]; - } - if( !p_dec->fmt_out.audio_replay_gain.pb_gain[i] ) - { - p_dec->fmt_out.audio_replay_gain.pb_gain[i] = fmt->audio_replay_gain.pb_gain[i]; - p_dec->fmt_out.audio_replay_gain.pf_gain[i] = fmt->audio_replay_gain.pf_gain[i]; - } - } + replay_gain_Merge( &p_dec->fmt_out.audio_replay_gain, &fmt->audio_replay_gain ); } /* */ diff --git a/src/input/es_out.c b/src/input/es_out.c index cde2a121ec..cf1901e7b6 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "input_internal.h" #include "./source.h" @@ -2037,22 +2038,10 @@ static void EsOutFillEsFmt(es_out_sys_t *p_sys, es_format_t *fmt) audio_replay_gain_t rg; memset( &rg, 0, sizeof(rg) ); vlc_mutex_lock( &input_priv(p_input)->p_item->lock ); - vlc_audio_replay_gain_MergeFromMeta( &rg, input_priv(p_input)->p_item->p_meta ); + vlc_replay_gain_CopyFromMeta( &rg, input_priv(p_input)->p_item->p_meta ); vlc_mutex_unlock( &input_priv(p_input)->p_item->lock ); + replay_gain_Merge( &fmt->audio_replay_gain, &rg ); - for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) - { - if( !fmt->audio_replay_gain.pb_peak[i] ) - { - fmt->audio_replay_gain.pb_peak[i] = rg.pb_peak[i]; - fmt->audio_replay_gain.pf_peak[i] = rg.pf_peak[i]; - } - if( !fmt->audio_replay_gain.pb_gain[i] ) - { - fmt->audio_replay_gain.pb_gain[i] = rg.pb_gain[i]; - fmt->audio_replay_gain.pf_gain[i] = rg.pf_gain[i]; - } - } break; } diff --git a/src/input/input_internal.h b/src/input/input_internal.h index a54514a02d..80054503c1 100644 --- a/src/input/input_internal.h +++ b/src/input/input_internal.h @@ -707,10 +707,6 @@ void vlc_object_InitInputConfig(vlc_object_t *obj, int subtitles_Detect( input_thread_t *, char *, const char *, input_item_slave_t ***, int * ); int subtitles_Filter( const char *); -/* meta.c */ -void vlc_audio_replay_gain_MergeFromMeta( audio_replay_gain_t *p_dst, - const vlc_meta_t *p_meta ); - /* stats.c */ typedef struct input_rate_t { diff --git a/src/input/meta.c b/src/input/meta.c index c4dcd97596..c12f9eadab 100644 --- a/src/input/meta.c +++ b/src/input/meta.c @@ -320,39 +320,3 @@ error: vlc_object_delete(p_export); return VLC_EGENERIC; } - -void vlc_audio_replay_gain_MergeFromMeta( audio_replay_gain_t *p_dst, - const vlc_meta_t *p_meta ) -{ - const char * psz_value; - - if( !p_meta ) - return; - - if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_TRACK_GAIN")) || - (psz_value = vlc_meta_GetExtra(p_meta, "RG_RADIO")) ) - { - p_dst->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true; - p_dst->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( psz_value ); - } - - if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_TRACK_PEAK" )) || - (psz_value = vlc_meta_GetExtra(p_meta, "RG_PEAK" )) ) - { - p_dst->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true; - p_dst->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( psz_value ); - } - - if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_ALBUM_GAIN" )) || - (psz_value = vlc_meta_GetExtra(p_meta, "RG_AUDIOPHILE" )) ) - { - p_dst->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true; - p_dst->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( psz_value ); - } - - if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_ALBUM_PEAK" )) ) - { - p_dst->pb_peak[AUDIO_REPLAY_GAIN_ALBUM] = true; - p_dst->pf_peak[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( psz_value ); - } -} diff --git a/src/input/replay_gain.c b/src/input/replay_gain.c new file mode 100644 index 0000000000..0861e5daea --- /dev/null +++ b/src/input/replay_gain.c @@ -0,0 +1,199 @@ +/***************************************************************************** + * replay_gain.c : common replay gain code + ***************************************************************************** + * Copyright © 2002-2004 VLC authors and VideoLAN + * Copyright © 2011-2012 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include + +int vlc_replay_gain_CopyFromMeta( audio_replay_gain_t *p_dst, const vlc_meta_t *p_meta ) +{ + if( !p_dst || !p_meta ) + return VLC_EINVAL; + + /* replay gain presence flags */ + enum audio_replay_gain_flags { + TRACK_GAIN = 0x01, + TRACK_PEAK = 0x02, + ALBUM_GAIN = 0x04, + ALBUM_PEAK = 0x08, + FLAGS_MASK = 0x0F, + GAINS_MASK = TRACK_GAIN | ALBUM_GAIN, + }; + + static const struct { + int mode; + enum audio_replay_gain_flags flag; + const char *tags[2]; + } rg_meta[4] = { + { AUDIO_REPLAY_GAIN_TRACK, TRACK_GAIN, + { "REPLAYGAIN_TRACK_GAIN", "replaygain_track_gain" } + }, + { AUDIO_REPLAY_GAIN_TRACK, TRACK_PEAK, + { "REPLAYGAIN_TRACK_PEAK", "replaygain_track_peak" } + }, + { AUDIO_REPLAY_GAIN_ALBUM, ALBUM_GAIN, + { "REPLAYGAIN_ALBUM_GAIN", "replaygain_album_gain" } + }, + { AUDIO_REPLAY_GAIN_ALBUM, ALBUM_PEAK, + { "REPLAYGAIN_ALBUM_PEAK", "replaygain_album_peak" } + } + }; + + enum audio_replay_gain_flags found = 0; + + for( size_t i = 0; i < ARRAY_SIZE( rg_meta ) && found != FLAGS_MASK; i++ ) + { + if( found & rg_meta[i].flag ) + continue; + + for( size_t j = 0; j < ARRAY_SIZE( rg_meta[i].tags ); j++ ) + { + const char *psz_meta = vlc_meta_GetExtra( p_meta, rg_meta[i].tags[j] ); + if( psz_meta ) + { + float f_value = vlc_strtof_c( psz_meta, NULL ); + + if( rg_meta[i].flag & GAINS_MASK ) + { + p_dst->pb_gain[rg_meta[i].mode] = true; + p_dst->pf_gain[rg_meta[i].mode] = f_value; + } + else + { + p_dst->pb_peak[rg_meta[i].mode] = true; + p_dst->pf_peak[rg_meta[i].mode] = f_value; + } + + found |= rg_meta[i].flag; + break; + } + } + } + + static const char *rg_loudness[2] = { + "REPLAYGAIN_REFERENCE_LOUDNESS", + "replaygain_reference_loudness" + }; + /* Only look for reference loudness if a track or album gain was found */ + for( size_t i = 0; i < ARRAY_SIZE( rg_loudness ) && ( found & GAINS_MASK ); i++ ) + { + const char *psz_meta = vlc_meta_GetExtra( p_meta, rg_loudness[i] ); + if( psz_meta ) + { + p_dst->pb_reference_loudness = true; + p_dst->pf_reference_loudness = vlc_strtof_c( psz_meta, NULL ); + break; + } + } + + /* Success if either a track or album gain was found. Peak defaults to 1.0 when absent */ + return ( found & GAINS_MASK ) ? VLC_SUCCESS : VLC_EGENERIC; +} + +float replay_gain_CalcMultiplier( vlc_object_t *p_obj, const audio_replay_gain_t *p_rg ) +{ + unsigned mode = AUDIO_REPLAY_GAIN_MAX; + + char *psz_mode = var_InheritString( p_obj, "audio-replay-gain-mode" ); + if( likely(psz_mode != NULL) ) + { /* Find selected mode */ + if (!strcmp (psz_mode, "track")) + mode = AUDIO_REPLAY_GAIN_TRACK; + else if( !strcmp( psz_mode, "album" ) ) + mode = AUDIO_REPLAY_GAIN_ALBUM; + free( psz_mode ); + } + + /* Command line / configuration gain */ + const float config_gain = var_InheritFloat( p_obj, "gain" ); + + if( mode == AUDIO_REPLAY_GAIN_MAX ) + return config_gain; + + const float preamp_gain = var_InheritFloat( p_obj, "audio-replay-gain-preamp" ); + const float default_gain = var_InheritFloat( p_obj, "audio-replay-gain-default" ); + const bool peak_protection = var_InheritBool( p_obj, "audio-replay-gain-peak-protection" ); + + /* If the selected mode is not available, prefer the other one */ + if( !p_rg->pb_gain[mode] && p_rg->pb_gain[!mode] ) + mode = !mode; + + float gain; + if( p_rg->pb_gain[mode] ) + { + /* replay gain uses -18 LUFS as the reference level */ + const float rg_ref_lufs = -18.f; + float rg_ref_lufs_delta = 0.f; + + if( p_rg->pb_reference_loudness ) + rg_ref_lufs_delta = rg_ref_lufs - p_rg->pf_reference_loudness; + + gain = p_rg->pf_gain[mode] + preamp_gain + rg_ref_lufs_delta; + msg_Dbg( p_obj, "replay gain: mode %s, gain %.2f dB, pre-amp %.2f dB, reference loudness %.2f LUFS", + mode ? "album" : "track", + p_rg->pf_gain[mode], + preamp_gain, + rg_ref_lufs - rg_ref_lufs_delta ); + } + else + { + gain = default_gain; + msg_Dbg( p_obj, "replay gain: mode default, gain %.2f dB", gain ); + } + + float multiplier = powf( 10.f, gain / 20.f ); + + /* Skip peak protection for default gain case, as the default peak value of 1.0 would limit gains greater than 0 dB */ + if( p_rg->pb_gain[mode] && peak_protection ) + { + /* Use peak of 1.0 if peak value is missing or invalid */ + float peak = p_rg->pb_peak[mode] && p_rg->pf_peak[mode] > 0.f ? p_rg->pf_peak[mode] : 1.f; + float peak_limit = 1.f / peak; + + /* To avoid clipping, max gain multiplier must be <= 1.0 / peak + * e.g.: peak of 0.5 -> max gain +6.02 dB (can double) + * e.g.: peak of 1.0 -> max gain 0 dB (unity gain) + * e.g.: peak of 1.5 -> max gain -3.52 dB (reduce by third) + * e.g.: peak of 2.0 -> max gain -6.02 dB (reduce by half) + */ + if( multiplier > peak_limit ) + { + multiplier = peak_limit; + msg_Dbg( p_obj, "replay gain: peak protection reducing gain from %.2f dB to %.2f dB (peak %.6f)", + gain, + 20.f * log10f( multiplier ), + peak ); + } + } + + /* apply configuration gain */ + multiplier *= config_gain; + msg_Dbg( p_obj, "replay gain: applying %.2f dB", 20.f * log10f( multiplier ) ); + + return multiplier; +} diff --git a/src/input/test/es_out.c b/src/input/test/es_out.c index a3b640a28e..a9f781daa2 100644 --- a/src/input/test/es_out.c +++ b/src/input/test/es_out.c @@ -299,12 +299,6 @@ sout_stream_t *sout_NewInstance(vlc_object_t *p_parent, const char *psz_dest) return NULL; } -void vlc_audio_replay_gain_MergeFromMeta(audio_replay_gain_t *p_dst, - const struct vlc_meta_t *p_meta) -{ - (void)p_dst; (void)p_meta; -} - static input_source_t *InputSourceNew(void) { input_source_t *in = calloc(1, sizeof(*in)); diff --git a/src/libvlccore.sym b/src/libvlccore.sym index b8c9c3cca7..47d8bee089 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -1004,6 +1004,7 @@ vlc_playlist_Pause vlc_playlist_Resume vlc_playlist_Export vlc_playlist_SetMediaStoppedAction +vlc_replay_gain_CopyFromMeta vlc_intf_GetMainPlaylist vlc_media_source_Hold vlc_media_source_Release diff --git a/src/meson.build b/src/meson.build index 01364169ac..fe830d640d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -168,6 +168,7 @@ libvlccore_sources_base = files( 'input/input_interface.h', 'input/vlm_internal.h', 'input/vlm_event.h', + 'input/replay_gain.c', 'input/resource.h', 'input/resource.c', 'input/services_discovery.c',