// an audio source // This is the actual sound import { SourceType } from './source-type'; export default class AudioSource { constructor(graph, scene, context, buffer = null, type = SourceType.WorldSource) { this.position = { x: 0, y: 0, z: 0 }; this.buffer = buffer; this.context = context; this.scene = scene; this.graph = graph; this.type = type; this.playbackRate = 1; this.volume = 1; this.init(); } init() { this.gain = this.context.createGain(); // bind methods so we can add and removve them from event listeners this.stop = this.stop.bind(this); } getBuffer() { return this.buffer; } setBuffer(data) { this.buffer = data; if (this.playOnLoad) { this.play(); this.playOnLoad = false; } } play(when = 0, offset = 0, duration = this.buffer ? this.buffer.duration : 0) { if (this.playing && this.node) { this.stop(); } if (!this.buffer) { this.playOnLoad = true; return; } if (!this.node) { this.node = this.context.createBufferSource(); this.node.buffer = this.buffer; this.createConnections(); } if (this.node) { this.node.playbackRate.value = this.playbackRate; this.node.start(when, offset, duration); this.node.loop = this.looping; this.playing = true; if (this.sceneNode) { this.sceneNode.setPosition(this.position.x, this.position.y, this.position.z); } this.node.addEventListener('ended', this.stop); } } setPosition(x, y, z) { this.position = { x, y, z }; if (this.sceneNode) this.sceneNode.setPosition(x, y, z); } setPlaybackRate(rate) { this.playbackRate = rate; if (this.node) this.node.playbackRate.value = rate; } getPlaybackRate() { return this.playbackRate; } setVolume(volume) { this.volume = volume; if (this.gain) this.gain.gain.value = volume; } getVolume() { return this.volume; } createConnections() { switch (this.type) { case SourceType.WorldSource: if (!this.sceneNode) { this.sceneNode = this.scene.createSource(); } this.node.connect(this.gain); this.gain.connect(this.sceneNode); break; case SourceType.UISource: this.node.connect(this.gain); this.graph.connectToUI(this.gain); break; default: this.node.connect(this.gain); this.graph.connectToMaster(this.gain); break; } } stop() { this.playing = false; if (this.node) { this.node.removeEventListener('ended', this.stop); this.node.stop(); this.node.disconnect(); this.node = null; this.playing = false; if (this.sceneNode) { this.sceneNode.disconnect(); this.sceneNode = null; } } } destroy() { this.stop(); // set all refs to null to encourage gc this.node = null; this.sceneNode = null; this.buffer = null; this.context = null; this.graph = null; this.scene = null; } loop(value) { this.looping = value; if (this.node) { this.node.loop = value; } } fadeOut(time) { this.gain.gain.setValueAtTime(this.getVolume(), this.context.getContext().currentTime); if (!this.node) { return; } this.gain.gain.exponentialRampToValueAtTime(0.0001, this.context.getContext().currentTime + time); setTimeout(() => this.stop(), time * 1000); } fadeIn(time) { this.gain.gain.setValueAtTime(0.0001, this.context.getContext().currentTime); if (!this.node) { this.play(); } this.gain.gain.exponentialRampToValueAtTime(this.volume, this.context.getContext().currentTime + time); } }