refactor: split sounds.ts into audio/ modules
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
94910820f8
commit
d998cb3ad0
@@ -0,0 +1,123 @@
|
||||
// Audio context, gain nodes, volume/mute management
|
||||
// This module has ZERO imports from other audio modules to prevent circular deps.
|
||||
|
||||
let ctx: AudioContext | null = null
|
||||
let musicGain: GainNode | null = null
|
||||
let sfxGain: GainNode | null = null
|
||||
let musicPlaying = false
|
||||
let musicTimeout: number | null = null
|
||||
|
||||
// AudioContext is ONLY created inside ensureAudioContext() (called from user gesture).
|
||||
// Before that, all SFX calls silently no-op to avoid Chrome autoplay warnings.
|
||||
let _audioUnlocked = false
|
||||
|
||||
let masterMuted = false
|
||||
const MUSIC_VOL = 0.12
|
||||
const SFX_VOL = 0.25
|
||||
|
||||
export function initCtx() {
|
||||
if (ctx) return
|
||||
ctx = new AudioContext()
|
||||
musicGain = ctx.createGain()
|
||||
musicGain.gain.value = masterMuted ? 0 : 0.12
|
||||
musicGain.connect(ctx.destination)
|
||||
sfxGain = ctx.createGain()
|
||||
sfxGain.gain.value = masterMuted ? 0 : 0.25
|
||||
sfxGain.connect(ctx.destination)
|
||||
}
|
||||
|
||||
export function getCtx(): AudioContext {
|
||||
if (!ctx) initCtx()
|
||||
if (ctx!.state === 'suspended') {
|
||||
ctx!.resume().catch(() => {})
|
||||
}
|
||||
return ctx!
|
||||
}
|
||||
|
||||
// Get the SFX destination node (gain is 0 when muted, so audio is silent but context stays valid)
|
||||
export function getSfxDest(): AudioNode {
|
||||
const c = getCtx()
|
||||
return sfxGain ?? c.destination
|
||||
}
|
||||
|
||||
export function getMusicGain(): GainNode | null {
|
||||
return musicGain
|
||||
}
|
||||
|
||||
export function getSfxGain(): GainNode | null {
|
||||
return sfxGain
|
||||
}
|
||||
|
||||
export function isMusicPlaying(): boolean {
|
||||
return musicPlaying
|
||||
}
|
||||
|
||||
export function setMusicPlaying(v: boolean) {
|
||||
musicPlaying = v
|
||||
}
|
||||
|
||||
export function getMusicTimeout(): number | null {
|
||||
return musicTimeout
|
||||
}
|
||||
|
||||
export function setMusicTimeout(v: number | null) {
|
||||
musicTimeout = v
|
||||
}
|
||||
|
||||
export function isAudioUnlocked(): boolean {
|
||||
return _audioUnlocked
|
||||
}
|
||||
|
||||
export function setAudioUnlocked(v: boolean) {
|
||||
_audioUnlocked = v
|
||||
}
|
||||
|
||||
export function isMasterMuted(): boolean {
|
||||
return masterMuted
|
||||
}
|
||||
|
||||
export function getMasterMuted(): boolean {
|
||||
return masterMuted
|
||||
}
|
||||
|
||||
export function setMusicVolume(v: number) {
|
||||
if (musicGain) musicGain.gain.value = Math.max(0, Math.min(1, v))
|
||||
}
|
||||
|
||||
export function setSfxVolume(v: number) {
|
||||
if (sfxGain) sfxGain.gain.value = Math.max(0, Math.min(1, v))
|
||||
}
|
||||
|
||||
export function setMasterMute(muted: boolean) {
|
||||
masterMuted = muted
|
||||
if (muted) {
|
||||
if (musicGain) musicGain.gain.value = 0
|
||||
if (sfxGain) sfxGain.gain.value = 0
|
||||
// Note: kokoroStop and speechSynthesis.cancel are called from the caller
|
||||
// to avoid circular deps — see index.ts setMasterMute wrapper
|
||||
} else {
|
||||
if (musicGain) musicGain.gain.value = MUSIC_VOL
|
||||
if (sfxGain) sfxGain.gain.value = SFX_VOL
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect gain nodes to instantly kill all in-flight oscillators/buffers,
|
||||
// then reconnect so future sounds still work
|
||||
export function resetGainNodes() {
|
||||
if (ctx) {
|
||||
if (musicGain) {
|
||||
musicGain.disconnect()
|
||||
musicGain = ctx.createGain()
|
||||
musicGain.gain.value = masterMuted ? 0 : MUSIC_VOL
|
||||
musicGain.connect(ctx.destination)
|
||||
}
|
||||
if (sfxGain) {
|
||||
sfxGain.disconnect()
|
||||
sfxGain = ctx.createGain()
|
||||
sfxGain.gain.value = masterMuted ? 0 : SFX_VOL
|
||||
sfxGain.connect(ctx.destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MUSIC_VOL, SFX_VOL }
|
||||
Reference in New Issue
Block a user