Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

midifile

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

midifile - npm Package Compare versions

Comparing version 0.1.3 to 0.2.0

dist/MIDIFile.js

66

package.json
{
"name": "midifile",
"description": "Read/write standard MIDI files.",
"version": "0.1.3",
"author": "Nicolas Froidure",
"repository": {
"type": "git",
"url": "http://github.com/nfroidure/MIDIFile.git"
},
"engines": {
"node": "*"
},
"dependencies": {},
"devDependencies": {
"mocha": "~1.7.0"
},
"scripts": {
"test": "mocha tests/*.mocha.js"
},
"keywords": [
"sound",
"music",
"midi",
"file",
"format",
"read",
"write"
],
"main": "./src/MIDIFile"
"name": "midifile",
"description": "Read/write standard MIDI files.",
"version": "0.2.0",
"author": "Nicolas Froidure",
"repository": {
"type": "git",
"url": "http://github.com/nfroidure/MIDIFile.git"
},
"engines": {
"node": "*"
},
"dependencies": {
"midievents": "~0.1.0",
"utf-8": "~0.1.0"
},
"devDependencies": {
"mocha": "~1.7.0",
"grunt": "~0.4.2",
"browserify": "~2.36.1",
"grunt-browserify": "~1.2.12",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-mocha-test": "~0.8.1",
"matchdep": "~0.3.0"
},
"scripts": {
"test": "mocha tests/*.mocha.js"
},
"keywords": [
"sound",
"music",
"midi",
"file",
"format",
"read",
"write"
],
"main": "./src/MIDIFile"
}

