audio-mixer-engine 0.2.0 → 0.3.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
@@ -36,77 +36,82 @@ npm install audio-mixer-engine
36
36
  ## Quick Start - Basic Playback
37
37
 
38
38
  ```javascript
39
- import {
40
- SpessaSynthAudioEngine,
41
- MidiParser,
42
- MidiPlayer
39
+ import {
40
+ SpessaSynthAudioEngine,
41
+ MidiParser,
42
+ MidiPlayer,
43
+ PlaybackManager
43
44
  } from 'audio-mixer-engine';
44
45
 
45
- // Initialize audio context and engine
46
- const audioContext = new AudioContext();
47
- const audioEngine = new SpessaSynthAudioEngine(audioContext);
46
+ // Initialize audio context
47
+ const ac = new AudioContext();
48
48
 
49
- // Load soundfont and parse MIDI
49
+ // Setup the audio engine - use the spessasynth engine with a soundfont
50
+ const audioEngine = new SpessaSynthAudioEngine(ac);
50
51
  await audioEngine.initialize('/path/to/soundfont.sf2');
52
+
53
+ // Parse the midi data
51
54
  const parser = new MidiParser();
52
55
  const midiData = await parser.parse(midiArrayBuffer);
53
56
 
54
- // Create player with optional instrument mapping
55
- // Note: MIDI program changes from files are used by default
56
- // instrumentMap only needed to override or for parts without program changes
57
- const instrumentMap = {
58
- soprano: { instrument: 'choir_aahs', volume: 1.0 }, // Overrides MIDI program
59
- alto: { instrument: 'choir_aahs', volume: 1.0 }, // Overrides MIDI program
60
- piano: { instrument: 'piano', volume: 0.8 } // Overrides MIDI program
61
- };
62
-
63
- // Optional structure metadata for advanced beat mapping
64
- const structureMetadata = {
65
- sections: [{ startBar: 1, endBar: 8 }],
66
- order: [{ section: 0 }]
67
- };
68
-
69
- const player = new MidiPlayer(audioEngine, instrumentMap, midiData, structureMetadata);
57
+ // Initialise the MIDI player and the playback manager
58
+ const player = new MidiPlayer(audioEngine, midiData);
59
+ const manager = new PlaybackManager(player);
70
60
 
71
61
  // Set up event listeners
72
- player.on('timeupdate', ({ currentTime }) => {
62
+ manager.on('timeupdate', ({currentTime}) => {
73
63
  console.log(`Playing: ${currentTime.toFixed(2)}s`);
74
64
  });
75
65
 
76
- player.on('barChanged', ({ bar, beat, time }) => {
66
+ manager.on('barChanged', ({bar, beat, time}) => {
77
67
  console.log(`Bar ${bar}, Beat ${beat} at ${time.toFixed(2)}s`);
78
68
  });
79
69
 
80
- player.play();
70
+ // Setup routing
71
+ // metronome
72
+ const metGain = ac.createGain()
73
+ metGain.gain.value = 0.5;
74
+ manager.getMetronomeOutput().connect(metGain).connect(ac.destination)
75
+ // audio part channels
76
+ const partGains = Object.keys(midiData.parts).map(partName=>{
77
+ const gain = ac.createGain()
78
+ gain.gain.value = 0.75;
79
+ manager.getPartoutput(partName).connect(gain).connact(ac.destination)
80
+ return gain
81
+ })
82
+
83
+ // Start playback with lead-in metronome ticks
84
+ manager.play({useLeadIn: true});
81
85
  ```
82
86
 
83
87
  ## Mixer Integration Pattern
84
88
 
