Comparing version 0.1.3 to 0.2.0
{ | ||
"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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 29 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 2 instances in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1370375
119
2126
99
4
2
8
34
+ Addedmidievents@~0.1.0
+ Addedutf-8@~0.1.0
+ Addedmidievents@0.1.0(transitive)
+ Addedstring.fromcodepoint@0.2.1(transitive)
+ Addedstring.prototype.codepointat@0.2.1(transitive)
+ Addedutf-8@0.1.2(transitive)