add decodeSoundPlayer to AudioEngine

- decodeSoundPlayer returns a SoundPlayer instance instance of the
  sound id
- Write deprecation notes in AudioEngine about audioBuffers
This commit is contained in:
Michael "Z" Goddard 2018-06-11 15:26:45 -04:00
parent 06a9af4dc3
commit a5702a7d49
No known key found for this signature in database
GPG key ID: 762CD40DD5349872

View file

@ -5,9 +5,9 @@ const log = require('./log');
const uid = require('./uid'); const uid = require('./uid');
const ADPCMSoundDecoder = require('./ADPCMSoundDecoder'); const ADPCMSoundDecoder = require('./ADPCMSoundDecoder');
const AudioPlayer = require('./AudioPlayer'); const AudioPlayer = require('./AudioPlayer');
const Loudness = require('./Loudness'); const Loudness = require('./Loudness');
const SoundPlayer = require('./GreenPlayer');
/** /**
* Wrapper to ensure that audioContext.decodeAudioData is a promise * Wrapper to ensure that audioContext.decodeAudioData is a promise
@ -103,40 +103,77 @@ class AudioEngine {
/** /**
* Decode a sound, decompressing it into audio samples. * Decode a sound, decompressing it into audio samples.
* 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
* @param {object} sound - an object containing audio data and metadata for a sound * a sound
* @property {Buffer} data - sound data loaded from scratch-storage. * @param {Buffer} sound.data - sound data loaded from scratch-storage
* @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored. * @returns {?Promise} - a promise which will resolve to the sound id and
* buffer if decoded
*/ */
decodeSound (sound) { _decodeSound (sound) {
// Make a copy of the buffer because decoding detaches the original buffer // Make a copy of the buffer because decoding detaches the original
// buffer
const bufferCopy1 = sound.data.buffer.slice(0); const bufferCopy1 = sound.data.buffer.slice(0);
// todo: multiple decodings of the same buffer create duplicate decoded
// copies in audioBuffers. Create a hash id of the buffer or deprecate
// audioBuffers to avoid memory issues for large audio buffers.
const soundId = uid(); 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);
// Attempt to decode the sound using the browser's native audio data decoder // Attempt to decode the sound using the browser's native audio data
// If that fails, attempt to decode as ADPCM // decoder If that fails, attempt to decode as ADPCM
return decodeAudioData(this.audioContext, bufferCopy1).then( const decoding = decodeAudioData(this.audioContext, bufferCopy1)
addDecodedAudio, .catch(() => {
() => {
// The audio context failed to parse the sound data // The audio context failed to parse the sound data
// we gave it, so try to decode as 'adpcm' // we gave it, so try to decode as 'adpcm'
// First we need to create another copy of our original data // First we need to create another copy of our original data
const bufferCopy2 = sound.data.buffer.slice(0); const bufferCopy2 = sound.data.buffer.slice(0);
// Try decoding as adpcm // Try decoding as adpcm
return (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy2) return new ADPCMSoundDecoder(this.audioContext).decode(bufferCopy2);
})
.then( .then(
addDecodedAudio, buffer => ([soundId, buffer]),
error => { error => {
log.warn('audio data could not be decoded', error); log.warn('audio data could not be decoded', error);
} }
); );
return decoding;
} }
);
/**
* Decode a sound, decompressing it into audio samples.
*
* 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
* @param {Buffer} sound.data - sound data loaded from scratch-storage
* @returns {?Promise} - a promise which will resolve to the sound id
*/
decodeSound (sound) {
return this._decodeSound(sound)
.then(([id, buffer]) => {
this.audioBuffers[id] = buffer;
return id;
});
}
/**
* Decode a sound, decompressing it into audio samples.
*
* Create a SoundPlayer instance that can be used to play the sound and
* stop and fade out playback.
*
* @param {object} sound - an object containing audio data and metadata for
* a sound
* @param {Buffer} sound.data - sound data loaded from scratch-storage
* @returns {?Promise} - a promise which will resolve to the buffer
*/
decodeSoundPlayer (sound) {
return this._decodeSound(sound)
.then(([id, buffer]) => new SoundPlayer(this, {id, buffer}));
} }
/** /**
@ -145,6 +182,11 @@ class AudioEngine {
* @return {AudioBuffer} the buffer corresponding to the given sound id. * @return {AudioBuffer} the buffer corresponding to the given sound id.
*/ */
getSoundBuffer (soundId) { getSoundBuffer (soundId) {
// todo: Deprecate audioBuffers. If something wants to hold onto the
// buffer, it should. Otherwise buffers need to be able to release their
// decoded memory to avoid running out of memory which is possible with
// enough large audio buffers as they are full 16bit pcm waveforms for
// each audio channel.
return this.audioBuffers[soundId]; return this.audioBuffers[soundId];
} }