import { Object3D } from '../core/Object3D.js'; class Audio extends Object3D { constructor(listener) { super(); this.type = 'Audio'; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = 'empty'; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = 'audioNode'; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = 'mediaNode'; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = 'mediaStreamNode'; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = 'buffer'; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn('THREE.Audio: Audio is already playing.'); return; } if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this._progress = 0; this.source.stop(); this.source.onended = null; this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } export { Audio };