UNPKG

webdaw-modules

Version:

a set of modules for building a web-based DAW

493 lines 17.9 kB
"use strict"; // based on: https://github.com/pravdomil/jasmid.ts var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseMIDIFile = void 0; // import { BufferReader } from 'jasmid.ts'; var uniqid_1 = __importDefault(require("uniqid")); var bufferreader_1 = require("./bufferreader"); var midi_1 = require("./util/midi"); var calculateMillis_1 = require("./calculateMillis"); var createTrack_1 = require("./createTrack"); var createNotes_1 = require("./createNotes"); function parseMIDIFile(buffer) { var reader = new bufferreader_1.BufferReader(buffer); var header = parseHeader(reader); // const { timeTrack, tracks } = parseTracks(reader, header.ticksPerBeat) var _a = parseTracks(reader, header.ticksPerBeat), tracks = _a.tracks, events = _a.events, initialTempo = _a.initialTempo, initialNumerator = _a.initialNumerator, initialDenominator = _a.initialDenominator; return { ppq: header.ticksPerBeat, latency: 17, bufferTime: 100, tracks: tracks, tracksById: tracks.reduce(function (acc, value) { acc[value.id] = value; return acc; }, {}), events: calculateMillis_1.calculateMillis(events, { ppq: header.ticksPerBeat, bpm: initialTempo, }), notes: createNotes_1.createNotes(events), initialTempo: initialTempo, initialNumerator: initialNumerator, initialDenominator: initialDenominator, }; } exports.parseMIDIFile = parseMIDIFile; function parseHeader(reader) { var headerChunk = reader.midiChunk(); if (headerChunk.id !== "MThd" || headerChunk.length !== 6) { throw new Error("Bad .mid file, header not found"); } var headerReader = new bufferreader_1.BufferReader(headerChunk.data); var formatType = headerReader.uint16(); var trackCount = headerReader.uint16(); var timeDivision = headerReader.uint16(); if (timeDivision & 0x8000) { throw new Error("Expressing time division in SMTPE frames is not supported yet"); } var ticksPerBeat = timeDivision; return { formatType: formatType, trackCount: trackCount, ticksPerBeat: ticksPerBeat }; } function parseTracks(reader, ppq) { var initialTempo = -1; var initialNumerator = -1; var initialDenominator = -1; var tracks = []; var events = []; while (!reader.eof()) { var trackChunk = reader.midiChunk(); if (trackChunk.id !== "MTrk") { throw new Error("Unexpected chunk, expected MTrk, got " + trackChunk.id); } var trackId = "T-" + uniqid_1.default(); var track = createTrack_1.createTrack(trackId); var trackTrack = new bufferreader_1.BufferReader(trackChunk.data); var ticks = 0; var lastTypeByte = null; while (!trackTrack.eof()) { var data = parseEvent(trackTrack, lastTypeByte); // console.log(data); var event_1 = data.event, deltaTime = data.deltaTime, bpm = data.bpm, numerator = data.numerator, denominator = data.denominator, trackName = data.trackName; if (bpm && initialTempo === -1) { initialTempo = bpm; } if (numerator && initialNumerator === -1) { initialNumerator = numerator; } if (denominator && initialDenominator === -1) { initialDenominator = denominator; } if (trackName) { track.name = trackName; } ticks += deltaTime; // console.log('TICKS', ticks, bpm, numerator); lastTypeByte = event_1.type; events.push(__assign(__assign({}, event_1), { trackId: trackId, ticks: ticks })); } tracks.push(track); } return { events: midi_1.sortMIDIEvents(events), tracks: tracks, initialTempo: initialTempo, initialNumerator: initialNumerator, initialDenominator: initialDenominator, }; } function parseEvent(reader, lastTypeByte) { var deltaTime = reader.midiInt(); var typeByte = reader.uint8(); // meta events: 0xff // system events: 0xf0, 0xf7 // midi events: all other bytes if (typeByte === 0xff) { var subTypeByte = reader.uint8(); var length_1 = reader.midiInt(); switch (subTypeByte) { // sequence number case 0x00: if (length_1 !== 2) { throw new Error("Expected length for sequenceNumber event is 2, got " + length_1); } return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.SEQUENCE_NUMBER, number: reader.uint16(), }, deltaTime: deltaTime, }; // text case 0x01: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.TEXT, text: reader.string(length_1), }, deltaTime: deltaTime, }; // copyright case 0x02: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.COPYRIGHT_NOTICE, text: reader.string(length_1), }, deltaTime: deltaTime, }; // track name case 0x03: var trackName = reader.string(length_1); return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.TRACK_NAME, text: trackName, }, deltaTime: deltaTime, trackName: trackName, }; // instrument name case 0x04: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.INSTRUMENT_NAME, text: reader.string(length_1), }, deltaTime: deltaTime, }; // lyrics case 0x05: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.LYRICS, text: reader.string(length_1), }, deltaTime: deltaTime, }; // marker case 0x06: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.MARKER, text: reader.string(length_1), }, deltaTime: deltaTime, }; // cue point case 0x07: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.CUE_POINT, text: reader.string(length_1), }, deltaTime: deltaTime, }; // channel prefix case 0x20: if (length_1 !== 1) { throw new Error("Expected length for midiChannelPrefix event is 1, got " + length_1); } return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.CHANNEL_PREFIX, channel: reader.uint8(), }, deltaTime: deltaTime, }; // end of track case 0x2f: if (length_1 !== 0) { throw new Error("Expected length for endOfTrack event is 0, got " + length_1); } return { event: { descr: midi_1.END_OF_TRACK, type: typeByte, subType: subTypeByte, }, deltaTime: deltaTime, }; // tempo case 0x51: if (length_1 !== 3) { throw new Error("Expected length for setTempo event is 3, got " + length_1); } var microsecondsPerBeat = (reader.uint8() << 16) + (reader.uint8() << 8) + reader.uint8(); var bpm = 60000000 / microsecondsPerBeat; return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.TEMPO, bpm: bpm, }, bpm: bpm, deltaTime: deltaTime, }; // smpte offset case 0x54: if (length_1 != 5) { throw new Error("Expected length for smpteOffset event is 5, got " + length_1); } var hourByte = reader.uint8(); return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.SMPTE_OFFSET, frameRate: getFrameRate(hourByte), hour: hourByte & 0x1f, min: reader.uint8(), sec: reader.uint8(), frame: reader.uint8(), subFrame: reader.uint8(), }, deltaTime: deltaTime, }; // time signature case 0x58: if (length_1 != 4) { throw new Error("Expected length for timeSignature event is 4, got " + length_1); } var numerator = reader.uint8(); var denominator = Math.pow(2, reader.uint8()); return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.TIME_SIGNATURE, numerator: numerator, denominator: denominator, metronome: reader.uint8(), thirtySeconds: reader.uint8(), }, numerator: numerator, denominator: denominator, deltaTime: deltaTime, }; // key signature case 0x59: if (length_1 != 2) { throw new Error("Expected length for keySignature event is 2, got " + length_1); } return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.KEY_SIGNATURE, key: reader.int8(), scale: reader.uint8(), }, deltaTime: deltaTime, }; // sequencer specific case 0x7f: return { event: { type: typeByte, subType: subTypeByte, descr: midi_1.SEQUENCER_SPECIFIC, data: reader.read(length_1), }, deltaTime: deltaTime, }; // undefined default: return { event: { type: typeByte, subType: subTypeByte, descr: "undefined", data: reader.read(length_1), }, deltaTime: deltaTime, }; } } else if (typeByte === 0xf0) { // system exclusive var length_2 = reader.midiInt(); return { event: { type: 0xf0, descr: midi_1.SYSTEM_EXCLUSIVE, data: reader.read(length_2), }, deltaTime: deltaTime, }; } else if (typeByte === 0xf7) { // divided system exclusive var length_3 = reader.midiInt(); return { event: { type: 0xf0, descr: midi_1.DIVIDED_SYSTEM_EXCLUSIVE, data: reader.read(length_3), }, deltaTime: deltaTime, }; } else { /** * running status - reuse lastEventTypeByte as the event type * typeByte is actually the first parameter */ var isRunningStatus = (typeByte & 128) === 0; var value = isRunningStatus ? typeByte : reader.uint8(); typeByte = isRunningStatus ? (lastTypeByte === null ? 0 : lastTypeByte) : typeByte; // console.log(isRunningStatus, typeByte, value); var channel = typeByte & 0x0f; // channels[channel] = true; switch (typeByte >> 4) { // note off case 0x08: return { event: { type: 0x80, descr: midi_1.NOTE_OFF, channel: channel, noteNumber: value, velocity: reader.uint8(), }, deltaTime: deltaTime, }; // note on case 0x09: var velocity = reader.uint8(); return { event: { type: velocity === 0 ? 0x80 : 0x90, descr: velocity === 0 ? midi_1.NOTE_OFF : midi_1.NOTE_ON, channel: channel, noteNumber: value, velocity: velocity, }, deltaTime: deltaTime, }; // note aftertouch case 0x0a: return { event: { type: 0xa0, descr: midi_1.NOTE_AFTERTOUCH, channel: channel, noteNumber: value, amount: reader.uint8(), }, deltaTime: deltaTime, }; // controller case 0x0b: return { event: { type: 0xb0, descr: midi_1.CONTROLLER, channel: channel, controllerNumber: value, value: reader.uint8(), }, deltaTime: deltaTime, }; // program change case 0x0c: return { event: { type: 0xc0, descr: midi_1.PROGRAM_CHANGE, channel: channel, program: value, }, deltaTime: deltaTime, }; // channel aftertouch case 0x0d: return { event: { type: 0xd0, descr: midi_1.CHANNEL_AFTERTOUCH, channel: channel, amount: value, }, deltaTime: deltaTime, }; // pitch bend case 0x0e: return { event: { type: 0xe0, descr: midi_1.PITCH_BEND, channel: channel, value: value + (reader.uint8() << 7), }, deltaTime: deltaTime, }; // default: // return { // event: { // type: typeByte >> 4, // descr: "unrecognized", // channel, // }, // deltaTime, // }; } } console.log("Unrecognized MIDI event type byte: " + typeByte + " (fix this)"); return { event: { type: typeByte === 255 ? 0 : typeByte, }, deltaTime: deltaTime, }; throw new Error("Unrecognized MIDI event type byte: " + typeByte); } function getFrameRate(hourByte) { switch (hourByte & 96) { case 0x00: return 24; case 0x20: return 25; case 0x40: return 29; case 0x60: return 30; default: return 0; } } //# sourceMappingURL=parseMIDIFile.js.map