Skip to content

Commit

Permalink
Added low hanging fruit perf optimisations
Browse files Browse the repository at this point in the history
  • Loading branch information
rakannimer committed Jan 8, 2017
1 parent bf66cb6 commit 9e9af5c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 129 deletions.
2 changes: 1 addition & 1 deletion native/src/MusicManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
57 changes: 42 additions & 15 deletions native/src/components/MidiTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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) => (
<Note
renderNotes(uniqueNotes) {
const notes = uniqueNotes.map((noteName, i) => {
const meta = this.props.meta;
const trackIndex = this.props.trackIndex;
const instrumentName = this.props.instrumentName || meta.instrumentNames[trackIndex];
return (<Note
play={
generateNoteKey(instrumentName, noteName) in isDefined(this.state.playingNotes, {})
}
generateNoteKey(instrumentName, noteName) in isDefined(this.state.playingNotes, {})
}
name={sharpToBemol(noteName)}
key={i}
onStartPlayingNote={this.props.onNotePlayed}
onStopPlayingNote={this.props.onNoteStopPlaying}
>
{
callIfExists(this.props.renderNote, instrumentName, sharpToBemol(noteName), i)
this.props.renderNote(instrumentName, sharpToBemol(noteName), i)
}
</Note>
));
</Note>);
});
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 (
<Instrument
name={instrumentName}
onInstrumentLoaded={this.onInstrumentLoaded}
style={this.props.instrumentStyle}
>
{ notesElements }
{
this.renderNotes(uniqueNotes)
// uniqueNotes.map((noteName, i) => (
// <Note
// play={
// generateNoteKey(instrumentName, noteName) in isDefined(this.state.playingNotes, {})
// }
// name={sharpToBemol(noteName)}
// key={i}
// onStartPlayingNote={this.props.onNotePlayed}
// onStopPlayingNote={this.props.onNoteStopPlaying}
// >
// {
// this.props.renderNote(instrumentName, sharpToBemol(noteName), i)
// }
// </Note>))
}
</Instrument>
);
}
}

MidiTrack.propTypes = {
onNotePlayed: PropTypes.func,
onNoteStopPlaying: PropTypes.func,
notes: PropTypes.arrayOf(PropTypes.shape(
{
noteNumber: PropTypes.number,
Expand Down
124 changes: 13 additions & 111 deletions native/src/components/Note.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down Expand Up @@ -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();
}
Expand All @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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, '<div> Loading Note </div>'))
// return isDefined(this.props.loader, <div> Loading Note </div>);
// }
// return (
// <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 || <div />
// }
// </div>
// );
// }
// }
// Note.defaultProps = {
// play: false,
// };
// export default Note;
5 changes: 3 additions & 2 deletions native/src/components/Orchestra.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ class Orchestra extends React.Component {
{
this.state.tracks.map((track, i) => (
<MidiTrack
onNotePlayed={this.onNotePlayed}
onNoteStopPlaying={this.onNoteStopPlaying}
onNotePlayed={this.props.onNotePlayed}
onNoteStopPlaying={this.props.onNoteStopPlaying}
notes={track}
meta={this.state.meta}
trackIndex={i}
Expand All @@ -84,5 +84,6 @@ Orchestra.propTypes = {
play: PropTypes.bool,
selectedTracks: PropTypes.arrayOf(PropTypes.number),
onNotePlayed: PropTypes.func,
onNoteStopPlaying: PropTypes.func,
};
export default Orchestra;

0 comments on commit 9e9af5c

Please sign in to comment.