Skip to content

Commit 148cd1a

Browse files
authored
Merge pull request #579 from GalaxyMaster2/bpm-event-refactor
BPM event refactor
2 parents 6c092c7 + ae2880b commit 148cd1a

37 files changed

+317
-283
lines changed

Assets/TestsEditMode/BeatmapBpmInfoTest.cs

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,16 @@ public void GetFromJson_V2BpmInfo()
8282
[Test]
8383
public void GetFromJson_V4AudioData()
8484
{
85-
var bpmInfo = V4AudioData.GetFromJson(JSON.Parse(audioDataJson));
85+
var bpmInfo = V4AudioData.GetFromJson(JSON.Parse(audioDataJson));
8686

87-
AssertCommonGetFromJson(bpmInfo);
88-
89-
Assert.AreEqual("4.0.0", bpmInfo.Version);
87+
AssertCommonGetFromJson(bpmInfo);
88+
89+
Assert.AreEqual("4.0.0", bpmInfo.Version);
9090

91-
Assert.AreEqual(1, bpmInfo.LufsRegions.Count);
92-
Assert.AreEqual(0, bpmInfo.LufsRegions[0].StartSampleIndex);
93-
Assert.AreEqual(882000, bpmInfo.LufsRegions[0].EndSampleIndex);
94-
Assert.AreEqual(3.1f, bpmInfo.LufsRegions[0].Loudness);
91+
Assert.AreEqual(1, bpmInfo.LufsRegions.Count);
92+
Assert.AreEqual(0, bpmInfo.LufsRegions[0].StartSampleIndex);
93+
Assert.AreEqual(882000, bpmInfo.LufsRegions[0].EndSampleIndex);
94+
Assert.AreEqual(3.1f, bpmInfo.LufsRegions[0].Loudness);
9595
}
9696

9797
private void AssertCommonGetFromJson(BaseBpmInfo bpmInfo)
@@ -162,13 +162,17 @@ public void GetBpmInfoRegions()
162162
var songBpm = 60f;
163163
var audioFrequency = 44100;
164164
var audiosamples = 44100 * 20;
165-
var bpmEvents = new List<BaseBpmEvent>
165+
var difficulty = new BaseDifficulty
166166
{
167-
new() { JsonTime = 0, Bpm = 60 },
168-
new() { JsonTime = 10, Bpm = 120 }
167+
BpmEvents = new List<BaseBpmEvent>
168+
{
169+
new() { JsonTime = 0, Bpm = 60 },
170+
new() { JsonTime = 10, Bpm = 120 }
171+
}
169172
};
173+
difficulty.BootstrapBpmEvents(songBpm);
170174

171-
var regions = BaseBpmInfo.GetBpmInfoRegions(bpmEvents, songBpm, audiosamples, audioFrequency);
175+
var regions = BaseBpmInfo.GetBpmInfoRegions(difficulty.BpmEvents, songBpm, audiosamples, audioFrequency);
172176
Assert.AreEqual(2, regions.Count);
173177

174178
Assert.AreEqual(0f, regions[0].StartBeat);
@@ -205,27 +209,41 @@ public void ConversionDoesNotIntroduceDriftOverTime()
205209
var songBpm = 60f;
206210
var audioFrequency = 44100;
207211
var audiosamples = 44100 * 20;
208-
209-
var initialBpmEvents = new List<BaseBpmEvent>
212+
213+
var initialDifficulty = new BaseDifficulty
210214
{
211-
new() { JsonTime = 0, Bpm = 60 },
212-
new() { JsonTime = 10, Bpm = 120 }
215+
BpmEvents = new List<BaseBpmEvent>
216+
{
217+
new() { JsonTime = 0, Bpm = 60 },
218+
new() { JsonTime = 10, Bpm = 120 }
219+
}
213220
};
221+
initialDifficulty.BootstrapBpmEvents(songBpm);
222+
223+
var initialBpmEvents = initialDifficulty.BpmEvents;
214224
var initialRegions = BaseBpmInfo.GetBpmInfoRegions(initialBpmEvents, songBpm, audiosamples, audioFrequency);
215225