85
89
  ```javascript
86
90
  // Create parts manually for mixing interface
87
- const sopranoChannel = audioEngine.createChannel('soprano', {
88
- instrument: 'choir_aahs',
91
+ const partChannel = audioEngine.createChannel('soprano', {
92
+ instrument: 'piano',
89
93
  initialVolume: 1.0
90
94
  });
91
95
 
92
96
  // Get individual output node for external mixing
93
- const sopranoOutput = sopranoChannel.getOutputNode();
97
+ const partOutput = partChannel.getOutputNode();
94
98
 
95
99
  // Set up mixer chain: Part Output -> External Gain -> Analyzer -> Master
96
- const sopranoGain = audioContext.createGain();
97
- const sopranoAnalyzer = audioContext.createAnalyser();
100
+ const partGain = audioContext.createGain();
101
+ const partAnalyzer = audioContext.createAnalyser();
98
102
 
99
- sopranoOutput.connect(sopranoGain);
100
- sopranoGain.connect(sopranoAnalyzer);
101
- sopranoAnalyzer.connect(masterGain); // External master gain
103
+ partOutput.connect(partGain);
104
+ partGain.connect(partAnalyzer);
105
+ partAnalyzer.connect(masterGain); // External master gain
102
106
 
103
107
  // External mixer controls
104
- sopranoGain.gain.value = 0.5; // 50% volume
105
- sopranoAnalyzer.getByteFrequencyData(dataArray); // Level metering
108
+ partGain.gain.value = 0.5; // 50% volume
109
+ partAnalyzer.getByteFrequencyData(dataArray); // Level metering
106
110
 
107
- // Play notes on individual parts
108
- sopranoChannel.noteOn(60, 100); // C4 at velocity 100
109
- sopranoChannel.noteOff(60);
111
+ // Play a note
112
+ const ac = audioEngine.audioContext;
113
+ // In half a second play a C4 (pitch=60) at velocity of 100, and will last half a second
114
+ partChannel.playNote(ac.currentTime + 0.5, 60, 100, 0.5)
110
115
  ```
111
116
 
112
117
  ## Audio Routing Architecture
@@ -164,9 +169,9 @@ player.skipToTime(30.5);
164
169
  player.setPlaybackSpeed(1.5);
165
170
 
166
171
  // Musical navigation
167
- player.setBar(4, 0); // Jump to bar 4, repeat 0
172
+ player.setBar(4, 1); // Jump to bar 4, repeat 1
168
173
  const time = player.getTimeFromBar(2); // Get time for bar 2
169
- const barInfo = player.getBeatFromTime(15.3); // Get bar info at time
174
+ const barInfo = player.getBeatFromTime(15.3); // Get beat info at time (includes bar, beat, time signature)
170
175
 
171
176
  // Emergency stop
172
177
  player.allSoundsOff();
@@ -642,7 +647,7 @@ const structureMetadata = {
642
647
  ]
643
648
  };
644
649
 
645
- const player = new MidiPlayer(audioEngine, instrumentMap, parsedData, structureMetadata);
650
+ const player = new MidiPlayer(audioEngine, parsedData, instrumentMap, structureMetadata);
646
651
 
647
652
  // Navigate by song sections
648
653
  player.setBar(9); // Jump to Chorus
@@ -680,10 +685,10 @@ player.on('barChanged', ({ bar, beat, repeat, time }) => { /* */ });
680
685
 
681
686
  ```javascript
682
687
  // Basic usage (backward compatible)
683
- new MidiPlayer(audioEngine, instrumentMap, parsedData);
688
+ new MidiPlayer(audioEngine, parsedData, instrumentMap);
684
689
 
685
690
  // With structure metadata for advanced navigation
686
- new MidiPlayer(audioEngine, instrumentMap, parsedData, structureMetadata);
691
+ new MidiPlayer(audioEngine, parsedData, instrumentMap, structureMetadata);
687
692
  ```
688
693
 
689
694
  ### New Methods