@@ -7,5 +7,6 @@ MIDIFile [![Build Status](https://travis-ci.org/nfroidure/MIDIFile.png?branch=master)](https://travis-ci.org/nfroidure/MIDIFile)

MIDIFile can be used int eh folder as global libray or as an AMD module. It can
also be used with NodeJS by installing the
[NPM module](https://npmjs.org/package/midifile) :
MIDIFile can be used either in modern browsers
([pick the last bundle](https://github.com/nfroidure/MIDIFile/blob/master/dist/MIDIFIle.js))
or with NodeJS by installing the following
[NPM module](https://npmjs.org/package/midifile) :
```bash

@@ -19,3 +20,3 @@ npm install midifile

* Check MIDI file structure (using strictMode)
* (Not yet but soon) Write MIDI files
* Write MIDI files (still experimental)

@@ -27,3 +28,3 @@ What it doesn't do

You can find a [Karaoke MIDI player](http://midiwebkaraoke.com) based on
You can also find a [Karaoke MIDI player](http://midiwebkaraoke.com) based on
MIDIFile and a very [trivial MIDI player](http://rest4.org/github/nfroidure/MIDIFile/master/tests/index.html)

@@ -92,4 +93,10 @@ in the test folder.

Contributing
-------------
* Feel free to PR
* If you find a MIDI File the library can't read an if it's under a free, PR
the file in the sounds folder and add tests for him. I'll work on it asap.
License
-------
Copyright Nicolas Froidure 2013. MIT licence.
// MIDIFile : Read (and soon edit) a MIDI file in a given ArrayBuffer
// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define(['./MIDIFileHeader','./MIDIFileTrack','./MIDIEvents'],
function(MIDIFileHeader,MIDIFileTrack, MIDIEvents) {
// START: Module logic start
// Dependencies
var MIDIFileHeader = require('./MIDIFileHeader')
, MIDIFileTrack = require('./MIDIFileTrack')
, MIDIEvents = require('midievents')
, UTF8 = require('utf-8')
;
function MIDIFile(buffer, strictMode) {
if(!(buffer instanceof ArrayBuffer))
// Constructor
function MIDIFile(buffer, strictMode) {
// If not buffer given, creating a new MIDI file
if(!buffer) {
// Creating the content
this.header=new MIDIFileHeader();
this.tracks=[new MIDIFileTrack()];
// if a buffer is provided, parsing him
} else {
if(!(buffer instanceof ArrayBuffer)) {
throw new Error('Invalid buffer received.');
}
// Minimum MIDI file size is a headerChunk size (14bytes)
// and an empty track (8+3bytes)
if(buffer.byteLength<25)
throw new Error('A buffer of a valid MIDI file must have size at least'
+' 25bytes.');
if(buffer.byteLength<25) {
throw new Error('A buffer of a valid MIDI file must have, at least, a'
+' size of 25bytes.');
}
// Reading header

@@ -25,4 +36,5 @@ this.header=new MIDIFileHeader(buffer, strictMode);

// Testing the buffer length
if(strictMode&&curIndex>=buffer.byteLength-1)
if(strictMode&&curIndex>=buffer.byteLength-1) {
throw new Error('Couldn\'t find datas corresponding to the track #'+i+'.');
}
// Creating the track object

@@ -35,160 +47,239 @@ var track=new MIDIFileTrack(buffer, curIndex, strictMode);

// Testing integrity : curIndex should be at the end of the buffer
if(strictMode&&curIndex!=buffer.byteLength)
if(strictMode&&curIndex!=buffer.byteLength) {
throw new Error('It seems that the buffer contains too much datas.');
}
}
}
MIDIFile.prototype.getEvents = function(type, subtype) {
var events, event, playTime=0, filteredEvents=[],
format=this.header.getFormat(),
tickResolution=this.header.getTickResolution();
// Reading events
// if the read is sequential
if(1!==format||1===this.tracks.length) {
for(var i=0, j=this.tracks.length; i<j; i++) {
// reset playtime if format is 2
playTime=(2==format&&playTime?playTime:0);
events=new MIDIEvents.createParser(this.tracks[i].getTrackEvents(),0,false);
// loooping throught events
while(event=events.next()) {
playTime+=(event.delta?(event.delta*tickResolution)/1000:0);
if(event.type===MIDIEvents.EVENT_META) {
// tempo change events
if(event.subtype===MIDIEvents.EVENT_META_SET_TEMPO) {
tickResolution=this.header.getTickResolution(event.tempo);
}
// Events reading helpers
MIDIFile.prototype.getEvents = function(type, subtype) {
var events, event, playTime=0, filteredEvents=[],
format=this.header.getFormat(),
tickResolution=this.header.getTickResolution();
// Reading events
// if the read is sequential
if(1!==format||1===this.tracks.length) {
for(var i=0, j=this.tracks.length; i<j; i++) {
// reset playtime if format is 2
playTime=(2==format&&playTime?playTime:0);
events=new MIDIEvents.createParser(this.tracks[i].getTrackContent(),0,false);
// loooping throught events
while(event=events.next()) {
playTime+=(event.delta?(event.delta*tickResolution)/1000:0);
if(event.type===MIDIEvents.EVENT_META) {
// tempo change events
if(event.subtype===MIDIEvents.EVENT_META_SET_TEMPO) {
tickResolution=this.header.getTickResolution(event.tempo);
}
// push the asked events
if(((!type)||event.type===type)
&&((!subtype)||(event.subtype&&event.subtype===type))) {
event.playTime=playTime;
filteredEvents.push(event);
}
// push the asked events
if(((!type)||event.type===type)
&&((!subtype)||(event.subtype&&event.subtype===type))) {
event.playTime=playTime;
filteredEvents.push(event);
}
}
}
// the read is concurrent
} else {
var trackParsers=[], smallestDelta=-1, i, j;
// Creating parsers
for(i=0, j=this.tracks.length; i<j; i++) {
trackParsers[i]={};
trackParsers[i].parser=new MIDIEvents.createParser(
this.tracks[i].getTrackContent(),0,false);
trackParsers[i].curEvent=trackParsers[i].parser.next();
}
// Filling events
do {
smallestDelta=-1;
// finding the smallest event
for(i=0, j=trackParsers.length; i<j; i++) {
if(trackParsers[i].curEvent) {
if(-1===smallestDelta||trackParsers[i].curEvent.delta
<trackParsers[smallestDelta].curEvent.delta) {
smallestDelta=i;
}
}
}
// the read is concurrent
} else {
var trackParsers=[], smallestDelta=-1, i, j;
// Creating parsers
for(i=0, j=this.tracks.length; i<j; i++) {
trackParsers[i]={};
trackParsers[i].parser=new MIDIEvents.createParser(
this.tracks[i].getTrackEvents(),0,false);
trackParsers[i].curEvent=trackParsers[i].parser.next();
}
// Filling events
do {
smallestDelta=-1;
// finding the smallest event
if(-1!==smallestDelta) {
// removing the delta of previous events
for(i=0, j=trackParsers.length; i<j; i++) {
if(trackParsers[i].curEvent) {
if(-1===smallestDelta||trackParsers[i].curEvent.delta
<trackParsers[smallestDelta].curEvent.delta) {
smallestDelta=i;
}
if(i!==smallestDelta&&trackParsers[i].curEvent) {
trackParsers[i].curEvent.delta-=trackParsers[smallestDelta].curEvent.delta;
}
}
if(-1!==smallestDelta) {
// removing the delta of previous events
for(i=0, j=trackParsers.length; i<j; i++) {
if(i!==smallestDelta&&trackParsers[i].curEvent) {
trackParsers[i].curEvent.delta-=trackParsers[smallestDelta].curEvent.delta;
}
// filling values
event=trackParsers[smallestDelta].curEvent;
playTime+=(event.delta?(event.delta*tickResolution)/1000:0);
if(event.type===MIDIEvents.EVENT_META) {
// tempo change events
if(event.subtype===MIDIEvents.EVENT_META_SET_TEMPO) {
tickResolution=this.header.getTickResolution(event.tempo);
}
// filling values
event=trackParsers[smallestDelta].curEvent;
playTime+=(event.delta?(event.delta*tickResolution)/1000:0);
if(event.type===MIDIEvents.EVENT_META) {
// tempo change events
if(event.subtype===MIDIEvents.EVENT_META_SET_TEMPO) {
tickResolution=this.header.getTickResolution(event.tempo);
}
}
// push midi events
if(((!type)||event.type===type)
&&((!subtype)||(event.subtype&&event.subtype===type))) {
event.playTime=playTime;
event.track=smallestDelta;
filteredEvents.push(event);
}
// getting next event
trackParsers[smallestDelta].curEvent=trackParsers[smallestDelta].parser.next();
}
} while(-1!==smallestDelta);
}
return filteredEvents;
};
// push midi events
if(((!type)||event.type===type)
&&((!subtype)||(event.subtype&&event.subtype===type))) {
event.playTime=playTime;
event.track=smallestDelta;
filteredEvents.push(event);
}
// getting next event
trackParsers[smallestDelta].curEvent=trackParsers[smallestDelta].parser.next();
}
} while(-1!==smallestDelta);
}
return filteredEvents;
};
MIDIFile.prototype.getMidiEvents = function() {
return this.getEvents(MIDIEvents.EVENT_MIDI);
};
MIDIFile.prototype.getMidiEvents = function() {
return this.getEvents(MIDIEvents.EVENT_MIDI);
};
MIDIFile.prototype.getLyrics = function() {
var events=this.getEvents(MIDIEvents.EVENT_META),
texts=[], lyrics=[], event, karaoke=-1, format=this.header.getFormat();
for(var i=0, j=events.length; i<j; i++) {
event=events[i];
// Lyrics
if(event.subtype===MIDIEvents.EVENT_META_LYRICS) {
lyrics.push(event);
// Texts
} else if(event.subtype===MIDIEvents.EVENT_META_TEXT) {
// Ignore special texts
if(event.text[0]=='@') {
if(event.text[1]=='T') {
//console.log('Title : '+event.text.substring(2));
} else if(event.text[1]=='I') {
//console.log('Info : '+event.text.substring(2));
} else if(event.text[1]=='L') {
//console.log('Lang : '+event.text.substring(2));
}
// karaoke text follows, remove all previous text
} else if(0===event.text.indexOf('words')) {
texts.length=0;
//console.log('Word marker found');
// Karaoke texts
} else {
// If playtime is greater than 0
if(0!==event.playTime) {
texts.push(event);
}
MIDIFile.prototype.getLyrics = function() {
var events=this.getEvents(MIDIEvents.EVENT_META),
texts=[], lyrics=[], event, karaoke=-1, format=this.header.getFormat();
for(var i=0, j=events.length; i<j; i++) {
event=events[i];
// Lyrics
if(event.subtype===MIDIEvents.EVENT_META_LYRICS) {
lyrics.push(event);
// Texts
} else if(event.subtype===MIDIEvents.EVENT_META_TEXT) {
// Ignore special texts
if('@'===String.fromCharCode(event.data[0])) {
if('T'===String.fromCharCode(event.data[1])) {
//console.log('Title : '+event.text.substring(2));
} else if('I'===String.fromCharCode(event.data[1])) {
//console.log('Info : '+event.text.substring(2));
} else if('L'===String.fromCharCode(event.data[1])) {
//console.log('Lang : '+event.text.substring(2));
}
// karaoke text follows, remove all previous text
} else if(0===event.data.map(function(c) {
return String.fromCharCode(c);
}).join('').indexOf('words')) {
texts.length=0;
//console.log('Word marker found');
// Karaoke texts
} else {
// If playtime is greater than 0
if(0!==event.playTime) {
texts.push(event);
}
}
}
// Choosing the right lyrics
if(lyrics.length>2) {
return lyrics;
} else if(texts.length) {
return texts;
}
// Choosing the right lyrics
if(lyrics.length>2) {
texts=lyrics;
} else if(!texts.length) {
texts=[];
}
// Convert texts and detect encoding
try {
texts.forEach(function(event) {
event.text=UTF8.getStringFromBytes(event.data,0,event.length,true);
});
} catch (e) {
texts.forEach(function(event) {
event.text=event.data.map(function(c) {
return String.fromCharCode(c);
}).join('');
});
}
return texts;
};
// Basic events reading
MIDIFile.prototype.getTrackEvents = function(index) {
var event, events=[], parser;
if(index>this.tracks.length||index<0) {
throw Error('Invalid track index ('+index+')');
}
parser=new MIDIEvents.createParser(
this.tracks[index].getTrackContent(),0,false);
event=parser.next();
do {
events.push(event);
event=parser.next();
} while(event);
return events;
};
// Basic events writting
MIDIFile.prototype.setTrackEvents = function(index, events) {
var bufferLength, destination;
if(index>this.tracks.length||index<0) {
throw Error('Invalid track index ('+index+')');
}
if((!events)||(!events.length)) {
throw Error('A track must contain at least one event, none given.');
}
bufferLength=MIDIEvents.getRequiredBufferLength(events);
destination = new Uint8Array(bufferLength);
MIDIEvents.writeToTrack(events, destination);
this.tracks[index].setTrackContent(destination);
};
// Remove a track
MIDIFile.prototype.deleteTrack = function(index) {
if(index>this.tracks.length||index<0) {
throw Error('Invalid track index ('+index+')');
}
this.tracks.splice(index,1);
this.header.setTracksCount(this.tracks.length);
};
// Add a track
MIDIFile.prototype.addTrack = function(index) {
if(index>this.tracks.length||index<0) {
throw Error('Invalid track index ('+index+')');
}
var track = new MIDIFileTrack();
if(index==this.tracks.length) {
this.tracks.push(track);
} else {
this.tracks.splice(index,0,track);
}
this.header.setTracksCount(this.tracks.length);
};
// Retrieve the content in a buffer
MIDIFile.prototype.getContent = function() {
var bufferLength, destination, origin, lastOffset=0;
// Calculating the buffer content
// - initialize with the header length
bufferLength=MIDIFileHeader.HEADER_LENGTH;
// - add tracks length
for(var i=0, j=this.tracks.length; i<j; i++) {
bufferLength+=this.tracks[i].getTrackLength()+8;
}
// Creating the destination buffer
destination=new Uint8Array(bufferLength);
// Adding header
origin=new Uint8Array(this.header.datas.buffer,
this.header.datas.byteOffset,
MIDIFileHeader.HEADER_LENGTH);
for(var i=0, j=MIDIFileHeader.HEADER_LENGTH; i<j; i++) {
destination[i]=origin[i];
}
// Adding tracks
for(var k=0, l=this.tracks.length; k<l; k++) {
origin=new Uint8Array(this.tracks[k].datas.buffer,
this.tracks[k].datas.byteOffset,
this.tracks[k].datas.byteLength);
for(var m=0, n=this.tracks[k].datas.byteLength; m<n; m++) {
destination[i++]=origin[m];
}
return [];
};
}
return destination.buffer;
};
// END: Module logic end
// Exports Track/Header constructors
MIDIFile.Header = MIDIFileHeader;
MIDIFile.Track = MIDIFileTrack;
return MIDIFile;
module.exports = MIDIFile;
});})(this,typeof define === 'function' && define.amd ?
// AMD
define :
// NodeJS
(typeof exports === 'object'?function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
module.exports=factory.apply(this, deps.map(function(dep){
return require(dep);
}));
}:
// Global
function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
this.MIDIFile=factory.apply(this, deps.map(function(dep){
return root[dep.substring(dep.lastIndexOf('/')+1)];
}));
}.bind(this)
)
);
// MIDIFileHeader : Read and edit a MIDI header chunk in a given ArrayBuffer
// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
// START: Module logic start
function MIDIFileHeader(buffer, strictMode) {
function MIDIFileHeader(buffer, strictMode) {
// No buffer creating him
if(!buffer) {
var 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,14);
this.datas=new DataView(buffer,0,MIDIFileHeader.HEADER_LENGTH);
// Reading MIDI header chunk

@@ -16,136 +27,115 @@ if(!('M'===String.fromCharCode(this.datas.getUint8(0))

&&'h'===String.fromCharCode(this.datas.getUint8(2))
&&'d'===String.fromCharCode(this.datas.getUint8(3))))
&&'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))
if(6!==this.datas.getUint32(4)) {
throw new Error('Invalid MIDIFileHeader : Chunk length must be 6');
}
}
}
// Static constants
MIDIFileHeader.FRAMES_PER_SECONDS=1;
MIDIFileHeader.TICKS_PER_BEAT=2;
// Static constants
MIDIFileHeader.HEADER_LENGTH=14;
MIDIFileHeader.FRAMES_PER_SECONDS=1;
MIDIFileHeader.TICKS_PER_BEAT=2;
// MIDI file format
MIDIFileHeader.prototype.getFormat=function() {
var 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;
};
// MIDI file format
MIDIFileHeader.prototype.getFormat=function() {
var 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) {
var format=this.datas.getUint16(8);
if(0!==format&&1!==format&&2!==format)
throw new Error('Invalid MIDI format given ('+format+'),'
+' format can be 0, 1 or 2 only.');
return format;
};
MIDIFileHeader.prototype.setFormat=function(format) {
var format=this.datas.getUint16(8);
if(0!==format&&1!==format&&2!==format) {
throw new Error('Invalid MIDI format given ('+format+'),'
+' format can be 0, 1 or 2 only.');
}
return format;
};
// Number of tracks
MIDIFileHeader.prototype.getTracksCount=function() {
return this.datas.getUint16(10);
};
// Number of tracks
MIDIFileHeader.prototype.getTracksCount=function() {
return this.datas.getUint16(10);
};
MIDIFileHeader.prototype.setTracksCount=function(n) {
return this.datas.setUint16(10,n);
};
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
} else {
// Default MIDI tempo is 120bpm, 500ms per beat
tempo=tempo||500000;
return tempo/this.getTicksPerBeat();
}
};
// 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
} else {
// 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;
};
// 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;
};
// 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);
};
MIDIFileHeader.prototype.setTicksPerBeat=function(ticksPerBeat) {
this.datas.setUint16(12,ticksPerBeat&0x7FFF);
};
// Frames per seconds
MIDIFileHeader.prototype.getSMPTEFrames=function() {
var divisionWord=this.datas.getUint16(12), smpteFrames;
if(!(divisionWord&0x8000)) {
throw new Error('Time division is not expressed as frames per seconds.');
}
smpteFrames=divisionWord&0x7F00;
if(smpteFrames!=24&&smpteFrames!=25&&smpteFrames!=29&&smpteFrames!=30) {
throw new Error('Invalid SMPTE frames value ('+smpteFrames+').');
}
return (29===smpteFrames?29.97:smpteFrames);
};
// Frames per seconds
MIDIFileHeader.prototype.getSMPTEFrames=function() {
var divisionWord=this.datas.getUint16(12), smpteFrames;
if(!(divisionWord&0x8000)) {
throw new Error('Time division is not expressed as frames per seconds.');
}
smpteFrames=divisionWord&0x7F00;
if(smpteFrames!=24&&smpteFrames!=25&&smpteFrames!=29&&smpteFrames!=30) {
throw new Error('Invalid SMPTE frames value ('+smpteFrames+').');
}
return (29===smpteFrames?29.97:smpteFrames);
};
MIDIFileHeader.prototype.getTicksPerFrame=function() {
var 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.getTicksPerFrame=function() {
var 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(smpteFrames!=24&&smpteFrames!=25&&smpteFrames!=29.97
&&smpteFrames!=29&&smpteFrames!=30) {
throw new Error('Invalid SMPTE frames value given ('+smpteFrames+').');
}
if(smpteFrames==29.97)
smpteFrames=29;
if(ticksPerFrame<0||ticksPerFrame>0xFF) {
throw new Error('Invalid ticks per frame value given ('+smpteFrames+').');
}
this.datas.setUint8(12,0x80|smpteFrames);
this.datas.setUint8(13,ticksPerFrame);
};
MIDIFileHeader.prototype.setSMTPEDivision=function(smpteFrames,ticksPerFrame) {
if(smpteFrames!=24&&smpteFrames!=25&&smpteFrames!=29.97
&&smpteFrames!=29&&smpteFrames!=30) {
throw new Error('Invalid SMPTE frames value given ('+smpteFrames+').');
}
if(smpteFrames==29.97)
smpteFrames=29;
if(ticksPerFrame<0||ticksPerFrame>0xFF) {
throw new Error('Invalid ticks per frame value given ('+smpteFrames+').');
}
this.datas.setUint8(12,0x80|smpteFrames);
this.datas.setUint8(13,ticksPerFrame);
};
// END: Module logic end
module.exports = MIDIFileHeader;
return MIDIFileHeader;
});})(this,typeof define === 'function' && define.amd ?
// AMD
define :
// NodeJS
(typeof exports === 'object'?function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
module.exports=factory.apply(this, deps.map(function(dep){
return require(dep);
}));
}:
// Global
function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
this.MIDIFileHeader=factory.apply(this, deps.map(function(dep){
return root[dep.substring(dep.lastIndexOf('/')+1)];
}));
}.bind(this)
)
);

@@ -1,17 +0,26 @@

// MIDIFileTrack : Read (and soon edit) a MIDI track chunk in a given ArrayBuffer
// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
// START: Module logic start
function MIDIFileTrack(buffer, start, strictMode) {
// MIDIFileTrack : Read and edit a MIDI track chunk in a given ArrayBuffer
function MIDIFileTrack(buffer, start, strictMode) {
// no buffer, creating him
if(!buffer) {
var 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 Error('Invalid buffer received.');
// Buffer length must size at least like an empty track (8+3bytes)
if(buffer.byteLength-start<11)
if(buffer.byteLength-start<12) {
throw Error('Invalid MIDIFileTrack (0x'+start.toString(16)+') :'
+' Buffer length must size at least 11bytes');
+' Buffer length must size at least 12bytes');
}
// Creating a temporary view to read the track header
this.datas=new DataView(buffer,start,8);
this.datas=new DataView(buffer, start, MIDIFileTrack.HDR_LENGTH);
// Reading MIDI track header chunk

@@ -21,64 +30,75 @@ if(!('M'===String.fromCharCode(this.datas.getUint8(0))

&&'r'===String.fromCharCode(this.datas.getUint8(2))
&&'k'===String.fromCharCode(this.datas.getUint8(3))))
&&'k'===String.fromCharCode(this.datas.getUint8(3)))) {
throw Error('Invalid MIDIFileTrack (0x'+start.toString(16)+') :'
+' MTrk prefix not found');
}
// Reading the track length
var trackLength=this.getTrackLength();
if(buffer.byteLength-start<trackLength)
if(buffer.byteLength-start<trackLength) {
throw Error('Invalid MIDIFileTrack (0x'+start.toString(16)+') :'
+' The track size exceed the buffer length.');
}
// Creating the final DataView
this.datas=new DataView(buffer,start,8+trackLength);
this.datas=new DataView(buffer, start,
MIDIFileTrack.HDR_LENGTH+trackLength);
// Trying to find the end of track event
if(!(0xFF===this.datas.getUint8(8+trackLength-3)
&&0x2F===this.datas.getUint8(8+trackLength-2)
&&0x00===this.datas.getUint8(8+trackLength-1)))
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 Error('Invalid MIDIFileTrack (0x'+start.toString(16)+') :'
+' No track end event found at the expected index'
+' ('+(8+trackLength-1).toString(16)+').');
+' No track end event found at the expected index'
+' ('+(MIDIFileTrack.HDR_LENGTH+trackLength-1).toString(16)+').');
}
}
}
// Track length
MIDIFileTrack.prototype.getTrackLength=function() {
return this.datas.getUint32(4);
};
// Static constants
MIDIFileTrack.HDR_LENGTH=8;
MIDIFileTrack.prototype.setTrackLength=function(trackLength) {
return this.datas.setUint32(trackLength);
};
// Track length
MIDIFileTrack.prototype.getTrackLength=function() {
return this.datas.getUint32(4);
};
// Track events buffer
MIDIFileTrack.prototype.getTrackEvents=function() {
return new DataView(this.datas.buffer,this.datas.byteOffset+8,this.datas.byteLength-8);
};
MIDIFileTrack.prototype.setTrackLength=function(trackLength) {
return this.datas.setUint32(trackLength);
};
// END: Module logic end
// 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);
};
return MIDIFileTrack;
// Set track content
MIDIFileTrack.prototype.setTrackContent=function(dataView) {
// Calculating the track length
var trackLength=dataView.byteLength-dataView.byteOffset;
// Track length must size at least like an empty track (4bytes)
if(trackLength<4) {
throw 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
var origin=new Uint8Array(dataView.buffer, dataView.byteOffset,
dataView.byteLength),
destination=new Uint8Array(this.datas.buffer,
MIDIFileTrack.HDR_LENGTH,
trackLength);
for(var i=0, j=origin.length; i<j; i++) {
destination[i]=origin[i];
}
};
module.exports = MIDIFileTrack;
});})(this,typeof define === 'function' && define.amd ?
// AMD
define :
// NodeJS
(typeof exports === 'object'?function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
module.exports=factory.apply(this, deps.map(function(dep){
return require(dep);
}));
}:
// Global
function (name, deps, factory) {
var root=this;
if(typeof name === 'object') {
factory=deps; deps=name;
}
this.MIDIFileTrack=factory.apply(this, deps.map(function(dep){
return root[dep.substring(dep.lastIndexOf('/')+1)];
}));
}.bind(this)
)
);
var fs=require('fs'), assert=require('assert'),
MIDIFile = require('./../src/MIDIFile.js'),
MIDIFileHeader = require('./../src/MIDIFileHeader.js'),
MIDIEvents = require('./../src/MIDIEvents.js');
MIDIEvents = require('midievents');

@@ -29,3 +29,3 @@ // Helper to get an ArrayBuffer from a NodeJS buffer

assert.equal(mF.tracks[0].getTrackLength(),59);
var events=mF.tracks[0].getTrackEvents();
var events=mF.tracks[0].getTrackContent();
assert.equal(events.buffer.byteLength,81);

@@ -111,3 +111,2 @@ assert.equal(events.byteLength,59);

assert.equal(events[10].param2,0x40);
});

@@ -124,25 +123,25 @@

// Track 1
assert.equal(mF.tracks[0].getTrackLength(),20);
var events=mF.tracks[0].getTrackEvents();
assert.equal(events.buffer.byteLength,118);
assert.equal(events.byteLength,20);
assert.equal(mF.tracks[0].getTrackLength(),19);
var events=mF.tracks[0].getTrackContent();
assert.equal(events.buffer.byteLength,117);
assert.equal(events.byteLength,19);
assert.equal(events.byteOffset,22);
// Track 2
assert.equal(mF.tracks[1].getTrackLength(),16);
var events=mF.tracks[1].getTrackEvents();
assert.equal(events.buffer.byteLength,118);
var events=mF.tracks[1].getTrackContent();
assert.equal(events.buffer.byteLength,117);
assert.equal(events.byteLength,16);
assert.equal(events.byteOffset,50);
assert.equal(events.byteOffset,49);
// Track 3
assert.equal(mF.tracks[2].getTrackLength(),15);
var events=mF.tracks[2].getTrackEvents();
assert.equal(events.buffer.byteLength,118);
var events=mF.tracks[2].getTrackContent();
assert.equal(events.buffer.byteLength,117);
assert.equal(events.byteLength,15);
assert.equal(events.byteOffset,74);
assert.equal(events.byteOffset,73);
// Track 4
assert.equal(mF.tracks[3].getTrackLength(),21);
var events=mF.tracks[3].getTrackEvents();
assert.equal(events.buffer.byteLength,118);
var events=mF.tracks[3].getTrackContent();
assert.equal(events.buffer.byteLength,117);
assert.equal(events.byteLength,21);
assert.equal(events.byteOffset,97);
assert.equal(events.byteOffset,96);
});

@@ -160,3 +159,3 @@

assert.equal(mF.tracks[0].getTrackLength(),24);
var events=mF.tracks[0].getTrackEvents();
var events=mF.tracks[0].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -167,3 +166,3 @@ assert.equal(events.byteLength,24);

assert.equal(mF.tracks[1].getTrackLength(),20);
var events=mF.tracks[1].getTrackEvents();
var events=mF.tracks[1].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -174,3 +173,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[2].getTrackLength(),20);
var events=mF.tracks[2].getTrackEvents();
var events=mF.tracks[2].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -181,3 +180,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[3].getTrackLength(),20);
var events=mF.tracks[3].getTrackEvents();
var events=mF.tracks[3].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -188,3 +187,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[4].getTrackLength(),20);
var events=mF.tracks[4].getTrackEvents();
var events=mF.tracks[4].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -195,3 +194,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[5].getTrackLength(),20);
var events=mF.tracks[5].getTrackEvents();
var events=mF.tracks[5].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -202,3 +201,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[6].getTrackLength(),20);
var events=mF.tracks[6].getTrackEvents();
var events=mF.tracks[6].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -209,3 +208,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[7].getTrackLength(),20);
var events=mF.tracks[7].getTrackEvents();
var events=mF.tracks[7].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -216,3 +215,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[8].getTrackLength(),20);
var events=mF.tracks[8].getTrackEvents();
var events=mF.tracks[8].getTrackContent();
assert.equal(events.buffer.byteLength,270);

@@ -232,3 +231,3 @@ assert.equal(events.byteLength,20);

assert.equal(mF.tracks[0].getTrackLength(),109);
var events=mF.tracks[0].getTrackEvents();
var events=mF.tracks[0].getTrackContent();
assert.equal(events.buffer.byteLength,131);

@@ -255,3 +254,3 @@ assert.equal(events.byteLength,109);

var mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/SampleMountainman.mid')));
fs.readFileSync(__dirname+'/../sounds/Avgvst/MountainMan.mid')));
assert.equal(mF.header.getFormat(),0);

@@ -263,3 +262,3 @@ assert.equal(mF.header.getTracksCount(),1);

assert.equal(mF.tracks[0].getTrackLength(),47411);
var events=mF.tracks[0].getTrackEvents();
var events=mF.tracks[0].getTrackContent();
assert.equal(events.buffer.byteLength,47433);

@@ -365,1 +364,142 @@ assert.equal(events.byteLength,47411);

});
describe('Calculating required buffer length ', function() {
it("Should work with events of Format 0 MIDI file", function() {
var mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/MIDIOkFormat0.mid')));
var events=mF.getTrackEvents(0);
// -2 bytes for running statuses
assert.equal(MIDIEvents.getRequiredBufferLength(events)-2,
mF.tracks[0].getTrackLength());
});
it("Should work with events of Format 1 MIDI file", function() {
var mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/MIDIOkFormat1.mid')));
var events=mF.getTrackEvents(0);
assert.equal(MIDIEvents.getRequiredBufferLength(events),
mF.tracks[0].getTrackLength());
events=mF.getTrackEvents(1);
// -1 byte for running status
assert.equal(MIDIEvents.getRequiredBufferLength(events)-1,
mF.tracks[1].getTrackLength());
events=mF.getTrackEvents(2);
// -1 byte for running statuses
assert.equal(MIDIEvents.getRequiredBufferLength(events)-1,
mF.tracks[2].getTrackLength());
events=mF.getTrackEvents(3);
// -3 bytes for running statuses
assert.equal(MIDIEvents.getRequiredBufferLength(events)-3,
mF.tracks[3].getTrackLength());
});
it("Should work with events of Format 1 MIDI file", function() {
var mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/MIDIOkFormat1-lyrics.mid')));
var events=mF.getTrackEvents(0);
assert.equal(MIDIEvents.getRequiredBufferLength(events),
mF.tracks[0].getTrackLength());
});
it("Should work with events of Format 2 MIDI file", function() {
var mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/MIDIOkFormat2.mid')));
var events=mF.getTrackEvents(0);
assert.equal(MIDIEvents.getRequiredBufferLength(events),
mF.tracks[0].getTrackLength());
});
});
describe('MIDI file', function(){
var mF, events;
it("creation should work", function() {
mF=new MIDIFile();
});
it("ticks per beat setting should work", function() {
mF.header.setTicksPerBeat(128);
});
it("Track addition should work", function() {
mF.addTrack(1); // at the end by specifying an index
mF.addTrack(); // at the end
mF.addTrack(1); // in the middle
assert(mF.tracks.length==mF.header.getTracksCount());
assert(mF.tracks.length==4);
});
it("Track deletion should work", function() {
mF.deleteTrack(1);
assert(mF.tracks.length==mF.header.getTracksCount());
assert(mF.tracks.length==3);
});
it("Track events basic reading should work", function() {
events=mF.getTrackEvents(1);
assert(events.length==1);
});
});
describe('MIDI file reencryption loop should work', function(){
function encodingLoop(filePath) {
var mF, newMF, mFEvents, newMFEvents;
mF=new MIDIFile(toArrayBuffer(
fs.readFileSync(__dirname+'/../sounds/'+filePath)));
mF.tracks.forEach(function(track, index) {
var events=mF.getTrackEvents(index);
mF.setTrackEvents(index, events);
});
newMF=new MIDIFile(mF.getContent());
mFEvents=mF.getEvents();
newMFEvents=mF.getEvents();
assert.equal(mFEvents.length,newMFEvents.length);
// Testing each events
mFEvents.forEach(function(event, index) {
assert.deepEqual(event, newMFEvents[index]);
});
}
function dirEncodingLoop(dirName) {
fs.readdirSync(__dirname+'/../sounds/'+dirName).forEach(function(file) {
if('README.md'!==file) {
//it('named '+file, function() {
encodingLoop(dirName+'/'+file);
//});
}
});
}
it("with the format 0 MIDI file", function() {
encodingLoop('/../sounds/MIDIOkFormat0.mid');
});
it("with the format 1 MIDI file", function() {
encodingLoop('/../sounds/MIDIOkFormat1.mid');
});
it("with the format 2 MIDI file", function() {
encodingLoop('/../sounds/MIDIOkFormat2.mid');
});
it("with the Avgvst MIDI files", function() {
dirEncodingLoop('Avgvst');
});
it("with the Avgvst MIDI files", function() {
dirEncodingLoop('GerardoMarset');
});
it("with the Avgvst MIDI files", function() {
dirEncodingLoop('HorrorPen');
});
it("with the Avgvst MIDI files", function() {
dirEncodingLoop('Yubatake');
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc