UNPKG

react-orchestra

Version:

A toolbox to build interactive and smart instruments on the web and mobile.

173 lines (153 loc) 5.55 kB
import Sound from 'react-native-sound'; import RNFS from 'react-native-fs'; import midiFileParser from 'midi-file-parser'; import base64 from 'base-64'; import MidiIO from './MidiIO/src/'; import Store from './stores/'; const baseURL = 'https://raw.githubusercontent.com/RakanNimer/midi-js-soundfonts/master/MusyngKite'; // const playingNotes = {}; const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); // const instrumentNameToRemotePath = instrumentName => `${baseURL}/${instrumentName}-mp3`; const instrumentAndNoteNameToRemotePath = (instrumentName, noteName) => `${baseURL}/${instrumentName}-mp3/${noteName}.mp3`; const generateNoteKey = (instrumentName, noteName) => `${instrumentName}-${noteName}`; const noteKeyToLocalPath = noteKey => `${RNFS.DocumentDirectoryPath}${noteKey}.mp3`; // const noteKeyToRemotePath = (noteKey) => { // const [instrumentName, noteName] = noteKey.split('-'); // return `${baseURL}/${instrumentName}-mp3/${noteName}.mp3`; // }; // TODO: make better hash const getFilenameFromURL = URL => URL.split('/').reduce((prev, cur, i, arr) => ((i === arr.length - 1) ? cur : '')); const generateMidiKey = midiURL => midiURL; const midiKeyToLocalPath = key => `${RNFS.DocumentDirectoryPath}/${getFilenameFromURL(key)}`; // const midiKeyToURL = key => key; const createSoundFromPath = async localPath => new Promise((resolve, reject) => { const sound = new Sound(localPath, '', (error) => { if (error) { reject(error); } else { resolve(sound); } }); }); const playNote = (sound, volume = 1, loopCount = 0) => new Promise((resolve, reject) => { // .setNumberOfLoops(5).play().setCurrentTime(0) sound.setNumberOfLoops(loopCount).setVolume(volume).play((success, err) => { if (success) { resolve(sound); } else { console.warn(`Couldn't play sound in MusicManager.playNote 😞 : ${JSON.stringify(err, 2, 2)}`); reject(err); } }).setCurrentTime(0); }); const stopPlayingNote = async (sound, fadeOutDuration = 500) => { try { await delay(fadeOutDuration); sound.pause(); } catch (err) { console.warn(`ERROR IN STOPPING NOTE ${err.message}`); } }; const getSoundFromRemote = async (instrumentName, noteName) => { const noteKey = generateNoteKey(instrumentName, noteName); const noteURL = instrumentAndNoteNameToRemotePath(instrumentName, noteName); const localPath = noteKeyToLocalPath(noteKey); await RNFS.downloadFile({ fromUrl: noteURL, toFile: localPath }).promise; const mp3File = RNFS.readFile(localPath, 'base64'); return { mp3File, localPath }; }; const loadSound = async (instrumentName, noteName) => { const noteKey = generateNoteKey(instrumentName, noteName); const isInDB = await Store.exists('Note', noteKey); const remotePath = instrumentAndNoteNameToRemotePath(instrumentName, noteName); let notePath; if (isInDB) { const localPath = noteKeyToLocalPath(noteKey); notePath = localPath; } else { const { localPath } = await getSoundFromRemote(instrumentName, noteName); await Store.set('Note', noteKey, { instrumentName, noteName, localPath, remotePath }); notePath = localPath; } const noteSound = await createSoundFromPath(notePath); return noteSound; }; // MIDI const getMidiFromRemote = async (midiURL) => { const midiKey = generateMidiKey(midiURL); const localPath = midiKeyToLocalPath(midiKey); await RNFS.downloadFile({ fromUrl: midiURL, toFile: localPath }).promise; return { localPath }; }; const loadMidi = async (midiURL) => { const midiKey = generateMidiKey(midiURL); const isInDB = await Store.exists('Midi', midiKey); let midiPath; // if (!isInDB) { // alert('IS IN DB'); // midiPath = midiKeyToLocalPath(midiURL); // } else { const { localPath } = await getMidiFromRemote(midiURL); await Store.set('Midi', midiKey, { localPath, remotePath: midiURL }); midiPath = localPath; // } let midifile; try { midifile = await RNFS.readFile(midiPath, 'base64'); } catch (err) { alert(`ERROR Reading Midi from filesystem ${err.message}`); } let bytes; try { bytes = base64.decode(midifile); } catch (err) { alert(`ERROR decodign Midi from filesystem ${err.message}`); } let jsonMidi; let metaAndTracks; try { jsonMidi = midiFileParser(bytes); metaAndTracks = MidiIO.getAllTracks(jsonMidi); } catch (err) { alert(`ERROR HERE ${err.message}`); // throw new Error(err.message, '', {}); } return metaAndTracks; }; const midiURLToMetaAndTracks = async (midiURL) => { // TODO : cache let parsedMidi; try { try { parsedMidi = await loadMidi(midiURL); // await MidiIO.parseMidi(midiURL); } catch (err) { alert(`ERROR ${err.message}`); } // alert(JSON.stringify(parsedMidi, 2, 2)); const { meta, tracks } = MidiIO.getTracksAndMetaFromParsedMidi(parsedMidi); return { meta, tracks }; } catch (err) { console.warn('Error parsing midi : ', err.message); return null; } }; const sharpToBemol = (noteName) => { if (noteName.indexOf('#') > -1) { const noteNameNoOctave = noteName[0]; const octave = noteName[noteName.length - 1]; const noteCharCode = noteNameNoOctave.charCodeAt(0); const newNote = noteNameNoOctave !== 'G' ? `${String.fromCharCode(noteCharCode + 1)}b${octave}` : `Ab${octave}`; return newNote; } return noteName; }; export { stopPlayingNote, playNote, loadSound, midiURLToMetaAndTracks, generateNoteKey, sharpToBemol, delay, };