Skip to content

Commit c879239

Browse files
committed
Fix PlayState sounds, and resyncing
1 parent 4129f4f commit c879239

File tree

2 files changed

+41
-116
lines changed

2 files changed

+41
-116
lines changed

source/funkin/play/PlayState.hx

Lines changed: 31 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,28 +1170,9 @@ class PlayState extends MusicBeatSubState
11701170
Conductor.instance.formatOffset = 0.0;
11711171
}
11721172

1173-
// Lime has some precision loss when getting the sound current position
1174-
// Since the notes scrolling is dependant on the sound time that caused it to appear "stuttery" for some people
1175-
// As a workaround for that, we lerp the conductor position to the music time to fill the gap in this lost precision making the scrolling smoother
1176-
// The previous method where it "guessed" the song position based on the elapsed time had some flaws
1177-
// Somtimes the songPosition would exceed the music length causing issues in other places
1178-
// And it was frame dependant which we don't like!!
11791173
if (FlxG.sound.music.playing)
11801174
{
1181-
final audioDiff:Float = Math.round(Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition - Conductor.instance.combinedOffset)));
1182-
if (audioDiff <= CONDUCTOR_DRIFT_THRESHOLD)
1183-
{
1184-
// Only do neat & smooth lerps as long as the lerp doesn't fuck up and go WAY behind the music time triggering false resyncs
1185-
final easeRatio:Float = 1.0 - Math.exp(-(MUSIC_EASE_RATIO * playbackRate) * elapsed);
1186-
Conductor.instance.update(FlxMath.lerp(Conductor.instance.songPosition, FlxG.sound.music.time + Conductor.instance.combinedOffset, easeRatio), false);
1187-
}
1188-
else
1189-
{
1190-
// Fallback to properly update the conductor incase the lerp messed up
1191-
// Shouldn't be fallen back to unless you're lagging alot
1192-
trace(' WARNING '.bg_yellow().bold() + ' Normal Conductor Update!! are you lagging?');
1193-
Conductor.instance.update();
1194-
}
1175+
Conductor.instance.update();
11951176
}
11961177
}
11971178

@@ -1551,31 +1532,19 @@ class PlayState extends MusicBeatSubState
15511532
FlxG.sound.music.pause();
15521533
musicPausedBySubState = true;
15531534
}
1535+
vocals?.pause();
15541536

15551537
// Pause any sounds that are playing and keep track of them.
15561538
// Vocals are also paused here but are not included as they are handled separately.
15571539
if (Std.isOfType(subState, PauseSubState))
15581540
{
15591541
FlxG.sound.list.forEachAlive(function(sound:FlxSound) {
1560-
if (!sound.active || sound == FlxG.sound.music) return;
1561-
// In case it's a scheduled sound
1562-
if (Std.isOfType(sound, FunkinSound))
1563-
{
1564-
var funkinSound:FunkinSound = cast sound;
1565-
if (funkinSound != null && !funkinSound.isPlaying) return;
1566-
}
1567-
if (!sound.playing && sound.time >= 0) return;
1568-
sound.pause();
1542+
if (!sound.playing || !sound.active || sound == FlxG.sound.music) return;
15691543
soundsPausedBySubState.add(sound);
15701544
});
1545+
vocals?.forEach(function(voice:FunkinSound) soundsPausedBySubState.remove(voice));
15711546

1572-
vocals?.forEach(function(voice:FunkinSound) {
1573-
soundsPausedBySubState.remove(voice);
1574-
});
1575-
}
1576-
else
1577-
{
1578-
vocals?.pause();
1547+
for (sound in soundsPausedBySubState) sound.pause();
15791548
}
15801549
}
15811550

@@ -1629,42 +1598,9 @@ class PlayState extends MusicBeatSubState
16291598

16301599
if (event.eventCanceled) return;
16311600

