Rework offsets to be built into the FlxSound

This commit is contained in:
EliteMasterEric 2023-12-06 15:04:24 -05:00
parent 1716ffc57f
commit 1abd587645
6 changed files with 168 additions and 52 deletions
source/funkin

View file

@ -0,0 +1,129 @@
package funkin.audio;
#if flash11
import flash.media.Sound;
import flash.utils.ByteArray;
#end
import flixel.sound.FlxSound;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.system.FlxAssets.FlxSoundAsset;
import openfl.Assets;
#if (openfl >= "8.0.0")
import openfl.utils.AssetType;
#end
/**
* A FlxSound which adds additional functionality:
* - Delayed playback via negative song position.
*/
@:nullSafety
class FunkinSound extends FlxSound
{
static var cache(default, null):FlxTypedGroup<FunkinSound> = new FlxTypedGroup<FunkinSound>();
/**
* Are we in a state where the song should play but time is negative?
*/
var shouldPlay:Bool = false;
public function new()
{
super();
}
public override function update(elapsedSec:Float)
{
if (!playing && !shouldPlay) return;
if (_time < 0)
{
var elapsedMs = elapsedSec * Constants.MS_PER_SEC;
_time += elapsedMs;
if (_time >= 0)
{
_time = 0;
shouldPlay = false;
super.play();
}
}
else
{
super.update(elapsedSec);
}
}
public override function play(forceRestart:Bool = false, startTime:Float = 0, ?endTime:Float):FunkinSound
{
if (!exists) return this;
if (forceRestart)
{
cleanup(false, true);
}
else if (playing || shouldPlay)
{
return this;
}
if (startTime < 0)
{
shouldPlay = true;
_time = startTime;
this.endTime = endTime;
return this;
}
if (_paused)
{
resume();
}
else
{
startSound(startTime);
}
this.endTime = endTime;
return this;
}
/**
* Creates a new `FunkinSound` object.
*
* @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.
*/
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);
sound.loadEmbedded(embeddedSound, looped, autoDestroy, onComplete);
sound.volume = volume;
sound.group = FlxG.sound.defaultSoundGroup;
if (autoPlay) sound.play();
// Call OnlLoad() because the sound already loaded
if (onLoad != null && sound._sound != null) onLoad();
return sound;
}
static function construct():FunkinSound
{
var sound:FunkinSound = new FunkinSound();
cache.add(sound);
FlxG.sound.list.add(sound);
return sound;
}
}

View file

