mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2025-03-25 22:29:42 -04:00
2hot stutter actually fixed!
This commit is contained in:
parent
7c99905312
commit
be8f5699b5
12 changed files with 222 additions and 53 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 2d3db0cce9bd06cf280bbf6a0b10e57982f32fc3
|
||||
Subproject commit 7e37fd971006140db30aa7b4746f4b94f2e5a613
|
|
@ -8,6 +8,8 @@ import flixel.sound.FlxSound;
|
|||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.system.FlxAssets.FlxSoundAsset;
|
||||
import funkin.util.tools.ICloneable;
|
||||
import funkin.data.song.SongData.SongMusicData;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.audio.waveform.WaveformData;
|
||||
import funkin.audio.waveform.WaveformDataParser;
|
||||
import flixel.math.FlxMath;
|
||||
|
@ -28,7 +30,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
/**
|
||||
* Using `FunkinSound.load` will override a dead instance from here rather than creating a new one, if possible!
|
||||
*/
|
||||
static var cache(default, null):FlxTypedGroup<FunkinSound> = new FlxTypedGroup<FunkinSound>();
|
||||
static var pool(default, null):FlxTypedGroup<FunkinSound> = new FlxTypedGroup<FunkinSound>();
|
||||
|
||||
public var muted(default, set):Bool = false;
|
||||
|
||||
|
@ -265,23 +267,55 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new `FunkinSound` object.
|
||||
* Creates a new `FunkinSound` object and loads it as the current music track.
|
||||
*
|
||||
* @param embeddedSound The embedded sound resource you want to play. To stream, use the optional URL parameter instead.
|
||||
* @param volume How loud to play it (0 to 1).
|
||||
* @param looped Whether to loop this sound.
|
||||
* @param group The group to add this sound to.
|
||||
* @param autoDestroy Whether to destroy this sound when it finishes playing.
|
||||
* @param key The key of the music you want to play. Music should be at `music/<key>/<key>.ogg`.
|
||||
* @param overrideExisting Whether to override music if it is already playing.
|
||||
* @param mapTimeChanges Whether to check for `SongMusicData` to update the Conductor with.
|
||||
* Data should be at `music/<key>/<key>-metadata.json`.
|
||||
*/
|
||||
public static function playMusic(key:String, overrideExisting:Bool = false, mapTimeChanges:Bool = true):Void
|
||||
{
|
||||
if (!overrideExisting && FlxG.sound.music?.playing) return;
|
||||
|
||||
if (mapTimeChanges)
|
||||
{
|
||||
var songMusicData:Null<SongMusicData> = SongRegistry.instance.parseMusicData(key);
|
||||
// Will fall back and return null if the metadata doesn't exist or can't be parsed.
|
||||
if (songMusicData != null)
|
||||
{
|
||||
Conductor.instance.mapTimeChanges(songMusicData.timeChanges);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn('Tried and failed to find music metadata for $key');
|
||||
}
|
||||
}
|
||||
|
||||
FlxG.sound.music = FunkinSound.load(Paths.music('$key/$key'));
|
||||
|
||||
// Prevent repeat update() and onFocus() calls.
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new `FunkinSound` object synchronously.
|
||||
*
|
||||
* @param embeddedSound The embedded sound resource you want to play. To stream, use the optional URL parameter instead.
|
||||
* @param volume How loud to play it (0 to 1).
|
||||
* @param looped Whether to loop this sound.
|
||||
* @param group The group to add this sound to.
|
||||
* @param autoDestroy Whether to destroy this sound when it finishes playing.
|
||||
* Leave this value set to `false` if you want to re-use this `FunkinSound` instance.
|
||||
* @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.
|
||||
* @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.
|
||||
*/
|
||||
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
|
||||
{
|
||||
var sound:FunkinSound = cache.recycle(construct);
|
||||
var sound:FunkinSound = pool.recycle(construct);
|
||||
|
||||
// Load the sound.
|
||||
// Sets `exists = true` as a side effect.
|
||||
|
@ -297,9 +331,11 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
sound.persist = true;
|
||||
if (autoPlay) sound.play();
|
||||
|
||||
// Call OnlLoad() because the sound already loaded
|
||||
// Call onLoad() because the sound already loaded
|
||||
if (onLoad != null && sound._sound != null) onLoad();
|
||||
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
|
@ -307,7 +343,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
{
|
||||
var sound:FunkinSound = new FunkinSound();
|
||||
|
||||
cache.add(sound);
|
||||
pool.add(sound);
|
||||
FlxG.sound.list.add(sound);
|
||||
|
||||
return sound;
|
||||
|
|
|
@ -441,6 +441,13 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
return {fileName: entryFilePath, contents: rawJson};
|
||||
}
|
||||
|
||||
function hasMusicDataFile(id:String, ?variation:String):Bool
|
||||
{
|
||||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
var entryFilePath:String = Paths.file('music/$id/$id-metadata${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.json');
|
||||
return openfl.Assets.exists(entryFilePath);
|
||||
}
|
||||
|
||||
function loadEntryChartFile(id:String, ?variation:String):Null<BaseRegistry.JsonFile>
|
||||
{
|
||||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.play;
|
||||
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.addons.display.FlxPieDial;
|
||||
import flixel.addons.display.FlxPieDial;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
|
@ -2711,7 +2712,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
if (targetSongId == null)
|
||||
{
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
||||
FunkinSound.playMusic('freakyMenu');
|
||||
|
||||
// transIn = FlxTransitionableState.defaultTransIn;
|
||||
// transOut = FlxTransitionableState.defaultTransOut;
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
import flash.text.TextField;
|
||||
import openfl.text.TextField;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import flixel.addons.ui.FlxInputText;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxGame;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.FlxState;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.input.touch.FlxTouch;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxMath;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.text.FlxText;
|
||||
|
@ -24,9 +22,12 @@ import flixel.tweens.FlxTween;
|
|||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.data.level.LevelRegistry;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.AngleMask;
|
||||
import funkin.graphics.shaders.HSVShader;
|
||||
import funkin.graphics.shaders.PureColor;
|
||||
|
@ -187,10 +188,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
isDebug = true;
|
||||
#end
|
||||
|
||||
if (FlxG.sound.music == null || (FlxG.sound.music != null && !FlxG.sound.music.playing))
|
||||
{
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
||||
}
|
||||
FunkinSound.playMusic('freakyMenu');
|
||||
|
||||
// Add a null entry that represents the RANDOM option
|
||||
songs.push(null);
|
||||
|
@ -590,7 +588,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
});
|
||||
}
|
||||
|
||||
public function generateSongList(?filterStuff:SongFilter, force:Bool = false)
|
||||
public function generateSongList(?filterStuff:SongFilter, force:Bool = false):Void
|
||||
{
|
||||
curSelected = 1;
|
||||
|
||||
|
@ -693,7 +691,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var busy:Bool = false; // Set to true once the user has pressed enter to select a song.
|
||||
|
||||
override function update(elapsed:Float)
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
|
@ -983,7 +981,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
function changeDiff(change:Int = 0)
|
||||
function changeDiff(change:Int = 0):Void
|
||||
{
|
||||
touchTimer = 0;
|
||||
|
||||
|
@ -1173,7 +1171,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
difficultyStars.difficulty = daSong?.songRating ?? 0;
|
||||
}
|
||||
|
||||
function changeSelection(change:Int = 0)
|
||||
function changeSelection(change:Int = 0):Void
|
||||
{
|
||||
// NGio.logEvent('Fresh');
|
||||
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
|
||||
|
@ -1228,7 +1226,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
// TODO: Stream the instrumental of the selected song?
|
||||
if (prevSelected == 0)
|
||||
{
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
||||
FunkinSound.playMusic('freakyMenu');
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
@ -1259,7 +1257,7 @@ class DifficultySelector extends FlxSprite
|
|||
flipX = flipped;
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if (flipX && controls.UI_RIGHT_P) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P) moveShitDown();
|
||||
|
@ -1267,7 +1265,7 @@ class DifficultySelector extends FlxSprite
|
|||
super.update(elapsed);
|
||||
}
|
||||
|
||||
function moveShitDown()
|
||||
function moveShitDown():Void
|
||||
{
|
||||
offset.y -= 5;
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ import flixel.util.typeLimit.NextState;
|
|||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.input.touch.FlxTouch;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.data.song.SongData.SongMusicData;
|
||||
import flixel.tweens.FlxEase;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import flixel.util.FlxTimer;
|
||||
|
@ -51,7 +53,7 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
if (!(FlxG?.sound?.music?.playing ?? false))
|
||||
{
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
||||
playMenuMusic();
|
||||
}
|
||||
|
||||
persistentUpdate = persistentDraw = true;
|
||||
|
@ -151,6 +153,11 @@ class MainMenuState extends MusicBeatState
|
|||
// NG.core.calls.event.logEvent('swag').send();
|
||||
}
|
||||
|
||||
function playMenuMusic():Void
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu');
|
||||
}
|
||||
|
||||
function resetCamStuff()
|
||||
{
|
||||
FlxG.cameras.reset(new FunkinCamera());
|
||||
|
|
|
@ -16,6 +16,7 @@ import flixel.tweens.FlxTween;
|
|||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.data.level.LevelRegistry;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.PlayState;
|
||||
|
@ -234,17 +235,7 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
function playMenuMusic():Void
|
||||
{
|
||||
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
|
||||
{
|
||||
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
||||
if (freakyMenuMetadata != null)
|
||||
{
|
||||
Conductor.instance.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
||||
}
|
||||
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||
}
|
||||
FunkinSound.playMusic('freakyMenu');
|
||||
}
|
||||
|
||||
function updateData():Void
|
||||
|
|
|
@ -18,6 +18,7 @@ import funkin.graphics.FunkinSprite;
|
|||
import funkin.ui.MusicBeatState;
|
||||
import funkin.data.song.SongData.SongMusicData;
|
||||
import funkin.graphics.shaders.TitleOutline;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import funkin.ui.AtlasText;
|
||||
import openfl.Assets;
|
||||
|
@ -219,16 +220,11 @@ class TitleState extends MusicBeatState
|
|||
|
||||
function playMenuMusic():Void
|
||||
{
|
||||
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
|
||||
{
|
||||
var freakyMenuMetadata:Null<SongMusicData> = SongRegistry.instance.parseMusicData('freakyMenu');
|
||||
if (freakyMenuMetadata != null)
|
||||
{
|
||||
Conductor.instance.mapTimeChanges(freakyMenuMetadata.timeChanges);
|
||||
}
|
||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'), 0);
|
||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||
}
|
||||
var shouldFadeIn = (FlxG.sound.music == null);
|
||||
// Load music. Includes logic to handle BPM changes.
|
||||
FunkinSound.playMusic('freakyMenu', false, true);
|
||||
// Fade from 0.0 to 0.7 over 4 seconds
|
||||
if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||
}
|
||||
|
||||
function getIntroTextShit():Array<Array<String>>
|
||||
|
|
|
@ -238,11 +238,38 @@ class LoadingState extends MusicBeatState
|
|||
FunkinSprite.cacheTexture(Paths.image('shit', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this
|
||||
|
||||
// List all image assets in the level's library.
|
||||
// This is crude and I want to remove it when we have a proper asset caching system.
|
||||
// TODO: Get rid of this junk!
|
||||
var library = openfl.utils.Assets.getLibrary(PlayStatePlaylist.campaignId);
|
||||
var assets = library.list(lime.utils.AssetType.IMAGE);
|
||||
trace('Got ${assets.length} assets: ${assets}');
|
||||
|
||||
// TODO: assets includes non-images! This is a bug with Polymod
|
||||
for (asset in assets)
|
||||
{
|
||||
// Exclude items of the wrong type.
|
||||
var path = '${PlayStatePlaylist.campaignId}:${asset}';
|
||||
// TODO DUMB HACK DUMB HACK why doesn't filtering by AssetType.IMAGE above work
|
||||
// I will fix this properly later I swear -eric
|
||||
if (!path.endsWith('.png')) continue;
|
||||
|
||||
FunkinSprite.cacheTexture(path);
|
||||
|
||||
// Another dumb hack: FlxAnimate fetches from OpenFL's BitmapData cache directly and skips the FlxGraphic cache.
|
||||
// Since FlxGraphic tells OpenFL to not cache it, we have to do it manually.
|
||||
if (path.endsWith('spritemap1.png'))
|
||||
{
|
||||
openfl.Assets.getBitmapData(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
// FunkinSprite.cacheAllNoteStyleTextures(noteStyle) // This will replace the stuff above!
|
||||
// FunkinSprite.cacheAllCharacterTextures(player)
|
||||
// FunkinSprite.cacheAllCharacterTextures(girlfriend)
|
||||
// FunkinSprite.cacheAllCharacterTextures(opponent)
|
||||
// FunkinSprite.cacheAllStageTextures(stage)
|
||||
// FunkinSprite.cacheAllSongTextures(stage)
|
||||
|
||||
FunkinSprite.purgeCache();
|
||||
|
||||
|
|
76
source/funkin/util/logging/Perf.hx
Normal file
76
source/funkin/util/logging/Perf.hx
Normal file
|
@ -0,0 +1,76 @@
|
|||
package funkin.util.logging;
|
||||
|
||||
/**
|
||||
* A small utility class for timing how long functions take.
|
||||
* Specify a string as a label (or don't, by default it uses the name of the function it was called from.)
|
||||
*
|
||||
* Example:
|
||||
* ```haxe
|
||||
*
|
||||
* var perf = new Perf();
|
||||
* ...
|
||||
* perf.print();
|
||||
* ```
|
||||
*/
|
||||
class Perf
|
||||
{
|
||||
final startTime:Float;
|
||||
final label:Null<String>;
|
||||
final posInfos:Null<haxe.PosInfos>;
|
||||
|
||||
/**
|
||||
* Create a new performance marker.
|
||||
* @param label Optionally specify a label to use for the performance marker. Defaults to the function name.
|
||||
* @param posInfos The position of the calling function. Used to build the default label.
|
||||
* Note: `haxe.PosInfos` is magic and automatically populated by the compiler!
|
||||
*/
|
||||
public function new(?label:String, ?posInfos:haxe.PosInfos)
|
||||
{
|
||||
this.label = label;
|
||||
this.posInfos = posInfos;
|
||||
startTime = current();
|
||||
}
|
||||
|
||||
/**
|
||||
* The current timestamp, in fractional seconds.
|
||||
* @return The current timestamp.
|
||||
*/
|
||||
static function current():Float
|
||||
{
|
||||
#if sys
|
||||
// This one is more accurate if it's available.
|
||||
return Sys.time();
|
||||
#else
|
||||
return haxe.Timer.stamp();
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration in seconds since this `Perf` was created.
|
||||
* @return The duration in seconds
|
||||
*/
|
||||
public function duration():Float
|
||||
{
|
||||
return current() - startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* A rounded millisecond duration
|
||||
* @return The duration in milliseconds
|
||||
*/
|
||||
public function durationClean():Float
|
||||
{
|
||||
var round:Float = 100;
|
||||
return Math.floor(duration() * Constants.MS_PER_SEC * round) / round;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanly prints the duration since this `Perf` was created.
|
||||
*/
|
||||
public function print():Void
|
||||
{
|
||||
var label:String = label ?? (posInfos == null ? 'unknown' : '${posInfos.className}#${posInfos.methodName}()');
|
||||
|
||||
trace('[PERF] [$label] Took ${durationClean()}ms.');
|
||||
}
|
||||
}
|
|
@ -27,6 +27,36 @@ class StringTools
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a given prefix from a string.
|
||||
* @param value The string to strip.
|
||||
* @param prefix The prefix to strip. If the prefix isn't found, the original string is returned.
|
||||
* @return The stripped string.
|
||||
*/
|
||||
public static function stripPrefix(value:String, prefix:String):String
|
||||
{
|
||||
if (value.startsWith(prefix))
|
||||
{
|
||||
return value.substr(prefix.length);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a given suffix from a string.
|
||||
* @param value The string to strip.
|
||||
* @param suffix The suffix to strip. If the suffix isn't found, the original string is returned.
|
||||
* @return The stripped string.
|
||||
*/
|
||||
public static function stripSuffix(value:String, suffix:String):String
|
||||
{
|
||||
if (value.endsWith(suffix))
|
||||
{
|
||||
return value.substr(0, value.length - suffix.length);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to lower kebab case. For example, "Hello World" becomes "hello-world".
|
||||
*
|
||||
|
|
Loading…
Add table
Reference in a new issue