@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
203 lines (150 loc) • 4.81 kB
JavaScript
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();
}
}