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/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 2b5af627c..a8cb879a3 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(); @@ -1902,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. @@ -2430,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)); } } @@ -2485,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)); } } @@ -2768,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; @@ -2846,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( diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index a78d61583..c695b1db4 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")); @@ -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/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/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..86e84117a 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -12,7 +12,6 @@ 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; @@ -179,7 +178,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..8e82c0fd8 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) 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 4e0d7ccf3..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; @@ -1058,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; } @@ -1091,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) { @@ -1133,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; @@ -1177,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..315444fb0 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()); } @@ -511,7 +515,7 @@ class StoryMenuState extends MusicBeatState { if (!currentLevel.isUnlocked()) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); return; } @@ -519,7 +523,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/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.