4 * - only AFMT_S16_LE supported
5 * - no parameters yet => tweak the values by editing the #defines
8 * Author: pl <p_l@gmx.fr> (c) 2002 and beyond...
10 * Sources: some ideas from volnorm plugin for xmms
17 * 1: uses a 1 value memory and coefficients new=a*old+b*cur (with a+b=1)
19 * 2: uses several samples to smooth the variations (standard weighted mean
28 #include <math.h> // for sqrt()
30 #include "audio_out.h"
31 #include "audio_plugin.h"
32 #include "audio_plugin_internal.h"
35 static ao_info_t info
= {
42 LIBAO_PLUGIN_EXTERN(volnorm
)
44 // mul is the value by which the samples are scaled
45 // and has to be in [MUL_MIN, MUL_MAX]
53 // "history" value of the filter
56 // SMOOTH_* must be in ]0.0, 1.0[
57 // The new value accounts for SMOOTH_MUL in the value and history
58 #define SMOOTH_MUL 0.06
59 #define SMOOTH_LASTAVG 0.06
63 // Size of the memory array
64 // FIXME: should depend on the frequency of the data (should be a few seconds)
67 // Indicates where to write (in 0..NSAMPLES-1)
71 float avg
; // average level of the sample
72 int32_t len
; // sample size (weight)
75 // If summing all the mem[].len is lower than MIN_SAMPLE_SIZE bytes, then we
76 // choose to ignore the computed value as it's not significant enough
77 // FIXME: should depend on the frequency of the data (0.5s maybe)
78 #define MIN_SAMPLE_SIZE 32000
87 #define MIN_S16 -32768
91 #define MID_S16 (MAX_S16 * 0.25)
94 // FIXME: should be relative to the level of the samples
95 #define SIL_S16 (MAX_S16 * 0.01)
100 int inuse
; // This plugin is in use TRUE, FALSE
101 int format
; // sample fomat
102 } pl_volnorm
= {0, 0};
106 static int control(int cmd
,void *arg
){
108 case AOCONTROL_PLUGIN_SET_LEN
:
111 return CONTROL_UNKNOWN
;
115 // open & setup audio device
116 // return: 1=success 0=fail
118 switch(ao_plugin_data
.format
){
122 fprintf(stderr
,"[pl_volnorm] Audio format not yet supported.\n");
126 pl_volnorm
.format
= ao_plugin_data
.format
;
127 pl_volnorm
.inuse
= 1;
131 printf("[pl_volnorm] Normalizer plugin in use.\n");
136 static void uninit(){
144 switch(ao_plugin_data
.format
) {
149 for(i
=0; i
< NSAMPLES
; ++i
) {
158 fprintf(stderr
,"[pl_volnorm] internal inconsistency - bugreport !\n");
163 // processes 'ao_plugin_data.len' bytes of 'data'
164 // called for every block of data
167 switch(pl_volnorm
.format
){
169 #define CLAMP(x,m,M) do { if ((x)<(m)) (x) = (m); else if ((x)>(M)) (x) = (M); } while(0)
171 int16_t* data
=(int16_t*)ao_plugin_data
.data
;
172 int len
=ao_plugin_data
.len
/ 2; // 16 bits samples
175 float curavg
, newavg
;
184 // Evaluate current samples average level
186 for (i
= 0; i
< len
; ++i
) {
190 curavg
= sqrt(curavg
/ (float) len
);
192 // Evaluate an adequate 'mul' coefficient based on previous state, current
193 // samples level, etc
195 if (curavg
> SIL_S16
) {
196 neededmul
= MID_S16
/ ( curavg
* mul
);
197 mul
= (1.0 - SMOOTH_MUL
) * mul
+ SMOOTH_MUL
* neededmul
;
199 // Clamp the mul coefficient
200 CLAMP(mul
, MUL_MIN
, MUL_MAX
);
206 for (i
= 0; i
< NSAMPLES
; ++i
) {
207 avg
+= mem
[i
].avg
* (float) mem
[i
].len
;
208 totallen
+= mem
[i
].len
;
211 if (totallen
> MIN_SAMPLE_SIZE
) {
212 avg
/= (float) totallen
;
213 if (avg
>= SIL_S16
) {
214 mul
= (float) MID_S16
/ avg
;
215 CLAMP(mul
, MUL_MIN
, MUL_MAX
);
220 // Scale & clamp the samples
221 for (i
= 0; i
< len
; ++i
) {
223 CLAMP(tmp
, MIN_S16
, MAX_S16
);
227 // Evaluation of newavg (not 100% accurate because of values clamping)
228 newavg
= mul
* curavg
;
230 // Stores computed values for future smoothing
232 lastavg
= (1.0 - SMOOTH_LASTAVG
) * lastavg
+ SMOOTH_LASTAVG
* newavg
;
233 //printf("\rmul=%02.1f ", mul);
236 mem
[idx
].avg
= newavg
;
237 idx
= (idx
+ 1) % NSAMPLES
;
238 //printf("\rmul=%02.1f (%04dKiB) ", mul, totallen/1024);