From 158adb08682dda5ba4cfb7b52895a96c43cb15fe Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 13 Apr 2018 11:40:34 -0400 Subject: [PATCH 1/3] refactor decodeSound function, to only use sound data buffer to decode the sound instead of relying metadata from the project json --- src/index.js | 56 +++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/index.js b/src/index.js index c9c5d63..f246b5c 100644 --- a/src/index.js +++ b/src/index.js @@ -189,7 +189,6 @@ class AudioEngine { * Store a reference to it the sound in the audioBuffers dictionary, indexed by soundId * @param {object} sound - an object containing audio data and metadata for a sound * @property {Buffer} data - sound data loaded from scratch-storage. - * @property {string} format - format type, either empty or adpcm. * @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored. */ decodeSound (sound) { @@ -197,38 +196,45 @@ class AudioEngine { let loaderPromise = null; // Make a copy of the buffer because decoding detaches the original buffer - const bufferCopy = sound.data.buffer.slice(0); + const bufferCopy1 = sound.data.buffer.slice(0); - switch (sound.format) { - case '': - // Check for newer promise-based API - if (this.audioContext.decodeAudioData.length === 1) { - loaderPromise = this.audioContext.decodeAudioData(bufferCopy); - } else { - // Fall back to callback API - loaderPromise = new Promise((resolve, reject) => { - this.audioContext.decodeAudioData(bufferCopy, - decodedAudio => resolve(decodedAudio), - error => reject(error) - ); - }); - } - break; - case 'adpcm': - loaderPromise = (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy); - break; - default: - return log.warn('unknown sound format', sound.format); + // Attempt to decode the sound using the browser's native audio data decoder + // Check for newer promise-based API + if (this.audioContext.decodeAudioData.length === 1) { + loaderPromise = this.audioContext.decodeAudioData(bufferCopy1); + } else { + // Fall back to callback API + loaderPromise = new Promise((resolve, reject) => { + this.audioContext.decodeAudioData(bufferCopy1, + decodedAudio => resolve(decodedAudio), + error => reject(error) + ); + }); } const storedContext = this; return loaderPromise.then( decodedAudio => { - storedContext.audioBuffers[soundId] = decodedAudio; + storedContext.updateSoundBuffer(soundId, decodedAudio); return soundId; }, - error => { - log.warn('audio data could not be decoded', error); + () => { + // The audio context failed to parse the sound data + // we gave it, so try to decode as 'adpcm' + + // First we need to create another copy of our original data + const bufferCopy2 = sound.data.buffer.slice(0); + // Try decoding as adpcm + return (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy2) + .then( + decodedAudio => { + storedContext.updateSoundBuffer(soundId, decodedAudio); + return soundId; + }, + sndError => { + log.warn('audio data could not be decoded', sndError); + } + ); } ); } From a3ecd2ddc25cd56e82202cd289d5fcfb859f4997 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 13 Apr 2018 17:46:09 -0400 Subject: [PATCH 2/3] More refactoring/code-cleanup addressing PR comments. --- src/index.js | 60 +++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/index.js b/src/index.js index f246b5c..e2217da 100644 --- a/src/index.js +++ b/src/index.js @@ -15,6 +15,27 @@ const ADPCMSoundDecoder = require('./ADPCMSoundDecoder'); * that handles global functionality, and AudioPlayers, belonging to individual sprites and clones. */ +/** + * Wrapper to ensure that audioContext.decodeAudioData is a promise + * @param {object} audioContext The current AudioContext + * @param {ArrayBuffer} buffer Audio data buffer to decode + * @return {Promise} A promise that resolves to the decoded audio + */ +const decodeAudioData = function (audioContext, buffer) { + // Check for newer promise-based API + if (audioContext.decodeAudioData.length === 1) { + return audioContext.decodeAudioData(buffer); + } + // Fall back to callback API + return new Promise((resolve, reject) => { + audioContext.decodeAudioData(buffer, + decodedAudio => resolve(decodedAudio), + error => reject(error) + ); + }); +}; + + class AudioPlayer { /** * Each sprite or clone has an audio player @@ -192,32 +213,18 @@ class AudioEngine { * @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored. */ decodeSound (sound) { - const soundId = uid(); - let loaderPromise = null; - // Make a copy of the buffer because decoding detaches the original buffer const bufferCopy1 = sound.data.buffer.slice(0); - // Attempt to decode the sound using the browser's native audio data decoder - // Check for newer promise-based API - if (this.audioContext.decodeAudioData.length === 1) { - loaderPromise = this.audioContext.decodeAudioData(bufferCopy1); - } else { - // Fall back to callback API - loaderPromise = new Promise((resolve, reject) => { - this.audioContext.decodeAudioData(bufferCopy1, - decodedAudio => resolve(decodedAudio), - error => reject(error) - ); - }); - } + const soundId = uid(); + // Partially apply updateSoundBuffer function with the current + // soundId so that it's ready to be used on successfully decoded audio + const addDecodedAudio = this.updateSoundBuffer.bind(this, soundId); - const storedContext = this; - return loaderPromise.then( - decodedAudio => { - storedContext.updateSoundBuffer(soundId, decodedAudio); - return soundId; - }, + // Attempt to decode the sound using the browser's native audio data decoder + // If that fails, attempt to decode as ADPCM + return decodeAudioData(this.audioContext, bufferCopy1).then( + addDecodedAudio, () => { // The audio context failed to parse the sound data // we gave it, so try to decode as 'adpcm' @@ -227,10 +234,7 @@ class AudioEngine { // Try decoding as adpcm return (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy2) .then( - decodedAudio => { - storedContext.updateSoundBuffer(soundId, decodedAudio); - return soundId; - }, + addDecodedAudio, sndError => { log.warn('audio data could not be decoded', sndError); } @@ -249,12 +253,14 @@ class AudioEngine { } /** - * Update the in-memory audio buffer to a new one by soundId. + * Add or update the in-memory audio buffer to a new one by soundId. * @param {!string} soundId - the id of the sound buffer to update. * @param {AudioBuffer} newBuffer - the new buffer to swap in. + * @return {string} The uid of the sound that was updated or added */ updateSoundBuffer (soundId, newBuffer) { this.audioBuffers[soundId] = newBuffer; + return soundId; } /** From 8d67c6a4c3499fa3522450dbf93221a99c440154 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 17 Apr 2018 09:36:21 -0400 Subject: [PATCH 3/3] Update variable name. --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index e2217da..95f3bbe 100644 --- a/src/index.js +++ b/src/index.js @@ -235,8 +235,8 @@ class AudioEngine { return (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy2) .then( addDecodedAudio, - sndError => { - log.warn('audio data could not be decoded', sndError); + error => { + log.warn('audio data could not be decoded', error); } ); }