UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

203 lines (150 loc) 4.81 kB
import { loadSoundTrackAsset } from "./loadSoundTrackAsset.js"; import { SoundTrackFlags } from "./SoundTrackFlags.js"; export class SoundEmitterComponentContext { constructor() { /** * * @type {SoundEmitter} */ this.emitter = null; /** * * @type {Transform} */ this.transform = null; /** * * @type {SoundEmitterSystem} */ this.system = null; /** * * @type {AudioNode} */ this.targetNode = null; /** * * @type {boolean} * @private */ this.__isConnected = false; } /** * * @param {SoundEmitterComponentContext} other */ compare(other) { return this.emitter.compare(other.emitter); } update() { const position = this.transform.position; /** * * @type {SoundEmitter} */ const emitter = this.emitter; const x = position.x; const y = position.y; const z = position.z; emitter.updatePosition(x, y, z); } /** * * @param {SoundTrack} soundTrack */ addTrack(soundTrack) { const system = this.system; const emitter = this.emitter; const context = system.webAudioContext; const assetManager = system.assetManager; const targetNode = emitter.nodes.volume; soundTrack.initializeNodes(context); const nodes = soundTrack.nodes; //connect to target nodes.volume.connect(targetNode); nodes.volume.gain.setValueAtTime(soundTrack.volume, 0); loadSoundTrackAsset(soundTrack, assetManager, (asset) => { soundTrack.buffer = asset.create(); if (soundTrack.getFlag(SoundTrackFlags.StartWhenReady)) { if (this.__isConnected) { soundTrack.start(context.currentTime); } else { soundTrack.setFlag(SoundTrackFlags.Playing); } } }, console.error); soundTrack.on.ended.add(emitter.tracks.removeOneOf, emitter.tracks); } /** * * @param {SoundTrack} track */ removeTrack(track) { /** * * @type {GainNode} */ const volume = track.nodes.volume; volume.disconnect(); const emitter = this.emitter; track.on.ended.remove(emitter.tracks.removeOneOf, emitter.tracks); } /** * @param {SoundTrack} t */ suspendTrack(t) { if (t.getFlag(SoundTrackFlags.Playing)) { t.suspend(); } } /** * * @param {SoundTrack} t */ resumeTrack(t) { if (t.getFlag(SoundTrackFlags.Playing)) { t.start(); } } connect() { if (this.__isConnected) { return; } const emitter = this.emitter; const targetNode = emitter.getTargetNode(); targetNode.connect(this.targetNode); // update all track times emitter.tracks.forEach(this.resumeTrack); this.__isConnected = true; } disconnect() { if (!this.__isConnected) { return; } const emitter = this.emitter; const targetNode = emitter.getTargetNode(); if (targetNode !== null) { targetNode.disconnect(); } /* The reason we suspend all tracks is to be able to track progression of a track after it is disconnected From experiments it appears that at least on Chrome v111 disconnected sources do not advance time, which leads to dis-synchronization when we later reconnected To circumvent this issue we introduced "Suspended" flag to keep track of timing on disconnected tracks */ emitter.tracks.forEach(this.suspendTrack, this) this.__isConnected = false; } link() { this.transform.position.onChanged.add(this.update, this); this.emitter.tracks.forEach(this.addTrack, this); this.emitter.tracks.on.added.add(this.addTrack, this); this.emitter.tracks.on.removed.add(this.removeTrack, this); } unlink() { this.transform.position.onChanged.remove(this.update, this); this.emitter.tracks.on.added.remove(this.addTrack, this); this.emitter.tracks.on.removed.remove(this.removeTrack, this); this.emitter.tracks.forEach(this.removeTrack, this); this.disconnect(); } }