Rockbox Technical Forums

Rockbox Development => Starting Development and Compiling => Topic started by: color43 on January 31, 2012, 01:22:28 AM

Title: ReplayGain as volume offset instead of "gain"
Post by: color43 on January 31, 2012, 01:22:28 AM
Hello all,

I'm trying to change how Rockbox uses ReplayGain. I want it to just add/subtract the replay gain value from the current volume behind-the-scenes instead of using the normal method which can cause clipping. For example, if my audio file has a replaygain of -5.75 dB, and my current volume is -25 dB, the volume would be set to -31 dB for this song. (the user will still see -25)

Basically, I edited the sound_set_volume function in /rockbox/firmware/sound.c to look like the following:

It works great when manually starting a new track or manually advancing to the next track.

The problem I'm having is when a new track is automatically started because the current track is over.

When this function is called in the above scenario, the mp3entry struct is for the "last" track, so the volume for the new track doesn't get set right.

Once the new track starts and I change the volume up or down, the code works like its supposed to.

My main question is: how can I get an mp3entry struct of the current track at the exact instant a new track automatically starts

Code: [Select]
void sound_set_volume(int value)
{  
    if(!audio_is_initialized)
        return;
        
    const int min_vol = sound_min(SOUND_VOLUME);
    const int max_vol = sound_max(SOUND_VOLUME);
    int volume = value;
    int volume_offset = 0;
    struct mp3entry *id3 = audio_current_track();
    
    if (id3 && global_settings.replaygain_type != REPLAYGAIN_OFF)
    {
        long gain = id3->track_level;
        volume_offset = abs(gain) >> 12;
        
        /* if this bit is set, the number's decimal part is .5 or greater */
        if (abs(gain) & (1 << 11))
            volume_offset++;
        if (gain < 0)
            volume_offset *= -1;
    }
    
    if (volume + volume_offset < min_vol)
    {
        volume = min_vol;
        volume_offset = 0;
    }
    if (value + volume_offset > max_vol)
    {
        volume = max_vol;
        volume_offset = 0;
    }
    
    volume += volume_offset;
    
    logf("act:%d off:%d vol:%d", value, volume_offset, volume);

#if defined(AUDIOHW_HAVE_CLIPPING)
    audiohw_set_volume(volume);
#elif CONFIG_CPU == PNX0101
    int tmp = (60 - volume * 4) & 0xff;
    CODECVOL = tmp | (tmp << 8);
#else
    current_volume = volume * 10;     /* tenth of dB */
    set_prescaled_volume();
#endif
}
Title: Re: ReplayGain as volume offset instead of "gain"
Post by: saratoga on January 31, 2012, 02:08:53 AM
I'm trying to change how Rockbox uses ReplayGain. I want it to just add/subtract the replay gain value from the current volume behind-the-scenes instead of using the normal method which can cause clipping.

I think you have this backwards.  The idea of applying the gain digitally is that it prevents clipping before it happens at the DAC.  Replaygain targets a reference level that is sufficiently quiet that clipping does not occur.  If you apply the gain on the analog side, you will get additional clipping since the volume adjustment will occur after the audio is already clipped.  Basically, you'll just scale the already clipped waveform up and down, which will give you equal loudness but more distortion on loud music.  I'm at a loss for a use case where this would be desirable.

Basically, I edited the sound_set_volume function in /rockbox/firmware/sound.c to look like the following:

Its probably easier to edit apps/dsp.c to set the volume directly when it applies gain.  Check out dsp_apply_gain.
Title: Re: ReplayGain as volume offset instead of "gain"
Post by: color43 on January 31, 2012, 02:22:50 AM
I forgot to mention that I removed most of the code in dsp_set_replaygain which stops Rockbox from doing its normal ReplayGain processing.

The method I'm using does not play with the waveform at all, it's just like manually changing the volume up or down with the buttons.

I'll take a look at the function you mentioned
Title: Re: ReplayGain as volume offset instead of "gain"
Post by: saratoga on January 31, 2012, 03:38:33 AM
The method I'm using does not play with the waveform at all, it's just like manually changing the volume up or down with the buttons.

Yes I know, you explained above.  You probably do not want to scale the volume rather then the gain since the way you're thinking won't prevent clipping on loud tracks. 

Basically, I think you're misunderstanding how replaygain works. 
Title: Re: ReplayGain as volume offset instead of "gain"
Post by: color43 on January 31, 2012, 03:50:27 AM
Say you have Replaygain set to "Off" in the playback settings. Now start playing a fairly loud track. Say the volume's at -25 dB and it's playing at a normal volume level. If I turn the volume up to -5 dB it would be blaring, but not clipping.

If it had a replaygain value of +20.00 dB and replay gain was on, it would clip all over the place?

How can my method cause clipping if its turning up the volume and disabling all gain handling?

Here's why I'm doing this: I have some podcasts that are pretty quiet compared to other files. I manually put my own ID3 tag on there with a replaygain_track_gain of +10.00 dB. I just want my Rockbox player to play it 10 dB (or 10 volume clicks) louder without it clipping.

Title: Re: ReplayGain as volume offset instead of "gain"
Post by: saratoga on January 31, 2012, 12:06:30 PM
Say you have Replaygain set to "Off" in the playback settings. Now start playing a fairly loud track. Say the volume's at -25 dB and it's playing at a normal volume level. If I turn the volume up to -5 dB it would be blaring, but not clipping.

Actually, that probably would be clipping if the source is an mp3.

Theres two kinds of clipping on an mp3 player.  The first is the one you're thinking of, analog clipping due to the amplifier being driven above 0dB.  The second is digital clipping, due to the level at the DAC being driven above 1.0.  Replaygain combined with not setting the volume above 0dB prevents both types, but only as we've implemented it.  

If it had a replaygain value of +20.00 dB and replay gain was on, it would clip all over the place?

If a file actually has a +20dB RG tag, it won't clip at +20 dB generally, but if you want to be sure, you can enable the "prevent clipping" option in rockbox.  This will prevent clipping even on tracks you've edited the tags.

How can my method cause clipping if its turning up the volume and disabling all gain handling?

Because you're effectively disabling replaygain's clipping protection.  If you play a normal peak normalized mp3 track without replaygain (or with your method) you'll certainly get clipping unless you compromise and lower the gain through a preamp because quantization error pushes the level above 1.0.  But a preamp sucks since you'll lose volume needlessly.  Replaygain fixes that by allowing you to have the maximum possible volume that does not clip.    

Here's why I'm doing this: I have some podcasts that are pretty quiet compared to other files. I manually put my own ID3 tag on there with a replaygain_track_gain of +10.00 dB. I just want my Rockbox player to play it 10 dB (or 10 volume clicks) louder without it clipping.

You can already do this without modifying rockbox or your replaygain tags:

1)  Undo any manual changes to your replaygain tags.
2) Turn the replaygain preamp all the way up to +12 dB.*
3)  Select "prevent clipping" under replaygain option in rockbox.
4)  Set the volume to 0dB.

This will give you the loudest possible undistorted output your player is physically capable of for a given track.  If you can tolerate some clipping, you can push the volume up to +1 or 2dB.  If thats still not loud enough you can enable dynamic range compression in rockbox which will squeeze the dynamics in a track to make it sound "louder".  Beyond that you'll need an amp or better headphones.



*If you just want to do it for one track, editing the tags is ok as well.