Skip to content

Commit 1aab4af

Browse files
LasercarRaltyro
authored andcommitted
Chart Editor all supported sound formats
Can now specify the extension of a music or sound file path. If none is passed, the default is used. Also makes waveform data nullable.
1 parent e78afa9 commit 1aab4af

File tree

14 files changed

+137
-88
lines changed

14 files changed

+137
-88
lines changed

source/funkin/Paths.hx

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,23 @@ class Paths implements ConsoleClass
4141
return parts[0];
4242
}
4343

44-
static function getPath(file:String, type:AssetType, library:Null<String>):String
44+
public static function fixPathExtension(path:String, defaultExtension:String):String
45+
{
46+
return if (path.lastIndexOf(".") == -1) '${path}.$defaultExtension'; else path;
47+
}
48+
49+
public static function normalizePath(path:String, ?defaultExtension:String):String
50+
{
51+
return if (defaultExtension == null) Path.normalize(path); else fixPathExtension(Path.normalize(path), defaultExtension);
52+
}
53+
54+
public static function getPath(file:String, type:AssetType, ?library:String):String
4555
{
4656
if (library != null) return getLibraryPath(file, library);
4757

4858
if (currentLevel != null)
4959
{
50-
var levelPath:String = getLibraryPathForce(file, currentLevel);
60+
var levelPath:String = getLibraryPath(file, currentLevel);
5161
if (Assets.exists(levelPath, type)) return levelPath;
5262
}
5363

@@ -84,47 +94,74 @@ class Paths implements ConsoleClass
8494

8595
public static function txt(key:String, ?library:String):String
8696
{
87-
return getPath('data/$key.txt', TEXT, library);
97+
return getPath(normalizePath('data/$key', 'txt'), TEXT, library);
8898
}
8999

90100
public static function frag(key:String, ?library:String):String
91101
{
92-
return getPath('shaders/$key.frag', TEXT, library);
102+
return getPath(normalizePath('shaders/$key', 'frag'), TEXT, library);
93103
}
94104

95105
public static function vert(key:String, ?library:String):String
96106
{
97-
return getPath('shaders/$key.vert', TEXT, library);
107+
return getPath(normalizePath('shaders/$key', 'vert'), TEXT, library);
98108
}
99109

100110
public static function xml(key:String, ?library:String):String
101111
{
102-
return getPath('data/$key.xml', TEXT, library);
112+
return getPath(normalizePath('data/$key', 'xml'), TEXT, library);
103113
}
104114

105115
public static function json(key:String, ?library:String):String
106116
{
107-
return getPath('data/$key.json', TEXT, library);
117+
return getPath(normalizePath('data/$key', 'json'), TEXT, library);
108118
}
109119

110-
public static function srt(key:String, ?library:String, ?directory:String = "data/"):String
120+
public static function srt(key:String, ?library:String, ?directory:String = "data"):String
111121
{
112-
return getPath('$directory$key.srt', TEXT, library);
122+
return getPath(normalizePath('${directory}/$key', 'srt'), TEXT, library);
113123
}
114124

115-
public static function sound(key:String, ?library:String):String
125+
public static function sound(key:String, ?library:String, ?directory:String = 'sounds', ?extension:String):String
116126
{
117-
return getPath('sounds/$key.${Constants.EXT_SOUND}', SOUND, library);
127+
var normalizedPath = Path.normalize((directory == '' ? '' : directory + '/') + key);
128+
if (extension != null) return getPath(fixPathExtension(normalizedPath, extension), SOUND, library);
129+
130+
// Attempt to find the sound by looping through the supported file formats.
131+
var path:String;
132+
for (extension in Constants.EXT_SOUNDS)
133+
{
134+
// no need to check if its exists in MUSIC type, as Openfl/Lime AssetLibrary have the same returns for SOUND and MUSIC internally.
135+
if (library != null)
136+
{
137+
path = getLibraryPath(fixPathExtension(normalizedPath, extension), library);
138+
if (Assets.exists(path, SOUND)/* || Assets.exists(path, MUSIC)*/) return path;
139+
}
140+
else
141+
{
142+
if (currentLevel != null)
143+
{
144+
path = getLibraryPath(fixPathExtension(normalizedPath, extension), currentLevel);
145+
if (Assets.exists(path, SOUND)/* || Assets.exists(path, MUSIC)*/) return path;
146+
}
147+
148+
path = getLibraryPathForce(fixPathExtension(normalizedPath, extension), 'shared');
149+
if (Assets.exists(path, SOUND)/* || Assets.exists(path, MUSIC)*/) return path;
150+
}
151+
}
152+
153+
if (library != null) return getLibraryPath(fixPathExtension(normalizedPath, Constants.EXT_SOUND), library);
154+
else return getPreloadPath(fixPathExtension(normalizedPath, Constants.EXT_SOUND));
118155
}
119156

120-
public static function soundRandom(key:String, min:Int, max:Int, ?library:String):String
157+
public static function soundRandom(key:String, min:Int, max:Int, ?library:String, ?extension:String):String
121158
{
122-
return sound(key + FlxG.random.int(min, max), library);
159+
return sound(key + FlxG.random.int(min, max), library, null, extension);
123160
}
124161

125-
public static function music(key:String, ?library:String):String
162+
public static function music(key:String, ?library:String, ?extension:String):String
126163
{
127-
return getPath('music/$key.${Constants.EXT_SOUND}', MUSIC, library);
164+
return sound(key, library, 'music', extension);
128165
}
129166

130167
public static function videos(key:String, ?library:String):String
@@ -139,24 +176,29 @@ class Paths implements ConsoleClass
139176
return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library ?? 'videos');
140177
}
141178

