Skip to content

Commit

Permalink
[Release] Commented public API
Browse files Browse the repository at this point in the history
  • Loading branch information
melanchall committed Oct 22, 2022
1 parent 948b577 commit c468caf
Show file tree
Hide file tree
Showing 17 changed files with 646 additions and 23 deletions.
168 changes: 168 additions & 0 deletions DryWetMidi.Tests/Core/Lazy/MidiTokensReaderUtilitiesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Tests.Common;
using Melanchall.DryWetMidi.Tests.Utilities;
using NUnit.Framework;
using System;
using System.Linq;

namespace Melanchall.DryWetMidi.Tests.Core
{
[TestFixture]
public sealed class MidiTokensReaderUtilitiesTests
{
#region Test methods

[Test]
public void EnumerateEvents_NoEvents() => EnumerateEvents(
initReader: ReadUntilTrackChunk,
midiFile: new MidiFile(),
expectedEvents: Array.Empty<MidiEvent>());

[Test]
public void EnumerateEvents_SingleTrackChunk_SingleEvent() => EnumerateEvents(
initReader: ReadUntilTrackChunk,
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A"))),
expectedEvents: new MidiEvent[]
{
new TextEvent("A"),
new EndOfTrackEvent()
},
format: MidiFileFormat.SingleTrack);

[Test]
public void EnumerateEvents_SingleTrackChunk_MultipleEvents() => EnumerateEvents(
initReader: ReadUntilTrackChunk,
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A"),
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 })),
expectedEvents: new MidiEvent[]
{
new TextEvent("A"),
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 },
new EndOfTrackEvent()
},
format: MidiFileFormat.SingleTrack);

[Test]
public void EnumerateEvents_MultipleTrackChunks_SingleEvent_ReadFirst() => EnumerateEvents(
initReader: ReadUntilTrackChunk,
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A")),
new TrackChunk(
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 })),
expectedEvents: new MidiEvent[]
{
new TextEvent("A"),
new EndOfTrackEvent()
});

[Test]
public void EnumerateEvents_MultipleTrackChunks_SingleEvent_ReadSecond() => EnumerateEvents(
initReader: reader =>
{
ReadUntilTrackChunk(reader);
ReadUntilTrackChunk(reader);
},
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A")),
new TrackChunk(
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 })),
expectedEvents: new MidiEvent[]
{
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 },
new EndOfTrackEvent()
});

[Test]
public void EnumerateEvents_MultipleTrackChunks_MultipleEvents_ReadFirst() => EnumerateEvents(
initReader: ReadUntilTrackChunk,
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A"),
new NoteOnEvent((SevenBitNumber)50, SevenBitNumber.MaxValue) { DeltaTime = 20 },
new NoteOffEvent((SevenBitNumber)50, SevenBitNumber.MinValue)),
new TrackChunk(
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)40, SevenBitNumber.MaxValue),
new NoteOffEvent((SevenBitNumber)40, SevenBitNumber.MinValue) { DeltaTime = 20 })),
expectedEvents: new MidiEvent[]
{
new TextEvent("A"),
new NoteOnEvent((SevenBitNumber)50, SevenBitNumber.MaxValue) { DeltaTime = 20 },
new NoteOffEvent((SevenBitNumber)50, SevenBitNumber.MinValue),
new EndOfTrackEvent()
});

[Test]
public void EnumerateEvents_MultipleTrackChunks_MultipleEvents_ReadSecond() => EnumerateEvents(
initReader: reader =>
{
ReadUntilTrackChunk(reader);
ReadUntilTrackChunk(reader);
},
midiFile: new MidiFile(
new TrackChunk(
new TextEvent("A"),
new NoteOnEvent((SevenBitNumber)50, SevenBitNumber.MaxValue) { DeltaTime = 20 },
new NoteOffEvent((SevenBitNumber)50, SevenBitNumber.MinValue)),
new TrackChunk(
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)40, SevenBitNumber.MaxValue),
new NoteOffEvent((SevenBitNumber)40, SevenBitNumber.MinValue) { DeltaTime = 20 })),
expectedEvents: new MidiEvent[]
{
new ProgramChangeEvent((SevenBitNumber)70) { DeltaTime = 30 },
new NoteOnEvent((SevenBitNumber)40, SevenBitNumber.MaxValue),
new NoteOffEvent((SevenBitNumber)40, SevenBitNumber.MinValue) { DeltaTime = 20 },
new EndOfTrackEvent()
});

