[MPlayer-dev-eng] OggVorbis replaygain support

Rémi Guyomarch rguyom at pobox.com
Sun Nov 17 11:55:08 CET 2002


Hi,

I implemented VorbisGain support into the Ogg Vorbis audio
decoder. Such support is already present in the latest Windows OggDS
filter/decoder.

I'm attaching the VorbisGain README which explain what's this program
does and why. Note that VorbisGain is lossless, all it does is adding
a few comments in the Ogg audio stream. VorbisGain can be downloaded
here : http://sjeng.sourceforge.net/ftp/vorbis/vorbisgain-0.32.zip

This patch shouldn't make any difference with current behaviour if
VorbisGain comments aren't there (ov->rg_scale == 1).

Ok to commit ?

-- 
Rémi
-------------- next part --------------

INTRODUCTION
------------

When listening to Ogg Vorbis (or MP3) files on your computer, you may have
noticed that some songs are louder than others. To a degree, this can be
what the artist or producer intended, at least when comparing songs from the
same CD. However, when (randomly) listening to songs from different CDs, the
sound level can vary quite much, so much that you need to adjust the volume
every now and then, to get a comfortable sound level. Wouldn't it be nice if
that could be handled automatically?

This is where VorbisGain comes in. It calculates a percieved sound level of
an Ogg Vorbis file using the ReplayGain algorithm. VorbisGain then stores in
the comments (tags) in the file a suggestion on how the volume should be 
changed during playback, to get a uniform sound level. However, this only 
solves half the problem; the player application needs to act on that
suggestion for it to be any useful.

PLAYER SUPPORT
--------------

As VorbisGain only stores suggested volume changes in the files, the player
application need to read these suggestions and act upon them. As of this
writing, the Vorbis plugin (in_vorbis) 1.2b22 for WinAmp supports this, and
XMMS 1.2.8 will also support it (there is a patch available for earlier
versions of XMMS available; however, it does not support the new format
of the suggestions this version of VorbisGain implements).

INSTALLATION
------------

Run './configure' followed by 'make' (use './configure --help' to see
configuration options). Then use 'make install' to install it (or just copy
the vorbisgain executable to a directory of your choice).

For details on usage, please see the man page (or vorbisgain.txt, if you are
on a Windows platform).

ACKNOWLEDGEMENTS
----------------

VorbisGain uses the ReplayGain algorithm, which was created by David
Robinson. The implementation of that algorithm was written by Glen Sawyer
and Frank Klemm. Using some example code from Ogg Vorbis, the first versions
of VorbisGain (then called ReplayGain) was written by Gian-Carlo Pascutto,
who also wrote a patch for XMMS, adding support for the new tags. Better
Windows support and various new features where then added by Magnus
Holmgren, with help from Gian-Carlo Pascutto. Magnus Holmgren also updated
the XMMS patch to include support for the new tags (this patch has been sent
to the XMMS developers, but is otherwise not available).

For more information about ReplayGain, see http://www.replaygain.org/
(However, note that the ReplayGain web site might not be up to date when it
comes to application support for ReplayGain.)

NOTES
-----

Please note that as of VorbisGain 0.30, the volume change suggestions are
stored in a new format. The following is an example on how it can look:

    REPLAYGAIN_TRACK_GAIN=-7.03 dB
    REPLAYGAIN_TRACK_PEAK=1.21822226
    REPLAYGAIN_ALBUM_GAIN=-6.37 dB
    REPLAYGAIN_ALBUM_PEAK=1.21822226

Earlier versions stored the information like this:

    RG_RADIO=-7.03 dB
    RG_PEAK=1.21822226
    RG_AUDIOPHILE=-6.37 dB

(The lack of an album peak was one reasonto change the format; it was also
desirable to make the tag names easier to understand.)

The implementation of ReplayGain support in a player is quite easy actually.
At http://sjeng.org/ftp/vorbis/ you can find a patch for XMMS 1.2.6 that
shows how it can be done. Adding support for the new format (the old format
should be supported as well) is left as an excercise for the reader. :)

TODO
----

Although a configure script is used, it has not been widely tested. As of
this writing, it is only known to work on (a certain distribution of) Linux.
Corrections to handle other platforms are welcomed. Please send them to
Magnus Holmgren <lear at algonet.se> and/or Gian-Carlo Pascutto
<gcp at sjeng.org>.
-------------- next part --------------
Index: ad_libvorbis.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpcodecs/ad_libvorbis.c,v
retrieving revision 1.7
diff -u -u -r1.7 ad_libvorbis.c
--- ad_libvorbis.c	31 Aug 2002 13:09:23 -0000	1.7
+++ ad_libvorbis.c	17 Nov 2002 10:54:51 -0000
@@ -2,10 +2,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <stdarg.h>
+#include <math.h>
 
 #include "config.h"
 #include "ad_internal.h"
 
