starry / backend /libs /three /audio /Audio.js
k-l-lambda's picture
feat: add Python ML services (CPU mode) with model download
2b7aae2
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 };