audio-mixer-engine 0.1.1 → 0.2.0

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/README.md CHANGED
@@ -166,7 +166,7 @@ player.setPlaybackSpeed(1.5);
166
166
  // Musical navigation
167
167
  player.setBar(4, 0); // Jump to bar 4, repeat 0
168
168
  const time = player.getTimeFromBar(2); // Get time for bar 2
169
- const barInfo = player.getBarFromTime(15.3); // Get bar info at time
169
+ const barInfo = player.getBeatFromTime(15.3); // Get bar info at time
170
170
 
171
171
  // Emergency stop
172
172
  player.allSoundsOff();
@@ -198,9 +198,12 @@ masterGain.gain.value = 0.8; // Master volume
198
198
  sopranoAnalyzer.getByteFrequencyData(dataArray);
199
199
 
200
200
  // Event handling
201
- player.on('timeupdate', ({ currentTime }) => { /* */ });
202
- player.on('ended', ({ finalTime }) => { /* */ });
203
- player.on('barChanged', ({ bar, beat, repeat, time }) => { /* */ });
201
+ player.on('timeupdate', ({currentTime}) => { /* */
202
+ });
203
+ player.on('ended', ({finalTime}) => { /* */
204
+ });
205
+ player.on('barChanged', ({bar, beat, repeat, time}) => { /* */
206
+ });
204
207
  ```
205
208
 
206
209
  ### PlaybackManager Methods
@@ -586,7 +589,7 @@ playbackManager.on('startupSettingsChanged', ({ delayMs }) => {
586
589
 
587
590
  ```javascript
588
591
  // Set up event listener for bar changes
