Skip to content

Commit

Permalink
Support on-the-fly tempo changes in Playback (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
melanchall committed Feb 6, 2025
1 parent 9b0592a commit af3cb6b
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3469,6 +3469,270 @@ public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_10()
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_11()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new NoteOnEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 300), TempoMap),
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 700), TempoMap),
};

var objectToAdd1 = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 200), TempoMap);
var objectToAdd2 = new TimedEvent(new NoteOffEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 500), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(100,
(playback, collection) => collection.Add(objectToAdd1)),
new PlaybackChanger(100,
(playback, collection) => collection.Add(objectToAdd2)),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2), TimeSpan.FromMilliseconds(200)),
new ReceivedEvent(new NoteOnEvent(), TimeSpan.FromMilliseconds(250)),
new ReceivedEvent(new NoteOffEvent(), TimeSpan.FromMilliseconds(350)),
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(450)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(200), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_12()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new NoteOnEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 300), TempoMap),
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 700), TempoMap),
};

var objectToAdd1 = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 200), TempoMap);
var objectToAdd2 = new TimedEvent(new NoteOffEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 500), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(100,
(playback, collection) => collection.Add(objectToAdd1)),
new PlaybackChanger(200,
(playback, collection) => collection.Add(objectToAdd2)),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2), TimeSpan.FromMilliseconds(200)),
new ReceivedEvent(new NoteOnEvent(), TimeSpan.FromMilliseconds(300)),
new ReceivedEvent(new NoteOffEvent(), TimeSpan.FromMilliseconds(350)),
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(450)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(200), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_13()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new NoteOnEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 400), TempoMap),
new TimedEvent(new NoteOffEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 700), TempoMap),
};

var objectToAdd = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 100), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(200, (playback, collection) =>
{
collection.Add(objectToAdd);
CheckCurrentTime(playback, TimeSpan.FromMilliseconds(150), "before time jump");
playback.MoveToTime((MetricTimeSpan)TimeSpan.FromMilliseconds(300));
}),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new NoteOnEvent(), TimeSpan.FromMilliseconds(200)),
new ReceivedEvent(new NoteOffEvent(), TimeSpan.FromMilliseconds(300)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(100), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_14()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new NoteOnEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 400), TempoMap),
new TimedEvent(new NoteOffEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 700), TempoMap),
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 900), TempoMap),
};

var objectToAdd = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 100), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(200, (playback, collection) =>
{
collection.Add(objectToAdd);
CheckCurrentTime(playback, TimeSpan.FromMilliseconds(150), "before time jump");
playback.MoveToTime((MetricTimeSpan)TimeSpan.FromMilliseconds(450));
}),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(250)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(100), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_15()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new NoteOnEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 400), TempoMap),
new TimedEvent(new NoteOffEvent())
.SetTime(new MetricTimeSpan(0, 0, 0, 700), TempoMap),
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 900), TempoMap),
};

var objectToAdd = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 100), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(200, (playback, collection) =>
{
collection.Add(objectToAdd);
CheckCurrentTime(playback, TimeSpan.FromMilliseconds(150), "before first time jump");
playback.MoveToTime((MetricTimeSpan)TimeSpan.FromMilliseconds(300));
}),
new PlaybackChanger(50, (playback, collection) =>
{
CheckCurrentTime(playback, TimeSpan.FromMilliseconds(350), "before second time jump");
playback.MoveToTime((MetricTimeSpan)TimeSpan.FromMilliseconds(450));
}),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new NoteOnEvent(), TimeSpan.FromMilliseconds(200)),
new ReceivedEvent(new NoteOffEvent(), TimeSpan.FromMilliseconds(250)),
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(300)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(100), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_16()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 800), TempoMap),
};

var objectToAdd1 = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 300), TempoMap);
var objectToAdd2 = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 400), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(100, (playback, collection) => collection.Add(objectToAdd1, objectToAdd2)),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2), TimeSpan.FromMilliseconds(300)),
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(550)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(300), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

[Retry(RetriesNumber)]
[Test]
public void CheckPlaybackDataChangesOnTheFly_Add_SetTempo_17()
{
var initialObjects = new ITimedObject[]
{
new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 200), TempoMap),
new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 400), TempoMap),
new TimedEvent(new TextEvent("END"))
.SetTime(new MetricTimeSpan(0, 0, 0, 800), TempoMap),
};

var objectToAdd = new TimedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))
.SetTime(new MetricTimeSpan(0, 0, 0, 500), TempoMap);

CheckPlaybackDataChangesOnTheFly(
initialObjects: initialObjects,
actions: new[]
{
new PlaybackChanger(100, (playback, collection) => collection.Add(objectToAdd)),
},
expectedReceivedEvents: new[]
{
new ReceivedEvent(new SetTempoEvent(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2), TimeSpan.FromMilliseconds(200)),
new ReceivedEvent(new TextEvent("END"), TimeSpan.FromMilliseconds(500)),
},
additionalChecks: playback => MidiAsserts.AreEqual(
AddTempoChanges(TempoMap, (TimeSpan.FromMilliseconds(200), new Tempo(SetTempoEvent.DefaultMicrosecondsPerQuarterNote / 2))),
playback.TempoMap,
"Invalid tempo map."));
}

#endregion

#region Private methods
Expand Down
Loading

0 comments on commit af3cb6b

Please sign in to comment.