1632-
// Pause any sounds that are playing and keep track of them.
1633-
// Vocals are also paused here but are not included as they are handled separately.
1634-
if (!isGameOverState)
1635-
{
1636-
FlxG.sound.list.forEachAlive(function(sound:FlxSound) {
1637-
if (!sound.active || sound == FlxG.sound.music) return;
1638-
// In case it's a scheduled sound
1639-
if (Std.isOfType(sound, FunkinSound))
1640-
{
1641-
var funkinSound:FunkinSound = cast sound;
1642-
if (funkinSound != null && !funkinSound.isPlaying) return;
1643-
}
1644-
if (!sound.playing && sound.time >= 0) return;
1645-
sound.pause();
1646-
soundsPausedBySubState.add(sound);
1647-
});
1648-
1649-
vocals?.forEach(function(voice:FunkinSound) {
1650-
soundsPausedBySubState.remove(voice);
1651-
});
1652-
}
1653-
else
1654-
{
1655-
vocals?.pause();
1656-
}
1657-
16581601
// Resume vwooshTimer
16591602
if (!vwooshTimer.finished) vwooshTimer.active = true;
16601603

1661-
// Resume music if we paused it.
1662-
if (musicPausedBySubState)
1663-
{
1664-
if (FlxG.sound.music != null) FlxG.sound.music.play();
1665-
musicPausedBySubState = false;
1666-
}
1667-
16681604
// The logic here is that if this sound doesn't auto-destroy
16691605
// then it's gonna be reused somewhere, so we just stop it instead.
16701606
forEachPausedSound(s -> needsReset ? (s.autoDestroy ? s.destroy() : s.stop()) : s.resume());
@@ -1687,8 +1623,10 @@ class PlayState extends MusicBeatSubState
16871623
currentConversation.resumeMusic();
16881624
}
16891625

1690-
// Re-sync vocals.
1691-
if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals();
1626+
// If we have started the song, resync it instead that plays the song, else resume music if we paused it.
1627+
if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals(true);
1628+
else if (musicPausedBySubState) FlxG.sound.music?.play();
1629+
musicPausedBySubState = false;
16921630

16931631
// Resume the countdown.
16941632
Countdown.resumeCountdown();
@@ -1890,19 +1828,19 @@ class PlayState extends MusicBeatSubState
18901828
@:privateAccess // todo: maybe make the groups public :thinking:
18911829
{
18921830
vocals.playerVoices?.forEachAlive(function(voice:FunkinSound) {
1893-
var currentRawVoiceTime:Float = voice.time + vocals.playerVoicesOffset;
1894-
if (Math.abs(currentRawVoiceTime - correctSync) > Math.abs(playerVoicesError)) playerVoicesError = currentRawVoiceTime - correctSync;
1831+
var currentRawVoiceTime:Float = voice.getActualTime() + vocals.playerVoicesOffset;
1832+
if (Math.abs(currentRawVoiceTime - correctSync) > Math.abs(playerVoicesError) && voice.playing) playerVoicesError = currentRawVoiceTime - correctSync;
18951833
});
18961834

18971835
vocals.opponentVoices?.forEachAlive(function(voice:FunkinSound) {
1898-
var currentRawVoiceTime:Float = voice.time + vocals.opponentVoicesOffset;
1899-
if (Math.abs(currentRawVoiceTime - correctSync) > Math.abs(opponentVoicesError)) opponentVoicesError = currentRawVoiceTime - correctSync;
1836+
var currentRawVoiceTime:Float = voice.getActualTime() + vocals.opponentVoicesOffset;
1837+
if (Math.abs(currentRawVoiceTime - correctSync) > Math.abs(opponentVoicesError) && voice.playing) opponentVoicesError = currentRawVoiceTime - correctSync;
19001838
});
19011839
}
19021840
}
19031841

