@@ -207,29 +207,122 @@ if (!Array.isArray(playingSounds)) {
207207 playingSounds = [ ] ;
208208}
209209globalThis . 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 ) ;
210262function 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}
228320function 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}
244338function 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