142-
public static function voices(song:String, ?suffix:String = ''):String
179+
public static function song(key:String, ?extension:String):String
143180
{
144-
if (suffix == null) suffix = ''; // no suffix, for a sorta backwards compatibility with older-ish voice files
181+
// For web platform that haven't loaded the library "songs" yet.
182+
if (Assets.getLibrary("songs") != null) return sound(key, 'songs', '', extension);
183+
else return getLibraryPathForce(normalizePath(key, extension ?? Constants.EXT_SOUND), 'songs');
184+
}
145185

146-
return 'songs:assets/songs/${song.toLowerCase()}/Voices$suffix.${Constants.EXT_SOUND}';
186+
public static function voices(song:String, ?suffix:String = '', ?extension:String):String
187+
{
188+
if (suffix == null) suffix = ''; // no suffix, for a sorta backwards compatibility with older-ish voice files
189+
return Paths.song('${song.toLowerCase()}/Voices$suffix', extension);
147190
}
148191

149192
/**
150193
* Gets the path to an `Inst.mp3/ogg` song instrumental from songs:assets/songs/`song`/
151194
* @param song name of the song to get instrumental for
152195
* @param suffix any suffix to add to end of song name, used for `-erect` variants usually
153-
* @param withExtension if it should return with the audio file extension `.mp3` or `.ogg`.
196+
* @param extension The audio file extension of the track. If empty, the default extension is passed.
154197
* @return String
155198
*/
156-
public static function inst(song:String, ?suffix:String = '', withExtension:Bool = true):String
199+
public static function inst(song:String, ?suffix:String = '', ?extension:String):String
157200
{
158-
var ext:String = withExtension ? '.${Constants.EXT_SOUND}' : '';
159-
return 'songs:assets/songs/${song.toLowerCase()}/Inst$suffix$ext';
201+
return Paths.song('${song.toLowerCase()}/Inst$suffix', extension);
160202
}
161203

162204
public static function image(key:String, ?library:String):String

source/funkin/audio/FunkinSound.hx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
5757
* Waveform data for this sound.
5858
* This is lazily loaded, so it will be built the first time it is accessed.
5959
*/
60-
public var waveformData(get, never):WaveformData;
60+
public var waveformData(get, never):Null<WaveformData>;
6161

6262
var _waveformData:Null<WaveformData> = null;
6363

64-
function get_waveformData():WaveformData
64+
function get_waveformData():Null<WaveformData>
6565
{
6666
if (_waveformData == null)
6767
{
6868
_waveformData = WaveformDataParser.interpretFlxSound(this);
69-
if (_waveformData == null) throw 'Could not interpret waveform data!';
69+
if (_waveformData == null) trace('Could not interpret waveform data!');
7070
}
7171
return _waveformData;
7272
}

source/funkin/audio/waveform/WaveformData.hx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ class WaveformData
195195
var thatChannel = that.channel(channelIndex);
196196
var resultChannel = result.channel(channelIndex);
197197

198+
if (thatChannel == null) return this.clone();
199+
198200
for (index in 0...this.length)
199201
{
200202
var thisMinSample = thisChannel.minSample(index);

source/funkin/data/song/importer/ChartManifestData.hx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package funkin.data.song.importer;
22

3+
import haxe.io.Path;
4+
35
/**
46
* A helper JSON blob found in `.fnfc` files.
57
*/
@@ -47,18 +49,32 @@ class ChartManifestData
4749
return '$songId-chart${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_DATA}';
4850
}
4951

