From 29263ae8fcc343f6cafce981f4ce02bfe536704f Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Sat, 1 Mar 2025 11:40:07 -0700 Subject: [PATCH] [MIRROR] ports 516 audio player from cmss (#10259) Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- tgui/packages/tgui-panel/audio/player.ts | 148 ++++++++++------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/tgui/packages/tgui-panel/audio/player.ts b/tgui/packages/tgui-panel/audio/player.ts index 6a26bc75b8..c9a690a160 100644 --- a/tgui/packages/tgui-panel/audio/player.ts +++ b/tgui/packages/tgui-panel/audio/player.ts @@ -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); } }