UNPKG

midifile

Version:
171 lines (154 loc) 4.83 kB
'use strict'; // MIDIFileHeader : Read and edit a MIDI header chunk in a given ArrayBuffer function MIDIFileHeader(buffer) { let a; // No buffer creating him if (!buffer) { a = new Uint8Array(MIDIFileHeader.HEADER_LENGTH); // Adding the header id (MThd) a[0] = 0x4d; a[1] = 0x54; a[2] = 0x68; a[3] = 0x64; // Adding the header chunk size a[4] = 0x00; a[5] = 0x00; a[6] = 0x00; a[7] = 0x06; // Adding the file format (1 here cause it's the most commonly used) a[8] = 0x00; a[9] = 0x01; // Adding the track count (1 cause it's a new file) a[10] = 0x00; a[11] = 0x01; // Adding the time division (192 ticks per beat) a[12] = 0x00; a[13] = 0xc0; // saving the buffer this.datas = new DataView(a.buffer, 0, MIDIFileHeader.HEADER_LENGTH); // Parsing the given buffer } else { if (!(buffer instanceof ArrayBuffer)) { throw Error('Invalid buffer received.'); } this.datas = new DataView(buffer, 0, MIDIFileHeader.HEADER_LENGTH); // Reading MIDI header chunk if ( !( 'M' === String.fromCharCode(this.datas.getUint8(0)) && 'T' === String.fromCharCode(this.datas.getUint8(1)) && 'h' === String.fromCharCode(this.datas.getUint8(2)) && 'd' === String.fromCharCode(this.datas.getUint8(3)) ) ) { throw new Error('Invalid MIDIFileHeader : MThd prefix not found'); } // Reading chunk length if (6 !== this.datas.getUint32(4)) { throw new Error('Invalid MIDIFileHeader : Chunk length must be 6'); } } } // Static constants MIDIFileHeader.HEADER_LENGTH = 14; MIDIFileHeader.FRAMES_PER_SECONDS = 1; MIDIFileHeader.TICKS_PER_BEAT = 2; // MIDI file format MIDIFileHeader.prototype.getFormat = function() { const format = this.datas.getUint16(8); if (0 !== format && 1 !== format && 2 !== format) { throw new Error( 'Invalid MIDI file : MIDI format (' + format + '),' + ' format can be 0, 1 or 2 only.' ); } return format; }; MIDIFileHeader.prototype.setFormat = function(format) { if (0 !== format && 1 !== format && 2 !== format) { throw new Error( 'Invalid MIDI format given (' + format + '),' + ' format can be 0, 1 or 2 only.' ); } this.datas.setUint16(8, format); }; // Number of tracks MIDIFileHeader.prototype.getTracksCount = function() { return this.datas.getUint16(10); }; MIDIFileHeader.prototype.setTracksCount = function(n) { return this.datas.setUint16(10, n); }; // Tick compute MIDIFileHeader.prototype.getTickResolution = function(tempo) { // Frames per seconds if (this.datas.getUint16(12) & 0x8000) { return 1000000 / (this.getSMPTEFrames() * this.getTicksPerFrame()); // Ticks per beat } // Default MIDI tempo is 120bpm, 500ms per beat tempo = tempo || 500000; return tempo / this.getTicksPerBeat(); }; // Time division type MIDIFileHeader.prototype.getTimeDivision = function() { if (this.datas.getUint16(12) & 0x8000) { return MIDIFileHeader.FRAMES_PER_SECONDS; } return MIDIFileHeader.TICKS_PER_BEAT; }; // Ticks per beat MIDIFileHeader.prototype.getTicksPerBeat = function() { var divisionWord = this.datas.getUint16(12); if (divisionWord & 0x8000) { throw new Error('Time division is not expressed as ticks per beat.'); } return divisionWord; }; MIDIFileHeader.prototype.setTicksPerBeat = function(ticksPerBeat) { this.datas.setUint16(12, ticksPerBeat & 0x7fff); }; // Frames per seconds MIDIFileHeader.prototype.getSMPTEFrames = function() { const divisionWord = this.datas.getUint16(12); let smpteFrames; if (!(divisionWord & 0x8000)) { throw new Error('Time division is not expressed as frames per seconds.'); } smpteFrames = divisionWord & 0x7f00; if (-1 === [24, 25, 29, 30].indexOf(smpteFrames)) { throw new Error('Invalid SMPTE frames value (' + smpteFrames + ').'); } return 29 === smpteFrames ? 29.97 : smpteFrames; }; MIDIFileHeader.prototype.getTicksPerFrame = function() { const divisionWord = this.datas.getUint16(12); if (!(divisionWord & 0x8000)) { throw new Error('Time division is not expressed as frames per seconds.'); } return divisionWord & 0x00ff; }; MIDIFileHeader.prototype.setSMTPEDivision = function( smpteFrames, ticksPerFrame ) { if (29.97 === smpteFrames) { smpteFrames = 29; } if (-1 === [24, 25, 29, 30].indexOf(smpteFrames)) { throw new Error('Invalid SMPTE frames value given (' + smpteFrames + ').'); } if (0 > ticksPerFrame || 0xff < ticksPerFrame) { throw new Error( 'Invalid ticks per frame value given (' + smpteFrames + ').' ); } this.datas.setUint8(12, 0x80 | smpteFrames); this.datas.setUint8(13, ticksPerFrame); }; module.exports = MIDIFileHeader;