@@ -1 +1 @@
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;
1
+ "use strict";var v=Object.create;var y=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var P=(c,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of E(t))!B.call(c,s)&&s!==e&&y(c,s,{get:()=>t[s],enumerable:!(i=M(t,s))||i.enumerable});return c};var O=(c,t,e)=>(e=c!=null?v(S(c)):{},P(t||!c||!c.__esModule?y(e,"default",{value:c,enumerable:!0}):e,c));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N="data:audio/mpeg;base64,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",z="data:audio/mpeg;base64,//uUxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAAMAAAQIAA0NDQ0NDQ0NE5OTk5OTk5OaWlpaWlpaWmAgICAgICAgICTk5OTk5OTk6qqqqqqqqqqu7u7u7u7u7u7zs7Ozs7Ozs7f39/f39/f3+/v7+/v7+/v7/n5+fn5+fn5//////////8AAABQTEFNRTMuMTAwBLkAAAAAAAAAABUgJAUHgQAB4AAAECAxW679AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//vUxAAACCABc/QAAASlsCq/NbBI//6YZkIUAAFLgQBA4oRqEAYLghE58ufUCCwQdg4cLg+fB8/KAMHwfPlAfB8Hwff/5cH3g+fy4Pg/+XPwQd/wQBD86Y8gkBAAEAAAAAAAAAgQrkCrEVHmoOI2GbVQgUVLGCAZk2giahgNUJfWEmZuCS6GQYHryLrek+YWAQUYGDJjZBcUZc/xZkxAJDiUv6cjCAkIBQcZEWkwrFnMRBNIDzDyqSPuYmdBd1NGE0AqpQKAGNiEXfAKhBiYOYGUjJkZ2AOyjaY0ImdI5lZETByCVvGepnGECLcW5goFEIwAAELA6QMjdpbgFAlHSELMYI6NhsXTlT0a85tNYaSysWCRGDNkX038MuQl7DiPSmsBRZZNyBo9HaWVW8U9F1wzIXGXk40RpLrPIy1qVQ1IHRXk7MxS3Pu14lTdsYzHOw7VlM7jjhVs6f7KYf65qepsuZYXYxJolGpbL5TLbNNSZXJm9p0M//iMsVQ7/6VK3d/rV2AgQAApEaRpCKTcVUUkVtqqLCJGtNg6y2jtwHDESSvk2Fw6N3+aXf7R0rLYHiC7AwuXLmKwOjqYF4Cw7uz6Q6jxc1eIrkEUD8+uLyva193XIM/kiYtbDp9tqTes/PzSb/uzzOWyZ2XrvWm1tmkXf87tJ/PepWrAEAoCaGjykkhihRd+7cjO7+xYQCJAAKmD4IYmMAkRJVGVgoqxsihA6IvC3dmL8QhTKbyvzcWEi1Yqn8Nxw+iSr5SxjlLdCXdF3Chq2GqmhH0HapSVXVjm0PmJjclY5ot8mZWeQo3Sli4Zr0gsEtIUA8FfWI9fP2eSDq6qlkGky9PhZFUT037KSvLeE8c2K8NvKfrAzDCxhsJ7mU9Vm07FWjX2tLxLQq2lvjQyS8yqG7DN9iqu/9xVUBAAABcGjNABl2BF8EOIWCATqRY08Bfh2GIJetn1bj8qt5zG4On8b9aPU0lrzLtNyztT9/lJVks/Wfh/luS1ajgshnKktaVD8Ujr7PfE//uUxMAAESEfTf2GAAKRL2l9h5o57CVDL1DUxE+16N/ZlErt0d6INCzx1MGSos2JUt3lWfLY5nki11ZSM6xtb3vTNd4HNv2PHg2roFE57bqRmzkX8dHGwUKEj00aazgk3DW3/7cM4oAXR8yqCLcwz1t2RCQUfk4mRuE/8tV2zdsVaBI3LojIxMcJgfx1zuK5vy3l1NN67gZjqF3iZF7JYhSpOY7UOy+nVNFM/VDGaZ3IQTIvjgSxHMbinYjE9ZWx6u2VOMivcV2m5bQIG9xSsb008hZkgymncftVx7hz2Rw/djtH3XsESkzENp/uY29u/2N17bvE/3K/tHpXvh6tn//6OHIgAAAnSNloBpAdRMhzluDPRIY5G1PMW1qMdGH+rLq+7w5m6FLCg7f7kP4nRnwIbPqAr3z+Sq5NEkz8M1GJBIpzMfL6qmwnkgnBnm8cgpbGf0JPSu4OYzZuJ8kmaNg2REliphhaQ1GNv2xwcoQkOczv23S5NOZDUaqY3db6//uUxN6Ak91LQ+wkfkpzLCi9h5o5m/kuQyJqdDKobXybFrBuLP2ZUVq8t3obzf/FeCNVgs9F5McOOmAhPUmiE0LFTZsTd5PPvG4KGI1mrDT7xZbWfMGWK4sTUXVfXEmoGG4/Fhsam3nSMA8mV7HQiZSPj/UNE4uFAsi3GeP1DyXqGEpWF9ZWiw5YU8jQAFwTqnZjplvvecwlhCe6n+RUtMErkJqWac+55tuWygx3UMW3XReqPwXZV9Xe79dshSru/e5HYRAAABdHqgdkdLIsIwNWlfDptNIBi+6q0GPS1prUB3I9agHNVpjZY08jLWAn1Asorb1qgRm6JMnkisbU6sVJq4W1twY2hvfs8NcqQqhiOYuKdEaS78/WFedz6osvG5yMR+1x1a1MS7ifEJpqpQcjeUGQTtd48omOqDGQuP7+W+/ZzzlFrinuP3j73Zr/3/Xz4twbleyOyTu97+/rSHBAVMZSQMqAREhdluKKDIZKPAQa1ZsklZOwFvo2yu1P//uExPYA0xEhQ+ek3gpHpCh5h5n5M3Sq5CW7uLj40BOOZlHgvYfxp9RZnadQwuTUnF0tRn7jhS1eMKQQpPHKcosSFtjMu3JRSxomIkDRDLB0OzxwwAodOIFFDal4zoQh9xUQmt+3kwqINMQcOmFtY7+L+VOxqzUXfKVevPvpcUdZVpKR8+Jz+1I63+/9eGEwAADLgoRDkhMWDZvH66VrO3JUGVutvizKB38fFVpreIUVGy2h3h0z9uKFp3Mb+0WDhss4HatoqOu3G0zFRib5GLbM4EzUb1TskJqa/CizSmo4hEQIFoQFyOZrGczw0Y/STG673ZA5IfmVs4UuaSBrTcMPTM5iII/zpKb87Xeu5rWv/e3zXn92LLiYIABexmOWmGikUwIJAXEntrwM7DKkoWFsYvj8rSD3LxcOnK/R+aLkEUGRdatbWULavvLikQcq//t0xP4Ak6FXP+y80cpiKig9l6H5RVFThop0KGGkgRGh0CwAgaDowc2nqVZRxQKRxRhDvXPCJvSS0iFI85U1+lmnNNOt64jha/7MFnFjRcq+x21mGoNV1UFWs2u22/6/aezV7f/+eINQQAVfQ5oBMXoZE9i7IPa2wZpK3YNo3Sau3kksskwNzLClcIIlSdXPtjjJs9fvMUkskrMlVE+LTnCpzv2cM2OTSJSc5krnXLMywyazLJWbtZ4au75+7aeX2/+V0/uPUdS3O7d3jWihOnC0MeKaZjXaLft85pbb57173f+RdIqAASvBmEzBisCQM0FZzTmvLujaergs8YNFZCuuDZU7bC5oKicRFULDSaTRghituw9ahmpIE5DIk7WX//uExOiAEUUhQ+w9D8okJqg9hiF54TxZEqxMmqScWJblwliNhGiYNkhnFX7qt/aV3QmB4ngkyP2J9ypCsWRXyTzPWkBEOA4NOnXKbKr//v+tpGDBAUwA1QSAmSX6dh3Y7Wd1MFnSVTktIT5eR2GaNxAOssKW4iaIeHsuOCCRKKpoeiZTh4IwJiKRuGZmN+SqOUCQKiQkB0VNy2S/hOW1yNtWtT62P82cfNovPjYvJh7JPib2kek0/dnfmv3jhJ6R8Zmu2/cyKfEsJxdbu9kXRmAAAHQCgrmAZQKJkLPGnRqBX+eVKpu7ZHFZkwNzB8QAFnRgqVhJEo/JTG25S3cnqScozDAZCYsOLEnOVaM4wPgPC4egqB9mDghpYcSeQNUT1sHvPvHN6ZdZsqKsMJjr7TI5kPTx1vMqs1+2guYHVnNUppvFrENscWYqzLu8qaRC//tkxPwAD61HQ+wkz8HdI+g9hI45gAS6BQcpTFqwRZcUONEHuQgyUG/n0dMLssFh8g8SJ3xCyjTdJpK1fVI3HZjM6E8txp3oGAVmqBw0RxkDt5cQjqEHKMJUcfF2nsvG0jYEEiouOG7m7ZtxJFy3M/9RpKEoh1KcZeu5KNpruysy8pVLBCdoB8kWLEx1S2IRvuMGXIlIoDf9gTxzs42MEdEBG0FoTpOcsJ2ixOihPbglHJnCSyBBEVZ/kKhKWIgNdcjGFkOeGrJlmBTJ5sqVsIT/4l38xIvAkm97OUV7x5gaenmUACAqQrC5xf1/3q3U//t0xO8AD70/Q+wkz+n5qGe9hKHtlsq7qqqakzcTDmwTWWuUEbDbnqtqtEb1afUxbmIMYiqi8bc8sJprNvdqi0NSyTUy69E7tmLYctB048TkJJITyTnV4QAxhEtzQ2/W/bYJkjw1HEgbf3//bfnnMIstHcz//1utqycFBToz9Zsftjy2ij/0z26l3mplGMDM1ACg5A6L2xqnxxw5cm2kNyX8RB+KTaK0JCBSwXpItftMy+crSfhDn98K0ycmFainmxRmnJnLsQDpfafL4c5MezncwxXVW41Vmu0smDHUckgSBwWtD42jqRW6CX3PyhmMACSVLELdkcLADC4/qy1L+Ht/uqLR8dHZVARYhQuMX3hx+zr2RRT6alzWRC7iBlCo//tkxPYADdkbO+elD2nQHud9pJn9TFUW552h+PSM431ETSTCJpbbra/leecUJ0D5UgCilASQAMP/CHi5OJhc5x5gGCe+N5bvM/sDJS+Vv8y3rfRYKi4jDSMS4i9KTEFNRTMuMTAwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//tkxPIADij5M+wwzYmAm6X9hJmwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//s0xPaAClDTI6OxLwCwkeLoEJkoqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//sUxNoDwAAB/gAAACAAADSAAAAEqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq";class p{constructor(t,e={}){if(new.target===p)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 q=Math.max(t,this.audioContext.currentTime);n.start(q)}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(N),fetch(z)]),[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 g{constructor(t,e,i={}){if(new.target===g)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,q=s;if(t<n){const d=n-t;r=n,q=Math.max(0,s-d)}if(q<=0)return a;const o=Math.max(0,(r-n)*1e3),l=setTimeout(()=>{this.noteOn(e,i),this.scheduledEvents.delete(`${a}_on`)},o),h=o+q*1e3,A=setTimeout(()=>{this.noteOff(e),this.scheduledEvents.delete(`${a}_off`)},h);return this.scheduledEvents.set(`${a}_on`,l),this.scheduledEvents.set(`${a}_off`,A),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")}}const _={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},D=Object.entries(_).reduce((c,[t,e])=>(c[e]=t,c),{});class I{static getInstrumentProgram(t){if(typeof t=="number")return t;const e=_[t.toLowerCase()];return e!==void 0?e:0}static getProgramName(t){return D[t]||`Program ${t}`}static generateNoteId(t,e,i){return`${t}_${e}_${Math.round(i)}`}}class k extends g{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=I.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 U extends p{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 k(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(()=>T),i=await Promise.resolve().then(()=>T),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,o=Math.round(Math.min(127,Math.max(0,i*(e?127:100)))),l=this.audioContext.currentTime,A=Math.max(t,l)-l;A<=.01?(n.noteOn&&n.noteOn(a,r,o),setTimeout(()=>{n.noteOff&&n.noteOff(a,r)},100)):setTimeout(()=>{n.noteOn&&n.noteOn(a,r,o),setTimeout(()=>{n.noteOff&&n.noteOff(a,r)},100)},A*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 L{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,q={format:s,ticksPerBeat:r,tracks:[],duration:0};let o=8+i;for(let l=0;l<n;l++)if(e[o]===77&&e[o+1]===84&&e[o+2]===114&&e[o+3]===107){const h=this._bytesToNumber(e.slice(o+4,o+8)),A=e.slice(o+8,o+8+h),d=this._parseTrack(A);q.tracks.push(d),o+=8+h}else throw new Error(`Invalid track header at position ${o}`);return q}_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 q=r;if((r&128)===0){if(n===null)throw new Error("Running status byte encountered before status byte");q=n,i--}else n=q;if(q===255){const o=t[i++],l=this._readVariableLengthValue(t,i);i+=l.bytesRead;const h=t.slice(i,i+l.value);switch(i+=l.value,o){case 3:e.name=this._bytesToString(h);break;case 1:e.events.push({type:"text",text:this._bytesToString(h),tick:s});break;case 5:e.lyrics.push({text:this._bytesToString(h),tick:s});break;case 81:const A=this._bytesToNumber(h),d=Math.round(6e7/A);e.events.push({type:"tempo",bpm:d,tick:s});break;case 88:e.events.push({type:"timeSignature",numerator:h[0],denominator:Math.pow(2,h[1]),tick:s});break;case 47:e.duration=s;break}}else if((q&240)===144){const o=q&15,l=t[i++],h=t[i++];h>0?e.notes.push({type:"noteOn",noteNumber:l,velocity:h,tick:s,channel:o}):e.notes.push({type:"noteOff",noteNumber:l,tick:s,channel:o})}else if((q&240)===128){const o=q&15,l=t[i++];t[i++],e.notes.push({type:"noteOff",noteNumber:l,tick:s,channel:o})}else if(q===240||q===247){const o=this._readVariableLengthValue(t,i);i+=o.bytesRead+o.value}else if((q&240)===176){const o=q&15,l=t[i++],h=t[i++];e.events.push({type:"controller",controllerNumber:l,value:h,channel:o,tick:s})}else if((q&240)===192){const o=q&15,l=t[i++];e.events.push({type:"programChange",programNumber:l,channel:o,tick:s})}else if((q&240)===208){const o=q&15,l=t[i++];e.events.push({type:"channelAftertouch",pressure:l,channel:o,tick:s})}else if((q&240)===224){const o=q&15,l=t[i++],A=(t[i++]<<7|l)-8192;e.events.push({type:"pitchBend",value:A,channel:o,tick:s})}else if((q&240)===160){const o=q&15,l=t[i++],h=t[i++];e.events.push({type:"noteAftertouch",noteNumber:l,pressure:h,channel:o,tick:s})}else console.warn(`Unknown event type: ${q.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(h=>{(h.type==="timeSignature"||h.type==="tempo")&&i.push(h)})}),i.sort((l,h)=>l.tick-h.tick);let s=0;t.tracks.forEach(l=>{l.notes&&l.notes.forEach(h=>{h.type==="noteOff"&&h.tick>s&&(s=h.tick)})}),s===0&&(s=e*8);const n=[],a=i.filter(l=>l.type==="timeSignature").sort((l,h)=>l.tick-h.tick);let r={numerator:4,denominator:4},q=0,o=0;for(;q<s;){for(;o<a.length&&a[o].tick<=q;)r=a[o],o++;let l;l=q+e*4*r.numerator/r.denominator,i.filter(u=>u.type==="tempo"&&u.tick>=q&&u.tick<l);const h=r.numerator,A=[],d=e*(4/r.denominator);for(let u=0;u<h;u++){const m=q+u*d,f=this._ticksToTime(m,t);A.push(f)}n.push({sig:[r.numerator,r.denominator],beats:A}),q=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 u=s.name.toLowerCase();for(const m of this.partNames)if(m.length===1){if(u===m){a=m;break}}else if(u.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,q=2;for(;e[r];)r=`${a} ${q}`,q++;a=r;const o=[],l={};s.notes.forEach(u=>{if(u.type==="noteOn")l[u.noteNumber]={tick:u.tick,velocity:u.velocity};else if(u.type==="noteOff"&&l[u.noteNumber]){const m=l[u.noteNumber],f=u.tick-m.tick;o.push({pitch:u.noteNumber,name:this._midiNoteToName(u.noteNumber),startTick:m.tick,endTick:u.tick,duration:f,startTime:this._ticksToTime(m.tick,t),endTime:this._ticksToTime(u.tick,t),velocity:m.velocity}),delete l[u.noteNumber]}});const h=s.lyrics.map(u=>({text:u.text,tick:u.tick,time:u.tick/i}));o.sort((u,m)=>u.startTick-m.startTick);const A=s.events.filter(u=>u.type==="programChange").map(u=>({programNumber:u.programNumber,tick:u.tick,time:this._ticksToTime(u.tick,t)})).sort((u,m)=>u.tick-m.tick),d=A.length>0?A[0].programNumber:0;e[a]={notes:o,lyrics:h,trackIndex:n,programChanges:A,defaultInstrument:d}}),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(q=>{q.events.forEach(o=>{o.type==="tempo"&&s.push(o)})}),s.sort((q,o)=>q.tick-o.tick);let n=0,a=0,r=120;for(const q of s){if(q.tick>t)break;if(q.tick>a){const l=(q.tick-a)/i*(60/r);n+=l,a=q.tick}r=q.bpm}if(t>a){const o=(t-a)/i*(60/r);n+=o}return n}}class C{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 q=s[r],o=n.from!==void 0?n.from:this._getSectionStartBar(t,n.section),l=n.to!==void 0?n.to:a.to,h=n.as||1;for(let A=o;A<=l;A++)this._shouldPlayBar(a,A,h)&&i.push({barNumber:A,repeat:q,sectionIndex:n.section,voltaTime:h})}return i}generateBeatTable(t,e){const i=[],s={};let n=0,a=0;const r=[...e];for(;a<t.length&&n<r.length;){const q=t[a],o=q.barNumber;if(s[o]===void 0){const d=r[n];if(!d||!d.sig)throw new Error(`Invalid MIDI bar structure at index ${n}`);s[o]=d.sig[0]}const l=s[o];let h=r[n],A=h.sig[0];for(;A<l&&n+1<r.length;){const d=r[n+1],u=[h.sig[0]+d.sig[0],h.sig[1]],m=[...h.beats||[],...d.beats||[]];h={sig:u,beats:m},r[n]=h,r.splice(n+1,1),A=u[0]}if(A>l){const d=l,u=A-l,m=h.beats?h.beats.slice(0,d):[],f=h.beats?h.beats.slice(d):[],b={sig:[d,h.sig[1]],beats:m},w={sig:[u,h.sig[1]],beats:f};r[n]=b,r.splice(n+1,0,w),h=b}this._generateBeatsForBar(i,q,h,l),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 o={time:a[r-1],repeat:e.repeat,bar:e.barNumber,beat:r,timeSig:s};t.push(o)}}_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 x(c){return{all:c=c||new Map,on:function(t,e){var i=c.get(t);i?i.push(e):c.set(t,[e])},off:function(t,e){var i=c.get(t);i&&(e?i.splice(i.indexOf(e)>>>0,1):c.set(t,[]))},emit:function(t,e){var i=c.get(t);i&&i.slice().map(function(s){s(e)}),(i=c.get("*"))&&i.slice().map(function(s){s(t,e)})}}}class F{constructor(t,e,i=null,s=null){if(!t||!t.isInitialized)throw new Error("Initialized AudioEngine is required");if(!e)throw new Error("Parsed MIDI data is required");this.audioEngine=t,this.instrumentMap=i||{},this.parsedData=e,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=x(),this.beatMapper=new C,this.beats=[],this._setupPartChannels(),this._calculateTotalDuration(),this._resetNotePointers(),this._resetProgramPointers();const n=s||this._createDefaultStructureMetadata();this.beats=this.beatMapper.mapBeats(e,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 q=a.programChanges;for(;r<q.length&&q[r].time<e;)r++;for(;r<q.length&&q[r].time<=i;){const o=q[r];n.setInstrument(o.programNumber),r++}this.partProgramPointers.set(s,r)}if(a.notes){let r=this.partNotePointers.get(s)||0;const q=a.notes;for(;r<q.length&&q[r].endTime<e;)r++;for(;r<q.length&&q[r].startTime<=i;){const o=q[r];if(o.endTime-o.startTime>=.01){const l=this.playbackStartTime+o.startTime/this.playbackSpeed,h=(o.endTime-o.startTime)/this.playbackSpeed;n.playNote(l,o.pitch,o.velocity,h)}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(){return{sections:[{from:1,to:this.parsedData.barStructure.length}],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()}}class G{constructor(t,e={}){if(!t)throw new Error("MidiPlayer is required");this.midiPlayer=t,this.audioEngine=t.audioEngine,this.eventBus=x(),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._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(){this.midiPlayer.allSoundsOff()}getTotalDuration(){return this.midiPlayer.getTotalDuration()}on(t,e){this.eventBus.on(t,e)}off(t,e){this.eventBus.off(t,e)}_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;let s=t.length-1,n=.5;for(;t[s].time>e;)s--;const a=t[s],r=t[s+1];r?n=r.time-a.time:s>0&&(n=a.time-t[s-1].time);const q=a.timeSig===1,o=q&&r?r.timeSig:a.timeSig,l=q?o-1:a.beat>1?a.beat-1:0,h=i*o+l;return{totalBeats:h,duration:h*n,beatSequence:this._generateBeatSequence(h,n,o),beatsPerBar:o,startBeat:a}}_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)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 q=Math.max(r,this.audioEngine.audioContext.currentTime+.001),o=a.isDownbeat||a.beat===1;this._scheduleTickAtTime(q,o),this.nextBeatIndex=n+1}}}_scheduleMetronomeTicks(){if(!this.metronomeConfig.enabled)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,q=this.audioEngine.audioContext.currentTime+Math.max(a,.005),o=Math.max(r,q),l=n.beat===1;this._scheduleTickAtTime(o,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(){this.stop(),this._stopLeadIn(),this._stopMetronome(),this._stopTimeUpdateLoop(),this.eventBus.all.clear()}}const T=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));exports.AudioEngine=p;exports.AudioEngineUtils=I;exports.BeatMapper=C;exports.ChannelHandle=g;exports.MidiParser=L;exports.MidiPlayer=F;exports.PlaybackManager=G;exports.SpessaSynthAudioEngine=U;exports.SpessaSynthChannelHandle=k;