mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 05:53:43 -05:00
comment SoundBank and EffectChain
This commit is contained in:
parent
7cef4e60a8
commit
c12a1a4766
2 changed files with 147 additions and 4 deletions
|
@ -1,21 +1,64 @@
|
|||
const log = require('./log');
|
||||
|
||||
/**
|
||||
* A symbol indicating all targets are to be effected.
|
||||
* @const {string}
|
||||
*/
|
||||
const ALL_TARGETS = '*';
|
||||
|
||||
class SoundBank {
|
||||
/**
|
||||
* A bank of sounds that can be played.
|
||||
* @constructor
|
||||
* @param {AudioEngine} audioEngine - related AudioEngine
|
||||
* @param {EffectChain} effectChainPrime - original EffectChain cloned for
|
||||
* playing sounds
|
||||
*/
|
||||
constructor (audioEngine, effectChainPrime) {
|
||||
/**
|
||||
* AudioEngine this SoundBank is related to.
|
||||
* @type {AudioEngine}
|
||||
*/
|
||||
this.audioEngine = audioEngine;
|
||||
|
||||
/**
|
||||
* Map of ids to soundPlayers.
|
||||
* @type {object<SoundPlayer>}
|
||||
*/
|
||||
this.soundPlayers = {};
|
||||
|
||||
/**
|
||||
* Map of targets by sound id.
|
||||
* @type {Map<string, Target>}
|
||||
*/
|
||||
this.playerTargets = new Map();
|
||||
|
||||
/**
|
||||
* Map of effect chains by sound id.
|
||||
* @type {Map<string, EffectChain}
|
||||
*/
|
||||
this.soundEffects = new Map();
|
||||
|
||||
/**
|
||||
* Original EffectChain cloned for every playing sound.
|
||||
* @type {EffectChain}
|
||||
*/
|
||||
this.effectChainPrime = effectChainPrime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sound player instance likely from AudioEngine.decodeSoundPlayer
|
||||
* @param {SoundPlayer} soundPlayer - SoundPlayer to add
|
||||
*/
|
||||
addSoundPlayer (soundPlayer) {
|
||||
this.soundPlayers[soundPlayer.id] = soundPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sound player by id.
|
||||
* @param {string} soundId - sound to look for
|
||||
* @returns {SoundPlayer} instance of sound player for the id
|
||||
*/
|
||||
getSoundPlayer (soundId) {
|
||||
if (!this.soundPlayers[soundId]) {
|
||||
log.error(`SoundBank.getSoundPlayer(${soundId}): called missing sound in bank`);
|
||||
|
@ -24,6 +67,11 @@ class SoundBank {
|
|||
return this.soundPlayers[soundId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sound EffectChain by id.
|
||||
* @param {string} sound - sound to look for an EffectChain
|
||||
* @returns {EffectChain} available EffectChain for this id
|
||||
*/
|
||||
getSoundEffects (sound) {
|
||||
if (!this.soundEffects.has(sound)) {
|
||||
this.soundEffects.set(sound, this.effectChainPrime.clone());
|
||||
|
@ -32,7 +80,12 @@ class SoundBank {
|
|||
return this.soundEffects.get(sound);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Play a sound.
|
||||
* @param {Target} target - Target to play for
|
||||
* @param {string} soundId - id of sound to play
|
||||
* @returns {Promise} promise that resolves when the sound finishes playback
|
||||
*/
|
||||
playSound (target, soundId) {
|
||||
const effects = this.getSoundEffects(soundId);
|
||||
const player = this.getSoundPlayer(soundId);
|
||||
|
@ -47,6 +100,10 @@ class SoundBank {
|
|||
return player.finished();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the effects (pan, pitch, and volume) from values on the given target.
|
||||
* @param {Target} target - target to set values from
|
||||
*/
|
||||
setEffects (target) {
|
||||
this.playerTargets.forEach((playerTarget, key) => {
|
||||
if (playerTarget === target) {
|
||||
|
@ -55,12 +112,22 @@ class SoundBank {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playback of sound by id if was lasted played by the target.
|
||||
* @param {Target} target - target to check if it last played the sound
|
||||
* @param {string} soundId - id of the sound to stop
|
||||
*/
|
||||
stop (target, soundId) {
|
||||
if (this.playerTargets.get(soundId) === target) {
|
||||
this.soundPlayers[soundId].stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all sounds for all targets or a specific target.
|
||||
* @param {Target|string} target - a symbol for all targets or the target
|
||||
* to stop sounds for
|
||||
*/
|
||||
stopAllSounds (target = ALL_TARGETS) {
|
||||
this.playerTargets.forEach((playerTarget, key) => {
|
||||
if (target === ALL_TARGETS || playerTarget === target) {
|
||||
|
@ -69,6 +136,9 @@ class SoundBank {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of all EffectChains and SoundPlayers.
|
||||
*/
|
||||
dispose () {
|
||||
this.playerTargets.clear();
|
||||
this.soundEffects.forEach(effects => effects.dispose());
|
||||
|
|
|
@ -1,15 +1,37 @@
|
|||
class EffectChain {
|
||||
/**
|
||||
* Chain of effects that can be applied to a group of SoundPlayers.
|
||||
* @param {AudioEngine} audioEngine - engine whose effects these belong to
|
||||
* @param {Array<Effect>} effects - array of Effect classes to construct
|
||||
*/
|
||||
constructor (audioEngine, effects) {
|
||||
/**
|
||||
* AudioEngine whose effects these belong to.
|
||||
* @type {AudioEngine}
|
||||
*/
|
||||
this.audioEngine = audioEngine;
|
||||
|
||||
/**
|
||||
* Node incoming connections will attach to. This node than connects to
|
||||
* the items in the chain which finally connect to some output.
|
||||
* @type {AudioNode}
|
||||
*/
|
||||
this.inputNode = this.audioEngine.audioContext.createGain();
|
||||
|
||||
/**
|
||||
* List of Effect types to create.
|
||||
* @type {Array<Effect>}
|
||||
*/
|
||||
this.effects = effects;
|
||||
|
||||
// Effects are instantiate in reverse so that the first refers to the
|
||||
// Effects are instantiated in reverse so that the first refers to the
|
||||
// second, the second refers to the third, etc and the last refers to
|
||||
// null.
|
||||
let lastEffect = null;
|
||||
/**
|
||||
* List of instantiated Effects.
|
||||
* @type {Array<Effect>}
|
||||
*/
|
||||
this._effects = effects
|
||||
.reverse()
|
||||
.map(Effect => {
|
||||
|
@ -20,12 +42,28 @@ class EffectChain {
|
|||
})
|
||||
.reverse();
|
||||
|
||||
/**
|
||||
* First effect of this chain.
|
||||
* @type {Effect}
|
||||
*/
|
||||
this.firstEffect = this._effects[0];
|
||||
|
||||
/**
|
||||
* Last effect of this chain.
|
||||
* @type {Effect}
|
||||
*/
|
||||
this.lastEffect = this._effects[this._effects.length - 1];
|
||||
|
||||
/**
|
||||
* A set of players this chain is managing.
|
||||
*/
|
||||
this._soundPlayers = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of the EffectChain.
|
||||
* @returns {EffectChain} a clone of this EffectChain
|
||||
*/
|
||||
clone () {
|
||||
const chain = new EffectChain(this.audioEngine, this.effects);
|
||||
if (this.target) {
|
||||
|
@ -34,6 +72,10 @@ class EffectChain {
|
|||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sound player.
|
||||
* @param {SoundPlayer} soundPlayer - a sound player to manage
|
||||
*/
|
||||
addSoundPlayer (soundPlayer) {
|
||||
if (!this._soundPlayers.has(soundPlayer)) {
|
||||
this._soundPlayers.add(soundPlayer);
|
||||
|
@ -41,16 +83,24 @@ class EffectChain {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a sound player.
|
||||
* @param {SoundPlayer} soundPlayer - a sound player to stop managing
|
||||
*/
|
||||
removeSoundPlayer (soundPlayer) {
|
||||
this._soundPlayers.remove(soundPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the audio input node.
|
||||
* @returns {AudioNode} audio node the upstream can connect to
|
||||
*/
|
||||
getInputNode () {
|
||||
return this.inputNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connnect this player's output to another audio node
|
||||
* Connnect this player's output to another audio node.
|
||||
* @param {object} target - target whose node to should be connected
|
||||
*/
|
||||
connect (target) {
|
||||
|
@ -70,11 +120,19 @@ class EffectChain {
|
|||
firstEffect.connect(target);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Array of SoundPlayers managed by this EffectChain.
|
||||
* @returns {Array<SoundPlayer>} sound players managed by this chain
|
||||
*/
|
||||
getSoundPlayers () {
|
||||
return [...this._soundPlayers];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Effect values with named values on target.soundEffects if it exist
|
||||
* and then from target itself.
|
||||
* @param {Target} target - target to set values from
|
||||
*/
|
||||
setEffectsFromTarget (target) {
|
||||
this._effects.forEach(effect => {
|
||||
if ('soundEffects' in target && effect.name in target.soundEffects) {
|
||||
|
@ -85,20 +143,35 @@ class EffectChain {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an effect value by its name.
|
||||
* @param {string} effect - effect name to change
|
||||
* @param {number} value - value to set effect to
|
||||
*/
|
||||
set (effect, value) {
|
||||
if (effect in this) {
|
||||
this[effect].set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update managed sound players with the effects on this chain.
|
||||
*/
|
||||
update () {
|
||||
this._effects.forEach(effect => effect.update());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all effects to their default values.
|
||||
*/
|
||||
clear () {
|
||||
this._effects.forEach(effect => effect.clear());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of all effects in this chain. Nothing is done to managed
|
||||
* SoundPlayers.
|
||||
*/
|
||||
dispose () {
|
||||
this._soundPlayers = null;
|
||||
this._effects.forEach(effect => effect.dispose());
|
||||
|
|
Loading…
Reference in a new issue