From 9e9af5ce8c1d9ba3273cb5b827a2d10527ce42b0 Mon Sep 17 00:00:00 2001 From: RakanNimer Date: Sun, 8 Jan 2017 18:02:25 +0200 Subject: [PATCH] Added low hanging fruit perf optimisations --- native/src/MusicManager.js | 2 +- native/src/components/MidiTrack.js | 57 +++++++++---- native/src/components/Note.js | 124 +++-------------------------- native/src/components/Orchestra.js | 5 +- 4 files changed, 59 insertions(+), 129 deletions(-) diff --git a/native/src/MusicManager.js b/native/src/MusicManager.js index 02942ba..83bda2c 100644 --- a/native/src/MusicManager.js +++ b/native/src/MusicManager.js @@ -43,7 +43,7 @@ const playNote = (sound, volume = 1, loopCount = 0) => if (success) { resolve(sound); } else { - console.warn(`Couldn't play sound in MusicManager.playNote 😞 : ${err.message}`); + console.warn(`Couldn't play sound in MusicManager.playNote 😞 : ${JSON.stringify(err, 2, 2)}`); reject(err); } }).setCurrentTime(0); diff --git a/native/src/components/MidiTrack.js b/native/src/components/MidiTrack.js index 14fd71f..d9bb402 100644 --- a/native/src/components/MidiTrack.js +++ b/native/src/components/MidiTrack.js @@ -15,7 +15,7 @@ import isDefined from '../utils/isDefined'; const waitAndRun = (fnc, waitTime, ...args) => setTimeout(fnc.bind(...args), waitTime); -export default class MidiTrack extends React.Component { +export default class MidiTrack extends React.PureComponent { constructor(props) { super(props); this.state = { @@ -69,39 +69,66 @@ export default class MidiTrack extends React.Component { for (let i = 0; i < this.noteTimers.length; i += 1) { clearTimeout(this.noteTimers[i]); } + this.noteTimers = []; } - render() { - const notes = this.props.notes; - const meta = this.props.meta; - const trackIndex = this.props.trackIndex; - const uniqueNotes = MidiIO.getUniqueFromMidiNotes(notes); - const instrumentName = this.props.instrumentName || meta.instrumentNames[trackIndex]; - const notesElements = uniqueNotes.map((noteName, i) => ( - { + const meta = this.props.meta; + const trackIndex = this.props.trackIndex; + const instrumentName = this.props.instrumentName || meta.instrumentNames[trackIndex]; + return ( { - callIfExists(this.props.renderNote, instrumentName, sharpToBemol(noteName), i) + this.props.renderNote(instrumentName, sharpToBemol(noteName), i) } - - )); + ); + }); + return notes; + } + render() { + const notes = this.props.notes; + const meta = this.props.meta; + const trackIndex = this.props.trackIndex; + const uniqueNotes = MidiIO.getUniqueFromMidiNotes(notes); + const instrumentName = this.props.instrumentName || meta.instrumentNames[trackIndex]; return ( - { notesElements } + { + this.renderNotes(uniqueNotes) + // uniqueNotes.map((noteName, i) => ( + // + // { + // this.props.renderNote(instrumentName, sharpToBemol(noteName), i) + // } + // )) + } ); } } MidiTrack.propTypes = { + onNotePlayed: PropTypes.func, + onNoteStopPlaying: PropTypes.func, notes: PropTypes.arrayOf(PropTypes.shape( { noteNumber: PropTypes.number, diff --git a/native/src/components/Note.js b/native/src/components/Note.js index 7d18dac..ee92f35 100644 --- a/native/src/components/Note.js +++ b/native/src/components/Note.js @@ -7,10 +7,9 @@ import { loadSound, } from '../MusicManager'; -import callIfExists from '../utils/callIfExists'; import isDefined from '../utils/isDefined'; -class Note extends React.Component { +class Note extends React.PureComponent { constructor(props) { super(props); this.playingBuffers = []; @@ -40,6 +39,12 @@ class Note extends React.Component { await this.stopPlayingNote(); } } + shouldComponentUpdate(nextProps, nextState) { + // TODO: split into consts + // return true; + const shouldUpdate = this.state.isLoading || (nextProps.name !== this.props.name) || (nextProps.instrumentName !== this.props.instrumentName) || (nextState.isPlaying !== this.state.isPlaying) || (nextProps.play !== this.props.play) || this.state.isPlaying || this.state.isPlaying !== nextState.isPlaying; + return shouldUpdate; + } componentWillUnmount() { this.sound.release(); } @@ -53,13 +58,14 @@ class Note extends React.Component { return null; } this.setState({ isLoading: false }); - callIfExists(this.props.onNoteLoaded, this.props.instrumentName, this.props.name); + this.props.onNoteLoaded(this.props.instrumentName, this.props.name); return this.sound; } async startPlayingNote() { this.setState({ isPlaying: true }); try { // Need to create a new sound each time to avoid audio glitch on IOS when playing fast + this.props.onStartPlayingNote(this.props.instrumentName, this.props.name); this.sound = await loadSound(this.props.instrumentName, this.props.name); const buffer = await playNote(this.sound); return buffer; @@ -70,6 +76,7 @@ class Note extends React.Component { } async stopPlayingNote() { await stopPlayingNote(this.sound, this.props.delayPressOut); + this.props.onStopPlayingNote(this.props.instrumentName, this.props.name); this.setState({ isPlaying: false }); } render() { @@ -99,114 +106,9 @@ class Note extends React.Component { Note.defaultProps = { play: false, + onStartPlayingNote: () => {}, + onStopPlayingNote: () => {}, + onNoteLoaded: () => {}, delayPressOut: 1500, }; export default Note; -// -// import React from 'react'; -// import classnames from 'classnames'; -// import { -// stopPlayingNote, -// playNote, -// loadSound, -// } from '../MusicManager'; -// import callIfExists from '../../../utils/callIfExists'; -// import isDefined from '../../../utils/isDefined'; -// -// class Note extends React.Component { -// constructor(props) { -// super(props); -// this.playingBuffers = []; -// this.state = { -// isPlaying: false, -// isLoading: true, -// }; -// this.startPlayingNote = this.startPlayingNote.bind(this); -// this.stopPlayingNote = this.stopPlayingNote.bind(this); -// this.onClickStart = this.onClickStart.bind(this); -// } -// async componentDidMount() { -// await this.loadSound(); -// } -// async componentWillReceiveProps(nextProps) { -// if ( -// (nextProps.instrumentName !== this.props.instrumentName) || -// (nextProps.name !== this.props.name) -// ) { -// await this.loadSound(); -// } -// if (!this.props.play && nextProps.play) { -// await this.startPlayingNote(); -// // console.log('Changed props to play, started playing note'); -// } -// if (this.props.play && !nextProps.play) { -// await this.stopPlayingNote(); -// // console.log('Changed props to stop playing'); -// } -// } -// onClickStart() { -// if (window.isTouchDevice) { -// return; -// } -// this.startPlayingNote(); -// } -// async loadSound() { -// this.setState({ isLoading: true }); -// try { -// await loadSound(this.props.instrumentName, this.props.name); -// } catch (err) { -// this.setState({ isLoading: false }); -// return; -// } -// this.setState({ isLoading: false }); -// callIfExists(this.props.onNoteLoaded, this.props.instrumentName, this.props.name); -// } -// async startPlayingNote() { -// // if (this.props.interactive === false) return; -// this.setState({ isPlaying: true }); -// try { -// const buffer = await playNote(this.props.instrumentName, this.props.name); -// this.playingBuffers.push(buffer); -// } catch (err) { -// console.warn('Something wrong happened with the audio api while playing note '); -// } -// } -// async stopPlayingNote() { -// if (this.playingBuffers && this.playingBuffers.length === 0) { -// return; -// } -// const buffer = this.playingBuffers.pop(); -// const fadeOutDuration = this.props.fadeOutDuration ? this.props.fadeOutDuration : 700; -// await stopPlayingNote(buffer, fadeOutDuration); -// this.setState({ isPlaying: false }); -// } -// render() { -// if (this.state.isLoading) { -// // console.log(isDefined(this.props.loader, '
Loading Note
')) -// return isDefined(this.props.loader,
Loading Note
); -// } -// return ( -//
-// { -// this.props.children ||
-// } -//
-// ); -// } -// } -// Note.defaultProps = { -// play: false, -// }; -// export default Note; diff --git a/native/src/components/Orchestra.js b/native/src/components/Orchestra.js index 503227b..baec2f1 100644 --- a/native/src/components/Orchestra.js +++ b/native/src/components/Orchestra.js @@ -59,8 +59,8 @@ class Orchestra extends React.Component { { this.state.tracks.map((track, i) => (