diff --git a/src/containers/sound-editor.jsx b/src/containers/sound-editor.jsx index 28fe3efb5087731ce706108ca8737aed3fcc8898..781b2cc5fe51f9c068c46a7ae7467d0c93e9a10f 100644 --- a/src/containers/sound-editor.jsx +++ b/src/containers/sound-editor.jsx @@ -16,7 +16,7 @@ class SoundEditor extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'copyCurrentSamples', + 'copyCurrentBuffer', 'handleStoppedPlaying', 'handleChangeName', 'handlePlay', @@ -70,7 +70,7 @@ class SoundEditor extends React.Component { if (this.undoStack.length >= UNDO_STACK_SIZE) { this.undoStack.shift(); // Drop the first element off the array } - this.undoStack.push(this.copyCurrentSamples()); + this.undoStack.push(this.copyCurrentBuffer()); } this.resetState(samples, sampleRate); this.props.onUpdateSoundBuffer( @@ -102,12 +102,12 @@ class SoundEditor extends React.Component { if (this.state.trimStart === null && this.state.trimEnd === null) { this.setState({trimEnd: 0.95, trimStart: 0.05}); } else { - const samples = this.copyCurrentSamples(); + const {samples, sampleRate} = this.copyCurrentBuffer(); const sampleCount = samples.length; const startIndex = Math.floor(this.state.trimStart * sampleCount); const endIndex = Math.floor(this.state.trimEnd * sampleCount); const clippedSamples = samples.slice(startIndex, endIndex); - this.submitNewSamples(clippedSamples, this.props.sampleRate); + this.submitNewSamples(clippedSamples, sampleRate); } } handleUpdateTrimEnd (trimEnd) { @@ -119,9 +119,12 @@ class SoundEditor extends React.Component { effectFactory (name) { return () => this.handleEffect(name); } - copyCurrentSamples () { + copyCurrentBuffer () { // Cannot reliably use props.samples because it gets detached by Firefox - return this.audioBufferPlayer.buffer.getChannelData(0); + return { + samples: this.audioBufferPlayer.buffer.getChannelData(0), + sampleRate: this.audioBufferPlayer.buffer.sampleRate + }; } handleEffect (name) { const effects = new AudioEffects(this.audioBufferPlayer.buffer, name); @@ -133,18 +136,18 @@ class SoundEditor extends React.Component { }); } handleUndo () { - this.redoStack.push(this.copyCurrentSamples()); - const samples = this.undoStack.pop(); + this.redoStack.push(this.copyCurrentBuffer()); + const {samples, sampleRate} = this.undoStack.pop(); if (samples) { - this.submitNewSamples(samples, this.props.sampleRate, true); + this.submitNewSamples(samples, sampleRate, true); this.handlePlay(); } } handleRedo () { - const samples = this.redoStack.pop(); + const {samples, sampleRate} = this.redoStack.pop(); if (samples) { - this.undoStack.push(this.copyCurrentSamples()); - this.submitNewSamples(samples, this.props.sampleRate, true); + this.undoStack.push(this.copyCurrentBuffer()); + this.submitNewSamples(samples, sampleRate, true); this.handlePlay(); } } diff --git a/src/lib/audio/audio-buffer-player.js b/src/lib/audio/audio-buffer-player.js index e9e89c787f9fae23eae39c4b3f08e8a6ce601b04..9814d9807503898b6f31d00960855cf43a1d8662 100644 --- a/src/lib/audio/audio-buffer-player.js +++ b/src/lib/audio/audio-buffer-player.js @@ -45,7 +45,13 @@ class AudioBufferPlayer { stop () { if (this.source) { this.source.onended = null; // Do not call onEnded callback if manually stopped - this.source.stop(); + try { + this.source.stop(); + } catch (e) { + // This is probably Safari, which dies when you call stop more than once + // which the spec says is allowed: https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode + console.log('Caught error while stopping buffer source node.'); // eslint-disable-line no-console + } } } } diff --git a/src/lib/audio/audio-effects.js b/src/lib/audio/audio-effects.js index ead04bf504550f85c9837f3acb1c38adf18835d6..067dcc6209397782d248a3a04ce8ad11f0f668d9 100644 --- a/src/lib/audio/audio-effects.js +++ b/src/lib/audio/audio-effects.js @@ -38,8 +38,14 @@ class AudioEffects { buffer.getChannelData(0).reverse(); break; } - const OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext; - this.audioContext = new OfflineAudioContext(1, sampleCount, buffer.sampleRate); + if (window.OfflineAudioContext) { + this.audioContext = new window.OfflineAudioContext(1, sampleCount, buffer.sampleRate); + } else { + // Need to use webkitOfflineAudioContext, which doesn't support all sample rates. + // Resample by adjusting sample count to make room and set offline context to desired sample rate. + const sampleScale = 44100 / buffer.sampleRate; + this.audioContext = new window.webkitOfflineAudioContext(1, sampleScale * sampleCount, 44100); + } this.buffer = buffer; this.source = this.audioContext.createBufferSource(); this.source.buffer = this.buffer;