mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 22:12:48 -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 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue