add: global cat volumes

This commit is contained in:
Tom Boullay
2026-05-09 23:37:07 +01:00
parent 53add29a48
commit ce67d07107
+35 -3
View File
@@ -3,6 +3,12 @@ import { logger } from "@/utils/core/logger";
export type AudioCategory = "music" | "sfx" | "dialogue"; export type AudioCategory = "music" | "sfx" | "dialogue";
export type OneShotAudioCategory = Exclude<AudioCategory, "music">; export type OneShotAudioCategory = Exclude<AudioCategory, "music">;
const DEFAULT_CATEGORY_VOLUMES: Record<AudioCategory, number> = {
music: 1,
sfx: 1,
dialogue: 1,
};
interface PlaySoundOptions { interface PlaySoundOptions {
category?: OneShotAudioCategory; category?: OneShotAudioCategory;
playbackRate?: number; playbackRate?: number;
@@ -11,8 +17,12 @@ interface PlaySoundOptions {
export class AudioManager { export class AudioManager {
private static _instance: AudioManager | null = null; private static _instance: AudioManager | null = null;
private readonly _audioPools = new Map<string, HTMLAudioElement[]>(); private readonly _audioPools = new Map<string, HTMLAudioElement[]>();
private readonly _categoryVolumes: Record<AudioCategory, number> = {
...DEFAULT_CATEGORY_VOLUMES,
};
private _music: HTMLAudioElement | null = null; private _music: HTMLAudioElement | null = null;
private _musicPath: string | null = null; private _musicPath: string | null = null;
private _musicVolume = 1;
private _musicUnlockHandler: (() => void) | null = null; private _musicUnlockHandler: (() => void) | null = null;
private static readonly MAX_POOL_SIZE_PER_SOUND = 6; private static readonly MAX_POOL_SIZE_PER_SOUND = 6;
@@ -32,10 +42,22 @@ export class AudioManager {
private constructor() {} 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 { playSound(path: string, volume = 1, options: PlaySoundOptions = {}): void {
const audio = this._acquireAudio(path); const audio = this._acquireAudio(path);
const category = options.category ?? AudioManager.DEFAULT_SOUND_CATEGORY; 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.playbackRate = options.playbackRate ?? 1;
audio.currentTime = 0; audio.currentTime = 0;
@@ -56,8 +78,10 @@ export class AudioManager {
} }
playMusic(path: string, volume = 1): void { playMusic(path: string, volume = 1): void {
this._musicVolume = AudioManager._clampVolume(volume);
if (this._musicPath === path && this._music) { 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; if (!this._music.paused) return;
} else { } else {
this.stopMusic(); this.stopMusic();
@@ -66,7 +90,7 @@ export class AudioManager {
this._musicPath = path; 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) => { void this._music.play().catch((error: unknown) => {
if ( if (
@@ -151,6 +175,14 @@ export class AudioManager {
this._musicUnlockHandler = null; 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 { private static _toLogValue(error: unknown): Error | DOMException | string {
if (error instanceof Error || error instanceof DOMException) { if (error instanceof Error || error instanceof DOMException) {
return error; return error;