[MPlayer-dev-eng] [PATCH] audio filter volnorm2 - two-pass audio normalizer

Vladislav Naumov vnaum at pochta.ru
Sat Jul 30 07:48:17 CEST 2005


volnorm2 - two-pass audio normalizer.
This filter should become a 2-pass audio normalizer:
on first pass, it should analyze audio for peak value
and store it in file, and on second act as "volume",
amplifying sound.
Currently only 1st pass is implemented.
It can be used to find peak/gain values.
Something like "cropdetect" filter, but for sound.

Explanation:
AC3 5.1 sound on DVDs is usually not loud enough
and needs aplification. There is number of ways to make
it louder
(like -af volume or encoder gain), but you must either
guess gain value
or encode some sound, analyze encoded sound in some
audio editor,
find peak value, calculate gain value and then encode
sound again.
-------------- next part --------------
diff -Naur MPlayer.orig/DOCS/man/en/mplayer.1 MPlayer.my/DOCS/man/en/mplayer.1
--- MPlayer.orig/DOCS/man/en/mplayer.1	2005-07-29 15:39:17.000000000 +0700
+++ MPlayer.my/DOCS/man/en/mplayer.1	2005-07-30 11:51:15.497534448 +0700
@@ -4036,6 +4036,22 @@
 .PD 1
 .
 .TP
+.B volnorm2=<pass>
+Two-pass audio normalizer. Useful mostly for encoding.
+.PD 0
+.RSs
+.IPs <pass>
+Sets pass number. Currently only 1st pass is implemented.
+Use -af volume or encoder parameters to amplify sound.
+.RSss
+1: Scan audio stream for peak value, then print peak/gain values.
+.br
+2: Amplify sound using stored gain value.
+.REss
+.RE
+.PD 1
+.
+.TP
 .B ladspa=file:label[:controls...]
 Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin.
 This filter is reentrant, so multiple LADSPA plugins can be used at once.
diff -Naur MPlayer.orig/libaf/af.c MPlayer.my/libaf/af.c
--- MPlayer.orig/libaf/af.c	2005-07-29 15:35:34.000000000 +0700
+++ MPlayer.my/libaf/af.c	2005-07-29 15:54:04.000000000 +0700
@@ -23,6 +23,7 @@
 extern af_info_t af_info_sub;
 extern af_info_t af_info_export;
 extern af_info_t af_info_volnorm;
+extern af_info_t af_info_volnorm2;
 extern af_info_t af_info_extrastereo;
 extern af_info_t af_info_lavcresample;
 extern af_info_t af_info_sweep;
@@ -47,6 +48,7 @@
    &af_info_export,
 #endif
    &af_info_volnorm,
+   &af_info_volnorm2,
    &af_info_extrastereo,
 #ifdef USE_LIBAVCODEC
    &af_info_lavcresample,
