Skip to content

Commit

Permalink
Added NoteMinVelocity to SanitizingSettings
Browse files Browse the repository at this point in the history
  • Loading branch information
melanchall committed Aug 8, 2024
1 parent 5187dc7 commit 3d41e0b
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 12 deletions.
266 changes: 266 additions & 0 deletions DryWetMidi.Tests/Tools/Sanitizer/SanitizerTests.NoteMinVelocity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Tools;
using NUnit.Framework;

namespace Melanchall.DryWetMidi.Tests.Tools
{
[TestFixture]
public sealed partial class SanitizerTests
{
#region Test methods

[Test]
public void Sanitize_NoteMinVelocity_EmptyFile() => Sanitize(
midiFile: new MidiFile(),
settings: null,
expectedMidiFile: new MidiFile());

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_1() => Sanitize(
midiFile: new MidiFile(
new TrackChunk()),
settings: null,
expectedMidiFile: new MidiFile());

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_2() => Sanitize(
midiFile: new MidiFile(
new TrackChunk()),
settings: new SanitizingSettings
{
RemoveEmptyTrackChunks = false
},
expectedMidiFile: new MidiFile(
new TrackChunk()));

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_3() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(),
new TrackChunk()),
settings: null,
expectedMidiFile: new MidiFile());

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_4() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(),
new TrackChunk()),
settings: new SanitizingSettings
{
RemoveEmptyTrackChunks = false
},
expectedMidiFile: new MidiFile(
new TrackChunk(),
new TrackChunk()));

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_5() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A") { DeltaTime = 20 })),
settings: new SanitizingSettings
{
Trim = false
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new TextEvent("A") { DeltaTime = 20 })));

[Test]
public void Sanitize_NoteMinVelocity_NoNotes_6() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A") { DeltaTime = 20 }),
new TrackChunk(
new TextEvent("B"),
new ControlChangeEvent((SevenBitNumber)70, SevenBitNumber.MaxValue))),
settings: new SanitizingSettings
{
RemoveEventsOnUnusedChannels = false
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new TextEvent("A") { DeltaTime = 20 }),
new TrackChunk(
new TextEvent("B"),
new ControlChangeEvent((SevenBitNumber)70, SevenBitNumber.MaxValue))));

[Test]
public void Sanitize_NoteMinVelocity_SingleNote_AboveOrEqual([Values(10, 20)] byte noteVelocity) => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)noteVelocity),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue))),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)10
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)noteVelocity),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue))));

[Test]
public void Sanitize_NoteMinVelocity_SingleNote_Below([Values(10, 29)] byte noteVelocity) => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)noteVelocity),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue))),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)30,
RemoveEmptyTrackChunks = false,
},
expectedMidiFile: new MidiFile(
new TrackChunk()));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_1() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent(),
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)100),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 20 })),
settings: null,
expectedMidiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent(),
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)100),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 20 })));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_2() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)15),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 10 },
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 },
new TextEvent("B") { DeltaTime = 70 })),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)20
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOnEvent() { DeltaTime = 10, Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 },
new TextEvent("B") { DeltaTime = 70 })));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_3() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)15),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 10 },
new TextEvent("B") { DeltaTime = 70 },
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 }),
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)10 },
new NoteOffEvent() { DeltaTime = 5 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOffEvent() { DeltaTime = 20 })),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)20
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new TextEvent("B") { DeltaTime = 80 },
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 }),
new TrackChunk(
new TextEvent("A") { DeltaTime = 35 })));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_4() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)15),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 10 },
new TextEvent("B") { DeltaTime = 70 },
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 }),
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)5 },
new NoteOffEvent() { DeltaTime = 5 },
new TextEvent("A") { DeltaTime = 30 },
new NoteOffEvent() { DeltaTime = 20 })),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)20,
RemoveOrphanedNoteOffEvents = false
},
expectedMidiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 40 },
new TextEvent("A") { DeltaTime = 30 },
new TextEvent("B") { DeltaTime = 80 },
new NoteOnEvent() { Velocity = (SevenBitNumber)100 },
new NoteOffEvent() { DeltaTime = 20 }),
new TrackChunk(
new TextEvent("A") { DeltaTime = 35 },
new NoteOffEvent() { DeltaTime = 20 })));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_5() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)5 },
new NoteOffEvent() { DeltaTime = 5 },
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)19),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 10 }),
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)18 },
new NoteOffEvent() { DeltaTime = 5 })),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)20,
RemoveEmptyTrackChunks = false
},
expectedMidiFile: new MidiFile(
new TrackChunk(),
new TrackChunk()));