216226
// Loop conversion to and from a bunch of times
217-
List<BaseBpmEvent> bpmEvents = new List<BaseBpmEvent>
227+
var difficulty = new BaseDifficulty
218228
{
219-
new() { JsonTime = 0, Bpm = 60 },
220-
new() { JsonTime = 10, Bpm = 120 }
229+
BpmEvents = new List<BaseBpmEvent>
230+
{
231+
new() { JsonTime = 0, Bpm = 60 },
232+
new() { JsonTime = 10, Bpm = 120 }
233+
}
221234
};
235+
difficulty.BootstrapBpmEvents(songBpm);
236+
222237
List<BpmInfoBpmRegion> regions = new List<BpmInfoBpmRegion>();
238+
223239
for (var i = 0; i < 100; i++)
224240
{
225-
regions = BaseBpmInfo.GetBpmInfoRegions(bpmEvents, songBpm, audiosamples, audioFrequency);
226-
bpmEvents = BaseBpmInfo.GetBpmEvents(regions, audioFrequency);
241+
regions = BaseBpmInfo.GetBpmInfoRegions(difficulty.BpmEvents, songBpm, audiosamples, audioFrequency);
242+
difficulty.BpmEvents = BaseBpmInfo.GetBpmEvents(regions, audioFrequency);
243+
difficulty.BootstrapBpmEvents(songBpm);
227244
}
228-
245+
var bpmEvents = difficulty.BpmEvents;
246+
229247
// Compare bpm events
230248
Assert.AreEqual(initialBpmEvents.Count, bpmEvents.Count);
231249

Assets/__Scripts/Beatmap/Animations/ObjectAnimator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public void SetData(BaseGrid obj)
178178
{
179179
continue;
180180
}
181-
var bpmChangeGridContainer = BeatmapObjectContainerCollection.GetCollectionForType<BPMChangeGridContainer>(ObjectType.BpmChange);
181+
var map = BeatSaberSongContainer.Instance.Map;
182182
foreach (var ce in events.Where(ev => ev.Type == "AssignPathAnimation"))
183183
{
184184
foreach (var jprop in ce.Data)
@@ -196,7 +196,7 @@ public void SetData(BaseGrid obj)
196196
TimeEnd = time_end,
197197
};
198198
if (p.Transition != 0) {
199-
p.Transition = bpmChangeGridContainer.JsonTimeToSongBpmTime(ce.JsonTime + p.Transition) - ce.SongBpmTime;
199+
p.Transition = (float)map.JsonTimeToSongBpmTime(ce.JsonTime + p.Transition) - ce.SongBpmTime;
200200
}
201201
AddPointDef(p, jprop.Key);
202202
}

Assets/__Scripts/Beatmap/Base/BaseArc.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ public BaseArc()
3535

