ReplayGain support for OggVorbis files (mostly useful for fake CD tracks)
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 2 Sep 2007 14:24:44 +0000 (14:24 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 2 Sep 2007 14:24:44 +0000 (14:24 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7553 d7cf8633-e32d-0410-b094-e92efae38249

snd_main.c
snd_main.h
snd_mem.c
snd_ogg.c

index ce790b3..93deceb 100644 (file)
@@ -1172,7 +1172,6 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags,
        // Initialize the channel
        memset (target_chan, 0, sizeof (*target_chan));
        VectorCopy (origin, target_chan->origin);
-       target_chan->master_vol = (int)(fvol * 255);
        target_chan->sfx = sfx;
        target_chan->flags = flags;
        target_chan->pos = 0; // start of the sound
@@ -1189,6 +1188,9 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags,
 
        // Lock the SFX during play
        S_LockSfx (sfx);
+
+       // and finally, apply the volume
+       S_SetChannelVolume(target_chan - channels, fvol);
 }
 
 
@@ -1339,6 +1341,16 @@ void S_PauseGameSounds (qboolean toggle)
 
 void S_SetChannelVolume (unsigned int ch_ind, float fvol)
 {
+       sfx_t *sfx = channels[ch_ind].sfx;
+       if(sfx->volume_peak > 0)
+       {
+               // Replaygain support
+               // Con_DPrintf("Setting volume on ReplayGain-enabled track... %f -> ", fvol);
+               fvol *= sfx->volume_mult;
+               if(fvol * sfx->volume_peak > 1)
+                       fvol = 1 / sfx->volume_peak;
+               // Con_DPrintf("%f\n", fvol);
+       }
        channels[ch_ind].master_vol = (int)(fvol * 255.0f);
 }
 
index 3b53b62..5efc871 100644 (file)
@@ -75,6 +75,9 @@ struct sfx_s
        unsigned int            total_length;   // in sample frames
        const snd_fetcher_t     *fetcher;
        void                            *fetcher_data;  // Per-sfx data for the sound fetching functions
+
+       float                           volume_mult;    // for replay gain (multiplier to apply)
+       float                           volume_peak;    // for replay gain (highest peak); if set to 0, ReplayGain isn't supported
 };
 
 // maximum supported speakers constant
index 3da76b3..adb296f 100644 (file)
--- a/snd_mem.c
+++ b/snd_mem.c
@@ -319,6 +319,9 @@ qboolean S_LoadSound (sfx_t *sfx, qboolean complain)
        if (snd_renderbuffer == NULL)
                return false;
 
+       // Initialize volume peak to 0; if ReplayGain is supported, the loader will change this away
+       sfx->volume_peak = 0.0;
+
        // LordHavoc: if the sound filename does not begin with sound/, try adding it
        if (strncasecmp(sfx->name, "sound/", 6))
        {
index 28ccab1..8abe92c 100644 (file)
--- a/snd_ogg.c
+++ b/snd_ogg.c
@@ -622,13 +622,16 @@ Load an Ogg Vorbis file into memory
 qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
 {
        unsigned char *data;
-       const char *loopcomment;
+       const char *thiscomment;
        fs_offset_t filesize;
        ov_decode_t ov_decode;
        OggVorbis_File vf;
        vorbis_info *vi;
        vorbis_comment *vc;
        ogg_int64_t len, buff_len;
+       double peak = 0.0;
+       double gaindb = 0.0;
+       double gain;
 
        if (!vf_dll)
                return false;
@@ -694,9 +697,15 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
                vc = qov_comment(&vf, -1);
                if(vc)
                {
-                       loopcomment = qvorbis_comment_query(vc, "LOOP_START", 0);
-                       if(loopcomment)
-                               sfx->loopstart = bound(0, (unsigned int) (atof(loopcomment) * (double)snd_renderbuffer->format.speed / (double)per_sfx->format.speed), sfx->total_length);
+                       thiscomment = qvorbis_comment_query(vc, "LOOP_START", 0);
+                       if(thiscomment)
+                               sfx->loopstart = bound(0, (unsigned int) (atof(thiscomment) * (double)snd_renderbuffer->format.speed / (double)per_sfx->format.speed), sfx->total_length);
+                       thiscomment = qvorbis_comment_query(vc, "REPLAYGAIN_TRACK_PEAK", 0);
+                       if(thiscomment)
+                               peak = atof(thiscomment);
+                       thiscomment = qvorbis_comment_query(vc, "REPLAYGAIN_TRACK_GAIN", 0);
+                       if(thiscomment)
+                               gaindb = atof(thiscomment);
                }
        }
        else
@@ -746,9 +755,15 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
                vc = qov_comment(&vf, -1);
                if(vc)
                {
-                       loopcomment = qvorbis_comment_query(vc, "LOOP_START", 0);
-                       if(loopcomment)
-                               sfx->loopstart = bound(0, (unsigned int) (atoi(loopcomment) * (double)snd_renderbuffer->format.speed / (double)sb->format.speed), sfx->total_length);
+                       thiscomment = qvorbis_comment_query(vc, "LOOP_START", 0);
+                       if(thiscomment)
+                               sfx->loopstart = bound(0, (unsigned int) (atoi(thiscomment) * (double)snd_renderbuffer->format.speed / (double)sb->format.speed), sfx->total_length);
+                       thiscomment = qvorbis_comment_query(vc, "REPLAYGAIN_TRACK_PEAK", 0);
+                       if(thiscomment)
+                               peak = atof(thiscomment);
+                       thiscomment = qvorbis_comment_query(vc, "REPLAYGAIN_TRACK_GAIN", 0);
+                       if(thiscomment)
+                               gaindb = atof(thiscomment);
                }
 
                qov_clear (&vf);
@@ -756,5 +771,12 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
                Mem_Free (buff);
        }
 
+       if(peak)
+       {
+               sfx->volume_mult = min(1 / peak, exp(gaindb * 0.05 * log(10)));
+               sfx->volume_peak = peak;
+               Con_DPrintf ("\"%s\" uses ReplayGain (gain %f, peak %f)\n", filename, sfx->volume_mult, sfx->volume_peak);
+       }
+
        return true;
 }