2022-03-08 03:13:53 -05:00
|
|
|
package funkin;
|
2020-10-03 02:50:15 -04:00
|
|
|
|
2022-09-23 00:49:42 -04:00
|
|
|
import flixel.util.FlxSignal;
|
2022-03-08 03:13:53 -05:00
|
|
|
import funkin.SongLoad.SwagSong;
|
2022-09-22 06:34:03 -04:00
|
|
|
import funkin.play.song.Song.SongDifficulty;
|
|
|
|
import funkin.play.song.SongData.SongTimeChange;
|
2021-02-11 17:06:26 -05:00
|
|
|
|
|
|
|
typedef BPMChangeEvent =
|
|
|
|
{
|
|
|
|
var stepTime:Int;
|
|
|
|
var songTime:Float;
|
2021-03-20 12:33:29 -04:00
|
|
|
var bpm:Float;
|
2021-02-11 17:06:26 -05:00
|
|
|
}
|
|
|
|
|
2020-10-03 02:50:15 -04:00
|
|
|
class Conductor
|
|
|
|
{
|
2022-03-29 21:56:04 -04:00
|
|
|
/**
|
2022-09-22 06:34:03 -04:00
|
|
|
* The list of time changes in the song.
|
|
|
|
* There should be at least one time change (at the beginning of the song) to define the BPM.
|
2022-03-29 21:56:04 -04:00
|
|
|
*/
|
2022-09-23 00:49:42 -04:00
|
|
|
private static var timeChanges:Array<SongTimeChange> = [];
|
2022-03-29 21:56:04 -04:00
|
|
|
|
|
|
|
/**
|
2022-09-22 06:34:03 -04:00
|
|
|
* The current time change.
|
|
|
|
*/
|
2022-09-23 00:49:42 -04:00
|
|
|
private static var currentTimeChange:SongTimeChange;
|
2022-09-22 06:34:03 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The current position in the song in milliseconds.
|
|
|
|
* Updated every frame based on the audio position.
|
|
|
|
*/
|
|
|
|
public static var songPosition:Float;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Beats per minute of the current song at the current time.
|
|
|
|
*/
|
2022-09-26 17:18:19 -04:00
|
|
|
public static var bpm(get, null):Float;
|
2022-09-22 06:34:03 -04:00
|
|
|
|
|
|
|
static function get_bpm():Float
|
|
|
|
{
|
2022-09-26 17:18:19 -04:00
|
|
|
if (bpmOverride != null)
|
|
|
|
return bpmOverride;
|
|
|
|
|
2022-09-22 06:34:03 -04:00
|
|
|
if (currentTimeChange == null)
|
|
|
|
return 100;
|
|
|
|
|
|
|
|
return currentTimeChange.bpm;
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:18:19 -04:00
|
|
|
static var bpmOverride:Null<Float> = null;
|
|
|
|
|
2022-09-22 06:34:03 -04:00
|
|
|
// OLD, replaced with timeChanges.
|
|
|
|
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Duration of a beat in millisecond. Calculated based on bpm.
|
2022-03-29 21:56:04 -04:00
|
|
|
*/
|
|
|
|
public static var crochet(get, null):Float;
|
|
|
|
|
|
|
|
static function get_crochet():Float
|
|
|
|
{
|
|
|
|
return ((60 / bpm) * 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-09-22 06:34:03 -04:00
|
|
|
* Duration of a step in milliseconds. Calculated based on bpm.
|
2022-03-29 21:56:04 -04:00
|
|
|
*/
|
|
|
|
public static var stepCrochet(get, null):Float;
|
|
|
|
|
|
|
|
static function get_stepCrochet():Float
|
|
|
|
{
|
|
|
|
return crochet / 4;
|
|
|
|
}
|
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
public static var currentBeat(get, null):Int;
|
2022-09-22 06:34:03 -04:00
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
static function get_currentBeat():Int
|
2022-09-22 06:34:03 -04:00
|
|
|
{
|
|
|
|
return currentBeat;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static var currentStep(get, null):Int;
|
|
|
|
|
|
|
|
static function get_currentStep():Int
|
|
|
|
{
|
|
|
|
return currentStep;
|
|
|
|
}
|
2022-06-01 22:07:42 -04:00
|
|
|
|
2022-09-23 00:49:42 -04:00
|
|
|
public static var beatHit(default, null):FlxSignal = new FlxSignal();
|
|
|
|
public static var stepHit(default, null):FlxSignal = new FlxSignal();
|
|
|
|
|
2020-10-24 05:19:13 -04:00
|
|
|
public static var lastSongPos:Float;
|
2022-07-06 15:27:45 -04:00
|
|
|
public static var visualOffset:Float = 0;
|
|
|
|
public static var audioOffset:Float = 0;
|
2020-10-03 02:50:15 -04:00
|
|
|
public static var offset:Float = 0;
|
|
|
|
|
2022-09-23 00:49:42 -04:00
|
|
|
private function new()
|
2022-09-22 06:34:03 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getLastBPMChange()
|
|
|
|
{
|
|
|
|
var lastChange:BPMChangeEvent = {
|
|
|
|
stepTime: 0,
|
|
|
|
songTime: 0,
|
|
|
|
bpm: 0
|
|
|
|
}
|
|
|
|
for (i in 0...Conductor.bpmChangeMap.length)
|
|
|
|
{
|
|
|
|
if (Conductor.songPosition >= Conductor.bpmChangeMap[i].songTime)
|
|
|
|
lastChange = Conductor.bpmChangeMap[i];
|
|
|
|
|
|
|
|
if (Conductor.songPosition < Conductor.bpmChangeMap[i].songTime)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return lastChange;
|
|
|
|
}
|
2021-02-11 17:06:26 -05:00
|
|
|
|
2022-09-26 17:18:19 -04:00
|
|
|
@:deprecated // Use loadSong with metadata files instead.
|
2022-09-22 06:34:03 -04:00
|
|
|
public static function forceBPM(bpm:Float)
|
|
|
|
{
|
2022-09-26 17:18:19 -04:00
|
|
|
Conductor.bpmOverride = bpm;
|
2022-09-22 06:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the conductor with the current song position.
|
|
|
|
* BPM, current step, etc. will be re-calculated based on the song position.
|
2022-09-26 17:18:19 -04:00
|
|
|
*
|
|
|
|
* @param songPosition The current position in the song in milliseconds.
|
|
|
|
* Leave blank to use the FlxG.sound.music position.
|
2022-09-22 06:34:03 -04:00
|
|
|
*/
|
2022-09-26 17:18:19 -04:00
|
|
|
public static function update(songPosition:Float = null)
|
2022-09-22 06:34:03 -04:00
|
|
|
{
|
2022-09-26 17:18:19 -04:00
|
|
|
if (songPosition == null)
|
|
|
|
songPosition = (FlxG.sound.music != null) ? (FlxG.sound.music.time + Conductor.offset) : 0;
|
|
|
|
|
2022-09-23 00:49:42 -04:00
|
|
|
var oldBeat = currentBeat;
|
|
|
|
var oldStep = currentStep;
|
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
Conductor.songPosition = songPosition;
|
2022-09-26 17:18:19 -04:00
|
|
|
// Conductor.bpm = Conductor.getLastBPMChange().bpm;
|
2022-09-23 00:49:42 -04:00
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
currentTimeChange = timeChanges[0];
|
2022-09-23 00:49:42 -04:00
|
|
|
for (i in 0...timeChanges.length)
|
|
|
|
{
|
2022-09-24 01:28:39 -04:00
|
|
|
if (songPosition >= timeChanges[i].timeStamp)
|
2022-09-23 00:49:42 -04:00
|
|
|
currentTimeChange = timeChanges[i];
|
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
if (songPosition < timeChanges[i].timeStamp)
|
2022-09-23 00:49:42 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:18:19 -04:00
|
|
|
if (currentTimeChange == null && bpmOverride == null)
|
2022-09-24 01:28:39 -04:00
|
|
|
{
|
|
|
|
trace('WARNING: Conductor is broken, timeChanges is empty.');
|
|
|
|
}
|
2022-09-26 17:18:19 -04:00
|
|
|
else if (currentTimeChange != null)
|
2022-09-24 01:28:39 -04:00
|
|
|
{
|
|
|
|
currentStep = Math.floor((currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepCrochet);
|
|
|
|
currentBeat = Math.floor(currentStep / 4);
|
|
|
|
}
|
2022-09-26 17:18:19 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Assume a constant BPM equal to the forced value.
|
|
|
|
currentStep = Math.floor((songPosition) / stepCrochet);
|
|
|
|
currentBeat = Math.floor(currentStep / 4);
|
|
|
|
}
|
2022-09-23 00:49:42 -04:00
|
|
|
|
|
|
|
// FlxSignals are really cool.
|
|
|
|
if (currentStep != oldStep)
|
|
|
|
stepHit.dispatch();
|
|
|
|
|
|
|
|
if (currentBeat != oldBeat)
|
|
|
|
beatHit.dispatch();
|
2022-09-22 06:34:03 -04:00
|
|
|
}
|
2020-10-04 20:53:49 -04:00
|
|
|
|
2022-09-26 17:18:19 -04:00
|
|
|
@:deprecated // Switch to TimeChanges instead.
|
2021-02-11 17:06:26 -05:00
|
|
|
public static function mapBPMChanges(song:SwagSong)
|
|
|
|
{
|
|
|
|
bpmChangeMap = [];
|
|
|
|
|
2021-03-20 12:33:29 -04:00
|
|
|
var curBPM:Float = song.bpm;
|
2021-02-11 17:06:26 -05:00
|
|
|
var totalSteps:Int = 0;
|
|
|
|
var totalPos:Float = 0;
|
2021-12-06 17:49:05 -05:00
|
|
|
for (i in 0...SongLoad.getSong().length)
|
2021-02-11 17:06:26 -05:00
|
|
|
{
|
2021-12-06 17:49:05 -05:00
|
|
|
if (SongLoad.getSong()[i].changeBPM && SongLoad.getSong()[i].bpm != curBPM)
|
2021-02-11 17:06:26 -05:00
|
|
|
{
|
2021-12-06 17:49:05 -05:00
|
|
|
curBPM = SongLoad.getSong()[i].bpm;
|
2021-02-11 17:06:26 -05:00
|
|
|
var event:BPMChangeEvent = {
|
|
|
|
stepTime: totalSteps,
|
|
|
|
songTime: totalPos,
|
|
|
|
bpm: curBPM
|
|
|
|
};
|
|
|
|
bpmChangeMap.push(event);
|
|
|
|
}
|
|
|
|
|
2021-12-06 17:49:05 -05:00
|
|
|
var deltaSteps:Int = SongLoad.getSong()[i].lengthInSteps;
|
2021-02-11 17:06:26 -05:00
|
|
|
totalSteps += deltaSteps;
|
|
|
|
totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps;
|
|
|
|
}
|
|
|
|
}
|
2022-09-22 06:34:03 -04:00
|
|
|
|
|
|
|
public static function mapTimeChanges(currentChart:SongDifficulty)
|
|
|
|
{
|
|
|
|
var songTimeChanges:Array<SongTimeChange> = currentChart.timeChanges;
|
|
|
|
|
|
|
|
timeChanges = [];
|
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
for (currentTimeChange in songTimeChanges)
|
2022-09-22 06:34:03 -04:00
|
|
|
{
|
2022-09-24 01:28:39 -04:00
|
|
|
// var prevTimeChange:SongTimeChange = timeChanges.length == 0 ? null : timeChanges[timeChanges.length - 1];
|
2022-09-23 00:49:42 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
if (prevTimeChange != null)
|
|
|
|
{
|
|
|
|
var deltaTime:Float = currentTimeChange.timeStamp - prevTimeChange.timeStamp;
|
|
|
|
var deltaSteps:Int = Math.round(deltaTime / (60 / prevTimeChange.bpm) * 1000 / 4);
|
|
|
|
|
|
|
|
currentTimeChange.stepTime = prevTimeChange.stepTime + deltaSteps;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We know the time and steps of this time change is 0, since this is the first time change.
|
|
|
|
currentTimeChange.stepTime = 0;
|
|
|
|
}
|
|
|
|
*/
|
2022-09-22 06:34:03 -04:00
|
|
|
|
|
|
|
timeChanges.push(currentTimeChange);
|
|
|
|
}
|
|
|
|
|
2022-09-24 01:28:39 -04:00
|
|
|
trace('Done mapping time changes: ' + timeChanges);
|
|
|
|
|
2022-09-22 06:34:03 -04:00
|
|
|
// Done.
|
|
|
|
}
|
2020-10-03 02:50:15 -04:00
|
|
|
}
|