3636
public BaseArc(BaseArc other)
3737
{
38-
SetTimes(other.JsonTime, other.SongBpmTime);
38+
SetTimes(other.JsonTime);
3939
Color = other.Color;
4040
PosX = other.PosX;
4141
PosY = other.PosY;
4242
CutDirection = other.CutDirection;
4343
HeadControlPointLengthMultiplier = other.HeadControlPointLengthMultiplier;
44-
SetTailTimes(other.TailJsonTime, other.TailSongBpmTime);
44+
SetTailTimes(other.TailJsonTime);
4545
TailPosX = other.TailPosX;
4646
TailPosY = other.TailPosY;
4747
TailCutDirection = other.TailCutDirection;
@@ -52,13 +52,13 @@ public BaseArc(BaseArc other)
5252

5353
public BaseArc(BaseNote start, BaseNote end)
5454
{
55-
SetTimes(start.JsonTime, start.SongBpmTime);
55+
SetTimes(start.JsonTime);
5656
Color = start.Color;
5757
PosX = start.PosX;
5858
PosY = start.PosY;
5959
CutDirection = start.CutDirection;
6060
HeadControlPointLengthMultiplier = 1f;
61-
SetTailTimes(end.JsonTime, end.SongBpmTime);
61+
SetTailTimes(end.JsonTime);
6262
TailPosX = end.PosX;
6363
TailPosY = end.PosY;
6464
TailCutDirection = end.CutDirection;

Assets/__Scripts/Beatmap/Base/BaseBpmEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public BaseBpmEvent() {}
2626

2727
public BaseBpmEvent(BaseBpmEvent other)
2828
{
29-
SetTimes(other.JsonTime, other.SongBpmTime);
29+
SetTimes(other.JsonTime);
3030
Bpm = other.Bpm;
3131
CustomData = other.CustomData.Clone();
3232
}

Assets/__Scripts/Beatmap/Base/BaseChain.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ public BaseChain()
3333

3434
public BaseChain(BaseChain other)
3535
{
36-
SetTimes(other.JsonTime, other.SongBpmTime);
36+
SetTimes(other.JsonTime);
3737
Color = other.Color;
3838
PosX = other.PosX;
3939
PosY = other.PosY;
4040
CutDirection = other.CutDirection;
41-
SetTailTimes(other.TailJsonTime, other.TailSongBpmTime);
41+
SetTailTimes(other.TailJsonTime);
4242
TailPosX = other.TailPosX;
4343
TailPosY = other.TailPosY;
4444
SliceCount = other.SliceCount;
@@ -48,12 +48,12 @@ public BaseChain(BaseChain other)
4848

4949
public BaseChain(BaseNote start, BaseNote end)
5050
{
51-
SetTimes(start.JsonTime, start.SongBpmTime);
51+
SetTimes(start.JsonTime);
5252
Color = start.Color;
5353
PosX = start.PosX;
5454
PosY = start.PosY;
5555
CutDirection = start.CutDirection;
56-
SetTailTimes(end.JsonTime, end.SongBpmTime);
56+
SetTailTimes(end.JsonTime);
5757
TailPosX = end.PosX;
5858
TailPosY = end.PosY;
5959
SliceCount = 5;

Assets/__Scripts/Beatmap/Base/BaseDifficulty.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,102 @@ public List<BaseLightTranslationEventBoxGroup<BaseLightTranslationEventBox>>
9393
new List<BaseObject>(CustomEvents),
9494
};
9595

96+
#region BPM Time Conversion Logic
97+
98+
private float? songBpm;
99+
100+
public void BootstrapBpmEvents(float songBpm)
101+
{
102+
this.songBpm = songBpm;
103+
104+
// remove invalid bpm events
105+
BpmEvents.RemoveAll(x => x.JsonTime < 0);
106+
BpmEvents.RemoveAll(x => x.Bpm < 0);
107+
108+
if (!BpmEvents.Any()) return;
109+
110+
BpmEvents.Sort();
111+
112+
// insert beat 0 bpm event if needed
113+
if (BpmEvents.First().JsonTime > 0)
114+
{
115+
var newBpmEvent = new BaseBpmEvent(0, songBpm);
116+
BpmEvents.Insert(0, newBpmEvent);
117+
}
118+
119+
BaseBpmEvent lastBpmEvent = null;
120+
foreach (var bpmEvent in BpmEvents)
121+
{
122+
if (lastBpmEvent is null)
123+
{
124+
bpmEvent.songBpmTime = bpmEvent.JsonTime;
125+
}
126+
else
127+
{
128+
bpmEvent.songBpmTime = lastBpmEvent.songBpmTime + (bpmEvent.JsonTime - lastBpmEvent.JsonTime) * (songBpm / lastBpmEvent.Bpm);
129+
}
130+
131+
lastBpmEvent = bpmEvent;
132+
}
133+
}
134+
135+
public float? JsonTimeToSongBpmTime(float jsonTime)
136+
{
137+
if (songBpm is null) return null;
138+
var lastBpmEvent = FindLastBpmEventByJsonTime(jsonTime, inclusive: false);
139+
if (lastBpmEvent is null)
140+
{
141+
return jsonTime;
142+
}
143+
return lastBpmEvent.SongBpmTime + (jsonTime - lastBpmEvent.JsonTime) * (songBpm / lastBpmEvent.Bpm);
144+
}
145+
146+
public float? SongBpmTimeToJsonTime(float songBpmTime)
147+
{
148+
if (songBpm is null) return null;
149+
var lastBpmEvent = FindLastBpmEventBySongBpmTime(songBpmTime, inclusive: false);
150+
if (lastBpmEvent is null)
151+
{
152+
return songBpmTime;
153+
}
154+
return lastBpmEvent.JsonTime + (songBpmTime - lastBpmEvent.SongBpmTime) * (lastBpmEvent.Bpm / songBpm);
155+
}
156+
157+
public BaseBpmEvent FindLastBpmEventByJsonTime(float jsonTime, bool inclusive = false)
158+
{
159+
return BpmEvents.LastOrDefault(x => inclusive ? x.JsonTime <= jsonTime : x.JsonTime < jsonTime);
160+
}
161+
162+
public BaseBpmEvent FindLastBpmEventBySongBpmTime(float songBpmTime, bool inclusive = false)
163+
{
164+
if (songBpm is null) return null;
165+
return BpmEvents.LastOrDefault(x => inclusive ? x.SongBpmTime <= songBpmTime : x.SongBpmTime < songBpmTime);
166+
}
167+
168+
public float? BpmAtJsonTime(float jsonTime)
169+
{
170+
return FindLastBpmEventByJsonTime(jsonTime, inclusive: true)?.Bpm ?? songBpm;
171+
}
172+
173+
public float? BpmAtSongBpmTime(float songBpmTime)
174+
{
175+
return FindLastBpmEventBySongBpmTime(songBpmTime, inclusive: true)?.Bpm ?? songBpm;
176+
}
177+
178+
public void RecomputeAllObjectSongBpmTimes()
179+
{
180+
foreach (var objList in AllBaseObjectProperties())
181+
{
182+
if (objList is null) continue;
183+
foreach (var obj in objList)
184+
{
185+
obj.RecomputeSongBpmTime();
186+
}
187+
}
188+
}
189+
190+
#endregion
191+
96192
public void ConvertCustomBpmToOfficial()
97193
{
98194
var songBpm = BeatSaberSongContainer.Instance.Info.BeatsPerMinute;

Assets/__Scripts/Beatmap/Base/BaseEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public BaseEvent()
3737

3838
public BaseEvent(BaseEvent other)
3939
{
40-
SetTimes(other.JsonTime, other.SongBpmTime);
40+
SetTimes(other.JsonTime);
4141
Type = other.Type;
4242
Value = other.Value;
4343
FloatValue = other.FloatValue;

Assets/__Scripts/Beatmap/Base/BaseGrid.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ protected BaseGrid(float jsonTime, float songBpmTime, int posX, int posY, JSONNo
5353

5454
public float EditorScale { get; private set; }
5555

56-
public virtual float SpawnSongBpmTime { get { return SongBpmTime - Hjd; } }
57-
public virtual float DespawnSongBpmTime { get { return SongBpmTime + Hjd; } }
56+
public virtual float SpawnSongBpmTime => SongBpmTime - Hjd;
57+
public virtual float DespawnSongBpmTime => SongBpmTime + Hjd;
5858

5959
public virtual JSONNode CustomAnimation { get; set; }
6060

Assets/__Scripts/Beatmap/Base/BaseNJSEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public BaseNJSEvent()
2929

3030
public BaseNJSEvent(BaseNJSEvent other)
3131
{
32-
SetTimes(other.JsonTime, other.SongBpmTime);
32+
SetTimes(other.JsonTime);
3333
UsePrevious = other.UsePrevious;
3434
Easing = other.Easing;
3535
RelativeNJS = other.RelativeNJS;

Assets/__Scripts/Beatmap/Base/BaseNote.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public BaseNote()
3636

3737
public BaseNote(BaseNote other)
3838
{
39-
SetTimes(other.JsonTime, other.SongBpmTime);
39+
SetTimes(other.JsonTime);
4040
PosX = other.PosX;
4141
PosY = other.PosY;
4242
Color = other.Color;

0 commit comments

Comments
 (0)