mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 14:02:29 -05:00
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:
parent
06a9af4dc3
commit
a5702a7d49
1 changed files with 66 additions and 24 deletions
|
@ -5,9 +5,9 @@ const log = require('./log');
|
|||
const uid = require('./uid');
|
||||
|
||||
const ADPCMSoundDecoder = require('./ADPCMSoundDecoder');
|
||||
|
||||
const AudioPlayer = require('./AudioPlayer');
|
||||
const Loudness = require('./Loudness');
|
||||
const SoundPlayer = require('./GreenPlayer');
|
||||
|
||||
/**
|
||||
* Wrapper to ensure that audioContext.decodeAudioData is a promise
|
||||
|
@ -103,40 +103,77 @@ class AudioEngine {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* @property {Buffer} data - sound data loaded from scratch-storage.
|
||||
* @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored.
|
||||
* @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 and
|
||||
* buffer if decoded
|
||||
*/
|
||||
decodeSound (sound) {
|
||||
// Make a copy of the buffer because decoding detaches the original buffer
|
||||
_decodeSound (sound) {
|
||||
// Make a copy of the buffer because decoding detaches the original
|
||||
// buffer
|
||||
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();
|
||||
// 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
|
||||
// If that fails, attempt to decode as ADPCM
|
||||
return decodeAudioData(this.audioContext, bufferCopy1).then(
|
||||
addDecodedAudio,
|
||||
() => {
|
||||
// Attempt to decode the sound using the browser's native audio data
|
||||
// decoder If that fails, attempt to decode as ADPCM
|
||||
const decoding = decodeAudioData(this.audioContext, bufferCopy1)
|
||||
.catch(() => {
|
||||
// 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)
|
||||
return new ADPCMSoundDecoder(this.audioContext).decode(bufferCopy2);
|
||||
})
|
||||
.then(
|
||||
addDecodedAudio,
|
||||
buffer => ([soundId, buffer]),
|
||||
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.
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue