nfroidure/midifile

View on GitHub
src/MIDIFileTrack.js

Summary

Maintainability
C
1 day
Test Coverage
'use strict';

// MIDIFileTrack : Read and edit a MIDI track chunk in a given ArrayBuffer
function MIDIFileTrack(buffer, start) {
  let a;
  let trackLength;

  // no buffer, creating him
  if (!buffer) {
    a = new Uint8Array(12);
    // Adding the empty track header (MTrk)
    a[0] = 0x4d;
    a[1] = 0x54;
    a[2] = 0x72;
    a[3] = 0x6b;
    // Adding the empty track size (4)
    a[4] = 0x00;
    a[5] = 0x00;
    a[6] = 0x00;
    a[7] = 0x04;
    // Adding the track end event
    a[8] = 0x00;
    a[9] = 0xff;
    a[10] = 0x2f;
    a[11] = 0x00;
    // Saving the buffer
    this.datas = new DataView(a.buffer, 0, MIDIFileTrack.HDR_LENGTH + 4);
    // parsing the given buffer
  } else {
    if (!(buffer instanceof ArrayBuffer)) {
      throw new Error('Invalid buffer received.');
    }
    // Buffer length must size at least like an  empty track (8+3bytes)
    if (12 > buffer.byteLength - start) {
      throw new Error(
        'Invalid MIDIFileTrack (0x' +
          start.toString(16) +
          ') :' +
          ' Buffer length must size at least 12bytes'
      );
    }
    // Creating a temporary view to read the track header
    this.datas = new DataView(buffer, start, MIDIFileTrack.HDR_LENGTH);
    // Reading MIDI track header chunk
    if (
      !(
        'M' === String.fromCharCode(this.datas.getUint8(0)) &&
        'T' === String.fromCharCode(this.datas.getUint8(1)) &&
        'r' === String.fromCharCode(this.datas.getUint8(2)) &&
        'k' === String.fromCharCode(this.datas.getUint8(3))
      )
    ) {
      throw new Error(
        'Invalid MIDIFileTrack (0x' +
          start.toString(16) +
          ') :' +
          ' MTrk prefix not found'
      );
    }
    // Reading the track length
    trackLength = this.getTrackLength();
    if (buffer.byteLength - start < trackLength) {
      throw new Error(
        'Invalid MIDIFileTrack (0x' +
          start.toString(16) +
          ') :' +
          ' The track size exceed the buffer length.'
      );
    }
    // Creating the final DataView
    this.datas = new DataView(
      buffer,
      start,
      MIDIFileTrack.HDR_LENGTH + trackLength
    );
    // Trying to find the end of track event
    if (
      !(
        0xff ===
          this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + (trackLength - 3)) &&
        0x2f ===
          this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + (trackLength - 2)) &&
        0x00 ===
          this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + (trackLength - 1))
      )
    ) {
      throw new Error(
        'Invalid MIDIFileTrack (0x' +
          start.toString(16) +
          ') :' +
          ' No track end event found at the expected index' +
          ' (' +
          (MIDIFileTrack.HDR_LENGTH + (trackLength - 1)).toString(16) +
          ').'
      );
    }
  }
}

// Static constants
MIDIFileTrack.HDR_LENGTH = 8;

// Track length
MIDIFileTrack.prototype.getTrackLength = function() {
  return this.datas.getUint32(4);
};

MIDIFileTrack.prototype.setTrackLength = function(trackLength) {
  return this.datas.setUint32(4, trackLength);
};

// Read track contents
MIDIFileTrack.prototype.getTrackContent = function() {
  return new DataView(
    this.datas.buffer,
    this.datas.byteOffset + MIDIFileTrack.HDR_LENGTH,
    this.datas.byteLength - MIDIFileTrack.HDR_LENGTH
  );
};

// Set track content
MIDIFileTrack.prototype.setTrackContent = function(dataView) {
  let origin;
  let destination;
  let i;
  let j;
  // Calculating the track length
  const trackLength = dataView.byteLength - dataView.byteOffset;

  // Track length must size at least like an  empty track (4bytes)
  if (4 > trackLength) {
    throw new Error('Invalid track length, must size at least 4bytes');
  }
  this.datas = new DataView(
    new Uint8Array(MIDIFileTrack.HDR_LENGTH + trackLength).buffer
  );
  // Adding the track header (MTrk)
  this.datas.setUint8(0, 0x4d); // M
  this.datas.setUint8(1, 0x54); // T
  this.datas.setUint8(2, 0x72); // r
  this.datas.setUint8(3, 0x6b); // k
  // Adding the track size
  this.datas.setUint32(4, trackLength);
  // Copying the content
  origin = new Uint8Array(
    dataView.buffer,
    dataView.byteOffset,
    dataView.byteLength
  );
  destination = new Uint8Array(
    this.datas.buffer,
    MIDIFileTrack.HDR_LENGTH,
    trackLength
  );
  for (i = 0, j = origin.length; i < j; i++) {
    destination[i] = origin[i];
  }
};

module.exports = MIDIFileTrack;