589
- player.on('barChanged', ({ bar, beat, repeat, time }) => {
592
+ player.on('barChanged', ({bar, beat, repeat, time}) => {
590
593
  console.log(`Now at Bar ${bar}, Beat ${beat} (${time.toFixed(2)}s)`);
591
594
  updateScoreHighlight(bar);
592
595
  });
@@ -597,7 +600,7 @@ player.setBar(3, 1); // Jump to bar 3, repeat 1
597
600
 
598
601
  // Time/bar conversion
599
602
  const bar8Time = player.getTimeFromBar(8); // Get time for bar 8
600
- const currentBar = player.getBarFromTime(player.getCurrentTime());
603
+ const currentBar = player.getBeatFromTime(player.getCurrentTime());
601
604
  ```
602
605
 
603
606
  ### Event-Driven Mixer Integration
@@ -684,11 +687,12 @@ new MidiPlayer(audioEngine, instrumentMap, parsedData, structureMetadata);
684
687
  ```
685
688
 
686
689
  ### New Methods
690
+
687
691
  ```javascript
688
692
  // Musical navigation
689
693
  player.setBar(barNumber, repeat);
690
694
  player.getTimeFromBar(barNumber, repeat);
691
- player.getBarFromTime(timeInSeconds);
695
+ player.getBeatFromTime(timeInSeconds);
692
696
 
693
697
  // Master volume control
694
698
  player.setMasterVolume(0.7);
@@ -783,26 +787,7 @@ Check out the demo files:
783
787
 
784
788
  MIT License - see LICENSE file for details.
785
789
 
786
- ## Browser Compatibility
787
-
788
- ### Firefox Compatibility Notes
789
-
790
- This library uses SpessaSynth for audio synthesis, which has specific Firefox compatibility considerations:
791
-
792
- - **Firefox is recommended** over Chromium-based browsers due to better Web Audio API support and memory handling for large soundfonts
793
- - **Known Firefox crashes** have been addressed in recent SpessaSynth versions (fixed crash due to Firefox browser bug #1918506)
794
- - **Mitigation strategies implemented**:
795
- - AudioWorklet module loading includes timing delays and retry logic to prevent initialization race conditions
796
- - Soundfont loading includes progress feedback and chunked processing
797
- - Engine initialization has automatic retry mechanism (3 attempts with exponential backoff)
798
- - Memory management optimization before SpessaSynth initialization
799
-
800
- If experiencing intermittent crashes (~20% rate) during initialization in Firefox:
801
- 1. Ensure you're using the latest SpessaSynth version
802
- 2. The built-in retry mechanism should automatically handle most timing-related failures
803
- 3. Consider implementing additional delays if crashes persist in your specific environment
804
-
805
- ### Other Browsers
790
+ ### Browsers
806
791
  - **Chromium-based browsers** (Chrome, Edge, Brave): May experience audio distortion due to Chromium Web Audio API bugs
807
792
  - **Safari**: Not extensively tested but should work with Web Audio API polyfills
808
793
 
@@ -1 +1 @@
1
- "use strict";var B=Object.create;var C=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var D=(m,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of N(t))!M.call(m,i)&&i!==e&&C(m,i,{get:()=>t[i],enumerable:!(s=E(t,i))||s.enumerable});return m};var A=(m,t,e)=>(e=m!=null?B(I(m)):{},D(t||!m||!m.__esModule?C(e,"default",{value:m,enumerable:!0}):e,m));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class k{constructor(t,e={}){if(new.target===k)throw new Error("AudioEngine is abstract and cannot be instantiated directly");this.audioContext=t,this.options=e,this.isInitialized=!1,this.channels=new WeakMap,this.activeChannels=new Set}async initialize(t){throw new Error("initialize() must be implemented by subclass")}createChannel(t,e={}){throw new Error("createChannel() must be implemented by subclass")}allSoundsOff(){throw new Error("allSoundsOff() must be implemented by subclass")}getActiveChannels(){return Array.from(this.activeChannels)}destroy(){this.allSoundsOff(),this.activeChannels.clear(),this.isInitialized=!1}_validateInitialized(){if(!this.isInitialized)throw new Error("AudioEngine not initialized. Call initialize() first.")}_registerChannel(t){this.activeChannels.add(t)}_unregisterChannel(t){this.activeChannels.delete(t),this.channels.delete(t)}}class v{constructor(t,e,s={}){if(new.target===v)throw new Error("ChannelHandle is abstract and cannot be instantiated directly");this.engine=t,this.partId=e,this.options={initialVolume:1,...s},this.isDestroyed=!1,this.noteRefCounts=new Map,this.scheduledEvents=new Map,this.activeNotes=new Set}getOutputNode(){throw new Error("getOutputNode() must be implemented by subclass")}noteOn(t,e){this._validateActive();const s=this.noteRefCounts.get(t)||0;this.noteRefCounts.set(t,s+1),this._actualNoteOn(t,e),s===0&&this.activeNotes.add(t)}noteOff(t){this._validateActive();const e=this.noteRefCounts.get(t)||0;if(e<=0)return;const s=e-1;this.noteRefCounts.set(t,s),s===0&&(this._actualNoteOff(t),this.activeNotes.delete(t),this.noteRefCounts.delete(t))}playNote(t,e,s,i){this._validateActive();const r=this.engine.audioContext.currentTime,n=`${this.partId}_${t}_${e}_${Date.now()}`;let l=t,o=i;if(t<r){const f=r-t;l=r,o=Math.max(0,i-f)}if(o<=0)return n;const a=Math.max(0,(l-r)*1e3),c=setTimeout(()=>{this.noteOn(e,s),this.scheduledEvents.delete(`${n}_on`)},a),h=a+o*1e3,d=setTimeout(()=>{this.noteOff(e),this.scheduledEvents.delete(`${n}_off`)},h);return this.scheduledEvents.set(`${n}_on`,c),this.scheduledEvents.set(`${n}_off`,d),n}allNotesOff(){this._validateActive(),this.scheduledEvents.forEach(t=>{clearTimeout(t)}),this.scheduledEvents.clear(),this.activeNotes.forEach(t=>{this._actualNoteOff(t)}),this.noteRefCounts.clear(),this.activeNotes.clear()}_actualNoteOn(t,e){throw new Error("_actualNoteOn() must be implemented by subclass")}_actualNoteOff(t){throw new Error("_actualNoteOff() must be implemented by subclass")}async setInstrument(t){throw new Error("setInstrument() must be implemented by subclass")}getInstrument(){throw new Error("getInstrument() must be implemented by subclass")}setVolume(t){throw new Error("setVolume() must be implemented by subclass")}getVolume(){throw new Error("getVolume() must be implemented by subclass")}getPartId(){return this.partId}isActive(){return!this.isDestroyed&&this.engine.isInitialized&&this.engine.activeChannels.has(this)}destroy(){if(!this.isDestroyed){this.allNotesOff();const t=this.getOutputNode();t&&t.disconnect(),this.noteRefCounts.clear(),this.scheduledEvents.clear(),this.activeNotes.clear(),this.engine._unregisterChannel(this),this.isDestroyed=!0}}_validateActive(){if(this.isDestroyed)throw new Error("Channel has been destroyed");if(!this.engine.isInitialized)throw new Error("AudioEngine is not initialized")}}class x{static getInstrumentProgram(t){if(typeof t=="number")return t;const s={piano:0,bright_piano:1,electric_grand:2,honky_tonk:3,electric_piano_1:4,electric_piano_2:5,harpsichord:6,clavinet:7,celesta:8,glockenspiel:9,music_box:10,vibraphone:11,marimba:12,xylophone:13,tubular_bells:14,dulcimer:15,drawbar_organ:16,percussive_organ:17,rock_organ:18,church_organ:19,reed_organ:20,accordion:21,harmonica:22,tango_accordion:23,organ:19,nylon_guitar:24,steel_guitar:25,electric_guitar_jazz:26,electric_guitar_clean:27,electric_guitar_muted:28,overdriven_guitar:29,distortion_guitar:30,guitar_harmonics:31,guitar:24,acoustic_bass:32,electric_bass_finger:33,electric_bass_pick:34,fretless_bass:35,slap_bass_1:36,slap_bass_2:37,synth_bass_1:38,synth_bass_2:39,bass:32,violin:40,viola:41,cello:42,contrabass:43,tremolo_strings:44,pizzicato_strings:45,orchestral_harp:46,timpani:47,strings:48,strings_ensemble:48,slow_strings:49,synth_strings_1:50,synth_strings_2:51,choir_aahs:52,voice_oohs:53,synth_voice:54,orchestra_hit:55,trumpet:56,trombone:57,tuba:58,muted_trumpet:59,french_horn:60,brass_section:61,synth_brass_1:62,synth_brass_2:63,soprano_sax:64,alto_sax:65,tenor_sax:66,baritone_sax:67,oboe:68,english_horn:69,bassoon:70,clarinet:71,saxophone:64,piccolo:72,flute:73,recorder:74,pan_flute:75,blown_bottle:76,shakuhachi:77,whistle:78,ocarina:79,lead_1_square:80,lead_2_sawtooth:81,lead_3_calliope:82,lead_4_chiff:83,lead_5_charang:84,lead_6_voice:85,lead_7_fifths:86,lead_8_bass:87,pad_1_new_age:88,pad_2_warm:89,pad_3_polysynth:90,pad_4_choir:91,pad_5_bowed:92,pad_6_metallic:93,pad_7_halo:94,pad_8_sweep:95,fx_1_rain:96,fx_2_soundtrack:97,fx_3_crystal:98,fx_4_atmosphere:99,fx_5_brightness:100,fx_6_goblins:101,fx_7_echoes:102,fx_8_sci_fi:103,sitar:104,banjo:105,shamisen:106,koto:107,kalimba:108,bag_pipe:109,fiddle:110,shanai:111,tinkle_bell:112,agogo:113,steel_drums:114,woodblock:115,taiko_drum:116,melodic_tom:117,synth_drum:118,reverse_cymbal:119,guitar_fret_noise:120,breath_noise:121,seashore:122,bird_tweet:123,telephone_ring:124,helicopter:125,applause:126,gunshot:127}[t.toLowerCase()];return s!==void 0?s:0}static getProgramName(t){return["Piano","Bright Piano","Electric Grand","Honky-tonk Piano","Electric Piano 1","Electric Piano 2","Harpsichord","Clavinet","Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer","Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accordion","Harmonica","Tango Accordion","Nylon Guitar","Steel Guitar","Electric Guitar (jazz)","Electric Guitar (clean)","Electric Guitar (muted)","Overdriven Guitar","Distortion Guitar","Guitar Harmonics","Acoustic Bass","Electric Bass (finger)","Electric Bass (pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Harp","Timpani","String Ensemble 1","String Ensemble 2","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet","Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Shakuhachi","Whistle","Ocarina","Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass + lead)","Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)","FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag pipe","Fiddle","Shanai","Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal","Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"][t]||`Program ${t}`}static generateNoteId(t,e,s){return`${t}_${e}_${Math.round(s)}`}}class S extends v{constructor(t,e,s,i={}){super(t,e,i),this.midiChannel=s,this.currentVolume=i.initialVolume||1,this.currentInstrument=i.instrument||"piano",this.outputGain=null,this._setupOutputNode(),this.setVolume(this.currentVolume),i.instrument&&this.setInstrument(i.instrument)}getOutputNode(){return this.outputGain}_actualNoteOn(t,e){const s=this.engine._getSynthesizer();if(s&&s.noteOn){const i=Math.round(e*this.currentVolume);s.noteOn(this.midiChannel,t,i)}}_actualNoteOff(t){const e=this.engine._getSynthesizer();e&&e.noteOff&&e.noteOff(this.midiChannel,t)}async setInstrument(t){this._validateActive();const e=x.getInstrumentProgram(t);this.currentInstrument=t;const s=this.engine._getSynthesizer();s&&s.programChange?s.programChange(this.midiChannel,e):console.warn("Cannot set instrument: synthesizer not available or no programChange method")}getInstrument(){return this.currentInstrument}setVolume(t){this._validateActive(),t=Math.max(0,Math.min(1,t)),this.currentVolume=t;const e=Math.round(t*127),s=this.engine._getSynthesizer();s&&s.controllerChange&&s.controllerChange(this.midiChannel,7,e)}getVolume(){return this.currentVolume}getMidiChannel(){return this.midiChannel}getActiveNoteCount(){return this.activeNotes.size}destroy(){if(!this.isDestroyed){const t=this.engine._getIndividualOutput(this.midiChannel);this.outputGain&&this.outputGain!==t&&this.outputGain.disconnect(),this.outputGain=null}super.destroy()}_setupOutputNode(){const t=this.engine._getIndividualOutput(this.midiChannel);t?this.outputGain=t:(console.warn(`No individual output available for MIDI channel ${this.midiChannel}, using fallback`),this.outputGain=this.engine.audioContext.createGain(),this.outputGain.gain.value=this.currentVolume)}}class z extends k{constructor(t,e={}){super(t,e),this.synthesizer=null,this.soundfont=null,this.channelCounter=0,this.partToMidiChannel=new Map,this.midiChannelToPart=new Map,this.individualOutputs=[]}async initialize(t){try{const{Synthetizer:e}=await import("spessasynth_lib");let s;if(typeof t=="string")console.log("Loading soundfont from path:",t),s=await this._loadSoundfontFromPath(t),console.log("Soundfont loaded successfully, size:",s.byteLength,"bytes");else if(t instanceof ArrayBuffer)s=t;else throw new Error("Invalid soundfont data type. Expected string path or ArrayBuffer.");await this._loadAudioWorkletSafely(),this._setupIndividualOutputs(),this.dummyTarget=this.audioContext.createGain(),await new Promise(i=>setTimeout(i,50)),this.synthesizer=new e(this.dummyTarget,s),this._connectIndividualOutputs(),this.isInitialized=!0}catch(e){throw console.error("Failed to initialize SpessaSynthAudioEngine:",e),e}}createChannel(t,e={}){if(this._validateInitialized(),this.partToMidiChannel.has(t))throw new Error(`Channel for part '${t}' already exists`);const s=this.channelCounter;if(s>=16)throw new Error("Maximum number of MIDI channels (16) exceeded");this.channelCounter++,this.partToMidiChannel.set(t,s),this.midiChannelToPart.set(s,t);const i=new S(this,t,s,e);return this._registerChannel(i),e.instrument&&i.setInstrument(e.instrument),i}allSoundsOff(){this.synthesizer&&this.midiChannelToPart.forEach((t,e)=>{this.synthesizer.controllerChange&&this.synthesizer.controllerChange(e,120,0)})}clearAllChannels(){this.allSoundsOff(),this.partToMidiChannel.clear(),this.midiChannelToPart.clear(),this.channelCounter=0}destroy(){this.allSoundsOff(),this.synthesizer&&typeof this.synthesizer.disconnect=="function"&&this.synthesizer.disconnect(),this.individualOutputs.forEach(t=>{t&&t.disconnect&&t.disconnect()}),this.individualOutputs=[],this.dummyTarget&&(this.dummyTarget.disconnect(),this.dummyTarget=null),this.partToMidiChannel.clear(),this.midiChannelToPart.clear(),this.channelCounter=0,super.destroy(),this.synthesizer=null,this.soundfont=null}getMidiChannelForPart(t){return this.partToMidiChannel.has(t)?this.partToMidiChannel.get(t):null}_getSynthesizer(){return this.synthesizer}_getIndividualOutput(t){return t>=0&&t<this.individualOutputs.length?this.individualOutputs[t]:null}_setupIndividualOutputs(){this.individualOutputs=[];for(let t=0;t<16;t++){const e=this.audioContext.createGain();e.gain.value=1,this.individualOutputs.push(e)}}_connectIndividualOutputs(){try{this.synthesizer&&this.synthesizer.connectIndividualOutputs?this.synthesizer.connectIndividualOutputs(this.individualOutputs):console.warn("Synthesizer does not support individual outputs, using master output only")}catch(t){console.warn("Failed to connect individual outputs:",t.message),console.warn("Falling back to master output routing")}}async _loadSoundfontFromPath(t){if(typeof window<"u"){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load soundfont: ${e.status} ${e.statusText}`);return await e.arrayBuffer()}else{const e=await Promise.resolve().then(()=>P),s=await Promise.resolve().then(()=>P),i=s.isAbsolute(t)?t:s.resolve(process.cwd(),t);return e.readFileSync(i).buffer}}async _loadAudioWorkletSafely(){const t="/node_modules/spessasynth_lib/synthetizer/worklet_processor.min.js",s=typeof navigator<"u"&&navigator.userAgent.toLowerCase().includes("firefox");for(let i=1;i<=5;i++)try{if(s){const r=i*200;await new Promise(n=>setTimeout(n,r)),"gc"in window&&typeof window.gc=="function"&&window.gc(),this.audioContext.state==="suspended"&&await this.audioContext.resume()}console.log(`Attempting to load AudioWorklet (attempt ${i}/5)`),await this.audioContext.audioWorklet.addModule(t),console.log(`AudioWorklet loaded successfully on attempt ${i}`);return}catch(r){if(console.warn(`AudioWorklet loading failed (attempt ${i}/5):`,r.message),i===5)throw new Error(`AudioWorklet failed after 5 attempts: ${r.message}`);const n=s?i*1e3:i*500;await new Promise(l=>setTimeout(l,n))}}}class F{constructor(){this.partNames=["soprano","alto","tenor","bass","treble","mezzo","baritone","s","a","t","b","satb"],this.parsedData={parts:{},barStructure:[],metadata:{}}}async parse(t){try{const e=await this._parseMidiBuffer(t);return this._extractMetadata(e),this._extractBarStructure(e),this._extractParts(e),this.parsedData}catch(e){throw console.error("Error parsing MIDI file:",e),e}}async _parseMidiBuffer(t){const e=new Uint8Array(t);if(!(e[0]===77&&e[1]===84&&e[2]===104&&e[3]===100))throw new Error("Not a valid MIDI file");const s=this._bytesToNumber(e.slice(4,8)),i=this._bytesToNumber(e.slice(8,10)),r=this._bytesToNumber(e.slice(10,12)),n=this._bytesToNumber(e.slice(12,14)),l=n&32768?null:n,o={format:i,ticksPerBeat:l,tracks:[],duration:0};let a=8+s;for(let c=0;c<r;c++)if(e[a]===77&&e[a+1]===84&&e[a+2]===114&&e[a+3]===107){const h=this._bytesToNumber(e.slice(a+4,a+8)),d=e.slice(a+8,a+8+h),f=this._parseTrack(d);o.tracks.push(f),a+=8+h}else throw new Error(`Invalid track header at position ${a}`);return o}_parseTrack(t){const e={notes:[],name:null,lyrics:[],events:[],duration:0};let s=0,i=0,r=null;for(;s<t.length;){let n=0,l=0;do l=t[s++],n=n<<7|l&127;while(l&128);i+=n,l=t[s++];let o=l;if((l&128)===0){if(r===null)throw new Error("Running status byte encountered before status byte");o=r,s--}else r=o;if(o===255){const a=t[s++],c=this._readVariableLengthValue(t,s);s+=c.bytesRead;const h=t.slice(s,s+c.value);switch(s+=c.value,a){case 3:e.name=this._bytesToString(h);break;case 1:e.events.push({type:"text",text:this._bytesToString(h),tick:i});break;case 5:e.lyrics.push({text:this._bytesToString(h),tick:i});break;case 81:const d=this._bytesToNumber(h),f=Math.round(6e7/d);e.events.push({type:"tempo",bpm:f,tick:i});break;case 88:e.events.push({type:"timeSignature",numerator:h[0],denominator:Math.pow(2,h[1]),tick:i});break;case 47:e.duration=i;break}}else if((o&240)===144){const a=o&15,c=t[s++],h=t[s++];h>0?e.notes.push({type:"noteOn",noteNumber:c,velocity:h,tick:i,channel:a}):e.notes.push({type:"noteOff",noteNumber:c,tick:i,channel:a})}else if((o&240)===128){const a=o&15,c=t[s++];t[s++],e.notes.push({type:"noteOff",noteNumber:c,tick:i,channel:a})}else if(o===240||o===247){const a=this._readVariableLengthValue(t,s);s+=a.bytesRead+a.value}else if((o&240)===176){const a=o&15,c=t[s++],h=t[s++];e.events.push({type:"controller",controllerNumber:c,value:h,channel:a,tick:i})}else if((o&240)===192){const a=o&15,c=t[s++];e.events.push({type:"programChange",programNumber:c,channel:a,tick:i})}else if((o&240)===208){const a=o&15,c=t[s++];e.events.push({type:"channelAftertouch",pressure:c,channel:a,tick:i})}else if((o&240)===224){const a=o&15,c=t[s++],d=(t[s++]<<7|c)-8192;e.events.push({type:"pitchBend",value:d,channel:a,tick:i})}else if((o&240)===160){const a=o&15,c=t[s++],h=t[s++];e.events.push({type:"noteAftertouch",noteNumber:c,pressure:h,channel:a,tick:i})}else console.warn(`Unknown event type: ${o.toString(16)} at position ${s-1}`),s++}return e}_extractMetadata(t){const e={title:null,composer:null,partNames:[],format:t.format,ticksPerBeat:t.ticksPerBeat};t.tracks.forEach((s,i)=>{if(s.name&&!e.title&&(e.title=s.name),s.events.filter(r=>r.type==="text").forEach(r=>{const n=r.text.toLowerCase();(n.includes("compos")||n.includes("by"))&&!e.composer&&(e.composer=r.text)}),s.name){const r=s.name.toLowerCase();for(const n of this.partNames)if(r.includes(n)){e.partNames.push({index:i,name:s.name});break}}}),this.parsedData.metadata=e}_extractBarStructure(t){const e=t.ticksPerBeat||480,s=[];t.tracks.forEach(h=>{h.events.forEach(d=>{(d.type==="timeSignature"||d.type==="tempo")&&s.push(d)})}),s.sort((h,d)=>h.tick-d.tick);let i=0;t.tracks.forEach(h=>{h.notes&&h.notes.forEach(d=>{d.type==="noteOff"&&d.tick>i&&(i=d.tick)})}),i===0&&(i=e*8);const r=[],n=s.filter(h=>h.type==="timeSignature").sort((h,d)=>h.tick-d.tick);let l={numerator:4,denominator:4},o=120,a=0,c=0;for(;a<i;){for(;c<n.length&&n[c].tick<=a;)l=n[c],c++;let h;h=a+e*4*l.numerator/l.denominator;const d=s.filter(g=>g.type==="tempo"&&g.tick>=a&&g.tick<h),f=l.numerator,u=[];for(let g=0;g<f;g++){const y=a+g*e;let w=o;for(let b=d.length-1;b>=0;b--){const T=d[b];if(T.tick<=y){w=T.bpm;break}}for(let b=s.length-1;b>=0;b--){const T=s[b];if(T.type==="tempo"&&T.tick<=y){w=T.bpm,T.tick<=a&&(o=T.bpm);break}}u.push(w*l.denominator/4)}const _=u.every(g=>g===u[0])?u[0]:u;r.push({sig:[l.numerator,l.denominator],bpm:_}),a=h}this.parsedData.barStructure=r}_extractParts(t){const e={},s=t.ticksPerBeat;t.tracks.forEach((i,r)=>{if(!i.notes.length)return;let n=null;if(i.name){const u=i.name.toLowerCase();for(const p of this.partNames)if(p.length===1){if(u===p){n=p;break}}else if(u.includes(p)){n=p;break}}n||(n=i.name||`Track ${r+1}`),n==="s"&&(n="soprano"),n==="a"&&(n="alto"),n==="t"&&(n="tenor"),n==="b"&&(n="bass");let l=n,o=2;for(;e[l];)l=`${n} ${o}`,o++;n=l;const a=[],c={};i.notes.forEach(u=>{if(u.type==="noteOn")c[u.noteNumber]={tick:u.tick,velocity:u.velocity};else if(u.type==="noteOff"&&c[u.noteNumber]){const p=c[u.noteNumber],_=u.tick-p.tick;a.push({pitch:u.noteNumber,name:this._midiNoteToName(u.noteNumber),startTick:p.tick,endTick:u.tick,duration:_,startTime:this._ticksToTime(p.tick,t),endTime:this._ticksToTime(u.tick,t),velocity:p.velocity}),delete c[u.noteNumber]}});const h=i.lyrics.map(u=>({text:u.text,tick:u.tick,time:u.tick/s}));a.sort((u,p)=>u.startTick-p.startTick);const d=i.events.filter(u=>u.type==="programChange").map(u=>({programNumber:u.programNumber,tick:u.tick,time:this._ticksToTime(u.tick,t)})).sort((u,p)=>u.tick-p.tick),f=d.length>0?d[0].programNumber:0;e[n]={notes:a,lyrics:h,trackIndex:r,programChanges:d,defaultInstrument:f}}),this.parsedData.parts=e}_midiNoteToName(t){const e=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],s=Math.floor(t/12)-1;return`${e[t%12]}${s}`}_bytesToNumber(t){let e=0;for(let s=0;s<t.length;s++)e=e<<8|t[s];return e}_bytesToString(t){return new TextDecoder().decode(t)}_readVariableLengthValue(t,e){let s=0,i,r=0;do i=t[e+r++],s=s<<7|i&127;while(i&128);return{value:s,bytesRead:r}}_ticksToTime(t,e){const s=e.ticksPerBeat||480,i=[];e.tracks.forEach(o=>{o.events.forEach(a=>{a.type==="tempo"&&i.push(a)})}),i.sort((o,a)=>o.tick-a.tick);let r=0,n=0,l=120;for(const o of i){if(o.tick>t)break;if(o.tick>n){const c=(o.tick-n)/s*(60/l);r+=c,n=o.tick}l=o.bpm}if(t>n){const a=(t-n)/s*(60/l);r+=a}return r}}class O{constructor(){this.barOrder=[],this.beatTable=[]}mapBeats(t,e){try{return this.barOrder=this.generateBarOrder(e.sections,e.order),this.beatTable=this.generateBeatTable(this.barOrder,t.barStructure),this.beatTable}catch(s){throw console.error("Error mapping beats:",s),s}}generateBarOrder(t,e){const s=[],i={};for(const r of e){const n=t[r.section];if(!n)throw new Error(`Invalid section index: ${r.section}`);const l=r.section;i[l]||(i[l]=0),i[l]++;const o=i[l],a=r.from!==void 0?r.from:this._getSectionStartBar(t,r.section),c=r.to!==void 0?r.to:n.to,h=r.as||1;for(let d=a;d<=c;d++)this._shouldPlayBar(n,d,h)&&s.push({barNumber:d,repeat:o,sectionIndex:r.section,voltaTime:h})}return s}generateBeatTable(t,e){const s=[];let i=0;for(const r of t){const n=r.barNumber,l=e[n];if(!l)throw new Error(`No bar structure data found for bar ${n}`);const{sig:[o,a],bpm:c}=l,h=60/c;for(let d=1;d<=o;d++)s.push({time:i,repeat:r.repeat,bar:n,beat:d}),i+=h}return s}_getSectionStartBar(t,e){return t[e].pickup!==void 0?0:e===0?1:t[e-1].to+1}_shouldPlayBar(t,e,s){if(!t.voltas)return!0;const i=t.voltas.indexOf(e);return i===-1?!0:i+1===s}}function $(m){return{all:m=m||new Map,on:function(t,e){var s=m.get(t);s?s.push(e):m.set(t,[e])},off:function(t,e){var s=m.get(t);s&&(e?s.splice(s.indexOf(e)>>>0,1):m.set(t,[]))},emit:function(t,e){var s=m.get(t);s&&s.slice().map(function(i){i(e)}),(s=m.get("*"))&&s.slice().map(function(i){i(t,e)})}}}class L{constructor(t,e,s,i=null){if(!t||!t.isInitialized)throw new Error("Initialized AudioEngine is required");if(!s)throw new Error("Parsed MIDI data is required");this.audioEngine=t,this.instrumentMap=e||{},this.parsedData=s,this._isPlaying=!1,this._currentTime=0,this._totalDuration=0,this.playbackSpeed=1,this.partChannels=new Map,this.partOutputs=new Map,this.playbackStartTime=0,this.lookAheadTime=.05,this.scheduleInterval=null,this.partNotePointers=new Map,this.partProgramPointers=new Map,this.eventBus=$(),this.beatMapper=new O,this.beats=[],this._setupPartChannels(),this._calculateTotalDuration(),this._resetNotePointers(),this._resetProgramPointers(),i?this.beats=this.beatMapper.mapBeats(s,i):this.beats=this._generateSimpleBeatMapping()}play(){this._isPlaying||(this._isPlaying=!0,this.playbackStartTime=this.audioEngine.audioContext.currentTime-this._currentTime/this.playbackSpeed,this._resetNotePointers(),this._resetProgramPointers(),this._schedulePlayback(),this._startTimeUpdateLoop())}playAt(t){this._isPlaying||(this._isPlaying=!0,this.playbackStartTime=t-this._currentTime/this.playbackSpeed,this._resetNotePointers(),this._resetProgramPointers(),this._schedulePlayback(),this._startTimeUpdateLoop())}pause(){this._isPlaying&&(this._isPlaying=!1,this._stopScheduling(),this._stopTimeUpdateLoop())}stop(){this._isPlaying=!1,this._currentTime=0,this._stopScheduling(),this._stopTimeUpdateLoop(),this._resetNotePointers(),this._resetProgramPointers()}skipToTime(t){t=Math.max(0,Math.min(t,this._totalDuration));const e=this._isPlaying;e&&this.pause(),this._currentTime=t,this._resetNotePointers(),this._resetProgramPointers(),e&&this.play()}setPlaybackSpeed(t){if(t<=0)throw new Error("Playback speed must be greater than 0");const e=this._isPlaying;e&&this.pause(),this.playbackSpeed=t,e&&this.play()}setBar(t,e=0){const s=this.getTimeFromBar(t,e);s!==null&&(this.skipToTime(s),this._emitEvent("barChanged",{bar:t,beat:1,repeat:e,time:s}))}getTimeFromBar(t,e=0){const s=this.beats.find(i=>i.bar===t&&i.beat===1&&i.repeat===e);return s?s.time:null}getBarFromTime(t){if(!this.beats.length)return null;let e=null;for(let s=this.beats.length-1;s>=0;s--)if(this.beats[s].time<=t){e=this.beats[s];break}return e}allSoundsOff(){this.audioEngine.allSoundsOff()}getPartOutput(t){return this.partOutputs.get(t)||null}getPartChannel(t){return this.partChannels.get(t)||null}getCurrentTime(){if(this._isPlaying){const t=(this.audioEngine.audioContext.currentTime-this.playbackStartTime)*this.playbackSpeed;this._currentTime=Math.min(t,this._totalDuration)}return this._currentTime}getTotalDuration(){return this._totalDuration}isPlaying(){return this._isPlaying}on(t,e){this.eventBus.on(t,e)}off(t,e){this.eventBus.off(t,e)}_setupPartChannels(){Object.keys(this.parsedData.parts).forEach(t=>{const e=this.parsedData.parts[t],s=this.instrumentMap[t]||{},i=s.instrument!==void 0?s.instrument:e.defaultInstrument!==void 0?e.defaultInstrument:0;try{const r=this.audioEngine.createChannel(t,{instrument:i,initialVolume:s.volume||1});this.partChannels.set(t,r);const n=this.audioEngine.audioContext.createGain();n.gain.value=1;const l=r.getOutputNode();l&&l.connect(n),this.partOutputs.set(t,n)}catch(r){console.error(`Failed to create channel for part '${t}':`,r),this._emitEvent("error",r)}})}_calculateTotalDuration(){let t=0;Object.values(this.parsedData.parts).forEach(e=>{e.notes.forEach(s=>{s.endTime>t&&(t=s.endTime)})}),this._totalDuration=t}_schedulePlayback(){this._stopScheduling(),this._startScheduleLoop()}_startScheduleLoop(){this.scheduleInterval||(this.scheduleInterval=setInterval(()=>{if(!this._isPlaying)return;const e=(this.audioEngine.audioContext.currentTime-this.playbackStartTime)*this.playbackSpeed,s=e+this.lookAheadTime;for(const[i,r]of this.partChannels){const n=this.parsedData.parts[i];if(n){if(n.programChanges&&n.programChanges.length>0){let l=this.partProgramPointers.get(i)||0;const o=n.programChanges;for(;l<o.length&&o[l].time<e;)l++;for(;l<o.length&&o[l].time<=s;){const a=o[l];r.setInstrument(a.programNumber),l++}this.partProgramPointers.set(i,l)}if(n.notes){let l=this.partNotePointers.get(i)||0;const o=n.notes;for(;l<o.length&&o[l].endTime<e;)l++;for(;l<o.length&&o[l].startTime<=s;){const a=o[l];if(a.endTime-a.startTime>=.01){const c=this.playbackStartTime+a.startTime/this.playbackSpeed,h=(a.endTime-a.startTime)/this.playbackSpeed;r.playNote(c,a.pitch,a.velocity,h)}l++}this.partNotePointers.set(i,l)}}}},50))}_resetNotePointers(){const t=this._currentTime;for(const[e]of this.partChannels){const s=this.parsedData.parts[e];if(!s||!s.notes)continue;let i=0;for(;i<s.notes.length&&s.notes[i].endTime<t;)i++;this.partNotePointers.set(e,i)}}_resetProgramPointers(){const t=this._currentTime;for(const[e,s]of this.partChannels){const i=this.parsedData.parts[e];if(!i||!i.programChanges){this.partProgramPointers.set(e,0);continue}let r=0,n=i.defaultInstrument;for(;r<i.programChanges.length&&i.programChanges[r].time<=t;)n=i.programChanges[r].programNumber,r++;s.setInstrument(n),this.partProgramPointers.set(e,r)}}_stopScheduling(){this.scheduleInterval&&(clearInterval(this.scheduleInterval),this.scheduleInterval=null),this.partChannels.forEach(t=>{t.isActive()&&t.allNotesOff()})}_startTimeUpdateLoop(){this.timeUpdateInterval=setInterval(()=>{const t=this.getCurrentTime();this._emitEvent("timeupdate",{currentTime:t});const e=this.getBarFromTime(t);e&&this._emitEvent("barChanged",e),t>=this._totalDuration&&(this.stop(),this._emitEvent("ended",{finalTime:t}))},100)}_stopTimeUpdateLoop(){this.timeUpdateInterval&&(clearInterval(this.timeUpdateInterval),this.timeUpdateInterval=null)}_emitEvent(t,e){(this.eventBus.all.get(t)||[]).forEach(i=>{try{i(e)}catch(r){console.error(`Error in ${t} event listener:`,r)}})}_generateSimpleBeatMapping(){const t=[],e=this.parsedData.tempoChanges||[],s=120,i=this.parsedData.barStructure||[],r=i[0]||{sig:[4,4],bpm:s},n=r.sig[0],a=60/(Array.isArray(r.bpm)?r.bpm[0]:r.bpm)*n;if(e.length===0){const d=Math.max(1,Math.ceil(this._totalDuration/a));let f=0;for(let u=1;u<=d;u++){const p=Math.max(0,u-1),_=i[p]||r,g=_.sig[0],y=Array.isArray(_.bpm)?_.bpm[0]:_.bpm,w=60/y;for(let b=1;b<=g;b++)t.push({bar:u,beat:b,repeat:0,tempo:y,time:f,timeSig:g}),f+=w}return t}let c=0,h=1;for(let d=0;d<e.length;d++){const f=e[d],u=e[d+1],p=u?u.time:this._totalDuration,_=f.tempo||s,g=60/_;for(;c<p;){for(let y=1;y<=n&&c<p;y++)t.push({bar:h,beat:y,repeat:0,tempo:_,time:c,timeSig:n}),c+=g;h++}}return t}destroy(){this.stop(),this.partChannels.forEach(t=>{t.destroy()}),this.partChannels.clear(),this.partOutputs.forEach(t=>{t.disconnect()}),this.partOutputs.clear(),this.partNotePointers.clear(),this.partProgramPointers.clear(),this.eventBus.all.clear()}}const P=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));exports.AudioEngine=k;exports.AudioEngineUtils=x;exports.BeatMapper=O;exports.ChannelHandle=v;exports.MidiParser=F;exports.MidiPlayer=L;exports.SpessaSynthAudioEngine=z;exports.SpessaSynthChannelHandle=S;
1
+ "use strict";var P=Object.create;var _=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var D=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var z=(d,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of O(t))!N.call(d,s)&&s!==e&&_(d,s,{get:()=>t[s],enumerable:!(i=E(t,s))||i.enumerable});return d};var L=(d,t,e)=>(e=d!=null?P(D(d)):{},z(t||!d||!d.__esModule?_(e,"default",{value:d,enumerable:!0}):e,d));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class g{constructor(t,e={}){if(new.target===g)throw new Error("AudioEngine is abstract and cannot be instantiated directly");this.audioContext=t,this.options=e,this.isInitialized=!1,this.channels=new WeakMap,this.activeChannels=new Set}async initialize(t){throw new Error("initialize() must be implemented by subclass")}createChannel(t,e={}){throw new Error("createChannel() must be implemented by subclass")}allSoundsOff(){throw new Error("allSoundsOff() must be implemented by subclass")}async playMetronomeTick(t,e,i){try{await this._ensureMetronomeBuffersLoaded();const s=e?this.accentTickBuffer:this.regularTickBuffer;if(!s){console.warn("Metronome buffer not available");return}const n=this.audioContext.createBufferSource();n.buffer=s;const a=this.audioContext.createGain();a.gain.value=i,n.connect(a);const r=this.getMetronomeOutput();r?a.connect(r):a.connect(this.audioContext.destination);const o=Math.max(t,this.audioContext.currentTime);n.start(o)}catch(s){console.warn("Buffer metronome playback failed:",s)}}getMetronomeOutput(){return this._metronomeOutput||(this._metronomeOutput=this.audioContext.createGain(),this._metronomeOutput.gain.value=1,this._metronomeOutput.connect(this.audioContext.destination)),this._metronomeOutput}async _ensureMetronomeBuffersLoaded(){if(!(this.regularTickBuffer&&this.accentTickBuffer)){try{if(typeof fetch<"u"){const[t,e]=await Promise.all([fetch("sounds/click-regular.mp3"),fetch("sounds/click-accent.mp3")]),[i,s]=await Promise.all([t.arrayBuffer(),e.arrayBuffer()]),[n,a]=await Promise.all([this.audioContext.decodeAudioData(i),this.audioContext.decodeAudioData(s)]);this.regularTickBuffer=n,this.accentTickBuffer=a;return}}catch(t){console.warn("Failed to load metronome sounds:",t)}this.regularTickBuffer=this.audioContext.createBuffer(2,1024,this.audioContext.sampleRate),this.accentTickBuffer=this.audioContext.createBuffer(2,1024,this.audioContext.sampleRate)}}getActiveChannels(){return Array.from(this.activeChannels)}destroy(){this.allSoundsOff(),this._metronomeOutput&&(this._metronomeOutput.disconnect(),this._metronomeOutput=null),this.regularTickBuffer=null,this.accentTickBuffer=null,this.activeChannels.clear(),this.isInitialized=!1}_validateInitialized(){if(!this.isInitialized)throw new Error("AudioEngine not initialized. Call initialize() first.")}_registerChannel(t){this.activeChannels.add(t)}_unregisterChannel(t){this.activeChannels.delete(t),this.channels.delete(t)}}class b{constructor(t,e,i={}){if(new.target===b)throw new Error("ChannelHandle is abstract and cannot be instantiated directly");this.engine=t,this.partId=e,this.options={initialVolume:1,...i},this.isDestroyed=!1,this.noteRefCounts=new Map,this.scheduledEvents=new Map,this.activeNotes=new Set}getOutputNode(){throw new Error("getOutputNode() must be implemented by subclass")}noteOn(t,e){this._validateActive();const i=this.noteRefCounts.get(t)||0;this.noteRefCounts.set(t,i+1),this._actualNoteOn(t,e),i===0&&this.activeNotes.add(t)}noteOff(t){this._validateActive();const e=this.noteRefCounts.get(t)||0;if(e<=0)return;const i=e-1;this.noteRefCounts.set(t,i),i===0&&(this._actualNoteOff(t),this.activeNotes.delete(t),this.noteRefCounts.delete(t))}playNote(t,e,i,s){this._validateActive();const n=this.engine.audioContext.currentTime,a=`${this.partId}_${t}_${e}_${Date.now()}`;let r=t,o=s;if(t<n){const A=n-t;r=n,o=Math.max(0,s-A)}if(o<=0)return a;const q=Math.max(0,(r-n)*1e3),l=setTimeout(()=>{this.noteOn(e,i),this.scheduledEvents.delete(`${a}_on`)},q),u=q+o*1e3,c=setTimeout(()=>{this.noteOff(e),this.scheduledEvents.delete(`${a}_off`)},u);return this.scheduledEvents.set(`${a}_on`,l),this.scheduledEvents.set(`${a}_off`,c),a}allNotesOff(){this._validateActive(),this.scheduledEvents.forEach(t=>{clearTimeout(t)}),this.scheduledEvents.clear(),this.activeNotes.forEach(t=>{this._actualNoteOff(t)}),this.noteRefCounts.clear(),this.activeNotes.clear()}_actualNoteOn(t,e){throw new Error("_actualNoteOn() must be implemented by subclass")}_actualNoteOff(t){throw new Error("_actualNoteOff() must be implemented by subclass")}async setInstrument(t){throw new Error("setInstrument() must be implemented by subclass")}getInstrument(){throw new Error("getInstrument() must be implemented by subclass")}setVolume(t){throw new Error("setVolume() must be implemented by subclass")}getVolume(){throw new Error("getVolume() must be implemented by subclass")}getPartId(){return this.partId}isActive(){return!this.isDestroyed&&this.engine.isInitialized&&this.engine.activeChannels.has(this)}destroy(){if(!this.isDestroyed){this.allNotesOff();const t=this.getOutputNode();t&&t.disconnect(),this.noteRefCounts.clear(),this.scheduledEvents.clear(),this.activeNotes.clear(),this.engine._unregisterChannel(this),this.isDestroyed=!0}}_validateActive(){if(this.isDestroyed)throw new Error("Channel has been destroyed");if(!this.engine.isInitialized)throw new Error("AudioEngine is not initialized")}}class x{static getInstrumentProgram(t){if(typeof t=="number")return t;const i={piano:0,bright_piano:1,electric_grand:2,honky_tonk:3,electric_piano_1:4,electric_piano_2:5,harpsichord:6,clavinet:7,celesta:8,glockenspiel:9,music_box:10,vibraphone:11,marimba:12,xylophone:13,tubular_bells:14,dulcimer:15,drawbar_organ:16,percussive_organ:17,rock_organ:18,church_organ:19,reed_organ:20,accordion:21,harmonica:22,tango_accordion:23,organ:19,nylon_guitar:24,steel_guitar:25,electric_guitar_jazz:26,electric_guitar_clean:27,electric_guitar_muted:28,overdriven_guitar:29,distortion_guitar:30,guitar_harmonics:31,guitar:24,acoustic_bass:32,electric_bass_finger:33,electric_bass_pick:34,fretless_bass:35,slap_bass_1:36,slap_bass_2:37,synth_bass_1:38,synth_bass_2:39,bass:32,violin:40,viola:41,cello:42,contrabass:43,tremolo_strings:44,pizzicato_strings:45,orchestral_harp:46,timpani:47,strings:48,strings_ensemble:48,slow_strings:49,synth_strings_1:50,synth_strings_2:51,choir_aahs:52,voice_oohs:53,synth_voice:54,orchestra_hit:55,trumpet:56,trombone:57,tuba:58,muted_trumpet:59,french_horn:60,brass_section:61,synth_brass_1:62,synth_brass_2:63,soprano_sax:64,alto_sax:65,tenor_sax:66,baritone_sax:67,oboe:68,english_horn:69,bassoon:70,clarinet:71,saxophone:64,piccolo:72,flute:73,recorder:74,pan_flute:75,blown_bottle:76,shakuhachi:77,whistle:78,ocarina:79,lead_1_square:80,lead_2_sawtooth:81,lead_3_calliope:82,lead_4_chiff:83,lead_5_charang:84,lead_6_voice:85,lead_7_fifths:86,lead_8_bass:87,pad_1_new_age:88,pad_2_warm:89,pad_3_polysynth:90,pad_4_choir:91,pad_5_bowed:92,pad_6_metallic:93,pad_7_halo:94,pad_8_sweep:95,fx_1_rain:96,fx_2_soundtrack:97,fx_3_crystal:98,fx_4_atmosphere:99,fx_5_brightness:100,fx_6_goblins:101,fx_7_echoes:102,fx_8_sci_fi:103,sitar:104,banjo:105,shamisen:106,koto:107,kalimba:108,bag_pipe:109,fiddle:110,shanai:111,tinkle_bell:112,agogo:113,steel_drums:114,woodblock:115,taiko_drum:116,melodic_tom:117,synth_drum:118,reverse_cymbal:119,guitar_fret_noise:120,breath_noise:121,seashore:122,bird_tweet:123,telephone_ring:124,helicopter:125,applause:126,gunshot:127}[t.toLowerCase()];return i!==void 0?i:0}static getProgramName(t){return["Piano","Bright Piano","Electric Grand","Honky-tonk Piano","Electric Piano 1","Electric Piano 2","Harpsichord","Clavinet","Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer","Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accordion","Harmonica","Tango Accordion","Nylon Guitar","Steel Guitar","Electric Guitar (jazz)","Electric Guitar (clean)","Electric Guitar (muted)","Overdriven Guitar","Distortion Guitar","Guitar Harmonics","Acoustic Bass","Electric Bass (finger)","Electric Bass (pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Harp","Timpani","String Ensemble 1","String Ensemble 2","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet","Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Shakuhachi","Whistle","Ocarina","Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass + lead)","Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)","FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag pipe","Fiddle","Shanai","Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal","Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"][t]||`Program ${t}`}static generateNoteId(t,e,i){return`${t}_${e}_${Math.round(i)}`}}class w extends b{constructor(t,e,i,s={}){super(t,e,s),this.midiChannel=i,this.currentVolume=s.initialVolume||1,this.currentInstrument=s.instrument||"piano",this.outputGain=null,this._setupOutputNode(),this.setVolume(this.currentVolume),s.instrument&&this.setInstrument(s.instrument)}getOutputNode(){return this.outputGain}_actualNoteOn(t,e){const i=this.engine._getSynthesizer();if(i&&i.noteOn){const s=Math.round(e*this.currentVolume);i.noteOn(this.midiChannel,t,s)}}_actualNoteOff(t){const e=this.engine._getSynthesizer();e&&e.noteOff&&e.noteOff(this.midiChannel,t)}async setInstrument(t){this._validateActive();const e=x.getInstrumentProgram(t);this.currentInstrument=t;const i=this.engine._getSynthesizer();i&&i.programChange?i.programChange(this.midiChannel,e):console.warn("Cannot set instrument: synthesizer not available or no programChange method")}getInstrument(){return this.currentInstrument}setVolume(t){this._validateActive(),t=Math.max(0,Math.min(1,t)),this.currentVolume=t;const e=Math.round(t*127),i=this.engine._getSynthesizer();i&&i.controllerChange&&i.controllerChange(this.midiChannel,7,e)}getVolume(){return this.currentVolume}getMidiChannel(){return this.midiChannel}getActiveNoteCount(){return this.activeNotes.size}destroy(){if(!this.isDestroyed){const t=this.engine._getIndividualOutput(this.midiChannel);this.outputGain&&this.outputGain!==t&&this.outputGain.disconnect(),this.outputGain=null}super.destroy()}_setupOutputNode(){const t=this.engine._getIndividualOutput(this.midiChannel);t?this.outputGain=t:(console.warn(`No individual output available for MIDI channel ${this.midiChannel}, using fallback`),this.outputGain=this.engine.audioContext.createGain(),this.outputGain.gain.value=this.currentVolume)}}class F extends g{constructor(t,e={}){super(t,e),this.synthesizer=null,this.soundfont=null,this.channelCounter=0,this.partToMidiChannel=new Map,this.midiChannelToPart=new Map,this.individualOutputs=[]}async initialize(t){try{const{WorkletSynthesizer:e}=await import("spessasynth_lib");let i;if(typeof t=="string")i=await this._loadSoundfontFromPath(t);else if(t instanceof ArrayBuffer)i=t;else throw new Error("Invalid soundfont data type. Expected string path or ArrayBuffer.");await this._loadAudioWorkletSafely(),this._setupIndividualOutputs(),this.dummyTarget=this.audioContext.createGain(),await new Promise(s=>setTimeout(s,50)),this.synthesizer=new e(this.audioContext),await this.synthesizer.soundBankManager.addSoundBank(i,"main"),await this.synthesizer.isReady,this._connectIndividualOutputs(),this._initializeMetronomeChannel(),this.isInitialized=!0}catch(e){throw console.error("Failed to initialize SpessaSynthAudioEngine:",e),e}}createChannel(t,e={}){if(this._validateInitialized(),this.partToMidiChannel.has(t))throw new Error(`Channel for part '${t}' already exists`);const i=this.channelCounter;if(i>=15)throw new Error("Maximum number of musical part channels (15) exceeded. Channel 15 is reserved for metronome.");this.channelCounter++,this.partToMidiChannel.set(t,i),this.midiChannelToPart.set(i,t);const s=new w(this,t,i,e);return this._registerChannel(s),e.instrument&&s.setInstrument(e.instrument),s}allSoundsOff(){this.synthesizer&&this.midiChannelToPart.forEach((t,e)=>{this.synthesizer.controllerChange&&this.synthesizer.controllerChange(e,120,0)})}clearAllChannels(){this.allSoundsOff(),this.partToMidiChannel.clear(),this.midiChannelToPart.clear(),this.channelCounter=0}destroy(){this.allSoundsOff(),this.synthesizer&&typeof this.synthesizer.disconnect=="function"&&this.synthesizer.disconnect(),this.individualOutputs.forEach(t=>{t&&t.disconnect&&t.disconnect()}),this.individualOutputs=[],this.dummyTarget&&(this.dummyTarget.disconnect(),this.dummyTarget=null),this.partToMidiChannel.clear(),this.midiChannelToPart.clear(),this.channelCounter=0,super.destroy(),this.synthesizer=null,this.soundfont=null}getMidiChannelForPart(t){return this.partToMidiChannel.has(t)?this.partToMidiChannel.get(t):null}_getSynthesizer(){return this.synthesizer}_getIndividualOutput(t){return t>=0&&t<this.individualOutputs.length?this.individualOutputs[t]:null}getMetronomeChannel(){const t=this._getIndividualOutput(15);return console.log("Metronome channel 15 output:",t?"Available":"NULL",`(total outputs: ${this.individualOutputs.length})`),t}_setupIndividualOutputs(){this.individualOutputs=[];for(let t=0;t<16;t++){const e=this.audioContext.createGain();e.gain.value=1,this.individualOutputs.push(e)}}_connectIndividualOutputs(){try{this.synthesizer&&this.synthesizer.connectIndividualOutputs?this.synthesizer.connectIndividualOutputs(this.individualOutputs):(console.warn("Synthesizer does not support individual outputs, using master output only"),this.synthesizer&&this.synthesizer.connect&&this.audioContext.destination&&this.synthesizer.connect(this.audioContext.destination))}catch(t){console.warn("Failed to connect individual outputs:",t.message),console.warn("Falling back to master output routing")}}async _loadSoundfontFromPath(t){if(typeof window<"u"){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load soundfont: ${e.status} ${e.statusText}`);return await e.arrayBuffer()}else{const e=await Promise.resolve().then(()=>C),i=await Promise.resolve().then(()=>C),s=i.isAbsolute(t)?t:i.resolve(process.cwd(),t);return e.readFileSync(s).buffer}}async _loadAudioWorkletSafely(){const t="/node_modules/spessasynth_lib/dist/spessasynth_processor.min.js";for(let i=1;i<=5;i++)try{await this.audioContext.audioWorklet.addModule(t);return}catch(s){if(console.warn(`AudioWorklet loading failed (attempt ${i}/5):`,s.message),i===5)throw new Error(`AudioWorklet failed after 5 attempts: ${s.message}`);const n=i*500;await new Promise(a=>setTimeout(a,n))}}_initializeMetronomeChannel(){try{const t=this._getSynthesizer();if(!t){console.warn("Cannot initialize metronome channel: synthesizer not available");return}const e=15;t.programChange&&(t.programChange(e,115),console.log("Metronome channel 15 initialized with woodblock instrument (115)")),t.controllerChange&&t.controllerChange(e,7,127)}catch(t){console.warn("Failed to initialize metronome channel:",t)}}async playMetronomeTick(t,e,i){try{const s=this.getMetronomeChannel(),n=this._getSynthesizer();if(!s||!n)return super.playMetronomeTick(t,e,i);const a=15,r=e?86:60,q=Math.round(Math.min(127,Math.max(0,i*(e?127:100)))),l=this.audioContext.currentTime,c=Math.max(t,l)-l;c<=.01?(n.noteOn&&n.noteOn(a,r,q),setTimeout(()=>{n.noteOff&&n.noteOff(a,r)},100)):setTimeout(()=>{n.noteOn&&n.noteOn(a,r,q),setTimeout(()=>{n.noteOff&&n.noteOff(a,r)},100)},c*1e3)}catch(s){return console.warn("MIDI metronome failed, falling back to buffers:",s),super.playMetronomeTick(t,e,i)}}getMetronomeOutput(){return!this.individualOutputs||this.individualOutputs.length<16?null:this.individualOutputs[15]}}class U{constructor(){this.partNames=["soprano","alto","tenor","bass","treble","mezzo","baritone","s","a","t","b","satb"],this.parsedData={parts:{},barStructure:[],metadata:{}}}async parse(t){try{const e=await this._parseMidiBuffer(t);return this._extractMetadata(e),this._extractBarStructure(e),this._extractParts(e),this.parsedData}catch(e){throw console.error("Error parsing MIDI file:",e),e}}async _parseMidiBuffer(t){const e=new Uint8Array(t);if(!(e[0]===77&&e[1]===84&&e[2]===104&&e[3]===100))throw new Error("Not a valid MIDI file");const i=this._bytesToNumber(e.slice(4,8)),s=this._bytesToNumber(e.slice(8,10)),n=this._bytesToNumber(e.slice(10,12)),a=this._bytesToNumber(e.slice(12,14)),r=a&32768?null:a,o={format:s,ticksPerBeat:r,tracks:[],duration:0};let q=8+i;for(let l=0;l<n;l++)if(e[q]===77&&e[q+1]===84&&e[q+2]===114&&e[q+3]===107){const u=this._bytesToNumber(e.slice(q+4,q+8)),c=e.slice(q+8,q+8+u),A=this._parseTrack(c);o.tracks.push(A),q+=8+u}else throw new Error(`Invalid track header at position ${q}`);return o}_parseTrack(t){const e={notes:[],name:null,lyrics:[],events:[],duration:0};let i=0,s=0,n=null;for(;i<t.length;){let a=0,r=0;do r=t[i++],a=a<<7|r&127;while(r&128);s+=a,r=t[i++];let o=r;if((r&128)===0){if(n===null)throw new Error("Running status byte encountered before status byte");o=n,i--}else n=o;if(o===255){const q=t[i++],l=this._readVariableLengthValue(t,i);i+=l.bytesRead;const u=t.slice(i,i+l.value);switch(i+=l.value,q){case 3:e.name=this._bytesToString(u);break;case 1:e.events.push({type:"text",text:this._bytesToString(u),tick:s});break;case 5:e.lyrics.push({text:this._bytesToString(u),tick:s});break;case 81:const c=this._bytesToNumber(u),A=Math.round(6e7/c);e.events.push({type:"tempo",bpm:A,tick:s});break;case 88:e.events.push({type:"timeSignature",numerator:u[0],denominator:Math.pow(2,u[1]),tick:s});break;case 47:e.duration=s;break}}else if((o&240)===144){const q=o&15,l=t[i++],u=t[i++];u>0?e.notes.push({type:"noteOn",noteNumber:l,velocity:u,tick:s,channel:q}):e.notes.push({type:"noteOff",noteNumber:l,tick:s,channel:q})}else if((o&240)===128){const q=o&15,l=t[i++];t[i++],e.notes.push({type:"noteOff",noteNumber:l,tick:s,channel:q})}else if(o===240||o===247){const q=this._readVariableLengthValue(t,i);i+=q.bytesRead+q.value}else if((o&240)===176){const q=o&15,l=t[i++],u=t[i++];e.events.push({type:"controller",controllerNumber:l,value:u,channel:q,tick:s})}else if((o&240)===192){const q=o&15,l=t[i++];e.events.push({type:"programChange",programNumber:l,channel:q,tick:s})}else if((o&240)===208){const q=o&15,l=t[i++];e.events.push({type:"channelAftertouch",pressure:l,channel:q,tick:s})}else if((o&240)===224){const q=o&15,l=t[i++],c=(t[i++]<<7|l)-8192;e.events.push({type:"pitchBend",value:c,channel:q,tick:s})}else if((o&240)===160){const q=o&15,l=t[i++],u=t[i++];e.events.push({type:"noteAftertouch",noteNumber:l,pressure:u,channel:q,tick:s})}else console.warn(`Unknown event type: ${o.toString(16)} at position ${i-1}`),i++}return e}_extractMetadata(t){const e={title:null,composer:null,partNames:[],format:t.format,ticksPerBeat:t.ticksPerBeat};t.tracks.forEach((i,s)=>{if(i.name&&!e.title&&(e.title=i.name),i.events.filter(n=>n.type==="text").forEach(n=>{const a=n.text.toLowerCase();(a.includes("compos")||a.includes("by"))&&!e.composer&&(e.composer=n.text)}),i.name){const n=i.name.toLowerCase();for(const a of this.partNames)if(n.includes(a)){e.partNames.push({index:s,name:i.name});break}}}),this.parsedData.metadata=e}_extractBarStructure(t){const e=t.ticksPerBeat||480,i=[];t.tracks.forEach(l=>{l.events.forEach(u=>{(u.type==="timeSignature"||u.type==="tempo")&&i.push(u)})}),i.sort((l,u)=>l.tick-u.tick);let s=0;t.tracks.forEach(l=>{l.notes&&l.notes.forEach(u=>{u.type==="noteOff"&&u.tick>s&&(s=u.tick)})}),s===0&&(s=e*8);const n=[],a=i.filter(l=>l.type==="timeSignature").sort((l,u)=>l.tick-u.tick);let r={numerator:4,denominator:4},o=0,q=0;for(;o<s;){for(;q<a.length&&a[q].tick<=o;)r=a[q],q++;let l;l=o+e*4*r.numerator/r.denominator,i.filter(h=>h.type==="tempo"&&h.tick>=o&&h.tick<l);const u=r.numerator,c=[],A=e*(4/r.denominator);for(let h=0;h<u;h++){const m=o+h*A,f=this._ticksToTime(m,t);c.push(f)}n.push({sig:[r.numerator,r.denominator],beats:c}),o=l}this.parsedData.barStructure=n}_extractParts(t){const e={},i=t.ticksPerBeat;t.tracks.forEach((s,n)=>{if(!s.notes.length)return;let a=null;if(s.name){const h=s.name.toLowerCase();for(const m of this.partNames)if(m.length===1){if(h===m){a=m;break}}else if(h.includes(m)){a=m;break}}a||(a=s.name||`Track ${n+1}`),a==="s"&&(a="soprano"),a==="a"&&(a="alto"),a==="t"&&(a="tenor"),a==="b"&&(a="bass");let r=a,o=2;for(;e[r];)r=`${a} ${o}`,o++;a=r;const q=[],l={};s.notes.forEach(h=>{if(h.type==="noteOn")l[h.noteNumber]={tick:h.tick,velocity:h.velocity};else if(h.type==="noteOff"&&l[h.noteNumber]){const m=l[h.noteNumber],f=h.tick-m.tick;q.push({pitch:h.noteNumber,name:this._midiNoteToName(h.noteNumber),startTick:m.tick,endTick:h.tick,duration:f,startTime:this._ticksToTime(m.tick,t),endTime:this._ticksToTime(h.tick,t),velocity:m.velocity}),delete l[h.noteNumber]}});const u=s.lyrics.map(h=>({text:h.text,tick:h.tick,time:h.tick/i}));q.sort((h,m)=>h.startTick-m.startTick);const c=s.events.filter(h=>h.type==="programChange").map(h=>({programNumber:h.programNumber,tick:h.tick,time:this._ticksToTime(h.tick,t)})).sort((h,m)=>h.tick-m.tick),A=c.length>0?c[0].programNumber:0;e[a]={notes:q,lyrics:u,trackIndex:n,programChanges:c,defaultInstrument:A}}),this.parsedData.parts=e}_midiNoteToName(t){const e=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],i=Math.floor(t/12)-1;return`${e[t%12]}${i}`}_bytesToNumber(t){let e=0;for(let i=0;i<t.length;i++)e=e<<8|t[i];return e}_bytesToString(t){return new TextDecoder().decode(t)}_readVariableLengthValue(t,e){let i=0,s,n=0;do s=t[e+n++],i=i<<7|s&127;while(s&128);return{value:i,bytesRead:n}}_ticksToTime(t,e){const i=e.ticksPerBeat||480,s=[];e.tracks.forEach(o=>{o.events.forEach(q=>{q.type==="tempo"&&s.push(q)})}),s.sort((o,q)=>o.tick-q.tick);let n=0,a=0,r=120;for(const o of s){if(o.tick>t)break;if(o.tick>a){const l=(o.tick-a)/i*(60/r);n+=l,a=o.tick}r=o.bpm}if(t>a){const q=(t-a)/i*(60/r);n+=q}return n}}class B{constructor(){this.barOrder=[],this.beatTable=[]}mapBeats(t,e){try{return this.barOrder=this.generateBarOrder(e.sections,e.order),this.beatTable=this.generateBeatTable(this.barOrder,t.barStructure),this.beatTable}catch(i){throw console.error("Error mapping beats:",i),i}}generateBarOrder(t,e){const i=[],s={};for(const n of e){const a=t[n.section];if(!a)throw new Error(`Invalid section index: ${n.section}`);const r=n.section;s[r]||(s[r]=0),s[r]++;const o=s[r],q=n.from!==void 0?n.from:this._getSectionStartBar(t,n.section),l=n.to!==void 0?n.to:a.to,u=n.as||1;for(let c=q;c<=l;c++)this._shouldPlayBar(a,c,u)&&i.push({barNumber:c,repeat:o,sectionIndex:n.section,voltaTime:u})}return i}generateBeatTable(t,e){return this._needsComplexAlgorithm(t,e)?this._generateBeatTableComplex(t,e):this._generateBeatTableSimple(t,e)}_needsComplexAlgorithm(t,e){const i=t.map(r=>r.barNumber),n=new Set(i).size<i.length,a=Math.abs(t.length-e.length)>1;return n||a}_generateBeatTableSimple(t,e){const i=[],s=e[0]&&e[0].sig&&e[0].sig[0]===1,n=t.length>0&&t[0].barNumber===0,a=s&&n;for(let r=0;r<t.length;r++){const o=t[r];let q;if(a?q=e[o.barNumber]:q=e[r+(s&&!n?1:0)],!q)throw new Error(`No MIDI bar data for bar ${o.barNumber}`);this._generateBeatsForBar(i,o,q,q.sig[0])}return i}_generateBeatTableComplex(t,e){var l;const i=[],s={};let n=0,a=0;const r=[...e],o=r[0]&&r[0].sig&&r[0].sig[0]===1,q=((l=t[0])==null?void 0:l.barNumber)||0;for(o&&q>0&&(n=q);a<t.length&&n<r.length;){const u=t[a],c=u.barNumber;if(s[c]===void 0){const f=r[n];if(!f||!f.sig)throw new Error(`Invalid MIDI bar structure at index ${n}`);s[c]=f.sig[0]}const A=s[c];let h=r[n],m=h.sig[0];for(;m<A&&n+1<r.length;){const f=r[n+1],p=[h.sig[0]+f.sig[0],h.sig[1]],y=[...h.beats||[],...f.beats||[]];h={sig:p,beats:y},r[n]=h,r.splice(n+1,1),m=p[0]}if(m>A){const f=A,p=m-A,y=h.beats?h.beats.slice(0,f):[],S=h.beats?h.beats.slice(f):[],T={sig:[f,h.sig[1]],beats:y},M={sig:[p,h.sig[1]],beats:S};r[n]=T,r.splice(n+1,0,M),h=T}this._generateBeatsForBar(i,u,h,A),n++,a++}if(a<t.length)throw new Error(`Ran out of MIDI bars before completing score. Score bar ${a}/${t.length}, MIDI bar ${n}/${r.length}`);return i}_generateBeatsForBar(t,e,i,s){const{beats:n}=i;if(!n||!Array.isArray(n))throw new Error(`Invalid MIDI bar: missing beats array. Got: ${JSON.stringify(i)}`);const a=n.slice(0,s);for(let r=1;r<=s;r++){const q={time:a[r-1],repeat:e.repeat,bar:e.barNumber,beat:r,timeSig:s};t.push(q)}}_getSectionStartBar(t,e){return t[e].pickup!==void 0?0:e===0?1:t[e-1].to+1}_shouldPlayBar(t,e,i){if(!t.voltas)return!0;const s=t.voltas.indexOf(e);return s===-1?!0:s+1===i}}function v(d){return{all:d=d||new Map,on:function(t,e){var i=d.get(t);i?i.push(e):d.set(t,[e])},off:function(t,e){var i=d.get(t);i&&(e?i.splice(i.indexOf(e)>>>0,1):d.set(t,[]))},emit:function(t,e){var i=d.get(t);i&&i.slice().map(function(s){s(e)}),(i=d.get("*"))&&i.slice().map(function(s){s(t,e)})}}}class G{constructor(t,e,i,s=null){if(!t||!t.isInitialized)throw new Error("Initialized AudioEngine is required");if(!i)throw new Error("Parsed MIDI data is required");this.audioEngine=t,this.instrumentMap=e||{},this.parsedData=i,this._isPlaying=!1,this._currentTime=0,this._totalDuration=0,this.playbackSpeed=1,this.partChannels=new Map,this.partOutputs=new Map,this.playbackStartTime=0,this.lookAheadTime=.05,this.scheduleInterval=null,this.partNotePointers=new Map,this.partProgramPointers=new Map,this.eventBus=v(),this.beatMapper=new B,this.beats=[],this._setupPartChannels(),this._calculateTotalDuration(),this._resetNotePointers(),this._resetProgramPointers();const n=s||this._createDefaultStructureMetadata();this.beats=this.beatMapper.mapBeats(i,n)}play(){this._isPlaying||(this._isPlaying=!0,this.playbackStartTime=this.audioEngine.audioContext.currentTime-this._currentTime/this.playbackSpeed,this._resetNotePointers(),this._resetProgramPointers(),this._schedulePlayback(),this._startTimeUpdateLoop())}playAt(t){this._isPlaying||(this._isPlaying=!0,this.playbackStartTime=t-this._currentTime/this.playbackSpeed,this._resetNotePointers(),this._resetProgramPointers(),this._schedulePlayback(),this._startTimeUpdateLoop())}pause(){this._isPlaying&&(this._isPlaying=!1,this._stopScheduling(),this._stopTimeUpdateLoop())}stop(){this._isPlaying=!1,this._currentTime=0,this._stopScheduling(),this._stopTimeUpdateLoop(),this._resetNotePointers(),this._resetProgramPointers()}skipToTime(t){t=Math.max(0,Math.min(t,this._totalDuration));const e=this._isPlaying;e&&this.pause(),this._currentTime=t,this._resetNotePointers(),this._resetProgramPointers(),e&&this.play()}setPlaybackSpeed(t){if(t<=0)throw new Error("Playback speed must be greater than 0");const e=this._isPlaying;e&&this.pause(),this.playbackSpeed=t,e&&this.play()}setBar(t,e=0){const i=this.getTimeFromBar(t,e);i!==null&&(this.skipToTime(i),this._emitEvent("barChanged",{bar:t,beat:1,repeat:e,time:i}))}getTimeFromBar(t,e=0){const i=e+1,s=this.beats.find(n=>n.bar===t&&n.beat===1&&n.repeat===i);return s?s.time:null}getBeatFromTime(t){if(!this.beats.length)return null;let e=null;for(let i=this.beats.length-1;i>=0;i--)if(this.beats[i].time<=t){e=this.beats[i];break}return e}allSoundsOff(){this.audioEngine.allSoundsOff()}getPartOutput(t){return this.partOutputs.get(t)||null}getPartChannel(t){return this.partChannels.get(t)||null}getCurrentTime(){if(this._isPlaying){const t=(this.audioEngine.audioContext.currentTime-this.playbackStartTime)*this.playbackSpeed;this._currentTime=Math.min(t,this._totalDuration)}return this._currentTime}getTotalDuration(){return this._totalDuration}isPlaying(){return this._isPlaying}on(t,e){this.eventBus.on(t,e)}off(t,e){this.eventBus.off(t,e)}_setupPartChannels(){Object.keys(this.parsedData.parts).forEach(t=>{const e=this.parsedData.parts[t],i=this.instrumentMap[t]||{},s=i.instrument!==void 0?i.instrument:e.defaultInstrument!==void 0?e.defaultInstrument:0;try{const n=this.audioEngine.createChannel(t,{instrument:s,initialVolume:i.volume||1});this.partChannels.set(t,n);const a=this.audioEngine.audioContext.createGain();a.gain.value=1;const r=n.getOutputNode();r&&r.connect(a),this.partOutputs.set(t,a)}catch(n){console.error(`Failed to create channel for part '${t}':`,n),this._emitEvent("error",n)}})}_calculateTotalDuration(){let t=0;Object.values(this.parsedData.parts).forEach(e=>{e.notes.forEach(i=>{i.endTime>t&&(t=i.endTime)})}),this._totalDuration=t}_schedulePlayback(){this._stopScheduling(),this._startScheduleLoop()}_startScheduleLoop(){this.scheduleInterval||(this.scheduleInterval=setInterval(()=>{if(!this._isPlaying)return;const e=(this.audioEngine.audioContext.currentTime-this.playbackStartTime)*this.playbackSpeed,i=e+this.lookAheadTime;for(const[s,n]of this.partChannels){const a=this.parsedData.parts[s];if(a){if(a.programChanges&&a.programChanges.length>0){let r=this.partProgramPointers.get(s)||0;const o=a.programChanges;for(;r<o.length&&o[r].time<e;)r++;for(;r<o.length&&o[r].time<=i;){const q=o[r];n.setInstrument(q.programNumber),r++}this.partProgramPointers.set(s,r)}if(a.notes){let r=this.partNotePointers.get(s)||0;const o=a.notes;for(;r<o.length&&o[r].endTime<e;)r++;for(;r<o.length&&o[r].startTime<=i;){const q=o[r];if(q.endTime-q.startTime>=.01){const l=this.playbackStartTime+q.startTime/this.playbackSpeed,u=(q.endTime-q.startTime)/this.playbackSpeed;n.playNote(l,q.pitch,q.velocity,u)}r++}this.partNotePointers.set(s,r)}}}},50))}_resetNotePointers(){const t=this._currentTime;for(const[e]of this.partChannels){const i=this.parsedData.parts[e];if(!i||!i.notes)continue;let s=0;for(;s<i.notes.length&&i.notes[s].endTime<t;)s++;this.partNotePointers.set(e,s)}}_resetProgramPointers(){const t=this._currentTime;for(const[e,i]of this.partChannels){const s=this.parsedData.parts[e];if(!s||!s.programChanges){this.partProgramPointers.set(e,0);continue}let n=0,a=s.defaultInstrument;for(;n<s.programChanges.length&&s.programChanges[n].time<=t;)a=s.programChanges[n].programNumber,n++;i.setInstrument(a),this.partProgramPointers.set(e,n)}}_stopScheduling(){this.scheduleInterval&&(clearInterval(this.scheduleInterval),this.scheduleInterval=null),this.partChannels.forEach(t=>{t.isActive()&&t.allNotesOff()})}_startTimeUpdateLoop(){this.timeUpdateInterval=setInterval(()=>{const t=this.getCurrentTime();this._emitEvent("timeupdate",{currentTime:t});const e=this.getBeatFromTime(t);e&&this._emitEvent("beatChanged",e),t>=this._totalDuration+.05&&(this.stop(),this._emitEvent("ended",{finalTime:t}))},100)}_stopTimeUpdateLoop(){this.timeUpdateInterval&&(clearInterval(this.timeUpdateInterval),this.timeUpdateInterval=null)}_emitEvent(t,e){(this.eventBus.all.get(t)||[]).forEach(s=>{try{s(e)}catch(n){console.error(`Error in ${t} event listener:`,n)}})}_createDefaultStructureMetadata(){const t=this.parsedData.barStructure||[];let e=1;if(t.length>1)e=t.length;else{this._totalDuration>0?e=Math.max(1,Math.ceil(this._totalDuration/2)):e=1,this.parsedData.barStructure=[null];for(let r=1;r<=e;r++){const o=(r-1)*2;this.parsedData.barStructure[r]={sig:[4,4],beats:[o,o+.5,o+1,o+1.5]}}}return{sections:[{from:1,to:e}],order:[{section:0}]}}destroy(){this.stop(),this.partChannels.forEach(t=>{t.destroy()}),this.partChannels.clear(),this.partOutputs.forEach(t=>{t.disconnect()}),this.partOutputs.clear(),this.partNotePointers.clear(),this.partProgramPointers.clear(),this.eventBus.all.clear()}}const I="data:audio/mpeg;base64,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",k="data:audio/mpeg;base64,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qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//tkxPIADij5M+wwzYmAm6X9hJmwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//s0xPaAClDTI6OxLwCwkeLoEJkoqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//sUxNoDwAAB/gAAACAAADSAAAAEqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq";class V{constructor(t,e={}){if(!t)throw new Error("MidiPlayer is required");this.midiPlayer=t,this.audioEngine=t.audioEngine,this.eventBus=v(),this.metronomeConfig={enabled:!1,tickInstrument:115,accentInstrument:116,volume:.7,...e.metronome},this.leadInConfig={enabled:!1,bars:1,...e.leadIn},this.startupConfig={delayMs:25,...e.startup},this.state="stopped",this.frozenTime=0,this.leadInData=null,this.leadInStartTime=null,this.leadInProgress=null,this.leadInInterval=null,this.timeUpdateInterval=null,this.metronomeScheduleInterval=null,this.nextBeatIndex=0,this._setupMetronomeAudio(),this._setupEventDelegation(),this._validateConfig()}async play(t={}){if(!(this.state==="playing"||this.state==="lead-in"))try{const e=t.leadIn!==void 0?t.leadIn:this.leadInConfig.enabled,i=t.metronome!==void 0?t.metronome:this.metronomeConfig.enabled;this.frozenTime===0&&(this.frozenTime=this.midiPlayer.getCurrentTime()),e?await this._startLeadIn(i):await this._startMidiPlayback(i)}catch(e){throw this.state="stopped",this._emitEvent("error",e),e}}pause(){if(this.state==="stopped"||this.state==="paused")return;const t=this.state;this.state="paused",t==="lead-in"?this._pauseLeadIn():t==="playing"&&(this.midiPlayer.pause(),this._stopMetronome()),this._stopTimeUpdateLoop(),this._emitEvent("playbackPaused",{})}resume(){this.state==="paused"&&(this.leadInData&&this.leadInProgress!==null&&this.leadInProgress<1?this._resumeLeadIn():(this.state="playing",this._resetMetronomeBeatTracking(),this.midiPlayer.play(),this._startMetronomeIfEnabled(),this._startTimeUpdateLoop(),this._emitEvent("playbackStarted",{})))}stop(){if(this.state==="stopped")return;const t=this.state==="playing";this.state="stopped",this.frozenTime=0,this.leadInData=null,this.leadInProgress=null,this.leadInStartTime=null,this._stopLeadIn(),this._stopMetronome(),this._stopTimeUpdateLoop(),this._resetMetronomeBeatTracking(),t&&this.midiPlayer.stop(),this._emitEvent("playbackStopped",{})}skipToTime(t){this.frozenTime=t,this.state!=="lead-in"&&(this.midiPlayer.skipToTime(t),this.state==="playing"&&this.metronomeConfig.enabled&&this._resetMetronomeBeatTracking())}setBar(t,e=0){if(this.state==="lead-in"){const i=this.midiPlayer.getTimeFromBar(t,e);i!==null&&(this.frozenTime=i);return}if(this.midiPlayer.setBar(t,e),this.state==="stopped"){const i=this.midiPlayer.getTimeFromBar(t,e);i!==null&&(this.frozenTime=i)}this.state==="playing"&&this.metronomeConfig.enabled&&this._resetMetronomeBeatTracking()}getCurrentTime(){return this.state==="lead-in"||this.state==="paused"&&this.leadInProgress!==null?this.frozenTime:this.midiPlayer.getCurrentTime()}getLeadInProgress(){return this.leadInProgress}setMetronomeEnabled(t){const e=this.metronomeConfig.enabled;this.metronomeConfig.enabled=t,this.state==="playing"&&(t&&!e?this._startMetronome():!t&&e&&this._stopMetronome()),this._emitEvent("metronomeEnabledChanged",{enabled:t})}setMetronomeSettings(t){if(t.volume!==void 0&&(t.volume<0||t.volume>1))throw new Error("Metronome volume must be between 0.0 and 1.0");if(Object.assign(this.metronomeConfig,t),t.volume!==void 0){const e=this.audioEngine.getMetronomeOutput();e&&e.gain&&(e.gain.value=t.volume)}this._emitEvent("metronomeSettingsChanged",{...t})}setLeadInEnabled(t){this.leadInConfig.enabled=t,this._emitEvent("leadInSettingsChanged",{enabled:t,bars:this.leadInConfig.bars})}setLeadInBars(t){if(t<1)throw new Error("Lead-in bars must be at least 1");this.leadInConfig.bars=t,this._emitEvent("leadInSettingsChanged",{enabled:this.leadInConfig.enabled,bars:t})}setPlaybackSpeed(t){this.midiPlayer.setPlaybackSpeed(t)}getMetronomeSettings(){return{...this.metronomeConfig}}isMetronomeEnabled(){return this.metronomeConfig.enabled}getLeadInSettings(){return{enabled:this.leadInConfig.enabled,bars:this.leadInConfig.bars}}getStartupSettings(){return{...this.startupConfig}}setStartupDelay(t){if(typeof t!="number"||t<0||t>1e3)throw new Error("Startup delay must be a number between 0 and 1000 milliseconds");this.startupConfig.delayMs=t,this._emitEvent("startupSettingsChanged",{delayMs:t})}getState(){return this.state}isInLeadIn(){return this.state==="lead-in"}isPlaying(){return this.state==="playing"||this.state==="lead-in"}getMetronomeOutput(){return this.audioEngine.getMetronomeOutput()}getPartOutput(t){return this.midiPlayer.getPartOutput(t)}allSoundsOff(){if(this.midiPlayer.allSoundsOff(),this.currentMetronomeSource)try{this.currentMetronomeSource.disconnect(),this.currentMetronomeSource.stop(),this.currentMetronomeSource=null}catch{}}getTotalDuration(){return this.midiPlayer.getTotalDuration()}on(t,e){this.eventBus.on(t,e)}off(t,e){this.eventBus.off(t,e)}async _setupMetronomeAudio(){try{this.metronomeOutput=this.audioEngine.audioContext.createGain(),this.metronomeOutput.gain.value=this.metronomeConfig.volume,this.metronomeBuffers=null,this.currentMetronomeSource=null}catch(t){throw console.error("Failed to setup metronome audio:",t),new Error("Failed to initialize metronome audio: "+t.message)}}async _ensureMetronomeBuffersLoaded(){if(!this.metronomeBuffers)try{if(typeof window>"u"||typeof fetch>"u"||!I||!k){console.log("Test environment detected, using mock metronome buffers"),this.metronomeBuffers=[null,null];return}this.metronomeBuffers=await Promise.all([fetch(I).then(t=>t.arrayBuffer()).then(t=>this.audioEngine.audioContext.decodeAudioData(t)),fetch(k).then(t=>t.arrayBuffer()).then(t=>this.audioEngine.audioContext.decodeAudioData(t))])}catch(t){throw console.error("Failed to load metronome sound buffers:",t),this.metronomeBuffers=[null,null],t}}_setupEventDelegation(){this.midiPlayer.on("timeupdate",t=>{this.state==="playing"&&this._emitEvent("timeupdate",{...t,effectiveTime:t.currentTime,leadInProgress:null})}),this.midiPlayer.on("beatChanged",t=>{this.state==="playing"&&this._emitEvent("beatChanged",{...t,isLeadIn:!1})}),this.midiPlayer.on("barChanged",t=>{this.state==="playing"&&this._emitEvent("barChanged",t)}),this.midiPlayer.on("ended",t=>{this.state==="playing"&&(this.state="stopped",this._stopMetronome(),this._stopTimeUpdateLoop(),this._emitEvent("playbackEnded",t))}),this.midiPlayer.on("error",t=>{this._emitEvent("error",t)})}_validateConfig(){if(this.metronomeConfig.volume<0||this.metronomeConfig.volume>1)throw new Error("Metronome volume must be between 0.0 and 1.0");if(this.leadInConfig.bars<1)throw new Error("Lead-in bars must be at least 1")}async _startLeadIn(t){this.state="lead-in";const e=this.startupConfig.delayMs/1e3;this.leadInStartTime=this.audioEngine.audioContext.currentTime+e,this.leadInProgress=0,this.leadInData=this._calculateLeadInBeats(),this._emitEvent("leadInStarted",{totalBeats:this.leadInData.totalBeats,duration:this.leadInData.duration,bars:this.leadInConfig.bars,startupDelayMs:this.startupConfig.delayMs}),setTimeout(()=>{this.state==="lead-in"&&this._startLeadInScheduling(t)},this.startupConfig.delayMs),this._startTimeUpdateLoop()}_calculateLeadInBeats(){const t=this.midiPlayer.beats,e=this.frozenTime,i=this.leadInConfig.bars;if(!t||t.length===0){const h=i*4;return{totalBeats:h,duration:h*.5,beatSequence:this._generateBeatSequence(h,.5,4),beatsPerBar:4}}let s=t[0],n=.5;for(let c=t.length-1;c>=0;c--)if(t[c].time<=e){s=t[c],c+1<t.length?n=t[c+1].time-s.time:c>0&&(n=s.time-t[c-1].time);break}const a=Math.max(0,s.bar-1),o=(this.midiPlayer.parsedData.barStructure||[])[a]||{bpm:120},q=s.timeSig;Array.isArray(o.bpm)?o.bpm[0]:o.bpm;const l=s.beat>1?s.beat-1:0,u=i*q+l;return{totalBeats:u,duration:u*n,beatSequence:this._generateBeatSequence(u,n,q),beatsPerBar:q,startBeat:s}}_generateBeatSequence(t,e,i){const s=[];for(let n=0;n<t;n++){const a=n%i+1;s.push({beat:a,isAccent:a===1,time:n*e,absoluteTime:this.leadInStartTime+n*e})}return s}_startLeadInScheduling(t){this.leadInBeatIndex=0,this.leadInScheduledBeats=new Set;const e=this.leadInData.beatSequence,i=10;this.leadInStartTime=this.audioEngine.audioContext.currentTime,this.leadInInterval=setInterval(()=>{const s=this.audioEngine.audioContext.currentTime-this.leadInStartTime;for(this.leadInProgress=Math.min(1,s/this.leadInData.duration);this.leadInBeatIndex<e.length;){const n=e[this.leadInBeatIndex],a=n.time-s;if(a>.05)break;if(!this.leadInScheduledBeats.has(this.leadInBeatIndex)&&a>=-.05&&a<=.05){const r=a<=0?this.audioEngine.audioContext.currentTime+.01:this.audioEngine.audioContext.currentTime+a;this._scheduleTickAtTime(r,n.isAccent),this._emitEvent("beatChanged",{bar:Math.floor(this.leadInBeatIndex/this.leadInData.beatsPerBar)+1,beat:n.beat,repeat:1,time:this.frozenTime,isLeadIn:!0}),this.leadInScheduledBeats.add(this.leadInBeatIndex)}this.leadInBeatIndex++}this.leadInProgress>=1&&this._completeLeadIn(t)},i)}_completeLeadIn(t){const e=this.leadInStartTime+this.leadInData.duration;this._stopLeadIn(),this._emitEvent("leadInEnded",{}),this.leadInData=null,this.leadInProgress=null,this.leadInStartTime=null,this._startMidiPlaybackAt(e,t,!0)}async _startMidiPlaybackAt(t,e,i=!0){this.state="playing",i&&this._emitEvent("playbackStarted",{startupDelayMs:0,scheduledStartTime:t}),this.midiPlayer.playAt(t),e&&this._startMetronomeAt(t),this.timeUpdateInterval||this._startTimeUpdateLoop()}async _startMidiPlayback(t,e=!0,i=!1){this.state="playing",this._resetMetronomeBeatTracking(),e&&this._emitEvent("playbackStarted",{startupDelayMs:i?0:this.startupConfig.delayMs});const s=()=>{if(this.state==="playing")try{this.midiPlayer.play(),t&&this._startMetronome()}catch(n){this.state="stopped",this._emitEvent("error",n)}};i?s():setTimeout(s,this.startupConfig.delayMs),this.timeUpdateInterval||this._startTimeUpdateLoop()}_startMetronome(){!this.metronomeConfig.enabled||this.state!=="playing"||(this._scheduleMetronomeTicks(),this.metronomeScheduleInterval=setInterval(()=>{this._scheduleMetronomeTicks()},50))}_startMetronomeAt(t){this.metronomeConfig.enabled&&(this.metronomeScheduledStartTime=t,this._resetMetronomeBeatTracking(),this._scheduleMetronomeTicksAt(t),this.metronomeScheduleInterval=setInterval(()=>{this._scheduleMetronomeTicks()},50))}_startMetronomeIfEnabled(){this.metronomeConfig.enabled&&this._startMetronome()}_scheduleMetronomeTicksAt(t){if(!this.metronomeConfig.enabled||!this.metronomeOutput)return;const e=this.midiPlayer.beats;if(!e||e.length===0)return;const i=this.midiPlayer.getCurrentTime(),s=.1;for(let n=this.nextBeatIndex;n<e.length;n++){const a=e[n],r=t+(a.time-i)/this.midiPlayer.playbackSpeed;if(r>this.audioEngine.audioContext.currentTime+s)break;if(r>=this.audioEngine.audioContext.currentTime-.01){const o=Math.max(r,this.audioEngine.audioContext.currentTime+.001),q=a.isDownbeat||a.beat===1;this._scheduleTickAtTime(o,q),this.nextBeatIndex=n+1}}}_scheduleMetronomeTicks(){if(!this.metronomeConfig.enabled||!this.metronomeOutput)return;const t=this.midiPlayer.getCurrentTime(),e=this.midiPlayer.beats;if(!e||e.length===0)return;for(;this.nextBeatIndex<e.length&&e[this.nextBeatIndex].time<t-.025;)this.nextBeatIndex++;const s=t+.15;for(;this.nextBeatIndex<e.length;){const n=e[this.nextBeatIndex],a=n.time-t;if(n.time>s)break;if(a>=-.025&&a<=.15){const r=this.audioEngine.audioContext.currentTime+.005,o=this.audioEngine.audioContext.currentTime+Math.max(a,.005),q=Math.max(r,o),l=n.beat===1;this._scheduleTickAtTime(q,l)}this.nextBeatIndex++}}async _scheduleTickAtTime(t,e){try{await this.audioEngine.playMetronomeTick(t,e,this.metronomeConfig.volume)}catch(i){console.warn("Failed to schedule metronome tick:",i)}}_resetMetronomeBeatTracking(){const t=this.getCurrentTime(),e=this.midiPlayer.beats;if(!e||e.length===0){this.nextBeatIndex=0;return}for(this.nextBeatIndex=0;this.nextBeatIndex<e.length&&e[this.nextBeatIndex].time<t-.01;)this.nextBeatIndex++}_stopMetronome(){this.metronomeScheduleInterval&&(clearInterval(this.metronomeScheduleInterval),this.metronomeScheduleInterval=null)}_stopLeadIn(){this.leadInInterval&&(clearInterval(this.leadInInterval),this.leadInInterval=null)}_pauseLeadIn(){this._stopLeadIn()}_resumeLeadIn(){if(!this.leadInData||this.leadInProgress===null)return;this.state="lead-in";const t=this.audioEngine.audioContext.currentTime,e=this.leadInProgress*this.leadInData.duration;this.leadInStartTime=t-e,this._startLeadInScheduling(this.metronomeConfig.enabled),this._startTimeUpdateLoop()}_startTimeUpdateLoop(){this.timeUpdateInterval||(this.timeUpdateInterval=setInterval(()=>{this.state==="lead-in"?this._emitEvent("timeupdate",{currentTime:this.frozenTime,effectiveTime:this.frozenTime,leadInProgress:this.leadInProgress||0}):this.state},100))}_stopTimeUpdateLoop(){this.timeUpdateInterval&&(clearInterval(this.timeUpdateInterval),this.timeUpdateInterval=null)}_emitEvent(t,e){(this.eventBus.all.get(t)||[]).forEach(s=>{try{s(e)}catch(n){console.error(`Error in ${t} event listener:`,n)}})}destroy(){if(this.stop(),this._stopLeadIn(),this._stopMetronome(),this._stopTimeUpdateLoop(),this.currentMetronomeSource){try{this.currentMetronomeSource.disconnect(),this.currentMetronomeSource.stop()}catch{}this.currentMetronomeSource=null}this.metronomeOutput&&this.metronomeOutput.disconnect(),this.eventBus.all.clear()}}const C=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));exports.AudioEngine=g;exports.AudioEngineUtils=x;exports.BeatMapper=B;exports.ChannelHandle=b;exports.MidiParser=U;exports.MidiPlayer=G;exports.PlaybackManager=V;exports.SpessaSynthAudioEngine=F;exports.SpessaSynthChannelHandle=w;