Shuttle2 icon indicating copy to clipboard operation
Shuttle2 copied to clipboard

Fix ReplayGain Issue

Open timusus opened this issue 3 months ago • 0 comments

Description:

Summary

Fixes #121 - ReplayGain volume adjustments are now applied before playback starts instead of after, eliminating the audible volume shift that occurred ~500ms into each track.

Problem

The original issue reported that "ReplayGain volume adjustments are only applied after the track begins playing, which leads to a noticeable change in volume a fraction of a second into the track."

Root Cause:

  • setReplayGain() was called in the success callback AFTER player.prepare() completed
  • This meant the audio processing pipeline started with incorrect gain values
  • The ~500ms delay corresponded to the time it took to load, prepare, and start playback

Solution

The fix uses a proactive update model with ReplayGain values embedded in MediaItems:

1. MediaItem Tagging Architecture

fun getMediaItem(mediaInfo: MediaInfo, trackGain: Double?, albumGain: Double?): MediaItem =
    MediaItem.Builder()
        .setTag(ReplayGainTag(trackGain, albumGain))
        .build()
2. Manual Track Changes
// In load() - BEFORE prepare()
replayGainAudioProcessor.trackGain = current.replayGainTrack
replayGainAudioProcessor.albumGain = current.replayGainAlbum
player.prepare()  // Audio processing starts after gain is set
3. Automatic Track Transitions
// In onMediaItemTransition
mediaItem?.localConfiguration?.tag?.let { tag ->
    if (tag is ReplayGainTag) {
        replayGainAudioProcessor.trackGain = tag.trackGain
        replayGainAudioProcessor.albumGain = tag.albumGain
    }
}
Changes
PlaybackManager.kt: Removed reactive setReplayGain() calls
ExoPlayerPlayback.kt: Added MediaItem tagging, proactive ReplayGain updates
ReplayGainAudioProcessor.kt: Enhanced thread safety with @Volatile and documentation
Known Limitations
During gapless playback, there's a potential ~10-50ms window where a few audio buffers from the new track may be processed with the previous track's gain due to ExoPlayer's async callback model vs continuous audio rendering. This is ~90-95% smaller than the original 500ms issue and is likely imperceptible.

timusus avatar Nov 16 '25 06:11 timusus