Browse Source

Fix music position interpolation causing audio desync

kiwec 2 months ago
parent
commit
71075eedd3
4 changed files with 43 additions and 13 deletions
  1. 11 2
      src/App/Osu/Beatmap.cpp
  2. 20 5
      src/Engine/Sound.cpp
  3. 2 3
      src/Engine/Sound.h
  4. 10 3
      src/Engine/SoundEngine.cpp

+ 11 - 2
src/App/Osu/Beatmap.cpp

@@ -553,10 +553,13 @@ void Beatmap::skipEmptySection() {
 
     const long nextHitObjectDelta = m_iNextHitObjectTime - (long)m_iCurMusicPosWithOffsets;
 
-    if(!osu_end_skip.getBool() && nextHitObjectDelta < 0)
+    if(!osu_end_skip.getBool() && nextHitObjectDelta < 0) {
         m_music->setPositionMS(max(m_music->getLengthMS(), (u32)1) - 1);
-    else
+        m_bWasSeekFrame = true;
+    } else {
         m_music->setPositionMS(max(m_iNextHitObjectTime - (long)(offset * offsetMultiplier), (long)0));
+        m_bWasSeekFrame = true;
+    }
 
     engine->getSound()->play(osu->getSkin()->getMenuHit());
 
@@ -1039,6 +1042,7 @@ void Beatmap::actualRestart() {
 
     // reset position
     m_music->setPositionMS(0);
+    m_bWasSeekFrame = true;
     m_iCurMusicPos = 0;
 
     m_bIsPlaying = true;
@@ -1778,12 +1782,15 @@ void Beatmap::handlePreviewPlay() {
 
             if(start_at_song_beginning) {
                 m_music->setPositionMS(0);
+                m_bWasSeekFrame = true;
             } else if(m_iContinueMusicPos != 0) {
                 m_music->setPositionMS(m_iContinueMusicPos);
+                m_bWasSeekFrame = true;
             } else {
                 m_music->setPositionMS(m_selectedDifficulty2->getPreviewTime() < 0
                                            ? (unsigned long)(m_music->getLengthMS() * 0.40f)
                                            : m_selectedDifficulty2->getPreviewTime());
+                m_bWasSeekFrame = true;
             }
 
             m_music->setVolume(getIdealVolume());
@@ -2555,6 +2562,7 @@ void Beatmap::update2() {
                     engine->getSound()->play(m_music);
                     m_music->setLoop(false);
                     m_music->setPositionMS(0);
+                    m_bWasSeekFrame = true;
                     m_music->setVolume(getIdealVolume());
                     m_music->setSpeed(osu->getSpeedMultiplier());
 
@@ -2564,6 +2572,7 @@ void Beatmap::update2() {
                        m_hitobjects[0]->getTime() > (long)osu_quick_retry_time.getInt())
                         m_music->setPositionMS(
                             max((long)0, m_hitobjects[0]->getTime() - (long)osu_quick_retry_time.getInt()));
+                        m_bWasSeekFrame = true;
 
                     m_bIsRestartScheduledQuick = false;
 

+ 20 - 5
src/Engine/Sound.cpp

@@ -171,7 +171,9 @@ void Sound::destroy() {
     m_bReady = false;
     m_bAsyncReady = false;
     m_fLastPlayTime = 0.0;
+    m_fChannelCreationTime = 0.0;
     m_bPaused = false;
+    m_paused_position_ms = 0;
 
     if(m_bStream) {
         BASS_Mixer_ChannelRemove(m_stream);
@@ -216,7 +218,7 @@ void Sound::setPosition(double percent) {
     }
 
     if(m_bStarted) {
-        m_fLastPlayTime = engine->getTime() - (lengthInSeconds * percent);
+        m_fLastPlayTime = m_fChannelCreationTime - (lengthInSeconds * percent);
     }
 }
 
@@ -241,7 +243,7 @@ void Sound::setPositionMS(unsigned long ms) {
     }
 
     if(m_bStarted) {
-        m_fLastPlayTime = engine->getTime() - ((f64)ms / 1000.0);
+        m_fLastPlayTime = m_fChannelCreationTime - ((f64)ms / 1000.0);
     }
 }
 
@@ -317,6 +319,9 @@ float Sound::getPosition() {
         engine->showMessageError("Programmer Error", "Called getPosition on a sample!");
         return 0.f;
     }
+    if(m_bPaused) {
+        return (f64)m_paused_position_ms / (f64)m_length;
+    }
 
     i64 lengthBytes = BASS_ChannelGetLength(m_stream, BASS_POS_BYTE);
     if(lengthBytes < 0) {
@@ -335,6 +340,9 @@ u32 Sound::getPositionMS() {
         engine->showMessageError("Programmer Error", "Called getPositionMS on a sample!");
         return 0;
     }
+    if(m_bPaused) {
+        return m_paused_position_ms;
+    }
 
     i64 position = BASS_ChannelGetPosition(m_stream, BASS_POS_BYTE);
     if(position < 0) {
@@ -352,15 +360,22 @@ u32 Sound::getPositionMS() {
     // special case: a freshly started channel position jitters, lerp with engine time over a set duration to smooth
     // things over
     f64 interpDuration = snd_play_interp_duration.getFloat();
-    u32 interpDurationMS = interpDuration * 1000;
-    if(interpDuration <= 0.0 || positionMS >= interpDurationMS) return positionMS;
+    if(interpDuration <= 0.0) return positionMS;
+
+    f64 channel_age = engine->getTime() - m_fChannelCreationTime;
+    if(channel_age >= interpDuration) return positionMS;
 
     f64 speedMultiplier = getSpeed();
-    f64 delta = (engine->getTime() - m_fLastPlayTime) * speedMultiplier;
+    f64 delta = channel_age * speedMultiplier;
     f64 interp_ratio = snd_play_interp_ratio.getFloat();
     if(delta < interpDuration) {
+        auto pre_interp_pos = positionMS;
+        delta = (engine->getTime() - m_fLastPlayTime) * speedMultiplier;
         f64 lerpPercent = clamp<f64>(((delta / interpDuration) - interp_ratio) / (1.0 - interp_ratio), 0.0, 1.0);
         positionMS = (u32)lerp<f64>(delta * 1000.0, (f64)positionMS, lerpPercent);
+        if(pre_interp_pos != positionMS) {
+            debugLog("Interpolating music position! %d ms -> %d ms\n", pre_interp_pos, positionMS);
+        }
     }
 
     return positionMS;

+ 2 - 3
src/Engine/Sound.h

@@ -35,7 +35,6 @@ class Sound : public Resource {
     void setFrequency(float frequency);
     void setPan(float pan);
     void setLoop(bool loop);
-    void setLastPlayTime(double lastPlayTime) { m_fLastPlayTime = lastPlayTime; }
 
     float getPosition();
     u32 getPositionMS();
@@ -44,8 +43,6 @@ class Sound : public Resource {
     float getSpeed();
     float getFrequency();
 
-    inline double getLastPlayTime() const { return m_fLastPlayTime; }
-
     bool isPlaying();
     bool isFinished();
 
@@ -74,5 +71,7 @@ class Sound : public Resource {
     float m_fSpeed;
     float m_fVolume;
     f64 m_fLastPlayTime = 0.0;
+    f64 m_fChannelCreationTime = 0.0;
+    u32 m_paused_position_ms = 0;
     u32 m_length = 0;
 };

+ 10 - 3
src/Engine/SoundEngine.cpp

@@ -685,7 +685,7 @@ bool SoundEngine::play(Sound *snd, float pan, float pitch) {
     }
 
     if(snd->isOverlayable() && snd_restrict_play_frame.getBool()) {
-        if(engine->getTime() <= snd->getLastPlayTime()) {
+        if(engine->getTime() <= snd->m_fLastPlayTime) {
             return false;
         }
     }
@@ -729,8 +729,14 @@ bool SoundEngine::play(Sound *snd, float pan, float pitch) {
     }
 
     snd->m_bStarted = true;
-    snd->m_bPaused = false;
-    snd->setLastPlayTime(engine->getTime());
+    snd->m_fChannelCreationTime = engine->getTime();
+    if(snd->m_bPaused) {
+        snd->m_bPaused = false;
+        snd->m_fLastPlayTime = snd->m_fChannelCreationTime - (((f64)snd->m_paused_position_ms) / 1000.0);
+    } else {
+        snd->m_fLastPlayTime = snd->m_fChannelCreationTime;
+    }
+
     return true;
 }
 
@@ -756,6 +762,7 @@ void SoundEngine::pause(Sound *snd) {
     snd->setPan(pan);
     snd->setLoop(loop);
     snd->m_bPaused = true;
+    snd->m_paused_position_ms = pos;
 }
 
 void SoundEngine::stop(Sound *snd) {