+#ifdef USE_SETLOCALE
+#include <locale.h>
+#endif
+
 #ifdef HAVE_OGGVORBIS
 
 static ad_info_t info = 
@@ -28,8 +34,27 @@
   vorbis_comment   vc; /* struct that stores all the bitstream user comments */
   vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
   vorbis_block     vb; /* local working space for packet->PCM decode */
+  float            rg_scale; /* replaygain scale */
 } ov_struct_t;
 
+static int read_vorbis_comment( char* ptr, char* comment, char* format, ... ) {
+  va_list va;
+  int clen, ret;
+
+  va_start( va, format );
+  clen = strlen( comment );
+#ifdef USE_SETLOCALE
+  setlocale( LC_NUMERIC, "C" );
+#endif
+  ret = strncasecmp( ptr, comment, clen) == 0 ? vsscanf( ptr+clen, format, va ) : 0;
+#ifdef USE_SETLOCALE
+  setlocale( LC_NUMERIC, "" );
+#endif
+  va_end( va );
+
+  return ret;
+}
+
 static int preinit(sh_audio_t *sh)
 {
   sh->audio_out_minsize=1024*4; // 1024 samples/frame
@@ -71,13 +96,39 @@
     mp_msg(MSGT_DECAUDIO,MSGL_WARN,"OggVorbis: codebook header broken!\n");
     ERROR();
   } else { /// Print the infos
+    float rg_gain=0.f, rg_peak=0.f;
     char **ptr=vc.user_comments;
     while(*ptr){
       mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbisComment: %s\n",*ptr);
+      /* replaygain */
+      read_vorbis_comment( *ptr, "replaygain_album_gain=", "%f", &rg_gain );
+      read_vorbis_comment( *ptr, "rg_audiophile=", "%f", &rg_gain );
+      if( !rg_gain ) {
+	read_vorbis_comment( *ptr, "replaygain_track_gain=", "%f", &rg_gain );
+	read_vorbis_comment( *ptr, "rg_radio=", "%f", &rg_gain );
+      }
+      read_vorbis_comment( *ptr, "replaygain_album_peak=", "%f", &rg_peak );
+      if( !rg_peak ) {
+	read_vorbis_comment( *ptr, "replaygain_track_peak=", "%f", &rg_peak );
+	read_vorbis_comment( *ptr, "rg_peak=", "%f", &rg_peak );
+      }
       ++ptr;
     }
-    mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Bitstream is %d channel, %dHz, %dbit/s %cBR\n",(int)ov->vi.channels,(int)ov->vi.rate,(int)ov->vi.bitrate_nominal,
+    /* replaygain: scale */
+    if(!rg_gain)
+      ov->rg_scale = 1.f; /* just in case pow() isn't standard-conformant */
+    else
+      ov->rg_scale = pow(10.f, rg_gain/20);
+    /* replaygain: anticlip */
+    if(ov->rg_scale * rg_peak > 1.f)
+      ov->rg_scale = 1.f / rg_peak;
+    /* replaygain: security */
+    if(ov->rg_scale > 15.) 
+      ov->rg_scale = 15.;
+    mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Bitstream is %d channel%s, %dHz, %dbit/s %cBR\n",(int)ov->vi.channels,ov->vi.channels>1?"s":"",(int)ov->vi.rate,(int)ov->vi.bitrate_nominal,
 	(ov->vi.bitrate_lower!=ov->vi.bitrate_nominal)||(ov->vi.bitrate_upper!=ov->vi.bitrate_nominal)?'V':'C');
+    if(rg_gain || rg_peak)
+      mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Gain = %+.2f dB, Peak = %.4f, Scale = %.2f\n", rg_gain, rg_peak, ov->rg_scale);
     mp_msg(MSGT_DECAUDIO,MSGL_V,"OggVorbis: Encoded by: %s\n",vc.vendor);
   }
   vorbis_comment_clear(&vc);
@@ -151,9 +202,9 @@
 	      float  *mono=pcm[i];
 	      for(j=0;j<bout;j++){
 #if 1
-		int val=mono[j]*32767.f;
+		int val=mono[j]*32767.f*ov->rg_scale;
 #else /* optional dither */
-		int val=mono[j]*32767.f+drand48()-0.5f;
+		int val=mono[j]*32767.f*ov->rg_scale+drand48()-0.5f;
 #endif
 		/* might as well guard against clipping */
 		if(val>32767){


More information about the MPlayer-dev-eng mailing list