diff -Naur MPlayer.orig/libaf/af_volnorm2.c MPlayer.my/libaf/af_volnorm2.c
--- MPlayer.orig/libaf/af_volnorm2.c	1970-01-01 06:00:00.000000000 +0600
+++ MPlayer.my/libaf/af_volnorm2.c	2005-07-30 10:41:52.000000000 +0700
@@ -0,0 +1,192 @@
+/*=============================================================================
+//	
+//  This software has been released under the terms of the GNU General Public
+//  license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+//  Copyright 2005 Vladislav Naumov <vnaum at inbox.ru>
+//
+//=============================================================================
+*/
+
+/* This filter should become a 2-pass audio normalizer:
+ * on first pass, it should analyze audio for peak value,
+ * and on second act as "volume", amplifying it.
+ * Currently only 1st pass is implemented.
+ * Still, its somewhat useful.
+ *
+ * Use with "-ao pcm:file=/dev/null -vc dummy -vo null -benchmark"
+ * to speed up things.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+
+#include <limits.h>
+
+// Data for specific instances of this filter
+typedef struct af_volnorm2_s
+{
+  int pass_number;		// Pass number
+  int16_t peak_sample_value;	// Peak sample value
+  float normalize_to;		// Desired peak value -
+  //   float, since we only use it as float
+} af_volnorm2_t;
+
+// function prototypes
+// Allocate memory and set function pointers
+static int open (af_instance_t * af);
+// Initialization and runtime control
+static int control (struct af_instance_s *af, int cmd, void *arg);
+// Filter data through filter
+static af_data_t *play (struct af_instance_s *af, af_data_t * data);
+// Deallocate memory, print message
+static void uninit (struct af_instance_s *af);
+
+// Allocate memory and set function pointers
+static int
+open (af_instance_t * af)
+{
+  af->control = control;
+  af->uninit = uninit;
+  af->play = play;
+  af->mul.d = 1;
+  af->mul.n = 1;
+  af->data = calloc (1, sizeof (af_data_t));
+  if (af->data == NULL)
+    return AF_ERROR;
+
+  af_volnorm2_t *s = calloc (1, sizeof (af_volnorm2_t));
+  if (s == NULL)
+    return AF_ERROR;
+  s->peak_sample_value = 0;
+  // normalize to 98% of scale
+  s->normalize_to = (float) SHRT_MAX *98 / 100;
+
+  af->setup = s;
+
+  return AF_OK;
+}
+
+// Initialization and runtime control
+static int
+control (struct af_instance_s *af, int cmd, void *arg)
+{
+  // this instance settings
+  af_volnorm2_t *s = (af_volnorm2_t *) af->setup;
+
+  switch (cmd)
+    {
+    case AF_CONTROL_REINIT:
+      memcpy (af->data, (af_data_t *) arg, sizeof (af_data_t));
+      af_msg (AF_MSG_VERBOSE, "[volnorm2] Was reinitialized: %iHz/%ich/%s\n",
+	      af->data->rate, af->data->nch,
+	      af_fmt2str_short (af->data->format));
+      return AF_OK;
+
+    case AF_CONTROL_COMMAND_LINE:
+      {
+	int pass = 0;
+	sscanf ((char *) arg, "%d", &pass);
+	if (pass == 1)
+	  {
+	    s->pass_number = pass;
+	    return AF_OK;
+	  }
+	else if (pass == 2)
+	  {
+	    // Should I abort after this?
+	    af_msg (AF_MSG_FATAL,
+		    "[volnorm2] Sorry, pass 2 is not yet implemented\n");
+	    return AF_ERROR;
+	  }
+	else
+	  {
+	    af_msg (AF_MSG_FATAL, "[volnorm2] Invalid pass number: %d\n",
+		    pass);
+	    return AF_ERROR;
+	  }
+      }
+    }
+
+  return AF_UNKNOWN;
+}
+
+// Filter data through filter
+static af_data_t *
+play (struct af_instance_s *af, af_data_t * data)
+{
+  af_volnorm2_t *s = (af_volnorm2_t *) af->setup;	// Setup for this instance
+
+  if (s->pass_number == 1)
+    {
+      if (af->data->format == (AF_FORMAT_S16_NE))
+	{
+	  // This is most popular data format (mp3, dvd ac3).
+	  // In fact, every video file on my disk was AF_FORMAT_S16_NE.
+	  int16_t *a = (int16_t *) data->audio;
+	  int16_t peak = s->peak_sample_value;
+
+	  /* scan for peak value,
+	   * backwards should be faster.
+	   * We don't care about channels - any sample is ok.
+	   */
+
+	  for (int i = data->len / data->bps - 1; i >= 0; i--)
+	    if (peak < abs (a[i]))
+	      peak = abs (a[i]);
+
+	  s->peak_sample_value = peak;
+	}
+      else if (af->data->format == (AF_FORMAT_FLOAT_NE))
+	{
+	  af_msg (AF_MSG_ERROR,
+		  "[volnorm2] Sorry, AF_FORMAT_FLOAT_NE is (not yet) supported.\n");
+	}
+      else
+	{
+	  af_msg (AF_MSG_ERROR,
+		  "[volnorm2] Audio format %d is not supported.\n",
+		  af->data->format);
+	}
+    }
+  else				// if (s->pass_number == 2)
+    {
+      // TODO
+      af_msg (AF_MSG_FATAL,
+	      "[volnorm2] Sorry, pass 2 is not yet implemented\n");
+    }
+  return data;
+}
+
+// Deallocate memory, print message
+static void
+uninit (struct af_instance_s *af)
+{
+  // this one should always be true. assert it?
+  if (af->setup)
+    {
+      af_volnorm2_t *s = (af_volnorm2_t *) af->setup;
+      af_msg (AF_MSG_INFO,
+	      "\n[volnorm2] Peak value reached: %d, gain to %.0f is %.3f\n",
+	      s->peak_sample_value,
+	      s->normalize_to, s->normalize_to / s->peak_sample_value);
+
+      free (af->setup);
+    }
+
+  if (af->data)
+    free (af->data);
+}
+
+// Description of this filter
+af_info_t af_info_volnorm2 = {
+  "Volume normalizer filter, two-pass version",
+  "volnorm2",
+  "Vladislav Naumov",
+  "",
+  AF_FLAGS_NOT_REENTRANT,
+  open
+};
diff -Naur MPlayer.orig/libaf/Makefile MPlayer.my/libaf/Makefile
--- MPlayer.orig/libaf/Makefile	2005-07-29 15:35:34.000000000 +0700
+++ MPlayer.my/libaf/Makefile	2005-07-29 15:47:21.000000000 +0700
@@ -22,6 +22,7 @@
      af_sweep.c \
      af_tools.c \
      af_volnorm.c \
+     af_volnorm2.c \
      af_volume.c \
      filter.c \
      format.c \


More information about the MPlayer-dev-eng mailing list