{
  "version": 3,
  "sources": ["../../../plugins/sound/sound.js", "../../../plugins/sound/play.js", "../../../plugins/sound/index.js"],
  "sourcesContent": ["/**\n * Pi.js - Sound Module (Plugin)\n * \n * Sound effects, audio files, and volume control using Web Audio API.\n * \n * @module plugins/sound/sound\n */\n\n\"use strict\";\n\nlet m_audioContext = null;\nlet m_audioPools = {};\nlet m_nextAudioId = 0;\nlet m_soundPool = {};\nlet m_nextSoundId = 0;\nlet m_volume = 0.75;\n\n\n/***************************************************************************************************\n * Internal Functions\n **************************************************************************************************/\n\n\n/**\n * Load an audio element and add to pool when ready\n * \n * @param {Object} pluginApi - Plugin API\n * @param {Object} audioItem - Audio pool item\n * @param {HTMLAudioElement} audio - Audio element\n * @param {number} retryCount - Number of retries remaining\n */\nfunction loadAudioItem( pluginApi, audioItem, audio, retryCount = 3 ) {\n\n\t// Audio ready callback\n\tfunction audioReady() {\n\t\taudioItem.pool.push( {\n\t\t\t\"audio\": audio,\n\t\t\t\"timeout\": 0,\n\t\t\t\"volume\": 1\n\t\t} );\n\t\taudio.removeEventListener( \"canplay\", audioReady );\n\t\tpluginApi.done();\n\t}\n\n\t// Audio error callback\n\tfunction audioError() {\n\t\tconst errors = [\n\t\t\t\"MEDIA_ERR_ABORTED - fetching process aborted by user\",\n\t\t\t\"MEDIA_ERR_NETWORK - error occurred when downloading\",\n\t\t\t\"MEDIA_ERR_DECODE - error occurred when decoding\",\n\t\t\t\"MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\"\n\t\t];\n\n\t\tconst errorCode = audio.error.code;\n\t\tconst index = errorCode - 1;\n\n\t\tif( index >= 0 && index < errors.length ) {\n\t\t\tconsole.error( \"loadAudio: \" + errors[ index ] );\n\n\t\t\t// Retry loading if retries remain\n\t\t\tif( retryCount > 0 ) {\n\t\t\t\tsetTimeout( () => {\n\t\t\t\t\taudio.removeEventListener( \"canplay\", audioReady );\n\t\t\t\t\taudio.removeEventListener( \"error\", audioError );\n\t\t\t\t\tconst newAudio = new Audio( audio.src );\n\t\t\t\t\tloadAudioItem( pluginApi, audioItem, newAudio, retryCount - 1 );\n\t\t\t\t}, 100 );\n\t\t\t} else {\n\t\t\t\tconsole.error( \"loadAudio: Max retries exceeded for \" + audio.src );\n\t\t\t\tpluginApi.done();\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.error( \"loadAudio: Unknown error - \" + errorCode );\n\t\t\tpluginApi.done();\n\t\t}\n\t}\n\n\t// Wait for audio to load (only on first attempt)\n\tif( retryCount === 3 ) {\n\t\tpluginApi.wait();\n\t}\n\n\t// Set up event listeners\n\taudio.addEventListener( \"canplay\", audioReady );\n\taudio.addEventListener( \"error\", audioError );\n}\n\n\n/***************************************************************************************************\n * Exported Functions (for play.js)\n **************************************************************************************************/\n\n\n/**\n * Stop a sound by ID (internal function for play module)\n * \n * @param {string} soundId - Sound ID to stop\n */\nexport function stopSoundById( soundId ) {\n\tif( m_soundPool[ soundId ] ) {\n\t\tm_soundPool[ soundId ].oscillator.stop( 0 );\n\t}\n}\n\n/**\n * Create a sound using Web Audio API (internal function exported for play module)\n * \n * @param {AudioContext} audioContext - Audio context\n * @param {number} frequency - Frequency in Hz\n * @param {number} volume - Volume (0-1)\n * @param {number} attackTime - Attack time in seconds\n * @param {number} sustainTime - Sustain time in seconds\n * @param {number} decayTime - Decay time in seconds\n * @param {number} stopTime - Total sound duration\n * @param {string} oType - Oscillator type\n * @param {Array} waveTables - Custom wave tables (if oType is \"custom\")\n * @param {number} delay - Delay before playing\n * @returns {string} Sound ID\n */\nexport function createSound(\n\taudioContext, frequency, volume, attackTime, sustainTime,\n\tdecayTime, stopTime, oType, waveTables, delay\n) {\n\tconst oscillator = audioContext.createOscillator();\n\tconst envelope = audioContext.createGain();\n\tconst master = audioContext.createGain();\n\n\tmaster.gain.value = m_volume;\n\toscillator.frequency.value = frequency;\n\n\t// Set oscillator type\n\tif( oType === \"custom\" ) {\n\t\tconst real = waveTables[ 0 ];\n\t\tconst imag = waveTables[ 1 ];\n\t\tconst wave = audioContext.createPeriodicWave( real, imag );\n\t\toscillator.setPeriodicWave( wave );\n\t} else {\n\t\toscillator.type = oType;\n\t}\n\n\t// Set initial envelope gain\n\tif( attackTime === 0 ) {\n\t\tenvelope.gain.value = volume;\n\t} else {\n\t\tenvelope.gain.value = 0;\n\t}\n\n\t// Connect audio nodes\n\toscillator.connect( envelope );\n\tenvelope.connect( master );\n\tmaster.connect( audioContext.destination );\n\n\tconst currentTime = audioContext.currentTime + delay;\n\n\t// Set attack envelope\n\tif( attackTime > 0 ) {\n\t\tenvelope.gain.setValueCurveAtTime(\n\t\t\tnew Float32Array( [ 0, volume ] ),\n\t\t\tcurrentTime,\n\t\t\tattackTime\n\t\t);\n\t}\n\n\t// Set sustain envelope\n\tif( sustainTime > 0 ) {\n\t\tenvelope.gain.setValueCurveAtTime(\n\t\t\tnew Float32Array( [ volume, 0.8 * volume ] ),\n\t\t\tcurrentTime + attackTime,\n\t\t\tsustainTime\n\t\t);\n\t}\n\n\t// Set decay envelope\n\tif( decayTime > 0 ) {\n\t\tenvelope.gain.setValueCurveAtTime(\n\t\t\tnew Float32Array( [ 0.8 * volume, 0.1 * volume, 0 ] ),\n\t\t\tcurrentTime + attackTime + sustainTime,\n\t\t\tdecayTime\n\t\t);\n\t}\n\n\t// Start and stop oscillator\n\toscillator.start( currentTime );\n\toscillator.stop( currentTime + stopTime );\n\n\t// Add to sound pool\n\tconst soundId = \"sound_\" + m_nextSoundId;\n\tm_nextSoundId += 1;\n\tm_soundPool[ soundId ] = {\n\t\t\"oscillator\": oscillator,\n\t\t\"master\": master,\n\t\t\"audioContext\": audioContext\n\t};\n\n\t// Auto-cleanup when done\n\tsetTimeout( () => {\n\t\tdelete m_soundPool[ soundId ];\n\t}, ( currentTime + stopTime ) * 1000 );\n\n\treturn soundId;\n}\n\n\n/***************************************************************************************************\n * Plugin Registration\n **************************************************************************************************/\n\n\n/**\n * Register sound module commands\n * \n * @param {Object} pluginApi - Plugin API\n */\nexport function registerSound( pluginApi ) {\n\tconst utils = pluginApi.utils;\n\n\t/**\n\t * Create an audio pool for playing multiple instances of the same sound file\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.src - Audio file URL\n\t * @param {number} options.poolSize - Number of audio instances (default: 1)\n\t * @returns {string} Audio ID for use with playAudio\n\t */\n\tpluginApi.addCommand( \"loadAudio\", loadAudio, false, [ \"src\", \"name\", \"poolSize\" ] );\n\tfunction loadAudio( options ) {\n\t\tconst src = options.src;\n\t\tlet poolSize = utils.getInt( options.poolSize, 1 );\n\t\tlet audioName = options.name;\n\n\t\t// Validate src\n\t\tif( !src || typeof src !== \"string\" ) {\n\t\t\tconst error = new TypeError( \"loadAudio: Parameter src must be a non-empty string.\" );\n\t\t\terror.code = \"INVALID_SRC\";\n\t\t\tthrow error;\n\t\t}\n\n\t\tlet audioId = \"audioPool_\" + m_nextAudioId;\n\t\tif( audioName ) {\n\n\t\t\t// Name must be unique\n\t\t\tif( m_audioPools[ audioName ] ) {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`loadAudio: Audio pool name \"${audioName}\" is already in use.`\n\t\t\t\t);\n\t\t\t\terror.code = \"DUPLICATE_AUDIO_NAME\";\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\taudioId = audioName;\n\t\t} else {\n\t\t\tm_nextAudioId += 1;\n\t\t}\n\n\t\t// Validate poolSize\n\t\tif( poolSize < 1 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"loadAudio: Parameter poolSize must be an integer greater than 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_POOL_SIZE\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Create the audio pool item\n\t\tconst audioItem = {\n\t\t\t\"pool\": [],\n\t\t\t\"index\": 0\n\t\t};\n\n\t\t// Create each audio instance in the pool\n\t\tfor( let i = 0; i < poolSize; i++ ) {\n\t\t\tconst audio = new Audio( src );\n\t\t\tloadAudioItem( pluginApi, audioItem, audio );\n\t\t}\n\n\t\t// Save audioId to global array\n\t\tm_audioPools[ audioId ] = audioItem;\n\n\t\treturn audioId;\n\t}\n\n\t/**\n\t * Delete an audio pool and free its resources\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.audioId - Audio pool ID returned from loadAudio\n\t */\n\tpluginApi.addCommand( \"removeAudio\", removeAudio, false, [ \"audioId\" ] );\n\tfunction removeAudio( options ) {\n\t\tconst audioId = options.audioId;\n\n\t\t// Validate audioId\n\t\tif( !m_audioPools[ audioId ] ) {\n\t\t\tconst error = new Error( `removeAudio: Audio pool \"${audioId}\" not found.` );\n\t\t\terror.code = \"AUDIO_POOL_NOT_FOUND\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Stop all audio and clear timeouts in the pool\n\t\tfor( let i = 0; i < m_audioPools[ audioId ].pool.length; i++ ) {\n\t\t\tconst poolItem = m_audioPools[ audioId ].pool[ i ];\n\t\t\tpoolItem.audio.pause();\n\t\t\tclearTimeout( poolItem.timeout );\n\t\t}\n\n\t\t// Delete the pool\n\t\tdelete m_audioPools[ audioId ];\n\t}\n\n\t/**\n\t * Play audio from an audio pool\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.audioId - Audio pool ID\n\t * @param {number} options.volume - Volume (0-1, default: 1)\n\t * @param {number} options.startTime - Start time in seconds (default: 0)\n\t * @param {number} options.duration - Play duration in seconds (default: 0 = play full)\n\t */\n\tpluginApi.addCommand(\n\t\t\"playAudio\", playAudio, false, [ \"audioId\", \"volume\", \"startTime\", \"duration\" ]\n\t);\n\tfunction playAudio( options ) {\n\t\tconst audioId = options.audioId;\n\t\tconst volume = utils.getFloat( options.volume, 1 );\n\t\tconst startTime = utils.getFloat( options.startTime, 0 );\n\t\tconst duration = utils.getFloat( options.duration, 0 );\n\n\t\t// Validate audioId\n\t\tif( !m_audioPools[ audioId ] ) {\n\t\t\tconst error = new Error( `playAudio: Audio pool \"${audioId}\" not found.` );\n\t\t\terror.code = \"AUDIO_POOL_NOT_FOUND\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate volume\n\t\tif( volume < 0 || volume > 1 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"playAudio: Parameter volume must be a number between 0 and 1.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_VOLUME\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate startTime\n\t\tif( startTime < 0 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"playAudio: Parameter startTime must be a number greater than or equal to 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_START_TIME\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate duration\n\t\tif( duration < 0 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"playAudio: Parameter duration must be a number greater than or equal to 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_DURATION\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Get the audio pool\n\t\tconst audioItem = m_audioPools[ audioId ];\n\n\t\t// Make sure pool has sounds loaded\n\t\tif( audioItem.pool.length === 0 ) {\n\t\t\tconst error = new Error( \"playAudio: Audio pool has no sounds loaded.\" );\n\t\t\terror.code = \"EMPTY_POOL\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Get the next audio player from the pool\n\t\tconst poolItem = audioItem.pool[ audioItem.index ];\n\t\tconst audio = poolItem.audio;\n\n\t\t// Set volume and start time\n\t\taudio.volume = m_volume * volume;\n\t\tpoolItem.volume = volume;\n\t\taudio.currentTime = startTime;\n\n\t\t// Set duration if specified\n\t\tif( duration > 0 ) {\n\t\t\tclearTimeout( poolItem.timeout );\n\t\t\tpoolItem.timeout = setTimeout( () => {\n\t\t\t\taudio.pause();\n\t\t\t\taudio.currentTime = 0;\n\t\t\t}, duration * 1000 );\n\t\t}\n\n\t\t// Play the sound\n\t\t// Note: audio.play() returns a promise that may be rejected due to browser autoplay policies\n\t\tconst playPromise = audio.play();\n\t\tif( playPromise !== undefined ) {\n\t\t\tplayPromise.catch( ( error ) => {\n\t\t\t\tconsole.warn( \"playAudio: Audio playback failed:\", error.message );\n\t\t\t} );\n\t\t}\n\n\t\t// Move to next sound in pool (round-robin)\n\t\taudioItem.index += 1;\n\t\tif( audioItem.index >= audioItem.pool.length ) {\n\t\t\taudioItem.index = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Stop audio from an audio pool or all audio pools\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.audioId - Audio pool ID (null to stop all pools)\n\t */\n\tpluginApi.addCommand( \"stopAudio\", stopAudio, false, [ \"audioId\" ] );\n\tfunction stopAudio( options ) {\n\t\tconst audioId = options.audioId;\n\n\t\t// If no audioId, stop all audio pools\n\t\tif( audioId == null ) {\n\t\t\tfor( const poolId in m_audioPools ) {\n\t\t\t\tfor( let j = 0; j < m_audioPools[ poolId ].pool.length; j++ ) {\n\t\t\t\t\tconst poolItem = m_audioPools[ poolId ].pool[ j ];\n\t\t\t\t\tpoolItem.audio.pause();\n\t\t\t\t\tclearTimeout( poolItem.timeout );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate audioId\n\t\tif( !m_audioPools[ audioId ] ) {\n\t\t\tconst error = new Error( `stopAudio: Audio pool \"${audioId}\" not found.` );\n\t\t\terror.code = \"AUDIO_POOL_NOT_FOUND\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Stop all audio and clear timeouts in the specified pool\n\t\tfor( let i = 0; i < m_audioPools[ audioId ].pool.length; i++ ) {\n\t\t\tconst poolItem = m_audioPools[ audioId ].pool[ i ];\n\t\t\tpoolItem.audio.pause();\n\t\t\tclearTimeout( poolItem.timeout );\n\t\t}\n\t}\n\n\t/**\n\t * Play a sound by frequency using Web Audio API\n\t * \n\t * @param {Object} options - Command options\n\t * @param {number} options.frequency - Frequency in Hz\n\t * @param {number} options.duration - Duration in seconds (default: 1)\n\t * @param {number} options.volume - Volume 0-1 (default: 1)\n\t * @param {string|Array} options.oType - Oscillator type or custom wave table (default: \"triangle\")\n\t * @param {number} options.delay - Delay before playing in seconds (default: 0)\n\t * @param {number} options.attack - Attack time in seconds (default: 0)\n\t * @param {number} options.decay - Decay time in seconds (default: 0.1)\n\t * @returns {string} Sound ID for use with stopSound\n\t */\n\tpluginApi.addCommand( \"sound\", sound, false, [\n\t\t\"frequency\", \"duration\", \"volume\", \"oType\", \"delay\", \"attack\", \"decay\"\n\t] );\n\tfunction sound( options ) {\n\t\tconst frequency = Math.round( utils.getFloat( options.frequency, 440 ) );\n\t\tconst duration = utils.getFloat( options.duration, 1 );\n\t\tconst volume = utils.getFloat( options.volume, 1 );\n\t\tlet oType = options.oType != null ? options.oType : \"triangle\";\n\t\tconst delay = utils.getFloat( options.delay, 0 );\n\t\tconst attack = utils.getFloat( options.attack, 0 );\n\t\tconst decay = utils.getFloat( options.decay, 0.1 );\n\n\t\t// Validate duration\n\t\tif( duration < 0 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"sound: Parameter duration must be a number greater than or equal to 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_DURATION\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate volume\n\t\tif( volume < 0 || volume > 1 ) {\n\t\t\tconst error = new RangeError( \"sound: Parameter volume must be a number between 0 and 1.\" );\n\t\t\terror.code = \"INVALID_VOLUME\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate attack\n\t\tif( attack < 0 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"sound: Parameter attack must be a number greater than or equal to 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_ATTACK\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Validate delay\n\t\tif( delay < 0 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"sound: Parameter delay must be a number greater than or equal to 0.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_DELAY\";\n\t\t\tthrow error;\n\t\t}\n\n\t\tlet waveTables = null;\n\n\t\t// Check for custom waveform (array)\n\t\tif( Array.isArray( oType ) ) {\n\t\t\tif(\n\t\t\t\toType.length !== 2 ||\n\t\t\t\toType[ 0 ].length === 0 ||\n\t\t\t\toType[ 1 ].length === 0 ||\n\t\t\t\toType[ 0 ].length !== oType[ 1 ].length\n\t\t\t) {\n\t\t\t\tconst error = new TypeError(\n\t\t\t\t\t\"sound: Parameter oType array must contain two non-empty arrays of equal length.\"\n\t\t\t\t);\n\t\t\t\terror.code = \"INVALID_WAVE_TABLE\";\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\twaveTables = [];\n\n\t\t\t// Validate all values are numbers\n\t\t\tfor( let i = 0; i < oType.length; i++ ) {\n\t\t\t\tfor( let j = 0; j < oType[ i ].length; j++ ) {\n\t\t\t\t\tif( isNaN( oType[ i ][ j ] ) ) {\n\t\t\t\t\t\tconst error = new TypeError(\n\t\t\t\t\t\t\t\"sound: Parameter oType array must only contain numbers.\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\terror.code = \"INVALID_WAVE_TABLE_VALUE\";\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twaveTables.push( new Float32Array( oType[ i ] ) );\n\t\t\t}\n\n\t\t\toType = \"custom\";\n\t\t} else if( typeof oType !== \"string\" ) {\n\t\t\tconst error = new TypeError( \"sound: Parameter oType must be a string or an array.\" );\n\t\t\terror.code = \"INVALID_OTYPE\";\n\t\t\tthrow error;\n\t\t} else {\n\n\t\t\t// Validate oType string\n\t\t\tconst validTypes = [ \"triangle\", \"sine\", \"square\", \"sawtooth\" ];\n\t\t\tif( validTypes.indexOf( oType ) === -1 ) {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t\"sound: Parameter oType must be one of: triangle, sine, square, sawtooth.\"\n\t\t\t\t);\n\t\t\t\terror.code = \"INVALID_OTYPE\";\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\t// Create audio context if needed\n\t\tif( !m_audioContext ) {\n\t\t\tconst AudioContextClass = window.AudioContext || window.webkitAudioContext;\n\t\t\tm_audioContext = new AudioContextClass();\n\t\t}\n\n\t\t// Calculate stop time\n\t\tconst stopTime = attack + duration + decay;\n\n\t\treturn createSound(\n\t\t\tm_audioContext, frequency, volume, attack, duration,\n\t\t\tdecay, stopTime, oType, waveTables, delay\n\t\t);\n\t}\n\n\t/**\n\t * Stop a playing sound or all sounds\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.soundId - Sound ID (null to stop all sounds)\n\t */\n\tpluginApi.addCommand( \"stopSound\", stopSound, false, [ \"soundId\" ] );\n\tfunction stopSound( options ) {\n\t\tconst soundId = options.soundId;\n\n\t\t// If no soundId, stop all sounds\n\t\tif( soundId == null ) {\n\t\t\tfor( const id in m_soundPool ) {\n\t\t\t\tm_soundPool[ id ].oscillator.stop( 0 );\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate soundId exists\n\t\tif( !m_soundPool[ soundId ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Stop the sound\n\t\tm_soundPool[ soundId ].oscillator.stop( 0 );\n\t}\n\n\t/**\n\t * Set global volume for all sounds\n\t * \n\t * @param {Object} options - Command options\n\t * @param {number} options.volume - Volume (0-1)\n\t */\n\tpluginApi.addCommand( \"setVolume\", setVolume, false, [ \"volume\" ] );\n\tfunction setVolume( options ) {\n\t\tconst volume = utils.getFloat( options.volume, 0.75 );\n\n\t\t// Validate volume\n\t\tif( volume < 0 || volume > 1 ) {\n\t\t\tconst error = new RangeError(\n\t\t\t\t\"setVolume: Parameter volume must be a number between 0 and 1.\"\n\t\t\t);\n\t\t\terror.code = \"INVALID_VOLUME\";\n\t\t\tthrow error;\n\t\t}\n\n\t\tm_volume = volume;\n\n\t\t// Update all active sounds\n\t\tfor( const soundId in m_soundPool ) {\n\t\t\tconst sound = m_soundPool[ soundId ];\n\t\t\tif( volume === 0 ) {\n\n\t\t\t\t// Use exponential ramp to near-zero, then set to zero\n\t\t\t\tsound.master.gain.exponentialRampToValueAtTime(\n\t\t\t\t\t0.01, sound.audioContext.currentTime + 0.1\n\t\t\t\t);\n\t\t\t\tsound.master.gain.setValueAtTime(\n\t\t\t\t\t0, sound.audioContext.currentTime + 0.11\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tsound.master.gain.exponentialRampToValueAtTime(\n\t\t\t\t\tvolume, sound.audioContext.currentTime + 0.1\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Update all audio pools\n\t\tfor( const poolId in m_audioPools ) {\n\t\t\tfor( let j = 0; j < m_audioPools[ poolId ].pool.length; j++ ) {\n\t\t\t\tconst poolItem = m_audioPools[ poolId ].pool[ j ];\n\t\t\t\tpoolItem.audio.volume = m_volume * poolItem.volume;\n\t\t\t}\n\t\t}\n\t}\n}\n\n", "/**\n * Pi.js - Play Module (Plugin)\n * \n * BASIC-style music notation playback (inspired by QBasic PLAY command).\n * \n * @module plugins/sound/play\n */\n\n\"use strict\";\n\nimport { createSound, stopSoundById } from \"./sound.js\";\n\nconst m_tracks = {};\nconst m_allTracks = [];\nlet m_lastTrackId = 0;\nlet m_playData = [];\nlet m_utils = null;\n\n// Note frequency data (in Hz) by note name and octave\nconst m_notesData = {\n\t\"A\": [ 27.50, 55.00, 110, 220, 440, 880, 1760, 3520, 7040, 14080 ],\n\t\"A#\": [ 29.14, 58.27, 116.541, 233.082, 466.164, 932.328, 1864.655, 3729.31, 7458.62, 14917.24 ],\n\t\"B\": [ 30.87, 61.74, 123.471, 246.942, 493.883, 987.767, 1975.533, 3951.066, 7902.132, 15804.264 ],\n\t\"C\": [ 16.35, 32.70, 65.41, 130.813, 261.626, 523.251, 1046.502, 2093.005, 4186.009, 8372.018 ],\n\t\"C#\": [ 17.32, 34.65, 69.296, 138.591, 277.183, 554.365, 1108.731, 2217.461, 4434.922, 8869.844 ],\n\t\"D\": [ 18.35, 36.71, 73.416, 146.832, 293.665, 587.33, 1174.659, 2349.318, 4698.636, 9397.272 ],\n\t\"D#\": [ 19.45, 38.89, 77.782, 155.563, 311.127, 622.254, 1244.508, 2489.016, 4978.032, 9956.064 ],\n\t\"E\": [ 20.60, 41.20, 82.407, 164.814, 329.628, 659.255, 1318.51, 2637.021, 5274.042, 10548.084 ],\n\t\"F\": [ 21.83, 43.65, 87.307, 174.614, 349.228, 698.456, 1396.913, 2793.826, 5587.652, 11175.304 ],\n\t\"F#\": [ 23.12, 46.25, 92.499, 184.997, 369.994, 739.989, 1479.978, 2959.955, 5919.91, 11839.82 ],\n\t\"G\": [ 24.50, 49.00, 97.999, 195.998, 391.995, 783.991, 1567.982, 3135.964, 6271.928, 12543.856 ],\n\t\"G#\": [ 25.96, 51.91, 103.826, 207.652, 415.305, 830.609, 1661.219, 3322.438, 6644.876, 13289.752 ]\n};\n\n// All notes by number (for N command)\nconst m_allNotes = [\n\t0, 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, 32.70,\n\t34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74, 65.406, 69.296,\n\t73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813,\n\t138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942,\n\t261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164,\n\t493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880,\n\t932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982,\n\t1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.021, 2793.826,\n\t2959.955, 3135.964, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032,\n\t5274.042, 5587.652, 5919.91, 6271.928, 6644.876, 7040, 7458.62, 7902.132, 8372.018, 8869.844,\n\t9397.272, 9956.064, 10548.084, 11175.304, 11839.82, 13289.752, 14080, 14917.24, 15804.264\n];\n\n\n/***************************************************************************************************\n * Internal Functions\n **************************************************************************************************/\n\n\n/**\n * Create a track from a play string\n * \n * @param {string} playString - Music notation string\n * @returns {number} First track ID\n */\nfunction createTrack( playString ) {\n\tlet firstTrackId;\n\n\t// Convert to uppercase and remove spaces\n\tplayString = playString.split( /\\s+/ ).join( \"\" ).toUpperCase();\n\n\t// Find and extract wavetables\n\tconst waveTables = [];\n\tlet start = 0;\n\twhile( start > -1 ) {\n\t\tstart = playString.indexOf( \"[[\" );\n\t\tif( start > -1 ) {\n\t\t\tconst end = playString.indexOf( \"]]\", start );\n\t\t\twaveTables.push( playString.substring( start, end + 2 ) );\n\t\t\tconst i = waveTables.length - 1;\n\t\t\tplayString = playString.replace( waveTables[ i ], \"W\" + i );\n\t\t}\n\t}\n\n\t// Convert wavetables to arrays\n\tfor( let i = 0; i < waveTables.length; i++ ) {\n\t\twaveTables[ i ] = JSON.parse( waveTables[ i ] );\n\n\t\t// Validate wavetable\n\t\tif(\n\t\t\twaveTables[ i ].length !== 2 ||\n\t\t\twaveTables[ i ][ 0 ].length !== waveTables[ i ][ 1 ].length\n\t\t) {\n\t\t\tconsole.error(\n\t\t\t\t\"play: Wavetables must have 2 arrays of same length. Defaulting to triangle wave.\"\n\t\t\t);\n\t\t\twaveTables[ i ] = \"triangle\";\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Validate all values are numbers\n\t\tfor( let j = 0; j < 2; j++ ) {\n\t\t\tfor( let k = 0; k < waveTables[ i ][ j ].length; k++ ) {\n\t\t\t\twaveTables[ i ][ j ][ k ] = parseFloat( waveTables[ i ][ j ][ k ] );\n\t\t\t\tif( isNaN( waveTables[ i ][ j ][ k ] ) ) {\n\t\t\t\t\twaveTables[ i ][ j ][ k ] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\twaveTables[ i ][ j ] = new Float32Array( waveTables[ i ][ j ] );\n\t\t}\n\t}\n\n\t// Split tracks by commas\n\tconst trackStrings = playString.split( \",\" );\n\tconst trackIds = [];\n\n\t// Regular expression for parsing play commands\n\tconst regString =\n\t\t\"(?=WS|WQ|WW|WT|W\\\\d[\\\\d]?|V\\\\d|Q\\\\d|O\\\\d|\\\\<|\\\\>|N\\\\d\\\\d?|\" +\n\t\t\"L\\\\d\\\\d?|MS|MN|ML|MU\\\\d|MU\\\\-\\\\d|MK\\\\d[\\\\d]?[\\\\d]?|\" +\n\t\t\"MZ\\\\d[\\\\d]?[\\\\d]?|MX\\\\d[\\\\d]?[\\\\d]?|MY\\\\d[\\\\d]?[\\\\d]?|\" +\n\t\t\"MW|P[\\\\d]?|T\\\\d|\" +\n\t\t\"[[A|B|C|D|E|F|G][\\\\d]?[\\\\+|\\\\-|\\\\#|\\\\.\\\\.?]?)\";\n\tconst reg = new RegExp( regString );\n\n\tlet lastNote;\n\n\tfor( let i = 0; i < trackStrings.length; i++ ) {\n\n\t\t// Replace complex keywords with short symbols\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /SINE/g, \"WS\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /SQUARE/g, \"WQ\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /SAWTOOTH/g, \"WW\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /TRIANGLE/g, \"WT\" );\n\n\t\t// Replace conflicting symbols\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MD/g, \"MZ\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MA/g, \"MY\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MT/g, \"MX\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MO/g, \"MU\" );\n\n\t\t// Remove deprecated symbols\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MB/g, \"\" );\n\t\ttrackStrings[ i ] = trackStrings[ i ].replace( /MF/g, \"\" );\n\n\t\t// Create track\n\t\tconst trackId = m_lastTrackId;\n\t\tif( firstTrackId === undefined ) {\n\t\t\tfirstTrackId = trackId;\n\t\t}\n\t\tm_lastTrackId += 1;\n\n\t\tm_tracks[ trackId ] = {\n\t\t\t\"id\": trackId,\n\t\t\t\"notes\": [],\n\t\t\t\"noteId\": 0,\n\t\t\t\"decayRate\": 0.20,\n\t\t\t\"attackRate\": 0.15,\n\t\t\t\"sustainRate\": 0.65,\n\t\t\t\"fullNote\": false,\n\t\t\t\"extra\": 1,\n\t\t\t\"space\": \"normal\",\n\t\t\t\"interval\": 0,\n\t\t\t\"time\": 0,\n\t\t\t\"simultaneousPlay\": i > 0,\n\t\t\t\"tempo\": 60 / 120,\n\t\t\t\"noteLength\": 0.25,\n\t\t\t\"pace\": 0.875,\n\t\t\t\"octave\": 4,\n\t\t\t\"octaveExtra\": 0,\n\t\t\t\"volume\": 1,\n\t\t\t\"trackIds\": trackIds,\n\t\t\t\"type\": \"triangle\",\n\t\t\t\"waveTables\": waveTables,\n\t\t\t\"sounds\": []\n\t\t};\n\t\tm_allTracks.push( trackId );\n\t\ttrackIds.push( trackId );\n\n\t\t// Mark previous note for simultaneous play\n\t\tif( i > 0 ) {\n\t\t\tlastNote.simultaneousPlay = trackId;\n\t\t}\n\n\t\t// Split track into command parts\n\t\tconst trackParts = trackStrings[ i ].split( reg );\n\n\t\tfor( let j = 0; j < trackParts.length; j++ ) {\n\t\t\tconst index = trackParts[ j ].indexOf( \"-\" );\n\n\t\t\t// Split minus symbol (only if not a music note)\n\t\t\tif( index > -1 && \"ABCDEFG\".indexOf( trackParts[ j ][ 0 ] ) === -1 ) {\n\t\t\t\tconst noteData = {\n\t\t\t\t\t\"name\": trackParts[ j ].substring( 0, index ),\n\t\t\t\t\t\"val\": trackParts[ j ].substring( index )\n\t\t\t\t};\n\t\t\t\tm_tracks[ trackId ].notes.push( noteData );\n\t\t\t\tlastNote = noteData;\n\t\t\t} else {\n\t\t\t\tconst noteParts = trackParts[ j ].split( /(\\d+)/ );\n\t\t\t\tconst noteData = {\n\t\t\t\t\t\"name\": noteParts[ 0 ]\n\t\t\t\t};\n\t\t\t\tif( noteParts.length > 1 ) {\n\t\t\t\t\tnoteData.val = noteParts[ 1 ];\n\t\t\t\t}\n\t\t\t\tm_tracks[ trackId ].notes.push( noteData );\n\t\t\t\tlastNote = noteData;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn firstTrackId;\n}\n\n/**\n * Play a track recursively\n * \n * @param {number} trackId - Track ID to play\n */\nfunction playTrack( trackId ) {\n\tconst track = m_tracks[ trackId ];\n\tif( track.noteId >= track.notes.length ) {\n\t\treturn;\n\t}\n\n\tconst cmd = track.notes[ track.noteId ];\n\tlet frequency = 0;\n\tlet val;\n\tlet wait = false;\n\ttrack.extra = 0;\n\n\tswitch( cmd.name.charAt( 0 ) ) {\n\t\tcase \"A\":\n\t\tcase \"B\":\n\t\tcase \"C\":\n\t\tcase \"D\":\n\t\tcase \"E\":\n\t\tcase \"F\":\n\t\tcase \"G\":\n\t\t\tfrequency = processNote( track, cmd );\n\t\t\twait = true;\n\t\t\tbreak;\n\t\tcase \"N\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\tval = m_utils.getInt( cmd.val, 0 );\n\t\t\t\tif( val >= 0 && val < m_allNotes.length ) {\n\t\t\t\t\tfrequency = m_allNotes[ val ];\n\t\t\t\t}\n\t\t\t\twait = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"O\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\tval = m_utils.getInt( cmd.val, 4 );\n\t\t\t\tif( val >= 0 && val < m_notesData[ \"A\" ].length ) {\n\t\t\t\t\ttrack.octave = val;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \">\":\n\t\t\ttrack.octave += 1;\n\t\t\tif( track.octave >= m_notesData[ \"A\" ].length ) {\n\t\t\t\ttrack.octave = m_notesData[ \"A\" ].length - 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"<\":\n\t\t\ttrack.octave -= 1;\n\t\t\tif( track.octave < 0 ) {\n\t\t\t\ttrack.octave = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"L\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\tval = m_utils.getInt( cmd.val, 1 );\n\t\t\t\ttrack.noteLength = getNoteLength( val );\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"T\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\tval = m_utils.getInt( cmd.val, 120 );\n\t\t\t\tif( val >= 32 && val < 256 ) {\n\t\t\t\t\ttrack.tempo = 60 / val;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"M\":\n\t\t\tprocessMusic( track, cmd );\n\t\t\tbreak;\n\t\tcase \"P\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\twait = true;\n\t\t\t\tval = m_utils.getInt( cmd.val, 1 );\n\t\t\t\ttrack.extra = getNoteLength( val );\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"V\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\t\t\tval = m_utils.getInt( cmd.val, 50 );\n\t\t\t\tif( val < 0 ) {\n\t\t\t\t\tval = 0;\n\t\t\t\t} else if( val > 100 ) {\n\t\t\t\t\tval = 100;\n\t\t\t\t}\n\t\t\t\ttrack.volume = val / 100;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"W\":\n\t\t\tprocessWaveform( track, cmd );\n\t\t\tbreak;\n\t}\n\n\t// Calculate interval for next note\n\tif( track.extra > 0 ) {\n\t\ttrack.interval = track.tempo * track.extra * track.pace * 4;\n\t} else {\n\t\ttrack.interval = track.tempo * track.noteLength * track.pace * 4;\n\t}\n\n\t// Play simultaneous track\n\tif( m_tracks[ cmd.simultaneousPlay ] ) {\n\t\tm_tracks[ cmd.simultaneousPlay ].time = track.time;\n\t\tcopyTrackData( m_tracks[ cmd.simultaneousPlay ].id, trackId );\n\t\tplayTrack( m_tracks[ cmd.simultaneousPlay ].id );\n\t}\n\n\t// Play note if frequency > 0\n\tif( frequency > 0 ) {\n\t\tplayNote( track, frequency );\n\t}\n\n\t// Move to next instruction\n\ttrack.noteId += 1;\n\n\t// Continue playing track\n\tif( track.noteId < track.notes.length ) {\n\t\tif( wait ) {\n\t\t\ttrack.time += track.interval;\n\t\t}\n\t\tplayTrack( trackId );\n\t} else {\n\n\t\t// Schedule track removal after completion\n\t\tsetTimeout( () => {\n\t\t\tif( m_tracks[ trackId ] ) {\n\t\t\t\tremoveTrack( trackId );\n\t\t\t}\n\t\t}, ( track.time + track.interval ) * 1000 );\n\t}\n}\n\n/**\n * Process a note command (A-G)\n * \n * @param {Object} track - Track object\n * @param {Object} cmd - Command object\n * @returns {number} Frequency in Hz\n */\nfunction processNote( track, cmd ) {\n\tlet note = cmd.name;\n\n\t// Convert + to sharp\n\tnote = note.replace( /\\+/g, \"#\" );\n\n\t// Convert flats to equivalent sharps\n\tnote = note.replace( \"C-\", \"B\" );\n\tnote = note.replace( \"D-\", \"C#\" );\n\tnote = note.replace( \"E-\", \"D#\" );\n\tnote = note.replace( \"G-\", \"F#\" );\n\tnote = note.replace( \"A-\", \"G#\" );\n\tnote = note.replace( \"B-\", \"A#\" );\n\n\t// Convert enharmonic equivalents\n\tnote = note.replace( \"E#\", \"F\" );\n\tnote = note.replace( \"B#\", \"C\" );\n\n\t// Check for extra note length (dotted notes)\n\tif( cmd.name.indexOf( \"..\" ) > 0 ) {\n\t\ttrack.extra = 1.75 * track.noteLength;\n\t} else if( cmd.name.indexOf( \".\" ) > 0 ) {\n\t\ttrack.extra = 1.5 * track.noteLength;\n\t}\n\n\t// Remove dots from note\n\tnote = note.replace( /\\./g, \"\" );\n\n\t// Get note frequency\n\tlet frequency = 0;\n\tif( m_notesData[ note ] ) {\n\t\tconst octave = track.octave + track.octaveExtra;\n\t\tif( octave < m_notesData[ note ].length ) {\n\t\t\tfrequency = m_notesData[ note ][ octave ];\n\t\t}\n\t}\n\n\t// Check if note length included\n\tif( !isNaN( Number( cmd.val ) ) ) {\n\t\tconst val = m_utils.getInt( cmd.val, 1 );\n\t\ttrack.extra = getNoteLength( val );\n\t}\n\n\treturn frequency;\n}\n\n/**\n * Process music style commands (M...)\n * \n * @param {Object} track - Track object\n * @param {Object} cmd - Command object\n */\nfunction processMusic( track, cmd ) {\n\tswitch( cmd.name ) {\n\t\tcase \"MS\":\n\n\t\t\t// Staccato\n\t\t\ttrack.pace = 0.75;\n\t\t\tbreak;\n\t\tcase \"MN\":\n\n\t\t\t// Normal\n\t\t\ttrack.pace = 0.875;\n\t\t\tbreak;\n\t\tcase \"ML\":\n\n\t\t\t// Legato\n\t\t\ttrack.pace = 1;\n\t\t\tbreak;\n\t\tcase \"MU\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\n\t\t\t\t// Modify octave\n\t\t\t\tconst val = m_utils.getInt( cmd.val, 0 );\n\t\t\t\ttrack.octaveExtra = val;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"MY\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\n\t\t\t\t// Modify attack rate\n\t\t\t\tconst val = m_utils.getInt( cmd.val, 25 );\n\t\t\t\ttrack.attackRate = val / 100;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"MX\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\n\t\t\t\t// Modify sustain rate\n\t\t\t\tconst val = m_utils.getInt( cmd.val, 25 );\n\t\t\t\ttrack.sustainRate = val / 100;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"MZ\":\n\t\t\tif( !isNaN( Number( cmd.val ) ) ) {\n\n\t\t\t\t// Modify decay rate\n\t\t\t\tconst val = m_utils.getInt( cmd.val, 25 );\n\t\t\t\ttrack.decayRate = val / 100;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"MW\":\n\n\t\t\t// Toggle full note\n\t\t\ttrack.fullNote = !track.fullNote;\n\t\t\tbreak;\n\t}\n}\n\n/**\n * Process waveform commands (W...)\n * \n * @param {Object} track - Track object\n * @param {Object} cmd - Command object\n */\nfunction processWaveform( track, cmd ) {\n\tif( cmd.name === \"WS\" ) {\n\t\ttrack.type = \"sine\";\n\t} else if( cmd.name === \"WQ\" ) {\n\t\ttrack.type = \"square\";\n\t} else if( cmd.name === \"WW\" ) {\n\t\ttrack.type = \"sawtooth\";\n\t} else if( cmd.name === \"WT\" ) {\n\t\ttrack.type = \"triangle\";\n\t} else if( !isNaN( Number( cmd.val ) ) ) {\n\n\t\t// Custom wavetable\n\t\tconst val = m_utils.getInt( cmd.val, -1 );\n\t\tif( track.waveTables[ val ] ) {\n\t\t\ttrack.type = val;\n\t\t}\n\t}\n}\n\n/**\n * Play a single note\n * \n * @param {Object} track - Track object\n * @param {number} frequency - Frequency in Hz\n */\nfunction playNote( track, frequency ) {\n\tconst volume = track.volume;\n\tconst attackTime = track.interval * track.attackRate;\n\tconst sustainTime = track.interval * track.sustainRate;\n\tconst decayTime = track.interval * track.decayRate;\n\n\tlet stopTime;\n\tif( track.fullNote && attackTime + sustainTime + decayTime > track.interval ) {\n\t\tstopTime = track.interval;\n\t} else {\n\t\tstopTime = attackTime + sustainTime + decayTime;\n\t}\n\n\tlet oType;\n\tlet waveTables = null;\n\tif( typeof track.type === \"string\" ) {\n\t\toType = track.type;\n\t} else {\n\t\twaveTables = track.waveTables[ track.type ];\n\t\tif( Array.isArray( waveTables ) ) {\n\t\t\toType = \"custom\";\n\t\t} else {\n\t\t\toType = waveTables;\n\t\t\twaveTables = null;\n\t\t}\n\t}\n\n\tconst soundData = {\n\t\t\"frequency\": frequency,\n\t\t\"volume\": volume,\n\t\t\"attackTime\": attackTime,\n\t\t\"sustainTime\": sustainTime,\n\t\t\"decayTime\": decayTime,\n\t\t\"stopTime\": stopTime,\n\t\t\"oType\": oType,\n\t\t\"waveTables\": waveTables,\n\t\t\"time\": track.time,\n\t\t\"track\": track\n\t};\n\n\tm_playData.push( soundData );\n}\n\n/**\n * Copy track data from source to destination\n * \n * @param {number} trackDestId - Destination track ID\n * @param {number} trackSourceId - Source track ID\n */\nfunction copyTrackData( trackDestId, trackSourceId ) {\n\tconst trackDest = m_tracks[ trackDestId ];\n\tconst trackSource = m_tracks[ trackSourceId ];\n\n\ttrackDest.decayRate = trackSource.decayRate;\n\ttrackDest.attackRate = trackSource.attackRate;\n\ttrackDest.sustainRate = trackSource.sustainRate;\n\ttrackDest.fullNote = trackSource.fullNote;\n\ttrackDest.extra = trackSource.extra;\n\ttrackDest.space = trackSource.space;\n\ttrackDest.interval = trackSource.interval;\n\ttrackDest.tempo = trackSource.tempo;\n\ttrackDest.noteLength = trackSource.noteLength;\n\ttrackDest.pace = trackSource.pace;\n\ttrackDest.octave = trackSource.octave;\n\ttrackDest.octaveExtra = trackSource.octaveExtra;\n\ttrackDest.volume = trackSource.volume;\n\ttrackDest.type = trackSource.type;\n}\n\n/**\n * Remove a track and all its sub-tracks\n * \n * @param {number} trackId - Track ID to remove\n */\nfunction removeTrack( trackId ) {\n\n\t// Delete all sub-tracks as well as main track\n\tconst trackIds = m_tracks[ trackId ].trackIds;\n\tfor( let i = trackIds.length; i >= 0; i-- ) {\n\t\tdelete m_tracks[ trackIds[ i ] ];\n\t}\n\n\t// Remove track from all tracks array\n\tfor( let i = m_allTracks.length - 1; i >= 0; i-- ) {\n\t\tif( !m_tracks[ m_allTracks[ i ] ] ) {\n\t\t\tm_allTracks.splice( i, 1 );\n\t\t}\n\t}\n}\n\n/**\n * Get note length from note value\n * \n * @param {number} val - Note value (1-64)\n * @returns {number} Note length\n */\nfunction getNoteLength( val ) {\n\tif( val >= 1 && val < 65 ) {\n\t\treturn 1 / val;\n\t}\n\treturn 0.875;\n}\n\n\n/***************************************************************************************************\n * Plugin Registration\n **************************************************************************************************/\n\n\n/**\n * Register play module commands\n * \n * @param {Object} pluginApi - Plugin API\n */\nexport function registerPlay( pluginApi ) {\n\tm_utils = pluginApi.utils;\n\n\t/**\n\t * Play music using BASIC-style notation\n\t * \n\t * Format: \"NOTE[length][.][#|+|-] ...\"\n\t * - Notes: A-G (can include sharps # or +, flats -)\n\t * - Length: 1-64 (1=whole, 4=quarter, etc.)\n\t * - Dot modifiers: . (1.5x), .. (1.75x)\n\t * - Multiple tracks: separated by commas\n\t * \n\t * Commands:\n\t * - O[n]: Set octave (0-9)\n\t * - L[n]: Set default note length\n\t * - T[n]: Set tempo (32-255 BPM)\n\t * - V[n]: Set volume (0-100)\n\t * - P[n]: Pause for note length\n\t * - N[n]: Play note by number (0-127)\n\t * - W[type]: Set waveform (SINE, SQUARE, SAWTOOTH, TRIANGLE)\n\t * - MS: Staccato (75% of note length)\n\t * - MN: Normal (87.5% of note length)\n\t * - ML: Legato (100% of note length)\n\t * - MW: Toggle full note mode\n\t * - MO[n]: Modify octave offset\n\t * - MA[n]: Modify attack rate (0-100)\n\t * - MT[n]: Modify sustain rate (0-100)\n\t * - MD[n]: Modify decay rate (0-100)\n\t * - <: Decrease octave\n\t * - >: Increase octave\n\t * \n\t * @param {Object} options - Command options\n\t * @param {string} options.playString - Music notation string\n\t */\n\tpluginApi.addCommand( \"play\", play, false, [ \"playString\" ] );\n\tfunction play( options ) {\n\t\tlet playString = options.playString;\n\n\t\t// Validate playString\n\t\tif( typeof playString !== \"string\" ) {\n\t\t\tconst error = new TypeError( \"play: Parameter playString must be a string.\" );\n\t\t\terror.code = \"INVALID_PLAY_STRING\";\n\t\t\tthrow error;\n\t\t}\n\n\t\t// Create track from play string\n\t\tconst trackId = createTrack( playString );\n\n\t\t// Generate all play data\n\t\tm_playData = [];\n\t\tplayTrack( trackId );\n\n\t\t// Sort by time\n\t\tm_playData.sort( ( a, b ) => a.time - b.time );\n\n\t\t// Create audio context\n\t\tconst AudioContextClass = window.AudioContext || window.webkitAudioContext;\n\t\tconst audioContext = new AudioContextClass();\n\n\t\t// Create all sounds\n\t\tfor( let i = 0; i < m_playData.length; i++ ) {\n\t\t\tconst playData = m_playData[ i ];\n\t\t\tplayData.track.sounds.push(\n\t\t\t\tcreateSound(\n\t\t\t\t\taudioContext, playData.frequency, playData.volume, playData.attackTime,\n\t\t\t\t\tplayData.sustainTime, playData.decayTime, playData.stopTime, playData.oType,\n\t\t\t\t\tplayData.waveTables, playData.time\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Stop playing music\n\t * \n\t * @param {Object} options - Command options\n\t * @param {number} options.trackId - Track ID to stop (null to stop all tracks)\n\t */\n\tpluginApi.addCommand( \"stopPlay\", stopPlay, false, [ \"trackId\" ] );\n\tfunction stopPlay( options ) {\n\t\tconst trackId = options.trackId;\n\n\t\t// Stop all tracks\n\t\tif( trackId === null ) {\n\t\t\tfor( let i = 0; i < m_allTracks.length; i++ ) {\n\t\t\t\tconst track = m_tracks[ m_allTracks[ i ] ];\n\t\t\t\tif( track ) {\n\t\t\t\t\tfor( let j = 0; j < track.sounds.length; j++ ) {\n\t\t\t\t\t\tstopSoundById( track.sounds[ j ] );\n\t\t\t\t\t}\n\t\t\t\t\tdelete m_tracks[ m_allTracks[ i ] ];\n\t\t\t\t}\n\t\t\t}\n\t\t\tm_allTracks.length = 0;\n\t\t\treturn;\n\t\t}\n\n\t\t// Stop specific track\n\t\tif( m_tracks[ trackId ] ) {\n\t\t\tconst track = m_tracks[ trackId ];\n\t\t\tfor( let j = 0; j < track.sounds.length; j++ ) {\n\t\t\t\tstopSoundById( track.sounds[ j ] );\n\t\t\t}\n\t\t\tremoveTrack( trackId );\n\t\t}\n\t}\n}\n\n", "/**\n * Pi.js - Play-Sound Plugin\n * \n * Music playback and sound effects using Web Audio API.\n * Combines play.js (BASIC-style music notation) and sound.js (sound effects).\n * \n * @module plugins/sound\n * @version 1.0.0\n */\n\n\"use strict\";\n\nimport { registerSound } from \"./sound.js\";\nimport { registerPlay } from \"./play.js\";\n\n\n/***************************************************************************************************\n * Plugin Initialization\n **************************************************************************************************/\n\n\n/**\n * Play-sound plugin initialization\n * \n * @param {Object} pluginApi - Plugin API provided by Pi.js\n */\nexport default function playSoundPlugin( pluginApi ) {\n\n\t// Register sound module commands\n\tregisterSound( pluginApi );\n\n\t// Register play module commands\n\tregisterPlay( pluginApi );\n}\n\n\n// Auto-register in IIFE mode (when loaded via <script> tag)\nif( typeof window !== \"undefined\" && window.pi ) {\n\twindow.pi.registerPlugin( {\n\t\t\"name\": \"sound\",\n\t\t\"version\": \"1.0.0\",\n\t\t\"description\": \"Music playback and sound effects using Web Audio API\",\n\t\t\"init\": playSoundPlugin\n\t} );\n}\n\n"],
  "mappings": "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",
  "names": ["m_audioContext", "m_audioPools", "m_nextAudioId", "m_soundPool", "m_nextSoundId", "m_volume", "loadAudioItem", "pluginApi", "audioItem", "audio", "retryCount", "audioReady", "audioError", "errors", "errorCode", "index", "newAudio", "stopSoundById", "soundId", "createSound", "audioContext", "frequency", "volume", "attackTime", "sustainTime", "decayTime", "stopTime", "oType", "waveTables", "delay", "oscillator", "envelope", "master", "real", "imag", "wave", "currentTime", "registerSound", "utils", "loadAudio", "options", "src", "poolSize", "audioName", "error", "audioId", "i", "removeAudio", "poolItem", "playAudio", "startTime", "duration", "playPromise", "stopAudio", "poolId", "j", "sound", "attack", "decay", "AudioContextClass", "stopSound", "id", "setVolume", "m_tracks", "m_allTracks", "m_lastTrackId", "m_playData", "m_utils", "m_notesData", "m_allNotes", "createTrack", "playString", "firstTrackId", "waveTables", "start", "end", "i", "j", "k", "trackStrings", "trackIds", "regString", "reg", "lastNote", "trackId", "trackParts", "index", "noteData", "noteParts", "playTrack", "track", "cmd", "frequency", "val", "wait", "processNote", "getNoteLength", "processMusic", "processWaveform", "copyTrackData", "playNote", "removeTrack", "note", "octave", "volume", "attackTime", "sustainTime", "decayTime", "stopTime", "oType", "soundData", "trackDestId", "trackSourceId", "trackDest", "trackSource", "registerPlay", "pluginApi", "play", "options", "error", "a", "b", "AudioContextClass", "audioContext", "playData", "createSound", "stopPlay", "stopSoundById", "playSoundPlugin", "pluginApi", "registerSound", "registerPlay"]
}
