[vlc-commits] [Git][videolan/vlc][master] 7 commits: meta: case-insensitive replay gain tag parsing
Steve Lhomme (@robUx4)
gitlab at videolan.org
Sun Mar 9 07:40:26 UTC 2025
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
14247db3 by Robert Stone at 2025-03-09T07:22:33+00:00
meta: case-insensitive replay gain tag parsing
- - - - -
6b089c6a by Robert Stone at 2025-03-09T07:22:33+00:00
demux: es: use common replay gain parser
- - - - -
e55a6c24 by Robert Stone at 2025-03-09T07:22:33+00:00
demux: mp4: use common replay gain parser
- - - - -
06b28313 by Robert Stone at 2025-03-09T07:22:33+00:00
demux: mp4: fix iTunes SoundCheck gain calculation
Use max from the first pair (mW/dBm) with a 1000.0 divisor to
match Apple's behavior. Verified with iTunNORM (`00001000 00000100`),
which yields -6.12 dB; min would produce +15.92 dB.
The negative sign in the formula -10 * log10(x/1000) causes larger values
to produce increasingly negative gain. Using the max from the louder
channel results in greater attenuation.
- - - - -
f1d0f4e2 by Robert Stone at 2025-03-09T07:22:33+00:00
demux: xiph: use common replay gain parser
- - - - -
694405be by Robert Stone at 2025-03-09T07:22:33+00:00
demux: aiff: add support for meta tags
- - - - -
f4abe288 by Robert Stone at 2025-03-09T07:22:33+00:00
libvlc-module: add section for replay gain
- - - - -
23 changed files:
- include/meson.build
- include/vlc_es.h
- + include/vlc_replay_gain.h
- modules/codec/vorbis.c
- modules/demux/aiff.c
- modules/demux/flac.c
- modules/demux/mp4/meta.c
- modules/demux/mp4/mp4.c
- modules/demux/mpeg/es.c
- modules/demux/ogg.c
- modules/demux/xiph_metadata.c
- modules/demux/xiph_metadata.h
- src/Makefile.am
- src/audio_output/volume.c
- src/input/decoder.c
- src/input/es_out.c
- src/input/input_internal.h
- src/input/meta.c
- + src/input/replay_gain.c
- src/input/test/es_out.c
- src/libvlc-module.c
- src/libvlccore.sym
- src/meson.build
Changes:
=====================================
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',
=====================================
include/vlc_es.h
=====================================
@@ -26,6 +26,7 @@
#include <vlc_common.h>
#include <vlc_fourcc.h>
#include <vlc_viewpoint.h>
+#include <vlc_replay_gain.h>
/**
* \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
*/
=====================================
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 <vlc_common.h>
+
+/**
+ * \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
=====================================
modules/codec/vorbis.c
=====================================
@@ -38,6 +38,7 @@
#include <vlc_aout.h>
#include <vlc_input_item.h>
#include <vlc_sout.h>
+#include <vlc_replay_gain.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
@@ -405,6 +406,36 @@ static int ProcessHeaders( decoder_t *p_dec )
}
ParseVorbisComments( p_dec );
+ int i_ret = vlc_replay_gain_CopyFromMeta( &p_dec->fmt_out.audio_replay_gain, p_dec->p_description );
+
+ /* use replay gain if available; otherwise see vorbisgain(1) */
+ if( i_ret != VLC_SUCCESS && p_dec->p_description != NULL )
+ {
+ audio_replay_gain_t *p_arg = &p_dec->fmt_out.audio_replay_gain;
+ replay_gain_Reset( p_arg );
+
+ const char *track_gain = vlc_meta_GetExtra( p_dec->p_description, "RG_RADIO" );
+ if( track_gain )
+ {
+ p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = vlc_strtof_c( track_gain, NULL );
+ }
+
+ const char *track_peak = vlc_meta_GetExtra( p_dec->p_description, "RG_PEAK" );
+ if( track_peak )
+ {
+ p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_arg->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = vlc_strtof_c( track_peak, NULL );
+ }
+
+ const char *album_gain = vlc_meta_GetExtra( p_dec->p_description, "RG_AUDIOPHILE" );
+ if( album_gain )
+ {
+ p_arg->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = vlc_strtof_c( album_gain, NULL );
+ }
+ }
+
/* The next packet in order is the codebooks header
* We need to watch out that this packet is not missing as a
* missing or corrupted header is fatal. */
@@ -620,38 +651,7 @@ static void ParseVorbisComments( decoder_t *p_dec )
*psz_value = '\0';
psz_value++;
- if( !strcasecmp( psz_name, "REPLAYGAIN_TRACK_GAIN" ) ||
- !strcasecmp( psz_name, "RG_RADIO" ) )
- {
- audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain;
-
- r->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
- r->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( psz_value );
- }
- else if( !strcasecmp( psz_name, "REPLAYGAIN_TRACK_PEAK" ) ||
- !strcasecmp( psz_name, "RG_PEAK" ) )
- {
- audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain;
-
- r->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
- r->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( psz_value );
- }
- else if( !strcasecmp( psz_name, "REPLAYGAIN_ALBUM_GAIN" ) ||
- !strcasecmp( psz_name, "RG_AUDIOPHILE" ) )
- {
- audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain;
-
- r->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
- r->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( psz_value );
- }
- else if( !strcasecmp( psz_name, "REPLAYGAIN_ALBUM_PEAK" ) )
- {
- audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain;
-
- r->pb_peak[AUDIO_REPLAY_GAIN_ALBUM] = true;
- r->pf_peak[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( psz_value );
- }
- else if( !strcasecmp( psz_name, "METADATA_BLOCK_PICTURE" ) )
+ if( !strcasecmp( psz_name, "METADATA_BLOCK_PICTURE" ) )
{ /* Do nothing, for now */ }
else
{
=====================================
modules/demux/aiff.c
=====================================
@@ -32,6 +32,7 @@
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include <vlc_aout.h>
+#include <vlc_meta.h>
#include <limits.h>
#include "mp4/coreaudio.h"
@@ -44,12 +45,13 @@
* Module descriptor
*****************************************************************************/
static int Open ( vlc_object_t * );
+static void Close ( vlc_object_t * );
vlc_module_begin ()
set_subcategory( SUBCAT_INPUT_DEMUX )
set_description( N_("AIFF demuxer" ) )
set_capability( "demux", 10 )
- set_callback( Open )
+ set_callbacks( Open, Close )
add_shortcut( "aiff" )
add_file_extension("aiff")
vlc_module_end ()
@@ -78,6 +80,7 @@ typedef struct
bool b_reorder;
uint8_t pi_chan_table[AOUT_CHAN_MAX];
+ vlc_meta_t *p_meta;
vlc_fourcc_t audio_fourcc;
} demux_sys_t;
@@ -103,12 +106,66 @@ static unsigned int GetF80BE( const uint8_t p[10] )
return i_mantissa;
}
+/*****************************************************************************
+ * ReadTextChunk: reads aiff text chunks - name, author, copyright, annotation
+ *****************************************************************************/
+static int ReadTextChunk( demux_t *p_demux, uint64_t i_chunk_size, uint32_t i_data_size )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ const uint8_t *p_peek;
+
+ ssize_t ret = vlc_stream_Peek( p_demux->s, &p_peek, i_chunk_size );
+ if( ret == -1 || (size_t) ret != i_chunk_size )
+ return VLC_EGENERIC;
+
+ static const struct {
+ const char psz_name[4];
+ int i_meta;
+ } p_dsc[4] = {
+ { "NAME", vlc_meta_Title },
+ { "AUTH", vlc_meta_Artist },
+ { "(c) ", vlc_meta_Copyright },
+ { "ANNO", vlc_meta_Description }
+ };
+
+ for( size_t i = 0; i < ARRAY_SIZE( p_dsc ); i++ )
+ {
+ if( memcmp(p_peek, p_dsc[i].psz_name, 4) )
+ continue;
+
+ char *psz_value = malloc( i_data_size + 1 );
+
+ if( unlikely(psz_value == NULL) )
+ return VLC_ENOMEM;
+
+ if( !p_sys->p_meta )
+ {
+ p_sys->p_meta = vlc_meta_New();
+ if( unlikely(p_sys->p_meta == NULL) )
+ {
+ free( psz_value );
+ return VLC_ENOMEM;
+ }
+ }
+
+ memcpy( psz_value, (char*)&p_peek[8], i_data_size );
+ psz_value[i_data_size] = '\0';
+ vlc_meta_Set( p_sys->p_meta, p_dsc[i].i_meta, psz_value );
+ free( psz_value );
+
+ return VLC_SUCCESS;
+ }
+
+ return VLC_EGENERIC;
+}
+
/*****************************************************************************
* Open
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys;
const uint8_t *p_peek;
@@ -121,19 +178,24 @@ static int Open( vlc_object_t *p_this )
if( vlc_stream_Read( p_demux->s, NULL, 12 ) != 12 )
return VLC_EGENERIC;
- /* Fill p_demux field */
- demux_sys_t *p_sys = vlc_obj_calloc( p_this, 1, sizeof (*p_sys) );
- es_format_Init( &p_sys->fmt, AUDIO_ES, VLC_FOURCC( 't', 'w', 'o', 's' ) );
+ p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) );
+
+ if( unlikely(p_sys == NULL) )
+ return VLC_ENOMEM;
+
+ p_demux->p_sys = p_sys;
+
p_sys->i_time = 0;
p_sys->i_ssnd_pos = -1;
p_sys->b_reorder = false;
+ es_format_Init( &p_sys->fmt, AUDIO_ES, VLC_FOURCC( 't', 'w', 'o', 's' ) );
const uint32_t *pi_channels_in = NULL;
for( ;; )
{
if( vlc_stream_Peek( p_demux->s, &p_peek, 8 ) < 8 )
- return VLC_EGENERIC;
+ break;
uint32_t i_data_size = GetDWBE( &p_peek[4] );
uint64_t i_chunk_size = UINT64_C( 8 ) + i_data_size + ( i_data_size & 1 );
@@ -184,13 +246,13 @@ static int Open( vlc_object_t *p_this )
&p_sys->fmt.audio.i_channels,
&pi_channels_in ) != VLC_SUCCESS )
msg_Warn( p_demux, "discarding chan mapping" );
-
}
-
- if( p_sys->i_ssnd_pos >= 12 && p_sys->fmt.audio.i_channels != 0 )
+ else
{
- /* We have found the 2 needed chunks */
- break;
+ int i_ret = ReadTextChunk( p_demux, i_chunk_size, i_data_size );
+
+ if( unlikely(i_ret == VLC_ENOMEM) )
+ return VLC_ENOMEM;
}
/* consume chunk data */
@@ -228,7 +290,7 @@ static int Open( vlc_object_t *p_this )
p_sys->i_ssnd_fsize = p_sys->fmt.audio.i_channels *
((p_sys->fmt.audio.i_bitspersample + 7) / 8);
- if( p_sys->i_ssnd_fsize <= 0 || p_sys->fmt.audio.i_rate == 0 )
+ if( p_sys->i_ssnd_fsize <= 0 || p_sys->fmt.audio.i_rate == 0 || p_sys->i_ssnd_pos < 12 )
{
msg_Err( p_demux, "invalid audio parameters" );
return VLC_EGENERIC;
@@ -255,7 +317,6 @@ static int Open( vlc_object_t *p_this )
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
- p_demux->p_sys = p_sys;
return VLC_SUCCESS;
}
@@ -307,6 +368,18 @@ static int Demux( demux_t *p_demux )
return VLC_DEMUXER_SUCCESS;
}
+/*****************************************************************************
+ * Close: frees unused data
+ *****************************************************************************/
+static void Close( vlc_object_t * p_this )
+{
+ demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if( p_sys->p_meta )
+ vlc_meta_Delete( p_sys->p_meta );
+}
+
/*****************************************************************************
* Control:
*****************************************************************************/
@@ -374,6 +447,15 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
}
return VLC_EGENERIC;
}
+ case DEMUX_GET_META:
+ {
+ vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * );
+ if( !p_sys->p_meta )
+ return VLC_EGENERIC;
+ vlc_meta_Merge( p_meta, p_sys->p_meta );
+ return VLC_SUCCESS;
+ }
+
case DEMUX_SET_TIME:
case DEMUX_GET_FPS:
return VLC_EGENERIC;
=====================================
modules/demux/flac.c
=====================================
@@ -36,6 +36,7 @@
#include <vlc_input.h> /* vlc_input_attachment, vlc_seekpoint */
#include <vlc_codec.h> /* decoder_t */
#include <vlc_charset.h> /* EnsureUTF8 */
+#include <vlc_replay_gain.h>
#include <assert.h>
#include <limits.h>
@@ -744,6 +745,8 @@ static int ParseHeaders( demux_t *p_demux, es_format_t *p_fmt )
i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4+i_len );
if( i_peek == 4+i_len )
ParseComment( p_demux, p_peek, i_peek );
+
+ vlc_replay_gain_CopyFromMeta( &p_fmt->audio_replay_gain, p_sys->p_meta );
}
else if( i_type == META_PICTURE )
{
@@ -818,7 +821,7 @@ static void ParseComment( demux_t *p_demux, const uint8_t *p_data, size_t i_data
vorbis_ParseComment( NULL, &p_sys->p_meta, &p_data[4], i_data - 4,
&p_sys->i_attachments, &p_sys->attachments,
&p_sys->i_cover_score, &p_sys->i_cover_idx,
- &p_sys->i_title_seekpoints, &p_sys->pp_title_seekpoints, NULL, NULL );
+ &p_sys->i_title_seekpoints, &p_sys->pp_title_seekpoints );
}
static void ParsePicture( demux_t *p_demux, const uint8_t *p_data, size_t i_data )
=====================================
modules/demux/mp4/meta.c
=====================================
@@ -395,9 +395,10 @@ static void iTUNTripletCallback( const char *psz_key,
"%*"PRIX32" %*"PRIX32" %"PRIX32" %"PRIX32,
&values[0], &values[1], &values[2], &values[3] ) == 4 )
{
- values[0] = __MIN(values[0], values[1]); /* left / right volume */
+ values[0] = __MAX(values[0], values[1]); /* left / right volume */
if( values[0] )
- data.NORM.volume_adjust = -10.0 * log10( values[0] / 10000.0 );
+ data.NORM.volume_adjust = -10.0 * log10( values[0] / 1000.0 );
+
values[2] = __MAX(values[2], values[3]); /* left / right peak */
if( values[2] )
data.NORM.peak = values[2] / 32768.0;
@@ -420,6 +421,13 @@ static void iTUNTripletCallback( const char *psz_key,
}
ctx->ituncb( &data, ctx->priv );
}
+ else if( !strncasecmp(psz_key, "REPLAYGAIN_", 11) )
+ {
+ char *psz_val = StringConvert( p_data );
+ if( psz_val )
+ vlc_meta_SetExtra( ctx->p_meta, psz_key, psz_val );
+ free( psz_val );
+ }
}
static void SetupmdirMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box, void *priv )
@@ -618,4 +626,3 @@ void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta,
}
}
}
-
=====================================
modules/demux/mp4/mp4.c
=====================================
@@ -36,8 +36,10 @@
#include <vlc_plugin.h>
#include <vlc_dialog.h>
#include <vlc_url.h>
+#include <vlc_replay_gain.h>
#include <assert.h>
#include <limits.h>
+#include <math.h>
#include "meta.h"
#include "attachments.h"
#include "heif.h"
@@ -1048,6 +1050,8 @@ static int Open( vlc_object_t * p_this )
p_sys->context.i_lastseqnumber = UINT32_MAX;
p_sys->i_attachments = -1;
+ p_sys->qt.f_replay_gain_norm = NAN;
+ p_sys->qt.f_replay_gain_peak = NAN;
p_demux->p_sys = p_sys;
@@ -3258,37 +3262,36 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
(float)p_fmt->video.i_frame_rate_base;
break;
case AUDIO_ES:
+ {
+ int i_ret = VLC_EGENERIC;
if( p_sys->p_meta )
{
- audio_replay_gain_t *p_arg = &p_fmt->audio_replay_gain;
- const char *psz_meta = vlc_meta_GetExtra( p_sys->p_meta, "replaygain_track_gain" );
- if( psz_meta )
- {
- double f_gain = vlc_atof_c( psz_meta );
- p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = f_gain;
- p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = f_gain != 0;
- }
- psz_meta = vlc_meta_GetExtra( p_sys->p_meta, "replaygain_track_peak" );
- if( psz_meta )
- {
- double f_gain = vlc_atof_c( psz_meta );
- p_arg->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = f_gain;
- p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = f_gain > 0;
- }
+ i_ret = vlc_replay_gain_CopyFromMeta( &p_fmt->audio_replay_gain, p_sys->p_meta );
}
- if( p_sys->qt.f_replay_gain_peak > 0 )
+ /* use replay gain if available; otherwise use sound check */
+ if( i_ret != VLC_SUCCESS )
{
audio_replay_gain_t *p_arg = &p_fmt->audio_replay_gain;
- if( !p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] )
+ replay_gain_Reset( p_arg );
+
+ if( isfinite(p_sys->qt.f_replay_gain_norm) )
{
- p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_norm;
p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_norm;
}
- if( !p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] )
+
+ if( isfinite(p_sys->qt.f_replay_gain_peak) )
{
- p_arg->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_peak;
p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_arg->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_peak;
+ }
+
+ if( p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] )
+ {
+ /* sound check uses -16 LUFS as the reference level */
+ p_arg->pb_reference_loudness = true;
+ p_arg->pf_reference_loudness = -16.f;
}
}
@@ -3358,6 +3361,7 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
break;
}
break;
+ }
default:
break;
}
=====================================
modules/demux/mpeg/es.c
=====================================
@@ -30,6 +30,8 @@
# include "config.h"
#endif
+#include <math.h>
+
#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_plugin.h>
@@ -37,6 +39,7 @@
#include <vlc_codec.h>
#include <vlc_codecs.h>
#include <vlc_input.h>
+#include <vlc_replay_gain.h>
#include "../../packetizer/a52.h"
#include "../../packetizer/dts_header.h"
@@ -362,8 +365,7 @@ typedef struct
struct mpga_frameheader_s mpgah;
struct xing_info_s xing;
- float rgf_replay_gain[AUDIO_REPLAY_GAIN_MAX];
- float rgf_replay_peak[AUDIO_REPLAY_GAIN_MAX];
+ audio_replay_gain_t audio_replay_gain;
sync_table_t mllt;
struct
@@ -435,6 +437,9 @@ static int OpenCommon( demux_t *p_demux,
p_sys->f_fps = var_InheritFloat( p_demux, "es-fps" );
p_sys->p_packetized_data = NULL;
p_sys->chapters.i_current = 0;
+ p_sys->xing.f_peak_signal = NAN;
+ p_sys->xing.f_radio_replay_gain = NAN;
+ p_sys->xing.f_audiophile_replay_gain = NAN;
TAB_INIT(p_sys->chapters.i_count, p_sys->chapters.p_entry);
if( vlc_stream_Seek( p_demux->s, p_sys->i_stream_offset ) )
@@ -468,19 +473,7 @@ static int OpenCommon( demux_t *p_demux,
}
es_format_t *p_fmt = &p_sys->p_packetizer->fmt_out;
- for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
- {
- if ( p_sys->rgf_replay_gain[i] != 0.0 )
- {
- p_fmt->audio_replay_gain.pb_gain[i] = true;
- p_fmt->audio_replay_gain.pf_gain[i] = p_sys->rgf_replay_gain[i];
- }
- if ( p_sys->rgf_replay_peak[i] != 0.0 )
- {
- p_fmt->audio_replay_gain.pb_peak[i] = true;
- p_fmt->audio_replay_gain.pf_peak[i] = p_sys->rgf_replay_peak[i];
- }
- }
+ replay_gain_Merge( &p_fmt->audio_replay_gain, &p_sys->audio_replay_gain );
for( ;; )
{
@@ -1270,30 +1263,7 @@ static int ID3TAG_Parse_Handler( uint32_t i_tag, const uint8_t *p_payload, size_
bool b_updated;
if( ID3HandleTag( p_payload, i_payload, i_tag, p_meta, &b_updated ) )
{
- char ** ppsz_keys = vlc_meta_CopyExtraNames( p_meta );
- if( ppsz_keys )
- {
- for( size_t i = 0; ppsz_keys[i]; ++i )
- {
- float *pf = NULL;
- if( !strcasecmp( ppsz_keys[i], "REPLAYGAIN_TRACK_GAIN" ) )
- pf = &p_sys->rgf_replay_gain[AUDIO_REPLAY_GAIN_TRACK];
- else if( !strcasecmp( ppsz_keys[i], "REPLAYGAIN_TRACK_PEAK" ) )
- pf = &p_sys->rgf_replay_peak[AUDIO_REPLAY_GAIN_TRACK];
- else if( !strcasecmp( ppsz_keys[i], "REPLAYGAIN_ALBUM_GAIN" ) )
- pf = &p_sys->rgf_replay_gain[AUDIO_REPLAY_GAIN_ALBUM];
- else if( !strcasecmp( ppsz_keys[i], "REPLAYGAIN_ALBUM_PEAK" ) )
- pf = &p_sys->rgf_replay_peak[AUDIO_REPLAY_GAIN_ALBUM];
- if( pf )
- {
- const char *psz_val = vlc_meta_GetExtra( p_meta, ppsz_keys[i] );
- if( psz_val )
- *pf = vlc_atof_c( psz_val );
- }
- free( ppsz_keys[i] );
- }
- free( ppsz_keys );
- }
+ vlc_replay_gain_CopyFromMeta( &p_sys->audio_replay_gain, p_meta );
}
vlc_meta_Delete( p_meta );
}
@@ -1421,9 +1391,23 @@ static int MpgaInit( demux_t *p_demux )
p_sys->b_estimate_bitrate = false;
}
- p_sys->rgf_replay_peak[AUDIO_REPLAY_GAIN_TRACK] = xing->f_peak_signal;
- p_sys->rgf_replay_gain[AUDIO_REPLAY_GAIN_TRACK] = xing->f_radio_replay_gain;
- p_sys->rgf_replay_gain[AUDIO_REPLAY_GAIN_ALBUM] = xing->f_audiophile_replay_gain;
+ if( isfinite(xing->f_radio_replay_gain) )
+ {
+ p_sys->audio_replay_gain.pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_sys->audio_replay_gain.pf_gain[AUDIO_REPLAY_GAIN_TRACK] = xing->f_radio_replay_gain;
+ }
+
+ if( isfinite(xing->f_peak_signal) )
+ {
+ p_sys->audio_replay_gain.pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_sys->audio_replay_gain.pf_peak[AUDIO_REPLAY_GAIN_TRACK] = xing->f_peak_signal;
+ }
+
+ if( isfinite(xing->f_audiophile_replay_gain) )
+ {
+ p_sys->audio_replay_gain.pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
+ p_sys->audio_replay_gain.pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = xing->f_audiophile_replay_gain;
+ }
msg_Dbg( p_demux, "Using '%4.4s' infotag"
"(%"PRIu32" bytes, %"PRIu32" frames, %u samples/frame)",
=====================================
modules/demux/ogg.c
=====================================
@@ -39,6 +39,7 @@
#include <vlc_demux.h>
#include <vlc_meta.h>
#include <vlc_input.h>
+#include <vlc_replay_gain.h>
#include <ogg/ogg.h>
@@ -2569,18 +2570,12 @@ static void Ogg_ExtractComments( demux_t *p_demux, es_format_t *p_fmt,
demux_sys_t *p_ogg = p_demux->p_sys;
int i_cover_score = 0;
int i_cover_idx = 0;
- float pf_replay_gain[AUDIO_REPLAY_GAIN_MAX];
- float pf_replay_peak[AUDIO_REPLAY_GAIN_MAX];
- for(int i=0; i< AUDIO_REPLAY_GAIN_MAX; i++ )
- {
- pf_replay_gain[i] = 0;
- pf_replay_peak[i] = 0;
- }
+
vorbis_ParseComment( p_fmt, &p_ogg->p_meta, p_headers, i_headers,
&p_ogg->i_attachments, &p_ogg->attachments,
&i_cover_score, &i_cover_idx,
- &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints,
- &pf_replay_gain, &pf_replay_peak );
+ &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints );
+
if( p_ogg->p_meta != NULL && i_cover_idx < p_ogg->i_attachments )
{
char psz_url[128];
@@ -2589,19 +2584,33 @@ static void Ogg_ExtractComments( demux_t *p_demux, es_format_t *p_fmt,
vlc_meta_Set( p_ogg->p_meta, vlc_meta_ArtworkURL, psz_url );
}
- for ( int i=0; i<AUDIO_REPLAY_GAIN_MAX;i++ )
+ int i_ret = vlc_replay_gain_CopyFromMeta( &p_fmt->audio_replay_gain, p_ogg->p_meta );
+
+ /* use replay gain if available; otherwise use IETF RFC7845 §5.2.1 */
+ if( i_ret != VLC_SUCCESS && p_ogg->p_meta != NULL )
{
- if ( pf_replay_gain[i] != 0 )
+ audio_replay_gain_t *p_arg = &p_fmt->audio_replay_gain;
+ replay_gain_Reset( p_arg );
+
+ const char *track_gain = vlc_meta_GetExtra( p_ogg->p_meta, "R128_TRACK_GAIN" );
+ if( track_gain )
{
- p_fmt->audio_replay_gain.pb_gain[i] = true;
- p_fmt->audio_replay_gain.pf_gain[i] = pf_replay_gain[i];
- msg_Dbg( p_demux, "setting replay gain %d to %f", i, pf_replay_gain[i] );
+ p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = vlc_strtof_c( track_gain, NULL ) / 256.f;
}
- if ( pf_replay_peak[i] != 0 )
+
+ const char *album_gain = vlc_meta_GetExtra( p_ogg->p_meta, "R128_ALBUM_GAIN" );
+ if( album_gain )
+ {
+ p_arg->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = vlc_strtof_c( album_gain, NULL ) / 256.f;
+ }
+
+ if( track_gain || album_gain )
{
- p_fmt->audio_replay_gain.pb_peak[i] = true;
- p_fmt->audio_replay_gain.pf_peak[i] = pf_replay_peak[i];
- msg_Dbg( p_demux, "setting replay peak %d to %f", i, pf_replay_gain[i] );
+ /* EBU R128 uses -23 LUFS as the reference level */
+ p_arg->pb_reference_loudness = true;
+ p_arg->pf_reference_loudness = -23.f;
}
}
=====================================
modules/demux/xiph_metadata.c
=====================================
@@ -327,9 +327,7 @@ void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta,
const uint8_t *p_data, size_t i_data,
int *i_attachments, input_attachment_t ***attachments,
int *i_cover_score, int *i_cover_idx,
- int *i_seekpoint, seekpoint_t ***ppp_seekpoint,
- float (* ppf_replay_gain)[AUDIO_REPLAY_GAIN_MAX],
- float (* ppf_replay_peak)[AUDIO_REPLAY_GAIN_MAX] )
+ int *i_seekpoint, seekpoint_t ***ppp_seekpoint )
{
if( i_data < 8 )
return;
@@ -465,27 +463,6 @@ void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta,
*i_attachments, *attachments, p_attachment );
}
}
- else if ( ppf_replay_gain && ppf_replay_peak && !strncmp(psz_comment, "REPLAYGAIN_", 11) )
- {
- char *p = strchr( psz_comment, '=' );
- if (!p) goto next_comment;
- if ( !strncasecmp(psz_comment, "REPLAYGAIN_TRACK_GAIN=", 22) )
- {
- (*ppf_replay_gain)[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( ++p );
- }
- else if ( !strncasecmp(psz_comment, "REPLAYGAIN_ALBUM_GAIN=", 22) )
- {
- (*ppf_replay_gain)[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( ++p );
- }
- else if ( !strncasecmp(psz_comment, "REPLAYGAIN_ALBUM_PEAK=", 22) )
- {
- (*ppf_replay_peak)[AUDIO_REPLAY_GAIN_ALBUM] = vlc_atof_c( ++p );
- }
- else if ( !strncasecmp(psz_comment, "REPLAYGAIN_TRACK_PEAK=", 22) )
- {
- (*ppf_replay_peak)[AUDIO_REPLAY_GAIN_TRACK] = vlc_atof_c( ++p );
- }
- }
else if( !strncasecmp(psz_comment, "CHAPTER", 7) )
{
unsigned int i_chapt;
=====================================
modules/demux/xiph_metadata.h
=====================================
@@ -35,9 +35,7 @@ void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta,
const uint8_t *p_data, size_t i_data,
int *i_attachments, input_attachment_t ***attachments,
int *i_cover_score, int *i_cover_idx,
- int *i_seekpoint, seekpoint_t ***ppp_seekpoint,
- float (* ppf_replay_gain)[AUDIO_REPLAY_GAIN_MAX],
- float (* ppf_replay_peak)[AUDIO_REPLAY_GAIN_MAX] );
+ int *i_seekpoint, seekpoint_t ***ppp_seekpoint );
static const struct {
const char *psz_tag;
=====================================
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 \
=====================================
src/audio_output/volume.c
=====================================
@@ -32,6 +32,7 @@
#include <vlc_modules.h>
#include <vlc_aout.h>
#include <vlc_aout_volume.h>
+#include <vlc_replay_gain.h>
#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;
}
=====================================
src/input/decoder.c
=====================================
@@ -45,6 +45,7 @@
#include <vlc_picture_pool.h>
#include <vlc_tracer.h>
#include <vlc_list.h>
+#include <vlc_replay_gain.h>
#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; i<AUDIO_REPLAY_GAIN_MAX; i++ )
- {
- if( a->pb_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 );
}
/* */
=====================================
src/input/es_out.c
=====================================
@@ -44,6 +44,7 @@
#include <vlc_decoder.h>
#include <vlc_memstream.h>
#include <vlc_tracer.h>
+#include <vlc_replay_gain.h>
#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;
}
=====================================
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
{
=====================================
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 );
- }
-}
=====================================
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 <math.h>
+
+#include <vlc_replay_gain.h>
+#include <vlc_meta.h>
+#include <vlc_charset.h>
+#include <vlc_variables.h>
+
+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;
+}
=====================================
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));
=====================================
src/libvlc-module.c
=====================================
@@ -1617,6 +1617,8 @@ vlc_module_begin ()
VOLUME_STEP_LONGTEXT )
change_float_range( 1., AOUT_VOLUME_DEFAULT )
add_bool( "volume-save", true, VOLUME_SAVE_TEXT, NULL )
+ add_bool( "audio-time-stretch", true, AUDIO_TIME_STRETCH_TEXT,
+ AUDIO_TIME_STRETCH_LONGTEXT )
#if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32)
add_bool( "spdif", false, SPDIF_TEXT, SPDIF_LONGTEXT )
#else
@@ -1633,7 +1635,7 @@ vlc_module_begin ()
DESYNC_LONGTEXT )
change_safe ()
- /* FIXME TODO create a subcat replay gain ? */
+ set_section( N_("Replay gain") , NULL )
add_string( "audio-replay-gain-mode", ppsz_replay_gain_mode[0], AUDIO_REPLAY_GAIN_MODE_TEXT,
AUDIO_REPLAY_GAIN_MODE_LONGTEXT )
change_string_list( ppsz_replay_gain_mode, ppsz_replay_gain_mode_text )
@@ -1644,9 +1646,6 @@ vlc_module_begin ()
add_bool( "audio-replay-gain-peak-protection", true,
AUDIO_REPLAY_GAIN_PEAK_PROTECTION_TEXT, AUDIO_REPLAY_GAIN_PEAK_PROTECTION_LONGTEXT )
- add_bool( "audio-time-stretch", true,
- AUDIO_TIME_STRETCH_TEXT, AUDIO_TIME_STRETCH_LONGTEXT )
-
set_subcategory( SUBCAT_AUDIO_AOUT )
add_module("aout", "audio output", "any", AOUT_TEXT, AOUT_LONGTEXT)
change_short('A')
=====================================
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
=====================================
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',
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3435039ab4b4687441774d949d0c7fbd5935dac6...f4abe288bb35e04e9540cfe1978d7132b5e23b53
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3435039ab4b4687441774d949d0c7fbd5935dac6...f4abe288bb35e04e9540cfe1978d7132b5e23b53
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list