Skip to content

Commit

Permalink
Merge pull request #12 from kionell/uncapped-bpm
Browse files Browse the repository at this point in the history
Add unlimited variants of beat length properties
  • Loading branch information
kionell authored Apr 26, 2024
2 parents 75113fa + 8bfc8ae commit 5f7f300
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 33 deletions.
75 changes: 54 additions & 21 deletions src/Beatmaps/Beatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from './Sections';

import { IBeatmap } from './IBeatmap';
import { ControlPointInfo } from './ControlPoints';
import { ControlPointInfo, TimingPoint } from './ControlPoints';
import { HitObject, HitType, IHasDuration } from '../Objects';
import { RoundHelper } from '../Utils';

Expand Down Expand Up @@ -118,40 +118,59 @@ export class Beatmap implements IBeatmap {
return endTime / this.difficulty.clockRate;
}

private _getLongestBeatLength(unlimited: boolean): number {
const longestBeat = this.controlPoints.timingPoints.reduce((b, t) => {
const beatLength = unlimited ? t.beatLengthUnlimited : t.beatLength;

return beatLength >= 0 ? Math.max(beatLength, b) : b;
}, -Infinity);

return isFinite(longestBeat) ? longestBeat : TimingPoint.DEFAULT_BEAT_LENGTH;
}

/**
* Minimal BPM of a beatmap.
*/
get bpmMin(): number {
const longestBeat = this.controlPoints.timingPoints
.reduce((b, t) =>
t.beatLength >= 0 ? Math.max(t.beatLength, b) : b, -Infinity,
);
return 60000 / this._getLongestBeatLength(false) * this.difficulty.clockRate;
}

if (!isFinite(longestBeat)) return 60;
/**
* Minimal BPM of a beatmap without any limits as it was in osu!stable.
* Usage of {@link bpmMin} is preferable when working with osu!lazer.
*/
get bpmMinUnlimited(): number {
return 60000 / this._getLongestBeatLength(true) * this.difficulty.clockRate;
}

private _getShortestBeatLength(unlimited: boolean): number {
const shortestBeat = this.controlPoints.timingPoints.reduce((b, t) => {
const beatLength = unlimited ? t.beatLengthUnlimited : t.beatLength;

return beatLength >= 0 ? Math.min(beatLength, b) : b;
}, Infinity);

return (60000 / longestBeat) * this.difficulty.clockRate;
return isFinite(shortestBeat) ? shortestBeat : TimingPoint.DEFAULT_BEAT_LENGTH;
}

/**
* Maximal BPM of a beatmap.
*/
get bpmMax(): number {
const shortestBeat = this.controlPoints.timingPoints
.reduce((b, t) =>
t.beatLength >= 0 ? Math.min(t.beatLength, b) : b, Infinity,
);

if (!isFinite(shortestBeat)) return 60;

return (60000 / shortestBeat) * this.difficulty.clockRate;
return 60000 / this._getShortestBeatLength(false) * this.difficulty.clockRate;
}

