Module mididi.reader
mididi.reader
contains functions for reading the MIDI format.
Currently, the reader is compatible with the standard MIDI format 1.0 and forward compatible with newer formats (in which case newer features will be ignored, as the specification demands).
The implementation (and some of the documentation) is based on this specification: https://www.cs.cmu.edu/~music/cmsip/readings/Standard-MIDI-file-format-updated.pdf
Authors: https://github.com/w2ptr
Members
Name | Kind | Description |
---|---|---|
readMIDIFile |
template | description |
readMIDIFile |
template | description |
MIDIReader |
template | description |
function MIDI readMIDIFile()(File file)
function MIDI readMIDIFile()(string path)
readMIDIFile()
reads a MIDI file, of course.
This essentially does the same as MIDIReader!(ubyte[]).readMIDI()
, but is
given for convenience.
It has two overloads, one for std.stdio.File
objects, and one if you have the
file path only.
struct MIDIReader(T)
MIDIReader
is the raw MIDI reader object.
This can be used to read the standard MIDI chunk-by-chunk or event-by-event.
Constraints:
T
must be an input range of ubyte
Example: Reads a complete file with a single track of a single event
import mididi.def : MetaEventType, TrackFormat;
auto reader = MIDIReader!(ubyte[])([
'M', 'T', 'h', 'd', // header chunk
0x00, 0x00, 0x00, 0x06, // length
0x00, 0x01, // format 1
0x00, 0x02, // 2 tracks
0b0_0000000, 0x60, // time division: 0, 0, 0x60
'M', 'T', 'r', 'k', // track chunk 1
0, 0, 0, 4, // length = 4
0x00, 0xFF, 0x2F, 0x00, // end of track
'M', 'T', 'r', 'k', // track chunk 2
0, 0, 0, 4, // length = 4
0x00, 0xFF, 0x2F, 0x00, // end of track
]);
auto midi = reader.readMIDI();
assert(midi.headerChunk.trackFormat == TrackFormat.simultaneous);
assert(midi.headerChunk.nTracks == 2);
assert(midi.headerChunk.division.getFormat() == 0);
assert(midi.headerChunk.division.getTicksPerQuarterNote() == 0x0060);
assert(midi.trackChunks.length == 2);
assert(midi.trackChunks[0].events.length == 1);
const event = midi.trackChunks[0].events[0];
assert(event.asMetaEvent().type == MetaEventType.endOfTrack);
Example: Reads a file that has bytes past the last chunk, which is disallowed.
auto reader = MIDIReader!(ubyte[])([
'M', 'T', 'h', 'd',
0x00, 0x00, 0x00, 0x06, // length
0x00, 0x00, // format: 0
0x00, 0x01, // nTracks: 1
0b0_0000000, 0x60, // time division: 0, 0, 0x60
'M', 'T', 'r', 'k',
0, 0, 0, 4,
0x00, 0xFF, 0x2F, 0x00,
'M', 'T', 'r', 'k', // too many chunks
0, 0, 0, 4,
0x00, 0xFF, 0x2F, 0x00,
]);
try {
const _ = reader.readMIDI();
assert(false, "File is invalid, but no exception was thrown");
} catch (Exception e) {}
method MIDI readMIDI()
Reads the entire input and returns the data in a MIDI
object.
Throws:
Exception
if all chunks are read and the input is not empty yet;
Exception
if the header chunk or any of the track chunks is invalid
method Chunk readChunk()
Reads a chunk from the input (meaning, it can either read a header chunk or a track chunk).
Throws:
Exception
if the chunk is invalid
method const pure nothrow @nogc @safe bool isFinished()
Returns: whether the reader has no more content to read.