19041842
if (!startingSong
1905-
&& (Math.abs(FlxG.sound.music.time - correctSync) > RESYNC_THRESHOLD
1843+
&& (Math.abs(FlxG.sound.music.getActualTime() - correctSync) > RESYNC_THRESHOLD
19061844
|| Math.abs(playerVoicesError) > RESYNC_THRESHOLD
19071845
|| Math.abs(opponentVoicesError) > RESYNC_THRESHOLD))
19081846
{
@@ -1912,7 +1850,7 @@ class PlayState extends MusicBeatSubState
19121850
trace(playerVoicesError);
19131851
trace(opponentVoicesError);
19141852
}
1915-
trace(FlxG.sound.music.time);
1853+
trace(FlxG.sound.music.getActualTime());
19161854
trace(correctSync);
19171855
resyncVocals();
19181856
}
@@ -2588,7 +2526,7 @@ class PlayState extends MusicBeatSubState
25882526

25892527
if (!overrideMusic && !isGamePaused && currentChart != null)
25902528
{
2591-
currentChart?.playInst(1.0, currentInstrumental, false);
2529+
currentChart?.buildInst(1.0, currentInstrumental, false);
25922530
}
25932531

25942532
if (FlxG.sound.music == null)
@@ -2601,8 +2539,6 @@ class PlayState extends MusicBeatSubState
26012539
if (mayPauseGame) endSong(skipEndingTransition);
26022540
};
26032541

2604-
FlxG.sound.music.pause();
2605-
FlxG.sound.music.time = startTimestamp;
26062542
FlxG.sound.music.pitch = playbackRate;
26072543

26082544
if (Preferences.subtitles)
@@ -2624,7 +2560,6 @@ class PlayState extends MusicBeatSubState
26242560
trace('Playing vocals...');
26252561
add(vocals);
26262562

2627-
vocals.time = startTimestamp - Conductor.instance.instrumentalOffset;
26282563
vocals.pitch = playbackRate;
26292564
vocals.playerVolume = playerVocalsVolume;
26302565
vocals.opponentVolume = opponentVocalsVolume;
@@ -2633,10 +2568,10 @@ class PlayState extends MusicBeatSubState
26332568
// trace('${FlxG.sound.music.time}');
26342569
// trace('${vocals.time}');
26352570

2636-
vocals.play();
2571+
vocals.play(true, startTimestamp - Conductor.instance.instrumentalOffset);
26372572
}
26382573

2639-
FlxG.sound.music.play();
2574+
FlxG.sound.music.play(true, startTimestamp);
26402575

26412576
#if FEATURE_DISCORD_RPC
26422577
// Updating Discord Rich Presence (with Time Left)
@@ -2662,32 +2597,22 @@ class PlayState extends MusicBeatSubState
26622597
#if FEATURE_NEWGROUNDS
26632598
Events.logStartSong(currentSong.id, currentVariation);
26642599
#end
2665-
2666-
resyncVocals();
26672600
}
26682601

26692602
/**
26702603
* Resynchronize the vocal tracks if they have become offset from the instrumental.
26712604
*/
2672-
function resyncVocals():Void
2605+
function resyncVocals(force = false):Void
26732606
{
2674-
if (vocals == null) return;
2675-
2676-
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
2677-
if (!(FlxG.sound.music?.playing ?? false)) return;
2678-
2679-
var timeToPlayAt:Float = Math.min(FlxG.sound.music.length,
2680-
Math.max(Math.min(Conductor.instance.combinedOffset, 0), Conductor.instance.songPosition) - Conductor.instance.combinedOffset);
2681-
trace('Resyncing vocals to ${timeToPlayAt}');
2682-
2683-
FlxG.sound.music.pause();
2684-
vocals.pause();
2685-
2686-
FlxG.sound.music.time = timeToPlayAt;
2687-
FlxG.sound.music.play(false, timeToPlayAt);
2607+
if (FlxG.sound.music != null && force || (vocals != null && FlxG.sound.music.playing))
2608+
{
2609+
var timeToPlayAt:Float = Math.min(FlxG.sound.music.length,
2610+
Math.max(Math.min(Conductor.instance.combinedOffset, 0), Conductor.instance.songPosition) - Conductor.instance.combinedOffset);
2611+
trace('Resyncing vocals to ${timeToPlayAt}');
26882612

2689-
vocals.time = timeToPlayAt;
2690-
vocals.play(false, timeToPlayAt);
2613+
FlxG.sound.music.play(true, timeToPlayAt);
2614+
vocals?.play(true, timeToPlayAt);
2615+
}
26912616
}
26922617

