k-l-lambda's picture
updated node_modules
4cadbaf
/*
class to encode the .mid file format
(depends on streamEx.js)
*/
const OStream = require("./streamEx.js");
module.exports = function OMidiFile ({ header, tracks }) {
function writeChunk (stream, id, data) {
console.assert(id.length === 4, "chunk id must be 4 byte");
stream.write(id);
stream.writeInt32(data.length);
stream.write(data);
}
function writeEvent (stream, event) {
if (event.subtype === "unknown")
return;
stream.writeVarInt(event.deltaTime);
switch (event.type) {
case "meta":
stream.writeInt8(0xff);
switch (event.subtype) {
case "sequenceNumber":
stream.writeInt8(0x00);
stream.writeVarInt(2);
stream.writeInt16(event.number);
break;
case "text":
stream.writeInt8(0x01);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "copyrightNotice":
stream.writeInt8(0x02);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "trackName":
stream.writeInt8(0x03);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "instrumentName":
stream.writeInt8(0x04);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "lyrics":
stream.writeInt8(0x05);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "marker":
stream.writeInt8(0x06);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "cuePoint":
stream.writeInt8(0x07);
stream.writeVarInt(event.text.length);
stream.write(event.text);
break;
case "midiChannelPrefix":
stream.writeInt8(0x20);
stream.writeVarInt(1);
stream.writeInt8(event.channel);
break;
case "endOfTrack":
stream.writeInt8(0x2f);
stream.writeVarInt(0);
break;
case "setTempo":
stream.writeInt8(0x51);
stream.writeVarInt(3);
stream.writeInt8((event.microsecondsPerBeat >> 16) & 0xff);
stream.writeInt8((event.microsecondsPerBeat >> 8) & 0xff);
stream.writeInt8(event.microsecondsPerBeat & 0xff);
break;
case "smpteOffset":
stream.writeInt8(0x54);
stream.writeVarInt(5);
var frameByte = { 24: 0x00, 25: 0x20, 29: 0x40, 30: 0x60 }[event.frameRate];
stream.writeInt8(event.hour | frameByte);
stream.writeInt8(event.min);
stream.writeInt8(event.sec);
stream.writeInt8(event.frame);
stream.writeInt8(event.subframe);
break;
case "timeSignature":
stream.writeInt8(0x58);
stream.writeVarInt(4);
stream.writeInt8(event.numerator);
stream.writeInt8(Math.log2(event.denominator));
stream.writeInt8(event.metronome);
stream.writeInt8(event.thirtyseconds);
break;
case "keySignature":
stream.writeInt8(0x59);
stream.writeVarInt(2);
stream.writeInt8(event.key);
stream.writeInt8(event.scale);
break;
case "sequencerSpecific":
stream.writeInt8(0x7f);
stream.writeVarInt(event.data.length);
stream.write(event.data);
break;
default:
throw new Error("unhandled event subtype:" + event.subtype);
}
break;
case "sysEx":
stream.writeInt8(0xf0);
stream.writeVarInt(event.data.length);
stream.write(event.data);
break;
case "dividedSysEx":
stream.writeInt8(0xf7);
stream.writeVarInt(event.data.length);
stream.write(event.data);
break;
case "channel":
switch (event.subtype) {
case "noteOn":
stream.writeInt8(0x90 | event.channel);
stream.writeInt8(event.noteNumber);
stream.writeInt8(event.velocity);
break;
case "noteOff":
stream.writeInt8(0x80 | event.channel);
stream.writeInt8(event.noteNumber);
stream.writeInt8(event.velocity ? event.velocity : 0);
break;
case "noteAftertouch":
stream.writeInt8(0xa0 | event.channel);
stream.writeInt8(event.noteNumber);
stream.writeInt8(event.amount);
break;
case "controller":
stream.writeInt8(0xb0 | event.channel);
stream.writeInt8(event.controllerType);
stream.writeInt8(event.value);
break;
case "programChange":
stream.writeInt8(0xc0 | event.channel);
stream.writeInt8(event.programNumber);
break;
case "channelAftertouch":
stream.writeInt8(0xd0 | event.channel);
stream.writeInt8(event.amount);
break;
case "pitchBend":
stream.writeInt8(0xe0 | event.channel);
stream.writeInt8(event.value & 0xff);
stream.writeInt8((event.value >> 7) & 0xff);
break;
default:
throw new Error("unhandled event subtype:" + event.subtype);
}
break;
default:
throw new Error("unhandled event type:" + event.type);
}
}
const stream = new OStream();
const headerChunk = new OStream();
headerChunk.writeInt16(header.formatType);
headerChunk.writeInt16(tracks.length);
headerChunk.writeInt16(header.ticksPerBeat);
writeChunk(stream, "MThd", headerChunk.getBuffer());
for (let i = 0; i < tracks.length; ++i) {
const trackChunk = new OStream();
for (let ei = 0; ei < tracks[i].length; ++ei)
writeEvent(trackChunk, tracks[i][ei]);
writeChunk(stream, "MTrk", trackChunk.getBuffer());
}
return stream.getArrayBuffer();
};