pixi-rainman-game-engine 0.3.42 → 0.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/SoundManager/SoundManager.d.ts +28 -6
- package/dist/application/SoundManager/SoundManager.js +149 -56
- package/dist/connectivity/ConnectionWrapper.d.ts +1 -0
- package/dist/connectivity/ConnectionWrapper.js +13 -1
- package/dist/utils/common/sound.js +8 -7
- package/package.json +1 -1
|
@@ -16,8 +16,11 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
16
16
|
readonly speedLevelMapper: SpeedLevelDirectory<SoundOverrides>;
|
|
17
17
|
private musicCatalogue;
|
|
18
18
|
private currentAmbientTrack;
|
|
19
|
-
private
|
|
19
|
+
private currentAmbientOffset;
|
|
20
|
+
private loadedSoundAssets;
|
|
21
|
+
private audioContextRecoveryPromise;
|
|
20
22
|
constructor();
|
|
23
|
+
private loadSound;
|
|
21
24
|
/**
|
|
22
25
|
* Function for loading sounds into pixi
|
|
23
26
|
* @public
|
|
@@ -62,7 +65,19 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
62
65
|
getVolumeLevel(soundName: SoundTrack): number;
|
|
63
66
|
private isAmbientTrack;
|
|
64
67
|
private getOppositeAmbientTrack;
|
|
68
|
+
private getCurrentAmbientTrack;
|
|
65
69
|
private getSound;
|
|
70
|
+
private getAmbientPlaybackOffset;
|
|
71
|
+
private trackAmbientPlaybackProgress;
|
|
72
|
+
/**
|
|
73
|
+
* Saves active ambient position before the browser interrupts WebAudio playback.
|
|
74
|
+
* @public
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
captureAmbientPlaybackPositionForPageHide(): void;
|
|
78
|
+
private isIOSWebAudioRuntime;
|
|
79
|
+
private recreatePixiSoundContext;
|
|
80
|
+
private recreatePixiSoundContextOnce;
|
|
66
81
|
stopAmbientMusic(): void;
|
|
67
82
|
/**
|
|
68
83
|
* Function for resuming all sounds in game
|
|
@@ -78,11 +93,18 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
78
93
|
*/
|
|
79
94
|
restoreAmbientPlayback(): void;
|
|
80
95
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
96
|
+
* Recreates the active ambient WebAudio source after iOS app interruptions.
|
|
97
|
+
* Existing Pixi instances can report isPlaying=true even when the native audio pipeline is silent.
|
|
98
|
+
* @private
|
|
83
99
|
* @returns {void}
|
|
84
100
|
*/
|
|
85
|
-
|
|
101
|
+
private restartAmbientPlaybackAfterInterruption;
|
|
102
|
+
/**
|
|
103
|
+
* Restores ambient playback after page visibility change or iOS app interruption.
|
|
104
|
+
* @public
|
|
105
|
+
* @returns {boolean} True when audio context is ready after recovery.
|
|
106
|
+
*/
|
|
107
|
+
restorePlaybackAfterPageHide(): Promise<boolean>;
|
|
86
108
|
/**
|
|
87
109
|
* Function for pausing all sounds in game
|
|
88
110
|
* If sound is not playing, it will be skipped
|
|
@@ -128,13 +150,13 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
128
150
|
/**
|
|
129
151
|
* Function that resumes sounds and music when game is back in focus.
|
|
130
152
|
* @public
|
|
131
|
-
* @returns {
|
|
153
|
+
* @returns {Promise<boolean>} True when audio context is running.
|
|
132
154
|
*/
|
|
133
155
|
resumeAudioContext(): Promise<boolean>;
|
|
134
156
|
/**
|
|
135
157
|
* Function that resumes sounds and music when game is back in focus.
|
|
136
158
|
* @public
|
|
137
|
-
* @returns {
|
|
159
|
+
* @returns {Promise<boolean>} True when audio context and ambient recovery are ready.
|
|
138
160
|
*/
|
|
139
161
|
resumeOnFocus(): Promise<boolean>;
|
|
140
162
|
/**
|
|
@@ -31,10 +31,25 @@ export class SoundManagerInstance {
|
|
|
31
31
|
};
|
|
32
32
|
musicCatalogue = new Map();
|
|
33
33
|
currentAmbientTrack = null;
|
|
34
|
-
|
|
34
|
+
currentAmbientOffset = 0;
|
|
35
|
+
loadedSoundAssets = {};
|
|
36
|
+
audioContextRecoveryPromise = null;
|
|
35
37
|
constructor() {
|
|
36
38
|
this.overrides = this.speedLevelMapper.slow;
|
|
37
39
|
}
|
|
40
|
+
loadSound(soundName, url) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const sound = Sound.from({
|
|
43
|
+
url,
|
|
44
|
+
autoPlay: false,
|
|
45
|
+
preload: true,
|
|
46
|
+
volume: this.getVolumeLevel(soundName),
|
|
47
|
+
loaded: () => resolve()
|
|
48
|
+
});
|
|
49
|
+
sound.name = soundName;
|
|
50
|
+
this.musicCatalogue.set(soundName, sound);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
38
53
|
/**
|
|
39
54
|
* Function for loading sounds into pixi
|
|
40
55
|
* @public
|
|
@@ -42,22 +57,13 @@ export class SoundManagerInstance {
|
|
|
42
57
|
* @returns {Promise<void>} - promise that resolves when all sounds are loaded
|
|
43
58
|
*/
|
|
44
59
|
async loadSounds(assets) {
|
|
60
|
+
this.loadedSoundAssets = { ...assets };
|
|
45
61
|
const loadingSoundsPromises = [];
|
|
46
62
|
for (const [soundName, url] of Object.entries(assets)) {
|
|
47
63
|
if (!url) {
|
|
48
64
|
continue;
|
|
49
65
|
}
|
|
50
|
-
loadingSoundsPromises.push(
|
|
51
|
-
const sound = Sound.from({
|
|
52
|
-
url,
|
|
53
|
-
autoPlay: false,
|
|
54
|
-
preload: true,
|
|
55
|
-
volume: this.getVolumeLevel(soundName),
|
|
56
|
-
loaded: () => resolve()
|
|
57
|
-
});
|
|
58
|
-
sound.name = soundName;
|
|
59
|
-
this.musicCatalogue.set(soundName, sound);
|
|
60
|
-
}));
|
|
66
|
+
loadingSoundsPromises.push(this.loadSound(soundName, url));
|
|
61
67
|
}
|
|
62
68
|
await Promise.allSettled(loadingSoundsPromises);
|
|
63
69
|
}
|
|
@@ -96,7 +102,6 @@ export class SoundManagerInstance {
|
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
pauseSoundsForPageHide() {
|
|
99
|
-
this.pausedSoundsForPageHide.clear();
|
|
100
105
|
this.musicCatalogue.forEach((sound, soundName) => {
|
|
101
106
|
if (!sound.isPlaying || sound.paused) {
|
|
102
107
|
return;
|
|
@@ -104,9 +109,8 @@ export class SoundManagerInstance {
|
|
|
104
109
|
if (soundName === SoundTracks.freeSpinsMusic || soundName === SoundTracks.music) {
|
|
105
110
|
this.currentAmbientTrack = soundName;
|
|
106
111
|
}
|
|
107
|
-
this.pausedSoundsForPageHide.add(soundName);
|
|
108
|
-
sound.pause();
|
|
109
112
|
});
|
|
113
|
+
sound.pauseAll();
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
112
116
|
* Function for resuming and pausing sound in game
|
|
@@ -150,12 +154,15 @@ export class SoundManagerInstance {
|
|
|
150
154
|
return;
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
|
-
sound.play({
|
|
157
|
+
const instance = sound.play({
|
|
154
158
|
singleInstance: true,
|
|
155
159
|
loop: soundsToLoop.includes(soundName),
|
|
156
160
|
volume: this.getVolumeLevel(soundName),
|
|
157
161
|
speed: soundName === "cardDrawing" ? 0.8 : 1
|
|
158
162
|
});
|
|
163
|
+
if (this.isAmbientTrack(soundName) && !(instance instanceof Promise)) {
|
|
164
|
+
this.trackAmbientPlaybackProgress(soundName, sound, instance);
|
|
165
|
+
}
|
|
159
166
|
}
|
|
160
167
|
/**
|
|
161
168
|
* Returns the volume level for a given sound track.
|
|
@@ -178,10 +185,86 @@ export class SoundManagerInstance {
|
|
|
178
185
|
getOppositeAmbientTrack(soundName) {
|
|
179
186
|
return soundName === SoundTracks.freeSpinsMusic ? SoundTracks.music : SoundTracks.freeSpinsMusic;
|
|
180
187
|
}
|
|
188
|
+
getCurrentAmbientTrack() {
|
|
189
|
+
return (this.currentAmbientTrack ??
|
|
190
|
+
(RainMan.settingsStore.isFreeSpinsStarted ? SoundTracks.freeSpinsMusic : SoundTracks.music));
|
|
191
|
+
}
|
|
181
192
|
getSound(soundName) {
|
|
182
193
|
const overrideSoundName = this.overrides[soundName];
|
|
183
194
|
return this.musicCatalogue.get(overrideSoundName ?? soundName);
|
|
184
195
|
}
|
|
196
|
+
getAmbientPlaybackOffset(sound) {
|
|
197
|
+
if (!sound.isPlayable || !Number.isFinite(sound.duration) || sound.duration <= 0) {
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
return this.currentAmbientOffset % sound.duration;
|
|
201
|
+
}
|
|
202
|
+
trackAmbientPlaybackProgress(soundName, sound, instance) {
|
|
203
|
+
if (!sound.isPlayable || sound.duration <= 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
instance.on("progress", (progress) => {
|
|
207
|
+
if (this.currentAmbientTrack !== soundName || !Number.isFinite(progress)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.currentAmbientOffset = (progress * sound.duration) % sound.duration;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Saves active ambient position before the browser interrupts WebAudio playback.
|
|
215
|
+
* @public
|
|
216
|
+
* @returns {void}
|
|
217
|
+
*/
|
|
218
|
+
captureAmbientPlaybackPositionForPageHide() {
|
|
219
|
+
if (!RainMan.settingsStore.isSoundStarted || !RainMan.settingsStore.shouldPlayAmbientMusic) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const ambientTrack = this.getCurrentAmbientTrack();
|
|
223
|
+
const ambientSound = this.getSound(ambientTrack);
|
|
224
|
+
const ambientInstance = ambientSound?.instances.find((instance) => Number.isFinite(instance.progress) && instance.progress > 0);
|
|
225
|
+
if (!ambientSound || !ambientInstance || !ambientSound.isPlayable || ambientSound.duration <= 0) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.currentAmbientTrack = ambientTrack;
|
|
229
|
+
this.currentAmbientOffset = (ambientInstance.progress * ambientSound.duration) % ambientSound.duration;
|
|
230
|
+
}
|
|
231
|
+
isIOSWebAudioRuntime() {
|
|
232
|
+
const userAgent = navigator.userAgent;
|
|
233
|
+
const platform = navigator.platform;
|
|
234
|
+
return /iPad|iPhone|iPod/.test(userAgent) || (platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
235
|
+
}
|
|
236
|
+
async recreatePixiSoundContext() {
|
|
237
|
+
if (this.audioContextRecoveryPromise) {
|
|
238
|
+
return this.audioContextRecoveryPromise;
|
|
239
|
+
}
|
|
240
|
+
this.audioContextRecoveryPromise = this.recreatePixiSoundContextOnce().finally(() => {
|
|
241
|
+
this.audioContextRecoveryPromise = null;
|
|
242
|
+
});
|
|
243
|
+
return this.audioContextRecoveryPromise;
|
|
244
|
+
}
|
|
245
|
+
async recreatePixiSoundContextOnce() {
|
|
246
|
+
if (!Object.keys(this.loadedSoundAssets).length) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.musicCatalogue.forEach((sound) => sound.destroy());
|
|
250
|
+
this.musicCatalogue.clear();
|
|
251
|
+
try {
|
|
252
|
+
sound.context.destroy();
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
console.error("Could not destroy Pixi sound context", error);
|
|
256
|
+
}
|
|
257
|
+
sound.init();
|
|
258
|
+
sound.disableAutoPause = false;
|
|
259
|
+
const loadingSoundsPromises = [];
|
|
260
|
+
for (const [soundName, url] of Object.entries(this.loadedSoundAssets)) {
|
|
261
|
+
if (!url) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
loadingSoundsPromises.push(this.loadSound(soundName, url));
|
|
265
|
+
}
|
|
266
|
+
await Promise.allSettled(loadingSoundsPromises);
|
|
267
|
+
}
|
|
185
268
|
stopAmbientMusic() {
|
|
186
269
|
this.stop(SoundTracks.music);
|
|
187
270
|
this.stop(SoundTracks.freeSpinsMusic);
|
|
@@ -196,23 +279,8 @@ export class SoundManagerInstance {
|
|
|
196
279
|
if (!RainMan.settingsStore.isSoundStarted) {
|
|
197
280
|
return;
|
|
198
281
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
sound.resume();
|
|
202
|
-
}
|
|
203
|
-
if (RainMan.settingsStore.shouldPlayAmbientMusic &&
|
|
204
|
-
!RainMan.settingsStore.isFreeSpinsPlayEnabled &&
|
|
205
|
-
soundName === SoundTracks.music &&
|
|
206
|
-
sound.paused) {
|
|
207
|
-
sound.resume();
|
|
208
|
-
}
|
|
209
|
-
if (RainMan.settingsStore.shouldPlayAmbientMusic &&
|
|
210
|
-
RainMan.settingsStore.isFreeSpinsPlayEnabled &&
|
|
211
|
-
soundName === SoundTracks.freeSpinsMusic &&
|
|
212
|
-
sound.paused) {
|
|
213
|
-
sound.resume();
|
|
214
|
-
}
|
|
215
|
-
});
|
|
282
|
+
sound.resumeAll();
|
|
283
|
+
this.restoreAmbientPlayback();
|
|
216
284
|
}
|
|
217
285
|
/**
|
|
218
286
|
* Restores currently active ambient loop if browser dropped it while page was inactive.
|
|
@@ -227,35 +295,61 @@ export class SoundManagerInstance {
|
|
|
227
295
|
this.stopAmbientMusic();
|
|
228
296
|
return;
|
|
229
297
|
}
|
|
230
|
-
const ambientTrack = this.
|
|
231
|
-
(RainMan.settingsStore.isFreeSpinsStarted ? SoundTracks.freeSpinsMusic : SoundTracks.music);
|
|
298
|
+
const ambientTrack = this.getCurrentAmbientTrack();
|
|
232
299
|
this.play(ambientTrack);
|
|
233
300
|
}
|
|
234
301
|
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
302
|
+
* Recreates the active ambient WebAudio source after iOS app interruptions.
|
|
303
|
+
* Existing Pixi instances can report isPlaying=true even when the native audio pipeline is silent.
|
|
304
|
+
* @private
|
|
237
305
|
* @returns {void}
|
|
238
306
|
*/
|
|
239
|
-
|
|
307
|
+
restartAmbientPlaybackAfterInterruption() {
|
|
240
308
|
if (!RainMan.settingsStore.isSoundStarted) {
|
|
241
|
-
this.pausedSoundsForPageHide.clear();
|
|
242
309
|
return;
|
|
243
310
|
}
|
|
244
|
-
if (!
|
|
245
|
-
this.
|
|
311
|
+
if (!RainMan.settingsStore.shouldPlayAmbientMusic) {
|
|
312
|
+
this.stopAmbientMusic();
|
|
246
313
|
return;
|
|
247
314
|
}
|
|
248
|
-
const
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
315
|
+
const ambientTrack = this.getCurrentAmbientTrack();
|
|
316
|
+
const ambientSound = this.getSound(ambientTrack);
|
|
317
|
+
if (!ambientSound) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
this.stop(this.getOppositeAmbientTrack(ambientTrack));
|
|
321
|
+
ambientSound.stop();
|
|
322
|
+
this.currentAmbientTrack = ambientTrack;
|
|
323
|
+
const instance = ambientSound.play({
|
|
324
|
+
singleInstance: true,
|
|
325
|
+
loop: soundsToLoop.includes(ambientTrack),
|
|
326
|
+
start: this.getAmbientPlaybackOffset(ambientSound),
|
|
327
|
+
volume: this.getVolumeLevel(ambientTrack),
|
|
328
|
+
speed: 1
|
|
258
329
|
});
|
|
330
|
+
if (!(instance instanceof Promise)) {
|
|
331
|
+
this.trackAmbientPlaybackProgress(ambientTrack, ambientSound, instance);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Restores ambient playback after page visibility change or iOS app interruption.
|
|
336
|
+
* @public
|
|
337
|
+
* @returns {boolean} True when audio context is ready after recovery.
|
|
338
|
+
*/
|
|
339
|
+
async restorePlaybackAfterPageHide() {
|
|
340
|
+
if (!RainMan.settingsStore.isSoundStarted) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
sound.resumeAll();
|
|
344
|
+
if (this.isIOSWebAudioRuntime()) {
|
|
345
|
+
await this.recreatePixiSoundContext();
|
|
346
|
+
}
|
|
347
|
+
const isAudioContextRunning = await this.resumeAudioContext();
|
|
348
|
+
if (!isAudioContextRunning) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
this.restartAmbientPlaybackAfterInterruption();
|
|
352
|
+
return true;
|
|
259
353
|
}
|
|
260
354
|
/**
|
|
261
355
|
* Function for pausing all sounds in game
|
|
@@ -264,7 +358,7 @@ export class SoundManagerInstance {
|
|
|
264
358
|
* @returns {void}
|
|
265
359
|
*/
|
|
266
360
|
pauseAll() {
|
|
267
|
-
|
|
361
|
+
sound.pauseAll();
|
|
268
362
|
}
|
|
269
363
|
/**
|
|
270
364
|
* Function for stopping all sounds in game
|
|
@@ -314,7 +408,7 @@ export class SoundManagerInstance {
|
|
|
314
408
|
/**
|
|
315
409
|
* Function that resumes sounds and music when game is back in focus.
|
|
316
410
|
* @public
|
|
317
|
-
* @returns {
|
|
411
|
+
* @returns {Promise<boolean>} True when audio context is running.
|
|
318
412
|
*/
|
|
319
413
|
async resumeAudioContext() {
|
|
320
414
|
const pixiSoundContext = sound.context;
|
|
@@ -333,7 +427,7 @@ export class SoundManagerInstance {
|
|
|
333
427
|
/**
|
|
334
428
|
* Function that resumes sounds and music when game is back in focus.
|
|
335
429
|
* @public
|
|
336
|
-
* @returns {
|
|
430
|
+
* @returns {Promise<boolean>} True when audio context and ambient recovery are ready.
|
|
337
431
|
*/
|
|
338
432
|
async resumeOnFocus() {
|
|
339
433
|
if (!RainMan.settingsStore.isSoundStarted) {
|
|
@@ -343,8 +437,7 @@ export class SoundManagerInstance {
|
|
|
343
437
|
if (!isAudioContextRunning) {
|
|
344
438
|
return false;
|
|
345
439
|
}
|
|
346
|
-
this.restorePlaybackAfterPageHide();
|
|
347
|
-
return true;
|
|
440
|
+
return this.restorePlaybackAfterPageHide();
|
|
348
441
|
}
|
|
349
442
|
/**
|
|
350
443
|
* Returns information if audio context is ready to play sounds.
|
|
@@ -11,6 +11,7 @@ export declare class ConnectionWrapper implements SubscribableConnectionWrapper
|
|
|
11
11
|
private fetchInProgress;
|
|
12
12
|
private lastFetchResponse;
|
|
13
13
|
private lastGambleResponse;
|
|
14
|
+
private pendingSpinEndPromise;
|
|
14
15
|
private subscribers;
|
|
15
16
|
/**
|
|
16
17
|
* Mapper function for data that comes from the server
|
|
@@ -17,6 +17,7 @@ export class ConnectionWrapper {
|
|
|
17
17
|
fetchInProgress = false;
|
|
18
18
|
lastFetchResponse = new Map();
|
|
19
19
|
lastGambleResponse = null;
|
|
20
|
+
pendingSpinEndPromise = null;
|
|
20
21
|
subscribers = [];
|
|
21
22
|
/**
|
|
22
23
|
* Mapper function for data that comes from the server
|
|
@@ -83,6 +84,7 @@ export class ConnectionWrapper {
|
|
|
83
84
|
* @memberof ConnectionWrapper
|
|
84
85
|
*/
|
|
85
86
|
async fetchInitData() {
|
|
87
|
+
this.pendingSpinEndPromise = null;
|
|
86
88
|
this._initData = (await this.fetch({
|
|
87
89
|
action: "init",
|
|
88
90
|
reference: RainMan.config.gameInitReference,
|
|
@@ -177,6 +179,9 @@ export class ConnectionWrapper {
|
|
|
177
179
|
if (typeof this.config?.token === "undefined") {
|
|
178
180
|
throw new Error("Token was not defined");
|
|
179
181
|
}
|
|
182
|
+
if (this.pendingSpinEndPromise !== null) {
|
|
183
|
+
await this.pendingSpinEndPromise;
|
|
184
|
+
}
|
|
180
185
|
const data = await this.fetch({
|
|
181
186
|
action: "spin",
|
|
182
187
|
reference: RainMan.config.gameInitReference,
|
|
@@ -186,7 +191,14 @@ export class ConnectionWrapper {
|
|
|
186
191
|
extra_data
|
|
187
192
|
});
|
|
188
193
|
this._messageHistory.push(data);
|
|
189
|
-
this.sendEndOfRoundData(data.round_number);
|
|
194
|
+
const endPromise = this.sendEndOfRoundData(data.round_number);
|
|
195
|
+
let trackedEndPromise = null;
|
|
196
|
+
trackedEndPromise = endPromise.finally(() => {
|
|
197
|
+
if (this.pendingSpinEndPromise === trackedEndPromise) {
|
|
198
|
+
this.pendingSpinEndPromise = null;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
this.pendingSpinEndPromise = trackedEndPromise;
|
|
190
202
|
return new SpinData(this.config, data);
|
|
191
203
|
}
|
|
192
204
|
/**
|
|
@@ -16,15 +16,16 @@ const interruptedAudioResume = () => {
|
|
|
16
16
|
window.addEventListener("touchend", resumeAudioOnInterruption, { once: true, passive: true });
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Clears pending foreground recovery when the page goes to the background.
|
|
20
|
+
* Pixi handles the actual pause state through its WebAudio auto-pause flow.
|
|
20
21
|
* @returns {void}
|
|
21
22
|
*/
|
|
22
|
-
const
|
|
23
|
+
const prepareAudioForPageHide = () => {
|
|
23
24
|
isResumingAudio = false;
|
|
24
25
|
shouldResumeAudioOnInterruption = false;
|
|
25
26
|
removeInterruptedAudioResumeListeners();
|
|
26
27
|
if (RainMan.settingsStore.isSoundEnabled()) {
|
|
27
|
-
SoundManager.
|
|
28
|
+
SoundManager.captureAmbientPlaybackPositionForPageHide();
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
/**
|
|
@@ -71,19 +72,19 @@ function resumeAudioOnInterruption() {
|
|
|
71
72
|
*/
|
|
72
73
|
const handleVisibilityChange = () => {
|
|
73
74
|
if (document.hidden) {
|
|
74
|
-
|
|
75
|
+
prepareAudioForPageHide();
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
+
resumeAudio();
|
|
78
79
|
};
|
|
79
80
|
/**
|
|
80
81
|
* Sets up audio states for tab switches and iOS app interruptions.
|
|
81
82
|
* @returns {void}
|
|
82
83
|
*/
|
|
83
84
|
export const loadSoundsOnTabChange = () => {
|
|
84
|
-
sound.disableAutoPause =
|
|
85
|
+
sound.disableAutoPause = false;
|
|
85
86
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
86
|
-
window.addEventListener("pagehide", () =>
|
|
87
|
+
window.addEventListener("pagehide", () => prepareAudioForPageHide());
|
|
87
88
|
window.addEventListener("pageshow", () => resumeAudio());
|
|
88
89
|
window.addEventListener("focus", () => resumeAudio());
|
|
89
90
|
};
|
package/package.json
CHANGED