#endregion

#region Private methods

private void EnumerateEvents(
Action<MidiTokensReader> initReader,
MidiFile midiFile,
MidiEvent[] expectedEvents,
MidiFileFormat format = MidiFileFormat.MultiTrack)
{
var filePath = FileOperations.GetTempFilePath();

try
{
midiFile.Write(filePath, true, format);

using (var reader = MidiFile.ReadLazy(filePath))
{
initReader(reader);

var actualEvents = reader.EnumerateEvents().Events.ToArray();
MidiAsserts.AreEqual(expectedEvents, actualEvents, true, "Invalid events.");
}
}
finally
{
FileOperations.DeleteFile(filePath);
}
}

private void ReadUntilTrackChunk(MidiTokensReader reader)
{
while (true)
{
var token = reader.ReadToken();
if (token == null || (token is ChunkHeaderToken chunkHeaderToken && chunkHeaderToken.ChunkId == TrackChunk.Id))
return;
}
}

#endregion
}
}
17 changes: 2 additions & 15 deletions DryWetMidi.Tests/Core/MidiFile/MidiFileTests.ReadLazy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,21 +250,8 @@ public void ReadLazy(
}
}

private MidiToken[] ReadAllTokens(MidiTokensReader reader)
{
var tokens = new List<MidiToken>();

while (true)
{
var token = reader.ReadToken();
if (token == null)
break;

tokens.Add(token);
}

return tokens.ToArray();
}
private MidiToken[] ReadAllTokens(MidiTokensReader reader) =>
reader.EnumerateTokens().ToArray();

