[vlc-commits] [Git][videolan/vlc][master] 5 commits: audio_filter: extract shared dynamics core from compressor module
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sun May 3 06:19:12 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
8d0d1a4f by Brandon Li at 2026-05-03T08:03:02+02:00
audio_filter: extract shared dynamics core from compressor module
- - - - -
0039cadb by Brandon Li at 2026-05-03T08:03:02+02:00
audio_filter: add expander module
- - - - -
72980d67 by Brandon Li at 2026-05-03T08:03:02+02:00
audio_filter: add peak limiter module
- - - - -
c859a6a5 by Brandon Li at 2026-05-03T08:03:02+02:00
qt: add expander tab to extended audio dialog
- - - - -
d86f220f by Brandon Li at 2026-05-03T08:03:02+02:00
qt: add limiter tab to extended audio dialog
- - - - -
11 changed files:
- modules/audio_filter/Makefile.am
- modules/audio_filter/compressor.c
- + modules/audio_filter/dynamics.c
- + modules/audio_filter/dynamics.h
- + modules/audio_filter/expander.c
- + modules/audio_filter/limiter.c
- modules/audio_filter/meson.build
- modules/gui/qt/dialogs/extended/extended.cpp
- modules/gui/qt/dialogs/extended/extended_panels.cpp
- modules/gui/qt/dialogs/extended/extended_panels.hpp
- po/POTFILES.in
Changes:
=====================================
modules/audio_filter/Makefile.am
=====================================
@@ -15,8 +15,20 @@ libaudiobargraph_a_plugin_la_SOURCES = audio_filter/audiobargraph_a.c
libaudiobargraph_a_plugin_la_LIBADD = $(LIBM)
libchorus_flanger_plugin_la_SOURCES = audio_filter/chorus_flanger.c
libchorus_flanger_plugin_la_LIBADD = $(LIBM)
-libcompressor_plugin_la_SOURCES = audio_filter/compressor.c
-libcompressor_plugin_la_LIBADD = $(LIBM)
+# Shared dynamics core
+libdynamics_common_la_SOURCES = audio_filter/dynamics.c \
+ audio_filter/dynamics.h
+libdynamics_common_la_LDFLAGS = -static
+noinst_LTLIBRARIES += libdynamics_common.la
+libcompressor_plugin_la_SOURCES = audio_filter/compressor.c \
+ audio_filter/dynamics.h
+libcompressor_plugin_la_LIBADD = libdynamics_common.la $(LIBM)
+libexpander_plugin_la_SOURCES = audio_filter/expander.c \
+ audio_filter/dynamics.h
+libexpander_plugin_la_LIBADD = libdynamics_common.la $(LIBM)
+liblimiter_plugin_la_SOURCES = audio_filter/limiter.c \
+ audio_filter/dynamics.h
+liblimiter_plugin_la_LIBADD = libdynamics_common.la $(LIBM)
libequalizer_plugin_la_SOURCES = audio_filter/equalizer.c \
audio_filter/equalizer_presets.h
libequalizer_plugin_la_LIBADD = $(LIBM)
@@ -55,6 +67,8 @@ audio_filter_PLUGINS += \
libaudiobargraph_a_plugin.la \
libchorus_flanger_plugin.la \
libcompressor_plugin.la \
+ libexpander_plugin.la \
+ liblimiter_plugin.la \
libequalizer_plugin.la \
libgate_plugin.la \
libkaraoke_plugin.la \
=====================================
modules/audio_filter/compressor.c
=====================================
@@ -6,6 +6,10 @@
* Author: Ronald Wright <logiconcepts819 at gmail.com>
* Original author: Steve Harris <steve at plugin.org.uk>
*
+ * Modified by Brandon Li <brandonli2006ma at gmail.com>, 2026
+ * - Renamed file from compressor.c to dynamics.c
+ * - Turned into shared static library for other audio modules
+ *
* 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
@@ -21,707 +25,59 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <math.h>
-#include <stdint.h>
-
#include <vlc_common.h>
#include <vlc_plugin.h>
-#include <vlc_aout.h>
-#include <vlc_filter.h>
-
-/*****************************************************************************
-* Local prototypes.
-*****************************************************************************/
+#include "dynamics.h"
-#define A_TBL (256)
-
-#define DB_TABLE_SIZE (1024)
-#define DB_MIN (-60.0f)
-#define DB_MAX (24.0f)
-#define LIN_TABLE_SIZE (1024)
-#define LIN_MIN (0.0000000002f)
-#define LIN_MAX (9.0f)
-#define DB_DEFAULT_CUBE
-#define RMS_BUF_SIZE (960)
-#define LOOKAHEAD_SIZE ((RMS_BUF_SIZE)<<1)
-
-#define LIN_INTERP(f,a,b) ((a) + (f) * ( (b) - (a) ))
-#define LIMIT(v,l,u) (v < l ? l : ( v > u ? u : v ))
-
-typedef struct
+static float CompressorGain( float f_env,
+ float f_threshold, float f_knee, float f_rs,
+ float f_kn_lo, float f_kn_hi,
+ filter_sys_t *p_sys )
{
- float pf_buf[RMS_BUF_SIZE];
- unsigned int i_pos;
- unsigned int i_count;
- float f_sum;
+ if( f_env <= f_kn_lo )
+ return 1.0f;
-} rms_env;
-
-typedef struct
-{
- struct
+ if( f_env < f_kn_hi )
{
- float pf_vals[AOUT_CHAN_MAX];
- float f_lev_in;
-
- } p_buf[LOOKAHEAD_SIZE];
- unsigned int i_pos;
- unsigned int i_count;
-
-} lookahead;
-
-typedef struct
-{
- float f_amp;
- float pf_as[A_TBL];
- unsigned int i_count;
- float f_env;
- float f_env_peak;
- float f_env_rms;
- float f_gain;
- float f_gain_out;
- rms_env rms;
- float f_sum;
- lookahead la;
-
- float pf_db_data[DB_TABLE_SIZE];
- float pf_lin_data[LIN_TABLE_SIZE];
+ const float f_x = -( f_threshold - f_knee - vlc_dynamics_Lin2Db( f_env, p_sys ) ) / f_knee;
+ return vlc_dynamics_Db2Lin( -f_knee * f_rs * f_x * f_x * 0.25f, p_sys );
+ }
- vlc_mutex_t lock;
+ return vlc_dynamics_Db2Lin( ( f_threshold - vlc_dynamics_Lin2Db( f_env, p_sys ) ) * f_rs, p_sys );
+}
- float f_rms_peak;
- float f_attack;
- float f_release;
- float f_threshold;
- float f_ratio;
- float f_knee;
- float f_makeup_gain;
-} filter_sys_t;
+static const vlc_dynamics_varnames_t compressor_varnames = {
+ .rms_peak = "compressor-rms-peak",
+ .attack = "compressor-attack",
+ .release = "compressor-release",
+ .threshold = "compressor-threshold",
+ .ratio = "compressor-ratio",
+ .knee = "compressor-knee",
+ .makeup_gain = "compressor-makeup-gain",
+};
-typedef union
+static int Open( vlc_object_t *p_this )
{
- float f;
- int32_t i;
-
-} ls_pcast32;
-
-static int Open ( vlc_object_t * );
-static void Close ( filter_t * );
-static block_t *DoWork ( filter_t *, block_t * );
-
-static void DbInit ( filter_sys_t * );
-static float Db2Lin ( float, filter_sys_t * );
-static float Lin2Db ( float, filter_sys_t * );
-#ifdef DB_DEFAULT_CUBE
-static float CubeInterp ( const float, const float, const float,
- const float, const float );
-#endif
-static void RoundToZero ( float * );
-static float Max ( float, float );
-static float Clamp ( float, float, float );
-static int Round ( float );
-static float RmsEnvProcess ( rms_env *, const float );
-static void BufferProcess ( float *, int, float, float, lookahead * );
-
-static int RMSPeakCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int AttackCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int ReleaseCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int ThresholdCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int RatioCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int KneeCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-static int MakeupGainCallback ( vlc_object_t *, char const *, vlc_value_t,
- vlc_value_t, void * );
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-
-#define RMS_PEAK_TEXT N_( "RMS/peak" )
-#define RMS_PEAK_LONGTEXT N_( "Set the RMS/peak." )
-
-#define ATTACK_TEXT N_( "Attack time" )
-#define ATTACK_LONGTEXT N_( "Set the attack time in milliseconds." )
-
-#define RELEASE_TEXT N_( "Release time" )
-#define RELEASE_LONGTEXT N_( "Set the release time in milliseconds." )
-
-#define THRESHOLD_TEXT N_( "Threshold level" )
-#define THRESHOLD_LONGTEXT N_( "Set the threshold level in dB." )
-
-#define RATIO_TEXT N_( "Ratio" )
-#define RATIO_LONGTEXT N_( "Set the ratio (n:1)." )
-
-#define KNEE_TEXT N_( "Knee radius" )
-#define KNEE_LONGTEXT N_( "Set the knee radius in dB." )
-
-#define MAKEUP_GAIN_TEXT N_( "Makeup gain" )
-#define MAKEUP_GAIN_LONGTEXT N_( "Set the makeup gain in dB (0 ... 24)." )
+ return vlc_dynamics_OpenCommon( (filter_t*)p_this, &compressor_varnames, CompressorGain, -30.0f );
+}
vlc_module_begin()
set_shortname( N_("Compressor") )
set_description( N_("Dynamic range compressor") )
set_capability( "audio filter", 0 )
set_subcategory( SUBCAT_AUDIO_AFILTER )
-
- add_float_with_range( "compressor-rms-peak", 0.2, 0.0, 1.0,
- RMS_PEAK_TEXT, RMS_PEAK_LONGTEXT )
- add_float_with_range( "compressor-attack", 25.0, 1.5, 400.0,
- ATTACK_TEXT, ATTACK_LONGTEXT )
- add_float_with_range( "compressor-release", 100.0, 2.0, 800.0,
- RELEASE_TEXT, RELEASE_LONGTEXT )
- add_float_with_range( "compressor-threshold", -11.0, -30.0, 0.0,
- THRESHOLD_TEXT, THRESHOLD_LONGTEXT )
- add_float_with_range( "compressor-ratio", 4.0, 1.0, 20.0,
- RATIO_TEXT, RATIO_LONGTEXT )
- add_float_with_range( "compressor-knee", 5.0, 1.0, 10.0,
- KNEE_TEXT, KNEE_LONGTEXT )
- add_float_with_range( "compressor-makeup-gain", 7.0, 0.0, 24.0,
- MAKEUP_GAIN_TEXT, MAKEUP_GAIN_LONGTEXT )
+ add_float_with_range( compressor_varnames.rms_peak, 0.2, 0.0, 1.0, RMS_PEAK_TEXT, RMS_PEAK_LONGTEXT )
+ add_float_with_range( compressor_varnames.attack, 25.0, 1.5, 400.0, ATTACK_TEXT, ATTACK_LONGTEXT )
+ add_float_with_range( compressor_varnames.release, 100.0, 2.0, 800.0, RELEASE_TEXT, RELEASE_LONGTEXT )
+ add_float_with_range( compressor_varnames.threshold, -11.0, -30.0, 0.0, THRESHOLD_TEXT, THRESHOLD_LONGTEXT )
+ add_float_with_range( compressor_varnames.ratio, 4.0, 1.0, 20.0, RATIO_TEXT, RATIO_LONGTEXT )
+ add_float_with_range( compressor_varnames.knee, 5.0, 1.0, 10.0, KNEE_TEXT, KNEE_LONGTEXT )
+ add_float_with_range( compressor_varnames.makeup_gain, 7.0, 0.0, 24.0, MAKEUP_GAIN_TEXT, MAKEUP_GAIN_LONGTEXT )
set_callback( Open )
add_shortcut( "compressor" )
-vlc_module_end ()
-
-/*****************************************************************************
- * Open: initialize interface
- *****************************************************************************/
-
-static int Open( vlc_object_t *p_this )
-{
- filter_t *p_filter = (filter_t*)p_this;
- vlc_object_t *p_aout = vlc_object_parent(p_filter);
- float f_sample_rate = p_filter->fmt_in.audio.i_rate;
- float f_num;
-
- /* Initialize the filter parameter structure */
- filter_sys_t *p_sys = p_filter->p_sys = calloc( 1, sizeof(*p_sys) );
- if( !p_sys )
- {
- return VLC_ENOMEM;
- }
-
- /* Initialize the attack lookup table */
- p_sys->pf_as[0] = 1.0f;
- for( int i = 1; i < A_TBL; i++ )
- {
- p_sys->pf_as[i] = expf( -1.0f / ( f_sample_rate * i / A_TBL ) );
- }
-
- /* Calculate the RMS and lookahead sizes from the sample rate */
- f_num = 0.01f * f_sample_rate;
- p_sys->rms.i_count = Round( Clamp( 0.5f * f_num, 1.0f, RMS_BUF_SIZE ) );
- p_sys->la.i_count = Round( Clamp( f_num, 1.0f, LOOKAHEAD_SIZE ) );
-
- /* Initialize decibel lookup tables */
- DbInit( p_sys );
-
- /* Restore the last saved settings */
- p_sys->f_rms_peak = var_CreateGetFloat( p_aout, "compressor-rms-peak" );
- p_sys->f_attack = var_CreateGetFloat( p_aout, "compressor-attack" );
- p_sys->f_release = var_CreateGetFloat( p_aout, "compressor-release" );
- p_sys->f_threshold = var_CreateGetFloat( p_aout, "compressor-threshold" );
- p_sys->f_ratio = var_CreateGetFloat( p_aout, "compressor-ratio" );
- p_sys->f_knee = var_CreateGetFloat( p_aout, "compressor-knee" );
- p_sys->f_makeup_gain =
- var_CreateGetFloat( p_aout, "compressor-makeup-gain" );
-
- /* Initialize the mutex */
- vlc_mutex_init( &p_sys->lock );
-
- /* Add our own callbacks */
- var_AddCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
- var_AddCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
- var_AddCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
- var_AddCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
- var_AddCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
- var_AddCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
- var_AddCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
-
- /* Set the filter function */
- p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
- aout_FormatPrepare(&p_filter->fmt_in.audio);
- p_filter->fmt_out.audio = p_filter->fmt_in.audio;
-
- static const struct vlc_filter_operations filter_ops =
- {
- .filter_audio = DoWork, .close = Close,
- };
- p_filter->ops = &filter_ops;
-
- /* At this stage, we are ready! */
- msg_Dbg( p_filter, "compressor successfully initialized" );
- return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * Close: destroy interface
- *****************************************************************************/
-
-static void Close( filter_t *p_filter )
-{
- vlc_object_t *p_aout = vlc_object_parent(p_filter);
- filter_sys_t *p_sys = p_filter->p_sys;
-
- /* Remove our callbacks */
- var_DelCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
- var_DelCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
- var_DelCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
- var_DelCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
- var_DelCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
- var_DelCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
- var_DelCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
-
- /* Destroy the filter parameter structure */
- free( p_sys );
-}
-
-/*****************************************************************************
- * DoWork: process samples buffer
- *****************************************************************************/
-
-static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
-{
- int i_samples = p_in_buf->i_nb_samples;
- int i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
- float *pf_buf = (float*)p_in_buf->p_buffer;
-
- /* Current parameters */
- filter_sys_t *p_sys = p_filter->p_sys;
-
- /* Fetch the configurable parameters */
- vlc_mutex_lock( &p_sys->lock );
-
- float f_rms_peak = p_sys->f_rms_peak; /* RMS/peak */
- float f_attack = p_sys->f_attack; /* Attack time (ms) */
- float f_release = p_sys->f_release; /* Release time (ms) */
- float f_threshold = p_sys->f_threshold; /* Threshold level (dB) */
- float f_ratio = p_sys->f_ratio; /* Ratio (n:1) */
- float f_knee = p_sys->f_knee; /* Knee radius (dB) */
- float f_makeup_gain = p_sys->f_makeup_gain; /* Makeup gain (dB) */
-
- vlc_mutex_unlock( &p_sys->lock );
-
- /* Fetch the internal parameters */
- float f_amp = p_sys->f_amp;
- float *pf_as = p_sys->pf_as;
- float f_env = p_sys->f_env;
- float f_env_peak = p_sys->f_env_peak;
- float f_env_rms = p_sys->f_env_rms;
- float f_gain = p_sys->f_gain;
- float f_gain_out = p_sys->f_gain_out;
- rms_env *p_rms = &p_sys->rms;
- float f_sum = p_sys->f_sum;
- lookahead *p_la = &p_sys->la;
-
- /* Prepare other compressor parameters */
- float f_ga = f_attack < 2.0f ? 0.0f :
- pf_as[Round( f_attack * 0.001f * ( A_TBL - 1 ) )];
- float f_gr = pf_as[Round( f_release * 0.001f * ( A_TBL - 1 ) )];
- float f_rs = ( f_ratio - 1.0f ) / f_ratio;
- float f_mug = Db2Lin( f_makeup_gain, p_sys );
- float f_knee_min = Db2Lin( f_threshold - f_knee, p_sys );
- float f_knee_max = Db2Lin( f_threshold + f_knee, p_sys );
- float f_ef_a = f_ga * 0.25f;
- float f_ef_ai = 1.0f - f_ef_a;
-
- /* Process the current buffer */
- for( int i = 0; i < i_samples; i++ )
- {
- float f_lev_in_old, f_lev_in_new;
-
- /* Now, compress the pre-equalized audio (ported from sc4_1882
- * plugin with a few modifications) */
-
- /* Fetch the old delayed buffer value */
- f_lev_in_old = p_la->p_buf[p_la->i_pos].f_lev_in;
-
- /* Find the peak value of current sample. This becomes the new delayed
- * buffer value that replaces the old one in the lookahead array */
- f_lev_in_new = fabs( pf_buf[0] );
- for( int i_chan = 1; i_chan < i_channels; i_chan++ )
- {
- f_lev_in_new = Max( f_lev_in_new, fabs( pf_buf[i_chan] ) );
- }
- p_la->p_buf[p_la->i_pos].f_lev_in = f_lev_in_new;
-
- /* Add the square of the peak value to a running sum */
- f_sum += f_lev_in_new * f_lev_in_new;
-
- /* Update the RMS envelope */
- if( f_amp > f_env_rms )
- {
- f_env_rms = f_env_rms * f_ga + f_amp * ( 1.0f - f_ga );
- }
- else
- {
- f_env_rms = f_env_rms * f_gr + f_amp * ( 1.0f - f_gr );
- }
- RoundToZero( &f_env_rms );
-
- /* Update the peak envelope */
- if( f_lev_in_old > f_env_peak )
- {
- f_env_peak = f_env_peak * f_ga + f_lev_in_old * ( 1.0f - f_ga );
- }
- else
- {
- f_env_peak = f_env_peak * f_gr + f_lev_in_old * ( 1.0f - f_gr );
- }
- RoundToZero( &f_env_peak );
-
- /* Process the RMS value and update the output gain every 4 samples */
- if( ( p_sys->i_count++ & 3 ) == 3 )
- {
- /* Process the RMS value by placing in the mean square value, and
- * reset the running sum */
- f_amp = RmsEnvProcess( p_rms, f_sum * 0.25f );
- f_sum = 0.0f;
- if( isnan( f_env_rms ) )
- {
- /* This can happen sometimes, but I don't know why. */
- f_env_rms = 0.0f;
- }
-
- /* Find the superposition of the RMS and peak envelopes */
- f_env = LIN_INTERP( f_rms_peak, f_env_rms, f_env_peak );
-
- /* Update the output gain */
- if( f_env <= f_knee_min )
- {
- /* Gain below the knee (and below the threshold) */
- f_gain_out = 1.0f;
- }
- else if( f_env < f_knee_max )
- {
- /* Gain within the knee */
- const float f_x = -( f_threshold
- - f_knee - Lin2Db( f_env, p_sys ) ) / f_knee;
- f_gain_out = Db2Lin( -f_knee * f_rs * f_x * f_x * 0.25f,
- p_sys );
- }
- else
- {
- /* Gain above the knee (and above the threshold) */
- f_gain_out = Db2Lin( ( f_threshold - Lin2Db( f_env, p_sys ) )
- * f_rs, p_sys );
- }
- }
-
- /* Find the total gain */
- f_gain = f_gain * f_ef_a + f_gain_out * f_ef_ai;
-
- /* Write the resulting buffer to the output */
- BufferProcess( pf_buf, i_channels, f_gain, f_mug, p_la );
- pf_buf += i_channels;
- }
-
- /* Update the internal parameters */
- p_sys->f_sum = f_sum;
- p_sys->f_amp = f_amp;
- p_sys->f_gain = f_gain;
- p_sys->f_gain_out = f_gain_out;
- p_sys->f_env = f_env;
- p_sys->f_env_rms = f_env_rms;
- p_sys->f_env_peak = f_env_peak;
-
- return p_in_buf;
-}
-
-/*****************************************************************************
- * Helper functions for compressor
- *****************************************************************************/
-
-static void DbInit( filter_sys_t * p_sys )
-{
- float *pf_lin_data = p_sys->pf_lin_data;
- float *pf_db_data = p_sys->pf_db_data;
-
- /* Fill linear lookup table */
- for( int i = 0; i < LIN_TABLE_SIZE; i++ )
- {
- pf_lin_data[i] = powf( 10.0f, ( ( DB_MAX - DB_MIN ) *
- (float)i / LIN_TABLE_SIZE + DB_MIN ) / 20.0f );
- }
-
- /* Fill logarithmic lookup table */
- for( int i = 0; i < DB_TABLE_SIZE; i++ )
- {
- pf_db_data[i] = 20.0f * log10f( ( LIN_MAX - LIN_MIN ) *
- (float)i / DB_TABLE_SIZE + LIN_MIN );
- }
-}
-
-static float Db2Lin( float f_db, filter_sys_t * p_sys )
-{
- float f_scale = ( f_db - DB_MIN ) * LIN_TABLE_SIZE / ( DB_MAX - DB_MIN );
- int i_base = Round( f_scale - 0.5f );
- float f_ofs = f_scale - i_base;
- float *pf_lin_data = p_sys->pf_lin_data;
-
- if( i_base < 1 )
- {
- return 0.0f;
- }
- else if( i_base > LIN_TABLE_SIZE - 3 )
- {
- return pf_lin_data[LIN_TABLE_SIZE - 2];
- }
-
-#ifdef DB_DEFAULT_CUBE
- return CubeInterp( f_ofs, pf_lin_data[i_base - 1],
- pf_lin_data[i_base],
- pf_lin_data[i_base + 1],
- pf_lin_data[i_base + 2] );
-#else
- return ( 1.0f - f_ofs ) * pf_lin_data[i_base]
- + f_ofs * pf_lin_data[i_base + 1];
-#endif
-}
-
-static float Lin2Db( float f_lin, filter_sys_t * p_sys )
-{
- float f_scale = ( f_lin - LIN_MIN ) * DB_TABLE_SIZE / ( LIN_MAX - LIN_MIN );
- int i_base = Round( f_scale - 0.5f );
- float f_ofs = f_scale - i_base;
- float *pf_db_data = p_sys->pf_db_data;
-
- if( i_base < 2 )
- {
- return pf_db_data[2] * f_scale * 0.5f - 23.0f * ( 2.0f - f_scale );
- }
- else if( i_base > DB_TABLE_SIZE - 3 )
- {
- return pf_db_data[DB_TABLE_SIZE - 2];
- }
-
-#ifdef DB_DEFAULT_CUBE
- return CubeInterp( f_ofs, pf_db_data[i_base - 1],
- pf_db_data[i_base],
- pf_db_data[i_base + 1],
- pf_db_data[i_base + 2] );
-#else
- return ( 1.0f - f_ofs ) * pf_db_data[i_base]
- + f_ofs * pf_db_data[i_base + 1];
-#endif
-}
-
-#ifdef DB_DEFAULT_CUBE
-/* Cubic interpolation function */
-static float CubeInterp( const float f_fr, const float f_inm1,
- const float f_in,
- const float f_inp1,
- const float f_inp2 )
-{
- return f_in + 0.5f * f_fr * ( f_inp1 - f_inm1 +
- f_fr * ( 4.0f * f_inp1 + 2.0f * f_inm1 - 5.0f * f_in - f_inp2 +
- f_fr * ( 3.0f * ( f_in - f_inp1 ) - f_inm1 + f_inp2 ) ) );
-}
-#endif
-
-/* Zero out denormals by adding and subtracting a small number, from Laurent
- * de Soras */
-static void RoundToZero( float *pf_x )
-{
- static const float f_anti_denormal = 1e-18;
-
- *pf_x += f_anti_denormal;
- *pf_x -= f_anti_denormal;
-}
-
-/* A set of branchless clipping operations from Laurent de Soras */
-
-static float Max( float f_x, float f_a )
-{
- f_x -= f_a;
- f_x += fabsf( f_x );
- f_x *= 0.5f;
- f_x += f_a;
-
- return f_x;
-}
-
-static float Clamp( float f_x, float f_a, float f_b )
-{
- const float f_x1 = fabsf( f_x - f_a );
- const float f_x2 = fabsf( f_x - f_b );
-
- f_x = f_x1 + f_a + f_b;
- f_x -= f_x2;
- f_x *= 0.5f;
-
- return f_x;
-}
-
-/* Round float to int using IEEE int* hack */
-static int Round( float f_x )
-{
- ls_pcast32 p;
-
- p.f = f_x;
- p.f += ( 3 << 22 );
-
- return p.i - 0x4b400000;
-}
-
-/* Calculate current level from root-mean-squared of circular buffer ("RMS") */
-static float RmsEnvProcess( rms_env * p_r, const float f_x )
-{
- /* Remove the old term from the sum */
- p_r->f_sum -= p_r->pf_buf[p_r->i_pos];
-
- /* Add the new term to the sum */
- p_r->f_sum += f_x;
-
- /* If the sum is small enough, make it zero */
- if( p_r->f_sum < 1.0e-6f )
- {
- p_r->f_sum = 0.0f;
- }
-
- /* Replace the old term in the array with the new one */
- p_r->pf_buf[p_r->i_pos] = f_x;
-
- /* Go to the next position for the next RMS calculation */
- p_r->i_pos = ( p_r->i_pos + 1 ) % ( p_r->i_count );
-
- /* Return the RMS value */
- return sqrt( p_r->f_sum / p_r->i_count );
-}
-
-/* Output the compressed delayed buffer and store the current buffer. Uses a
- * circular array, just like the one used in calculating the RMS of the buffer
- */
-static void BufferProcess( float * pf_buf, int i_channels, float f_gain,
- float f_mug, lookahead * p_la )
-{
- /* Loop through every channel */
- for( int i_chan = 0; i_chan < i_channels; i_chan++ )
- {
- float f_x = pf_buf[i_chan]; /* Current buffer value */
-
- /* Output the compressed delayed buffer value */
- pf_buf[i_chan] = p_la->p_buf[p_la->i_pos].pf_vals[i_chan]
- * f_gain * f_mug;
-
- /* Update the delayed buffer value */
- p_la->p_buf[p_la->i_pos].pf_vals[i_chan] = f_x;
- }
-
- /* Go to the next delayed buffer value for the next run */
- p_la->i_pos = ( p_la->i_pos + 1 ) % ( p_la->i_count );
-}
-
-/*****************************************************************************
- * Callback functions
- *****************************************************************************/
-static int RMSPeakCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_rms_peak = Clamp( newval.f_float, 0.0f, 1.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int AttackCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_attack = Clamp( newval.f_float, 1.5f, 400.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int ReleaseCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_release = Clamp( newval.f_float, 2.0f, 800.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int ThresholdCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_threshold = Clamp( newval.f_float, -30.0f, 0.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int RatioCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_ratio = Clamp( newval.f_float, 1.0f, 20.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int KneeCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_knee = Clamp( newval.f_float, 1.0f, 10.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
-
-static int MakeupGainCallback( vlc_object_t *p_this, char const *psz_cmd,
- vlc_value_t oldval, vlc_value_t newval,
- void * p_data )
-{
- VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
- filter_sys_t *p_sys = p_data;
-
- vlc_mutex_lock( &p_sys->lock );
- p_sys->f_makeup_gain = Clamp( newval.f_float, 0.0f, 24.0f );
- vlc_mutex_unlock( &p_sys->lock );
-
- return VLC_SUCCESS;
-}
+vlc_module_end()
=====================================
modules/audio_filter/dynamics.c
=====================================
@@ -0,0 +1,630 @@
+/*****************************************************************************
+ * dynamics.c: shared core for the dynamic range compressor, expander, and
+ * limiter modules. Ported from plugins from LADSPA SWH.
+ *****************************************************************************
+ * Copyright (C) 2010 Ronald Wright
+ *
+ * Author: Ronald Wright <logiconcepts819 at gmail.com>
+ * Original author: Steve Harris <steve at plugin.org.uk>
+ *
+ * Modified by Brandon Li <brandonli2006ma at gmail.com>, 2026
+ * - Renamed file from compressor.c to dynamics.c
+ * - Turned into shared static library for other audio modules
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <stdint.h>
+
+#include <vlc_common.h>
+#include <vlc_aout.h>
+#include <vlc_filter.h>
+
+#include "dynamics.h"
+
+#define A_TBL (256)
+#define DB_TABLE_SIZE (1024)
+#define DB_MIN (-60.0f)
+#define DB_MAX (24.0f)
+#define LIN_TABLE_SIZE (1024)
+#define LIN_MIN (0.0000000002f)
+#define LIN_MAX (9.0f)
+#define DB_DEFAULT_CUBE
+#define RMS_BUF_SIZE (960)
+#define LOOKAHEAD_SIZE ((RMS_BUF_SIZE)<<1)
+
+#define LIN_INTERP(f,a,b) ((a) + (f) * ( (b) - (a) ))
+#define LIMIT(v,l,u) (v < l ? l : ( v > u ? u : v ))
+
+typedef struct
+{
+ float pf_buf[RMS_BUF_SIZE];
+ unsigned int i_pos;
+ unsigned int i_count;
+ float f_sum;
+
+} rms_env;
+
+typedef struct
+{
+ struct
+ {
+ float pf_vals[AOUT_CHAN_MAX];
+ float f_lev_in;
+
+ } p_buf[LOOKAHEAD_SIZE];
+ unsigned int i_pos;
+ unsigned int i_count;
+
+} lookahead;
+
+struct filter_sys
+{
+ float f_amp;
+ float pf_as[A_TBL];
+ unsigned int i_count;
+ float f_env;
+ float f_env_peak;
+ float f_env_rms;
+ float f_gain;
+ float f_gain_out;
+ rms_env rms;
+ float f_sum;
+ lookahead la;
+
+ float pf_db_data[DB_TABLE_SIZE];
+ float pf_lin_data[LIN_TABLE_SIZE];
+
+ vlc_mutex_t lock;
+
+ float f_rms_peak;
+ float f_attack;
+ float f_release;
+ float f_threshold;
+ float f_ratio;
+ float f_knee;
+ float f_makeup_gain;
+
+ gain_fn_t pf_gain;
+ float f_threshold_min;
+ const vlc_dynamics_varnames_t *p_varnames;
+};
+
+typedef union
+{
+ float f;
+ int32_t i;
+
+} ls_pcast32;
+
+/*****************************************************************************
+ * Helper functions
+ *****************************************************************************/
+
+/* A set of branchless clipping operations from Laurent de Soras */
+
+static float Max( float f_x, float f_a )
+{
+ f_x -= f_a;
+ f_x += fabsf( f_x );
+ f_x *= 0.5f;
+ f_x += f_a;
+
+ return f_x;
+}
+
+static float Clamp( float f_x, float f_a, float f_b )
+{
+ const float f_x1 = fabsf( f_x - f_a );
+ const float f_x2 = fabsf( f_x - f_b );
+
+ f_x = f_x1 + f_a + f_b;
+ f_x -= f_x2;
+ f_x *= 0.5f;
+
+ return f_x;
+}
+
+/* Round float to int using IEEE int* hack */
+static int Round( float f_x )
+{
+ ls_pcast32 p;
+
+ p.f = f_x;
+ p.f += ( 3 << 22 );
+
+ return p.i - 0x4b400000;
+}
+
+/* Zero out denormals by adding and subtracting a small number, from Laurent
+ * de Soras */
+static void RoundToZero( float *pf_x )
+{
+ static const float f_anti_denormal = 1e-18;
+
+ *pf_x += f_anti_denormal;
+ *pf_x -= f_anti_denormal;
+}
+
+#ifdef DB_DEFAULT_CUBE
+/* Cubic interpolation function */
+static float CubeInterp( const float f_fr, const float f_inm1,
+ const float f_in,
+ const float f_inp1,
+ const float f_inp2 )
+{
+ return f_in + 0.5f * f_fr * ( f_inp1 - f_inm1 +
+ f_fr * ( 4.0f * f_inp1 + 2.0f * f_inm1 - 5.0f * f_in - f_inp2 +
+ f_fr * ( 3.0f * ( f_in - f_inp1 ) - f_inm1 + f_inp2 ) ) );
+}
+#endif
+
+static void DbInit( filter_sys_t * p_sys )
+{
+ float *pf_lin_data = p_sys->pf_lin_data;
+ float *pf_db_data = p_sys->pf_db_data;
+
+ /* Fill linear lookup table */
+ for( int i = 0; i < LIN_TABLE_SIZE; i++ )
+ {
+ pf_lin_data[i] = powf( 10.0f, ( ( DB_MAX - DB_MIN ) *
+ (float)i / LIN_TABLE_SIZE + DB_MIN ) / 20.0f );
+ }
+
+ /* Fill logarithmic lookup table */
+ for( int i = 0; i < DB_TABLE_SIZE; i++ )
+ {
+ pf_db_data[i] = 20.0f * log10f( ( LIN_MAX - LIN_MIN ) *
+ (float)i / DB_TABLE_SIZE + LIN_MIN );
+ }
+}
+
+float vlc_dynamics_Db2Lin( float f_db, filter_sys_t * p_sys )
+{
+ float f_scale = ( f_db - DB_MIN ) * LIN_TABLE_SIZE / ( DB_MAX - DB_MIN );
+ int i_base = Round( f_scale - 0.5f );
+ float f_ofs = f_scale - i_base;
+ float *pf_lin_data = p_sys->pf_lin_data;
+
+ if( i_base < 1 )
+ {
+ return 0.0f;
+ }
+ else if( i_base > LIN_TABLE_SIZE - 3 )
+ {
+ return pf_lin_data[LIN_TABLE_SIZE - 2];
+ }
+
+#ifdef DB_DEFAULT_CUBE
+ return CubeInterp( f_ofs, pf_lin_data[i_base - 1],
+ pf_lin_data[i_base],
+ pf_lin_data[i_base + 1],
+ pf_lin_data[i_base + 2] );
+#else
+ return ( 1.0f - f_ofs ) * pf_lin_data[i_base]
+ + f_ofs * pf_lin_data[i_base + 1];
+#endif
+}
+
+float vlc_dynamics_Lin2Db( float f_lin, filter_sys_t * p_sys )
+{
+ float f_scale = ( f_lin - LIN_MIN ) * DB_TABLE_SIZE / ( LIN_MAX - LIN_MIN );
+ int i_base = Round( f_scale - 0.5f );
+ float f_ofs = f_scale - i_base;
+ float *pf_db_data = p_sys->pf_db_data;
+
+ if( i_base < 2 )
+ {
+ return pf_db_data[2] * f_scale * 0.5f - 23.0f * ( 2.0f - f_scale );
+ }
+ else if( i_base > DB_TABLE_SIZE - 3 )
+ {
+ return pf_db_data[DB_TABLE_SIZE - 2];
+ }
+
+#ifdef DB_DEFAULT_CUBE
+ return CubeInterp( f_ofs, pf_db_data[i_base - 1],
+ pf_db_data[i_base],
+ pf_db_data[i_base + 1],
+ pf_db_data[i_base + 2] );
+#else
+ return ( 1.0f - f_ofs ) * pf_db_data[i_base]
+ + f_ofs * pf_db_data[i_base + 1];
+#endif
+}
+
+/* Calculate current level from root-mean-squared of circular buffer ("RMS") */
+static float RmsEnvProcess( rms_env * p_r, const float f_x )
+{
+ /* Remove the old term from the sum */
+ p_r->f_sum -= p_r->pf_buf[p_r->i_pos];
+
+ /* Add the new term to the sum */
+ p_r->f_sum += f_x;
+
+ /* If the sum is small enough, make it zero */
+ if( p_r->f_sum < 1.0e-6f )
+ {
+ p_r->f_sum = 0.0f;
+ }
+
+ /* Replace the old term in the array with the new one */
+ p_r->pf_buf[p_r->i_pos] = f_x;
+
+ /* Go to the next position for the next RMS calculation */
+ p_r->i_pos = ( p_r->i_pos + 1 ) % ( p_r->i_count );
+
+ /* Return the RMS value */
+ return sqrt( p_r->f_sum / p_r->i_count );
+}
+
+/* Output the compressed delayed buffer and store the current buffer. Uses a
+ * circular array, just like the one used in calculating the RMS of the buffer
+ */
+static void BufferProcess( float * pf_buf, int i_channels, float f_gain,
+ float f_mug, lookahead * p_la )
+{
+ /* Loop through every channel */
+ for( int i_chan = 0; i_chan < i_channels; i_chan++ )
+ {
+ float f_x = pf_buf[i_chan]; /* Current buffer value */
+
+ /* Output the compressed delayed buffer value */
+ pf_buf[i_chan] = p_la->p_buf[p_la->i_pos].pf_vals[i_chan]
+ * f_gain * f_mug;
+
+ /* Update the delayed buffer value */
+ p_la->p_buf[p_la->i_pos].pf_vals[i_chan] = f_x;
+ }
+
+ /* Go to the next delayed buffer value for the next run */
+ p_la->i_pos = ( p_la->i_pos + 1 ) % ( p_la->i_count );
+}
+
+/*****************************************************************************
+ * DoWork: process samples buffer
+ *****************************************************************************/
+
+static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
+{
+ int i_samples = p_in_buf->i_nb_samples;
+ int i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
+ float *pf_buf = (float*)p_in_buf->p_buffer;
+
+ /* Current parameters */
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ /* Fetch the configurable parameters */
+ vlc_mutex_lock( &p_sys->lock );
+
+ float f_rms_peak = p_sys->f_rms_peak; /* RMS/peak */
+ float f_attack = p_sys->f_attack; /* Attack time (ms) */
+ float f_release = p_sys->f_release; /* Release time (ms) */
+ float f_threshold = p_sys->f_threshold; /* Threshold level (dB) */
+ float f_ratio = p_sys->f_ratio; /* Ratio (n:1) */
+ float f_knee = p_sys->f_knee; /* Knee radius (dB) */
+ float f_makeup_gain = p_sys->f_makeup_gain; /* Makeup gain (dB) */
+
+ vlc_mutex_unlock( &p_sys->lock );
+
+ /* Fetch the internal parameters */
+ float f_amp = p_sys->f_amp;
+ float *pf_as = p_sys->pf_as;
+ float f_env = p_sys->f_env;
+ float f_env_peak = p_sys->f_env_peak;
+ float f_env_rms = p_sys->f_env_rms;
+ float f_gain = p_sys->f_gain;
+ float f_gain_out = p_sys->f_gain_out;
+ rms_env *p_rms = &p_sys->rms;
+ float f_sum = p_sys->f_sum;
+ lookahead *p_la = &p_sys->la;
+
+ /* Prepare other dynamics parameters */
+ float f_ga = f_attack < 2.0f ? 0.0f :
+ pf_as[Round( f_attack * 0.001f * ( A_TBL - 1 ) )];
+ float f_gr = pf_as[Round( f_release * 0.001f * ( A_TBL - 1 ) )];
+ float f_rs = ( f_ratio - 1.0f ) / f_ratio;
+ float f_mug = vlc_dynamics_Db2Lin( f_makeup_gain, p_sys );
+ float f_knee_min = vlc_dynamics_Db2Lin( f_threshold - f_knee, p_sys );
+ float f_knee_max = vlc_dynamics_Db2Lin( f_threshold + f_knee, p_sys );
+ float f_ef_a = f_ga * 0.25f;
+ float f_ef_ai = 1.0f - f_ef_a;
+
+ /* Process the current buffer */
+ for( int i = 0; i < i_samples; i++ )
+ {
+ float f_lev_in_old, f_lev_in_new;
+
+ /* Now, compress the pre-equalized audio (ported from sc4_1882
+ * plugin with a few modifications) */
+
+ /* Fetch the old delayed buffer value */
+ f_lev_in_old = p_la->p_buf[p_la->i_pos].f_lev_in;
+
+ /* Find the peak value of current sample. This becomes the new delayed
+ * buffer value that replaces the old one in the lookahead array */
+ f_lev_in_new = fabs( pf_buf[0] );
+ for( int i_chan = 1; i_chan < i_channels; i_chan++ )
+ {
+ f_lev_in_new = Max( f_lev_in_new, fabs( pf_buf[i_chan] ) );
+ }
+ p_la->p_buf[p_la->i_pos].f_lev_in = f_lev_in_new;
+
+ /* Add the square of the peak value to a running sum */
+ f_sum += f_lev_in_new * f_lev_in_new;
+
+ /* Update the RMS envelope */
+ if( f_amp > f_env_rms )
+ {
+ f_env_rms = f_env_rms * f_ga + f_amp * ( 1.0f - f_ga );
+ }
+ else
+ {
+ f_env_rms = f_env_rms * f_gr + f_amp * ( 1.0f - f_gr );
+ }
+ RoundToZero( &f_env_rms );
+
+ /* Update the peak envelope */
+ if( f_lev_in_old > f_env_peak )
+ {
+ f_env_peak = f_env_peak * f_ga + f_lev_in_old * ( 1.0f - f_ga );
+ }
+ else
+ {
+ f_env_peak = f_env_peak * f_gr + f_lev_in_old * ( 1.0f - f_gr );
+ }
+ RoundToZero( &f_env_peak );
+
+ /* Process the RMS value and update the output gain every 4 samples */
+ if( ( p_sys->i_count++ & 3 ) == 3 )
+ {
+ /* Process the RMS value by placing in the mean square value, and
+ * reset the running sum */
+ f_amp = RmsEnvProcess( p_rms, f_sum * 0.25f );
+ f_sum = 0.0f;
+ if( isnan( f_env_rms ) )
+ {
+ /* This can happen sometimes, but I don't know why. */
+ f_env_rms = 0.0f;
+ }
+
+ /* Find the superposition of the RMS and peak envelopes */
+ f_env = LIN_INTERP( f_rms_peak, f_env_rms, f_env_peak );
+
+ /* Update the output gain via the module's gain curve */
+ f_gain_out = p_sys->pf_gain( f_env, f_threshold, f_knee, f_rs,
+ f_knee_min, f_knee_max, p_sys );
+ }
+
+ /* Find the total gain */
+ f_gain = f_gain * f_ef_a + f_gain_out * f_ef_ai;
+
+ /* Write the resulting buffer to the output */
+ BufferProcess( pf_buf, i_channels, f_gain, f_mug, p_la );
+ pf_buf += i_channels;
+ }
+
+ /* Update the internal parameters */
+ p_sys->f_sum = f_sum;
+ p_sys->f_amp = f_amp;
+ p_sys->f_gain = f_gain;
+ p_sys->f_gain_out = f_gain_out;
+ p_sys->f_env = f_env;
+ p_sys->f_env_rms = f_env_rms;
+ p_sys->f_env_peak = f_env_peak;
+
+ return p_in_buf;
+}
+
+/*****************************************************************************
+ * Callback functions
+ *****************************************************************************/
+
+static int RMSPeakCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_rms_peak = Clamp( newval.f_float, 0.0f, 1.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int AttackCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_attack = Clamp( newval.f_float, 1.5f, 400.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int ReleaseCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_release = Clamp( newval.f_float, 2.0f, 800.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int ThresholdCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_threshold = Clamp( newval.f_float, p_sys->f_threshold_min, 0.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int RatioCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_ratio = Clamp( newval.f_float, 1.0f, 20.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int KneeCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_knee = Clamp( newval.f_float, 1.0f, 10.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int MakeupGainCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+ filter_sys_t *p_sys = p_data;
+
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->f_makeup_gain = Clamp( newval.f_float, 0.0f, 24.0f );
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: destroy interface
+ *****************************************************************************/
+
+static void Close( filter_t *p_filter )
+{
+ vlc_object_t *p_aout = vlc_object_parent(p_filter);
+ filter_sys_t *p_sys = p_filter->p_sys;
+ const vlc_dynamics_varnames_t *p_varnames = p_sys->p_varnames;
+
+ var_DelCallback( p_aout, p_varnames->rms_peak, RMSPeakCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->attack, AttackCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->release, ReleaseCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->threshold, ThresholdCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->ratio, RatioCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->knee, KneeCallback, p_sys );
+ var_DelCallback( p_aout, p_varnames->makeup_gain, MakeupGainCallback, p_sys );
+
+ free( p_sys );
+}
+
+/*****************************************************************************
+ * vlc_dynamics_OpenCommon: shared filter init
+ *****************************************************************************/
+
+int vlc_dynamics_OpenCommon( filter_t *p_filter,
+ const vlc_dynamics_varnames_t *p_varnames,
+ gain_fn_t pf_gain, float f_threshold_min )
+{
+ vlc_object_t *p_aout = vlc_object_parent(p_filter);
+ float f_sample_rate = p_filter->fmt_in.audio.i_rate;
+ float f_num;
+
+ /* Initialize the filter parameter structure */
+ filter_sys_t *p_sys = p_filter->p_sys = calloc( 1, sizeof(*p_sys) );
+ if( !p_sys )
+ {
+ return VLC_ENOMEM;
+ }
+
+ p_sys->pf_gain = pf_gain;
+ p_sys->f_threshold_min = f_threshold_min;
+ p_sys->p_varnames = p_varnames;
+
+ /* Initialize the attack lookup table */
+ p_sys->pf_as[0] = 1.0f;
+ for( int i = 1; i < A_TBL; i++ )
+ {
+ p_sys->pf_as[i] = expf( -1.0f / ( f_sample_rate * i / A_TBL ) );
+ }
+
+ /* Calculate the RMS and lookahead sizes from the sample rate */
+ f_num = 0.01f * f_sample_rate;
+ p_sys->rms.i_count = Round( Clamp( 0.5f * f_num, 1.0f, RMS_BUF_SIZE ) );
+ p_sys->la.i_count = Round( Clamp( f_num, 1.0f, LOOKAHEAD_SIZE ) );
+
+ /* Initialize decibel lookup tables */
+ DbInit( p_sys );
+
+ /* Initialize the mutex */
+ vlc_mutex_init( &p_sys->lock );
+
+ /* Bind each configuration variable */
+#define BIND( field, name, cb ) \
+ p_sys->field = var_CreateGetFloat( p_aout, p_varnames->name ); \
+ var_AddCallback( p_aout, p_varnames->name, cb, p_sys );
+
+ BIND( f_rms_peak, rms_peak, RMSPeakCallback )
+ BIND( f_attack, attack, AttackCallback )
+ BIND( f_release, release, ReleaseCallback )
+ BIND( f_threshold, threshold, ThresholdCallback )
+ BIND( f_ratio, ratio, RatioCallback )
+ BIND( f_knee, knee, KneeCallback )
+ BIND( f_makeup_gain, makeup_gain, MakeupGainCallback )
+#undef BIND
+
+ /* Set the filter function */
+ p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
+ aout_FormatPrepare(&p_filter->fmt_in.audio);
+ p_filter->fmt_out.audio = p_filter->fmt_in.audio;
+
+ static const struct vlc_filter_operations filter_ops =
+ {
+ .filter_audio = DoWork, .close = Close,
+ };
+ p_filter->ops = &filter_ops;
+
+ return VLC_SUCCESS;
+}
=====================================
modules/audio_filter/dynamics.h
=====================================
@@ -0,0 +1,74 @@
+/*****************************************************************************
+ * dynamics.h: shared declarations for dynamic modules
+ *****************************************************************************
+ * Copyright (C) 2010 Ronald Wright
+ *
+ * Author: Ronald Wright <logiconcepts819 at gmail.com>
+ * Original author: Steve Harris <steve at plugin.org.uk>
+ *
+ * Modified by Brandon Li <brandonli2006ma at gmail.com>, 2026
+ * - Renamed file from compressor.c to dynamics.c
+ * - Turned into shared static library for other audio modules
+ *
+ * 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_AUDIO_FILTER_DYNAMICS_H_
+#define VLC_AUDIO_FILTER_DYNAMICS_H_
+
+#include <vlc_common.h>
+
+#define RMS_PEAK_TEXT N_( "RMS/peak" )
+#define RMS_PEAK_LONGTEXT N_( "Set the RMS/peak." )
+#define ATTACK_TEXT N_( "Attack time" )
+#define ATTACK_LONGTEXT N_( "Set the attack time in milliseconds." )
+#define RELEASE_TEXT N_( "Release time" )
+#define RELEASE_LONGTEXT N_( "Set the release time in milliseconds." )
+#define THRESHOLD_TEXT N_( "Threshold level" )
+#define THRESHOLD_LONGTEXT N_( "Set the threshold level in dB." )
+#define RATIO_TEXT N_( "Ratio" )
+#define RATIO_LONGTEXT N_( "Set the ratio (n:1)." )
+#define KNEE_TEXT N_( "Knee radius" )
+#define KNEE_LONGTEXT N_( "Set the knee radius in dB." )
+#define MAKEUP_GAIN_TEXT N_( "Makeup gain" )
+#define MAKEUP_GAIN_LONGTEXT N_( "Set the makeup gain in dB (0 ... 24)." )
+
+typedef struct filter_sys filter_sys_t;
+
+typedef float (*gain_fn_t)( float f_env,
+ float f_threshold, float f_knee, float f_rs,
+ float f_kn_lo, float f_kn_hi,
+ filter_sys_t *p_sys );
+
+typedef struct vlc_dynamics_varnames
+{
+ const char *rms_peak;
+ const char *attack;
+ const char *release;
+ const char *threshold;
+ const char *ratio;
+ const char *knee;
+ const char *makeup_gain;
+} vlc_dynamics_varnames_t;
+
+/* dB <-> linear conversion using the filter's internal lookup tables. */
+float vlc_dynamics_Db2Lin( float f_db, filter_sys_t *p_sys );
+float vlc_dynamics_Lin2Db( float f_lin, filter_sys_t *p_sys );
+
+int vlc_dynamics_OpenCommon( filter_t *p_filter,
+ const vlc_dynamics_varnames_t *p_varnames,
+ gain_fn_t pf_gain, float f_threshold_min );
+
+#endif /* VLC_AUDIO_FILTER_DYNAMICS_H_ */
=====================================
modules/audio_filter/expander.c
=====================================
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * expander.c: Dynamic range expander module
+ *****************************************************************************
+ * Copyright (C) 2026 VideoLAN
+ *
+ * Authors: Brandon Li <brandonli2006ma at gmail.com>
+ *
+ * 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 <vlc_common.h>
+#include <vlc_plugin.h>
+
+#include "dynamics.h"
+
+static float ExpanderGain( float f_env,
+ float f_threshold, float f_knee, float f_rs,
+ float f_kn_lo, float f_kn_hi,
+ filter_sys_t *p_sys )
+{
+ if( f_env >= f_kn_hi )
+ return 1.0f;
+
+ if( f_env > f_kn_lo )
+ {
+ const float f_x = ( f_threshold + f_knee - vlc_dynamics_Lin2Db( f_env, p_sys ) ) / f_knee;
+ return vlc_dynamics_Db2Lin( -f_knee * f_rs * f_x * f_x * 0.25f, p_sys );
+ }
+
+ return vlc_dynamics_Db2Lin( ( vlc_dynamics_Lin2Db( f_env, p_sys ) - f_threshold ) * f_rs, p_sys );
+}
+
+static const vlc_dynamics_varnames_t expander_varnames = {
+ .rms_peak = "expander-rms-peak",
+ .attack = "expander-attack",
+ .release = "expander-release",
+ .threshold = "expander-threshold",
+ .ratio = "expander-ratio",
+ .knee = "expander-knee",
+ .makeup_gain = "expander-makeup-gain",
+};
+
+static int Open( vlc_object_t *p_this )
+{
+ return vlc_dynamics_OpenCommon( (filter_t*)p_this, &expander_varnames, ExpanderGain, -60.0f );
+}
+
+vlc_module_begin()
+ set_shortname( N_("Expander") )
+ set_description( N_("Dynamic range expander") )
+ set_capability( "audio filter", 0 )
+ set_subcategory( SUBCAT_AUDIO_AFILTER )
+ add_float_with_range( expander_varnames.rms_peak, 0.2, 0.0, 1.0, RMS_PEAK_TEXT, RMS_PEAK_LONGTEXT )
+ add_float_with_range( expander_varnames.attack, 25.0, 1.5, 400.0, ATTACK_TEXT, ATTACK_LONGTEXT )
+ add_float_with_range( expander_varnames.release, 100.0, 2.0, 800.0, RELEASE_TEXT, RELEASE_LONGTEXT )
+ add_float_with_range( expander_varnames.threshold, -25.0, -60.0, 0.0, THRESHOLD_TEXT, THRESHOLD_LONGTEXT )
+ add_float_with_range( expander_varnames.ratio, 2.0, 1.0, 20.0, RATIO_TEXT, RATIO_LONGTEXT )
+ add_float_with_range( expander_varnames.knee, 5.0, 1.0, 10.0, KNEE_TEXT, KNEE_LONGTEXT )
+ add_float_with_range( expander_varnames.makeup_gain, 0.0, 0.0, 24.0, MAKEUP_GAIN_TEXT, MAKEUP_GAIN_LONGTEXT )
+ set_callback( Open )
+ add_shortcut( "expander" )
+vlc_module_end()
=====================================
modules/audio_filter/limiter.c
=====================================
@@ -0,0 +1,81 @@
+/*****************************************************************************
+ * limiter.c: Peak limiter module
+ *****************************************************************************
+ * Copyright (C) 2026 VideoLAN
+ *
+ * Authors: Brandon Li <brandonli2006ma at gmail.com>
+ *
+ * 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 <vlc_common.h>
+#include <vlc_plugin.h>
+
+#include "dynamics.h"
+
+static float LimiterGain( float f_env,
+ float f_threshold, float f_knee, float f_rs,
+ float f_kn_lo, float f_kn_hi,
+ filter_sys_t *p_sys )
+{
+ VLC_UNUSED(f_rs);
+
+ if( f_env <= f_kn_lo )
+ return 1.0f;
+
+ const float f_env_db = vlc_dynamics_Lin2Db( f_env, p_sys );
+ const float f_excess_db = f_env_db - f_threshold;
+
+ if( f_env >= f_kn_hi )
+ return vlc_dynamics_Db2Lin( -f_excess_db, p_sys );
+
+ const float f_x = ( f_env_db - ( f_threshold - f_knee ) ) / ( 2.0f * f_knee );
+ return vlc_dynamics_Db2Lin( -f_excess_db * f_x * f_x, p_sys );
+}
+
+static const vlc_dynamics_varnames_t limiter_varnames = {
+ .rms_peak = "limiter-rms-peak",
+ .attack = "limiter-attack",
+ .release = "limiter-release",
+ .threshold = "limiter-threshold",
+ .ratio = "limiter-ratio",
+ .knee = "limiter-knee",
+ .makeup_gain = "limiter-makeup-gain",
+};
+
+static int Open( vlc_object_t *p_this )
+{
+ return vlc_dynamics_OpenCommon( (filter_t*)p_this, &limiter_varnames, LimiterGain, -60.0f );
+}
+
+vlc_module_begin()
+ set_shortname( N_("Limiter") )
+ set_description( N_("Peak limiter") )
+ set_capability( "audio filter", 0 )
+ set_subcategory( SUBCAT_AUDIO_AFILTER )
+ add_float_with_range( limiter_varnames.rms_peak, 1.0, 0.0, 1.0, RMS_PEAK_TEXT, RMS_PEAK_LONGTEXT )
+ add_float_with_range( limiter_varnames.attack, 1.5, 1.5, 400.0, ATTACK_TEXT, ATTACK_LONGTEXT )
+ add_float_with_range( limiter_varnames.release, 50.0, 2.0, 800.0, RELEASE_TEXT, RELEASE_LONGTEXT )
+ add_float_with_range( limiter_varnames.threshold, -3.0, -60.0, 0.0, THRESHOLD_TEXT, THRESHOLD_LONGTEXT )
+ add_float_with_range( limiter_varnames.ratio, 20.0, 1.0, 20.0, RATIO_TEXT, RATIO_LONGTEXT )
+ add_float_with_range( limiter_varnames.knee, 1.0, 1.0, 10.0, KNEE_TEXT, KNEE_LONGTEXT )
+ add_float_with_range( limiter_varnames.makeup_gain, 0.0, 0.0, 24.0, MAKEUP_GAIN_TEXT, MAKEUP_GAIN_LONGTEXT )
+ set_callback( Open )
+ add_shortcut( "limiter" )
+vlc_module_end()
=====================================
modules/audio_filter/meson.build
=====================================
@@ -19,11 +19,36 @@ vlc_modules += {
'dependencies' : [m_lib]
}
+# Shared dynamics core
+libdynamics = static_library('dynamics',
+ files('dynamics.c'),
+ dependencies: [m_lib],
+ include_directories: [vlc_include_dirs],
+ install: false,
+)
+
# Compressor module
vlc_modules += {
'name' : 'compressor',
'sources' : files('compressor.c'),
- 'dependencies' : [m_lib]
+ 'dependencies' : [m_lib],
+ 'link_with' : [libdynamics],
+}
+
+# Expander module
+vlc_modules += {
+ 'name' : 'expander',
+ 'sources' : files('expander.c'),
+ 'dependencies' : [m_lib],
+ 'link_with' : [libdynamics],
+}
+
+# Limiter module
+vlc_modules += {
+ 'name' : 'limiter',
+ 'sources' : files('limiter.c'),
+ 'dependencies' : [m_lib],
+ 'link_with' : [libdynamics],
}
# Equalizer filter module
=====================================
modules/gui/qt/dialogs/extended/extended.cpp
=====================================
@@ -71,6 +71,14 @@ ExtendedDialog::ExtendedDialog( qt_intf_t *_p_intf )
connect( compres, &AudioFilterControlWidget::configChanged, this, &ExtendedDialog::putAudioConfig );
audioTab->addTab( compres, qtr( "Compressor" ) );
+ Expander *expand = new Expander( p_intf, audioTab );
+ connect( expand, &AudioFilterControlWidget::configChanged, this, &ExtendedDialog::putAudioConfig );
+ audioTab->addTab( expand, qtr( "Expander" ) );
+
+ Limiter *limit = new Limiter( p_intf, audioTab );
+ connect( limit, &AudioFilterControlWidget::configChanged, this, &ExtendedDialog::putAudioConfig );
+ audioTab->addTab( limit, qtr( "Limiter" ) );
+
Spatializer *spatial = new Spatializer( p_intf, audioTab );
connect( spatial, &AudioFilterControlWidget::configChanged, this, &ExtendedDialog::putAudioConfig );
audioTab->addTab( spatial, qtr( "Spatializer" ) );
=====================================
modules/gui/qt/dialogs/extended/extended_panels.cpp
=====================================
@@ -1256,6 +1256,50 @@ Compressor::Compressor( qt_intf_t *p_intf, QWidget *parent )
build();
}
+/**********************************************************************
+ * Dynamic range expander
+ **********************************************************************/
+
+Expander::Expander( qt_intf_t *p_intf, QWidget *parent )
+ : AudioFilterControlWidget( p_intf, parent, "expander" )
+{
+ i_smallfont = -2;
+ const FilterSliderData::slider_data_t a[7] =
+ {
+ { "expander-rms-peak", qtr("RMS/peak"), "", 0.0f, 1.0f, 0.20f, 0.001f, 1.0 },
+ { "expander-attack", qtr("Attack"), qtr("ms"), 1.5f, 400.0f, 25.00f, 0.100f, 1.0 },
+ { "expander-release", qtr("Release"), qtr("ms"), 2.0f, 800.0f, 100.00f, 0.100f, 1.0 },
+ { "expander-threshold", qtr("Threshold"), qtr("dB"), -60.0f, 0.0f, -25.00f, 0.010f, 1.0 },
+ { "expander-ratio", qtr("Ratio"), ":1", 1.0f, 20.0f, 2.00f, 0.010f, 1.0 },
+ { "expander-knee", qtr("Knee\nradius"), qtr("dB"), 1.0f, 10.0f, 5.00f, 0.010f, 1.0 },
+ { "expander-makeup-gain", qtr("Makeup\ngain"), qtr("dB"), 0.0f, 24.0f, 0.00f, 0.010f, 1.0 },
+ };
+ for( int i=0; i<7 ;i++ ) controls.append( a[i] );
+ build();
+}
+
+/**********************************************************************
+ * Peak limiter
+ **********************************************************************/
+
+Limiter::Limiter( qt_intf_t *p_intf, QWidget *parent )
+ : AudioFilterControlWidget( p_intf, parent, "limiter" )
+{
+ i_smallfont = -2;
+ const FilterSliderData::slider_data_t a[7] =
+ {
+ { "limiter-rms-peak", qtr("RMS/peak"), "", 0.0f, 1.0f, 1.00f, 0.001f, 1.0 },
+ { "limiter-attack", qtr("Attack"), qtr("ms"), 1.5f, 400.0f, 1.50f, 0.100f, 1.0 },
+ { "limiter-release", qtr("Release"), qtr("ms"), 2.0f, 800.0f, 50.00f, 0.100f, 1.0 },
+ { "limiter-threshold", qtr("Threshold"), qtr("dB"), -60.0f, 0.0f, -3.00f, 0.010f, 1.0 },
+ { "limiter-ratio", qtr("Ratio"), ":1", 1.0f, 20.0f, 20.00f, 0.010f, 1.0 },
+ { "limiter-knee", qtr("Knee\nradius"), qtr("dB"), 1.0f, 10.0f, 1.00f, 0.010f, 1.0 },
+ { "limiter-makeup-gain", qtr("Makeup\ngain"), qtr("dB"), 0.0f, 24.0f, 0.00f, 0.010f, 1.0 },
+ };
+ for( int i=0; i<7 ;i++ ) controls.append( a[i] );
+ build();
+}
+
/**********************************************************************
* Spatializer
**********************************************************************/
=====================================
modules/gui/qt/dialogs/extended/extended_panels.hpp
=====================================
@@ -200,6 +200,22 @@ public:
Compressor( qt_intf_t *, QWidget * );
};
+class Expander: public AudioFilterControlWidget
+{
+ Q_OBJECT
+
+public:
+ Expander( qt_intf_t *, QWidget * );
+};
+
+class Limiter: public AudioFilterControlWidget
+{
+ Q_OBJECT
+
+public:
+ Limiter( qt_intf_t *, QWidget * );
+};
+
class Spatializer: public AudioFilterControlWidget
{
Q_OBJECT
=====================================
po/POTFILES.in
=====================================
@@ -228,6 +228,8 @@ modules/audio_filter/channel_mixer/spatialaudio.cpp
modules/audio_filter/channel_mixer/trivial.c
modules/audio_filter/chorus_flanger.c
modules/audio_filter/compressor.c
+modules/audio_filter/expander.c
+modules/audio_filter/limiter.c
modules/audio_filter/converter/format.c
modules/audio_filter/converter/tospdif.c
modules/audio_filter/equalizer.c
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7507fca293a2c77c713834f9ee85509cf1bef553...d86f220fc6dade96c636b38a35cccd7b265ebd86
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7507fca293a2c77c713834f9ee85509cf1bef553...d86f220fc6dade96c636b38a35cccd7b265ebd86
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list