react-orchestra
Version:
A toolbox to build interactive and smart instruments on the web and mobile.
1,260 lines (1,233 loc) • 54.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = _interopDefault(require('react'));
var classnames = _interopDefault(require('classnames'));
var midiFileParser = _interopDefault(require('midi-file-parser'));
var MidiWriterJS = _interopDefault(require('midi-writer-js'));
var defaultsDeep = _interopDefault(require('lodash/defaultsDeep'));
var localforage = _interopDefault(require('localforage'));
var ReactKeyMaster = _interopDefault(require('react-keymaster'));
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var callIfExists = function (fnc) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (fnc) {
return fnc.apply(void 0, args);
}
return null;
};
var Instrument = (function (_super) {
__extends(Instrument, _super);
function Instrument() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.loadingNotesCounter = 0;
_this.state = {
isLoaded: false
};
_this.onStartPlaying = function (noteName) {
callIfExists(_this.props.onStartPlaying, noteName);
};
_this.onStopPlaying = function (noteName) {
callIfExists(_this.props.onStopPlaying, noteName);
};
_this.onNoteLoaded = function (instrumentName, noteName) {
_this.loadingNotesCounter += 1;
var children = _this.props.children;
var noteCount = children ? children.length : 0;
if (noteCount === _this.loadingNotesCounter) {
_this.setState({ isLoaded: true });
callIfExists(_this.props.onInstrumentLoaded, _this.props.name, noteName);
}
};
return _this;
}
Instrument.prototype.render = function () {
var _this = this;
var loader = this.state.isLoaded ? null : this.props.loader ? (this.props.loader) : (React.createElement("div", null,
React.createElement("span", null, "Loading Instrument \uD83D\uDE9A \uD83D\uDE9A \uD83D\uDE9A")));
return (React.createElement("div", { style: this.props.style, className: classnames({ "ro-instrument-loading": !this.state.isLoaded }, { "ro-instrument-loaded": this.state.isLoaded }) }, React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, {
instrumentName: child.props.instrumentName || _this.props.name,
onStartPlaying: _this.onStartPlaying,
onStopPlaying: _this.onStopPlaying,
onNoteLoaded: _this.onNoteLoaded,
interactive: _this.props.interactive || true
});
})));
};
Instrument.defaultProps = {
name: "acoustic_grand_piano"
};
return Instrument;
}(React.Component));
var getAudioContext = function () {
var AudioContext = window.AudioContext ||
window.webkitAudioContext ||
false;
if (!AudioContext) {
console.warn("Sorry but the WebAudio API is not supported on this browser. Please consider using Chrome or Safari for the best experience ");
return {};
}
return new AudioContext();
};
var audioContext = getAudioContext();
var INSTRUMENT_MIDI_MAPPING = {
accordion: 21,
acoustic_bass: 32,
acoustic_grand_piano: 0,
acoustic_guitar_nylon: 24,
acoustic_guitar_steel: 25,
agogo: 113,
alto_sax: 65,
applause: 126,
bagpipe: 109,
banjo: 105,
baritone_sax: 67,
bassoon: 70,
bird_tweet: 123,
blown_bottle: 76,
brass_section: 61,
breath_noise: 121,
bright_acoustic_piano: 1,
celesta: 8,
cello: 42,
choir_aahs: 52,
church_organ: 19,
clarinet: 71,
clavinet: 7,
contrabass: 43,
distortion_guitar: 30,
drawbar_organ: 16,
dulcimer: 15,
electric_bass_finger: 33,
electric_bass_pick: 34,
electric_grand_piano: 2,
electric_guitar_clean: 27,
electric_guitar_jazz: 26,
electric_guitar_muted: 28,
electric_piano_1: 4,
electric_piano_2: 5,
english_horn: 69,
fiddle: 110,
flute: 73,
french_horn: 60,
fretless_bass: 35,
fx_1_rain: 96,
fx_2_soundtrack: 97,
fx_3_crystal: 98,
fx_4_atmosphere: 99,
fx_5_brightness: 100,
fx_6_goblins: 101,
fx_7_echoes: 101,
fx_8_scifi: 103,
glockenspiel: 9,
guitar_fret_noise: 120,
guitar_harmonics: 31,
gunshot: 127,
harmonica: 22,
harpsichord: 6,
helicopter: 125,
honkytonk_piano: 3,
kalimba: 108,
koto: 107,
lead_1_square: 80,
lead_2_sawtooth: 81,
lead_3_calliope: 82,
lead_4_chiff: 83,
lead_5_charang: 84,
lead_6_voice: 85,
lead_7_fifths: 86,
lead_8_bass__lead: 87,
marimba: 12,
melodic_tom: 117,
music_box: 10,
muted_trumpet: 59,
oboe: 68,
ocarina: 79,
orchestra_hit: 55,
orchestral_harp: 46,
overdriven_guitar: 29,
pad_1_new_age: 88,
pad_2_warm: 89,
pad_3_polysynth: 90,
pad_4_choir: 91,
pad_5_bowed: 92,
pad_6_metallic: 93,
pad_7_halo: 94,
pad_8_sweep: 95,
pan_flute: 75,
percussive_organ: 17,
piccolo: 72,
pizzicato_strings: 45,
recorder: 74,
reed_organ: 20,
reverse_cymbal: 119,
rock_organ: 18,
seashore: 122,
shakuhachi: 77,
shamisen: 106,
shanai: 111,
sitar: 104,
slap_bass_1: 36,
slap_bass_2: 37,
soprano_sax: 64,
steel_drums: 114,
string_ensemble_1: 48,
string_ensemble_2: 49,
synth_bass_1: 38,
synth_bass_2: 39,
synth_brass_1: 62,
synth_brass_2: 63,
synth_choir: 91,
synth_drum: 118,
synth_strings_1: 50,
synth_strings_2: 51,
taiko_drum: 116,
tango_accordion: 23,
telephone_ring: 124,
tenor_sax: 66,
timpani: 47,
tinkle_bell: 112,
tremolo_strings: 44,
trombone: 57,
trumpet: 56,
tuba: 58,
tubular_bells: 14,
vibraphone: 11,
viola: 41,
violin: 40,
voice_oohs: 53,
whistle: 78,
woodblock: 115,
xylophone: 13
};
var _this = undefined;
var minutesInMS = 60000000;
var getWriter = function () { return MidiWriterJS; };
var getUniqueFromMidiNotes = function (notes) {
var set = new Set();
notes.forEach(function (note) {
set.add(note.noteName);
});
return Array.from(set);
};
var getAllMetadata = function (parsedMidi, tracks) {
var metaTrack = parsedMidi.tracks[0];
var indexedMeta = metaTrack.reduce(function (prev, current, i) {
var _a;
var updated = Object.assign({}, prev, (_a = {},
_a[current.type + "_" + current.subtype] = current,
_a));
return updated;
}, {});
var meta_setTempo = indexedMeta.meta_setTempo, meta_keySignature = indexedMeta.meta_keySignature, meta_timeSignature = indexedMeta.meta_timeSignature, meta_endOfTrack = indexedMeta.meta_endOfTrack;
var ticksPerBeat = parsedMidi.header.ticksPerBeat;
var microsecondsPerBeat = meta_setTempo.microsecondsPerBeat;
var millisecondsPerTick = 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; });
var instrumentNames = instrumentNumbers.map(function (instrumentNumber) {
return Object.keys(INSTRUMENT_MIDI_MAPPING).find(function (instrumentKey) {
return INSTRUMENT_MIDI_MAPPING[instrumentKey] === instrumentNumber;
});
});
var BPM = getBPMData(microsecondsPerBeat, ticksPerBeat, timeSignature).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;
};
var midiNoteNumberToName = function (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;
};
var urlToJSON = function (midiURL) {
return urlToBinaryString(midiURL).then(function (binaryStringMidi) {
return binaryStringToJSON(binaryStringMidi);
});
};
var getAllTracks = function (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;
if (!prev[currentIndex]) {
prev[currentIndex] = [];
}
prev[currentIndex].push(current);
return prev;
}, {});
var programChangeEvent = indexedTrack
.channel_programChange[0];
var instrumentNumber = programChangeEvent.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 = getAllMetadata(parsedMidi, indexedTracks);
return {
meta: meta,
musicTracks: indexedTracks
};
};
var getInstrumentNameFromMidiTrack = function (track) {
var instrument = track.find(function (event) { return event.type === "channel" && event.subtype === "programChange"; });
var instrumentNumber = instrument.programNumber;
var instrumentName = Object.keys(INSTRUMENT_MIDI_MAPPING).find(function (instrumentKey) { return INSTRUMENT_MIDI_MAPPING[instrumentKey] === instrumentNumber; });
return instrumentName;
};
var binaryStringToJSON = function (binaryStringMidi) {
var parsedMidi = midiFileParser(binaryStringMidi);
return parsedMidi;
};
var getNoteOnEvents = function (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 };
};
var getNoteOffEvents = function (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 };
};
var getTempoFromTrack = function (track) {
var setTempoEvent = track.find(function (event) { return event.type === "meta" && event.subtype === "setTempo"; });
var deltaTime = setTempoEvent.deltaTime, microsecondsPerBeat = setTempoEvent.microsecondsPerBeat;
return { deltaTime: deltaTime, microsecondsPerBeat: microsecondsPerBeat };
};
var getMetadataFromTrack = function (track) {
var microsecondsPerBeat, keySignature, timeSignature, endOfTrack;
var metaEvents = track.filter(function (event) { return event.type === "meta"; });
metaEvents.forEach(function (event) {
switch (event.subtype) {
case "setTempo":
var setTempoEvent = event;
microsecondsPerBeat = setTempoEvent.microsecondsPerBeat;
break;
case "keySignature":
var _a = event, key = _a.key, scale = _a.scale;
keySignature = {
key: key,
scale: scale
};
break;
case "timeSignature":
var _b = event, numerator = _b.numerator, denominator = _b.denominator, metronome = _b.metronome, thirtyseconds = _b.thirtyseconds;
timeSignature = {
numerator: numerator,
denominator: denominator,
metronome: metronome,
thirtyseconds: thirtyseconds
};
break;
case "endOfTrack":
endOfTrack = {};
break;
default:
return;
}
});
return {
microsecondsPerBeat: microsecondsPerBeat,
keySignature: keySignature,
timeSignature: timeSignature,
endOfTrack: endOfTrack
};
};
var urlToBinaryString = function (midiURL) { return __awaiter(_this, void 0, void 0, function () {
var response, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, fetch(midiURL, {
method: "GET",
headers: {
Accept: "audio/mid"
}
})];
case 1:
response = _a.sent();
return [4, response.blob()];
case 2:
data = _a.sent();
return [2, new Promise(function (resolve) {
var reader = new FileReader();
reader.onloadend = function () {
var blob = reader.result;
resolve(blob);
};
reader.readAsBinaryString(data);
})];
}
});
}); };
var noteOffEventToNote = function (noteOffEvent, noteInstrumentName, previousEndTime, msPerTick) {
var noteNumber = noteOffEvent.noteNumber, deltaTime = noteOffEvent.deltaTime;
var noteName = 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
};
};
var getMillisecondsPerTick = function (microsecondsPerBeat, ticksPerBeat) {
var secondsPerBeat = microsecondsPerBeat / 1000000;
var secondsPerTick = secondsPerBeat / ticksPerBeat;
var millisecondsPerTick = secondsPerTick * 1000;
return millisecondsPerTick;
};
var getBPMData = function (microSecondsPerBeat, ticksPerBeat, timeSignature) {
var timeSignatureNumerator = timeSignature.numerator;
var timeSignatureDenominator = timeSignature.denominator;
var BPM = (minutesInMS / microSecondsPerBeat) *
(timeSignatureDenominator / timeSignatureNumerator);
return { BPM: BPM };
};
var bpmToMSPerBeat = function (BPM, timeSignatureNumerator, timeSignatureDenominator) {
if (timeSignatureNumerator === void 0) { timeSignatureNumerator = 4; }
if (timeSignatureDenominator === void 0) { timeSignatureDenominator = 4; }
return ((minutesInMS / BPM) * (timeSignatureNumerator / timeSignatureDenominator));
};
var getTracksAndMetaFromParsedMidi = function (parsedMidi) {
var meta = parsedMidi.meta, musicTracks = parsedMidi.musicTracks;
var tracks = musicTracks.map(function (track, i) {
var noteOns = track.noteOnValid;
var noteOffs = track.noteOff;
var trackInstrumentName = meta.instrumentNames[i];
var millisecondsPerTick = meta.millisecondsPerTick;
var previousEndTime = 0;
return noteOffs.map(function (noteOff) {
var _a = noteOffEventToNote(noteOff, trackInstrumentName, previousEndTime, millisecondsPerTick), noteNumber = _a.noteNumber, noteName = _a.noteName, startTimeInMS = _a.startTimeInMS, durationInMS = _a.durationInMS, endTimeInMS = _a.endTimeInMS, noteInstrumentName = _a.noteInstrumentName, deltaTime = _a.deltaTime, msPerTick = _a.msPerTick;
previousEndTime = endTimeInMS;
var note = {
noteNumber: noteNumber,
noteName: noteName,
startTimeInMS: startTimeInMS,
durationInMS: durationInMS,
endTimeInMS: endTimeInMS,
instrumentName: noteInstrumentName,
deltaTime: deltaTime,
msPerTick: msPerTick
};
return note;
});
});
return { meta: meta, tracks: tracks };
};
var parseMidi = function (url) {
return urlToJSON(url).then(function (parsedMidi) {
var _a = getAllTracks(parsedMidi), meta = _a.meta, musicTracks = _a.musicTracks;
return {
meta: meta,
musicTracks: musicTracks
};
});
};
var MidiIO = (function () {
function MidiIO(userSettings) {
this.settings = {
midiURL: null,
parsedMidi: null,
BPM: -1
};
this.isInitialised = false;
this.settings = defaultsDeep(userSettings, {
midiURL: null,
parsedMidi: null,
BPM: -1
});
}
MidiIO.prototype.setBPM = function (BPM) {
if (BPM === void 0) { BPM = 60; }
this.millisecondsPerTick = MidiIO.getMillisecondsPerTick(MidiIO.bpmToMSPerBeat(BPM, this.timeSignatureNumerator, this.timeSignatureDenominator), this.ticksPerBeat);
this.BPM = BPM;
};
MidiIO.parseMidi = parseMidi;
MidiIO.getTracksAndMetaFromParsedMidi = getTracksAndMetaFromParsedMidi;
MidiIO.getWriter = getWriter;
MidiIO.getUniqueFromMidiNotes = getUniqueFromMidiNotes;
MidiIO.midiNoteNumberToName = midiNoteNumberToName;
MidiIO.urlToJSON = urlToJSON;
MidiIO.getAllMetadata = getAllMetadata;
MidiIO.getAllTracks = getAllTracks;
MidiIO.getInstrumentNumberFromMidiTrack = getInstrumentNameFromMidiTrack;
MidiIO.binaryStringToJSON = binaryStringToJSON;
MidiIO.getNoteOnEvents = getNoteOnEvents;
MidiIO.getNoteOffEvents = getNoteOffEvents;
MidiIO.getTempoFromTrack = getTempoFromTrack;
MidiIO.getMetadataFromTrack = getMetadataFromTrack;
MidiIO.urlToBinaryString = urlToBinaryString;
MidiIO.noteOffEventToNote = noteOffEventToNote;
MidiIO.getMillisecondsPerTick = getMillisecondsPerTick;
MidiIO.getBPMData = getBPMData;
MidiIO.bpmToMSPerBeat = bpmToMSPerBeat;
return MidiIO;
}());
var Store = (function () {
function Store() {
localforage.config({
name: "react_orchestra",
version: 1.0,
size: 4980736,
storeName: "react_orchestra",
description: "React Orchestra storage to cache mp3 notes and store app state"
});
this.db = localforage.createInstance({
name: "react_orchestra"
});
}
Store.prototype.set = function (key, value) {
return __awaiter(this, void 0, void 0, function () {
var err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4, this.db.setItem(key, value)];
case 1: return [2, _a.sent()];
case 2:
err_1 = _a.sent();
console.warn("Error : " + JSON.stringify(err_1.message, null, 2));
return [2, null];
case 3: return [2];
}
});
});
};
Store.prototype.get = function (key) {
return __awaiter(this, void 0, void 0, function () {
var err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4, this.db.getItem(key)];
case 1: return [2, _a.sent()];
case 2:
err_2 = _a.sent();
console.warn("Error : " + JSON.stringify(err_2.message, null, 2));
return [2, null];
case 3: return [2];
}
});
});
};
Store.prototype.exists = function (key) {
return __awaiter(this, void 0, void 0, function () {
var value, err_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4, this.db.getItem(key)];
case 1:
value = _a.sent();
return [2, value !== null];
case 2:
err_3 = _a.sent();
console.warn("Error : " + JSON.stringify(err_3.message, null, 2));
return [2, false];
case 3: return [2];
}
});
});
};
return Store;
}());
var store = new Store();
var _this$1 = undefined;
var baseURL = "https://raw.githubusercontent.com/RakanNimer/midi-js-soundfonts/master/MusyngKite";
var delay = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
var instrumentNameToRemotePath = function (instrumentName) {
return baseURL + "/" + instrumentName + "-mp3";
};
var instrumentAndNoteNameToRemotePath = function (instrumentName, noteName) { return baseURL + "/" + instrumentName + "-mp3/" + noteName + ".mp3"; };
var generateNoteKey = function (instrumentName, noteName) {
return instrumentName + "-" + noteName;
};
var getSoundFromRemote = function (instrumentName, noteName) { return __awaiter(_this$1, void 0, void 0, function () {
var noteURL, mp3, mp3Blob;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
noteURL = instrumentAndNoteNameToRemotePath(instrumentName, noteName);
return [4, fetch(noteURL)];
case 1:
mp3 = _a.sent();
return [4, mp3.arrayBuffer()];
case 2:
mp3Blob = _a.sent();
return [2, mp3Blob];
}
});
}); };
var getNoteBlob = function (instrumentName, noteName) { return __awaiter(_this$1, void 0, void 0, function () {
var noteKey, isNoteCached, item, noteBlob;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
noteKey = generateNoteKey(instrumentName, noteName);
return [4, store.exists(noteKey)];
case 1:
isNoteCached = _a.sent();
if (!isNoteCached) return [3, 3];
return [4, store.get(noteKey)];
case 2:
item = _a.sent();
return [2, item];
case 3: return [4, getSoundFromRemote(instrumentName, noteName)];
case 4:
noteBlob = _a.sent();
return [2, noteBlob];
}
});
}); };
var loadSound = function (instrumentName, noteName) { return __awaiter(_this$1, void 0, void 0, function () {
var noteBlob;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, getNoteBlob(instrumentName, noteName)];
case 1:
noteBlob = _a.sent();
return [2, noteBlob];
}
});
}); };
var playSound = function (noteBlob, _a) {
var _b = _a.gain, gain = _b === void 0 ? 1 : _b;
return new Promise(function (resolve, reject) {
audioContext.decodeAudioData(noteBlob, function (buffer) { return __awaiter(_this$1, void 0, void 0, function () {
var source, gainNode;
return __generator(this, function (_a) {
source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
gainNode = audioContext.createGain();
source.connect(gainNode);
gainNode.connect(audioContext.destination);
gainNode.gain.value = gain;
source.start(0);
resolve(source);
return [2];
});
}); }, function (err) {
reject(err);
});
});
};
var playNote = function (instrumentName, noteName, _a) {
var _b = _a.gain, gain = _b === void 0 ? 1 : _b;
return __awaiter(_this$1, void 0, void 0, function () {
var noteBlob;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4, getNoteBlob(instrumentName, noteName)];
case 1:
noteBlob = _c.sent();
return [4, playSound(noteBlob, { gain: gain })];
case 2: return [2, _c.sent()];
}
});
});
};
var stopPlayingNote = function (noteBuffer, fadeOutDuration) {
if (fadeOutDuration === void 0) { fadeOutDuration = 700; }
return __awaiter(_this$1, void 0, void 0, function () {
var err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4, delay(fadeOutDuration)];
case 1:
_a.sent();
noteBuffer.stop();
return [3, 3];
case 2:
err_1 = _a.sent();
console.warn("ERROR stopping note playback " + err_1.message);
return [3, 3];
case 3: return [2];
}
});
});
};
var sharpToBemol = function (noteName) {
if (noteName.indexOf("#") > -1) {
var noteNameNoOctave = noteName[0];
var octave = noteName[noteName.length - 1];
var noteCharCode = noteNameNoOctave.charCodeAt(0);
var newNote = noteNameNoOctave !== "G"
? String.fromCharCode(noteCharCode + 1) + "b" + octave
: "Ab" + octave;
return newNote;
}
return noteName;
};
var midiURLToMetaAndTracks = function (midiURL) { return __awaiter(_this$1, void 0, void 0, function () {
var parsedMidi, _a, meta, tracks;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4, MidiIO.parseMidi(midiURL)];
case 1:
parsedMidi = _b.sent();
_a = MidiIO.getTracksAndMetaFromParsedMidi(parsedMidi), meta = _a.meta, tracks = _a.tracks;
return [2, { meta: meta, tracks: tracks }];
}
});
}); };
var MusicManager = {
generateNoteKey: generateNoteKey,
midiURLToMetaAndTracks: midiURLToMetaAndTracks,
playSound: playSound,
playNote: playNote,
sharpToBemol: sharpToBemol,
instrumentNameToRemotePath: instrumentNameToRemotePath,
stopPlayingNote: stopPlayingNote,
getNoteBlob: getNoteBlob,
getSoundFromRemote: getSoundFromRemote,
loadSound: loadSound,
delay: delay
};
var isDefined = function (checkMe, defaultValue) {
return typeof checkMe !== "undefined" && checkMe !== null ? checkMe : defaultValue;
};
var Note = (function (_super) {
__extends(Note, _super);
function Note() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.playingBuffers = [];
_this.state = {
isPlaying: false,
isLoading: true
};
_this.onClickStart = function () {
if (window.isTouchDevice) {
return;
}
_this.startPlayingNote();
};
_this.loadSound = function () { return __awaiter(_this, void 0, void 0, function () {
var err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.setState({ isLoading: true });
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4, loadSound(this.props.instrumentName, this.props.name)];
case 2:
_a.sent();
return [3, 4];
case 3:
err_1 = _a.sent();
this.setState({ isLoading: false });
return [2];
case 4:
this.setState({ isLoading: false });
callIfExists(this.props.onNoteLoaded, this.props.instrumentName, this.props.name);
return [2];
}
});
}); };
_this.startPlayingNote = function () { return __awaiter(_this, void 0, void 0, function () {
var buffer, err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.setState({ isPlaying: true });
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
callIfExists(this.props.onStartPlayingNote, this.props.instrumentName, this.props.name);
return [4, playNote(this.props.instrumentName, this.props.name, { gain: this.props.gain })];
case 2:
buffer = _a.sent();
this.playingBuffers.push(buffer);
return [3, 4];
case 3:
err_2 = _a.sent();
console.warn("Something wrong happened with the audio api while playing note ");
return [3, 4];
case 4: return [2];
}
});
}); };
_this.stopPlayingNote = function () { return __awaiter(_this, void 0, void 0, function () {
var buffer, fadeOutDuration;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.playingBuffers && this.playingBuffers.length === 0) {
return [2];
}
callIfExists(this.props.onStopPlayingNote, this.props.instrumentName, this.props.name);
buffer = this.playingBuffers.pop();
fadeOutDuration = this.props.fadeOutDuration
? this.props.fadeOutDuration
: 700;
return [4, stopPlayingNote(buffer, fadeOutDuration)];
case 1:
_a.sent();
this.setState({ isPlaying: false });
return [2];
}
});
}); };
return _this;
}
Note.prototype.componentDidMount = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.loadSound()];
case 1:
_a.sent();
return [2];
}
});
});
};
Note.prototype.componentWillReceiveProps = function (nextProps) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(nextProps.instrumentName !== this.props.instrumentName ||
nextProps.name !== this.props.name)) return [3, 2];
return [4, this.loadSound()];
case 1:
_a.sent();
_a.label = 2;
case 2:
if (!(!this.props.play && nextProps.play)) return [3, 4];
return [4, this.startPlayingNote()];
case 3:
_a.sent();
_a.label = 4;
case 4:
if (!(this.props.play && !nextProps.play)) return [3, 6];
return [4, this.stopPlayingNote()];
case 5:
_a.sent();
_a.label = 6;
case 6: return [2];
}
});
});
};
Note.prototype.render = function () {
if (this.state.isLoading) {
return isDefined(this.props.loader, React.createElement("div", null, " Loading Note "));
}
return (React.createElement("div", { onTouchStart: this.startPlayingNote, onTouchEnd: this.stopPlayingNote, onMouseDown: this.onClickStart, onMouseUp: this.stopPlayingNote, className: isDefined(this.props.className, "") + " " + classnames({
"ro-note-playing": this.state.isPlaying
}, {
"ro-note-loading": this.state.isLoading
}) }, this.props.children || React.createElement("div", null)));
};
Note.defaultProps = {
play: false,
instrumentName: "acoustic_grand_piano",
name: "C3",
onNoteLoaded: function () { },
interactive: true,
fadeOutDuration: 600,
loader: React.createElement("div", null),
className: "",
children: React.createElement("div", null),
gain: 1,
onStopPlayingNote: function () { },
onStartPlayingNote: function () { }
};
return Note;
}(React.Component));
var waitAndRun = function (fnc, waitTime) {
var args = [];
for (var _i = 2; _i < arguments.length; _i++) {
args[_i - 2] = arguments[_i];
}
return setTimeout(fnc.bind.apply(fnc, args), waitTime);
};
var MidiTrack = (function (_super) {
__extends(MidiTrack, _super);
function MidiTrack(props) {
var _this = _super.call(this, props) || this;
_this.noteTimers = [];
_this.onTimerCall = function (note) { return __awaiter(_this, void 0, void 0, function () {
var _a, noteName, instrumentName, durationInMS, key, updatedPlayingNotes;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
noteName = note.noteName, instrumentName = note.instrumentName, durationInMS = note.durationInMS;
key = generateNoteKey(instrumentName, noteName);
this.setState({
playingNotes: (_a = {},
_a[key] = true,
_a)
});
callIfExists(this.props.onNotePlayed, instrumentName, noteName);
updatedPlayingNotes = Object.assign({}, this.state.playingNotes);
delete updatedPlayingNotes[key];
return [4, delay(durationInMS)];
case 1:
_b.sent();
this.setState({ playingNotes: updatedPlayingNotes });
callIfExists(this.props.onNoteStopPlaying, instrumentName, noteName);
return [2];
}
});
}); };
_this.state = {
uniqueNotes: MidiIO.getUniqueFromMidiNotes(isDefined(props.notes, [])),
playingNotes: {}
};
return _this;
}
MidiTrack.prototype.componentDidMount = function () {
if (this.props.play) {
this.playTrack();
}
};
MidiTrack.prototype.componentWillReceiveProps = function (nextProps) {
if (nextProps.play && !this.props.play) {
this.playTrack();
}
else if (!nextProps.play && this.props.play) {
this.stopPlayingTrack();
}
};
MidiTrack.prototype.playTrack = function () {
return __awaiter(this, void 0, void 0, function () {
var notes, i, currentNote, startTimeInMS, noteTimer;
return __generator(this, function (_a) {
if (!Array.isArray(this.props.notes))
return [2];
notes = this.props.notes;
for (i = 0; i < notes.length; i += 1) {
currentNote = notes[i];
startTimeInMS = currentNote.startTimeInMS;
noteTimer = waitAndRun(this.onTimerCall, startTimeInMS, this, currentNote);
this.noteTimers.push(noteTimer);
}
return [2];
});
});
};
MidiTrack.prototype.stopPlayingTrack = function () {
for (var i = 0; i < this.noteTimers.length; i += 1) {
clearTimeout(this.noteTimers[i]);
}
};
MidiTrack.prototype.render = function () {
var _this = this;
var notes = Array.isArray(this.props.notes) ? this.props.notes : [];
var meta = this.props.meta;
var trackIndex = this.props.trackIndex;
var uniqueNotes = MidiIO.getUniqueFromMidiNotes(notes);
var instrumentName = meta.instrumentNames[trackIndex];
var notesElements = uniqueNotes.map(function (noteName, i) { return (React.createElement(Note, { play: generateNoteKey(instrumentName, noteName) in
isDefined(_this.state.playingNotes, {}), name: sharpToBemol(noteName), instrumentName: instrumentName, key: i }, callIfExists(_this.props.renderNote, instrumentName, noteName, i))); });
return React.createElement("div", null, notesElements);
};
return MidiTrack;
}(React.Component));
var _this$2 = undefined;
var parseMidi$1 = function (props) { return __awaiter(_this$2, void 0, void 0, function () {
var midiURL, metaAndTracks;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
midiURL = isDefined(props.midiURL, "");
return [4, midiURLToMetaAndTracks(midiURL)];
case 1:
metaAndTracks = _a.sent();
return [2, metaAndTracks];
}
});
}); };
var defaultState = {
meta: {},
tracks: [],
selectedTrackNumbers: []
};
var Orchestra = (function (_super) {
__extends(Orchestra, _super);
function Orchestra() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = defaultState;
_this.onNotePlayed = function (instrumentName, noteName) {
callIfExists(_this.props.onNotePlayed, instrumentName, noteName);
};
_this.onNoteStopPlaying = function (instrumentName, noteName) {
callIfExists(_this.props.onNoteStopPlaying, instrumentName, noteName);
};
return _this;
}
Orchestra.prototype.componentDidMount = function () {
return __awaiter(this, void 0, void 0, function () {
var newState;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, parseMidi$1(this.props)];
case 1:
newState = _a.sent();
this.setState(newState);
return [2];
}
});
});
};
Orchestra.prototype.render = function () {
var _this = this;
return (React.createElement("div", null,
this.props.children,
this.state.tracks.map(function (track, i) { return (React.createElement(MidiTrack, { onNotePlayed: _this.onNotePlayed, onNoteStopPlaying: _this.onNoteStopPlaying, notes: track, meta: _this.state.meta, trackIndex: i, key: i, renderNote: _this.props.renderNote, play: _this.props.selectedTracks &&
_this.props.selectedTracks.indexOf(i) > -1 &&
_this.props.play })); })));
};
return Orchestra;
}(React.Component));
var notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
var reOrderNotes = function (startingNote, notes$$1) {
if (notes$$1 === void 0) { notes$$1 = notes; }
var startingNoteIndex = notes$$1.indexOf(startingNote);
var reorderedNotes = [];
for (var i = startingNoteIndex; i < notes$$1.length + startingNoteIndex; i += 1) {
var currentNoteIndex = i % notes$$1.length;
reorderedNotes.push(notes$$1[currentNoteIndex]);
}
return reorderedNotes;
};
var isInHigherOctave = function (previousNote, nextNote) {
return notes.indexOf(previousNote) >= notes.indexOf(nextNote);
};
var scales = {
aeolian: {
name: "Minor",
sequence: [0, 2, 3, 5, 7, 8, 10]
},
ionian: {
name: "Major",
sequence: [0, 2, 4, 5, 7, 9, 11]
},
dorian: {
name: "Dorian",
sequence: [0, 2, 3, 5, 7, 9, 10]
},
phrygian: {
name: "Phrygian",
sequence: [0, 1, 3, 5, 7, 8, 10]
},
lydian: {
name: "Lydian",
sequence: [0, 2, 4, 6, 7, 9, 11]
},
mixolydian: {
name: "Mixolydian",
sequence: [0, 2, 4, 5, 7, 9, 10]
},
locrian: {
name: "Locrian",
sequence: [0, 1, 3, 5, 6, 8, 10]
},
chromatic: {
name: "Chromatic",
sequence: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
},
acoustic: {
name: "Acoustic",
sequence: [0, 2, 4, 6, 7, 9, 10]
},
algerian: {
name: "Algerian",
sequence: [0, 2, 3, 5, 6, 7, 9, 10]
},
altered: {
name: "Altered",
sequence: [0, 1, 3, 4, 6, 8, 10]
},
pentatonic: {
name: "Minoric",
sequence: [0, 2, 4, 7, 9]
},
wholetone: {
name: "Wholetone",
sequence: [0, 2, 4, 6, 8, 10]
},
blues: {
name: "Blues",
sequence: [0, 3, 5, 6, 7, 10]
},
minorBlues: {
name: "Minor Blues",
sequence: [0, 2, 3, 5, 6, 7, 8, 10]
},
majorBlues: {
name: "Major Blues",
sequence: [0, 2, 3, 4, 5, 6, 7, 9, 10]
},
harmonicMinor: {
name: "Harmonic Minor",
sequence: [0, 2, 3, 5, 7, 8, 11]
},
melodicMinorAsc: {
name: "Melodic Minor Asc",
sequence: [0, 2, 3, 5, 7, 9, 11]
},
melodicMinorDesc: {
name: "Melodic Minor Desc",
sequence: [0, 2, 3, 5, 7, 8, 10]
},
enigmatic: {
name: "Enigmatic",
sequence: [0, 1, 4, 6, 8, 10, 11]
},
doubleHarmonic: {
name: "Double Harmonic",
sequence: [0, 1, 4, 5, 7, 8, 11]
},
persian: {
name: "Persian",
sequence: [0, 1, 4, 5, 6, 8, 11]
},
arabian: {
name: "Arabian",
sequence: [0, 2, 4, 5, 6, 8, 10]
},
japanese: {
name: "Japanese",
sequence: [0, 1, 5, 7, 8]
},
egyptian: {
name: "Egyptian