50-
public function getInstFileName(?variation:String):String
52+
public function getInstFileName(?variation:String, fileEntries:Array<haxe.zip.Entry>):String
5153
{
5254
if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
5355

54-
return 'Inst${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
56+
var instFile = fileEntries.filter(function(file:haxe.zip.Entry):Bool
57+
{
58+
return Path.withoutExtension(file.fileName) == 'Inst${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}';
59+
});
60+
61+
if (instFile[0] == null) return 'Inst${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
62+
else
63+
return instFile[0].fileName;
5564
}
5665

57-
public function getVocalsFileName(charId:String, ?variation:String):String
66+
public function getVocalsFileName(charId:String, ?variation:String, fileEntries:Array<haxe.zip.Entry>):String
5867
{
5968
if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
6069

61-
return 'Voices-$charId${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
70+
var vocalFile = fileEntries.filter(function(file:haxe.zip.Entry):Bool
71+
{
72+
return Path.withoutExtension(file.fileName) == 'Voices-$charId${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}';
73+
});
74+
75+
if (vocalFile[0] == null) return 'Voices-$charId${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
76+
else
77+
return vocalFile[0].fileName;
6278
}
6379

6480
/**

source/funkin/ui/debug/charting/ChartEditorState.hx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
148148
public static final CHART_EDITOR_TOOLBOX_FREEPLAY_LAYOUT:String = Paths.ui('chart-editor/toolbox/freeplay');
149149
public static final CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:String = Paths.ui('chart-editor/toolbox/playtest-properties');
150150

151-
// Validation
152-
public static final SUPPORTED_MUSIC_FORMATS:Array<String> = #if sys ['ogg'] #else ['mp3'] #end;
153-
154151
// Layout
155152

156153
/**

source/funkin/ui/debug/charting/dialogs/ChartEditorUploadVocalsDialog.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ChartEditorUploadVocalsDialog extends ChartEditorBaseDialog
109109

110110
vocalsEntry.onClick = function(_event)
111111
{
112-
Dialogs.openBinaryFile('Open $charName Vocals', [{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile)
112+
Dialogs.openBinaryFile('Open $charName Vocals', FileUtil.FILE_EXTENSION_INFO_AUDIO, function(selectedFile)
113113
{
114114
if (selectedFile != null && selectedFile.bytes != null)
115115
{

source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import funkin.audio.waveform.WaveformSprite;
1313
import flixel.util.FlxColor;
1414
import haxe.io.Bytes;
1515
import haxe.io.Path;
16+
import lime.media.AudioBuffer;
1617

1718
/**
1819
* Functions for loading audio for the chart editor.
@@ -69,6 +70,7 @@ class ChartEditorAudioHandler
6970
*/
7071
public static function loadVocalsFromBytes(state:ChartEditorState, bytes:Bytes, charId:String, instId:String = '', wipeFirst:Bool = false):Bool
7172
{
73+
if (AudioBuffer.getCodec(bytes) == null) return false;
7274
var trackId:String = '${charId}${instId == '' ? '' : '-${instId}'}';
7375
if (wipeFirst) wipeVocalData(state);
7476
state.audioVocalTrackData.set(trackId, bytes);
@@ -119,6 +121,7 @@ class ChartEditorAudioHandler
119121
*/
120122
public static function loadInstFromBytes(state:ChartEditorState, bytes:Bytes, instId:String = '', wipeFirst:Bool = false):Bool
121123
{
124+
if (AudioBuffer.getCodec(bytes) == null) return false;
122125
if (instId == '') instId = 'default';
123126
if (wipeFirst) wipeInstrumentalData(state);
124127
state.audioInstTrackData.set(instId, bytes);
@@ -339,26 +342,14 @@ class ChartEditorAudioHandler
339342
var instTrackIds = state.audioInstTrackData.keys().array();
340343
for (key in instTrackIds)
341344
{
342-
if (key == 'default')
343-
{
344-
var data:Null<Bytes> = state.audioInstTrackData.get('default');
345-
if (data == null)
346-
{
347-
trace(' WARNING '.warning() + ' Failed to access inst track ($key)');
348-
continue;
349-
}
350-
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst.ogg', data));
351-
}
352-
else
345+
var data:Null<Bytes> = state.audioInstTrackData.get(key);
346+
if (data == null)
353347
{
354-
var data:Null<Bytes> = state.audioInstTrackData.get(key);
355-
if (data == null)
356-
{
357-
trace(' WARNING '.warning() + ' Failed to access inst track ($key)');
358-
continue;
359-
}
360-
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Inst-${key}.ogg', data));
348+
trace(' WARNING '.warning() + ' Failed to access inst track ($key)');
349+
continue;
361350
}
351+
var extension = AudioBuffer.getCodec(data).toFormat();
352+
zipEntries.push(FileUtil.makeZIPEntryFromBytes(key == 'default' ? 'Inst.${extension}' : 'Inst-${key}.${extension}', data));
362353
}
363354

364355
return zipEntries;
@@ -382,7 +373,8 @@ class ChartEditorAudioHandler
382373
trace(' WARNING '.warning() + ' Failed to access vocal track ($key)');
383374
continue;
384375
}
385-
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data));
376+
var extension = AudioBuffer.getCodec(data).toFormat();
377+
zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.${extension}', data));
386378
}
387379

