Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
The loudness of audio files varies significantly. In USDX, I need to frequently adjust the volume to compensate for the variable loudness, which is very inconvenient.
ReplayGain is a common solution to the variable loudness problem. A scanning program measures the loudness of a file and "tags" it with metadata. Then, the player can read the metadata tag and automatically adjust the volume of the file during playback, so all files play back at the same perceived volume.
USDX does not currently support ReplayGain (see #352, #638). The current workaround for loudness normalization is to encode the volume changes into the audio file itself. However, this is undesirable because it is a lossy operation, unlike ReplayGain, which is lossless.
Proposed Implementation
This PR adds ReplayGain functionality to USDX, using the existing Sound->Music Gain setting to minimize changes to the UI. The currently available options for this setting are: 'Soft', 'Medium', and 'Hard'. This PR adds 'ReplayGain' as another option.
The Soft/Medium/Hard presets are described in the code as "ReplayGain", but it's not actually ReplayGain. It just applies a static adjustment to the playback stream, irrespective of the characteristics of the file (see code here). This feature is also exclusive to the BASS playback engine. So, not functional for non-Windows users, despite being exposed in the Options UI.
The new ReplayGain feature uses the FFmpeg audio decoder to access FFmpeg's parsed dictionary of metadata. The program looks for the
REPLAYGAIN_TRACK_GAIN
tag, plusR128_TRACK_GAIN
for Opus files. If found, the program will store the requested volume adjustment in a new property inTAudioPlaybackStream
calledRGAdjustment
. Then, USDX will apply the requested volume adjustment during playback.The reason I chose FFmpeg's metadata implementation is because:
Summary of Code Changes
Here is a summary of the code changes:
TReplayGain
/FReplayGain
->TAutoGain
/FAutoGain
TReplayGainBass
->TAutoGainBass
TFFmpegDecodeStream
to parse ReplayGain value from fileRGAdjustment
property toTAudioPlaybackStream
, which is used to adjust the stream volume during playback. This adjustment is applied in addition to the volume setting. It does not replace the volume. This ensures that fading continues to work.EnableReplayGain
method toTAudioPlaybackStream
. This ensures that ReplayGain adjustments are only applied to the music stream. For example, if any of the game assets such as the background track are inadvertently tagged with ReplayGain metadata, it will not be applied because it's not enabled for that particular stream.Limitations
Windows Version
The ReplayGain parsing code is implemented in the FFmpeg implementation of
TAudioDecodeStream
. However, the Windows version uses the BASS audio decoder for most file formats, and the FFmpeg audio decoder is only used as a fallback for formats that BASS cannot handle.I saw in #750 there was discussion from the developers about removing the BASS audio decoder in favor of the FFmpeg audio decoder, and using BASS for audio input/playback only. However, it seems this change was never implemented. If the BASS audio decoder is removed, then all audio files will open with FFmpeg on all platforms, and my current ReplayGain implementation will work.
If removing the BASS audio decoder is not acceptable, I can look into refactoring the code by moving the ReplayGain tag parsing code into
TMediaCore_FFmpeg
. However, this would require opening and parsing the file twice on Windows: first in BASS then in FFmpeg. I think it would be best to harmonize around FFmpeg as disussed in #750 and avoid the duplicated effort.When I disable the BASS audio decoder locally, everything works fine in the Windows version.
Positive Gains
Currently, the program will not apply any positive gains in the ReplayGain tag. The reason is because there is no headroom available. The App volume and music stream volume are both set to 1 (0 dB).
Given that almost all commerically released pop music will have a negative gain, I don't see this as a huge issue. If positive gains must be supported, potential solutions could include turning down the app volume, or applying a constant negative preamp value to the music stream.
FFmpeg Bindings
I only changed the bindings for FFmpeg 5, 6, and 7. If support is needed for older versions, let me know and I can add the required changes for those bindings.
Fixes #352
Fixes #638