[MIRROR] ports 516 audio player from cmss (#10259)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-03-01 19:40:07 +01:00
committed by GitHub
co-authored by Kashargul
parent 0c363559ee
commit 29263ae8fc
+62 -86
View File
@@ -8,117 +8,93 @@ import { createLogger } from 'tgui/logging';
const logger = createLogger('AudioPlayer');
type CustomAudioElement = HTMLAudioElement & { stop: Function };
type AudioOptions = {
pitch?: number;
start?: number;
end?: number;
};
export class AudioPlayer {
node: CustomAudioElement;
playing: boolean;
element: HTMLAudioElement | null;
options: AudioOptions;
volume: number;
options: { pitch?: number; start?: number; end?: number };
onPlaySubscribers: Function[];
onStopSubscribers: Function[];
playbackInterval: NodeJS.Timeout;
onPlaySubscribers: { (): void }[];
onStopSubscribers: { (): void }[];
constructor() {
// Set up the HTMLAudioElement node
this.node = document.createElement('audio') as CustomAudioElement;
this.node.style.setProperty('display', 'none');
document.body.appendChild(this.node);
// Set up other properties
this.playing = false;
this.volume = 1;
this.options = {};
this.element = null;
this.onPlaySubscribers = [];
this.onStopSubscribers = [];
// Listen for playback start events
this.node.addEventListener('canplaythrough', () => {
logger.log('canplaythrough');
this.playing = true;
this.node.playbackRate = this.options.pitch || 1;
this.node.currentTime = this.options.start || 0;
this.node.volume = this.volume;
this.node.play();
for (let subscriber of this.onPlaySubscribers) {
subscriber();
}
});
// Listen for playback stop events
this.node.addEventListener('ended', () => {
logger.log('ended');
this.stop();
});
// Listen for playback errors
this.node.addEventListener('error', (e) => {
if (this.playing) {
logger.log('playback error', e.error);
this.stop();
}
});
// Check every second to stop the playback at the right time
this.playbackInterval = setInterval(() => {
if (!this.playing) {
return;
}
const shouldStop =
this.options.end &&
this.options.end > 0 &&
this.node.currentTime >= this.options.end;
if (shouldStop) {
this.stop();
}
}, 1000);
}
destroy() {
if (!this.node) {
return;
}
this.node.stop();
document.removeChild(this.node);
clearInterval(this.playbackInterval);
this.element = null;
}
play(url, options = {}) {
if (!this.node) {
return;
play(url: string, options: AudioOptions = {}) {
if (this.element) {
this.stop();
}
logger.log('playing', url, options);
this.options = options;
this.node.src = url;
const audio = (this.element = new Audio(url));
audio.volume = this.volume;
audio.playbackRate = this.options.pitch || 1;
logger.log('playing', url, options);
audio.addEventListener('ended', () => {
logger.log('ended');
this.stop();
});
audio.addEventListener('error', (error) => {
logger.log('playback error', error);
});
if (this.options.end) {
audio.addEventListener('timeupdate', () => {
if (
this.options.end &&
this.options.end > 0 &&
audio.currentTime >= this.options.end
) {
this.stop();
}
});
}
audio.play();
this.onPlaySubscribers.forEach((subscriber) => subscriber());
}
stop() {
if (!this.node) {
return;
}
if (this.playing) {
for (let subscriber of this.onStopSubscribers) {
subscriber();
}
}
if (!this.element) return;
logger.log('stopping');
this.playing = false;
this.node.src = '';
this.element.pause();
this.element = null;
this.onStopSubscribers.forEach((subscriber) => subscriber());
}
setVolume(volume) {
if (!this.node) {
return;
}
setVolume(volume: number): void {
this.volume = volume;
this.node.volume = volume;
if (!this.element) return;
this.element.volume = volume;
}
onPlay(subscriber) {
if (!this.node) {
return;
}
onPlay(subscriber: () => void): void {
this.onPlaySubscribers.push(subscriber);
}
onStop(subscriber) {
if (!this.node) {
return;
}
onStop(subscriber: () => void): void {
this.onStopSubscribers.push(subscriber);
}
}