@ -2,12 +2,13 @@ package funkin.audio;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.sound.FlxSound;
import funkin.audio.FunkinSound;
/**
* A group of FlxSounds that are all synced together.
* Unlike FlxSoundGroup, you cann also control their time and pitch.
* A group of FunkinSounds that are all synced together.
* Unlike FlxSoundGroup, you can also control their time and pitch.
*/
class SoundGroup extends FlxTypedGroup<FlxSound>
class SoundGroup extends FlxTypedGroup<FunkinSound>
{
public var time(get, set):Float;
@ -28,14 +29,13 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
if (files == null)
{
// Add an empty voice.
result.add(new FlxSound());
result.add(new FunkinSound());
return result;
}
for (sndFile in files)
{
var snd:FlxSound = new FlxSound().loadEmbedded(Paths.voices(song, '$sndFile'));
FlxG.sound.list.add(snd); // adds it to sound group for proper volumes
var snd:FunkinSound = FunkinSound.load(Paths.voices(song, '$sndFile'));
result.add(snd); // adds it to main group for other shit
}
@ -67,9 +67,9 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
/**
* Add a sound to the group.
*/
public override function add(sound:FlxSound):FlxSound
public override function add(sound:FunkinSound):FunkinSound
{
var result:FlxSound = super.add(sound);
var result:FunkinSound = super.add(sound);
if (result == null) return null;
@ -97,7 +97,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
*/
public function pause()
{
forEachAlive(function(sound:FlxSound) {
forEachAlive(function(sound:FunkinSound) {
sound.pause();
});
}
@ -107,7 +107,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
*/
public function play(forceRestart:Bool = false, startTime:Float = 0.0, ?endTime:Float)
{
forEachAlive(function(sound:FlxSound) {
forEachAlive(function(sound:FunkinSound) {
sound.play(forceRestart, startTime, endTime);
});
}
@ -117,7 +117,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
*/
public function resume()
{
forEachAlive(function(sound:FlxSound) {
forEachAlive(function(sound:FunkinSound) {
sound.resume();
});
}
@ -127,7 +127,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
*/
public function stop()
{
forEachAlive(function(sound:FlxSound) {
forEachAlive(function(sound:FunkinSound) {
sound.stop();
});
}
@ -151,7 +151,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
function set_time(time:Float):Float
{
forEachAlive(function(snd) {
forEachAlive(function(snd:FunkinSound) {
// account for different offsets per sound?
snd.time = time;
});
@ -169,7 +169,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
// in PlayState, adjust the code so that it only mutes the player1 vocal tracks?
function set_volume(volume:Float):Float
{
forEachAlive(function(snd) {
forEachAlive(function(snd:FunkinSound) {
snd.volume = volume;
});
@ -189,7 +189,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
{
#if FLX_PITCH
trace('Setting audio pitch to ' + val);
forEachAlive(function(snd) {
forEachAlive(function(snd:FunkinSound) {
snd.pitch = val;
});
#end

View file

@ -1,12 +1,12 @@
package funkin.audio;
import flixel.sound.FlxSound;
import funkin.audio.FunkinSound;
import flixel.group.FlxGroup.FlxTypedGroup;
class VoicesGroup extends SoundGroup
{
var playerVoices:FlxTypedGroup<FlxSound>;
var opponentVoices:FlxTypedGroup<FlxSound>;
var playerVoices:FlxTypedGroup<FunkinSound>;
var opponentVoices:FlxTypedGroup<FunkinSound>;
/**
* Control the volume of only the sounds in the player group.
@ -31,14 +31,14 @@ class VoicesGroup extends SoundGroup
public function new()
{
super();
playerVoices = new FlxTypedGroup<FlxSound>();
opponentVoices = new FlxTypedGroup<FlxSound>();
playerVoices = new FlxTypedGroup<FunkinSound>();
opponentVoices = new FlxTypedGroup<FunkinSound>();
}
/**
* Add a voice to the player group.
*/
public function addPlayerVoice(sound:FlxSound):Void
public function addPlayerVoice(sound:FunkinSound):Void
{
super.add(sound);
playerVoices.add(sound);
@ -46,7 +46,7 @@ class VoicesGroup extends SoundGroup
function set_playerVolume(volume:Float):Float
{
playerVoices.forEachAlive(function(voice:FlxSound) {
playerVoices.forEachAlive(function(voice:FunkinSound) {
voice.volume = volume;
});
return playerVolume = volume;
@ -59,10 +59,10 @@ class VoicesGroup extends SoundGroup
snd.time = time;
});
playerVoices.forEachAlive(function(voice:FlxSound) {
playerVoices.forEachAlive(function(voice:FunkinSound) {
voice.time -= playerVoicesOffset;
});
opponentVoices.forEachAlive(function(voice:FlxSound) {
opponentVoices.forEachAlive(function(voice:FunkinSound) {
voice.time -= opponentVoicesOffset;
});
@ -71,7 +71,7 @@ class VoicesGroup extends SoundGroup
function set_playerVoicesOffset(offset:Float):Float
{
playerVoices.forEachAlive(function(voice:FlxSound) {
playerVoices.forEachAlive(function(voice:FunkinSound) {
voice.time += playerVoicesOffset;
voice.time -= offset;
});
@ -80,33 +80,17 @@ class VoicesGroup extends SoundGroup
function set_opponentVoicesOffset(offset:Float):Float
{
opponentVoices.forEachAlive(function(voice:FlxSound) {
opponentVoices.forEachAlive(function(voice:FunkinSound) {
voice.time += opponentVoicesOffset;
voice.time -= offset;
});
return opponentVoicesOffset = offset;
}
public override function update(elapsed:Float):Void
{
forEachAlive(function(snd) {
if (snd.time < 0)
{
// Sync the time without calling update().
// time gets reset if it's negative.
snd.time += elapsed * 1000;
}
else
{
snd.update(elapsed);
}
});
}
/**
* Add a voice to the opponent group.
*/
public function addOpponentVoice(sound:FlxSound):Void
public function addOpponentVoice(sound:FunkinSound):Void
{
super.add(sound);
opponentVoices.add(sound);
@ -114,7 +98,7 @@ class VoicesGroup extends SoundGroup
function set_opponentVolume(volume:Float):Float
{
opponentVoices.forEachAlive(function(voice:FlxSound) {
opponentVoices.forEachAlive(function(voice:FunkinSound) {
voice.volume = volume;
});
return opponentVolume = volume;

View file

@ -2,6 +2,7 @@ package funkin.play.song;
import flixel.sound.FlxSound;
import funkin.audio.VoicesGroup;
import funkin.audio.FunkinSound;
import funkin.data.IRegistryEntry;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongChartData;
@ -532,16 +533,16 @@ class SongDifficulty
}
// Add player vocals.
if (voiceList[0] != null) result.addPlayerVoice(new FlxSound().loadEmbedded(Assets.getSound(voiceList[0])));
if (voiceList[0] != null) result.addPlayerVoice(FunkinSound.load(Assets.getSound(voiceList[0])));
// Add opponent vocals.
if (voiceList[1] != null) result.addOpponentVoice(new FlxSound().loadEmbedded(Assets.getSound(voiceList[1])));
if (voiceList[1] != null) result.addOpponentVoice(FunkinSound.load(Assets.getSound(voiceList[1])));
// Add additional vocals.
if (voiceList.length > 2)
{
for (i in 2...voiceList.length)
{
result.add(new FlxSound().loadEmbedded(Assets.getSound(voiceList[i])));
result.add(FunkinSound.load(Assets.getSound(voiceList[i])));
}
}

View file

@ -3,6 +3,7 @@ package funkin.ui.debug.charting.handlers;
import flixel.system.FlxAssets.FlxSoundAsset;
import flixel.system.FlxSound;
import funkin.audio.VoicesGroup;
import funkin.audio.FunkinSound;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.util.FileUtil;
import funkin.util.assets.SoundUtil;
@ -165,7 +166,7 @@ class ChartEditorAudioHandler
{
var trackId:String = '${charId}${instId == '' ? '' : '-${instId}'}';
var vocalTrackData:Null<Bytes> = state.audioVocalTrackData.get(trackId);
var vocalTrack:Null<FlxSound> = SoundUtil.buildFlxSoundFromBytes(vocalTrackData);
var vocalTrack:Null<FunkinSound> = SoundUtil.buildSoundFromBytes(vocalTrackData);
if (state.audioVocalTrackGroup == null) state.audioVocalTrackGroup = new VoicesGroup();

View file

@ -1,7 +1,8 @@
package funkin.util.assets;
import haxe.io.Bytes;
import flixel.system.FlxSound;
import openfl.media.Sound as OpenFLSound;
import funkin.audio.FunkinSound;
class SoundUtil
{
@ -11,13 +12,13 @@ class SoundUtil
* @param input The byte data.
* @return The playable sound, or `null` if loading failed.
*/
public static function buildFlxSoundFromBytes(input:Null<Bytes>):Null<FlxSound>
public static function buildSoundFromBytes(input:Null<Bytes>):Null<FunkinSound>
{
if (input == null) return null;
var openflSound:openfl.media.Sound = new openfl.media.Sound();
var openflSound:OpenFLSound = new OpenFLSound();
openflSound.loadCompressedDataFromByteArray(openfl.utils.ByteArray.fromBytes(input), input.length);
var output:FlxSound = FlxG.sound.load(openflSound, 1.0, false);
var output:FunkinSound = FunkinSound.load(openflSound, 1.0, false);
return output;
}
}