private bool AreTokensEqual(MidiToken expectedToken, MidiToken actualToken)
{
Expand Down
26 changes: 26 additions & 0 deletions DryWetMidi/Core/Lazy/EnumerateEventsResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.Generic;

namespace Melanchall.DryWetMidi.Core.Lazy
{
/// <summary>
/// Represents a result of the <see cref="MidiTokensReaderUtilities.EnumerateEvents(MidiTokensReader)"/>
/// method.
/// </summary>
public sealed class EnumerateEventsResult
{
#region Properties

/// <summary>
/// Gets a lazy collection of MIDI events.
/// </summary>
public IEnumerable<MidiEvent> Events { get; internal set; }

/// <summary>
/// Gets a MIDI token following the last MIDI event iterated by the
/// <see cref="MidiTokensReaderUtilities.EnumerateEvents(MidiTokensReader)"/> method.
/// </summary>
public MidiToken NextToken { get; internal set; }

#endregion
}
}
12 changes: 12 additions & 0 deletions DryWetMidi/Core/Lazy/MidiToken.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
namespace Melanchall.DryWetMidi.Core
{
/// <summary>
/// Represents a single MIDI token from a MIDI file.
/// </summary>
/// <seealso cref="MidiTokensReader"/>
public abstract class MidiToken
{
#region Constructor

/// <summary>
/// Initializes a new instance of the <see cref="MidiToken"/> with the
/// specified token type.
/// </summary>
/// <param name="tokenType">The type of a MIDI token.</param>
protected MidiToken(MidiTokenType tokenType)
{
TokenType = tokenType;
Expand All @@ -13,6 +22,9 @@ protected MidiToken(MidiTokenType tokenType)

#region Properties

/// <summary>
/// Gets the type of the current MIDI token.
/// </summary>
public MidiTokenType TokenType { get; }

#endregion
Expand Down
21 changes: 20 additions & 1 deletion DryWetMidi/Core/Lazy/MidiTokenType.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
namespace Melanchall.DryWetMidi.Core
{
/// <summary>
/// The type of a MIDI token.
/// </summary>
/// <seealso cref="MidiTokensReader"/>
public enum MidiTokenType
{
/// <summary>
/// A chunk's header. See <see cref="ChunkHeaderToken"/>.
/// </summary>
ChunkHeader,
HeaderChunkData,

/// <summary>
/// The data of a header chunk of a MIDI file. See <see cref="FileHeaderToken"/>.
/// </summary>
FileHeader,

/// <summary>
/// A MIDI event. See <see cref="MidiEventToken"/>.
/// </summary>
MidiEvent,

/// <summary>
/// A bytes packet. See <see cref="BytesPacketToken"/>.
/// </summary>
BytesPacket
}
}
46 changes: 46 additions & 0 deletions DryWetMidi/Core/Lazy/MidiTokensReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

namespace Melanchall.DryWetMidi.Core
{
/// <summary>
/// Provides a way to read a MIDI file sequentially token by token keeping
/// low memory consumption.
/// </summary>
/// <seealso cref="MidiTokensWriter"/>
public sealed class MidiTokensReader : IDisposable
{
#region Enums
Expand Down Expand Up @@ -103,6 +108,44 @@ internal MidiTokensReader(Stream stream, ReadingSettings settings, bool disposeS

#region Methods

/// <summary>
/// Reads a next MIDI token from the underlying stream.
/// </summary>
/// <returns>An instance of the <see cref="MidiToken"/>.</returns>
/// <exception cref="IOException">An I/O error occurred while reading the file.</exception>
/// <exception cref="NoHeaderChunkException">There is no header chunk in a file and that should be treated as error
/// according to the <see cref="ReadingSettings.NoHeaderChunkPolicy"/> of the used settings.</exception>
/// <exception cref="InvalidChunkSizeException">Actual header or track chunk's size differs from the one declared
/// in its header and that should be treated as error according to the <see cref="ReadingSettings.InvalidChunkSizePolicy"/>
/// of the used settings.</exception>
/// <exception cref="UnknownChunkException">Chunk to be read has unknown ID and that
/// should be treated as error accordng to the <see cref="ReadingSettings.UnknownChunkIdPolicy"/> of the
/// used settings.</exception>
/// <exception cref="UnexpectedTrackChunksCountException">Actual track chunks
/// count differs from the expected one (declared in the file header) and that should be treated as error according to
/// the <see cref="ReadingSettings.UnexpectedTrackChunksCountPolicy"/> of the used settings.</exception>
/// <exception cref="UnknownFileFormatException">The header chunk of the file specifies unknown file format and
/// that should be treated as error according to the <see cref="ReadingSettings.UnknownFileFormatPolicy"/> of
/// the used settings.</exception>
/// <exception cref="InvalidChannelEventParameterValueException">Value of a channel event's parameter
/// just read is invalid (is out of [0; 127] range) and that should be treated as error according to the
/// <see cref="ReadingSettings.InvalidChannelEventParameterValuePolicy"/> of the used settings.</exception>
/// <exception cref="InvalidMetaEventParameterValueException">Value of a meta event's parameter
/// just read is invalid and that should be treated as error according to the
/// <see cref="ReadingSettings.InvalidMetaEventParameterValuePolicy"/> of the used settings.</exception>
/// <exception cref="UnknownChannelEventException">Reader has encountered an unknown channel event and that
/// should be treated as error according to the <see cref="ReadingSettings.UnknownChannelEventPolicy"/> of
/// the used settings.</exception>
/// <exception cref="NotEnoughBytesException">MIDI file data cannot be read since the reader's underlying stream doesn't
/// have enough bytes and that should be treated as error according to the <see cref="ReadingSettings.NotEnoughBytesPolicy"/>
/// of the used settings.</exception>
/// <exception cref="UnexpectedRunningStatusException">Unexpected running status is encountered.</exception>
/// <exception cref="MissedEndOfTrackEventException">Track chunk doesn't end with <c>End Of Track</c> event and that
/// should be treated as error accordng to the <see cref="ReadingSettings.MissedEndOfTrackPolicy"/> of
/// the used settings.</exception>
/// <exception cref="InvalidOperationException"><see cref="ReaderSettings.Buffer"/> of the used settings
/// is <c>null</c> in case of <see cref="ReaderSettings.BufferingPolicy"/> set to
/// <see cref="BufferingPolicy.UseCustomBuffer"/>.</exception>
public MidiToken ReadToken()
{
Instruction instruction = null;
Expand Down Expand Up @@ -239,6 +282,9 @@ private void GoToNextState()

#region IDisposable

/// <summary>
/// Releases all resources used by the current <see cref="MidiTokensReader"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
Expand Down
Loading

0 comments on commit c468caf

Please sign in to comment.