@@ -19,6 +19,16 @@ export interface SoundManagerLike {
1919 removeByKey ?( key : string ) : void ;
2020}
2121
22+ type AudioContextLike = {
23+ state ?: string ;
24+ resume ?: ( ) => Promise < unknown > | unknown ;
25+ } ;
26+
27+ type UnlockableSoundManagerLike = SoundManagerLike & {
28+ context ?: AudioContextLike ;
29+ unlock ?: ( ) => void ;
30+ } ;
31+
2232function clamp01 ( value : number ) : number {
2333 if ( ! Number . isFinite ( value ) ) return 1 ;
2434 return Math . max ( 0 , Math . min ( 1 , value ) ) ;
@@ -33,7 +43,25 @@ export class BasicAudioService implements AudioService {
3343 private readonly getKey : ( assetId : string ) => string = ( assetId ) => `audio:${ assetId } ` ,
3444 ) { }
3545
46+ private resumeManagerIfNeeded ( ) : void {
47+ const manager = this . manager as UnlockableSoundManagerLike ;
48+ try {
49+ manager . unlock ?.( ) ;
50+ } catch {
51+ // ignore unlock errors
52+ }
53+ const context = manager . context ;
54+ if ( ! context ?. resume ) return ;
55+ if ( context . state && context . state !== 'suspended' && context . state !== 'interrupted' ) return ;
56+ try {
57+ void context . resume ( ) ;
58+ } catch {
59+ // ignore resume errors
60+ }
61+ }
62+
3663 public playMusic ( assetId : string , options : { loop ?: boolean ; volume ?: number ; fadeMs ?: number } = { } ) : void {
64+ this . resumeManagerIfNeeded ( ) ;
3765 const loop = options . loop ?? true ;
3866 const volume = clamp01 ( options . volume ?? 1 ) ;
3967 const fadeMs = Number . isFinite ( Number ( options . fadeMs ) ) ? Math . max ( 0 , Number ( options . fadeMs ) ) : 0 ;
@@ -101,6 +129,7 @@ export class BasicAudioService implements AudioService {
101129 }
102130
103131 public playSfx ( assetId : string , options : { volume ?: number } = { } ) : void {
132+ this . resumeManagerIfNeeded ( ) ;
104133 const volume = clamp01 ( options . volume ?? 1 ) ;
105134 const key = this . getKey ( assetId ) ;
106135 try {
0 commit comments