diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index 49bab1ac1..e217d1f18 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -13,8 +13,9 @@ jobs: apt update apt install -y sudo git curl unzip - name: Fix git config on posix runner + # this can't be {{ github.workspace }} because that's not docker-aware run: | - git config --global --add safe.directory ${{ github.workspace }} + git config --global --add safe.directory $GITHUB_WORKSPACE - name: Get checkout token uses: actions/create-github-app-token@v1 id: app_token @@ -90,8 +91,9 @@ jobs: runs-on: [self-hosted, macos] steps: - name: Fix git config on posix runner + # this can't be {{ github.workspace }} because that's not docker-aware run: | - git config --global --add safe.directory ${{ github.workspace }} + git config --global --add safe.directory $GITHUB_WORKSPACE - name: Get checkout token uses: actions/create-github-app-token@v1 id: app_token diff --git a/.vscode/settings.json b/.vscode/settings.json index 84af3a3fd..13a1862d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -204,6 +204,21 @@ "label": "HTML5 / Debug (Watch)", "target": "html5", "args": ["-debug", "-watch", "-DFORCE_DEBUG_VERSION"] + }, + { + "label": "macOS / Debug", + "target": "mac", + "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + }, + { + "label": "macOS / Release", + "target": "mac", + "args": ["-release"] + }, + { + "label": "macOS / Release (GitHub Actions)", + "target": "mac", + "args": ["-release", "-DGITHUB_BUILD"] } ], "cmake.configureOnOpen": false, diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index 01147ce31..7ac6382f2 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -35,7 +35,15 @@ class Conductor * You can also do stuff like store a reference to the Conductor and pass it around or temporarily replace it, * or have a second Conductor running at the same time, or other weird stuff like that if you need to. */ - public static var instance:Conductor = new Conductor(); + public static var instance(get, never):Conductor; + + static var _instance:Null = null; + + static function get_instance():Conductor + { + if (_instance == null) _instance = new Conductor(); + return _instance; + } /** * Signal fired when the current Conductor instance advances to a new measure. @@ -523,6 +531,6 @@ class Conductor */ public static function reset():Void { - Conductor.instance = new Conductor(); + _instance = new Conductor(); } } diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c64240909..687860265 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -8,6 +8,7 @@ import funkin.util.tools.ICloneable; import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongRegistry; import funkin.audio.waveform.WaveformData; +import openfl.media.SoundMixer; import funkin.audio.waveform.WaveformDataParser; import flixel.math.FlxMath; import openfl.Assets; @@ -18,6 +19,7 @@ import openfl.utils.AssetType; /** * A FlxSound which adds additional functionality: * - Delayed playback via negative song position. + * - Easy functions for immediate playback and recycling. */ @:nullSafety class FunkinSound extends FlxSound implements ICloneable @@ -286,15 +288,28 @@ class FunkinSound extends FlxSound implements ICloneable * Creates a new `FunkinSound` object and loads it as the current music track. * * @param key The key of the music you want to play. Music should be at `music//.ogg`. - * @param overrideExisting Whether to override music if it is already playing. - * @param mapTimeChanges Whether to check for `SongMusicData` to update the Conductor with. + * @param params A set of additional optional parameters. * Data should be at `music//-metadata.json`. + * @return Whether the music was started. `false` if music was already playing or could not be started */ - public static function playMusic(key:String, overrideExisting:Bool = false, mapTimeChanges:Bool = true):Void + public static function playMusic(key:String, params:FunkinSoundPlayMusicParams):Bool { - if (!overrideExisting && FlxG.sound.music?.playing) return; + if (!(params.overrideExisting ?? false) && (FlxG.sound.music?.exists ?? false) && FlxG.sound.music.playing) return false; - if (mapTimeChanges) + if (!(params.restartTrack ?? false) && FlxG.sound.music?.playing) + { + if (FlxG.sound.music != null && Std.isOfType(FlxG.sound.music, FunkinSound)) + { + var existingSound:FunkinSound = cast FlxG.sound.music; + // Stop here if we would play a matching music track. + if (existingSound._label == Paths.music('$key/$key')) + { + return false; + } + } + } + + if (params?.mapTimeChanges ?? true) { var songMusicData:Null = SongRegistry.instance.parseMusicData(key); // Will fall back and return null if the metadata doesn't exist or can't be parsed. @@ -308,10 +323,26 @@ class FunkinSound extends FlxSound implements ICloneable } } - FlxG.sound.music = FunkinSound.load(Paths.music('$key/$key')); + if (FlxG.sound.music != null) + { + FlxG.sound.music.stop(); + FlxG.sound.music.kill(); + } - // Prevent repeat update() and onFocus() calls. - FlxG.sound.list.remove(FlxG.sound.music); + var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, params.loop ?? true, false, true); + if (music != null) + { + FlxG.sound.music = music; + + // Prevent repeat update() and onFocus() calls. + FlxG.sound.list.remove(FlxG.sound.music); + + return true; + } + else + { + return false; + } } /** @@ -326,11 +357,18 @@ class FunkinSound extends FlxSound implements ICloneable * @param autoPlay Whether to play the sound immediately or wait for a `play()` call. * @param onComplete Called when the sound finished playing. * @param onLoad Called when the sound finished loading. Called immediately for succesfully loaded embedded sounds. - * @return A `FunkinSound` object. + * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ public static function load(embeddedSound:FlxSoundAsset, volume:Float = 1.0, looped:Bool = false, autoDestroy:Bool = false, autoPlay:Bool = false, - ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound + ?onComplete:Void->Void, ?onLoad:Void->Void):Null { + @:privateAccess + if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS) + { + FlxG.log.error('FunkinSound could not play sound, channels exhausted! Found ${SoundMixer.__soundChannels.length} active sound channels.'); + return null; + } + var sound:FunkinSound = pool.recycle(construct); // Load the sound. @@ -341,6 +379,10 @@ class FunkinSound extends FlxSound implements ICloneable { sound._label = embeddedSound; } + else + { + sound._label = 'unknown'; + } sound.volume = volume; sound.group = FlxG.sound.defaultSoundGroup; @@ -355,6 +397,36 @@ class FunkinSound extends FlxSound implements ICloneable return sound; } + public override function destroy():Void + { + // trace('[FunkinSound] Destroying sound "${this._label}"'); + super.destroy(); + } + + /** + * Play a sound effect once, then destroy it. + * @param key + * @param volume + * @return static function construct():FunkinSound + */ + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Void + { + var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); + } + + /** + * Stop all sounds in the pool and allow them to be recycled. + */ + public static function stopAllAudio(musicToo:Bool = false):Void + { + for (sound in pool) + { + if (sound == null) continue; + if (!musicToo && sound == FlxG.sound.music) continue; + sound.destroy(); + } + } + static function construct():FunkinSound { var sound:FunkinSound = new FunkinSound(); @@ -365,3 +437,39 @@ class FunkinSound extends FlxSound implements ICloneable return sound; } } + +/** + * Additional parameters for `FunkinSound.playMusic()` + */ +typedef FunkinSoundPlayMusicParams = +{ + /** + * The volume you want the music to start at. + * @default `1.0` + */ + var ?startingVolume:Float; + + /** + * Whether to override music if a different track is already playing. + * @default `false` + */ + var ?overrideExisting:Bool; + + /** + * Whether to override music if the same track is already playing. + * @default `false` + */ + var ?restartTrack:Bool; + + /** + * Whether the music should loop or play once. + * @default `true` + */ + var ?loop:Bool; + + /** + * Whether to check for `SongMusicData` to update the Conductor with. + * @default `true` + */ + var ?mapTimeChanges:Bool; +} diff --git a/source/funkin/audio/SoundGroup.hx b/source/funkin/audio/SoundGroup.hx index a26537c2a..9a754049b 100644 --- a/source/funkin/audio/SoundGroup.hx +++ b/source/funkin/audio/SoundGroup.hx @@ -1,7 +1,6 @@ package funkin.audio; import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.sound.FlxSound; import funkin.audio.FunkinSound; import flixel.tweens.FlxTween; diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index ad028fa94..7419d9425 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -55,6 +55,13 @@ abstract class BaseRegistry & Constructible(); this.scriptedEntryIds = []; + + // Lazy initialization of singletons should let this get called, + // but we have this check just in case. + if (FlxG.game != null) + { + FlxG.console.registerObject('registry$registryId', this); + } } /** diff --git a/source/funkin/data/dialogue/ConversationRegistry.hx b/source/funkin/data/dialogue/ConversationRegistry.hx index 9186ef786..4a8af2162 100644 --- a/source/funkin/data/dialogue/ConversationRegistry.hx +++ b/source/funkin/data/dialogue/ConversationRegistry.hx @@ -15,7 +15,14 @@ class ConversationRegistry extends BaseRegistry public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:ConversationRegistry = new ConversationRegistry(); + public static var instance(get, never):ConversationRegistry; + static var _instance:Null = null; + + static function get_instance():ConversationRegistry + { + if (_instance == null) _instance = new ConversationRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/dialogue/DialogueBoxRegistry.hx b/source/funkin/data/dialogue/DialogueBoxRegistry.hx index 87205d96c..d07ea6da2 100644 --- a/source/funkin/data/dialogue/DialogueBoxRegistry.hx +++ b/source/funkin/data/dialogue/DialogueBoxRegistry.hx @@ -15,7 +15,14 @@ class DialogueBoxRegistry extends BaseRegistry public static final DIALOGUEBOX_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x"; - public static final instance:DialogueBoxRegistry = new DialogueBoxRegistry(); + public static var instance(get, never):DialogueBoxRegistry; + static var _instance:Null = null; + + static function get_instance():DialogueBoxRegistry + { + if (_instance == null) _instance = new DialogueBoxRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/dialogue/SpeakerRegistry.hx b/source/funkin/data/dialogue/SpeakerRegistry.hx index 6bd301dd7..c76c6d766 100644 --- a/source/funkin/data/dialogue/SpeakerRegistry.hx +++ b/source/funkin/data/dialogue/SpeakerRegistry.hx @@ -15,7 +15,14 @@ class SpeakerRegistry extends BaseRegistry public static final SPEAKER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:SpeakerRegistry = new SpeakerRegistry(); + public static var instance(get, never):SpeakerRegistry; + static var _instance:Null = null; + + static function get_instance():SpeakerRegistry + { + if (_instance == null) _instance = new SpeakerRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/level/LevelRegistry.hx b/source/funkin/data/level/LevelRegistry.hx index 96712cba5..e37e78d8c 100644 --- a/source/funkin/data/level/LevelRegistry.hx +++ b/source/funkin/data/level/LevelRegistry.hx @@ -15,7 +15,14 @@ class LevelRegistry extends BaseRegistry public static final LEVEL_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:LevelRegistry = new LevelRegistry(); + public static var instance(get, never):LevelRegistry; + static var _instance:Null = null; + + static function get_instance():LevelRegistry + { + if (_instance == null) _instance = new LevelRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx index ffb9bf490..5e9fa9a3d 100644 --- a/source/funkin/data/notestyle/NoteStyleRegistry.hx +++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx @@ -15,7 +15,14 @@ class NoteStyleRegistry extends BaseRegistry public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:NoteStyleRegistry = new NoteStyleRegistry(); + public static var instance(get, never):NoteStyleRegistry; + static var _instance:Null = null; + + static function get_instance():NoteStyleRegistry + { + if (_instance == null) _instance = new NoteStyleRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index e7f74569c..d82e184a5 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -40,10 +40,17 @@ class SongRegistry extends BaseRegistry } /** - * TODO: What if there was a Singleton macro which created static functions - * that redirected to the instance? + * TODO: What if there was a Singleton macro which automatically created the property for us? */ - public static final instance:SongRegistry = new SongRegistry(); + public static var instance(get, never):SongRegistry; + + static var _instance:Null = null; + + static function get_instance():SongRegistry + { + if (_instance == null) _instance = new SongRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index b78292e5b..13a5afb8d 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -15,7 +15,14 @@ class StageRegistry extends BaseRegistry public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:StageRegistry = new StageRegistry(); + public static var instance(get, never):StageRegistry; + static var _instance:Null = null; + + static function get_instance():StageRegistry + { + if (_instance == null) _instance = new StageRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index a88476d4d..78f660d3f 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -209,7 +209,6 @@ class PolymodHandler // Add import aliases for certain classes. // NOTE: Scripted classes are automatically aliased to their parent class. Polymod.addImportAlias('flixel.math.FlxPoint', flixel.math.FlxPoint.FlxBasePoint); - Polymod.addImportAlias('flixel.system.FlxSound', flixel.sound.FlxSound); // Add blacklisting for prohibited classes and packages. // `polymod.*` diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 747565100..10636afdf 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -9,6 +9,7 @@ import funkin.modding.module.ModuleHandler; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; class Countdown { @@ -282,7 +283,7 @@ class Countdown if (soundPath == null) return; - FlxG.sound.play(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); + FunkinSound.playOnce(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); } public static function decrement(step:CountdownStep):CountdownStep diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index ec3282164..a1796e912 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -3,7 +3,6 @@ package funkin.play; import flixel.FlxG; import flixel.FlxObject; import flixel.FlxSprite; -import flixel.sound.FlxSound; import funkin.audio.FunkinSound; import flixel.util.FlxColor; import flixel.util.FlxTimer; @@ -418,7 +417,7 @@ class GameOverSubState extends MusicBeatSubState blueballed = true; if (Assets.exists(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix))) { - FlxG.sound.play(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)); + FunkinSound.playOnce(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)); } else { @@ -438,7 +437,7 @@ class GameOverSubState extends MusicBeatSubState if (!Preferences.naughtyness) randomCensor = [1, 3, 8, 13, 17, 21]; - FlxG.sound.play(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), 1, false, null, true, function() { + FunkinSound.playOnce(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), function() { // Once the quote ends, fade in the game over music. if (!isEnding && gameOverMusic != null) { diff --git a/source/funkin/play/GitarooPause.hx b/source/funkin/play/GitarooPause.hx index eae56a9c3..6abe78cd8 100644 --- a/source/funkin/play/GitarooPause.hx +++ b/source/funkin/play/GitarooPause.hx @@ -26,7 +26,11 @@ class GitarooPause extends MusicBeatState override function create():Void { - if (FlxG.sound.music != null) FlxG.sound.music.stop(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } var bg:FunkinSprite = FunkinSprite.create('pauseAlt/pauseBG'); add(bg); diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 03681ce13..f16aa00d8 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -366,7 +366,7 @@ class PauseSubState extends MusicBeatSubState */ function changeSelection(change:Int = 0):Void { - FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); currentEntry += change; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 12b290afd..169809a63 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1,20 +1,15 @@ package funkin.play; -import funkin.audio.FunkinSound; import flixel.addons.display.FlxPieDial; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.Transition; -import flixel.addons.transition.Transition; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxState; -import funkin.graphics.FunkinSprite; import flixel.FlxSubState; -import funkin.graphics.FunkinSprite; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import funkin.graphics.FunkinSprite; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; @@ -22,18 +17,19 @@ import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxTimer; import funkin.api.newgrounds.NGio; -import funkin.audio.VoicesGroup; +import funkin.audio.FunkinSound; import funkin.audio.VoicesGroup; import funkin.data.dialogue.ConversationRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.notestyle.NoteStyleData; import funkin.data.notestyle.NoteStyleRegistry; -import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongData.SongCharacterData; import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongRegistry; import funkin.data.stage.StageRegistry; +import funkin.graphics.FunkinCamera; +import funkin.graphics.FunkinSprite; import funkin.Highscore.Tallies; import funkin.input.PreciseInputManager; import funkin.modding.events.ScriptEvent; @@ -44,14 +40,11 @@ import funkin.play.components.ComboMilestone; import funkin.play.components.HealthIcon; import funkin.play.components.PopUpStuff; import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.play.notes.NoteDirection; import funkin.play.notes.NoteSplash; import funkin.play.notes.NoteSprite; -import funkin.play.notes.NoteSprite; -import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; @@ -65,7 +58,6 @@ import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatSubState; import funkin.ui.options.PreferencesMenu; import funkin.ui.story.StoryMenuState; -import funkin.graphics.FunkinCamera; import funkin.ui.transition.LoadingState; import funkin.util.SerializerUtil; import haxe.Int64; @@ -1293,16 +1285,35 @@ class PlayState extends MusicBeatSubState currentStage = null; } - // Stop the instrumental. - if (FlxG.sound.music != null) + if (!overrideMusic) { - FlxG.sound.music.stop(); - } + // Stop the instrumental. + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } - // Stop the vocals. - if (vocals != null && vocals.exists) + // Stop the vocals. + if (vocals != null && vocals.exists) + { + vocals.destroy(); + vocals = null; + } + } + else { - vocals.stop(); + // Stop the instrumental. + if (FlxG.sound.music != null) + { + FlxG.sound.music.stop(); + } + + // Stop the vocals. + if (vocals != null && vocals.exists) + { + vocals.stop(); + } } super.debug_refreshModules(); @@ -1353,7 +1364,10 @@ class PlayState extends MusicBeatSubState } // Only zoom camera if we are zoomed by less than 35%. - if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0) + if (Preferences.zoomCamera + && FlxG.camera.zoom < (1.35 * defaultCameraZoom) + && cameraZoomRate > 0 + && Conductor.instance.currentBeat % cameraZoomRate == 0) { // Zoom camera in (1.5%) currentCameraZoom += cameraZoomIntensity * defaultCameraZoom; @@ -1899,6 +1913,12 @@ class PlayState extends MusicBeatSubState currentChart.playInst(1.0, false); } + if (FlxG.sound.music == null) + { + FlxG.log.error('PlayState failed to initialize instrumental!'); + return; + } + FlxG.sound.music.onComplete = endSong.bind(false); // A negative instrumental offset means the song skips the first few milliseconds of the track. // This just gets added into the startTimestamp behavior so we don't need to do anything extra. @@ -2089,8 +2109,7 @@ class PlayState extends MusicBeatSubState holdNote.handledMiss = true; // We dropped a hold note. - // Mute vocals and play miss animation, but don't penalize. - vocals.opponentVolume = 0; + // Play miss animation, but don't penalize. currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true); } } @@ -2428,7 +2447,7 @@ class PlayState extends MusicBeatSubState if (playSound) { vocals.playerVolume = 0; - FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + FunkinSound.playOnce(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.5, 0.6)); } } @@ -2483,7 +2502,7 @@ class PlayState extends MusicBeatSubState if (event.playSound) { vocals.playerVolume = 0; - FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + FunkinSound.playOnce(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); } } @@ -2766,7 +2785,11 @@ class PlayState extends MusicBeatSubState if (targetSongId == null) { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); // transIn = FlxTransitionableState.defaultTransIn; // transOut = FlxTransitionableState.defaultTransOut; @@ -2844,7 +2867,7 @@ class PlayState extends MusicBeatSubState camHUD.visible = false; isInCutscene = true; - FlxG.sound.play(Paths.sound('Lights_Shut_off'), function() { + FunkinSound.playOnce(Paths.sound('Lights_Shut_off'), function() { // no camFollow so it centers on horror tree var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); LoadingState.loadPlayState( @@ -2918,7 +2941,10 @@ class PlayState extends MusicBeatSubState if (overrideMusic) { // Stop the music. Do NOT destroy it, something still references it! - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.pause(); + } if (vocals != null) { vocals.pause(); @@ -2928,7 +2954,10 @@ class PlayState extends MusicBeatSubState else { // Stop and destroy the music. - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.pause(); + } if (vocals != null) { vocals.destroy(); diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index a78d61583..821f4ba3c 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -13,6 +13,7 @@ import flixel.text.FlxBitmapText; import flixel.tweens.FlxEase; import funkin.ui.freeplay.FreeplayState; import flixel.tweens.FlxTween; +import funkin.audio.FunkinSound; import flixel.util.FlxGradient; import flixel.util.FlxTimer; import funkin.graphics.shaders.LeftMaskShader; @@ -48,9 +49,13 @@ class ResultState extends MusicBeatSubState else resultsVariation = NORMAL; - var loops:Bool = resultsVariation != SHIT; - - FlxG.sound.playMusic(Paths.music("results" + resultsVariation), 1, loops); + FunkinSound.playMusic('results$resultsVariation', + { + startingVolume: 1.0, + overrideExisting: true, + restartTrack: true, + loop: resultsVariation != SHIT + }); // TEMP-ish, just used to sorta "cache" the 3000x3000 image! var cacheBullShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("resultScreen/soundSystem")); @@ -104,7 +109,7 @@ class ResultState extends MusicBeatSubState add(gf); var boyfriend:FlxSprite = FunkinSprite.createSparrow(640, -200, 'resultScreen/resultBoyfriendGOOD'); - boyfriend.animation.addByPrefix("fall", "Boyfriend Good", 24, false); + boyfriend.animation.addByPrefix("fall", "Boyfriend Good Anim0", 24, false); boyfriend.visible = false; boyfriend.animation.finishCallback = function(_) { boyfriend.animation.play('fall', true, false, 14); @@ -159,7 +164,7 @@ class ResultState extends MusicBeatSubState add(blackTopBar); var resultsAnim:FunkinSprite = FunkinSprite.createSparrow(-200, -10, "resultScreen/results"); - resultsAnim.animation.addByPrefix("result", "results", 24, false); + resultsAnim.animation.addByPrefix("result", "results instance 1", 24, false); resultsAnim.animation.play("result"); add(resultsAnim); @@ -348,9 +353,12 @@ class ResultState extends MusicBeatSubState if (controls.PAUSE) { FlxTween.tween(FlxG.sound.music, {volume: 0}, 0.8); - FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1, {onComplete: _ -> { - FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4); - }}); + FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1, + { + onComplete: _ -> { + FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4); + } + }); if (params.storyMode) { openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker))); diff --git a/source/funkin/play/components/ComboMilestone.hx b/source/funkin/play/components/ComboMilestone.hx index 4119e45c2..22ce2d671 100644 --- a/source/funkin/play/components/ComboMilestone.hx +++ b/source/funkin/play/components/ComboMilestone.hx @@ -4,6 +4,7 @@ import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; class ComboMilestone extends FlxTypedSpriteGroup { @@ -78,7 +79,7 @@ class ComboMilestone extends FlxTypedSpriteGroup function setupCombo(daCombo:Int) { - FlxG.sound.play(Paths.sound('comboSound')); + FunkinSound.playOnce(Paths.sound('comboSound')); wasComboSetup = true; var loopNum:Int = 0; diff --git a/source/funkin/play/cutscene/VanillaCutscenes.hx b/source/funkin/play/cutscene/VanillaCutscenes.hx index a332d0795..7a4349e6a 100644 --- a/source/funkin/play/cutscene/VanillaCutscenes.hx +++ b/source/funkin/play/cutscene/VanillaCutscenes.hx @@ -4,6 +4,7 @@ import flixel.FlxSprite; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import funkin.audio.FunkinSound; import flixel.util.FlxTimer; /** @@ -40,7 +41,7 @@ class VanillaCutscenes FlxG.camera.zoom = 2.5; // Play the Sound effect. - FlxG.sound.play(Paths.sound('Lights_Turn_On'), function() { + FunkinSound.playOnce(Paths.sound('Lights_Turn_On'), function() { // Fade in the HUD. trace('SFX done...'); PlayState.instance.camHUD.visible = true; diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index ff56e0919..3da51185f 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -5,6 +5,7 @@ import flixel.FlxSprite; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import flixel.util.FlxSignal; import flixel.util.FlxTimer; #if html5 import funkin.graphics.video.FlxVideo; @@ -28,6 +29,31 @@ class VideoCutscene static var vid:FlxVideoSprite; #end + /** + * Called when the video is started. + */ + public static final onVideoStarted:FlxSignal = new FlxSignal(); + + /** + * Called if the video is paused. + */ + public static final onVideoPaused:FlxSignal = new FlxSignal(); + + /** + * Called if the video is resumed. + */ + public static final onVideoResumed:FlxSignal = new FlxSignal(); + + /** + * Called if the video is restarted. onVideoStarted is not called. + */ + public static final onVideoRestarted:FlxSignal = new FlxSignal(); + + /** + * Called when the video is ended or skipped. + */ + public static final onVideoEnded:FlxSignal = new FlxSignal(); + /** * Play a video cutscene. * TODO: Currently this is hardcoded to start the countdown after the video is done. @@ -94,6 +120,8 @@ class VideoCutscene PlayState.instance.add(vid); PlayState.instance.refresh(); + + onVideoStarted.dispatch(); } else { @@ -129,6 +157,8 @@ class VideoCutscene vid.y = 0; // vid.scale.set(0.5, 0.5); }); + + onVideoStarted.dispatch(); } else { @@ -143,6 +173,7 @@ class VideoCutscene if (vid != null) { vid.restartVideo(); + onVideoRestarted.dispatch(); } #end @@ -156,6 +187,8 @@ class VideoCutscene // Resume the video if it was paused. vid.resume(); } + + onVideoRestarted.dispatch(); } #end } @@ -166,6 +199,7 @@ class VideoCutscene if (vid != null) { vid.pauseVideo(); + onVideoPaused.dispatch(); } #end @@ -173,6 +207,45 @@ class VideoCutscene if (vid != null) { vid.pause(); + onVideoPaused.dispatch(); + } + #end + } + + public static function hideVideo():Void + { + #if html5 + if (vid != null) + { + vid.visible = false; + blackScreen.visible = false; + } + #end + + #if hxCodec + if (vid != null) + { + vid.visible = false; + blackScreen.visible = false; + } + #end + } + + public static function showVideo():Void + { + #if html5 + if (vid != null) + { + vid.visible = true; + blackScreen.visible = false; + } + #end + + #if hxCodec + if (vid != null) + { + vid.visible = true; + blackScreen.visible = false; } #end } @@ -183,6 +256,7 @@ class VideoCutscene if (vid != null) { vid.resumeVideo(); + onVideoResumed.dispatch(); } #end @@ -190,6 +264,7 @@ class VideoCutscene if (vid != null) { vid.resume(); + onVideoResumed.dispatch(); } #end } @@ -240,6 +315,7 @@ class VideoCutscene { ease: FlxEase.quadInOut, onComplete: function(twn:FlxTween) { + onVideoEnded.dispatch(); onCutsceneFinish(cutsceneType); } }); diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index f865f3b7b..c520c3e25 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -1,28 +1,28 @@ package funkin.play.cutscene.dialogue; -import funkin.data.IRegistryEntry; +import flixel.addons.display.FlxPieDial; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; -import flixel.util.FlxColor; -import funkin.graphics.FunkinSprite; -import flixel.tweens.FlxTween; import flixel.tweens.FlxEase; -import flixel.sound.FlxSound; -import funkin.util.SortUtil; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; import flixel.util.FlxSort; -import funkin.modding.events.ScriptEvent; -import funkin.modding.IScriptedClass.IEventHandler; -import funkin.play.cutscene.dialogue.DialogueBox; -import funkin.modding.IScriptedClass.IDialogueScriptedClass; -import funkin.modding.events.ScriptEventDispatcher; -import flixel.addons.display.FlxPieDial; +import funkin.audio.FunkinSound; import funkin.data.dialogue.ConversationData; import funkin.data.dialogue.ConversationData.DialogueEntryData; import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.SpeakerData; -import funkin.data.dialogue.SpeakerRegistry; import funkin.data.dialogue.DialogueBoxData; import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.IRegistryEntry; +import funkin.graphics.FunkinSprite; +import funkin.modding.events.ScriptEvent; +import funkin.modding.events.ScriptEventDispatcher; +import funkin.modding.IScriptedClass.IDialogueScriptedClass; +import funkin.modding.IScriptedClass.IEventHandler; +import funkin.play.cutscene.dialogue.DialogueBox; +import funkin.util.SortUtil; /** * A high-level handler for dialogue. @@ -90,7 +90,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl /** * AUDIO */ - var music:FlxSound; + var music:FunkinSound; /** * GRAPHICS @@ -129,8 +129,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl { if (_data.music == null) return; - music = new FlxSound().loadEmbedded(Paths.music(_data.music.asset), true, true); - music.volume = 0; + music = FunkinSound.load(Paths.music(_data.music.asset), 0.0, true, true, true); if (_data.music.fadeTime > 0.0) { @@ -140,9 +139,6 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl { music.volume = 1.0; } - - FlxG.sound.list.add(music); - music.play(); } public function pauseMusic():Void diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 567c388c7..0248e09ee 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -139,7 +139,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = fetchVariationMetadata(id, vari); - if (variMeta != null) _metadata.set(variMeta.variation, variMeta); + if (variMeta != null) + { + _metadata.set(variMeta.variation, variMeta); + trace(' Loaded variation: $vari'); + } + else + { + FlxG.log.warn('[SONG] Failed to load variation metadata (${id}:${vari}), is the path correct?'); + trace(' FAILED to load variation: $vari'); + } } } @@ -374,12 +383,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Null { - if (variations == null) possibleVariations = variations; + if (possibleVariations == null) + { + possibleVariations = variations; + possibleVariations.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_VARIATION_LIST)); + } if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0]; - for (variation in variations) + for (variationId in possibleVariations) { - if (difficulties.exists('$diffId-$variation')) return variation; + var variationSuffix = (variationId != Constants.DEFAULT_VARIATION) ? '-$variationId' : ''; + if (difficulties.exists('$diffId$variationSuffix')) return variationId; } return null; diff --git a/source/funkin/ui/Alphabet.hx b/source/funkin/ui/Alphabet.hx index 66b95f5b8..e0492bee5 100644 --- a/source/funkin/ui/Alphabet.hx +++ b/source/funkin/ui/Alphabet.hx @@ -5,6 +5,7 @@ import flixel.group.FlxSpriteGroup; import flixel.math.FlxMath; import flixel.util.FlxTimer; import funkin.util.MathUtil; +import funkin.audio.FunkinSound; /** * Loosley based on FlxTypeText lolol @@ -200,7 +201,7 @@ class Alphabet extends FlxSpriteGroup if (FlxG.random.bool(40)) { var daSound:String = "GF_"; - FlxG.sound.play(Paths.soundRandom(daSound, 1, 4)); + FunkinSound.playOnce(Paths.soundRandom(daSound, 1, 4)); } add(letter); diff --git a/source/funkin/ui/MenuList.hx b/source/funkin/ui/MenuList.hx index 3ffe3c330..63a688778 100644 --- a/source/funkin/ui/MenuList.hx +++ b/source/funkin/ui/MenuList.hx @@ -5,6 +5,7 @@ import flixel.effects.FlxFlicker; import flixel.group.FlxGroup; import flixel.math.FlxPoint; import flixel.util.FlxSignal; +import funkin.audio.FunkinSound; class MenuTypedList extends FlxTypedGroup { @@ -93,7 +94,7 @@ class MenuTypedList extends FlxTypedGroup if (newIndex != selectedIndex) { - FlxG.sound.play(Paths.sound('scrollMenu')); + FunkinSound.playOnce(Paths.sound('scrollMenu')); selectItem(newIndex); } @@ -163,7 +164,7 @@ class MenuTypedList extends FlxTypedGroup else { busy = true; - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) { busy = false; selected.callback(); diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 98197a0e7..2cdc747ef 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -7,6 +7,7 @@ import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; import flixel.text.FlxText; import flixel.util.FlxColor; +import funkin.audio.FunkinSound; import flixel.util.FlxSort; import funkin.modding.PolymodHandler; import funkin.modding.events.ScriptEvent; @@ -151,6 +152,8 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler } else { + FunkinSound.stopAllAudio(); + onComplete(); } } diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 375fb8f5c..56a05eb86 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -4,6 +4,7 @@ import flixel.math.FlxPoint; import flixel.FlxObject; import flixel.FlxSprite; import funkin.ui.MusicBeatSubState; +import funkin.audio.FunkinSound; import funkin.ui.TextMenuList; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.MusicBeatSubState; @@ -71,7 +72,7 @@ class DebugMenuSubState extends MusicBeatSubState if (controls.BACK) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exitDebugMenu(); } } diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 46a095a65..1aa5d6e12 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -1,32 +1,35 @@ package funkin.ui.debug.anim; -import funkin.util.SerializerUtil; -import funkin.play.character.CharacterData; -import flixel.FlxCamera; -import flixel.FlxSprite; -import flixel.FlxState; import flixel.addons.display.FlxGridOverlay; import flixel.addons.ui.FlxInputText; import flixel.addons.ui.FlxUIDropDownMenu; +import flixel.FlxCamera; +import flixel.FlxSprite; +import flixel.FlxState; import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFrame; import flixel.group.FlxGroup; import flixel.math.FlxPoint; -import flixel.sound.FlxSound; import flixel.text.FlxText; import flixel.util.FlxColor; -import funkin.util.MouseUtil; import flixel.util.FlxSpriteUtil; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; +import funkin.input.Cursor; import funkin.play.character.BaseCharacter; +import funkin.play.character.CharacterData; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.SparrowCharacter; -import haxe.ui.RuntimeComponentBuilder; +import funkin.ui.mainmenu.MainMenuState; +import funkin.util.MouseUtil; +import funkin.util.SerializerUtil; +import funkin.util.SortUtil; import haxe.ui.components.DropDown; import haxe.ui.core.Component; +import haxe.ui.core.Screen; import haxe.ui.events.ItemEvent; import haxe.ui.events.UIEvent; -import funkin.ui.mainmenu.MainMenuState; +import haxe.ui.RuntimeComponentBuilder; import lime.utils.Assets as LimeAssets; import openfl.Assets; import openfl.events.Event; @@ -34,13 +37,8 @@ import openfl.events.IOErrorEvent; import openfl.geom.Rectangle; import openfl.net.FileReference; import openfl.net.URLLoader; -import funkin.ui.mainmenu.MainMenuState; import openfl.net.URLRequest; import openfl.utils.ByteArray; -import funkin.input.Cursor; -import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.util.SortUtil; -import haxe.ui.core.Screen; using flixel.util.FlxSpriteUtil; @@ -179,7 +177,7 @@ class DebugBoundingState extends FlxState var objShit = js.html.URL.createObjectURL(swagList.item(0)); trace(objShit); - var funnysound = new FlxSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false, + var funnysound = new FunkinSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false, null, function() { trace('LOADED SHIT??'); }); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index bdc0d311e..888398f34 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -15,7 +15,6 @@ import flixel.input.mouse.FlxMouseEvent; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import flixel.sound.FlxSound; import flixel.system.debug.log.LogStyle; import flixel.system.FlxAssets.FlxSoundAsset; import flixel.text.FlxText; @@ -1091,7 +1090,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState * The chill audio track that plays in the chart editor. * Plays when the main music is NOT being played. */ - var welcomeMusic:FlxSound = new FlxSound(); + var welcomeMusic:FunkinSound = new FunkinSound(); /** * The audio track for the instrumental. @@ -3888,8 +3887,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handleCursor():Void { // Mouse sounds - if (FlxG.mouse.justPressed) FlxG.sound.play(Paths.sound("chartingSounds/ClickDown")); - if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp")); + if (FlxG.mouse.justPressed) FunkinSound.playOnce(Paths.sound("chartingSounds/ClickDown")); + if (FlxG.mouse.justReleased) FunkinSound.playOnce(Paths.sound("chartingSounds/ClickUp")); // Note: If a menu is open in HaxeUI, don't handle cursor behavior. var shouldHandleCursor:Bool = !(isHaxeUIFocused || playbarHeadDragging || isHaxeUIDialogOpen) @@ -4949,7 +4948,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState playbarNoteSnap.text = '1/${noteSnapQuant}'; playbarDifficulty.text = '${selectedDifficulty.toTitleCase()}'; - // playbarBPM.text = 'BPM: ${(Conductor.currentTimeChange?.bpm ?? 0.0)}'; + playbarBPM.text = 'BPM: ${(Conductor.instance.bpm ?? 0.0)}'; } function handlePlayhead():Void diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx index 1e1d165f3..26e246371 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx @@ -1,7 +1,6 @@ package funkin.ui.debug.charting.handlers; import flixel.system.FlxAssets.FlxSoundAsset; -import flixel.sound.FlxSound; import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; import funkin.play.character.BaseCharacter.CharacterType; @@ -302,7 +301,8 @@ class ChartEditorAudioHandler trace('WARN: Failed to play sound $path, asset not found.'); return; } - var snd:FunkinSound = FunkinSound.load(asset); + var snd:Null = FunkinSound.load(asset); + if (snd == null) return; snd.autoDestroy = true; snd.play(true); snd.volume = volume; diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx index 9ebd29537..7b2eabb1c 100644 --- a/source/funkin/ui/debug/latency/LatencyState.hx +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -71,8 +71,6 @@ class LatencyState extends MusicBeatSubState // trace("EVENT LISTENER: " + key); }); - // FlxG.sound.playMusic(Paths.sound('soundTest')); - // funnyStatsGraph.hi Conductor.instance.forceBPM(60); @@ -242,13 +240,6 @@ class LatencyState extends MusicBeatSubState } } - /* if (FlxG.keys.justPressed.SPACE) - { - FlxG.sound.music.stop(); - - FlxG.resetState(); - }*/ - noteGrp.forEach(function(daNote:NoteSprite) { daNote.y = (strumLine.y - ((Conductor.instance.songPosition - Conductor.instance.instrumentalOffset) - daNote.noteData.time) * 0.45); daNote.x = strumLine.x + 30; diff --git a/source/funkin/ui/debug/stage/StageBuilderState.hx b/source/funkin/ui/debug/stage/StageBuilderState.hx index 074914f58..b556b9fde 100644 --- a/source/funkin/ui/debug/stage/StageBuilderState.hx +++ b/source/funkin/ui/debug/stage/StageBuilderState.hx @@ -37,8 +37,6 @@ class StageBuilderState extends MusicBeatState FlxG.mouse.visible = true; - // var alsoSnd:FlxSound = new FlxSound(); - // snd = new Sound(); // var swagBytes:ByteArray = new ByteArray(8192); diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx index 55f43d2ef..8cd63dba1 100644 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ b/source/funkin/ui/freeplay/DJBoyfriend.hx @@ -4,8 +4,8 @@ import flixel.FlxSprite; import flixel.util.FlxSignal; import funkin.util.assets.FlxAnimationUtil; import funkin.graphics.adobeanimate.FlxAtlasSprite; -import flixel.sound.FlxSound; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; import funkin.audio.FlxStreamSound; class DJBoyfriend extends FlxAtlasSprite @@ -178,7 +178,7 @@ class DJBoyfriend extends FlxAtlasSprite if (cartoonSnd == null) { // tv is OFF, but getting turned on - FlxG.sound.play(Paths.sound('tv_on')); + FunkinSound.playOnce(Paths.sound('tv_on')); cartoonSnd = new FlxStreamSound(); FlxG.sound.defaultSoundGroup.add(cartoonSnd); @@ -187,7 +187,7 @@ class DJBoyfriend extends FlxAtlasSprite { // plays it smidge after the click new FlxTimer().start(0.1, function(_) { - FlxG.sound.play(Paths.sound('channel_switch')); + FunkinSound.playOnce(Paths.sound('channel_switch')); }); } // cartoonSnd.loadEmbedded(Paths.sound("cartoons/peck")); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index fc11eec28..f7554197f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -174,7 +174,11 @@ class FreeplayState extends MusicBeatSubState isDebug = true; #end - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); // Add a null entry that represents the RANDOM option songs.push(null); @@ -867,7 +871,7 @@ class FreeplayState extends MusicBeatSubState FlxTimer.globalManager.clear(); dj.onIntroDone.removeAll(); - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); var longestTimer:Float = 0; @@ -1013,7 +1017,14 @@ class FreeplayState extends MusicBeatSubState // Set the difficulty star count on the right. albumRoll.setDifficultyStars(daSong?.songRating); - albumRoll.albumId = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID; + + // Set the album graphic and play the animation if relevant. + var newAlbumId:String = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID; + if (albumRoll.albumId != newAlbumId) + { + albumRoll.albumId = newAlbumId; + albumRoll.playIntro(); + } } // Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String) @@ -1051,7 +1062,7 @@ class FreeplayState extends MusicBeatSubState trace('No songs available!'); busy = false; letterSort.inputEnabled = true; - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); return; } @@ -1084,7 +1095,7 @@ class FreeplayState extends MusicBeatSubState PlayStatePlaylist.campaignId = cap.songData.levelId; // Visual and audio effects. - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); dj.confirm(); new FlxTimer().start(1, function(tmr:FlxTimer) { @@ -1126,8 +1137,7 @@ class FreeplayState extends MusicBeatSubState function changeSelection(change:Int = 0):Void { - FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); - // FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName)); + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); var prevSelected:Int = curSelected; @@ -1170,15 +1180,25 @@ class FreeplayState extends MusicBeatSubState { if (curSelected == 0) { - FlxG.sound.playMusic(Paths.music('freeplay/freeplayRandom'), 0); + FunkinSound.playMusic('freeplayRandom', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: true + }); FlxG.sound.music.fadeIn(2, 0, 0.8); } else { // TODO: Stream the instrumental of the selected song? - if (prevSelected == 0) + var didReplace:Bool = FunkinSound.playMusic('freakyMenu', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: false + }); + if (didReplace) { - FunkinSound.playMusic('freakyMenu'); FlxG.sound.music.fadeIn(2, 0, 0.8); } } diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index f4d48dba3..a8c2039ab 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -155,7 +155,11 @@ class MainMenuState extends MusicBeatState function playMenuMusic():Void { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); } function resetCamStuff() @@ -321,7 +325,7 @@ class MainMenuState extends MusicBeatState if (controls.BACK && menuItems.enabled && !menuItems.busy) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); FlxG.switchState(() -> new TitleState()); } } diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 7b233f03d..0f33a0780 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -5,9 +5,11 @@ import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; import flixel.group.FlxGroup; import flixel.util.FlxSignal; +import funkin.audio.FunkinSound; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatState; import funkin.util.WindowUtil; +import funkin.audio.FunkinSound; import funkin.input.Controls; class OptionsState extends MusicBeatState @@ -143,7 +145,7 @@ class Page extends FlxGroup { if (canExit && controls.BACK) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exit(); } } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 9ce110c73..d3aa68c49 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -231,7 +231,11 @@ class StoryMenuState extends MusicBeatState function playMenuMusic():Void { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); } function updateData():Void @@ -382,7 +386,7 @@ class StoryMenuState extends MusicBeatState if (controls.BACK && !exitingMenu && !selectedLevel) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exitingMenu = true; FlxG.switchState(() -> new MainMenuState()); } @@ -446,7 +450,11 @@ class StoryMenuState extends MusicBeatState */ function changeDifficulty(change:Int = 0):Void { - var difficultyList:Array = currentLevel.getDifficulties(); + // "For now, NO erect in story mode" -Dave + + var difficultyList:Array = Constants.DEFAULT_DIFFICULTY_LIST; + // Use this line to displays all difficulties + // var difficultyList:Array = currentLevel.getDifficulties(); var currentIndex:Int = difficultyList.indexOf(currentDifficultyId); currentIndex += change; @@ -511,7 +519,7 @@ class StoryMenuState extends MusicBeatState { if (!currentLevel.isUnlocked()) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); return; } @@ -519,7 +527,7 @@ class StoryMenuState extends MusicBeatState selectedLevel = true; - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); currentLevelTitle.isFlashing = true; diff --git a/source/funkin/ui/title/AttractState.hx b/source/funkin/ui/title/AttractState.hx index 63a9e63ad..0af97afd9 100644 --- a/source/funkin/ui/title/AttractState.hx +++ b/source/funkin/ui/title/AttractState.hx @@ -22,7 +22,11 @@ class AttractState extends MusicBeatState public override function create():Void { // Pause existing music. - FlxG.sound.music.stop(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } #if html5 playVideoHTML5(ATTRACT_VIDEO_PATH); diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 5cc7b8cc1..1a4e13ab1 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -222,10 +222,14 @@ class TitleState extends MusicBeatState { var shouldFadeIn = (FlxG.sound.music == null); // Load music. Includes logic to handle BPM changes. - FunkinSound.playMusic('freakyMenu', false, true); - FlxG.sound.music.volume = 0; + FunkinSound.playMusic('freakyMenu', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: true + }); // Fade from 0.0 to 0.7 over 4 seconds - if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7); + if (shouldFadeIn) FlxG.sound.music.fadeIn(4.0, 0.0, 0.7); } function getIntroTextShit():Array> @@ -323,7 +327,7 @@ class TitleState extends MusicBeatState if (Date.now().getDay() == 5) NGio.unlockMedal(61034); titleText.animation.play('press'); FlxG.camera.flash(FlxColor.WHITE, 1); - FlxG.sound.play(Paths.sound('confirmMenu'), 0.7); + FunkinSound.playOnce(Paths.sound('confirmMenu'), 0.7); transitioning = true; var targetState:NextState = () -> new MainMenuState(); @@ -338,7 +342,7 @@ class TitleState extends MusicBeatState // ngSpr?? FlxG.switchState(targetState); }); - // FlxG.sound.play(Paths.music('titleShoot'), 0.7); + // FunkinSound.playOnce(Paths.music('titleShoot'), 0.7); } if (pressedEnter && !skippedIntro && initialized) skipIntro(); @@ -385,14 +389,12 @@ class TitleState extends MusicBeatState { cheatActive = true; - FlxG.sound.playMusic(Paths.music('tutorialTitle'), 1); - var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music); add(spec); Conductor.instance.forceBPM(190); FlxG.camera.flash(FlxColor.WHITE, 1); - FlxG.sound.play(Paths.sound('confirmMenu'), 0.7); + FunkinSound.playOnce(Paths.sound('confirmMenu'), 0.7); } function createCoolText(textArray:Array) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 23b3db6a9..980c264e3 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -171,7 +171,12 @@ class LoadingState extends MusicBeatState function onLoad():Void { - if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); + // Stop the instrumental. + if (stopMusic && FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } FlxG.switchState(target); } @@ -200,7 +205,8 @@ class LoadingState extends MusicBeatState // All assets preloaded, switch directly to play state (defualt on other targets). if (shouldStopMusic && FlxG.sound.music != null) { - FlxG.sound.music.stop(); + FlxG.sound.music.destroy(); + FlxG.sound.music = null; } // Load and cache the song's charts. diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 981a30e09..0b5e16f97 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -18,6 +18,7 @@ import flixel.addons.transition.FlxTransitionableState; import openfl.display.BitmapData; import funkin.ui.freeplay.FreeplayState; import openfl.geom.Matrix; +import funkin.audio.FunkinSound; import openfl.display.Sprite; import openfl.display.Bitmap; import flixel.FlxState; @@ -137,7 +138,7 @@ class StickerSubState extends MusicBeatSubState new FlxTimer().start(sticker.timing, _ -> { sticker.visible = false; var daSound:String = FlxG.random.getObject(sounds); - FlxG.sound.play(Paths.sound(daSound)); + FunkinSound.playOnce(Paths.sound(daSound)); if (grpStickers == null || ind == grpStickers.members.length - 1) { @@ -227,7 +228,7 @@ class StickerSubState extends MusicBeatSubState sticker.visible = true; var daSound:String = FlxG.random.getObject(sounds); - FlxG.sound.play(Paths.sound(daSound)); + FunkinSound.playOnce(Paths.sound(daSound)); var frameTimer:Int = FlxG.random.int(0, 2); diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index c9b99ed46..c7bc03139 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -157,6 +157,11 @@ class Constants */ public static final DEFAULT_VARIATION:String = 'default'; + /** + * Standard variations used by the game. + */ + public static final DEFAULT_VARIATION_LIST:Array = ['default', 'erect', 'pico']; + /** * The default intensity for camera zooms. */ diff --git a/source/funkin/util/MathUtil.hx b/source/funkin/util/MathUtil.hx index 532e92f15..5db8f8d76 100644 --- a/source/funkin/util/MathUtil.hx +++ b/source/funkin/util/MathUtil.hx @@ -19,6 +19,7 @@ class MathUtil * * @return The interpolated value. */ + @:deprecated('Use smoothLerp instead') public static function coolLerp(base:Float, target:Float, ratio:Float):Float { return base + cameraLerp(ratio) * (target - base); diff --git a/source/funkin/util/plugins/ScreenshotPlugin.hx b/source/funkin/util/plugins/ScreenshotPlugin.hx index d7e8109b8..9ac21d4b8 100644 --- a/source/funkin/util/plugins/ScreenshotPlugin.hx +++ b/source/funkin/util/plugins/ScreenshotPlugin.hx @@ -174,7 +174,7 @@ class ScreenshotPlugin extends FlxBasic FlxTween.tween(flashSpr, {alpha: 0}, 0.15, {ease: FlxEase.quadOut, onComplete: _ -> FlxG.stage.removeChild(flashSpr)}); // Play a sound (auto-play is true). - FunkinSound.load(Paths.sound('screenshot'), 1.0, false, true, true); + FunkinSound.playOnce(Paths.sound('screenshot'), 1.0); } static final PREVIEW_INITIAL_DELAY = 0.25; // How long before the preview starts fading in.