26932618
/**
@@ -3407,8 +3332,9 @@ class PlayState extends MusicBeatSubState
34073332
*/
34083333
public function endSong(rightGoddamnNow:Bool = false):Void
34093334
{
3410-
if (FlxG.sound.music != null) FlxG.sound.music.volume = 0;
3411-
if (vocals != null) vocals.volume = 0;
3335+
FlxG.sound.music?.stop();
3336+
vocals?.stop();
3337+
34123338
mayPauseGame = false;
34133339
isSongEnd = true;
34143340

@@ -3843,7 +3769,6 @@ class PlayState extends MusicBeatSubState
38433769
}
38443770

38453771
persistentUpdate = false;
3846-
vocals?.stop();
38473772
camHUD.alpha = 1;
38483773

38493774
var talliesToUse:Tallies = PlayStatePlaylist.isStoryMode ? Highscore.talliesLevel : Highscore.tallies;
@@ -4087,7 +4012,7 @@ class PlayState extends MusicBeatSubState
40874012

40884013
handleSkippedNotes();
40894014
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
4090-
if (FlxG.sound.music != null && FlxG.sound.music.playing && preventDeath) regenNoteData(FlxG.sound.music.time);
4015+
if (FlxG.sound.music != null && FlxG.sound.music.playing && preventDeath) regenNoteData(FlxG.sound.music.getActualTime());
40914016

40924017
Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0);
40934018

source/funkin/play/song/Song.hx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -803,13 +803,18 @@ class SongDifficulty
803803
}
804804

805805
public function playInst(volume:Float = 1.0, instId:String = '', looped:Bool = false):Void
806+
{
807+
buildInst(volume, instId, looped).play(true, 0);
808+
}
809+
810+
public function buildInst(volume:Float = 1.0, instId:String = '', looped:Bool = false):FunkinSound
806811
{
807812
var suffix:String = (instId != '') ? '-$instId' : '';
808813

809-
FlxG.sound.music = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, true, false, null, null, true);
814+
var snd = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, false, false, null, null);
815+
FunkinSound.setMusic(snd);
810816

811-
// Workaround for a bug where FlxG.sound.music.update() was being called twice.
812-
FlxG.sound.list.remove(FlxG.sound.music);
817+
return snd;
813818
}
814819

815820
/**
@@ -962,21 +967,16 @@ class SongDifficulty
962967
for (playerVoice in playerVoiceList)
963968
{
964969
if (!Assets.exists(playerVoice)) continue;
965-
result.addPlayerVoice(FunkinSound.load(playerVoice, 1.0, false, false, false, false, null, null, true));
970+
result.addPlayerVoice(FunkinSound.load(playerVoice, 1.0, false, false, false, false, null, null));
966971
}
967972

968973
// Add opponent vocals.
969974
for (opponentVoice in opponentVoiceList)
970975
{
971976
if (!Assets.exists(opponentVoice)) continue;
972-
result.addOpponentVoice(FunkinSound.load(opponentVoice, 1.0, false, false, false, false, null, null, true));
977+
result.addOpponentVoice(FunkinSound.load(opponentVoice, 1.0, false, false, false, false, null, null));
973978
}
974979

975-
// Sometimes the sounds don't set their important value to true, so we have to do this manually.
976-
result.forEach(function(snd:FunkinSound) {
977-
snd.important = true;
978-
});
979-
980980
result.playerVoicesOffset = offsets.getVocalOffset(characters.player, instId);
981981
result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent, instId);
982982

0 commit comments

Comments
 (0)