388380
return zipEntries;

source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ class ChartEditorDialogHandler
532532

533533
instrumentalBox.onClick = function(_)
534534
{
535-
Dialogs.openBinaryFile('Open Instrumental', [{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile:SelectedFileInfo)
535+
Dialogs.openBinaryFile('Open Instrumental', FileUtil.FILE_EXTENSION_INFO_AUDIO, function(selectedFile:SelectedFileInfo)
536536
{
537537
if (selectedFile != null && selectedFile.bytes != null)
538538
{
@@ -567,17 +567,8 @@ class ChartEditorDialogHandler
567567
}
568568
else
569569
{
570-
var message:String = if (!ChartEditorState.SUPPORTED_MUSIC_FORMATS.contains(path.ext ?? ''))
571-
{
572-
'File format (${path.ext}) not supported for instrumental track (${path.file}.${path.ext})';
573-
}
574-
else
575-
{
576-
'Failed to load instrumental track (${path.file}.${path.ext}) for variation (${state.selectedVariation})';
577-
}
578-
579570
// Tell the user the load was successful.
580-
state.error('Failed to Load Instrumental', message);
571+
state.error('Failed to Load Instrumental', 'Failed to load instrumental track (${path.file}.${path.ext}) for variation (${state.selectedVariation})');
581572
}
582573
};
583574

source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,15 @@ class ChartEditorImportExportHandler
305305
if (variMetadata == null) continue;
306306

307307
var instId:String = variMetadata?.playData?.characters?.instrumental ?? '';
308-
309-
var instFileName:String = manifest.getInstFileName(instId);
308+
var instFileName:String = manifest.getInstFileName(instId, fileEntries);
310309
var instFileBytes:Bytes = mappedFileEntries.get(instFileName)?.data ?? throw 'Could not locate instrumental ($instFileName).';
311310
if (!ChartEditorAudioHandler.loadInstFromBytes(state, instFileBytes, instId)) throw 'Could not load instrumental ($instFileName).';
312311

313312
var playerCharId:String = variMetadata?.playData?.characters?.player ?? Constants.DEFAULT_CHARACTER;
314313
var playerVoiceList:Array<String> = variMetadata?.playData.characters?.playerVocals ?? [playerCharId];
315314
for (voice in playerVoiceList)
316315
{
317-
var playerVocalsFileName:String = manifest.getVocalsFileName(voice, variation);
316+
var playerVocalsFileName:String = manifest.getVocalsFileName(voice, variation, fileEntries);
318317
var playerVocalsFileBytes:Null<Bytes> = mappedFileEntries.get(playerVocalsFileName)?.data;
319318
if (playerVocalsFileBytes == null)
320319
{
@@ -332,7 +331,7 @@ class ChartEditorImportExportHandler
332331
var opponentVoiceList:Array<String> = variMetadata?.playData.characters?.opponentVocals ?? [opponentCharId];
333332
for (voice in opponentVoiceList)
334333
{
335-
var opponentVocalsFileName:String = manifest.getVocalsFileName(voice, variation);
334+
var opponentVocalsFileName:String = manifest.getVocalsFileName(voice, variation, fileEntries);
336335
var opponentVocalsFileBytes:Null<Bytes> = mappedFileEntries.get(opponentVocalsFileName)?.data;
337336
if (opponentVocalsFileBytes == null)
338337
{

0 commit comments

Comments
 (0)