[MPlayer-dev-eng] [PATCH] ao effect plugin: volnorm (or a simple & naive volume normalizer)

pl p_l at gmx.fr
Sun Mar 3 23:43:51 CET 2002


Hi,

Here is a patch to "normalize" audio output, which means your playback
should sound almost the same level.  Well... it's designed to do that,
YMMV :)

The algo is relatively braindead and is around 20 lines:
 1. averages samples received by play()
 2. evaluates a scale factor to have a perfect average sample ('neededmul')
 3. 'mul' (the real scale factor) is a smoothed value based on 'neededmul'
 4. does the audio scaling by 'mul'

I know the numbers of samples received differ but on average it should
be OK.

I'm not completely satisfied with the results so far, so if someone has
clues clearer than mines about signal processing (really easy you know
;), I'd be glad to have the algo improved/replaced prior to committing
the plugin.

Parameters are currently hardcoded so edit the #define's to tweak them.

Usage: mplayer media.avi -aop list=volnorm

-- 
Best regards,
  pl
-------------- next part --------------
diff -x CVS -x *.mak -x *.o -ruN main+/libao2-orig/Makefile main+/libao2/Makefile
--- main+/libao2-orig/Makefile	Sun Mar  3 22:32:43 2002
+++ main+/libao2/Makefile	Sun Mar  3 22:40:27 2002
@@ -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 -x CVS -x *.mak -x *.o -ruN main+/libao2-orig/audio_plugin.h main+/libao2/audio_plugin.h
--- main+/libao2-orig/audio_plugin.h	Sun Mar  3 22:32:47 2002
+++ main+/libao2/audio_plugin.h	Sun Mar  3 22:40:27 2002
@@ -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 -x CVS -x *.mak -x *.o -ruN main+/libao2-orig/pl_volnorm.c main+/libao2/pl_volnorm.c
--- main+/libao2-orig/pl_volnorm.c	Thu Jan  1 01:00:00 1970
+++ main+/libao2/pl_volnorm.c	Sun Mar  3 23:09:34 2002
@@ -0,0 +1,165 @@
+/* Normalizer plugin
+ * 
+ * Limitations: 
+ *  - only AFMT_S16_LE supported
+ *  - no parameters yet => tweak the values by editing the #defines
+ *
+ * License: GPLv2
+ * Author: pl <p_l at gmx.fr> (c) 2002 and beyond...
+ *
+ * */
+
+#define PLUGIN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <math.h>	// 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 <p_l at gmx.fr>",
+        ""
+};
+
+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 new value and history
+#define SMOOTH_MUL 0.1
+#define SMOOTH_LASTAVG 0.1
+
+// average level we want to reach
+#define MID_S16 (INT16_MAX * 0.25)
+
+// 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);
+
+    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
+    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;
+
+}
+
+


More information about the MPlayer-dev-eng mailing list