Continuous Tempo Change

This commit is contained in:
Ralty
2024-10-23 23:19:22 +07:00
parent 2cde5bf8fc
commit 3d99cd1985
2 changed files with 339 additions and 210 deletions
+338 -209
View File
@@ -1,17 +1,24 @@
package funkin.backend.system;
import funkin.backend.chart.ChartData;
import flixel.FlxState;
import funkin.backend.system.interfaces.IBeatReceiver;
import flixel.util.FlxSignal.FlxTypedSignal;
import funkin.backend.chart.ChartData;
import funkin.backend.system.interfaces.IBeatReceiver;
@:structInit
class BPMChangeEvent {
public var stepTime:Float;
class BPMChangeEvent
{
public var songTime:Float;
public var bpm:Float;
public var beatsPerMeasure:Float;
public var stepsPerBeat:Int;
public var beatsPerMeasure:Float = 4;
public var stepsPerBeat:Int = 4;
public var endSongTime:Float = 0;
public var continuous:Bool = false;
public var stepTime:Float;
public var beatTime:Float;
public var measureTime:Float;
}
final class Conductor
@@ -22,41 +29,15 @@ final class Conductor
public static var onMeasureHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal();
public static var onBeatHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal();
public static var onStepHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal();
public static var onBPMChange:FlxTypedSignal<Float->Void> = new FlxTypedSignal();
public static var onBPMChange:FlxTypedSignal<(Float,Float)->Void> = new FlxTypedSignal();
public static var onTimeSignatureChange:FlxTypedSignal<(Float,Float)->Void> = new FlxTypedSignal();
/**
* Current BPM
*/
public static var bpm:Float = 100;
/**
* Current Crochet (time per beat), in milliseconds.
*/
public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds
/**
* Current StepCrochet (time per step), in milliseconds.
*/
public static var stepCrochet:Float = crochet / 4; // steps in milliseconds
/**
* Number of beats per mesure (top number in time signature). Defaults to 4.
*/
public static var beatsPerMeasure:Float = 4;
/**
* Number of steps per beat (bottom number in time signature). Defaults to 4.
*/
public static var stepsPerBeat:Int = 4;
/**
* Current position of the song, in milliseconds.
*/
public static var songPosition(get, default):Float;
private static function get_songPosition() {
if (songOffset != Options.songOffset) trace(songOffset = Options.songOffset);
if (songOffset != Options.songOffset) songOffset = Options.songOffset;
return songPosition - songOffset;
}
@@ -65,6 +46,60 @@ final class Conductor
*/
public static var songOffset:Float = 0;
/**
* Current bpmChangeMap index
*/
public static var curChangeIndex:Int = 0;
/**
* Current bpmChangeMap
*/
public static var curChange(get, never):Null<BPMChangeEvent>;
private static function get_curChange()
return bpmChangeMap[curChangeIndex];
/**
* Current BPM
*/
public static var bpm(get, never):Float;
private static function get_bpm()
return curChangeIndex == 0 ? startingBPM : getTimeWithIndexInBPM(songPosition, curChangeIndex);
/**
* Starting BPM
*/
public static var startingBPM(get, never):Float;
private static function get_startingBPM()
return bpmChangeMap.length == 0 ? 100 : bpmChangeMap[0].bpm;
/**
* Current Crochet (time per beat), in milliseconds.
* It should be crotchet but ehhh, now it's there for backward compatibility.
*/
public static var crochet(get, never):Float;
private static function get_crochet() return 60000 / bpm;
/**
* Current StepCrochet (time per step), in milliseconds.
*/
public static var stepCrochet(get, never):Float;
private static function get_stepCrochet() return crochet / stepsPerBeat;
/**
* Number of beats per mesure (top number in time signature). Defaults to 4.
*/
public static var beatsPerMeasure(get, never):Float;
private static function get_beatsPerMeasure()
return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].beatsPerMeasure;
/**
* Number of steps per beat (bottom number in time signature). Defaults to 4.
*/
public static var stepsPerBeat(get, never):Float;
private static function get_stepsPerBeat()
return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].stepsPerBeat;
/**
* Current step
*/
@@ -80,7 +115,6 @@ final class Conductor
*/
public static var curMeasure:Int = 0;
/**
* Current step, as a `Float` (ex: 4.94, instead of 4)
*/
@@ -103,89 +137,97 @@ final class Conductor
/**
* Array of all BPM changes that have been mapped.
*/
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
public static var bpmChangeMap:Array<BPMChangeEvent>;
@:dox(hide) public function new() {}
public static function reset() {
songPosition = lastSongPos = curBeatFloat = curStepFloat = curBeat = curStep = 0;
bpmChangeMap = [];
changeBPM(0, 4, 4);
changeBPM();
}
public static function changeBPM(bpm:Float = 100, beatsPerMeasure:Float = 4, stepsPerBeat:Int = 4)
bpmChangeMap = [{bpm: bpm, beatsPerMeasure: beatsPerMeasure, stepsPerBeat: stepsPerBeat, songTime: 0, stepTime: 0, beatTime: 0, measureTime: 0}];
public static function setupSong(SONG:ChartData) {
reset();
mapBPMChanges(SONG);
changeBPM(SONG.meta.bpm, cast SONG.meta.beatsPerMeasure.getDefault(4), cast SONG.meta.stepsPerBeat.getDefault(4));
}
private static function mapBPMChange(curChange:BPMChangeEvent, time:Float, bpm:Float, ?endTime:Null<Float>):BPMChangeEvent {
if (bpm == curChange.bpm) return curChange;
var beatTime:Float, measureTime:Float, stepTime:Float;
if (curChange.continuous) {
beatTime = curChange.beatTime + (curChange.endSongTime - curChange.songTime) * (bpm - curChange.bpm) / Math.log(bpm / curChange.bpm) / 60000 +
(time - curChange.endSongTime) / (60000 / curChange.bpm);
measureTime = curChange.measureTime + (beatTime - curChange.beatTime) / beatsPerMeasure;
stepTime = curChange.stepTime + (beatTime - curChange.beatTime) * stepsPerBeat;
}
else {
beatTime = curChange.beatTime + (time - curChange.songTime) / (60000 / curChange.bpm);
measureTime = curChange.measureTime + (beatTime - curChange.beatTime) / beatsPerMeasure;
stepTime = curChange.stepTime + (beatTime - curChange.beatTime) * stepsPerBeat;
}
bpmChangeMap.push(curChange = {
songTime: time,
stepTime: stepTime,
beatTime: beatTime,
measureTime: measureTime,
bpm: bpm,
continuous: endTime is Float,
beatsPerMeasure: curChange.beatsPerMeasure,
stepsPerBeat: curChange.stepsPerBeat
});
if (curChange.continuous) curChange.endSongTime = endTime;
return curChange;
}
/**
* Maps BPM changes from a song.
* @param song Song to map BPM changes from.
*/
public static function mapBPMChanges(song:ChartData) {
bpmChangeMap = [
{
stepTime: 0,
songTime: 0,
bpm: song.meta.bpm,
beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4),
stepsPerBeat: song.meta.stepsPerBeat.getDefault(4)
}
];
var curChange:BPMChangeEvent = {
songTime: 0,
stepTime: 0,
beatTime: 0,
measureTime: 0,
bpm: song.meta.bpm,
beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4),
stepsPerBeat: CoolUtil.floorInt(song.meta.stepsPerBeat.getDefault(4))
};
bpmChangeMap = [curChange];
if (song.events == null) return;
var curBPM:Float = song.meta.bpm;
var curBeatsPerMeasure:Float = song.meta.beatsPerMeasure.getDefault(4);
var curStepsPerBeat:Int = song.meta.stepsPerBeat.getDefault(4);
var songTime:Float = 0;
var stepTime:Float = 0;
// fix the sort first...
var events:Array<ChartEvent> = [];
for (e in song.events) if (e.params != null && (e.name == "BPM Change" || e.name == "Time Signature Change")) events.push(e);
events.sort(function(a, b) return Std.int(a.time - b.time));
for(e in song.events) {
var name = e.name;
var params = e.params;
var eventTime = e.time;
if(params == null) continue;
for (e in events) {
var name = e.name, params = e.params, time = e.time;
if (name == "BPM Change" && params[0] is Float)
curChange = mapBPMChange(curChange, time, params[0], params[1]);
else if (name == "Time Signature Change") {
var beatsPerMeasure = params[0], stepsPerBeat = params[1];
//if (beatsPerMeasure == curChange.beatsPerMeasure && stepsPerBeat == curChange.stepsPerBeat) continue;
/* TODO: make so time sigs doesnt stop the bpm change if its in the duration of bpm change */
if (name == "BPM Change" && params[0] is Float) {
if (params[0] == curBPM) continue;
var steps = (eventTime - songTime) / ((60 / curBPM) * 1000 / stepsPerBeat);
stepTime += steps;
songTime = eventTime;
curBPM = params[0];
bpmChangeMap.push({
stepTime: stepTime,
songTime: songTime,
bpm: curBPM,
beatsPerMeasure: curBeatsPerMeasure, // keep old beatsPerMeasure and stepsPerMeasure so shit doesnt break
stepsPerBeat: curStepsPerBeat
});
} else if (name == "Time Signature Change") {
var newBeatsPerMeasure = params[0];
var newStepsPerBeat = params[1];
if (newBeatsPerMeasure == curBeatsPerMeasure && newStepsPerBeat == curStepsPerBeat) continue;
var steps = (eventTime - songTime) / ((60 / curBPM) * 1000 / stepsPerBeat);
stepTime += steps;
songTime = eventTime;
curBeatsPerMeasure = newBeatsPerMeasure;
curStepsPerBeat = newStepsPerBeat;
bpmChangeMap.push({
stepTime: stepTime,
songTime: songTime,
bpm: curBPM, // keep old bpm so shit doesnt break
beatsPerMeasure: curBeatsPerMeasure,
stepsPerBeat: curStepsPerBeat
});
if (curChange.songTime == time) {
curChange.beatsPerMeasure = beatsPerMeasure;
curChange.stepsPerBeat = stepsPerBeat;
}
else
curChange = mapBPMChange(curChange, time, curChange.bpm);
curChange.stepTime = CoolUtil.floorInt(curChange.stepTime + .99998);
curChange.beatTime = CoolUtil.floorInt(curChange.beatTime + .99998);
curChange.measureTime = CoolUtil.floorInt(curChange.measureTime + .99998);
}
}
// sort from early to last
bpmChangeMap.sort(function(a, b) return Std.int(a.songTime - b.songTime));
}
private static var elapsed:Float;
@@ -223,144 +265,231 @@ final class Conductor
__updateSongPos(FlxG.elapsed);
if (bpm > 0) {
// Check for BPM change
__lastChange = {
stepTime: 0,
songTime: 0,
bpm: 0,
beatsPerMeasure: beatsPerMeasure,
stepsPerBeat: stepsPerBeat
};
var oldStep = curStep, oldBeat = curBeat, oldMeasure = curMeasure, oldChangeIndex = curChangeIndex;
var currentPos = Conductor.songPosition;
for (change in Conductor.bpmChangeMap) {
if (currentPos >= change.songTime)
__lastChange = change;
else
break;
}
// Change BPM if necessary and check for time signature change
if ((__lastChange.bpm > 0 && bpm != __lastChange.bpm) || (__lastChange.beatsPerMeasure != beatsPerMeasure || __lastChange.stepsPerBeat != stepsPerBeat)) {
changeBPM(__lastChange.bpm, __lastChange.beatsPerMeasure, __lastChange.stepsPerBeat);
}
curStepFloat = __lastChange.stepTime + ((currentPos - __lastChange.songTime) / Conductor.stepCrochet);
curBeatFloat = curStepFloat / stepsPerBeat;
if ((curChangeIndex = getTimeInChangeIndex(songPosition, curChangeIndex)) > 0) {
var change = curChange;
curBeatFloat = getTimeWithBPMInBeats(songPosition, curChangeIndex, getTimeWithIndexInBPM(songPosition, curChangeIndex));
curMeasureFloat = change.measureTime + (curBeatFloat - change.beatTime) / beatsPerMeasure;
curStepFloat = change.stepTime + (curBeatFloat - change.beatTime) * stepsPerBeat;
}
else {
curBeatFloat = songPosition / (60000 / bpm);
curMeasureFloat = curBeatFloat / beatsPerMeasure;
curStepFloat = curBeatFloat * stepsPerBeat;
}
var oldStep = curStep;
var oldBeat = curBeat;
var oldMeasure = curMeasure;
if (curStep != (curStep = CoolUtil.floorInt(curStepFloat))) {
if (curStep < oldStep && oldStep - curStep < 2) return;
// updates step
__updateBeat = curBeat != (curBeat = CoolUtil.floorInt(curBeatFloat));
__updateMeasure = __updateBeat && (curMeasure != (curMeasure = CoolUtil.floorInt(curMeasureFloat)));
if (curChangeIndex != oldChangeIndex) {
var prev = bpmChangeMap[oldChangeIndex];
if (beatsPerMeasure != prev.beatsPerMeasure || stepsPerBeat != prev.stepsPerBeat)
onTimeSignatureChange.dispatch(beatsPerMeasure, stepsPerBeat);
if (curStep > oldStep) {
for(i in oldStep...curStep) {
onStepHit.dispatch(i+1);
}
}
if (__updateBeat && curBeat > oldBeat) {
for(i in oldBeat...curBeat) {
onBeatHit.dispatch(i+1);
}
}
if (__updateMeasure && curMeasure > oldMeasure) {
for(i in oldMeasure...curMeasure) {
onMeasureHit.dispatch(i+1);
}
}
if (curChange.bpm != prev.bpm) onBPMChange.dispatch(curChange.bpm, curChange.endSongTime);
}
if (FlxG.state is IBeatReceiver) {
var state = FlxG.state;
while(state != null) {
if (state is IBeatReceiver && (state.subState == null || state.persistentUpdate)) {
var st = cast(state, IBeatReceiver);
if (curStep > oldStep) {
for(i in oldStep...curStep) {
st.stepHit(i+1);
}
if (curStep != (curStep = CoolUtil.floorInt(curStepFloat))) {
if (curStep < oldStep && oldStep - curStep < 2) return;
// updates step
__updateBeat = curBeat != (curBeat = CoolUtil.floorInt(curBeatFloat));
__updateMeasure = __updateBeat && (curMeasure != (curMeasure = CoolUtil.floorInt(curMeasureFloat)));
if (curStep > oldStep) {
for(i in oldStep...curStep) {
onStepHit.dispatch(i+1);
}
}
if (__updateBeat && curBeat > oldBeat) {
for(i in oldBeat...curBeat) {
onBeatHit.dispatch(i+1);
}
}
if (__updateMeasure && curMeasure > oldMeasure) {
for(i in oldMeasure...curMeasure) {
onMeasureHit.dispatch(i+1);
}
}
if (FlxG.state is IBeatReceiver) {
var state = FlxG.state;
while(state != null) {
if (state is IBeatReceiver && (state.subState == null || state.persistentUpdate)) {
var st = cast(state, IBeatReceiver);
if (curStep > oldStep) {
for(i in oldStep...curStep) {
st.stepHit(i+1);
}
if (__updateBeat && curBeat > oldBeat) {
for(i in oldBeat...curBeat) {
st.beatHit(i+1);
}
}
if (__updateBeat && curBeat > oldBeat) {
for(i in oldBeat...curBeat) {
st.beatHit(i+1);
}
if (__updateMeasure && curMeasure > oldMeasure) {
for(i in oldMeasure...curMeasure) {
st.measureHit(i+1);
}
}
if (__updateMeasure && curMeasure > oldMeasure) {
for(i in oldMeasure...curMeasure) {
st.measureHit(i+1);
}
}
state = state.subState;
}
state = state.subState;
}
}
}
}
public static function changeBPM(newBpm:Float, newBeatsPerMeasure:Float = 4, newStepsPerBeat:Int = 4)
{
var timesignChange = (beatsPerMeasure != newBeatsPerMeasure || stepsPerBeat != newStepsPerBeat);
var bpmChange = (bpm != newBpm);
beatsPerMeasure = newBeatsPerMeasure;
stepsPerBeat = newStepsPerBeat;
bpm = newBpm;
crochet = (60 / bpm) * 1000;
stepCrochet = crochet / stepsPerBeat;
if (timesignChange) onTimeSignatureChange.dispatch(beatsPerMeasure, stepsPerBeat);
if (bpmChange) onBPMChange.dispatch(bpm);
public static function getTimeInChangeIndex(time:Float, index:Int = 0):Int {
if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1;
else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].songTime > time) {
while (--index >= 0) if (time > bpmChangeMap[index].songTime) return index;
return 0;
}
else {
for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].songTime > time) return i;
return bpmChangeMap.length - 1;
}
}
public static function getTimeForStep(step:Float) {
var bpmChange:BPMChangeEvent = {
stepTime: 0,
songTime: 0,
bpm: bpm,
beatsPerMeasure: beatsPerMeasure,
stepsPerBeat: stepsPerBeat
};
for(change in bpmChangeMap)
if (change.stepTime < step && change.stepTime >= bpmChange.stepTime)
bpmChange = change;
// possible break here
return bpmChange.songTime + ((step - bpmChange.stepTime) * ((60 / bpmChange.bpm) * (1000 / bpmChange.stepsPerBeat)));
public static function getStepsInChangeIndex(stepTime:Float, index:Int = 0):Int {
if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1;
else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].stepTime > stepTime) {
while (--index >= 0) if (stepTime > bpmChangeMap[index].stepTime) return index;
return 0;
}
else {
for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].stepTime > stepTime) return i;
return bpmChangeMap.length - 1;
}
}
public static function getStepForTime(time:Float) {
var bpmChange:BPMChangeEvent = {
stepTime: 0,
songTime: 0,
bpm: bpm,
beatsPerMeasure: beatsPerMeasure,
stepsPerBeat: stepsPerBeat
};
for(change in bpmChangeMap)
if (change.songTime < time && change.songTime >= bpmChange.songTime)
bpmChange = change;
// possible break here
return bpmChange.stepTime + ((time - bpmChange.songTime) / ((60 / bpmChange.bpm) * (1000 / bpmChange.stepsPerBeat)));
public static function getBeatsInChangeIndex(beatTime:Float, index:Int = 0):Int {
if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1;
else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].beatTime > beatTime) {
while (--index >= 0) if (beatTime > bpmChangeMap[index].beatTime) return index;
return 0;
}
else {
for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].beatTime > beatTime) return i;
return bpmChangeMap.length - 1;
}
}
public static function getMeasuresInChangeIndex(measureTime:Float, index:Int = 0):Int {
if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1;
else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].measureTime > measureTime) {
while (--index >= 0) if (measureTime > bpmChangeMap[index].measureTime) return index;
return 0;
}
else {
for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].measureTime > measureTime) return i;
return bpmChangeMap.length - 1;
}
}
public static function getTimeWithIndexInBPM(time:Float, index:Int):Float {
var bpmChange = bpmChangeMap[index];
if (bpmChange.continuous && time < bpmChange.endSongTime && index > 0) {
var prevBPM = bpmChangeMap[index].bpm;
if (time <= bpmChange.songTime) return prevBPM;
var ratio = (time - bpmChange.songTime) / (bpmChange.endSongTime - bpmChange.songTime);
return Math.pow(prevBPM, 1 - ratio) * Math.pow(bpmChange.bpm, ratio);
}
return bpmChange.bpm;
}
public static function getBeatsWithIndexInBPM(beatTime:Float, index:Int):Float {
var bpmChange = bpmChangeMap[index];
if (bpmChange.continuous && index > 0) {
var prevBPM = bpmChangeMap[index].bpm;
if (beatTime <= bpmChange.beatTime) return prevBPM;
var endBeatTime = bpmChange.beatTime + (bpmChange.endSongTime - bpmChange.songTime) * (bpmChange.bpm - prevBPM) / Math.log(bpmChange.bpm / prevBPM) / 60000;
if (beatTime < endBeatTime) return FlxMath.remapToRange(beatTime, bpmChange.beatTime, endBeatTime, prevBPM, bpmChange.bpm);
}
return bpmChange.bpm;
}
public static function getTimeInBPM(time:Float):Float {
if (bpmChangeMap.length == 0) return 100;
return getTimeWithIndexInBPM(time, getTimeInChangeIndex(time));
}
public static function getTimeWithBPMInBeats(time:Float, index:Int, bpm:Float):Float {
var bpmChange = bpmChangeMap[index];
if (bpmChange.continuous && time > bpmChange.songTime && index > 0) {
var prevBPM = bpmChangeMap[index].bpm;
if (time > bpmChange.endSongTime)
return bpmChange.beatTime + (bpmChange.endSongTime - bpmChange.songTime) * (bpm - prevBPM) / Math.log(bpm / prevBPM) / 60000 +
(time - bpmChange.endSongTime) / (60000 / bpm);
else
return bpmChange.beatTime + (time - bpmChange.songTime) * (bpm - prevBPM) / Math.log(bpm / prevBPM) / 60000;
}
else {
return bpmChange.beatTime + (time - bpmChange.songTime) / (60000 / bpm);
}
}
public static function getTimeInSteps(time:Float):Float {
var index = getTimeInChangeIndex(time);
if (index == -1) return time / (15000 / 100);
else if (index == 0) return time / (60000 / getTimeInBPM(time)) * bpmChangeMap[index].stepsPerBeat;
else {
var change = bpmChangeMap[index];
return change.stepTime + (getTimeWithBPMInBeats(time, index, getTimeWithIndexInBPM(time, index)) - change.beatTime) * change.stepsPerBeat;
}
}
@:haxe.warning("-WDeprecated")
public static inline function getStepForTime(time:Float):Float return getTimeInSteps(time);
public static function getTimeInBeats(time:Float):Float {
var index = getTimeInChangeIndex(time);
return index < 1 ? time / (60000 / getTimeInBPM(time)) : getTimeWithBPMInBeats(time, index, getTimeWithIndexInBPM(time, index));
}
public static function getBeatsWithBPMInTime(beatTime:Float, index:Int, bpm:Float):Float {
var bpmChange = bpmChangeMap[index];
if (bpmChange.continuous && beatTime > bpmChange.beatTime && index > 0) {
var prevBPM = bpmChangeMap[index].bpm;
var time = bpmChange.songTime + (beatTime - bpmChange.beatTime) / (bpm - prevBPM) * Math.log(bpm / prevBPM) * 60000;
if (time > bpmChange.endSongTime)
return bpmChange.endSongTime + (beatTime - (
bpmChange.beatTime + (bpmChange.endSongTime - bpmChange.songTime) * (bpm - prevBPM) / Math.log(bpm / prevBPM) / 60000
)) * (60000 / bpm);
else
return time;
}
else {
return bpmChange.songTime + (beatTime - bpmChange.beatTime) * (60000 / bpm);
}
}
public static function getStepsInTime(stepTime:Float):Float {
var index = getStepsInChangeIndex(stepTime);
if (index == -1) return stepTime * (15000 / 100);
else if (index == 0) return stepTime * (60000 / getTimeInBPM(0)) / bpmChangeMap[index].stepsPerBeat;
else {
var change = bpmChangeMap[index];
var beatTime = change.beatTime + (stepTime - change.stepTime) / change.stepsPerBeat;
return getBeatsWithBPMInTime(beatTime, index, getBeatsWithIndexInBPM(beatTime, index));
}
}
@:haxe.warning("-WDeprecated")
public static inline function getTimeForStep(steps:Float):Float return getStepsInTime(steps);
public static function getBeatsInTime(beatTime:Float):Float {
var index = getBeatsInChangeIndex(beatTime);
return index < 1 ? beatTime * (60000 / getTimeInBPM(0)) : getBeatsWithBPMInTime(beatTime, index, getBeatsWithIndexInBPM(beatTime, index));
}
public static inline function getMeasureLength()
return stepsPerBeat * beatsPerMeasure;
public static inline function getMeasuresLength() {
if (FlxG.sound.music == null) return 0.0;
return getStepForTime(FlxG.sound.music.length) / getMeasureLength();
var length = FlxG.sound.music.length;
var index = getTimeInChangeIndex(length, bpmChangeMap.length - 1);
var change = bpmChangeMap[index];
return change.measureTime + (getTimeWithBPMInBeats(length, index, getTimeWithIndexInBPM(length, index)) - change.beatTime) / change.beatsPerMeasure;
}
}
}
+1 -1
View File
@@ -403,7 +403,7 @@ class CoolUtil
}
var bpm:Null<Float> = Std.parseFloat(musicInfo["BPM"]).getDefault(DefaultBPM);
Conductor.changeBPM(bpm, beatsPerMeasure, stepsPerBeat);
Conductor.changeBPM(bpm, beatsPerMeasure, floorInt(stepsPerBeat));
} else
Conductor.changeBPM(DefaultBPM);
}