From 41cb61ae2d834c644db3c9e9b0415c05d4750ad2 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Sat, 9 May 2026 23:37:07 +0100 Subject: [PATCH] add: global cat volumes --- src/managers/AudioManager.ts | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/managers/AudioManager.ts b/src/managers/AudioManager.ts index e0237b4..4e5d4bb 100644 --- a/src/managers/AudioManager.ts +++ b/src/managers/AudioManager.ts @@ -3,6 +3,12 @@ import { logger } from "@/utils/core/logger"; export type AudioCategory = "music" | "sfx" | "dialogue"; export type OneShotAudioCategory = Exclude; +const DEFAULT_CATEGORY_VOLUMES: Record = { + music: 1, + sfx: 1, + dialogue: 1, +}; + interface PlaySoundOptions { category?: OneShotAudioCategory; playbackRate?: number; @@ -11,8 +17,12 @@ interface PlaySoundOptions { export class AudioManager { private static _instance: AudioManager | null = null; private readonly _audioPools = new Map(); + private readonly _categoryVolumes: Record = { + ...DEFAULT_CATEGORY_VOLUMES, + }; private _music: HTMLAudioElement | null = null; private _musicPath: string | null = null; + private _musicVolume = 1; private _musicUnlockHandler: (() => void) | null = null; private static readonly MAX_POOL_SIZE_PER_SOUND = 6; @@ -32,10 +42,22 @@ export class AudioManager { private constructor() {} + setCategoryVolume(category: AudioCategory, volume: number): void { + this._categoryVolumes[category] = AudioManager._clampVolume(volume); + + if (category === "music" && this._music) { + this._music.volume = this._getEffectiveVolume("music", this._musicVolume); + } + } + + getCategoryVolume(category: AudioCategory): number { + return this._categoryVolumes[category]; + } + playSound(path: string, volume = 1, options: PlaySoundOptions = {}): void { const audio = this._acquireAudio(path); const category = options.category ?? AudioManager.DEFAULT_SOUND_CATEGORY; - audio.volume = Math.max(0, Math.min(1, volume)); + audio.volume = this._getEffectiveVolume(category, volume); audio.playbackRate = options.playbackRate ?? 1; audio.currentTime = 0; @@ -56,8 +78,10 @@ export class AudioManager { } playMusic(path: string, volume = 1): void { + this._musicVolume = AudioManager._clampVolume(volume); + if (this._musicPath === path && this._music) { - this._music.volume = Math.max(0, Math.min(1, volume)); + this._music.volume = this._getEffectiveVolume("music", this._musicVolume); if (!this._music.paused) return; } else { this.stopMusic(); @@ -66,7 +90,7 @@ export class AudioManager { this._musicPath = path; } - this._music.volume = Math.max(0, Math.min(1, volume)); + this._music.volume = this._getEffectiveVolume("music", this._musicVolume); void this._music.play().catch((error: unknown) => { if ( @@ -151,6 +175,14 @@ export class AudioManager { this._musicUnlockHandler = null; } + private _getEffectiveVolume(category: AudioCategory, volume: number): number { + return AudioManager._clampVolume(volume) * this._categoryVolumes[category]; + } + + private static _clampVolume(volume: number): number { + return Math.max(0, Math.min(1, volume)); + } + private static _toLogValue(error: unknown): Error | DOMException | string { if (error instanceof Error || error instanceof DOMException) { return error;