2024-12-01 12:40:07 +01:00

127 lines
3.6 KiB
TypeScript

import { AudioSource } from "./audiosource";
import { getApp } from "../olympusapp";
import { AudioSourcesChangedEvent } from "../events";
export class FileSource extends AudioSource {
#file: File;
#filename: string;
#source: AudioBufferSourceNode;
#duration: number = 0;
#currentPosition: number = 0;
#updateInterval: any;
#lastUpdateTime: number = 0;
#playing = false;
#audioBuffer: AudioBuffer;
#restartTimeout: any;
#looping = false;
constructor(file) {
super();
this.#file = file;
this.#filename = this.#file?.name;
this.setName(this.#file?.name ?? "N/A");
/* Create the file reader and read the file from disk */
var reader = new FileReader();
reader.onload = (e) => {
var contents = e.target?.result;
if (contents) {
getApp()
.getAudioManager()
.getAudioContext()
/* Decode the audio file. This method takes care of codecs */
.decodeAudioData(contents as ArrayBuffer, (audioBuffer) => {
this.#audioBuffer = audioBuffer;
this.#duration = audioBuffer.duration;
});
}
};
reader.readAsArrayBuffer(this.#file);
}
play() {
/* A new buffer source must be created every time the file is played */
this.#source = getApp().getAudioManager().getAudioContext().createBufferSource();
this.#source.buffer = this.#audioBuffer;
this.#source.connect(this.getOutputNode());
this.#source.loop = this.#looping;
/* Start playing the file at the selected position */
this.#source.start(0, this.#currentPosition);
this.#playing = true;
const now = Date.now() / 1000;
this.#lastUpdateTime = now;
AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
this.#updateInterval = setInterval(() => {
/* Update the current position value every second */
const now = Date.now() / 1000;
this.#currentPosition += now - this.#lastUpdateTime;
this.#lastUpdateTime = now;
if (this.#currentPosition > this.#duration) {
this.#currentPosition = 0;
if (!this.#looping) this.pause();
}
AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}, 1000);
}
pause() {
/* Disconnect the source and update the position to the current time (precisely)*/
this.#source.stop();
this.#source.disconnect();
this.#playing = false;
const now = Date.now() / 1000;
this.#currentPosition += now - this.#lastUpdateTime;
clearInterval(this.#updateInterval);
AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getPlaying() {
return this.#playing;
}
getCurrentPosition() {
return this.#currentPosition;
}
getDuration() {
return this.#duration;
}
setCurrentPosition(percentPosition) {
/* To change the current play position we must:
1) pause the current playback;
2) update the current position value;
3) after some time, restart playing. The delay is needed to avoid immediately restarting many times if the user drags the position slider;
*/
if (this.#playing) {
clearTimeout(this.#restartTimeout);
this.#restartTimeout = setTimeout(() => this.play(), 1000);
}
this.pause();
this.#currentPosition = (percentPosition / 100) * this.#duration;
}
setLooping(looping) {
this.#looping = looping;
if (this.#source) this.#source.loop = looping;
AudioSourcesChangedEvent.dispatch(getApp().getAudioManager().getSources());
}
getLooping() {
return this.#looping;
}
getFilename() {
return this.#filename;
}
}