constant qscale was broken due to libavcodec changes, fix taken from ve_lavc.c
[mplayer/greg.git] / libao2 / pl_volnorm.c
blobdd5d0a4e9b1cda95ea4f691c7fa513ace70a984f
1 /* Normalizer plugin
3 * Limitations:
4 * - only AFMT_S16_LE supported
5 * - no parameters yet => tweak the values by editing the #defines
7 * License: GPLv2
8 * Author: pl <p_l@gmx.fr> (c) 2002 and beyond...
10 * Sources: some ideas from volnorm plugin for xmms
12 * */
14 #define PLUGIN
16 /* Values for AVG:
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
20 * on past samples)
22 * */
23 #define AVG 1
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <inttypes.h>
28 #include <math.h> // for sqrt()
30 #include "audio_out.h"
31 #include "audio_plugin.h"
32 #include "audio_plugin_internal.h"
33 #include "afmt.h"
35 static ao_info_t info = {
36 "Volume normalizer",
37 "volnorm",
38 "pl <p_l@gmx.fr>",
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]
46 #define MUL_INIT 1.0
47 #define MUL_MIN 0.1
48 #define MUL_MAX 5.0
49 static float mul;
52 #if AVG==1
53 // "history" value of the filter
54 static float lastavg;
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
62 #elif AVG==2
63 // Size of the memory array
64 // FIXME: should depend on the frequency of the data (should be a few seconds)
65 #define NSAMPLES 128
67 // Indicates where to write (in 0..NSAMPLES-1)
68 static int idx;
69 // The array
70 static struct {
71 float avg; // average level of the sample
72 int32_t len; // sample size (weight)
73 } mem[NSAMPLES];
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
80 #else
81 // Kab00m !
82 #error "Unknown AVG"
83 #endif
86 // Some limits
87 #define MIN_S16 -32768
88 #define MAX_S16 32767
90 // "Ideal" level
91 #define MID_S16 (MAX_S16 * 0.25)
93 // Silence level
94 // FIXME: should be relative to the level of the samples
95 #define SIL_S16 (MAX_S16 * 0.01)
98 // Local data
99 static struct {
100 int inuse; // This plugin is in use TRUE, FALSE
101 int format; // sample fomat
102 } pl_volnorm = {0, 0};
105 // minimal interface
106 static int control(int cmd,void *arg){
107 switch(cmd){
108 case AOCONTROL_PLUGIN_SET_LEN:
109 return CONTROL_OK;
111 return CONTROL_UNKNOWN;
114 // minimal interface
115 // open & setup audio device
116 // return: 1=success 0=fail
117 static int init(){
118 switch(ao_plugin_data.format){
119 case(AFMT_S16_NE):
120 break;
121 default:
122 fprintf(stderr,"[pl_volnorm] Audio format not yet supported.\n");
123 return 0;
126 pl_volnorm.format = ao_plugin_data.format;
127 pl_volnorm.inuse = 1;
129 reset();
131 printf("[pl_volnorm] Normalizer plugin in use.\n");
132 return 1;
135 // close plugin
136 static void uninit(){
137 pl_volnorm.inuse=0;
140 // empty buffers
141 static void reset(){
142 int i;
143 mul = MUL_INIT;
144 switch(ao_plugin_data.format) {
145 case(AFMT_S16_NE):
146 #if AVG==1
147 lastavg = MID_S16;
148 #elif AVG==2
149 for(i=0; i < NSAMPLES; ++i) {
150 mem[i].len = 0;
151 mem[i].avg = 0;
153 idx = 0;
154 #endif
156 break;
157 default:
158 fprintf(stderr,"[pl_volnorm] internal inconsistency - bugreport !\n");
159 *(char *) 0 = 0;
163 // processes 'ao_plugin_data.len' bytes of 'data'
164 // called for every block of data
165 static int play(){
167 switch(pl_volnorm.format){
168 case(AFMT_S16_NE): {
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
174 int32_t i, tmp;
175 float curavg, newavg;
177 #if AVG==1
178 float neededmul;
179 #elif AVG==2
180 float avg;
181 int32_t totallen;
182 #endif
184 // Evaluate current samples average level
185 curavg = 0.0;
186 for (i = 0; i < len ; ++i) {
187 tmp = data[i];
188 curavg += tmp * tmp;
190 curavg = sqrt(curavg / (float) len);
192 // Evaluate an adequate 'mul' coefficient based on previous state, current
193 // samples level, etc
194 #if AVG==1
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);
202 #elif AVG==2
203 avg = 0.0;
204 totallen = 0;
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);
218 #endif
220 // Scale & clamp the samples
221 for (i = 0; i < len ; ++i) {
222 tmp = mul * data[i];
223 CLAMP(tmp, MIN_S16, MAX_S16);
224 data[i] = tmp;
227 // Evaluation of newavg (not 100% accurate because of values clamping)
228 newavg = mul * curavg;
230 // Stores computed values for future smoothing
231 #if AVG==1
232 lastavg = (1.0 - SMOOTH_LASTAVG) * lastavg + SMOOTH_LASTAVG * newavg;
233 //printf("\rmul=%02.1f ", mul);
234 #elif AVG==2
235 mem[idx].len = len;
236 mem[idx].avg = newavg;
237 idx = (idx + 1) % NSAMPLES;
238 //printf("\rmul=%02.1f (%04dKiB) ", mul, totallen/1024);
239 #endif
240 //fflush(stdout);
242 break;
244 default:
245 return 0;
247 return 1;