UNPKG

4.8 kBJavaScriptView Raw
1const log = require('./log');
2
3/**
4 * A symbol indicating all targets are to be effected.
5 * @const {string}
6 */
7const ALL_TARGETS = '*';
8
9class SoundBank {
10 /**
11 * A bank of sounds that can be played.
12 * @constructor
13 * @param {AudioEngine} audioEngine - related AudioEngine
14 * @param {EffectChain} effectChainPrime - original EffectChain cloned for
15 * playing sounds
16 */
17 constructor (audioEngine, effectChainPrime) {
18 /**
19 * AudioEngine this SoundBank is related to.
20 * @type {AudioEngine}
21 */
22 this.audioEngine = audioEngine;
23
24 /**
25 * Map of ids to soundPlayers.
26 * @type {object<SoundPlayer>}
27 */
28 this.soundPlayers = {};
29
30 /**
31 * Map of targets by sound id.
32 * @type {Map<string, Target>}
33 */
34 this.playerTargets = new Map();
35
36 /**
37 * Map of effect chains by sound id.
38 * @type {Map<string, EffectChain}
39 */
40 this.soundEffects = new Map();
41
42 /**
43 * Original EffectChain cloned for every playing sound.
44 * @type {EffectChain}
45 */
46 this.effectChainPrime = effectChainPrime;
47 }
48
49 /**
50 * Add a sound player instance likely from AudioEngine.decodeSoundPlayer
51 * @param {SoundPlayer} soundPlayer - SoundPlayer to add
52 */
53 addSoundPlayer (soundPlayer) {
54 this.soundPlayers[soundPlayer.id] = soundPlayer;
55 }
56
57 /**
58 * Get a sound player by id.
59 * @param {string} soundId - sound to look for
60 * @returns {SoundPlayer} instance of sound player for the id
61 */
62 getSoundPlayer (soundId) {
63 if (!this.soundPlayers[soundId]) {
64 log.error(`SoundBank.getSoundPlayer(${soundId}): called missing sound in bank`);
65 }
66
67 return this.soundPlayers[soundId];
68 }
69
70 /**
71 * Get a sound EffectChain by id.
72 * @param {string} sound - sound to look for an EffectChain
73 * @returns {EffectChain} available EffectChain for this id
74 */
75 getSoundEffects (sound) {
76 if (!this.soundEffects.has(sound)) {
77 this.soundEffects.set(sound, this.effectChainPrime.clone());
78 }
79
80 return this.soundEffects.get(sound);
81 }
82
83 /**
84 * Play a sound.
85 * @param {Target} target - Target to play for
86 * @param {string} soundId - id of sound to play
87 * @returns {Promise} promise that resolves when the sound finishes playback
88 */
89 playSound (target, soundId) {
90 const effects = this.getSoundEffects(soundId);
91 const player = this.getSoundPlayer(soundId);
92
93 if (this.playerTargets.get(soundId) !== target) {
94 // make sure to stop the old sound, effectively "forking" the output
95 // when the target switches before we adjust it's effects
96 player.stop();
97 }
98
99 this.playerTargets.set(soundId, target);
100 effects.addSoundPlayer(player);
101 effects.setEffectsFromTarget(target);
102 player.connect(effects);
103
104 player.play();
105
106 return player.finished();
107 }
108
109 /**
110 * Set the effects (pan, pitch, and volume) from values on the given target.
111 * @param {Target} target - target to set values from
112 */
113 setEffects (target) {
114 this.playerTargets.forEach((playerTarget, key) => {
115 if (playerTarget === target) {
116 this.getSoundEffects(key).setEffectsFromTarget(target);
117 }
118 });
119 }
120
121 /**
122 * Stop playback of sound by id if was lasted played by the target.
123 * @param {Target} target - target to check if it last played the sound
124 * @param {string} soundId - id of the sound to stop
125 */
126 stop (target, soundId) {
127 if (this.playerTargets.get(soundId) === target) {
128 this.soundPlayers[soundId].stop();
129 }
130 }
131
132 /**
133 * Stop all sounds for all targets or a specific target.
134 * @param {Target|string} target - a symbol for all targets or the target
135 * to stop sounds for
136 */
137 stopAllSounds (target = ALL_TARGETS) {
138 this.playerTargets.forEach((playerTarget, key) => {
139 if (target === ALL_TARGETS || playerTarget === target) {
140 this.getSoundPlayer(key).stop();
141 }
142 });
143 }
144
145 /**
146 * Dispose of all EffectChains and SoundPlayers.
147 */
148 dispose () {
149 this.playerTargets.clear();
150 this.soundEffects.forEach(effects => effects.dispose());
151 this.soundEffects.clear();
152 for (const soundId in this.soundPlayers) {
153 if (Object.prototype.hasOwnProperty.call(this.soundPlayers, soundId)) {
154 this.soundPlayers[soundId].dispose();
155 }
156 }
157 this.soundPlayers = {};
158 }
159
160}
161
162module.exports = SoundBank;