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.

Source

Click here to view the source of this module.