From 82934ab22e531fa2afbd48404db340850aa3645e Mon Sep 17 00:00:00 2001
From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Date: Sun, 21 Sep 2025 17:08:55 +0800
Subject: [PATCH 1/4] Add Opus audio playback support
---
building/libs.xml | 1 +
project.xml | 7 +++++++
source/funkin/backend/chart/ChartData.hx | 2 ++
source/funkin/backend/scripting/Script.hx | 2 +-
source/funkin/game/PlayState.hx | 6 +++---
source/openfl/utils/Assets.hx | 21 +++++++++++++++++++++
6 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/building/libs.xml b/building/libs.xml
index 531a07713..897cf843e 100644
--- a/building/libs.xml
+++ b/building/libs.xml
@@ -18,6 +18,7 @@
+
diff --git a/project.xml b/project.xml
index 3d6399a14..76a1d0b58 100644
--- a/project.xml
+++ b/project.xml
@@ -115,6 +115,11 @@
+
+
+
@@ -127,6 +132,8 @@
+
+
diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx
index dfb514bde..c8ca39f43 100644
--- a/source/funkin/backend/chart/ChartData.hx
+++ b/source/funkin/backend/chart/ChartData.hx
@@ -39,6 +39,8 @@ typedef ChartMetaData = {
public var ?instSuffix:String;
public var ?vocalsSuffix:String;
public var ?needsVoices:Bool;
+
+ public var ?musicExt:String;
}
typedef ChartStrumLine = {
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index 7fae347d3..4f7070537 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -106,7 +106,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
#if TRANSLATIONS_SUPPORT
"TranslationUtil" => funkin.backend.utils.TranslationUtil,
- "translate" => funkin.backend.utils.TranslationUtil.get,
+ "translate" => funkin.backend.utils.TranslationUtil.get,
#end
];
}
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index baad9d40d..ebd3e2638 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -1121,11 +1121,11 @@ class PlayState extends MusicBeatState
curSong = songData.meta.name.toLowerCase();
curSongID = curSong.replace(" ", "-");
- FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix))));
+ FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix, SONG.meta.musicExt))));
- var vocalsPath = Paths.voices(SONG.meta.name, difficulty, SONG.meta.vocalsSuffix);
+ var vocalsPath = Paths.voices(SONG.meta.name, difficulty, SONG.meta.vocalsSuffix, SONG.meta.musicExt);
if (SONG.meta.needsVoices && Assets.exists(vocalsPath))
- vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath);
+ vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath, musicExt);
else
vocals = new FlxSound();
diff --git a/source/openfl/utils/Assets.hx b/source/openfl/utils/Assets.hx
index c14b9f2c9..92994153c 100644
--- a/source/openfl/utils/Assets.hx
+++ b/source/openfl/utils/Assets.hx
@@ -22,6 +22,9 @@ import lime.utils.Assets as LimeAssets;
import lime.media.AudioBuffer;
import lime.media.vorbis.VorbisFile;
#end
+#if OPUS_AUDIO_PLAYBACK
+import hxopus.Opus;
+#end
/**
The Assets class provides a cross-platform interface to access
@@ -310,6 +313,24 @@ class Assets
return sound;
}
+
+ #if OPUS_AUDIO_PLAYBACK
+ // Try to load as Opus if normal loading failed
+ var bytes = getBytes(id);
+
+ if (bytes != null)
+ {
+ var sound = Opus.toOpenFL(bytes);
+
+ if (useCache && cache.enabled)
+ {
+ cache.setSound(id, sound);
+ }
+
+ return sound;
+ }
+ #end
+
#end
return null;
From 98856f0261fe423024db472cd323d22e083e47ca Mon Sep 17 00:00:00 2001
From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Date: Sun, 21 Sep 2025 22:46:39 +0800
Subject: [PATCH 2/4] Alright, now the audio loading will attempt to read the
Opus file if it doesn't detect the OGG version.
---
source/funkin/backend/assets/Paths.hx | 53 ++++++++++++++++++++++--
source/funkin/backend/system/Flags.hx | 5 +++
source/funkin/editors/charter/Charter.hx | 6 +--
source/funkin/game/PlayState.hx | 2 +-
source/funkin/menus/FreeplayState.hx | 2 +-
5 files changed, 60 insertions(+), 8 deletions(-)
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index 5b8d8395f..4114584cf 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -81,25 +81,72 @@ class Paths
return getPath('data/$key.ps1', library);
static public function sound(key:String, ?library:String, ?ext:String)
+ {
+ for (path in Flags.SOUND_EXTENSIONS) {
+ var path = 'sounds/$key.$path';
+ if (OpenFlAssets.exists(path))
+ return getPath(path, library);
+ }
+
return getPath('sounds/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
+ }
public static inline function soundRandom(key:String, min:Int, max:Int, ?library:String)
return sound(key + FlxG.random.int(min, max), library);
- inline static public function music(key:String, ?library:String, ?ext:String)
+ static public function music(key:String, ?library:String, ?ext:String)
+ {
+ for (path in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'music/$key.$path';
+ if (OpenFlAssets.exists(getPath(path, library)))
+ return getPath(path, library);
+ }
+
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
+ }
- inline static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
+ static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
+
+ for (e in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'songs/$song/song/Voices$suffix-${difficulty}.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+
+ path = 'songs/$song/song/Voices$suffix.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+ }
+
var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null);
+ trace(diff);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null);
}
- inline static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
+ static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
+
+ for (e in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'songs/$song/song/Inst$suffix-${difficulty}.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+
+ path = 'songs/$song/song/Inst$suffix.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+ }
+
var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null);
+ trace(diff);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null);
}
diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx
index f57eb7aef..333f53875 100644
--- a/source/funkin/backend/system/Flags.hx
+++ b/source/funkin/backend/system/Flags.hx
@@ -56,6 +56,11 @@ class Flags {
public static var REPO_OWNER:String = "CodenameCrew";
public static var REPO_URL:String = 'https://github.com/$REPO_OWNER/$REPO_NAME';
+ public static var SOUND_EXTENSIONS:Array = [
+ #if web "mp3" #else "ogg" #end,
+ "opus"
+ ];
+
/**
* Preferred sound extension for the game's audio files.
* Currently is set to `mp3` for web targets, and `ogg` for other targets.
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index 952fdacac..1baac0a08 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -608,9 +608,9 @@ class Charter extends UIState {
Conductor.setupSong(PlayState.SONG);
noteTypes = PlayState.SONG.noteTypes;
- FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
- if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix)))
- vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix));
+ FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix, PlayState.SONG.meta.musicExt)));
+ if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix, PlayState.SONG.meta.musicExt)))
+ vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix, PlayState.SONG.meta.musicExt));
else
vocals = new FlxSound();
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index ebd3e2638..53423639d 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -1125,7 +1125,7 @@ class PlayState extends MusicBeatState
var vocalsPath = Paths.voices(SONG.meta.name, difficulty, SONG.meta.vocalsSuffix, SONG.meta.musicExt);
if (SONG.meta.needsVoices && Assets.exists(vocalsPath))
- vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath, musicExt);
+ vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath, SONG.meta.musicExt);
else
vocals = new FlxSound();
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 69fc3d8de..d82615356 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -259,7 +259,7 @@ class FreeplayState extends MusicBeatState
var dontPlaySongThisFrame = false;
autoplayElapsed += elapsed;
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
- if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
+ if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix, curSong.musicExt))) {
var streamed = false;
if (Options.streamedMusic) {
var sound = Assets.getMusic(curPlayingInst, true, false);
From 6af14cdd3052ed419b305de172a8d3fdfd671934 Mon Sep 17 00:00:00 2001
From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Date: Sun, 21 Sep 2025 23:18:30 +0800
Subject: [PATCH 3/4] If a file extension is already specified, it will
directly skip the multi-extension detection process
---
source/funkin/backend/assets/Paths.hx | 78 ++++++++++++++-------------
1 file changed, 41 insertions(+), 37 deletions(-)
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index 4114584cf..5b343cac6 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -82,11 +82,12 @@ class Paths
static public function sound(key:String, ?library:String, ?ext:String)
{
- for (path in Flags.SOUND_EXTENSIONS) {
- var path = 'sounds/$key.$path';
- if (OpenFlAssets.exists(path))
- return getPath(path, library);
- }
+ if (ext == null)
+ for (path in Flags.SOUND_EXTENSIONS) {
+ var path = 'sounds/$key.$path';
+ if (OpenFlAssets.exists(path))
+ return getPath(path, library);
+ }
return getPath('sounds/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
}
@@ -96,33 +97,35 @@ class Paths
static public function music(key:String, ?library:String, ?ext:String)
{
- for (path in Flags.SOUND_EXTENSIONS)
- {
- var path = 'music/$key.$path';
- if (OpenFlAssets.exists(getPath(path, library)))
- return getPath(path, library);
- }
+ if (ext == null)
+ for (path in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'music/$key.$path';
+ if (OpenFlAssets.exists(getPath(path, library)))
+ return getPath(path, library);
+ }
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
}
static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
- if (ext == null) ext = Flags.SOUND_EXT;
- for (e in Flags.SOUND_EXTENSIONS)
- {
- var path = 'songs/$song/song/Voices$suffix-${difficulty}.$e';
- trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
- if (OpenFlAssets.exists(getPath(path, null)))
- return getPath(path, null);
-
- path = 'songs/$song/song/Voices$suffix.$e';
- trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
- if (OpenFlAssets.exists(getPath(path, null)))
- return getPath(path, null);
- }
+ if (ext == null)
+ for (e in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'songs/$song/song/Voices$suffix-${difficulty}.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+
+ path = 'songs/$song/song/Voices$suffix.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+ }
+ if (ext == null) ext = Flags.SOUND_EXT;
var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null);
trace(diff);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null);
@@ -130,21 +133,22 @@ class Paths
static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
- if (ext == null) ext = Flags.SOUND_EXT;
- for (e in Flags.SOUND_EXTENSIONS)
- {
- var path = 'songs/$song/song/Inst$suffix-${difficulty}.$e';
- trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
- if (OpenFlAssets.exists(getPath(path, null)))
- return getPath(path, null);
-
- path = 'songs/$song/song/Inst$suffix.$e';
- trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
- if (OpenFlAssets.exists(getPath(path, null)))
- return getPath(path, null);
- }
+ if (ext == null)
+ for (e in Flags.SOUND_EXTENSIONS)
+ {
+ var path = 'songs/$song/song/Inst$suffix-${difficulty}.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+
+ path = 'songs/$song/song/Inst$suffix.$e';
+ trace(path + " | " + OpenFlAssets.exists(getPath(path, null)));
+ if (OpenFlAssets.exists(getPath(path, null)))
+ return getPath(path, null);
+ }
+ if (ext == null) ext = Flags.SOUND_EXT;
var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null);
trace(diff);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null);
From 57f7747e3daceab3886bbce21db45843c7550546 Mon Sep 17 00:00:00 2001
From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Date: Mon, 29 Sep 2025 20:20:49 +0800
Subject: [PATCH 4/4] Update Assets.hx
---
source/openfl/utils/Assets.hx | 8 --------
1 file changed, 8 deletions(-)
diff --git a/source/openfl/utils/Assets.hx b/source/openfl/utils/Assets.hx
index 92994153c..5ac054dcf 100644
--- a/source/openfl/utils/Assets.hx
+++ b/source/openfl/utils/Assets.hx
@@ -291,13 +291,10 @@ class Assets
var sound = cache.getSound(id);
if (isValidSound(sound))
- {
return sound;
- }
}
var buffer = LimeAssets.getAudioBuffer(id, false);
-
if (buffer != null)
{
#if flash
@@ -307,9 +304,7 @@ class Assets
#end
if (useCache && cache.enabled)
- {
cache.setSound(id, sound);
- }
return sound;
}
@@ -317,15 +312,12 @@ class Assets
#if OPUS_AUDIO_PLAYBACK
// Try to load as Opus if normal loading failed
var bytes = getBytes(id);
-
if (bytes != null)
{
var sound = Opus.toOpenFL(bytes);
if (useCache && cache.enabled)
- {
cache.setSound(id, sound);
- }
return sound;
}