# HG changeset patch # User pl # Date 1015319856 0 # Node ID 9a468a190c4c215cd3f86458b9aa075ceea05c33 # Parent 098970f06dc2d13cd5e8ace6cb4387af6dd97fa4 volume normalizer plugin support diff -r 098970f06dc2 -r 9a468a190c4c libao2/Makefile --- a/libao2/Makefile Tue Mar 05 08:51:57 2002 +0000 +++ b/libao2/Makefile Tue Mar 05 09:17:36 2002 +0000 @@ -4,7 +4,7 @@ LIBNAME = libao2.a # TODO: moveout ao_sdl.c so it's only used when SDL is detected -SRCS=afmt.c audio_out.c ao_mpegpes.c ao_null.c ao_pcm.c ao_plugin.c pl_delay.c pl_format.c pl_surround.c remez.c pl_resample.c pl_volume.c pl_extrastereo.c $(OPTIONAL_SRCS) +SRCS=afmt.c audio_out.c ao_mpegpes.c ao_null.c ao_pcm.c ao_plugin.c pl_delay.c pl_format.c pl_surround.c remez.c pl_resample.c pl_volume.c pl_extrastereo.c pl_volnorm.c $(OPTIONAL_SRCS) OBJS=$(SRCS:.c=.o) diff -r 098970f06dc2 -r 9a468a190c4c libao2/audio_plugin.h --- a/libao2/audio_plugin.h Tue Mar 05 08:51:57 2002 +0000 +++ b/libao2/audio_plugin.h Tue Mar 05 09:17:36 2002 +0000 @@ -54,7 +54,7 @@ // This block should not be available in the pl_xxxx files // due to compilation issues #ifndef PLUGIN -#define NPL 6+1 // Number of PLugins ( +1 list ends with NULL ) +#define NPL 7+1 // Number of PLugins ( +1 list ends with NULL ) // List of plugins extern ao_plugin_functions_t audio_plugin_delay; extern ao_plugin_functions_t audio_plugin_format; @@ -62,6 +62,7 @@ extern ao_plugin_functions_t audio_plugin_resample; extern ao_plugin_functions_t audio_plugin_volume; extern ao_plugin_functions_t audio_plugin_extrastereo; +extern ao_plugin_functions_t audio_plugin_volnorm; #define AO_PLUGINS { \ @@ -71,6 +72,7 @@ &audio_plugin_resample, \ &audio_plugin_volume, \ &audio_plugin_extrastereo, \ + &audio_plugin_volnorm, \ NULL \ } #endif /* PLUGIN */ diff -r 098970f06dc2 -r 9a468a190c4c libao2/pl_volnorm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libao2/pl_volnorm.c Tue Mar 05 09:17:36 2002 +0000 @@ -0,0 +1,171 @@ +/* Normalizer plugin + * + * Limitations: + * - only AFMT_S16_LE supported + * - no parameters yet => tweak the values by editing the #defines + * + * License: GPLv2 + * Author: pl (c) 2002 and beyond... + * + * Sources: some ideas from volnorm for xmms + * + * */ + +#define PLUGIN + +#include +#include +#include +#include // for sqrt() + +#include "audio_out.h" +#include "audio_plugin.h" +#include "audio_plugin_internal.h" +#include "afmt.h" + +static ao_info_t info = { + "Volume normalizer", + "volnorm", + "pl ", + "" +}; + +LIBAO_PLUGIN_EXTERN(volnorm) + +// mul is the value by which the samples are scaled +// and has to be in [MUL_MIN, MUL_MAX] +#define MUL_INIT 1.0 +#define MUL_MIN 0.1 +#define MUL_MAX 15.0 +static float mul; + +// "history" value of the filter +static float lastavg; + +// SMOOTH_* must be in ]0.0, 1.0[ +// The new value accounts for SMOOTH_MUL in the value and history +#define SMOOTH_MUL 0.06 +#define SMOOTH_LASTAVG 0.06 + +// ideal average level +#define MID_S16 (INT16_MAX * 0.25) + +// silence level +#define SIL_S16 (INT16_MAX * 0.02) + +// local data +static struct { + int inuse; // This plugin is in use TRUE, FALSE + int format; // sample fomat +} pl_volnorm = {0, 0}; + + +// minimal interface +static int control(int cmd,int arg){ + switch(cmd){ + case AOCONTROL_PLUGIN_SET_LEN: + return CONTROL_OK; + } + return CONTROL_UNKNOWN; +} + +// minimal interface +// open & setup audio device +// return: 1=success 0=fail +static int init(){ + switch(ao_plugin_data.format){ + case(AFMT_S16_LE): + break; + default: + fprintf(stderr,"[pl_volnorm] Audio format not yet supported.\n"); + return 0; + } + + pl_volnorm.format = ao_plugin_data.format; + pl_volnorm.inuse = 1; + + reset(); + + printf("[pl_volnorm] Normalizer plugin in use.\n"); + return 1; +} + +// close plugin +static void uninit(){ + pl_volnorm.inuse=0; +} + +// empty buffers +static void reset(){ + mul = MUL_INIT; + switch(ao_plugin_data.format) { + case(AFMT_S16_LE): + lastavg = MID_S16; + break; + default: + fprintf(stderr,"[pl_volnorm] internal inconsistency - please bugreport.\n"); + *(char *) 0 = 0; + } +} + +// processes 'ao_plugin_data.len' bytes of 'data' +// called for every block of data +static int play(){ + + switch(pl_volnorm.format){ + case(AFMT_S16_LE): { + +#define CLAMP(x,m,M) do { if ((x)<(m)) (x) = (m); else if ((x)>(M)) (x) = (M); } while(0) + + int16_t* data=(int16_t*)ao_plugin_data.data; + int len=ao_plugin_data.len / 2; // 16 bits samples + + int32_t i; + register int32_t tmp; + register float curavg; + float newavg; + float neededmul; + + // average of the current samples + curavg = 0.0; + for (i = 0; i < len ; ++i) { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + if (curavg > SIL_S16) { + neededmul = MID_S16 / ( curavg * mul); + mul = (1.0 - SMOOTH_MUL) * mul + SMOOTH_MUL * neededmul; + + // Clamp the mul coefficient + CLAMP(mul, MUL_MIN, MUL_MAX); + } + + // Scale & clamp the samples + for (i=0; i < len ; ++i) { + tmp = data[i] * mul; + CLAMP(tmp, INT16_MIN, INT16_MAX); + data[i] = tmp; + } + + // Evaluation of newavg (not 100% accurate because of values clamping) + newavg = mul * curavg; + +#if 0 + printf("time = %d len = %d curavg = %6.0f lastavg = %6.0f newavg = %6.0f\n" + " needed_m = %2.2f m = %2.2f\n\n", + time(NULL), len, curavg, lastavg, newavg, neededmul, mul); +#endif + + lastavg = (1.0 - SMOOTH_LASTAVG) * lastavg + SMOOTH_LASTAVG * newavg; + + break; + } + default: + return 0; + } + return 1; + +} +