Spaces:
Sleeping
Sleeping
| 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 }; | |