Merge pull request #1972 from mzgoddard/sound-bank-over-sprite

Sound bank over sprite
This commit is contained in:
Michael "Z" Goddard 2019-03-04 12:51:10 -05:00 committed by GitHub
commit 33f4482127
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 22 deletions

View file

@ -8,10 +8,10 @@ const log = require('../util/log');
* @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 {Sprite} sprite - Scratch sprite to add sounds to.
* @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, sprite) {
const loadSoundFromAsset = function (sound, soundAsset, runtime, soundBank) {
sound.assetId = soundAsset.assetId;
if (!runtime.audioEngine) {
log.error('No audio engine present; cannot load sound asset: ', sound.md5);
@ -30,8 +30,8 @@ const loadSoundFromAsset = function (sound, soundAsset, runtime, sprite) {
sound.rate = soundBuffer.sampleRate;
sound.sampleCount = soundBuffer.length;
if (sprite.soundBank !== null) {
sprite.soundBank.addSoundPlayer(soundPlayer);
if (soundBank !== null) {
soundBank.addSoundPlayer(soundPlayer);
}
return sound;
@ -44,10 +44,10 @@ const loadSoundFromAsset = function (sound, soundAsset, runtime, sprite) {
* @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 {Sprite} sprite - Scratch sprite to add sounds to.
* @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, sprite) {
const loadSound = function (sound, runtime, soundBank) {
if (!runtime.storage) {
log.error('No storage module present; cannot load sound asset: ', sound.md5);
return Promise.resolve(sound);
@ -61,7 +61,7 @@ const loadSound = function (sound, runtime, sprite) {
runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext)
).then(soundAsset => {
sound.asset = soundAsset;
return loadSoundFromAsset(sound, soundAsset, runtime, sprite);
return loadSoundFromAsset(sound, soundAsset, runtime, soundBank);
});
};

View file

@ -399,9 +399,10 @@ const parseMonitorObject = (object, runtime, targets, extensions) => {
* @param {!Runtime} runtime - Runtime object to load all structures into.
* @param {boolean} topLevel - Whether this is the top-level object (stage).
* @param {?object} zip - Optional zipped assets for local file import
* @return {?{costumePromises:Array.<Promise>,soundPromises:Array.<Promise>,children:object}}
* @return {?{costumePromises:Array.<Promise>,soundPromises:Array.<Promise>,soundBank:SoundBank,children:object}}
* Object of arrays of promises and child objects for asset objects used in
* Sprites. null for unsupported objects.
* Sprites. As well as a SoundBank for the sound assets. null for unsupported
* objects.
*/
const parseScratchAssets = function (object, runtime, topLevel, zip) {
if (!object.hasOwnProperty('objName')) {
@ -409,7 +410,12 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
return null;
}
const assets = {costumePromises: [], soundPromises: [], children: []};
const assets = {
costumePromises: [],
soundPromises: [],
soundBank: runtime.audioEngine && runtime.audioEngine.createBank(),
children: []
};
// Costumes from JSON.
const costumePromises = assets.costumePromises;
@ -457,7 +463,7 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
}
}
// Sounds from JSON
const soundPromises = assets.soundPromises;
const {soundBank, soundPromises} = assets;
if (object.hasOwnProperty('sounds')) {
for (let s = 0; s < object.sounds.length; s++) {
const soundSource = object.sounds[s];
@ -486,7 +492,10 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
// the file name of the sound should be the soundID (provided from the project.json)
// followed by the file ext
const assetFileName = `${soundSource.soundID}.${ext}`;
soundPromises.push(deserializeSound(sound, runtime, zip, assetFileName).then(() => sound));
soundPromises.push(
deserializeSound(sound, runtime, zip, assetFileName)
.then(() => loadSound(sound, runtime, soundBank))
);
}
}
@ -546,11 +555,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
// Costumes from JSON.
const costumePromises = assets.costumePromises;
// Sounds from JSON
const soundPromises = assets.soundPromises;
for (let s = 0; s < soundPromises.length; s++) {
soundPromises[s] = soundPromises[s]
.then(sound => loadSound(sound, runtime, sprite));
}
const {soundBank, soundPromises} = assets;
// Create the first clone, and load its run-state from JSON.
const target = sprite.createClone(topLevel ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
@ -738,6 +743,8 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
Promise.all(soundPromises).then(sounds => {
sprite.sounds = sounds;
// Make sure if soundBank is undefined, sprite.soundBank is then null.
sprite.soundBank = soundBank || null;
});
// The stage will have child objects; recursively process them.

View file

@ -914,7 +914,7 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
// any translation that needs to happen will happen in the process
// of building up the costume object into an sb3 format
return deserializeSound(sound, runtime, zip)
.then(() => loadSound(sound, runtime, sprite));
.then(() => loadSound(sound, runtime, sprite.soundBank));
// Only attempt to load the sound after the deserialization
// process has been completed.
});

View file

@ -153,7 +153,7 @@ class Sprite {
newSprite.sounds = this.sounds.map(sound => {
const newSound = Object.assign({}, sound);
const soundAsset = sound.asset;
assetPromises.push(loadSoundFromAsset(newSound, soundAsset, this.runtime, newSprite));
assetPromises.push(loadSoundFromAsset(newSound, soundAsset, this.runtime, newSprite.soundBank));
return newSound;
});

View file

@ -678,7 +678,7 @@ class VirtualMachine extends EventEmitter {
duplicateSound (soundIndex) {
const originalSound = this.editingTarget.getSounds()[soundIndex];
const clone = Object.assign({}, originalSound);
return loadSound(clone, this.runtime, this.editingTarget.sprite).then(() => {
return loadSound(clone, this.runtime, this.editingTarget.sprite.soundBank).then(() => {
this.editingTarget.addSound(clone, soundIndex + 1);
this.emitTargetsUpdate();
});
@ -723,7 +723,7 @@ class VirtualMachine extends EventEmitter {
const target = optTargetId ? this.runtime.getTargetById(optTargetId) :
this.editingTarget;
if (target) {
return loadSound(soundObject, this.runtime, target.sprite).then(() => {
return loadSound(soundObject, this.runtime, target.sprite.soundBank).then(() => {
target.addSound(soundObject);
this.emitTargetsUpdate();
});
@ -1250,7 +1250,7 @@ class VirtualMachine extends EventEmitter {
const originalSound = this.editingTarget.getSounds()[soundIndex];
const clone = Object.assign({}, originalSound);
const target = this.runtime.getTargetById(targetId);
return loadSound(clone, this.runtime, target.sprite).then(() => {
return loadSound(clone, this.runtime, target.sprite.soundBank).then(() => {
if (target) {
target.addSound(clone);
this.emitTargetsUpdate();