Spaces:
Running
Running
const StringUtil = require('../util/string-util'); | |
const log = require('../util/log'); | |
/** | |
* Initialize a sound from an asset asynchronously. | |
* @param {!object} sound - the Scratch sound object. | |
* @property {string} md5 - the MD5 and extension of the sound to be loaded. | |
* @property {Buffer} data - sound data will be written here once loaded. | |
* @param {!Asset} soundAsset - the asset loaded from storage. | |
* @param {!Runtime} runtime - Scratch runtime, used to access the storage module. | |
* @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to. | |
* @returns {!Promise} - a promise which will resolve to the sound when ready. | |
*/ | |
const loadSoundFromAsset = function (sound, soundAsset, runtime, soundBank) { | |
sound.assetId = soundAsset.assetId; | |
if (!runtime.audioEngine) { | |
log.warn('No audio engine present; cannot load sound asset: ', sound.md5); | |
return Promise.resolve(sound); | |
} | |
return runtime.audioEngine.decodeSoundPlayer(Object.assign( | |
{}, | |
sound, | |
{data: soundAsset.data} | |
)).then(soundPlayer => { | |
sound.soundId = soundPlayer.id; | |
// Set the sound sample rate and sample count based on the | |
// the audio buffer from the audio engine since the sound | |
// gets resampled by the audio engine | |
const soundBuffer = soundPlayer.buffer; | |
sound.rate = soundBuffer.sampleRate; | |
sound.sampleCount = soundBuffer.length; | |
if (soundBank !== null) { | |
soundBank.addSoundPlayer(soundPlayer); | |
} | |
if (runtime.isPackaged) { | |
sound.asset = null; | |
} | |
return sound; | |
}); | |
}; | |
// Handle sound loading errors by replacing the runtime sound with the | |
// default sound from storage, but keeping track of the original sound metadata | |
// in a `broken` field | |
const handleSoundLoadError = function (sound, runtime, soundBank) { | |
// Keep track of the old asset information until we're done loading the default sound | |
const oldAsset = sound.asset; // could be null | |
const oldAssetId = sound.assetId; | |
const oldSample = sound.sampleCount; | |
const oldRate = sound.rate; | |
const oldFormat = sound.format; | |
const oldDataFormat = sound.dataFormat; | |
// Use default asset if original fails to load | |
sound.assetId = runtime.storage.defaultAssetId.Sound; | |
sound.asset = runtime.storage.get(sound.assetId); | |
sound.md5 = `${sound.assetId}.${sound.asset.dataFormat}`; | |
return loadSoundFromAsset(sound, sound.asset, runtime, soundBank).then(loadedSound => { | |
loadedSound.broken = {}; | |
loadedSound.broken.assetId = oldAssetId; | |
loadedSound.broken.md5 = `${oldAssetId}.${oldDataFormat}`; | |
// Should be null if we got here because the sound was missing | |
loadedSound.broken.asset = oldAsset; | |
loadedSound.broken.sampleCount = oldSample; | |
loadedSound.broken.rate = oldRate; | |
loadedSound.broken.format = oldFormat; | |
loadedSound.broken.dataFormat = oldDataFormat; | |
return loadedSound; | |
}); | |
}; | |
/** | |
* Load a sound's asset into memory asynchronously. | |
* @param {!object} sound - the Scratch sound object. | |
* @property {string} md5 - the MD5 and extension of the sound to be loaded. | |
* @property {Buffer} data - sound data will be written here once loaded. | |
* @param {!Runtime} runtime - Scratch runtime, used to access the storage module. | |
* @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to. | |
* @returns {!Promise} - a promise which will resolve to the sound when ready. | |
*/ | |
const loadSound = function (sound, runtime, soundBank) { | |
if (!runtime.storage) { | |
log.warn('No storage module present; cannot load sound asset: ', sound.md5); | |
return Promise.resolve(sound); | |
} | |
const idParts = StringUtil.splitFirst(sound.md5, '.'); | |
const md5 = idParts[0]; | |
const ext = idParts[1].toLowerCase(); | |
sound.dataFormat = ext; | |
return ( | |
(sound.asset && Promise.resolve(sound.asset)) || | |
runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext) | |
) | |
.then(soundAsset => { | |
sound.asset = soundAsset; | |
if (!soundAsset) { | |
log.warn('Failed to find sound data: ', sound.md5); | |
return handleSoundLoadError(sound, runtime, soundBank); | |
} | |
return loadSoundFromAsset(sound, soundAsset, runtime, soundBank); | |
}) | |
.catch(e => { | |
log.warn(`Failed to load sound: ${sound.md5} with error: ${e}`); | |
return handleSoundLoadError(sound, runtime, soundBank); | |
}); | |
}; | |
module.exports = { | |
loadSound, | |
loadSoundFromAsset | |
}; | |