/**
* The most common BPM of a beatmap.
* Maximal BPM of a beatmap without any limits as it was in osu!stable.
* Usage of {@link bpmMax} is preferable when working with osu!lazer.
*/
get bpm(): number {
get bpmMaxUnlimited(): number {
return 60000 / this._getShortestBeatLength(true) * this.difficulty.clockRate;
}

private _getMostCommonBeatLength(unlimited: boolean): number {
if (!this.controlPoints.timingPoints.length) {
return this.bpmMax;
return TimingPoint.DEFAULT_BEAT_LENGTH;
}

const timingPoints = this.controlPoints.timingPoints;
Expand All @@ -170,7 +189,8 @@ export class Beatmap implements IBeatmap {
const groups = new Map<number, number>();

timingPoints.forEach((t, i) => {
const nextBeat = RoundHelper.round(t.beatLength * 1000) / 1000;
const beatLength = unlimited ? t.beatLengthUnlimited : t.beatLength;
const nextBeat = RoundHelper.round(beatLength * 1000) / 1000;

if (!groups.has(nextBeat)) {
groups.set(nextBeat, 0);
Expand All @@ -194,9 +214,22 @@ export class Beatmap implements IBeatmap {

if (groups.size === 0) return this.bpmMax;

const mostCommon = [...groups.entries()].sort((a, b) => b[1] - a[1])[0];
return [...groups.entries()].sort((a, b) => b[1] - a[1])[0][0];
}

/**
* The most common BPM of a beatmap.
*/
get bpm(): number {
return 60000 / this._getMostCommonBeatLength(false) * this.difficulty.clockRate;
}

return 60000 / Number(mostCommon[0]) * this.difficulty.clockRate;
/**
* The most common BPM of a beatmap without any limits as it was in osu!stable.
* Usage of {@link bpm} is preferable when working with osu!lazer.
*/
get bpmUnlimited(): number {
return 60000 / this._getMostCommonBeatLength(true) * this.difficulty.clockRate;
}

/**
Expand Down
12 changes: 8 additions & 4 deletions src/Beatmaps/ControlPoints/DifficultyPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ export class DifficultyPoint extends ControlPoint {
*/
isLegacy = false;

private _sliderVelocity = 1;
/**
* The real slider velocity of this difficulty point
* without any limits as it was in osu!stable.
* Usage of {@link sliderVelocity} is preferable when working with osu!lazer.
*/
sliderVelocityUnlimited = 1;

/**
* The slider velocity at this difficulty point.
*/
get sliderVelocity(): number {
// Imitate bindable value with range [0.1, 10].
return clamp(this._sliderVelocity, 0.1, 10);
return clamp(this.sliderVelocityUnlimited, 0.1, 10);
}

set sliderVelocity(value: number) {
this._sliderVelocity = value;
this.sliderVelocityUnlimited = value;
}

/**
Expand Down
13 changes: 9 additions & 4 deletions src/Beatmaps/ControlPoints/EffectPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ export class EffectPoint extends ControlPoint {
omitFirstBarLine = false;

/**
* The relative scroll speed at this control point.
* The real scroll speed of this effect point
* without any limits as it was in osu!stable.
* Usage of {@link scrollSpeed} is preferable when working with osu!lazer.
*/
private _scrollSpeed = 1;
scrollSpeedUnlimited = 1;

/**
* The relative scroll speed at this control point.
*/
get scrollSpeed(): number {
return clamp(this._scrollSpeed, 0.1, 10);
return clamp(this.scrollSpeedUnlimited, 0.1, 10);
}

set scrollSpeed(value: number) {
this._scrollSpeed = value;
this.scrollSpeedUnlimited = value;
}

/**
Expand Down
28 changes: 24 additions & 4 deletions src/Beatmaps/ControlPoints/TimingPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,49 @@ export class TimingPoint extends ControlPoint {
*/
static default: TimingPoint = new TimingPoint();

/**
* Default length of a beat in milliseconds.
* Used whenever there is no beatmap or track playing.
*/
static DEFAULT_BEAT_LENGTH = 1000;

/**
* The type of a timing point.
*/
pointType: ControlPointType = ControlPointType.TimingPoint;

/**
* The beat length of this timing point.
* The real beat length of this timing point
* without any limits as it was in osu!stable.
* Usage of {@link beatLength} is preferable when working with osu!lazer.
*/
private _beatLength = 1000;
beatLengthUnlimited = TimingPoint.DEFAULT_BEAT_LENGTH;

/**
* The beat length of this timing point.
*/
get beatLength(): number {
return clamp(this._beatLength, 6, 60000);
return clamp(this.beatLengthUnlimited, 6, 60000);
}

set beatLength(value: number) {
this._beatLength = value;
this.beatLengthUnlimited = value;
}

/**
* The time signature of this timing point.
*/
timeSignature: TimeSignature = TimeSignature.SimpleQuadruple;

/**
* The real BPM of this timing point
* without any limits as it was in osu!stable.
* Usage of {@link bpm} is preferable when working with osu!lazer.
*/
get bpmUnlimited(): number {
return 60000 / this.beatLengthUnlimited;
}

/**
* The BPM of this timing point.
*/
Expand Down

0 comments on commit 5f7f300

Please sign in to comment.