react-orchestra
Version:
A toolbox to build interactive and smart instruments on the web and mobile.
406 lines (353 loc) • 15.3 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
import midiFileParser from 'midi-file-parser';
import MidiWriterJS from 'midi-writer-js';
import _ from 'lodash';
import INSTRUMENT_MIDI_MAPPING from './constants/INSTRUMENT_MIDI_MAPPING';
var minutesInMS = 60000000;
var MidiIO = function () {
MidiIO.getWriter = function getWriter() {
return MidiWriterJS;
};
MidiIO.getUniqueFromMidiNotes = function getUniqueFromMidiNotes(notes) {
var set = new Set();
notes.forEach(function (note) {
set.add(note.payload.noteName);
});
return Array.from(set);
};
MidiIO.midiNoteNumberToName = function midiNoteNumberToName(midi) {
var scaleIndexToNote = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
var octave = Math.floor(midi / 12) - 1;
var note = midi % 12;
return scaleIndexToNote[note] + octave;
};
MidiIO.urlToJSON = function urlToJSON(midiURL) {
return MidiIO.urlToBinaryString(midiURL).then(function (binaryStringMidi) {
return MidiIO.binaryStringToJSON(binaryStringMidi);
});
};
MidiIO.getAllMetadata = function getAllMetadata(parsedMidi, tracks) {
var metaTrack = parsedMidi.tracks[0];
var indexedMeta = metaTrack.reduce(function (prev, current, i) {
var _Object$assign;
var updated = Object.assign({}, prev, (_Object$assign = {}, _Object$assign[current.type + '_' + current.subtype] = current, _Object$assign));
return updated;
}, {});
var meta_setTempo = indexedMeta.meta_setTempo;
var meta_keySignature = indexedMeta.meta_keySignature;
var meta_timeSignature = indexedMeta.meta_timeSignature;
var meta_endOfTrack = indexedMeta.meta_endOfTrack;
var ticksPerBeat = parsedMidi.header.ticksPerBeat;
var microsecondsPerBeat = meta_setTempo.microsecondsPerBeat;
var millisecondsPerTick = MidiIO.getMillisecondsPerTick(microsecondsPerBeat, ticksPerBeat);
var tempo = meta_setTempo;
var timeSignature = meta_timeSignature;
var keySignature = meta_keySignature;
var endOfTrack = meta_endOfTrack;
var timeSignatureNumerator = timeSignature.numerator;
var timeSignatureDenominator = timeSignature.denominator;
var timeSignatureMetronome = timeSignature.metronome;
var timeSignatureThirtyseconds = timeSignature.thirtyseconds;
var instrumentNumbers = tracks.map(function (track) {
return track.programChange[0].programNumber;
}); // [0].programNumber
var instrumentNames = instrumentNumbers.map(function (instrumentNumber) {
return Object.keys(INSTRUMENT_MIDI_MAPPING).find(function (instrumentKey) {
return INSTRUMENT_MIDI_MAPPING[instrumentKey] === instrumentNumber;
});
});
var _MidiIO$getBPMData = MidiIO.getBPMData(microsecondsPerBeat, ticksPerBeat, timeSignature);
var BPM = _MidiIO$getBPMData.BPM;
var metaObject = {
tempo: tempo,
keySignature: keySignature,
timeSignature: timeSignature,
timeSignatureNumerator: timeSignatureNumerator,
timeSignatureDenominator: timeSignatureDenominator,
timeSignatureMetronome: timeSignatureMetronome,
timeSignatureThirtyseconds: timeSignatureThirtyseconds,
endOfTrack: endOfTrack,
trackCount: tracks.length,
microsecondsPerBeat: microsecondsPerBeat,
instrumentNumbers: instrumentNumbers,
instrumentNames: instrumentNames,
millisecondsPerTick: millisecondsPerTick,
ticksPerBeat: ticksPerBeat,
BPM: BPM
};
return metaObject;
};
MidiIO.getAllTracks = function getAllTracks(parsedMidi) {
var tracks = parsedMidi.tracks.filter(function (track, i) {
return i >= 1;
});
var indexedTracks = tracks.map(function (track, i) {
var indexedTrack = track.reduce(function (prev, current) {
var currentIndex = current.type + '_' + current.subtype;
var updated = Object.assign({}, prev);
if (!updated[currentIndex]) {
updated[currentIndex] = [];
}
updated[currentIndex].push(current);
return updated;
}, {});
var instrumentNumber = indexedTrack.channel_programChange[0].programNumber;
var instrumentName = Object.keys(INSTRUMENT_MIDI_MAPPING).find(function (instrumentKey) {
return INSTRUMENT_MIDI_MAPPING[instrumentKey] === instrumentNumber;
});
return {
controller: indexedTrack.channel_controller,
programChange: indexedTrack.channel_programChange,
trackname: indexedTrack.meta_trackName,
noteOn: indexedTrack.channel_noteOn,
noteOnValid: indexedTrack.channel_noteOn.filter(function (event) {
return event.deltaTime > 0;
}),
noteOnInvalid: indexedTrack.channel_noteOn.filter(function (event) {
return event.deltaTime === 0;
}),
noteOff: indexedTrack.channel_noteOff,
endOfTrack: indexedTrack.meta_endOfTrack,
noteCount: indexedTrack.channel_noteOn.filter(function (event) {
return event.deltaTime > 0;
}).length,
instrumentNumber: instrumentNumber,
instrumentName: instrumentName
};
});
var meta = MidiIO.getAllMetadata(parsedMidi, indexedTracks);
return {
meta: meta,
musicTracks: indexedTracks
};
};
MidiIO.getInstrumentNameFromMidiTrack = function getInstrumentNameFromMidiTrack(track) {
var instrument = track.find(function (event) {
return event.type === 'channel' && event.subtype === 'programChange';
});
// alert(`instrumentNumber ${instrument}`);
var instrumentNumber = instrument.programNumber;
// alert(`instrumentNumber ${instrumentNumber}`);
var instrumentName = Object.keys(INSTRUMENT_MIDI_MAPPING).find(function (instrumentKey) {
return INSTRUMENT_MIDI_MAPPING[instrumentKey] === instrumentNumber;
});
return instrumentName;
};
MidiIO.getInstrumentNumberFromMidiTrack = function getInstrumentNumberFromMidiTrack(track) {
var instrument = track.find(function (event) {
return event.type === 'channel' && event.subtype === 'programChange';
});
var instrumentNumber = void 0;
try {
instrumentNumber = instrument.programNumber;
} catch (err) {
instrumentNumber = 0;
console.warn('COULDN\'T FIND INSTRUMENT NUMBER ' + instrumentNumber + ' defaulting to ' + instrumentNumber);
}
return instrumentNumber;
};
MidiIO.binaryStringToJSON = function binaryStringToJSON(binaryStringMidi) {
var parsedMidi = midiFileParser(binaryStringMidi);
return parsedMidi;
};
MidiIO.getNoteOnEvents = function getNoteOnEvents(track) {
var noteOnEvents = track.filter(function (event) {
return event.type === 'channel' && event.subtype === 'noteOn';
});
var invalidNoteOnEvents = noteOnEvents.filter(function (event) {
return event.deltaTime === 0;
});
var validNoteOnEvents = noteOnEvents.filter(function (event) {
return event.deltaTime > 0;
});
return { noteOnEvents: noteOnEvents, validNoteOnEvents: validNoteOnEvents, invalidNoteOnEvents: invalidNoteOnEvents };
};
MidiIO.getNoteOffEvents = function getNoteOffEvents(track) {
var noteOffEvents = track.filter(function (event) {
return event.type === 'channel' && event.subtype === 'noteOff';
});
var invalidNoteOffEvents = noteOffEvents.filter(function (event) {
return event.deltaTime === 0;
});
var validNoteOffEvents = noteOffEvents.filter(function (event) {
return event.deltaTime > 0;
});
return { noteOffEvents: noteOffEvents, validNoteOffEvents: validNoteOffEvents, invalidNoteOffEvents: invalidNoteOffEvents };
};
MidiIO.getTempoFromTrack = function getTempoFromTrack(track) {
var setTempoEvent = track.find(function (event) {
return event.type === 'meta' && event.subtype === 'setTempo';
});
var deltaTime = setTempoEvent.deltaTime;
var microsecondsPerBeat = setTempoEvent.microsecondsPerBeat;
return { deltaTime: deltaTime, microsecondsPerBeat: microsecondsPerBeat };
};
MidiIO.getMetadataFromTrack = function getMetadataFromTrack(track) {
var _ref;
var microsecondsPerBeat = void 0,
keySignature = void 0,
timeSignature = void 0,
endOfTrack = void 0;
track.filter(function (event) {
return event.type === 'meta';
}).forEach(function (event) {
switch (event.subtype) {
case 'setTempo':
microsecondsPerBeat = event.microsecondsPerBeat;
break;
case 'keySignature':
keySignature = {
key: event.key,
scale: event.scale
};
break;
case 'timeSignature':
timeSignature = {
numerator: event.numerator,
denominator: event.denominator,
metronome: event.metronome,
thirtyseconds: event.thirtyseconds
};
break;
case 'endOfTrack':
endOfTrack = {};
default:
return {
name: event.subtype,
payload: _extends({}, event)
};
}
});
return _ref = {
microsecondsPerBeat: microsecondsPerBeat,
keySignature: keySignature,
timeSignature: timeSignature,
endOfTrack: endOfTrack
}, _ref[event.subtype] = _extends({}, event), _ref;
};
MidiIO.urlToBinaryString = function urlToBinaryString(midiURL) {
return Promise.resolve(true).then(function () {
return fetch(midiURL, {
method: 'GET',
headers: {
Accept: 'audio/mid'
}
});
}).then(function (response) {
return response.blob();
}).then(function (response) {
return new Promise(function (resolve) {
var reader = new FileReader();
reader.onloadend = function () {
var blob = reader.result;
resolve(blob);
};
reader.readAsBinaryString(response);
});
}).catch(function (err) {
console.warn('ERROR IN URLTOBINARYSTRING, ' + JSON.stringify(err));
});
};
MidiIO.noteOffEventToNote = function noteOffEventToNote(noteOffEvent, noteInstrumentName, previousEndTime, msPerTick) {
var noteNumber = noteOffEvent.noteNumber;
var deltaTime = noteOffEvent.deltaTime;
var noteName = MidiIO.midiNoteNumberToName(noteNumber);
var startTimeInMS = previousEndTime;
var durationInMS = deltaTime * msPerTick;
var endTimeInMS = startTimeInMS + durationInMS;
return { noteNumber: noteNumber, noteName: noteName, startTimeInMS: startTimeInMS, durationInMS: durationInMS, endTimeInMS: endTimeInMS, noteInstrumentName: noteInstrumentName, deltaTime: deltaTime, msPerTick: msPerTick };
};
MidiIO.getMillisecondsPerTick = function getMillisecondsPerTick(microsecondsPerBeat, ticksPerBeat) {
var secondsPerBeat = microsecondsPerBeat / 1000000;
var secondsPerTick = secondsPerBeat / ticksPerBeat;
var millisecondsPerTick = secondsPerTick * 1000;
return millisecondsPerTick;
};
MidiIO.getBPMData = function getBPMData(microSecondsPerBeat, ticksPerBeat, timeSignature) {
// const ticksPerBeat = parsedMidi.header.ticksPerBeat;
var timeSignatureNumerator = timeSignature.numerator;
var timeSignatureDenominator = timeSignature.denominator;
var secondsPerBeat = microSecondsPerBeat / 1000000;
var secondsPerTick = secondsPerBeat / ticksPerBeat;
var BPM = minutesInMS / microSecondsPerBeat * (timeSignatureDenominator / timeSignatureNumerator);
// const millisecondsPerTick = secondsPerTick * 1000;
return { BPM: BPM };
};
MidiIO.bpmToMSPerBeat = function bpmToMSPerBeat(BPM) {
var timeSignatureNumerator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 4;
var timeSignatureDenominator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4;
return minutesInMS / BPM * (timeSignatureNumerator / timeSignatureDenominator);
};
MidiIO.prototype.setBPM = function setBPM() {
var BPM = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 60;
this.millisecondsPerTick = MidiIO.getMillisecondsPerTick(MidiIO.bpmToMSPerBeat(BPM, this.timeSignatureNumerator, this.timeSignatureDenominator), this.ticksPerBeat);
this.BPM = BPM;
};
function MidiIO(userSettings) {
_classCallCheck(this, MidiIO);
this.settings = _.defaultsDeep(userSettings, {
midiURL: null,
parsedMidi: null,
BPM: -1
});
this.isInitialised = false;
}
MidiIO.prototype.parseMidiOld = function parseMidiOld(parsedMidi) {
var bpm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
this.parsedMidi = parsedMidi;
this.ticksPerBeat = parsedMidi.header.ticksPerBeat;
var trackMetadata = MidiIO.getMetadataFromTrack(parsedMidi.tracks[0]);
var microsecondsPerBeat = trackMetadata.microsecondsPerBeat;
var keySignature = trackMetadata.keySignature;
var timeSignature = trackMetadata.timeSignature;
var endOfTrack = trackMetadata.endOfTrack;
this.noteOnEvents = MidiIO.getNoteOnEvents(parsedMidi.tracks[1]);
this.noteOffEvents = MidiIO.getNoteOffEvents(parsedMidi.tracks[1]);
this.millisecondsPerTick = MidiIO.getMillisecondsPerTick(microsecondsPerBeat, this.ticksPerBeat);
// this.secondsPerTick = this.millisecondsPerTick * 10;
try {
this.mainInstrumentNumber = MidiIO.getInstrumentNumberFromMidiTrack(parsedMidi.tracks[1]);
} catch (err) {}
try {
this.mainInstrumentName = MidiIO.getInstrumentNameFromMidiTrack(parsedMidi.tracks[1]);
} catch (err) {
this.mainInstrumentName = 'acoustic_grand_piano';
}
// this.mainInstrumentName = MidiIO.getInstrumentNameFromMidiTrack(parsedMidi.tracks[1]);
this.isInitialised = true;
this.timeSignature = timeSignature;
this.timeSignatureNumerator = timeSignature.numerator;
this.timeSignatureDenominator = timeSignature.denominator;
var _MidiIO$getBPMData2 = MidiIO.getBPMData(microsecondsPerBeat, this.ticksPerBeat, timeSignature);
var BPM = _MidiIO$getBPMData2.BPM;
this.BPM = BPM;
if (bpm !== -1) {
this.setBPM(bpm);
}
return this;
};
MidiIO.parseMidi = function parseMidi(url) {
return MidiIO.urlToJSON(url).then(function (parsedMidi) {
var _MidiIO$getAllTracks = MidiIO.getAllTracks(parsedMidi);
var meta = _MidiIO$getAllTracks.meta;
var musicTracks = _MidiIO$getAllTracks.musicTracks;
return {
meta: meta,
musicTracks: musicTracks
};
});
};
MidiIO.prototype.init = function init() {
var _this = this;
if (this.settings.midiURL) {
return MidiIO.urlToJSON(this.settings.midiURL).then(function (parsedMidi) {
// return this.getAllTracks,
return _this.parseMidi(parsedMidi, _this.settings.BPM);
}).catch();
}
return Promise.resolve(false);
};
return MidiIO;
}();
export { MidiIO as default };