Skip to content

Commit 42f491e

Browse files
committed
improve audio performance
1 parent 1a220f6 commit 42f491e

File tree

1 file changed

+107
-13
lines changed

1 file changed

+107
-13
lines changed

game/js/Custom.js

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,29 +207,122 @@ if (!Array.isArray(playingSounds)) {
207207
playingSounds = [];
208208
}
209209
globalThis.playingSounds = playingSounds;
210+
const audioPools = globalThis.audioPools || new Map();
211+
globalThis.audioPools = audioPools;
212+
const MAX_TOTAL_SOUNDS = 32;
213+
const MAX_SOUNDS_PER_KEY = 4;
214+
const AUDIO_POOL_SIZE = 6;
215+
const CLEANUP_INTERVAL_MS = 5000;
216+
const MIN_INTERVAL_MS_PER_KEY = 50;
217+
const PLAY_RATE_WINDOW_MS = 250;
218+
const MAX_PLAYS_PER_WINDOW = 12;
219+
const lastPlayAt = globalThis.audioLastPlayAt || new Map();
220+
globalThis.audioLastPlayAt = lastPlayAt;
221+
const recentPlayTimes = globalThis.audioRecentPlayTimes || [];
222+
globalThis.audioRecentPlayTimes = recentPlayTimes;
223+
224+
function getAudioKey(path, name, tag) {
225+
return `${path}|${name || path}|${tag || "default"}`;
226+
}
227+
228+
function getPool(key) {
229+
if (!audioPools.has(key)) {
230+
audioPools.set(key, []);
231+
}
232+
return audioPools.get(key);
233+
}
234+
235+
function cleanupPlayingSounds() {
236+
for (let i = playingSounds.length - 1; i >= 0; i -= 1) {
237+
const audio = playingSounds[i];
238+
if (!audio || audio.ended || audio.paused) {
239+
playingSounds.splice(i, 1);
240+
}
241+
}
242+
}
243+
244+
function canPlayNow(key) {
245+
const now = Date.now();
246+
const last = lastPlayAt.get(key) || 0;
247+
if (now - last < MIN_INTERVAL_MS_PER_KEY) {
248+
return false;
249+
}
250+
while (recentPlayTimes.length && now - recentPlayTimes[0] > PLAY_RATE_WINDOW_MS) {
251+
recentPlayTimes.shift();
252+
}
253+
if (recentPlayTimes.length >= MAX_PLAYS_PER_WINDOW) {
254+
return false;
255+
}
256+
recentPlayTimes.push(now);
257+
lastPlayAt.set(key, now);
258+
return true;
259+
}
260+
261+
setInterval(cleanupPlayingSounds, CLEANUP_INTERVAL_MS);
210262
function PlaySound2(path, loop, name, tag) {
211-
console.log(path, loop, name, tag);
212263
name = name || path;
213-
path = `audio/${path}.mp3`;
214-
// console.log(`Playing sound: ${path}`);
215-
let audio = new Audio(path);
216-
audio.loop = loop;
264+
const tagName = tag || "default";
265+
const audioPath = `audio/${path}.mp3`;
266+
const key = getAudioKey(audioPath, name, tagName);
267+
const pool = getPool(key);
268+
269+
if (!canPlayNow(key)) {
270+
return null;
271+
}
272+
273+
cleanupPlayingSounds();
274+
if (playingSounds.length >= MAX_TOTAL_SOUNDS) {
275+
const oldest = playingSounds.shift();
276+
if (oldest) {
277+
oldest.pause();
278+
oldest.currentTime = 0;
279+
}
280+
}
281+
282+
let playingForKeyCount = 0;
283+
for (let i = 0; i < playingSounds.length; i += 1) {
284+
const a = playingSounds[i];
285+
if (a && a.dataset && a.dataset.name === name && a.dataset.tag === tagName) {
286+
playingForKeyCount += 1;
287+
if (playingForKeyCount >= MAX_SOUNDS_PER_KEY) {
288+
return null;
289+
}
290+
}
291+
}
292+
293+
let audio = pool.find((a) => a.paused || a.ended);
294+
if (!audio) {
295+
if (pool.length >= AUDIO_POOL_SIZE) {
296+
return null;
297+
} else {
298+
audio = new Audio(audioPath);
299+
audio.preload = "auto";
300+
pool.push(audio);
301+
}
302+
}
303+
304+
audio.loop = Boolean(loop);
217305
audio.muted = Boolean(oS.Silence);
218-
audio.dataset.tag = tag || "default";
306+
audio.dataset.tag = tagName;
219307
audio.dataset.name = name;
220-
audio.play();
221-
playingSounds.push(audio);
222-
// remove it after it's done playing
223-
audio.onended = function () {
224-
playingSounds.splice(playingSounds.indexOf(audio), 1);
225-
};
308+
audio.currentTime = 0;
309+
310+
const playPromise = audio.play();
311+
if (playPromise && typeof playPromise.catch === "function") {
312+
playPromise.catch(() => {});
313+
}
314+
315+
if (!playingSounds.includes(audio)) {
316+
playingSounds.push(audio);
317+
}
226318
return audio;
227319
}
228320
function StopSound2(name) {
229321
// console.log(`Stopping sound: ${name}`);
230322
playingSounds.forEach((audio) => {
231323
if (audio.dataset.name.includes(name)) {
232324
audio.pause();
325+
audio.currentTime = 0;
233326
}
234327
});
235328
}
@@ -238,14 +331,15 @@ function StopSoundTag2(tag) {
238331
playingSounds.forEach((audio) => {
239332
if (audio.dataset.tag === tag) {
240333
audio.pause();
334+
audio.currentTime = 0;
241335
}
242336
});
243337
}
244338
function EditSound2(name, loop = false) {
245339
// console.log(`Editing sound: ${name}`);
246340
playingSounds.forEach((audio) => {
247341
if (audio.src.includes(name)) {
248-
audio.loop = loop;
342+
audio.loop = Boolean(loop);
249343
}
250344
});
251345
}

0 commit comments

Comments
 (0)