[Test]
public void Sanitize_NoteMinVelocity_MultipleNotes_6() => Sanitize(
midiFile: new MidiFile(
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)5 },
new NoteOffEvent() { DeltaTime = 5 },
new NoteOnEvent((SevenBitNumber)70, (SevenBitNumber)19),
new NoteOffEvent((SevenBitNumber)70, SevenBitNumber.MinValue) { DeltaTime = 10 }),
new TrackChunk(
new NoteOnEvent() { Velocity = (SevenBitNumber)18 },
new NoteOffEvent() { DeltaTime = 5 })),
settings: new SanitizingSettings
{
NoteMinVelocity = (SevenBitNumber)20
},
expectedMidiFile: new MidiFile());

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ public sealed class HighPrecisionTickGenerator : TickGenerator
/// [<see cref="MinInterval"/>; <see cref="MaxInterval"/>] range.</exception>
protected override void Start(TimeSpan interval)
{
ThrowIfArgument.IsOutOfRange(nameof(interval),
interval,
MinInterval,
MaxInterval,
$"Interval is out of [{MinInterval}, {MaxInterval}] range.");
ThrowIfArgument.IsOutOfRange(
nameof(interval),
interval,
MinInterval,
MaxInterval,
$"Interval is out of [{MinInterval}, {MaxInterval}] range.");
EnsureSessionIsCreated();

var intervalInMilliseconds = (int)interval.TotalMilliseconds;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ public sealed class RegularPrecisionTickGenerator : TickGenerator
/// [<see cref="MinInterval"/>; <see cref="MaxInterval"/>] range.</exception>
protected override void Start(TimeSpan interval)
{
ThrowIfArgument.IsOutOfRange(nameof(interval),
interval,
MinInterval,
MaxInterval,
$"Interval is out of [{MinInterval}, {MaxInterval}] range.");
ThrowIfArgument.IsOutOfRange(
nameof(interval),
interval,
MinInterval,
MaxInterval,
$"Interval is out of [{MinInterval}, {MaxInterval}] range.");

_timer = new Timer(interval.TotalMilliseconds);
_timer.Elapsed += OnElapsed;
Expand Down
12 changes: 11 additions & 1 deletion DryWetMidi/Tools/Sanitizer/Sanitizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,13 @@ private static void RemoveNoteData(
var noteMinLength = settings.NoteMinLength;
var removeShortNotes = noteMinLength != null && !noteMinLength.IsZeroTimeSpan();

if (!removeShortNotes && !settings.RemoveOrphanedNoteOnEvents && !settings.RemoveOrphanedNoteOffEvents)
var noteMinVelocity = settings.NoteMinVelocity;
var removeSilentNotes = noteMinVelocity > 0;

if (!removeShortNotes &&
!removeSilentNotes &&
!settings.RemoveOrphanedNoteOnEvents &&
!settings.RemoveOrphanedNoteOffEvents)
return;

var tempoMap = midiFile.GetTempoMap();
Expand All @@ -219,6 +225,10 @@ private static void RemoveNoteData(
if (removeShortNotes &&
LengthConverter.ConvertTo((MidiTimeSpan)note.Length, timeSpanType, note.Time, tempoMap).CompareTo(noteMinLength) < 0)
return true;

if (removeSilentNotes &&
note.Velocity < noteMinVelocity)
return true;
}
else
{
Expand Down
5 changes: 4 additions & 1 deletion DryWetMidi/Tools/Sanitizer/SanitizingSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;

namespace Melanchall.DryWetMidi.Tools
Expand All @@ -19,6 +20,8 @@ public sealed class SanitizingSettings
/// </summary>
public ITimeSpan NoteMinLength { get; set; }

public SevenBitNumber NoteMinVelocity { get; set; }

/// <summary>
/// Gets or sets settings which define how notes should be detected and built. More info in the
/// <see href="xref:a_getting_objects#settings">Getting objects: GetNotes: Settings</see> article.
Expand Down

0 comments on commit 3d41e0b

Please sign in to comment.