From 898ef6f29923fbe87e983e2f3ad2cc9c8bba09db Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:46:34 +0800 Subject: [PATCH 01/22] New ResyncVocals --- source/funkin/game/PlayState.hx | 38 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index baad9d40d..54397b5aa 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -559,8 +559,8 @@ class PlayState extends MusicBeatState @:noCompletion @:dox(hide) private var _startCountdownCalled:Bool = false; @:noCompletion @:dox(hide) private var _endSongCalled:Bool = false; - @:dox(hide) - var __vocalSyncTimer:Float = 1; + @:dox(hide) var __vocalSyncTimer:Float = 0; + @:dox(hide) var __vocalOffsetTimer:Float = 0; private function get_accuracy():Float { if (accuracyPressedNotes <= 0) return -1; @@ -1391,17 +1391,39 @@ class PlayState extends MusicBeatState } } else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) { - __vocalSyncTimer = 1; + __vocalSyncTimer = 1 / 30; var instTime = FlxG.sound.music.getActualTime(); - var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 100; - if (!isOffsync) { - for (strumLine in strumLines.members) { - if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 100)) break; + + var vocalsLoaded = vocals.loaded; + var vocalTime = vocalsLoaded ? vocals.getActualTime() : instTime; + var offsetTime = instTime - vocalTime; + + for (i in 0...strumLines.members.length) + { + var strumLine = strumLines.members[i]; + var strumVocals = strumLine.vocals; + + if (strumVocals.loaded) + { + var strumVocalTime = strumVocals.getActualTime(); + var currentOffset = instTime - strumVocalTime; + + if (currentOffset * currentOffset > offsetTime * offsetTime) + offsetTime = currentOffset; } } - if (isOffsync) resyncVocals(); + __vocalOffsetTimer += (offsetTime - __vocalOffsetTimer) * __vocalSyncTimer * 60 * 0.1; + + + // abs + if (__vocalOffsetTimer * __vocalOffsetTimer > 100) // +-10ms + { + // trace('ResyncVocals - OffsetTimer: ' + __vocalOffsetTimer); + __vocalOffsetTimer = 0; + resyncVocals(); + } } while(events.length > 0 && events.last().time <= Conductor.songPosition) From 60b6842cc3f8246d5892fc62851e9cb1a5ff8956 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:07:29 +0800 Subject: [PATCH 02/22] Update PlayState.hx --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 54397b5aa..771c0f423 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1418,7 +1418,7 @@ class PlayState extends MusicBeatState // abs - if (__vocalOffsetTimer * __vocalOffsetTimer > 100) // +-10ms + if (__vocalOffsetTimer * __vocalOffsetTimer > 64) // +-8ms { // trace('ResyncVocals - OffsetTimer: ' + __vocalOffsetTimer); __vocalOffsetTimer = 0; From 159225791448a7f96a8676e42144f887f5adc133 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 9 Sep 2025 18:17:12 +0800 Subject: [PATCH 03/22] Stabilize the detection mechanism. --- source/funkin/game/PlayState.hx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 771c0f423..2e8ba1b03 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1391,7 +1391,8 @@ class PlayState extends MusicBeatState } } else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) { - __vocalSyncTimer = 1 / 30; + __vocalSyncTimer += 1 / 30; + if (__vocalSyncTimer < -1 / 30) __vocalSyncTimer = -1 / 30; var instTime = FlxG.sound.music.getActualTime(); @@ -1414,13 +1415,15 @@ class PlayState extends MusicBeatState } } - __vocalOffsetTimer += (offsetTime - __vocalOffsetTimer) * __vocalSyncTimer * 60 * 0.1; + __vocalOffsetTimer += (offsetTime - __vocalOffsetTimer) * (1 / 30) * 60 * 0.04; + // trace('OffsetTimer: ' + __vocalOffsetTimer + ' - Timer: ' + __vocalSyncTimer); // abs - if (__vocalOffsetTimer * __vocalOffsetTimer > 64) // +-8ms + if (__vocalOffsetTimer * __vocalOffsetTimer > 50) // +-7.071ms { // trace('ResyncVocals - OffsetTimer: ' + __vocalOffsetTimer); + __vocalSyncTimer += 1; __vocalOffsetTimer = 0; resyncVocals(); } From 388670c42fb9a8654a05486d8e019a98a4b3d990 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 9 Sep 2025 18:54:42 +0800 Subject: [PATCH 04/22] Update PlayState.hx Unless any issues are discovered, this should be the final version of the modifications. --- source/funkin/game/PlayState.hx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 2e8ba1b03..451a36a22 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1417,13 +1417,8 @@ class PlayState extends MusicBeatState __vocalOffsetTimer += (offsetTime - __vocalOffsetTimer) * (1 / 30) * 60 * 0.04; - // trace('OffsetTimer: ' + __vocalOffsetTimer + ' - Timer: ' + __vocalSyncTimer); - - // abs - if (__vocalOffsetTimer * __vocalOffsetTimer > 50) // +-7.071ms + if (__vocalOffsetTimer * __vocalOffsetTimer > 50) // ±7.071ms { - // trace('ResyncVocals - OffsetTimer: ' + __vocalOffsetTimer); - __vocalSyncTimer += 1; __vocalOffsetTimer = 0; resyncVocals(); } From 1c73cfa343c712d4204e9fbc373f65048c1ed76a Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Fri, 12 Sep 2025 20:39:34 +0800 Subject: [PATCH 05/22] Update PlayState.hx Alright, to prevent cases where a sudden large audio offset could cause the smoothed value to instantly exceed the threshold and trigger readjustment, I've made further modifications. I also took the opportunity to tweak a few other things. --- source/funkin/game/PlayState.hx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 451a36a22..9ae50adb8 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -561,6 +561,7 @@ class PlayState extends MusicBeatState @:dox(hide) var __vocalSyncTimer:Float = 0; @:dox(hide) var __vocalOffsetTimer:Float = 0; + @:dox(hide) var __vocalSmoothFactor:Float = 0.05; // (1 / 30) * 60 * 0.025 private function get_accuracy():Float { if (accuracyPressedNotes <= 0) return -1; @@ -1398,7 +1399,7 @@ class PlayState extends MusicBeatState var vocalsLoaded = vocals.loaded; var vocalTime = vocalsLoaded ? vocals.getActualTime() : instTime; - var offsetTime = instTime - vocalTime; + var maxOffset = instTime - vocalTime; for (i in 0...strumLines.members.length) { @@ -1410,12 +1411,14 @@ class PlayState extends MusicBeatState var strumVocalTime = strumVocals.getActualTime(); var currentOffset = instTime - strumVocalTime; - if (currentOffset * currentOffset > offsetTime * offsetTime) - offsetTime = currentOffset; + if (currentOffset * currentOffset > maxOffset * maxOffset) + maxOffset = currentOffset; } } - __vocalOffsetTimer += (offsetTime - __vocalOffsetTimer) * (1 / 30) * 60 * 0.04; + maxOffset = Math.min(Math.max(maxOffset, -40), 40); + + __vocalOffsetTimer += (maxOffset - __vocalOffsetTimer) * __vocalSmoothFactor; if (__vocalOffsetTimer * __vocalOffsetTimer > 50) // ±7.071ms { From 366320298ff429ae59667f5be1fd40076316636a Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Fri, 12 Sep 2025 20:50:31 +0800 Subject: [PATCH 06/22] Additional update --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 9ae50adb8..99a9a79a9 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -561,7 +561,7 @@ class PlayState extends MusicBeatState @:dox(hide) var __vocalSyncTimer:Float = 0; @:dox(hide) var __vocalOffsetTimer:Float = 0; - @:dox(hide) var __vocalSmoothFactor:Float = 0.05; // (1 / 30) * 60 * 0.025 + @:dox(hide) final __vocalSmoothFactor:Float = 0.05; // (1 / 30) * 60 * 0.025 private function get_accuracy():Float { if (accuracyPressedNotes <= 0) return -1; From 431723d1f477eefe1a39647542e0040c25e14d73 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 27 Sep 2025 21:57:43 +0800 Subject: [PATCH 07/22] NEW Resync Vocals --- source/funkin/game/PlayState.hx | 98 ++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 31 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 295357a3d..3976d0412 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -556,12 +556,18 @@ class PlayState extends MusicBeatState */ public var hitWindow:Float = Options.hitWindow; // is calculated in create(), is safeFrames in milliseconds. + /** + * Whether or not to use pitch correction when resyncing vocals. + * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. + */ + public var usePitchCorrection:Bool = true; + @:noCompletion @:dox(hide) private var _startCountdownCalled:Bool = false; @:noCompletion @:dox(hide) private var _endSongCalled:Bool = false; @:dox(hide) var __vocalSyncTimer:Float = 0; @:dox(hide) var __vocalOffsetTimer:Float = 0; - @:dox(hide) final __vocalSmoothFactor:Float = 0.05; // (1 / 30) * 60 * 0.025 + @:dox(hide) var __sounds:Array>; private function get_accuracy():Float { if (accuracyPressedNotes <= 0) return -1; @@ -1237,6 +1243,29 @@ class PlayState extends MusicBeatState super.onFocusLost(); } + /** + * Call this function whenever you load new sounds + * to make sure the sound list is up to date + */ + public function soundUpdate():Void + { + var sounds:Array> = []; + var idx:Int = 0; + + // add main vocals + if (vocals.loaded) sounds[idx++] = [vocals, 0]; + + // also add strumline vocals + var sl = strumLines.members; + var sln = sl.length; + for (i in 0...sln) { + var sv = sl[i].vocals; + if (sv.loaded) sounds[idx++] = [sv, 0]; // [sound, offset] + } + + __sounds = sounds; // update sound list + } + @:dox(hide) function resyncVocals():Void { @@ -1399,40 +1428,47 @@ class PlayState extends MusicBeatState startSong(); } } - else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) { - __vocalSyncTimer += 1 / 30; - if (__vocalSyncTimer < -1 / 30) __vocalSyncTimer = -1 / 30; - - var instTime = FlxG.sound.music.getActualTime(); - - var vocalsLoaded = vocals.loaded; - var vocalTime = vocalsLoaded ? vocals.getActualTime() : instTime; - var maxOffset = instTime - vocalTime; - - for (i in 0...strumLines.members.length) + else if (FlxG.sound.music != null) + { + if ((__vocalSyncTimer -= elapsed) <= 0) { - var strumLine = strumLines.members[i]; - var strumVocals = strumLine.vocals; - - if (strumVocals.loaded) - { - var strumVocalTime = strumVocals.getActualTime(); - var currentOffset = instTime - strumVocalTime; + __vocalSyncTimer = (__vocalSyncTimer < -0.1) ? -0.1 : __vocalSyncTimer + 0.1; // max 10fps - if (currentOffset * currentOffset > maxOffset * maxOffset) - maxOffset = currentOffset; + if (__sounds != null) { + final soundCount = __sounds.length; + if (soundCount > 0) + { + final mt = FlxG.sound.music.getActualTime(); // in ms + final vs = usePitchCorrection ? 2500 : 225; // 15ms for no pitch correction, 50ms for pitch correction + final pf = 0.00025; // pitch factor + final sm = 0.1; // smoothing + + // account for offset changes + var i = soundCount; + while (i-- > 0) + { + var sd:Array = __sounds[i]; + var s:FlxSound = sd[0]; + final ct = s.getActualTime(); + + final diff = mt - ct; + sd[1] += (diff - sd[1]) * sm; // smooth the difference + + if (usePitchCorrection) s.pitch = 1 + sd[1] * pf; // pitch adjustment + trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); + + final os = sd[1]; + if (os * os > vs) + { + sd[1] = 0; + s.play(true, Conductor.songPosition); // restart sound at music position + } + } + } + } else { + soundUpdate(); } } - - maxOffset = Math.min(Math.max(maxOffset, -40), 40); - - __vocalOffsetTimer += (maxOffset - __vocalOffsetTimer) * __vocalSmoothFactor; - - if (__vocalOffsetTimer * __vocalOffsetTimer > 50) // ±7.071ms - { - __vocalOffsetTimer = 0; - resyncVocals(); - } } while(events.length > 0 && events.last().time <= Conductor.songPosition) From 131dc09c672b3a181b2baf160bbaf109903134f4 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 27 Sep 2025 22:25:10 +0800 Subject: [PATCH 08/22] Update PlayState.hx --- source/funkin/game/PlayState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 3976d0412..6e01038fa 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1455,7 +1455,6 @@ class PlayState extends MusicBeatState sd[1] += (diff - sd[1]) * sm; // smooth the difference if (usePitchCorrection) s.pitch = 1 + sd[1] * pf; // pitch adjustment - trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); final os = sd[1]; if (os * os > vs) From 9dec8798e1fcef52b137c818af2a7600db8f2aa0 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 27 Sep 2025 22:28:58 +0800 Subject: [PATCH 09/22] eee --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 6e01038fa..fd686c0c3 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1439,7 +1439,7 @@ class PlayState extends MusicBeatState if (soundCount > 0) { final mt = FlxG.sound.music.getActualTime(); // in ms - final vs = usePitchCorrection ? 2500 : 225; // 15ms for no pitch correction, 50ms for pitch correction + final vs = usePitchCorrection ? 625 : 144; // 12ms for no pitch correction, 25ms for pitch correction final pf = 0.00025; // pitch factor final sm = 0.1; // smoothing From 34c5c1e6cec4fa1f75f078262022eefca31efb3d Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 27 Sep 2025 22:53:26 +0800 Subject: [PATCH 10/22] Combining Two Synchronization Methods Using time-stretching for minor corrections (subtle drift), and hard playback resets for high-latency scenarios. This approach minimizes audible pitch distortion while maintaining high-precision audio playback. --- source/funkin/game/PlayState.hx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index fd686c0c3..92413101f 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1256,10 +1256,11 @@ class PlayState extends MusicBeatState if (vocals.loaded) sounds[idx++] = [vocals, 0]; // also add strumline vocals - var sl = strumLines.members; - var sln = sl.length; - for (i in 0...sln) { - var sv = sl[i].vocals; + final sl = strumLines.members; + final sln = sl.length; + for (i in 0...sln) + { + final sv = sl[i].vocals; if (sv.loaded) sounds[idx++] = [sv, 0]; // [sound, offset] } @@ -1269,11 +1270,17 @@ class PlayState extends MusicBeatState @:dox(hide) function resyncVocals():Void { - var time = Conductor.songPosition + Conductor.songOffset; + final time = Conductor.songPosition + Conductor.songOffset; for (strumLine in strumLines.members) strumLine.vocals.play(true, time); vocals.play(true, time); if (!inst.playing) inst.play(true, time); + var sounds = __sounds; + var sln = sounds.length; + for (i in 0...sln) + sounds[i][1] = 0; + + gameAndCharsCall("onVocalsResync"); } @@ -1439,7 +1446,7 @@ class PlayState extends MusicBeatState if (soundCount > 0) { final mt = FlxG.sound.music.getActualTime(); // in ms - final vs = usePitchCorrection ? 625 : 144; // 12ms for no pitch correction, 25ms for pitch correction + final vs = usePitchCorrection ? 256 : 100; // 10ms for no pitch correction, 16ms for pitch correction final pf = 0.00025; // pitch factor final sm = 0.1; // smoothing @@ -1462,6 +1469,7 @@ class PlayState extends MusicBeatState sd[1] = 0; s.play(true, Conductor.songPosition); // restart sound at music position } + trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } } } else { From dcd8220b91ad5db0960627cccb162c7560f65357 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 27 Sep 2025 23:25:17 +0800 Subject: [PATCH 11/22] =?UTF-8?q?trace=20=F0=9F=98=B5=20=20=F0=9F=98=B5=20?= =?UTF-8?q?=20=F0=9F=98=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/funkin/game/PlayState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 92413101f..f65bce049 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1469,7 +1469,6 @@ class PlayState extends MusicBeatState sd[1] = 0; s.play(true, Conductor.songPosition); // restart sound at music position } - trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } } } else { From 16c90c1023460203ed3e4fa96ee51fa21714cebe Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:00:38 +0800 Subject: [PATCH 12/22] Update PlayState.hx --- source/funkin/game/PlayState.hx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index f65bce049..5db3ad60c 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1435,7 +1435,7 @@ class PlayState extends MusicBeatState startSong(); } } - else if (FlxG.sound.music != null) + else if (FlxG.sound.music != null && FlxG.sound.music.playing) { if ((__vocalSyncTimer -= elapsed) <= 0) { @@ -1456,6 +1456,8 @@ class PlayState extends MusicBeatState { var sd:Array = __sounds[i]; var s:FlxSound = sd[0]; + if (!s.playing) continue; + final ct = s.getActualTime(); final diff = mt - ct; @@ -1468,6 +1470,7 @@ class PlayState extends MusicBeatState { sd[1] = 0; s.play(true, Conductor.songPosition); // restart sound at music position + trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } } } From fb3278a7cbea7b8534e9cff5b8148556f0c0e11a Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:20:13 +0800 Subject: [PATCH 13/22] Update PlayState.hx --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 5db3ad60c..afa430cd0 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1470,8 +1470,8 @@ class PlayState extends MusicBeatState { sd[1] = 0; s.play(true, Conductor.songPosition); // restart sound at music position - trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } + trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } } } else { From 39993f364de4c3f2d835599842ea6795e99ec9d6 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Mon, 27 Oct 2025 18:09:46 +0800 Subject: [PATCH 14/22] FlxG.sound.music.getActualTime() --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 1909546bb..483203373 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1469,7 +1469,7 @@ class PlayState extends MusicBeatState if (os * os > vs) { sd[1] = 0; - s.play(true, Conductor.songPosition); // restart sound at music position + s.play(true, mt); // restart sound at music position } trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); } From 40ba57e0550a92a11373afe601d3af96b22199bb Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:48:40 +0800 Subject: [PATCH 15/22] Update PlayState.hx --- source/funkin/game/PlayState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 483203373..f2130c6a5 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1476,6 +1476,7 @@ class PlayState extends MusicBeatState } } else { soundUpdate(); + __vocalSyncTimer += 4.9; // next update in 5 seconds } } } From 443a496ce0bd1c63f100788edf8e77cbe3c0dd24 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:51:44 +0800 Subject: [PATCH 16/22] 0.9 --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index f2130c6a5..3c62ce24a 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1476,7 +1476,7 @@ class PlayState extends MusicBeatState } } else { soundUpdate(); - __vocalSyncTimer += 4.9; // next update in 5 seconds + __vocalSyncTimer += 0.9; // next update in 5 seconds } } } From 07a541637b3bd698ef3f2bda27cef9ae3a9a9105 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:06:50 +0800 Subject: [PATCH 17/22] Add vocal sync interval and pitch correction flags --- source/funkin/backend/system/Flags.hx | 11 +++++++++++ source/funkin/game/PlayState.hx | 25 ++++++++++++++----------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 1b96f7260..76821d407 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -169,6 +169,17 @@ class Flags { public static var GITAROO_CHANCE:Float = 0.1; public static var DEFAULT_MUTE_VOCALS_ON_MISS:Bool = true; + /** + * Whether or not to use pitch correction when resyncing vocals. + * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. + */ + public static var VOCAL_PITCH_CORRECTION:Bool = true; + /** + * Interval (in seconds) for vocal synchronization updates. + * Smaller values mean more frequent synchronization but higher CPU usage. + */ + public static var VOCAL_SYNC_INTERVAL:Float = 0.1; + public static var DEFAULT_MAX_HEALTH:Float = 2.0; public static var DEFAULT_HEALTH:Null = null;//DEFAULT_MAX_HEALTH / 2.0; public static var DEFAULT_ICONBOP:Bool = true; diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 3c62ce24a..e66d7ba8f 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -560,13 +560,15 @@ class PlayState extends MusicBeatState * Whether or not to use pitch correction when resyncing vocals. * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. */ - public var usePitchCorrection:Bool = true; + public var usePitchCorrection:Bool = Flags.VOCAL_PITCH_CORRECTION; @:noCompletion @:dox(hide) private var _startCountdownCalled:Bool = false; @:noCompletion @:dox(hide) private var _endSongCalled:Bool = false; - @:dox(hide) var __vocalSyncTimer:Float = 0; + @:dox(hide) var __vocalIntervalMoment:Float = 0.1; // moment for interval updates + @:dox(hide) var __vocalSyncTimer:Float = 0.1; @:dox(hide) var __vocalOffsetTimer:Float = 0; + @:dox(hide) var __vocalSound:Int = 0; @:dox(hide) var __sounds:Array>; private function get_accuracy():Float { @@ -1265,6 +1267,7 @@ class PlayState extends MusicBeatState } __sounds = sounds; // update sound list + __vocalIntervalMoment = 0.05; // reset interval moment } @:dox(hide) @@ -1439,7 +1442,7 @@ class PlayState extends MusicBeatState { if ((__vocalSyncTimer -= elapsed) <= 0) { - __vocalSyncTimer = (__vocalSyncTimer < -0.1) ? -0.1 : __vocalSyncTimer + 0.1; // max 10fps + __vocalSyncTimer = (__vocalSyncTimer < -__vocalIntervalMoment) ? 0 : __vocalSyncTimer + __vocalIntervalMoment; // max 10fps if (__sounds != null) { final soundCount = __sounds.length; @@ -1451,13 +1454,11 @@ class PlayState extends MusicBeatState final sm = 0.1; // smoothing // account for offset changes - var i = soundCount; - while (i-- > 0) - { - var sd:Array = __sounds[i]; - var s:FlxSound = sd[0]; - if (!s.playing) continue; + final i = __vocalSound; + var sd:Array = __sounds[i]; + var s:FlxSound = sd[0]; + if (s.playing) { final ct = s.getActualTime(); final diff = mt - ct; @@ -1471,12 +1472,14 @@ class PlayState extends MusicBeatState sd[1] = 0; s.play(true, mt); // restart sound at music position } - trace('Sound ' + i + ': music=' + mt + ', sound=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch); + trace('Sound ' + i + ': music=' + mt + ', time=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch + ', __vocalSyncTimer=' + __vocalSyncTimer); + + __vocalSound = (i + 1) % soundCount; } } } else { soundUpdate(); - __vocalSyncTimer += 0.9; // next update in 5 seconds + __vocalSyncTimer = 1; // next update in 1 seconds } } } From 6e19d323272f91192797e4d754e9789b80b39a8b Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:08:34 +0800 Subject: [PATCH 18/22] Remove __vocalOffsetTimer --- source/funkin/game/PlayState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index e66d7ba8f..d03e94b8c 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -567,7 +567,6 @@ class PlayState extends MusicBeatState @:dox(hide) var __vocalIntervalMoment:Float = 0.1; // moment for interval updates @:dox(hide) var __vocalSyncTimer:Float = 0.1; - @:dox(hide) var __vocalOffsetTimer:Float = 0; @:dox(hide) var __vocalSound:Int = 0; @:dox(hide) var __sounds:Array>; From 99a03f27ca499ba0c167c2733b37efcbf2443bd0 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:18:42 +0800 Subject: [PATCH 19/22] Improve vocal sync interval handling and docs --- source/funkin/backend/system/Flags.hx | 1 + source/funkin/game/PlayState.hx | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 76821d407..900453225 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -172,6 +172,7 @@ class Flags { /** * Whether or not to use pitch correction when resyncing vocals. * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. + * If you just want to adjust the overall playback speed, you can try modifying FlxG.timeScale. */ public static var VOCAL_PITCH_CORRECTION:Bool = true; /** diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index d03e94b8c..58eae33a7 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -557,8 +557,9 @@ class PlayState extends MusicBeatState public var hitWindow:Float = Options.hitWindow; // is calculated in create(), is safeFrames in milliseconds. /** - * Whether or not to use pitch correction when resyncing vocals. - * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. + * Whether or not to use pitch correction when resyncing vocals. + * Without using pitch adjustment, the audio may occasionally exhibit subtle sync drift. + * If you just want to adjust the overall playback speed, you can try modifying FlxG.timeScale. */ public var usePitchCorrection:Bool = Flags.VOCAL_PITCH_CORRECTION; @@ -1265,8 +1266,14 @@ class PlayState extends MusicBeatState if (sv.loaded) sounds[idx++] = [sv, 0]; // [sound, offset] } + if (sounds.length < 1) + { + __sounds = []; // no sounds + return; + } + __sounds = sounds; // update sound list - __vocalIntervalMoment = 0.05; // reset interval moment + __vocalIntervalMoment = Flags.VOCAL_SYNC_INTERVAL / sounds.length; // reset interval moment } @:dox(hide) @@ -1450,7 +1457,7 @@ class PlayState extends MusicBeatState final mt = FlxG.sound.music.getActualTime(); // in ms final vs = usePitchCorrection ? 256 : 100; // 10ms for no pitch correction, 16ms for pitch correction final pf = 0.00025; // pitch factor - final sm = 0.1; // smoothing + final sm = Flags.VOCAL_SYNC_INTERVAL; // smoothing // account for offset changes final i = __vocalSound; @@ -1471,7 +1478,7 @@ class PlayState extends MusicBeatState sd[1] = 0; s.play(true, mt); // restart sound at music position } - trace('Sound ' + i + ': music=' + mt + ', time=' + ct + ', diff=' + diff + ', smoothDiff=' + sd[1] + ', pitch=' + s.pitch + ', __vocalSyncTimer=' + __vocalSyncTimer); + trace('Sound ' + i + ': music=' + Math.round(mt) + ' time=' + Math.round(ct) + ' diff=' + Math.round(diff * 100) / 100 + ' smoothDiff=' + Math.round(sd[1] * 100) / 100 + ' pitch=' + Math.round(s.pitch * 100000) / 100000); __vocalSound = (i + 1) % soundCount; } From cead367a825bc71172c18075fc4790a5151115de Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:30:25 +0800 Subject: [PATCH 20/22] Update Flags.hx --- source/funkin/backend/system/Flags.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 900453225..3e23ec04c 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -179,7 +179,7 @@ class Flags { * Interval (in seconds) for vocal synchronization updates. * Smaller values mean more frequent synchronization but higher CPU usage. */ - public static var VOCAL_SYNC_INTERVAL:Float = 0.1; + public static var VOCAL_SYNC_INTERVAL:Float = 0.05; public static var DEFAULT_MAX_HEALTH:Float = 2.0; public static var DEFAULT_HEALTH:Null = null;//DEFAULT_MAX_HEALTH / 2.0; From cf0df4f010df400ba594b723e03daa91860b9c93 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:25:10 +0800 Subject: [PATCH 21/22] Better Algorithm . --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 58eae33a7..dcd9a603f 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1480,7 +1480,7 @@ class PlayState extends MusicBeatState } trace('Sound ' + i + ': music=' + Math.round(mt) + ' time=' + Math.round(ct) + ' diff=' + Math.round(diff * 100) / 100 + ' smoothDiff=' + Math.round(sd[1] * 100) / 100 + ' pitch=' + Math.round(s.pitch * 100000) / 100000); - __vocalSound = (i + 1) % soundCount; + __vocalSound = i + 1 >= soundCount ? 0 : i; // next sound } } } else { From 1a4e47999066810d3dc864ed3d398007a3727982 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:52:06 +0800 Subject: [PATCH 22/22] variable --- source/funkin/game/PlayState.hx | 71 ++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index dcd9a603f..a1cc19f89 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1251,29 +1251,31 @@ class PlayState extends MusicBeatState */ public function soundUpdate():Void { - var sounds:Array> = []; - var idx:Int = 0; + var soundList:Array> = []; + var currentIndex:Int = 0; // add main vocals - if (vocals.loaded) sounds[idx++] = [vocals, 0]; + if (vocals.loaded) + soundList[currentIndex++] = [vocals, 0]; // also add strumline vocals - final sl = strumLines.members; - final sln = sl.length; - for (i in 0...sln) + final strumlineArray = strumLines.members; + final strumlineCount = strumlineArray.length; + for (i in 0...strumlineCount) { - final sv = sl[i].vocals; - if (sv.loaded) sounds[idx++] = [sv, 0]; // [sound, offset] + final strumlineVocals = strumlineArray[i].vocals; + if (strumlineVocals.loaded) + soundList[currentIndex++] = [strumlineVocals, 0]; // [sound, offset] } - if (sounds.length < 1) + if (soundList.length < 1) { __sounds = []; // no sounds return; } - __sounds = sounds; // update sound list - __vocalIntervalMoment = Flags.VOCAL_SYNC_INTERVAL / sounds.length; // reset interval moment + __sounds = soundList; // update sound list + __vocalIntervalMoment = Flags.VOCAL_SYNC_INTERVAL / soundList.length; // reset interval moment } @:dox(hide) @@ -1450,37 +1452,42 @@ class PlayState extends MusicBeatState { __vocalSyncTimer = (__vocalSyncTimer < -__vocalIntervalMoment) ? 0 : __vocalSyncTimer + __vocalIntervalMoment; // max 10fps - if (__sounds != null) { - final soundCount = __sounds.length; - if (soundCount > 0) + if (__sounds != null) + { + final totalSounds = __sounds.length; + if (totalSounds > 0) { - final mt = FlxG.sound.music.getActualTime(); // in ms - final vs = usePitchCorrection ? 256 : 100; // 10ms for no pitch correction, 16ms for pitch correction - final pf = 0.00025; // pitch factor - final sm = Flags.VOCAL_SYNC_INTERVAL; // smoothing + final musicTime = FlxG.sound.music.getActualTime(); // in ms + final varianceThreshold = usePitchCorrection ? 256 : 100; // 10ms for no pitch correction, 16ms for pitch correction + final pitchAdjustmentFactor = 0.00025; // pitch factor + final smoothingFactor = Flags.VOCAL_SYNC_INTERVAL; // smoothing // account for offset changes - final i = __vocalSound; + final currentSoundIndex = __vocalSound; - var sd:Array = __sounds[i]; - var s:FlxSound = sd[0]; - if (s.playing) { - final ct = s.getActualTime(); + var soundData:Array = __sounds[currentSoundIndex]; + var vocalSound:FlxSound = soundData[0]; + if (vocalSound.playing) + { + final currentVocalTime = vocalSound.getActualTime(); - final diff = mt - ct; - sd[1] += (diff - sd[1]) * sm; // smooth the difference + final timeDifference = musicTime - currentVocalTime; + soundData[1] += (timeDifference - soundData[1]) * smoothingFactor; // smooth the difference - if (usePitchCorrection) s.pitch = 1 + sd[1] * pf; // pitch adjustment + if (usePitchCorrection) + vocalSound.pitch = 1 + soundData[1] * pitchAdjustmentFactor; // pitch adjustment - final os = sd[1]; - if (os * os > vs) + final smoothedOffset = soundData[1]; + if (smoothedOffset * smoothedOffset > varianceThreshold) { - sd[1] = 0; - s.play(true, mt); // restart sound at music position + soundData[1] = 0; + vocalSound.play(true, musicTime); // restart sound at music position } - trace('Sound ' + i + ': music=' + Math.round(mt) + ' time=' + Math.round(ct) + ' diff=' + Math.round(diff * 100) / 100 + ' smoothDiff=' + Math.round(sd[1] * 100) / 100 + ' pitch=' + Math.round(s.pitch * 100000) / 100000); + trace('Sound ' + currentSoundIndex + ': music=' + Math.round(musicTime) + ' time=' + Math.round(currentVocalTime) + ' diff=' + + Math.round(timeDifference * 100) / 100 + ' smoothDiff=' + Math.round(soundData[1] * 100) / 100 + ' pitch=' + + Math.round(vocalSound.pitch * 100000) / 100000); - __vocalSound = i + 1 >= soundCount ? 0 : i; // next sound + __vocalSound = currentSoundIndex + 1 >= totalSounds ? 0 : currentSoundIndex; // next sound } } } else {