UNPKG

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
'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