[MPlayer-dev-eng] Dithering 16-bit output for libmad

Vladimir Mosgalin mosgalin at VM10124.spb.edu
Sun Jan 30 12:13:46 CET 2005


Since a lot of people use mplayer as mp3 player (personally, xmms
crashed a lot lately, I disliked UI in other gui players, and console
ones were lacking good controls), I made a patch that adds dithered
16-bit output for libmad decoder, to increase mp3 playback quality. It
works because output from libmad is 24-bit, and producing dithered
16-bit samples out if gives more quality results than rounding to 16
bits. The code is ported from madplay.

Of course, if you don't have mp3s with high bitrates and good sound
hardware, probably you won't be able to notice the difference. It's
easier to notice it with headphones. Some more steps are required in
order to get higher quality sound: of course, you need to use libmad
decoder (afm = libmad in config), and if you have sound card from
Creative or some other card that doesn't natively support 44.1khz sound,
you need to use software resampling to 48k, as hardware one is lame.
In mplayer, af = lavcresample=48000:32:1:12 works good. Some options may
be returned to default or linear interpolation turned off, but not both.

At first I wanted to add ability to switch back to rounding instead of
dithering, but I can't find out how to pass suboptions to audio
decoders...

Anyway, there is only one drawback: because of dithering, the output
now can give worse analytic results. For example, it is less likely that
auCDtect will detect dithered output as mp3 file.

Since there are only few integer operations, the performance drop is
very small, less than 0.1% of total cpu usage. It is about 10-50 times
less than performance penalty from using libmad instead of mp3lib or
from using lavcresample.

-- 

Vladimir
-------------- next part --------------
diff -uNr main.old/libmpcodecs/ad_libmad.c main/libmpcodecs/ad_libmad.c
--- main.old/libmpcodecs/ad_libmad.c	2005-01-30 12:00:58.875997400 +0300
+++ main/libmpcodecs/ad_libmad.c	2005-01-30 12:44:02.979154008 +0300
@@ -1,5 +1,3 @@
-// SAMPLE audio decoder - you can use this file as template when creating new codec!
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -36,6 +34,11 @@
 
 } mad_decoder_t;
 
+typedef struct audio_dither {
+  mad_fixed_t error[3];
+  mad_fixed_t random;
+} audio_dither;
+
 static int preinit(sh_audio_t *sh){
 
   mad_decoder_t *this = (mad_decoder_t *) malloc(sizeof(mad_decoder_t));
@@ -101,6 +104,12 @@
   free(sh->context);
 }
 
+/* fast 32-bit pseudo-random number generator */
+static inline unsigned long prng(unsigned long state)
+{
+  return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+}   
+
 /* utility to scale and round samples to 16 bits */
 static inline signed int scale(mad_fixed_t sample) {
   /* round */
@@ -116,9 +125,61 @@
   return sample >> (MAD_F_FRACBITS + 1 - 16);
 }
 
+/* dithers 24-bit output to 16 bits instead of simple rounding */
+/* code from madplay */
+static inline signed int dither(mad_fixed_t sample, audio_dither *dither) {
+  unsigned int scalebits;
+  mad_fixed_t output, mask, random;
+
+  enum {
+    MIN = -MAD_F_ONE,
+    MAX =  MAD_F_ONE - 1
+  };
+
+  /* noise shape */
+  sample += dither->error[0] - dither->error[1] + dither->error[2];
+
+  dither->error[2] = dither->error[1];
+  dither->error[1] = dither->error[0] / 2;
+
+  /* bias */
+  output = sample + (1L << (MAD_F_FRACBITS + 1 - 16 - 1));
+
+  scalebits = MAD_F_FRACBITS + 1 - 16;
+  mask = (1L << scalebits) - 1;
+
+  /* dither */
+  random  = prng(dither->random);
+  output += (random & mask) - (dither->random & mask);
+
+  dither->random = random;
+
+  /* clip */
+  /* TODO: better clipping function */
+  if (sample >= MAD_F_ONE)
+    sample = MAD_F_ONE - 1;
+  else if (sample < -MAD_F_ONE)
+    sample = -MAD_F_ONE;
+  if (output >= MAD_F_ONE)
+    output = MAD_F_ONE - 1;
+  else if (output < -MAD_F_ONE)
+    output = -MAD_F_ONE;
+
+  /* quantize */
+  output &= ~mask;
+
+  /* error feedback */
+  dither->error[0] = sample - output;
+
+  /* scale */
+  return output >> scalebits;
+}
+
+
 static int decode_audio(sh_audio_t *sh,unsigned char *buf,int minlen,int maxlen){
   mad_decoder_t *this = (mad_decoder_t *) sh->context;
   int len=0;
+  static audio_dither left_dither, right_dither;
 
   while(len<minlen && len+4608<=maxlen){
     if(!this->have_frame) this->have_frame=read_frame(sh);
@@ -143,10 +204,12 @@
 	  while (nsamples--) {
 	    /* output sample(s) in 16-bit signed little-endian PCM */
 	    
-	    *output++ = scale(*left_ch++);
+/*	    *output++ = scale(*left_ch++);*/
+	    *output++ = dither(*left_ch++, &left_dither);
 	    
 	    if (nchannels == 2) 
-	      *output++ = scale(*right_ch++);
+/*	      *output++ = scale(*right_ch++); */
+	      *output++ = dither(*right_ch++, &right_dither);
 
 	  }
 	}


More information about the MPlayer-dev-eng mailing list