1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-01-14 08:37:49 +00:00

Refactor conductor (crochet->lengthMs)

This commit is contained in:
EliteMasterEric 2023-06-15 00:13:18 -04:00
parent 6ac4db48d9
commit f6c38f9bdd

View file

@ -1,9 +1,8 @@
package funkin; package funkin;
import flixel.util.FlxSignal;
import funkin.SongLoad.SwagSong;
import funkin.play.song.Song.SongDifficulty;
import funkin.play.song.SongData.SongTimeChange; import funkin.play.song.SongData.SongTimeChange;
import flixel.util.FlxSignal;
import funkin.play.song.Song.SongDifficulty;
typedef BPMChangeEvent = typedef BPMChangeEvent =
{ {
@ -12,18 +11,27 @@ typedef BPMChangeEvent =
var bpm:Float; var bpm:Float;
} }
/**
* A global source of truth for timing information.
*/
class Conductor class Conductor
{ {
/** static final STEPS_PER_BEAT:Int = 4;
* 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.
*/
static var timeChanges:Array<SongTimeChange> = [];
/** // onBeatHit is called every quarter note
* The current time change. // onStepHit is called every sixteenth note
*/ // 4/4 = 4 beats per measure = 16 steps per measure
static var currentTimeChange:SongTimeChange; // 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second
// 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second
// 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
// 3/4 = 3 beats per measure = 12 steps per measure
// (IDENTICAL TO 4/4 but shorter measure length)
// 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second
// 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second
// 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
// 7/8 = 3.5 beats per measure = 14 steps per measure
/** /**
* The current position in the song in milliseconds. * The current position in the song in milliseconds.
@ -47,8 +55,25 @@ class Conductor
static var bpmOverride:Null<Float> = null; static var bpmOverride:Null<Float> = null;
// OLD, replaced with timeChanges. /**
public static var bpmChangeMap:Array<BPMChangeEvent> = []; * Current position in the song, in whole measures.
*/
public static var currentMeasure(default, null):Int;
/**
* Current position in the song, in whole beats.
**/
public static var currentBeat(default, null):Int;
/**
* Current position in the song, in whole steps.
*/
public static var currentStep(default, null):Int;
/**
* Current position in the song, in steps and fractions of a step.
*/
public static var currentStepTime(default, null):Float;
/** /**
* Duration of a measure in milliseconds. Calculated based on bpm. * Duration of a measure in milliseconds. Calculated based on bpm.
@ -57,29 +82,33 @@ class Conductor
static function get_measureLengthMs():Float static function get_measureLengthMs():Float
{ {
return crochet * timeSignatureNumerator; return beatLengthMs * timeSignatureNumerator;
} }
/** /**
* Duration of a beat in millisecond. Calculated based on bpm. * Duration of a beat (quarter note) in milliseconds. Calculated based on bpm.
*/ */
public static var crochet(get, null):Float; public static var beatLengthMs(get, null):Float;
static function get_crochet():Float static function get_beatLengthMs():Float
{ {
// Tied directly to BPM.
return ((60 / bpm) * 1000); return ((60 / bpm) * 1000);
} }
/** /**
* Duration of a step (quarter) in milliseconds. Calculated based on bpm. * Duration of a step (sixteenth) in milliseconds. Calculated based on bpm.
*/ */
public static var stepCrochet(get, null):Float; public static var stepLengthMs(get, null):Float;
static function get_stepCrochet():Float static function get_stepLengthMs():Float
{ {
return crochet / timeSignatureNumerator; return beatLengthMs / STEPS_PER_BEAT;
} }
/**
* The numerator of the current time signature (number of notes in a measure)
*/
public static var timeSignatureNumerator(get, null):Int; public static var timeSignatureNumerator(get, null):Int;
static function get_timeSignatureNumerator():Int static function get_timeSignatureNumerator():Int
@ -89,6 +118,9 @@ class Conductor
return currentTimeChange.timeSignatureNum; return currentTimeChange.timeSignatureNum;
} }
/**
* The numerator of the current time signature (length of notes in a measure)
*/
public static var timeSignatureDenominator(get, null):Int; public static var timeSignatureDenominator(get, null):Int;
static function get_timeSignatureDenominator():Int static function get_timeSignatureDenominator():Int
@ -98,30 +130,57 @@ class Conductor
return currentTimeChange.timeSignatureDen; return currentTimeChange.timeSignatureDen;
} }
/**
* Current position in the song, in beats.
**/
public static var currentBeat(default, null):Int;
/**
* Current position in the song, in steps.
*/
public static var currentStep(default, null):Int;
/**
* Current position in the song, in steps and fractions of a step.
*/
public static var currentStepTime(default, null):Float;
public static var beatHit(default, null):FlxSignal = new FlxSignal();
public static var stepHit(default, null):FlxSignal = new FlxSignal();
public static var lastSongPos:Float;
public static var visualOffset:Float = 0;
public static var audioOffset:Float = 0;
public static var offset:Float = 0; public static var offset:Float = 0;
// TODO: Add code to update this. // TODO: What's the difference between visualOffset and audioOffset?
public static var visualOffset:Float = 0;
public static var audioOffset:Float = 0;
//
// Signals
//
/**
* Signal that is dispatched every measure.
* At 120 BPM 4/4, this is dispatched every 2 seconds.
* At 120 BPM 3/4, this is dispatched every 1.5 seconds.
*/
public static var measureHit(default, null):FlxSignal = new FlxSignal();
/**
* Signal that is dispatched every beat.
* At 120 BPM 4/4, this is dispatched every 0.5 seconds.
* At 120 BPM 3/4, this is dispatched every 0.5 seconds.
*/
public static var beatHit(default, null):FlxSignal = new FlxSignal();
/**
* Signal that is dispatched when a step is hit.
* At 120 BPM 4/4, this is dispatched every 0.125 seconds.
* At 120 BPM 3/4, this is dispatched every 0.125 seconds.
*/
public static var stepHit(default, null):FlxSignal = new FlxSignal();
//
// Internal Variables
//
/**
* 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.
*/
static var timeChanges:Array<SongTimeChange> = [];
/**
* The current time change.
*/
static var currentTimeChange:SongTimeChange;
public static var lastSongPos:Float;
/**
* The number of beats (whole notes) in a measure.
*/
public static var beatsPerMeasure(get, null):Int; public static var beatsPerMeasure(get, null):Int;
static function get_beatsPerMeasure():Int static function get_beatsPerMeasure():Int
@ -129,33 +188,17 @@ class Conductor
return timeSignatureNumerator; return timeSignatureNumerator;
} }
/**
* The number of steps (quarter-notes) in a measure.
*/
public static var stepsPerMeasure(get, null):Int; public static var stepsPerMeasure(get, null):Int;
static function get_stepsPerMeasure():Int static function get_stepsPerMeasure():Int
{ {
// Is this always x4? // This is always 4, b
return timeSignatureNumerator * 4; return timeSignatureNumerator * 4;
} }
function new() {}
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;
}
/** /**
* Forcibly defines the current BPM of the song. * Forcibly defines the current BPM of the song.
* Useful for things like the chart editor that need to manipulate BPM in real time. * Useful for things like the chart editor that need to manipulate BPM in real time.
@ -165,11 +208,16 @@ class Conductor
* WARNING: Avoid this for things like setting the BPM of the title screen music, * WARNING: Avoid this for things like setting the BPM of the title screen music,
* you should have a metadata file for it instead. * you should have a metadata file for it instead.
*/ */
public static function forceBPM(?bpm:Float = null) public static function forceBPM(?bpm:Float = null):Void
{ {
if (bpm != null) trace('[CONDUCTOR] Forcing BPM to ' + bpm); if (bpm != null)
{
trace('[CONDUCTOR] Forcing BPM to ' + bpm);
}
else else
{
trace('[CONDUCTOR] Resetting BPM to default'); trace('[CONDUCTOR] Resetting BPM to default');
}
Conductor.bpmOverride = bpm; Conductor.bpmOverride = bpm;
} }
@ -180,15 +228,15 @@ class Conductor
* @param songPosition The current position in the song in milliseconds. * @param songPosition The current position in the song in milliseconds.
* Leave blank to use the FlxG.sound.music position. * Leave blank to use the FlxG.sound.music position.
*/ */
public static function update(songPosition:Float = null) public static function update(songPosition:Float = null):Void
{ {
if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0; if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0;
var oldBeat = currentBeat; var oldMeasure:Int = currentMeasure;
var oldStep = currentStep; var oldBeat:Int = currentBeat;
var oldStep:Int = currentStep;
Conductor.songPosition = songPosition; Conductor.songPosition = songPosition;
// Conductor.bpm = Conductor.getLastBPMChange().bpm;
currentTimeChange = timeChanges[0]; currentTimeChange = timeChanges[0];
for (i in 0...timeChanges.length) for (i in 0...timeChanges.length)
@ -204,14 +252,14 @@ class Conductor
} }
else if (currentTimeChange != null) else if (currentTimeChange != null)
{ {
currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepCrochet; currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepLengthMs;
currentStep = Math.floor(currentStepTime); currentStep = Math.floor(currentStepTime);
currentBeat = Math.floor(currentStep / 4); currentBeat = Math.floor(currentStep / 4);
} }
else else
{ {
// Assume a constant BPM equal to the forced value. // Assume a constant BPM equal to the forced value.
currentStepTime = (songPosition / stepCrochet); currentStepTime = (songPosition / stepLengthMs);
currentStep = Math.floor(currentStepTime); currentStep = Math.floor(currentStepTime);
currentBeat = Math.floor(currentStep / 4); currentBeat = Math.floor(currentStep / 4);
} }
@ -226,37 +274,14 @@ class Conductor
{ {
beatHit.dispatch(); beatHit.dispatch();
} }
}
@:deprecated // Switch to TimeChanges instead. if (currentMeasure != oldMeasure)
public static function mapBPMChanges(song:SwagSong)
{ {
bpmChangeMap = []; measureHit.dispatch();
var curBPM:Float = song.bpm;
var totalSteps:Int = 0;
var totalPos:Float = 0;
for (i in 0...SongLoad.getSong().length)
{
if (SongLoad.getSong()[i].changeBPM && SongLoad.getSong()[i].bpm != curBPM)
{
curBPM = SongLoad.getSong()[i].bpm;
var event:BPMChangeEvent =
{
stepTime: totalSteps,
songTime: totalPos,
bpm: curBPM
};
bpmChangeMap.push(event);
}
var deltaSteps:Int = SongLoad.getSong()[i].lengthInSteps;
totalSteps += deltaSteps;
totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps;
} }
} }
public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>) public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>):Void
{ {
timeChanges = []; timeChanges = [];
@ -278,7 +303,7 @@ class Conductor
if (timeChanges.length == 0) if (timeChanges.length == 0)
{ {
// Assume a constant BPM equal to the forced value. // Assume a constant BPM equal to the forced value.
return Math.floor(ms / stepCrochet); return Math.floor(ms / stepLengthMs);
} }
else else
{ {
@ -299,7 +324,7 @@ class Conductor
} }
} }
resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepCrochet); resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepLengthMs);
return resultStep; return resultStep;
} }