1var E=Object.defineProperty;var k=(y,e,t)=>e in y?E(y,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):y[e]=t;var d=(y,e,t)=>k(y,typeof e!="symbol"?e+"":e,t);import{M as b,R as C,u as M}from"./audioUnlock-DK0S9xzD.js";import{M as A,a as w}from"./MIDIService-CF3kZgy9.js";import{n as G,a as V}from"./export-CbG4o32l.js";class g{constructor(e,t){d(this,"audioContext");d(this,"config");d(this,"masterGain");d(this,"layers",new Map);d(this,"roleAssignments",new Map);d(this,"isPlaying",!1);d(this,"schedulerIntervalId",null);d(this,"startTime",0);d(this,"nextEventIndex",new Map);this.audioContext=e,this.config=t,this.masterGain=e.createGain(),this.masterGain.connect(e.destination),this.masterGain.gain.value=.3}connectOutput(e){this.masterGain.connect(e)}disconnectOutput(e){try{this.masterGain.disconnect(e)}catch{}}setupLayers(e){this.cleanupLayers();let t=1/0;for(const s of e)s.events.length>0&&(t=Math.min(t,s.events[0].time)),s.chords.length>0&&(t=Math.min(t,s.chords[0].time));if(t!==1/0&&t>0){console.log("Normalizing event times, earliest was:",t);for(const s of e){for(const i of s.events)i.time-=t;for(const i of s.chords)i.time-=t}}for(const s of e){const i=this.createSynthLayer(s.role);this.layers.set(s.role,i),this.roleAssignments.set(s.role,s),this.nextEventIndex.set(s.role,0)}console.log("Setup layers for roles:",Array.from(this.roleAssignments.keys()))}start(){if(!this.isPlaying){this.isPlaying=!0,this.startTime=this.audioContext.currentTime;for(const e of this.roleAssignments.keys())this.nextEventIndex.set(e,0);for(const[e,t]of this.layers)t.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.restoreLayerGain(t.gainNode,e);this.schedulerIntervalId=window.setInterval(()=>{this.scheduleEvents()},this.config.scheduleInterval),console.log("Started synthesis with",this.roleAssignments.size,"roles")}}stop(){this.isPlaying&&(this.isPlaying=!1,this.schedulerIntervalId&&(clearInterval(this.schedulerIntervalId),this.schedulerIntervalId=null),this.fadeOutAllLayers())}setVolume(e){this.masterGain.gain.value=Math.max(0,Math.min(1,e))}seek(e){if(!this.isPlaying)return;const t=this.getDuration(),s=e*t;this.startTime=this.audioContext.currentTime-s;for(const[i,n]of this.roleAssignments){const a=n.events;if(a.length>0){let o=0;for(;o<a.length&&a[o].time<s;)o++;this.nextEventIndex.set(i,o)}}}getProgress(){if(!this.isPlaying)return 0;const e=this.getDuration();if(e===0)return 0;const t=this.audioContext.currentTime-this.startTime;return Math.max(0,Math.min(1,t/e))}getCurrentTime(){return this.isPlaying?Math.max(0,this.audioContext.currentTime-this.startTime):0}getDuration(){let e=0;for(const t of this.roleAssignments.values()){if(t.events.length>0){const s=t.events[t.events.length-1],i=s.time+s.duration;e=Math.max(e,i)}if(t.chords.length>0){const s=t.chords[t.chords.length-1],i=s.time+s.duration;e=Math.max(e,i)}}return e}createSynthLayer(e){const t=this.audioContext.createGain(),s=this.audioContext.createBiquadFilter();return s.connect(t),t.connect(this.masterGain),this.configureLayerForRole(t,s,e),{role:e,oscillators:[],gainNode:t,filterNode:s}}getLayerGainForRole(e){switch(e){case"bass":return .4;case"drone":return .2;case"ostinato":return .3;case"texture":return .1;case"accents":return .5;case"melody":return .35;default:return .3}}restoreLayerGain(e,t){e.gain.setValueAtTime(this.getLayerGainForRole(t),this.audioContext.currentTime)}configureLayerForRole(e,t,s){switch(e.gain.value=this.getLayerGainForRole(s),s){case"bass":t.type="lowpass",t.frequency.value=200;break;case"drone":t.type="bandpass",t.frequency.value=400;break;case"ostinato":t.type="highpass",t.frequency.value=300;break;case"texture":t.type="bandpass",t.frequency.value=800;break;case"accents":t.type="peaking",t.frequency.value=1e3;break;case"melody":t.type="lowpass",t.frequency.value=4e3;break}}midiToFrequency(e){return 440*Math.pow(2,(e-69)/12)}scheduleNote(e,t,s,i,n){const a=this.layers.get(e);if(!a)return;const o=this.audioContext.createOscillator(),r=this.audioContext.createGain(),c=this.midiToFrequency(t);switch(o.frequency.value=c,e){case"bass":o.type="square";break;case"drone":o.type="sawtooth";break;case"ostinato":o.type="triangle";break;case"melody":o.type="triangle";break;case"texture":case"accents":o.type="sine";break}o.connect(r),r.connect(a.filterNode);const h=i*.5,l=Math.max(.005,Math.min(.05,s*.1)),u=Math.max(.01,Math.min(.1,s*.3));r.gain.setValueAtTime(0,n),r.gain.linearRampToValueAtTime(h,n+l),r.gain.setValueAtTime(h,n+Math.max(l,s-u)),r.gain.exponentialRampToValueAtTime(.001,n+s+u),o.start(n),o.stop(n+s+u+.01),setTimeout(()=>{try{o.disconnect(),r.disconnect()}catch{}},(s+u+.1)*1e3)}scheduleEvents(){if(!this.isPlaying)return;const t=this.audioContext.currentTime+this.config.lookaheadTime;for(const[s,i]of this.roleAssignments)this.scheduleRoleEvents(s,i,t)}scheduleRoleEvents(e,t,s){const i=t.events,n=t.chords;!i.length&&!n.length||((e==="drone"||e==="texture")&&n.length>0?this.scheduleChordEvents(e,n,s):this.scheduleSingleEvents(e,i,s))}scheduleChordEvents(e,t,s){let i=this.nextEventIndex.get(e)||0;for(;i<t.length;){const n=t[i],a=this.startTime+n.time;if(a>s)break;a>=this.audioContext.currentTime&&this.scheduleChord(e,n.pitches,Math.max(.05,n.duration),n.velocity,a),i++}this.nextEventIndex.set(e,i),i>=t.length&&(this.nextEventIndex.set(e,0),Array.from(this.nextEventIndex.values()).every(n=>n===0)&&(this.startTime=this.audioContext.currentTime))}scheduleSingleEvents(e,t,s){let i=this.nextEventIndex.get(e)||0;for(;i<t.length;){const n=t[i],a=this.startTime+n.time;if(a>s)break;a>=this.audioContext.currentTime&&this.scheduleNote(e,n.pitch,Math.max(.05,n.duration),n.velocity,a),i++}this.nextEventIndex.set(e,i),i>=t.length&&(this.nextEventIndex.set(e,0),Array.from(this.nextEventIndex.values()).every(n=>n===0)&&(this.startTime=this.audioContext.currentTime))}fadeOutAllLayers(){const e=this.config.fadeTime,t=this.audioContext.currentTime;for(const s of this.layers.values())s.gainNode.gain.cancelScheduledValues(t),s.gainNode.gain.setValueAtTime(s.gainNode.gain.value,t),s.gainNode.gain.linearRampToValueAtTime(0,t+e)}scheduleChord(e,t,s,i,n){const a=this.layers.get(e);if(a)for(const o of t){const r=this.audioContext.createOscillator(),c=this.audioContext.createGain(),h=this.midiToFrequency(o);switch(r.frequency.value=h,e){case"bass":r.type="square";break;case"drone":r.type="sawtooth";break;case"ostinato":r.type="triangle";break;case"texture":r.type="sine";break;case"melody":r.type="triangle";break;case"accents":r.type="sine";break}r.connect(c),c.connect(a.filterNode);const l=i*.3/Math.max(t.length*.5,1),u=Math.max(.005,Math.min(.05,s*.1)),m=Math.max(.01,Math.min(.1,s*.3));c.gain.setValueAtTime(0,n),c.gain.linearRampToValueAtTime(l,n+u),c.gain.setValueAtTime(l,n+Math.max(u,s-m)),c.gain.exponentialRampToValueAtTime(.001,n+s+m),r.start(n),r.stop(n+s+m+.01),setTimeout(()=>{try{r.disconnect(),c.disconnect()}catch{}},(s+m+.1)*1e3)}}cleanupLayers(){for(const e of this.layers.values()){for(const t of e.oscillators)try{t.stop(),t.disconnect()}catch{}e.gainNode.disconnect(),e.filterNode.disconnect()}this.layers.clear()}static async renderOffline(e,t,s=44100){let i=0;for(const h of e){for(const l of h.events){const u=l.time+l.duration;i=Math.max(i,u)}for(const l of h.chords){const u=l.time+l.duration;i=Math.max(i,u)}}const n=i+.5,a=Math.ceil(n*s),o=new OfflineAudioContext(2,a,s),r=o.createGain();r.connect(o.destination),r.gain.value=.3;let c=1/0;for(const h of e)h.events.length>0&&(c=Math.min(c,h.events[0].time)),h.chords.length>0&&(c=Math.min(c,h.chords[0].time));if(c!==1/0&&c>0)for(const h of e){for(const l of h.events)l.time-=c;for(const l of h.chords)l.time-=c}for(const h of e){const{role:l}=h;let{events:u,chords:m}=h;const p=800;if(u.length>p){const f=u.length/p;u=Array.from({length:p},(I,x)=>u[Math.round(x*f)])}const v=400;if(m.length>v){const f=m.length/v;m=Array.from({length:v},(I,x)=>m[Math.round(x*f)])}const{filterNode:T}=g.createOfflineLayer(o,r,l);if((l==="drone"||l==="texture")&&m.length>0)for(const f of m)g.scheduleOfflineChord(o,T,l,f.pitches,Math.max(.05,f.duration),f.velocity,f.time);else for(const f of u)g.scheduleOfflineNote(o,T,l,f.pitch,Math.max(.05,f.duration),f.velocity,f.time)}return o.startRendering()}static createOfflineLayer(e,t,s){const i=e.createGain(),n=e.createBiquadFilter();switch(n.connect(i),i.connect(t),s){case"bass":i.gain.value=.4;break;case"drone":i.gain.value=.2;break;case"ostinato":i.gain.value=.3;break;case"texture":i.gain.value=.1;break;case"accents":i.gain.value=.5;break;case"melody":i.gain.value=.35;break;default:i.gain.value=.3}switch(s){case"bass":n.type="lowpass",n.frequency.value=200;break;case"drone":n.type="bandpass",n.frequency.value=400;break;case"ostinato":n.type="highpass",n.frequency.value=300;break;case"texture":n.type="bandpass",n.frequency.value=800;break;case"accents":n.type="peaking",n.frequency.value=1e3;break;case"melody":n.type="lowpass",n.frequency.value=4e3;break}return{gainNode:i,filterNode:n}}static midiToFreq(e){return 440*Math.pow(2,(e-69)/12)}static getOscillatorType(e){switch(e){case"bass":return"square";case"drone":return"sawtooth";
1case"ostinato":return"triangle";case"melody":return"triangle";case"texture":return"sine";case"accents":return"sine";default:return"sine"}}static scheduleOfflineNote(e,t,s,i,n,a,o){const r=e.createOscillator(),c=e.createGain();r.frequency.value=g.midiToFreq(i),r.type=g.getOscillatorType(s),r.connect(c),c.connect(t);const h=a*.5,l=Math.max(.005,Math.min(.05,n*.1)),u=Math.max(.01,Math.min(.1,n*.3));c.gain.setValueAtTime(0,o),c.gain.linearRampToValueAtTime(h,o+l),c.gain.setValueAtTime(h,o+Math.max(l,n-u)),c.gain.exponentialRampToValueAtTime(.001,o+n+u),r.start(o),r.stop(o+n+u+.01)}static scheduleOfflineChord(e,t,s,i,n,a,o){for(const r of i){const c=e.createOscillator(),h=e.createGain();c.frequency.value=g.midiToFreq(r),c.type=g.getOscillatorType(s),c.connect(h),h.connect(t);const l=a*.3/Math.max(i.length*.5,1),u=Math.max(.005,Math.min(.05,n*.1)),m=Math.max(.01,Math.min(.1,n*.3));h.gain.setValueAtTime(0,o),h.gain.linearRampToValueAtTime(l,o+u),h.gain.setValueAtTime(l,o+Math.max(u,n-m)),h.gain.exponentialRampToValueAtTime(.001,o+n+m),c.start(o),c.stop(o+n+m+.01)}}}class P{constructor(){d(this,"audioContext",null);d(this,"config");d(this,"midiProcessor");d(this,"midiService");d(this,"roleMapper");d(this,"synthesisEngine",null);d(this,"lastInputEvents",[]);d(this,"lastTransformMode","passthrough");d(this,"lastGeneratedEvents",[]);this.config={lookaheadTime:.1,scheduleInterval:25,fadeTime:.05,maxOscillators:8},this.midiProcessor=new b,this.midiService=new A,this.roleMapper=new C}async generateFromMIDI(e,t="passthrough"){if(this.audioContext||(this.audioContext=await M()),this.synthesisEngine=new g(this.audioContext,this.config),this.lastInputEvents=[],this.lastGeneratedEvents=[],t==="passthrough"){const s=[{role:"melody",sourceTrack:0,events:e,chords:[],confidence:1,features:{medianPitch:60,pitchRange:48,noteDensity:1,polyphonyRatio:.5,averageDuration:.5,repetitionScore:.5,isMonophonic:!1,hasPhraseContinuity:!0,register:"mid"}}];this.synthesisEngine.setupLayers(s),this.lastInputEvents=e.map(i=>({...i})),this.lastTransformMode=t,this.lastGeneratedEvents=s.flatMap(i=>i.events.map(n=>({...n}))),console.log("Motif: Passthrough mode - playing original MIDI patterns")}else{const s=this.midiProcessor.extractFeatures(e),i=this.roleMapper.assignRoles(s,e);this.synthesisEngine.setupLayers(i),this.lastInputEvents=e.map(n=>({...n})),this.lastTransformMode=t,this.lastGeneratedEvents=i.flatMap(n=>n.events.map(a=>({...a}))),console.log("Motif: Procedural mode - transforming MIDI with role mapping")}}async generateFromSong(e){let t;try{console.log(`Searching for MIDI: ${e}`);const n=await this.midiService.search(e);if(n.length>0){const a=n[0];console.log(`Attempting to fetch: ${a.title} (${a.confidence})`);const o=await this.midiService.fetchMIDI(a.midiUrl);if(o)t=w.parseMIDI(o),console.log(`Successfully parsed MIDI with ${t.length} events`);else throw new Error("Failed to fetch MIDI")}else throw new Error("No MIDI results found")}catch(n){console.warn(`MIDI search/fetch failed: ${n}. Falling back to synthetic.`),t=this.generateSyntheticMIDI(e)}const s=this.midiProcessor.extractFeatures(t),i=this.roleMapper.assignRoles(s,t);this.audioContext||(this.audioContext=await M()),this.synthesisEngine=new g(this.audioContext,this.config),this.synthesisEngine.setupLayers(i)}async play(){if(!this.audioContext||!this.synthesisEngine)throw new Error("No audio generated yet");this.audioContext.state==="suspended"&&await this.audioContext.resume(),this.synthesisEngine.start()}stop(){this.synthesisEngine&&this.synthesisEngine.stop()}setVolume(e){this.synthesisEngine&&this.synthesisEngine.setVolume(e)}connectOutput(e){this.synthesisEngine&&this.synthesisEngine.connectOutput(e)}disconnectOutput(e){this.synthesisEngine&&this.synthesisEngine.disconnectOutput(e)}seek(e){this.synthesisEngine&&this.synthesisEngine.seek(e)}getProgress(){return this.synthesisEngine?this.synthesisEngine.getProgress():0}getCurrentTime(){return this.synthesisEngine?this.synthesisEngine.getCurrentTime():0}getDuration(){return this.synthesisEngine?this.synthesisEngine.getDuration():0}hasGeneratedContent(){return this.lastInputEvents.length>0&&this.lastGeneratedEvents.length>0}exportMotifMIDIBytes(){if(!this.hasGeneratedContent())throw new Error("No generated motif to export");return G(this.lastGeneratedEvents,480,120)}async exportMotifWAVBytes(e=44100){if(!this.hasGeneratedContent())throw new Error("No generated motif to export");const t=await this.renderOffline(this.lastInputEvents.map(s=>({...s})),this.lastTransformMode,e);return V(t)}async renderOffline(e,t="passthrough",s=44100){let i;if(t==="passthrough")i=[{role:"melody",sourceTrack:0,events:[...e],chords:[],confidence:1,features:{medianPitch:60,pitchRange:48,noteDensity:1,polyphonyRatio:.5,averageDuration:.5,repetitionScore:.5,isMonophonic:!1,hasPhraseContinuity:!0,register:"mid"}}];else{const n=e.map(o=>({...o})),a=this.midiProcessor.extractFeatures(n);i=this.roleMapper.assignRoles(a,n)}return console.log(`Motif: Offline rendering in ${t} mode`),g.renderOffline(i,this.config,s)}generateSyntheticMIDI(e){const t=this.simpleHash(e),s=[],i=32,n=(120+t%60)/60;for(let a=0;a<i*n;a+=1)a%4===0&&s.push({time:a/n,duration:.5,pitch:36+t%12,velocity:.7+t%3*.1,track:0});s.push({time:0,duration:i,pitch:48+t*3%12,velocity:.3,track:1});for(let a=0;a<20;a++)s.push({time:t*a%i,duration:.2+t*a%10*.1,pitch:60+t*a%24,velocity:.2+t*a%5*.1,track:2});return s.sort((a,o)=>a.time-o.time)}simpleHash(e){let t=0;for(let s=0;s<e.length;s++){const i=e.charCodeAt(s);t=(t<<5)-t+i,t=t&t}return Math.abs(t)}}export{P as M}; 2//# sourceMappingURL=MotifEngine-BcRjXeCQ.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.