playlist-data-engine 1.5.8 → 1.5.9

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.
@@ -50,7 +50,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50
50
  ${t(i)}
51
51
  ${t(e)}
52
52
  ${t(a)}`)}return{easy:i,medium:e,hard:a,natural:C}}generateAtDensity(A,B,I,Q,E){this.currentUnifiedBeatMap=I;const i=I?.quarterNoteBpm??120,e=I?.duration??120;this.calculateDensity(A.beats,e);const a=cd(B,i),C=Gd(a,i);let o=B.targetDensity,t=!1;B.targetDensity>C&&(o=C,t=!0,console.warn(`[DifficultyVariantGenerator] Target density ${B.targetDensity.toFixed(2)} nps exceeds max achievable ${C.toFixed(2)} nps for grid types [${a.join(", ")}] at ${i} BPM. Clamping to ${o.toFixed(2)} nps.`));const{beats:s,gridLock:n}=this.lockGridPerBeatIndex(A.beats,"custom",i,E,a),r=this.calculateDensity(s,e),c=1e-10,G=Math.max(o,1e-10),D=s.some(h=>!a.includes(h.gridType));if(this.config.logConversions&&console.log(`[DifficultyVariantGenerator] generateAtDensity: current=${r.toFixed(2)} nps, target=${G.toFixed(2)} nps, maxGridType=${B.maxGridType}, allowedTypes=[${a.join(", ")}], clamped=${t}`),r>G*(1+c)){const h=this.simplifyBeats(s,"custom",A.quarterNoteInterval,!1,Q,i,n,e,a,o);return{difficulty:"custom",beats:this.enforceSingleGridPerBeat(h.beats),isUnedited:!1,editType:"simplified",editAmount:h.metadata.totalBeatsBefore>0?(h.metadata.totalBeatsBefore-h.metadata.totalBeatsAfter)/h.metadata.totalBeatsBefore:0,conversionMetadata:h.metadata,densityClamped:t}}else if(r<o*(1-c)){const h=this.enhanceBeats(s,"custom",i,I,Q,E,A.quarterNoteInterval,n,a,o),w=this.enforceSingleGridPerBeat(h.beats),y=h.metadata.patternsInserted>0?"pattern_inserted":"interpolated";return{difficulty:"custom",beats:w,isUnedited:!1,editType:y,editAmount:h.metadata.totalBeatsBefore>0?(h.metadata.totalBeatsAfter-h.metadata.totalBeatsBefore)/h.metadata.totalBeatsBefore:0,patternsInserted:h.metadata.insertedPatternIds.length>0?h.metadata.insertedPatternIds:void 0,enhancementMetadata:h.metadata}}else{if(D){this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Density approximately equal (${r.toFixed(2)} ≈ ${o.toFixed(2)}), but grid restrictions needed`);const w=this.simplifyBeats(s,"custom",A.quarterNoteInterval,!1,Q,i,n,e,a,o);return{difficulty:"custom",beats:this.enforceSingleGridPerBeat(w.beats),isUnedited:w.metadata.beatsRemoved===0&&!t,editType:w.metadata.beatsRemoved>0?"simplified":"none",editAmount:w.metadata.totalBeatsBefore>0?(w.metadata.totalBeatsBefore-w.metadata.totalBeatsAfter)/w.metadata.totalBeatsBefore:0,conversionMetadata:w.metadata,densityClamped:t}}return{difficulty:"custom",beats:this.enforceSingleGridPerBeat(s),isUnedited:!t,editType:"none",editAmount:0}}}generateAtDensities(A,B,I,Q,E){const i=new Map;for(const{label:e,config:a}of B){const C={...A,beats:A.beats.map(t=>({...t}))},o=this.generateAtDensity(C,a,I,Q,E);i.set(e,o)}return i}validateVariant(A,B){const I=hd(A.beats,B);if(I.isValid)this.config.logConversions&&console.log(`[DifficultyVariantGenerator] ${B} variant validated: ${I.totalBeats} beats, no subdivision violations`);else if(console.warn(`[DifficultyVariantGenerator] ${B} variant has ${I.violationCount} subdivision violations out of ${I.totalBeats} beats. This indicates a bug in the variant generation logic.`),this.config.logConversions){for(const Q of I.violations.slice(0,5))console.warn(` - Beat at index ${Q.beat.beatIndex}, position ${Q.beat.gridPosition}, grid: ${Q.gridType} (suggested: ${Q.suggestedConversion})`);I.violations.length>5&&console.warn(` ... and ${I.violations.length-5} more violations`)}}validateGridLockResult(A,B){const I=this.validateSingleGridPerBeat(A);if(!I.isValid&&(console.warn(`[DifficultyVariantGenerator] ${B} variant has ${I.violations.length} grid lock violations (mixed grid types at same beat index). This indicates a bug in the grid lock implementation.`),this.config.logConversions)){for(const Q of I.violations.slice(0,5))console.warn(` - Beat index ${Q.beatIndex} has mixed grids: ${Q.gridTypes.join(", ")}`);I.violations.length>5&&console.warn(` ... and ${I.violations.length-5} more violations`)}}validateDensityInRange(A,B){const I=this.calculateDensity(A.beats,B),Q=vB[A.difficulty].targetDensityRange,E=I>=Q.min&&(Q.max===1/0||I<=Q.max);if(E){if(this.config.logConversions){const i=Q.max===1/0?"∞":Q.max.toFixed(2);console.log(`[DifficultyVariantGenerator] ${A.difficulty} variant density ${I.toFixed(2)} nps is within target range [${Q.min.toFixed(2)}, ${i}] ✓`)}}else{const i=Q.max===1/0?"∞":Q.max.toFixed(2);console.warn(`[DifficultyVariantGenerator] ${A.difficulty} variant density ${I.toFixed(2)} nps is OUTSIDE target range [${Q.min.toFixed(2)}, ${i}]. This may occur for edge cases (short songs, extreme tempos).`)}return{inRange:E,density:I,targetRange:Q,difficulty:A.difficulty}}generateVariant(A,B,I,Q,E,i){const e=B===I,a=Q?.quarterNoteBpm??120,C=Q?.duration??120,o=ng(B,a),{beats:t,gridLock:s}=this.lockGridPerBeatIndex(A.beats,B,a,i);if(e){if(t.every(D=>o.includes(D.gridType))){const{targetCount:D}=this.calculateBeatCountTarget(t.length,C,B);if(t.length<D){const h=this.enhanceBeats(t,B,a,Q,E,i,A.quarterNoteInterval,s),w=h.metadata.patternsInserted>0?"pattern_inserted":"interpolated";return{difficulty:B,beats:h.beats,isUnedited:!1,editType:w,editAmount:h.metadata.totalBeatsBefore>0?(h.metadata.totalBeatsAfter-h.metadata.totalBeatsBefore)/h.metadata.totalBeatsBefore:0,enhancementMetadata:h.metadata}}return{difficulty:B,beats:[...t],isUnedited:!0,editType:"none",editAmount:0}}const G=this.simplifyBeats(t,B,A.quarterNoteInterval,!1,E,a,s,C);return{difficulty:B,beats:G.beats,isUnedited:!1,editType:"simplified",editAmount:G.metadata.totalBeatsBefore>0?(G.metadata.totalBeatsBefore-G.metadata.totalBeatsAfter)/G.metadata.totalBeatsBefore:0,conversionMetadata:G.metadata}}const n=this.needsSimplification(B,I),r=this.needsEnhancement(B,I);if(n){const c=this.isHeavySimplification(B,I),G=this.simplifyBeats(t,B,A.quarterNoteInterval,c,E,a,s,C),{targetCount:D}=this.calculateBeatCountTarget(G.beats.length,C,B);if(G.beats.length<D-1){const h=this.enhanceBeats(G.beats,B,a,Q,E,i,A.quarterNoteInterval,s),w=h.metadata.patternsInserted>0?"pattern_inserted":"interpolated";return{difficulty:B,beats:h.beats,isUnedited:!1,editType:w,editAmount:1,conversionMetadata:G.metadata,enhancementMetadata:h.metadata}}return{difficulty:B,beats:G.beats,isUnedited:!1,editType:"simplified",editAmount:G.metadata.totalBeatsBefore>0?(G.metadata.totalBeatsBefore-G.metadata.totalBeatsAfter)/G.metadata.totalBeatsBefore:0,conversionMetadata:G.metadata}}if(r){const c=this.enhanceBeats(t,B,a,Q,E,i,A.quarterNoteInterval,s);let G=c.beats;if(G.some(w=>!o.includes(w.gridType))){const w=[];for(const y of G)if(o.includes(y.gridType))w.push(y);else{const R=this.convertBeatGridType(y,B,A.quarterNoteInterval,a);R&&w.push(R)}G=this.deduplicateConvertedBeats(w)}const h=c.metadata.patternsInserted>0?"pattern_inserted":"interpolated";return{difficulty:B,beats:G,isUnedited:!1,editType:h,editAmount:c.metadata.totalBeatsBefore>0?(c.metadata.totalBeatsAfter-c.metadata.totalBeatsBefore)/c.metadata.totalBeatsBefore:0,patternsInserted:c.metadata.insertedPatternIds.length>0?c.metadata.insertedPatternIds:void 0,enhancementMetadata:c.metadata}}return{difficulty:B,beats:[...t],isUnedited:!0,editType:"none",editAmount:0}}simplifyBeats(A,B,I,Q=!1,E,i=120,e,a=120,C,o){const t={sixteenthToEighth:0,tripletToQuarterTriplet:0,eighthToQuarter:0,beatsRemoved:0,totalBeatsBefore:A.length,totalBeatsAfter:0},s=this.buildPhraseMembershipMap(E),n=e?A:this.enforceSingleGridPerBeat(A);t.totalBeatsBefore=n.length;const r=C??ng(B,i);if(n.every(W=>r.includes(W.gridType))&&!Q){const W=o!==void 0?Math.round(o*a):this.calculateBeatCountTarget(n.length,a,B).targetCount,_=this.reduceDensityToTarget(n,B,t,s,i,W,a);return t.totalBeatsAfter=_.length,{beats:_,metadata:t}}let G=n;Q&&(G=this.filterBeatsForHeavySimplification(n,t,s));const D=[];for(const W of G){if(r.includes(W.gridType)){D.push(W);continue}const _=e?.get(W.beatIndex),X=this.convertBeatGridType(W,B,I,i,_,r);if(X)if(D.push(X),W.gridType==="straight_16th"){if(t.sixteenthToEighth++,this.config.logConversions){const z=X.gridType==="straight_4th"?"quarter":"8th";console.log(`[DifficultyVariantGenerator] Converted 16th note at beat ${W.beatIndex} position ${W.gridPosition} to ${z} note`)}}else W.gridType==="triplet_8th"?(t.tripletToQuarterTriplet++,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Converted 8th triplet at beat ${W.beatIndex} position ${W.gridPosition} to quarter triplet`)):W.gridType==="straight_8th"&&(t.eighthToQuarter++,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Converted 8th note at beat ${W.beatIndex} position ${W.gridPosition} to quarter note`));else t.beatsRemoved++,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Removed beat at beat ${W.beatIndex} position ${W.gridPosition} (grid: ${W.gridType})`)}const h=this.deduplicateConvertedBeats(D);t.beatsRemoved+=D.length-h.length;const w=D.length-h.length,y=this.calculateDensity(h,a),R=o!==void 0?Math.round(o*a):this.calculateBeatCountTarget(h.length,a,B).targetCount,p=o??this.calculateBeatCountTarget(h.length,a,B).targetDensity;if(h.length<=R)return this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Grid conversion achieved target density: ${y.toFixed(2)} notes/sec (target midpoint ${p.toFixed(2)} nps, ${R} beats) for ${B}`+(w>0?` (${w} beats collapsed)`:"")),t.totalBeatsAfter=h.length,{beats:h,metadata:t};const k=this.reduceDensityToTarget(h,B,t,s,i,R,a);return t.totalBeatsAfter=k.length,{beats:k,metadata:t}}filterBeatsForHeavySimplification(A,B,I=new Map){const Q=this.config.heavySimplificationIntensityThreshold,E=[];for(const i of A){const e=this.isStrongBeat(i.beatIndex),a=i.gridPosition===0,C=i.gridPosition===1||i.gridPosition===3,o=this.shouldPreserveForPhrase(i,I,Q);e||o||a&&i.intensity>=Q*.7||C&&i.intensity>=Q||i.gridPosition===2&&i.intensity>=Q*.8?E.push(i):(B.beatsRemoved++,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Heavy simplification: removed beat at beat ${i.beatIndex} position ${i.gridPosition} (intensity: ${i.intensity.toFixed(2)}, strongBeat: ${e})`))}return E}isStrongBeat(A){const B=this.config.rhythmicBalanceConfig?.strongBeatEmphasis??"natural";if(B==="neutral")return!1;const I=this.currentUnifiedBeatMap;if(!I){const a=A%4;return a===0||a===2}const Q=I.beats[A];if(!Q){const a=A%4;return a===0||a===2}const E=I.downbeatConfig?.segments;if(!E||E.length===0){const a=A%4;return a===0||a===2}const e=Cd(E,A).timeSignature.beatsPerMeasure;return od(Q.beatInMeasure,e,B)}calculateDensity(A,B){return A.length===0||B<=0?0:A.length/B}calculateBeatCountTarget(A,B,I){const Q=vB[I].targetDensityRange;if(I==="natural"||I==="custom"){const s=B>0?A/B:0;return{targetCount:A,maxCount:A,minCount:A,targetDensity:s}}const i={easy:.9,medium:1.25,hard:1.75}[I];let e;switch(this.config.densityTargetStrategy){case"lower":e=i*.75+Q.min*.25;break;case"upper":{const s=Q.max===1/0?i*1.5:Q.max;e=i*.75+s*.25;break}default:e=i;break}const a=Q.max===1/0?e:Q.max;e=Math.max(Q.min,Math.min(a,e));const C=Math.round(Q.min*B),o=Q.max===1/0?1/0:Math.round(Q.max*B);return{targetCount:Math.round(e*B),maxCount:o,minCount:C,targetDensity:e}}calculateBeatsToAdd(A,B,I,Q){const E=this.calculateBeatCountTarget(A,B,Q),i=Math.max(0,E.targetCount-A),e=ng(Q,I),a=this.getMaxBeatsPerIndexFromGridTypes(e);return{beatsToAdd:i,targetCount:E.targetCount,maxBeatsPerIndex:a}}getMaxBeatsPerIndexFromGridTypes(A){let B=1;for(const I of A){const Q=Zo[I];Q>B&&(B=Q)}return B}getMaxBeatsForGridType(A){return Zo[A]??1}distributeBeatsAcrossIndices(A,B,I,Q,E,i){const e=new Map;let a=A;for(let n=0;n<=Q;n++){const r=B.get(n)??[];e.set(n,r.length)}if(a<=0)return e;const C=n=>{const r=I.get(n);return r?i&&!i.includes(r)?this.getMaxBeatsPerIndexFromGridTypes(i):this.getMaxBeatsForGridType(r):i?this.getMaxBeatsPerIndexFromGridTypes(i):4},o=[],t=[];for(let n=0;n<=Q;n++){const r=e.get(n)??0,c=C(n);r===0?o.push(n):r<c&&t.push(n)}const s=this.groupConsecutiveEmptyIndices(o);s.sort((n,r)=>{const c=n.length-r.length;if(c!==0)return c;const G=mg(Pg(E,`gap:${n[0]}`)),D=mg(Pg(E,`gap:${r[0]}`));return G-D});for(const n of s){if(a<=0)break;const r=n.length<=2;for(const c of n){if(a<=0)break;const G=C(c);let D;r?D=Math.min(G,a,2):D=Math.min(G,a,1),D>0&&(e.set(c,D),a-=D)}}t.sort((n,r)=>{const c=e.get(n)??0,G=e.get(r)??0,D=C(n),h=C(r),w=D-c,R=h-G-w;if(R!==0)return R;const p=mg(Pg(E,`partial:${n}`)),k=mg(Pg(E,`partial:${r}`));return p-k});for(const n of t){if(a<=0)break;const r=e.get(n)??0,G=C(n)-r;if(G>0){const D=Math.min(G,a);e.set(n,r+D),a-=D}}if(a>0)for(const n of o){if(a<=0)break;const r=e.get(n)??0,G=C(n)-r;if(G>0){const D=Math.min(G,a);e.set(n,r+D),a-=D}}return e}groupConsecutiveEmptyIndices(A){if(A.length===0)return[];const B=[];let I=[A[0]];for(let Q=1;Q<A.length;Q++){const E=A[Q-1],i=A[Q];i===E+1?I.push(i):(B.push(I),I=[i])}return B.push(I),B}reduceDensityToTarget(A,B,I,Q=new Map,E=120,i,e=120){const a=vB[B].targetDensityRange,C=this.calculateDensity(A,e);if(i!==void 0&&A.length<=i)return this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Density ${C.toFixed(2)} notes/sec already at or below target midpoint (${i} beats) for ${B}`),A;if(C<=a.max&&i===void 0)return this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Density ${C.toFixed(2)} notes/sec already within target range [${a.min}, ${a.max}] for ${B}`),A;this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Reducing density from ${C.toFixed(2)} notes/sec to target max ${a.max} notes/sec for ${B}`);const o=this.buildDownbeatCountMap(A),t=A.map(y=>({beat:y,priority:this.calculateRemovalPriority(y,Q,o)}));t.sort((y,R)=>y.priority-R.priority);const s=new Set;let n=A.length;const r=this.config.maxReductionPasses;let c=0;for(let y=1;y<=r;y++){c=y;let R=0;const p=y===r;let k,W,_=!1;y===1?(k=1-this.config.densityReductionMinIntensity,W=this.config.moderateSimplificationIntensityThreshold):y===2?(k=1-this.config.densityReductionMinIntensity-.15,W=this.config.moderateSimplificationIntensityThreshold-.1):(_=!0,k=0,W=0);for(const{beat:X,priority:z}of t)if(!s.has(X)){if(i!==void 0){if(n-1<=i)break}else if((n-1)/e<=a.max)break;if(_){if(this.isStrongBeat(X.beatIndex))continue}else if(z>=k||X.intensity>=W)continue;s.add(X),n--,R++,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Pass ${y}: Removed beat at index ${X.beatIndex} position ${X.gridPosition} (priority: ${z.toFixed(2)}, intensity: ${X.intensity.toFixed(2)})`)}if(this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Pass ${y}${p?" (final)":""}: Removed ${R} beats, density now: ${(n/e).toFixed(2)} notes/sec`),i!==void 0){if(n<=i)break}else if(n/e<=a.max)break}const G=n/e;if((i!==void 0?n>i:G>a.max)&&this.config.logConversions){const y=i!==void 0?`${i} beats (midpoint ${(i/e).toFixed(2)} nps)`:`${a.max} notes/sec`;console.warn(`[DifficultyVariantGenerator] Could not reach target density ${y} for ${B} after ${r} passes. Final density: ${G.toFixed(2)} notes/sec (${n} beats remaining)`)}const h=A.filter(y=>!s.has(y));I.reductionPasses=c,I.beatsRemoved+=s.size;const w=Math.ceil(a.min*e);if(h.length<w&&A.length>0){const y=t.filter(({beat:R})=>s.has(R)).sort((R,p)=>p.priority-R.priority);for(const{beat:R}of y){if(h.length>=w)break;h.push(R),I.beatsRemoved--}}return h}buildDownbeatCountMap(A){const B=new Map,I=this.currentUnifiedBeatMap;for(const Q of A){if(Q.gridPosition!==0)continue;let E;I?.beats[Q.beatIndex]?E=I.beats[Q.beatIndex].measureNumber:E=Math.floor(Q.beatIndex/4),B.set(E,(B.get(E)??0)+1)}return B}calculateRemovalPriority(A,B,I=new Map){const Q=this.config.rhythmicBalanceConfig?.strongBeatEmphasis??"natural";let E=.5;if(Q!=="neutral"&&this.isStrongBeat(A.beatIndex)&&(E+=.3),A.gridPosition===0){E+=.2;const e=this.currentUnifiedBeatMap;let a;e?.beats[A.beatIndex]?a=e.beats[A.beatIndex].measureNumber:a=Math.floor(A.beatIndex/4),(I.get(a)??0)===1&&(E+=.2)}E+=A.intensity*.3;const i=B.get(A.beatIndex);if(i&&i.length>0){const e=Math.max(...i.map(a=>a.significance));E+=Math.min(.15,e*.05)}return(A.gridPosition===1||A.gridPosition===3)&&(E-=.1),Math.max(0,Math.min(1,E))}convertBeatGridType(A,B,I,Q=120,E,i){const e=E??A.gridType,a=Vi(e,B,Q,i);if(a===e)return{...A,gridType:a};let C,o,t;switch(e){case"straight_16th":{const s=I/4;if(t=A.timestamp-A.gridPosition*s,a==="straight_4th")C=0,o=t;else{A.gridPosition===1||A.gridPosition===3?C=A.gridPosition===1?0:2:C=A.gridPosition;const n=I/2;o=t+(C===0?0:n)}break}case"triplet_8th":{const s=I/3;t=A.timestamp-A.gridPosition*s,C=0,o=t;break}case"straight_8th":{const s=I/2;t=A.timestamp-A.gridPosition*s,C=0,o=t;break}default:return{...A,gridType:A.gridType}}return{...A,gridType:a,gridPosition:C,timestamp:o}}deduplicateConvertedBeats(A){const B=new Map;for(const I of A){const Q=`${I.beatIndex}:${I.gridPosition}:${I.gridType}`,E=B.get(Q);(!E||I.intensity>E.intensity)&&B.set(Q,I)}return Array.from(B.values()).sort((I,Q)=>I.timestamp-Q.timestamp)}needsSimplification(A,B){const I=["easy","medium","hard"],Q=I.indexOf(A),E=I.indexOf(B);return Q<E}needsEnhancement(A,B){const I=["easy","medium","hard"],Q=I.indexOf(A),E=I.indexOf(B);return Q>E}isHeavySimplification(A,B){return B==="hard"&&A==="easy"}enhanceBeats(A,B,I,Q,E,i,e=.5,a,C,o){const t=Q?.duration??120,s={totalBeatsBefore:A.length,totalBeatsAfter:0,patternsInserted:0,interpolatedBeats:0,insertedPatternIds:[]},n=X=>{if(C){if(C.includes(X))return X;const z=["straight_16th","triplet_8th","straight_8th","quarter_triplet","straight_4th"],QA=z.indexOf(X);for(let tA=QA+1;tA<z.length;tA++)if(C.includes(z[tA]))return z[tA];return C[C.length-1]??X}return Vi(X,B,I)};if(A.length===0)return s.totalBeatsAfter=0,{beats:[],metadata:s};let r=a?A:this.enforceSingleGridPerBeat(A);if(C&&r.some(z=>!C.includes(z.gridType))){const z=[];for(const tA of r)if(C.includes(tA.gridType))z.push(tA);else{const dA=this.convertBeatGridType(tA,B,e,I,void 0,C);dA&&z.push(dA)}r=this.deduplicateConvertedBeats(z)}s.totalBeatsBefore=r.length;const c=this.groupBeatsByIndex(r),G=Math.max(...Array.from(c.keys()));let D;if(o!==void 0){const X=Math.round(o*t);D=Math.max(0,X-r.length);const z=Math.max(0,3*r.length);D=Math.min(D,z)}else D=this.calculateBeatsToAdd(r.length,t,I,B).beatsToAdd;const h=this.config.seed??`enhance:${G}:${r.length}`,w=this.distributeBeatsAcrossIndices(D,c,a??new Map,G,h,C),y=[],R=new Map,p=(X,z)=>`${X}:${z}`;for(const X of r){const z=p(X.beatIndex,X.gridPosition);R.has(z)||R.set(z,X.gridType)}for(let X=0;X<=G;X++){const z=c.get(X)??[],QA=w.get(X)??0;if(z.length>=QA){y.push(...z);continue}const tA=QA-z.length;if(z.length===0){const EA=a?.get(X),ZA=EA?n(EA):void 0,NA=this.createBeatsForEmptyIndex(X,tA,Q,i,e,c,R,ZA),Pa=ZA??this.getGridForBeatIndex(X,NA,i);for(const aA of NA){const SA=p(aA.beatIndex,aA.gridPosition);!R.has(SA)&&aA.gridType===Pa&&(R.set(SA,aA.gridType),y.push(aA),s.interpolatedBeats++)}continue}const dA=a?.get(X)??z[0].gridType,uA=n(dA);let AA=0;if(this.config.preferPatternInsertion&&E){const EA=this.tryInsertPattern(z,X,tA,E,R,uA);AA=EA.beatsAdded,EA.patternId&&(s.patternsInserted++,s.insertedPatternIds.push(EA.patternId))}if(AA<tA){const EA=this.interpolateBeats(z,X,tA-AA,e,R,uA,Q);for(const ZA of EA){const NA=p(ZA.beatIndex,ZA.gridPosition);R.has(NA)||(R.set(NA,ZA.gridType),y.push(ZA),s.interpolatedBeats++)}}y.push(...z)}const k=y.sort((X,z)=>X.beatIndex!==z.beatIndex?X.beatIndex-z.beatIndex:X.gridPosition-z.gridPosition);let W;a?(this.validateSingleGridPerBeat(k).isValid,W=k):W=this.enforceSingleGridPerBeat(k);const _=this.deduplicateEnhancedBeats(W);return s.totalBeatsAfter=_.length,this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Enhanced beats for ${B}: ${s.totalBeatsBefore} → ${s.totalBeatsAfter} beats (patterns: ${s.patternsInserted}, interpolated: ${s.interpolatedBeats})`),{beats:_,metadata:s}}groupBeatsByIndex(A){const B=new Map;for(const I of A){const Q=B.get(I.beatIndex);Q?Q.push(I):B.set(I.beatIndex,[I])}return B}calculateTargetBeatsPerBeat(A,B,I){const Q=new Map;for(let E=0;E<=I;E++){const i=A.get(E);if(i){const e=Math.min(Math.ceil(i.length*B),4);Q.set(E,e)}else{const e=Math.min(Math.ceil(B*.7),4);e>=2&&Q.set(E,e)}}return Q}tryInsertPattern(A,B,I,Q,E,i){const a=Q.patternLibrary.filter(h=>h.sizeInBeats<=this.config.maxPatternInsertionSize&&h.availableForReuse);if(a.length===0)return{beatsAdded:0};const C=a.filter(h=>h.pattern.some(w=>w.gridType===i));if(C.length===0)return{beatsAdded:0};C.sort((h,w)=>w.significance-h.significance);const o=C[0],t=o.pattern.filter(h=>h.beatIndex===0),s=new Set(A.map(h=>h.gridPosition)),n=(h,w)=>`${h}:${w}`,r=t.filter(h=>!(h.gridType!==i||s.has(h.gridPosition)||E.has(n(B,h.gridPosition))));if(r.length===0)return{beatsAdded:0};const c=r.slice(0,I),G=o.sourceBand,D=c.map(h=>({...h,beatIndex:B,band:G,sourceBand:G,intensity:h.intensity*.8}));for(const h of D)E.set(n(h.beatIndex,h.gridPosition),h.gridType);return{beatsAdded:D.length,patternId:o.id}}createBeatsForEmptyIndex(A,B,I,Q,E=.5,i,e,a){let C=a??"straight_16th",o="mid",t="mid";if(!a&&Q){const y=Q.get(A);y&&(C=y.selectedGrid)}if(!a&&!Q?.has(A)&&i)for(const y of[1,-1,2,-2,3,-3]){const R=i.get(A+y);if(R&&R.length>0){C=R[0].gridType,o=R[0].band,t=R[0].sourceBand;break}}const s=new Set;if(e){const y=(R,p)=>`${R}:${p}`;for(let R=0;R<4;R++)e.has(y(A,R))&&s.add(R)}const r={straight_16th:4,straight_8th:2,triplet_8th:3,straight_4th:1,quarter_triplet:1}[C],c=[];for(let y=0;y<r;y++)s.has(y)||c.push(y);c.sort((y,R)=>{const p=k=>k===0?0:k===r-1?2:1;return p(y)-p(R)});const G=c.slice(0,B),D=I?.beats[A]?.timestamp??A*E,w={straight_16th:E/4,straight_8th:E/2,triplet_8th:E/3,straight_4th:E,quarter_triplet:E}[C];return G.map(y=>({timestamp:D+y*w,beatIndex:A,gridPosition:y,gridType:C,intensity:this.config.interpolatedBeatIntensity*.8,band:o,sourceBand:t,quantizationError:0}))}interpolateBeats(A,B,I,Q=.5,E,i,e){if(I<=0||A.length===0)return[];const a=i??A[0].gridType,o=(y=>y==="straight_16th"?4:y==="straight_8th"?2:y==="straight_4th"||y==="quarter_triplet"?1:3)(a),t=(y,R)=>`${y}:${R}`,s=new Set(A.map(y=>y.gridPosition));if(E)for(let y=0;y<o;y++)E.has(t(B,y))&&s.add(y);const n=[];for(let y=0;y<o;y++)s.has(y)||n.push(y);n.sort((y,R)=>(y===0||y===3?2:1)-(R===0||R===3?2:1));const r=n.slice(0,I),c=A[0],G=e?.beats[B]?.timestamp??B*Q,h={straight_16th:Q/4,straight_8th:Q/2,triplet_8th:Q/3,straight_4th:Q,quarter_triplet:Q}[a];return r.map(y=>({timestamp:G+y*h,beatIndex:B,gridPosition:y,gridType:a,intensity:this.config.interpolatedBeatIntensity,band:c.band,sourceBand:c.sourceBand,quantizationError:0}))}getGridForBeatIndex(A,B,I,Q){if(B.length>0)return B[0].gridType;if(I?.has(A))return I.get(A).selectedGrid;if(Q)for(const E of[1,-1,2,-2,3,-3]){const i=Q.get(A+E);if(i&&i.length>0)return i[0].gridType}return"straight_16th"}deduplicateEnhancedBeats(A){const B=new Map;for(const E of A){const i=B.get(E.beatIndex)??new Map;i.set(E.gridType,(i.get(E.gridType)??0)+1),B.set(E.beatIndex,i)}const I=new Map;for(const[E,i]of B){let e="straight_16th",a=0;for(const[C,o]of i)o>a&&(a=o,e=C);I.set(E,e)}const Q=new Map;for(const E of A){const i=`${E.beatIndex}:${E.gridPosition}`,e=Q.get(i),a=I.get(E.beatIndex);e?E.gridType===a&&e.gridType!==a&&Q.set(i,E):Q.set(i,E)}return Array.from(Q.values())}enforceSingleGridPerBeat(A){const B=new Map;for(const Q of A){const E=B.get(Q.beatIndex)??[];E.push(Q),B.set(Q.beatIndex,E)}const I=[];for(const[,Q]of B){if(Q.length<=1){I.push(...Q);continue}const E=new Set(Q.map(C=>C.gridType));if(E.size<=1){I.push(...Q);continue}let i="straight_16th",e=0;for(const C of E){const o=Q.filter(t=>t.gridType===C).reduce((t,s)=>t+s.intensity,0);o>e&&(e=o,i=C)}const a=Q.filter(C=>C.gridType===i);if(this.config.logConversions&&a.length<Q.length){const C=Q.filter(o=>o.gridType!==i);console.log(`[DifficultyVariantGenerator] Enforced single grid at beat ${Q[0].beatIndex}: kept ${i} (${a.length} beats), removed ${C.map(o=>o.gridType).join(", ")} (${C.length} beats)`)}I.push(...a)}return I.sort((Q,E)=>Q.timestamp-E.timestamp)}validateSingleGridPerBeat(A){const B=new Map;for(const Q of A){const E=B.get(Q.beatIndex)??[];E.push(Q),B.set(Q.beatIndex,E)}const I=[];for(const[Q,E]of B){if(E.length<=1)continue;const i=new Set(E.map(e=>e.gridType));i.size>1&&I.push({beatIndex:Q,gridTypes:Array.from(i)})}if(I.length>0&&this.config.logConversions){console.warn(`[DifficultyVariantGenerator] Grid validation found ${I.length} violations. This indicates a bug in grid lock implementation.`);for(const Q of I.slice(0,5))console.warn(` - Beat index ${Q.beatIndex} has mixed grids: ${Q.gridTypes.join(", ")}`)}return{isValid:I.length===0,violations:I}}getConfig(){return{...this.config}}buildPhraseMembershipMap(A){const B=new Map;if(!A||!this.config.preservePhraseBoundaries)return B;const I=A.phrases.filter(Q=>Q.hasVariation);for(const Q of I)for(const E of Q.occurrences)for(let i=0;i<Q.sizeInBeats;i++){const e=E.beatIndex+i,a=B.get(e)??[];a.push(Q),B.set(e,a)}return B}shouldPreserveForPhrase(A,B,I){if(!this.config.preservePhraseBoundaries)return!1;const Q=B.get(A.beatIndex);if(!Q||Q.length===0)return!1;const E=Q.reduce((o,t)=>t.significance>o.significance?t:o),e=A.intensity-I>=-.15,C=E.significance>=1.5;return e&&C?(this.config.logConversions&&console.log(`[DifficultyVariantGenerator] Preserving beat at index ${A.beatIndex} position ${A.gridPosition} (intensity: ${A.intensity.toFixed(2)}) due to phrase membership (phrase significance: ${E.significance.toFixed(2)})`),!0):!1}}const No={casual:{difficulty:"easy",outputMode:"composite",description:"Easy difficulty for relaxed gameplay"},standard:{difficulty:"medium",outputMode:"composite",description:"Balanced experience for most players"},challenge:{difficulty:"hard",outputMode:"composite",description:"Hard difficulty for skilled players"},bass:{difficulty:"medium",outputMode:"low",description:"Focus on bass/low-frequency rhythms"}};function nN(g){return No[g]}function rN(){return Object.keys(No)}const cN={difficulty:"medium",outputMode:"composite",measureStartOffset:0,minimumTransientIntensity:0,transientConfig:void 0,scoringConfig:void 0,phraseAnalyzerConfig:void 0,rhythmicBalanceConfig:Li,seed:void 0,verbose:!1,enableCache:!0,cacheMaxAge:1800*1e3,skipDifficultyVariants:!1};class kg{constructor(A={}){this.cache=new Map,this.cacheHits=0,this.cacheMisses=0;const B={...Li,...A.rhythmicBalanceConfig};this.options={...cN,...A,rhythmicBalanceConfig:B},this.multiBandAnalyzer=new PD,this.transientDetector=new _D({bandConfig:this.options.transientConfig}),this.rhythmQuantizer=new fo({minimumTransientIntensity:this.options.minimumTransientIntensity,densityValidation:this.options.densityValidation}),this.phraseAnalyzer=new Bd(this.options.phraseAnalyzerConfig),this.densityAnalyzer=new gd,this.streamScorer=new Qd(this.options.scoringConfig),this.compositeGenerator=new Ed,this.variantGenerator=new vi({seed:this.options.seed,rhythmicBalanceConfig:B}),this.rhythmicBalancer=new td(this.options.rhythmicBalanceConfig),this.tempoAwareQuantizer=new Ad(this.options.tempoQuantizationConfig??ko,this.rhythmQuantizer)}generateCacheKey(A,B,I){return`${A}:${B}:${I}`}createConfigFingerprint(){const A={minimumTransientIntensity:this.options.minimumTransientIntensity,measureStartOffset:this.options.measureStartOffset,tempoQuantizationConfig:this.options.tempoQuantizationConfig};return JSON.stringify(A)}getFromCache(A){if(!this.options.enableCache)return null;const B=this.cache.get(A);return B?Date.now()-B.timestamp>this.options.cacheMaxAge?(this.cache.delete(A),this.cacheMisses++,null):(this.cacheHits++,this.options.verbose&&console.log(`[RhythmGenerator] Cache HIT for key: ${A}`),B.result):(this.cacheMisses++,null)}setCache(A,B){this.options.enableCache&&(this.cache.set(A,{result:B,timestamp:Date.now(),key:A}),this.options.verbose&&console.log(`[RhythmGenerator] Cache SET for key: ${A}`))}clearCache(){this.cache.clear(),this.cacheHits=0,this.cacheMisses=0}pruneExpiredCache(){const A=Date.now();for(const[B,I]of this.cache.entries())A-I.timestamp>this.options.cacheMaxAge&&this.cache.delete(B)}getCacheStats(){let A=0;const B=[];let I=null;for(const[Q,E]of this.cache.entries()){const i=Q.split(":")[1];B.includes(i)||B.push(i);try{A+=JSON.stringify(E.result).length*2}catch{A+=1e3}(I===null||E.timestamp<I)&&(I=E.timestamp)}return{entryCount:this.cache.size,estimatedSize:A,cachedPhases:B,oldestEntry:I,hits:this.cacheHits,misses:this.cacheMisses}}isCached(A,B){const I=this.createConfigFingerprint(),Q=this.generateCacheKey(A,B,I);return this.cache.has(Q)}getOptions(){return{...this.options}}async generate(A,B,I,Q){const E=(R,p,k)=>{this.options.verbose&&console.log(`[RhythmGenerator] [${R}] ${Math.round(p*100)}%: ${k}`),Q?.(R,p,k)};I?.throwIfAborted();const i=this.generateCacheKey(B.audioId,"variants",this.createConfigFingerprint()),e=this.getFromCache(i);if(e)return E("Cache",1,"Retrieved complete result from cache"),e;E("Phase 1",0,"Starting multi-band analysis"),I?.throwIfAborted();const a=this.analyzeMultiBand(A);E("Phase 1",.2,`Analyzed ${a.bands.size} frequency bands`),I?.throwIfAborted();const C=this.detectTransients(a);E("Phase 1",.4,`Detected ${C.transients.length} transients`),I?.throwIfAborted();const o=this.quantizeTransients(C,B);E("Phase 1",.8,`Quantized to ${o.streams.low.beats.length+o.streams.mid.beats.length+o.streams.high.beats.length} beats across all bands`),E("Phase 1",1,"Phase 1 complete"),I?.throwIfAborted(),E("Phase 2",0,"Starting phrase and density analysis"),I?.throwIfAborted();const t=this.analyzePhrases(o.streams);E("Phase 2",.3,`Detected ${t.phrases.length} rhythmic phrases`),I?.throwIfAborted();const s=this.analyzeDensity(o,B.quarterNoteBpm,B.duration);E("Phase 2",.7,`Density category: ${s.combinedMetrics.densityCategory}`),E("Phase 2",1,"Phase 2 complete"),I?.throwIfAborted(),E("Phase 3",0,"Starting scoring and composite generation"),I?.throwIfAborted();const n=this.scoreStreams(o,t,s,B.quarterNoteBpm);E("Phase 3",.2,`Scored ${n.sectionWinners.length} sections`),I?.throwIfAborted();const r=this.generateComposite(o,n,s,B.duration);E("Phase 3",.4,`Generated composite with ${r.beats.length} beats, natural difficulty: ${r.naturalDifficulty}`),I?.throwIfAborted();const c=this.rhythmicBalancer.balance(r,B),G=c.composite,D=c.stats;E("Phase 3",.6,`Balanced composite: ${G.beats.length} beats (${D.beatsAdded} added, ${D.beatsShifted} shifted for rhythmic structure)`);const h=this.options.skipDifficultyVariants?null:this.generateDifficultyVariants(G,t,o,B);E("Phase 3",.8,this.options.skipDifficultyVariants?"Skipped preset difficulty variants (density-based mode)":"Generated easy/medium/hard difficulty variants"),E("Phase 3",1,"Phase 3 complete"),I?.throwIfAborted();const w={difficulty:this.options.difficulty,bandsAnalyzed:["low","mid","high"],transientsDetected:C.metadata.totalTransients,transientsFilteredByIntensity:o.metadata.transientsFilteredByIntensity,densityValidationRetries:o.metadata.densityValidation.maxRetryCount,phrasesDetected:t.phrases.length,averageDensity:s.combinedMetrics.notesPerSecond,naturalDifficulty:G.naturalDifficulty,generationConfig:this.options,duration:A.duration,totalBeats:B.beats.length,balanceStats:D},y={difficultyVariants:h,bandStreams:o.streams,composite:G,analysis:{transientAnalysis:C,quantizationResult:o,phraseAnalysis:t,densityAnalysis:s,scoringResult:n},metadata:w};return this.setCache(i,y),y}analyzeMultiBand(A){return this.multiBandAnalyzer.analyze(A)}detectTransients(A){return this.transientDetector.detect(A)}quantizeTransients(A,B){return this.rhythmQuantizer.quantize(A,B,(I,Q,E)=>this.tempoAwareQuantizer.decideGrids(I,Q,E))}analyzePhrases(A){return this.phraseAnalyzer.analyze(A)}analyzeDensity(A,B,I){return this.densityAnalyzer.analyze(A,B,I)}scoreStreams(A,B,I,Q){return this.streamScorer.updateConfig({bpm:Q}),this.streamScorer.score(A,B,I)}generateComposite(A,B,I,Q){return this.compositeGenerator.generate(A,B,I,Q)}generateDifficultyVariants(A,B,I,Q){const E=this.collectGridDecisions(I);return this.variantGenerator.generate(A,Q,B,E)}collectGridDecisions(A){const B=new Map,I=[...A.streams.low.gridDecisions,...A.streams.mid.gridDecisions,...A.streams.high.gridDecisions];for(const Q of I){const E=B.get(Q.beatIndex);(!E||Q.confidence>E.confidence)&&B.set(Q.beatIndex,Q)}return B}static async quickGenerate(A,B){return new kg().generate(A,B)}static async generateForDifficulty(A,B,I){return(await new kg().generate(A,B)).difficultyVariants[I]}static toJSON(A){const B={difficultyVariants:A.difficultyVariants?{easy:this.serializeDifficultyVariant(A.difficultyVariants.easy),medium:this.serializeDifficultyVariant(A.difficultyVariants.medium),hard:this.serializeDifficultyVariant(A.difficultyVariants.hard),natural:this.serializeDifficultyVariant(A.difficultyVariants.natural)}:null,bandStreams:{low:this.serializeGeneratedRhythmMap(A.bandStreams.low),mid:this.serializeGeneratedRhythmMap(A.bandStreams.mid),high:this.serializeGeneratedRhythmMap(A.bandStreams.high)},composite:this.serializeCompositeStream(A.composite),analysis:{transientAnalysis:this.serializeTransientAnalysis(A.analysis.transientAnalysis),quantizationResult:this.serializeQuantizedBandStreams(A.analysis.quantizationResult),phraseAnalysis:this.serializePhraseAnalysisResult(A.analysis.phraseAnalysis),densityAnalysis:this.serializeDensityAnalysisResult(A.analysis.densityAnalysis),scoringResult:this.serializeStreamScoringResult(A.analysis.scoringResult)},metadata:A.metadata};return JSON.stringify(B,null,2)}static fromJSON(A){const B=JSON.parse(A);return{difficultyVariants:B.difficultyVariants?{easy:this.deserializeDifficultyVariant(B.difficultyVariants.easy),medium:this.deserializeDifficultyVariant(B.difficultyVariants.medium),hard:this.deserializeDifficultyVariant(B.difficultyVariants.hard),natural:this.deserializeDifficultyVariant(B.difficultyVariants.natural)}:null,bandStreams:{low:this.deserializeGeneratedRhythmMap(B.bandStreams.low),mid:this.deserializeGeneratedRhythmMap(B.bandStreams.mid),high:this.deserializeGeneratedRhythmMap(B.bandStreams.high)},composite:this.deserializeCompositeStream(B.composite),analysis:{transientAnalysis:this.deserializeTransientAnalysis(B.analysis.transientAnalysis),quantizationResult:this.deserializeQuantizedBandStreams(B.analysis.quantizationResult),phraseAnalysis:this.deserializePhraseAnalysisResult(B.analysis.phraseAnalysis),densityAnalysis:this.deserializeDensityAnalysisResult(B.analysis.densityAnalysis),scoringResult:this.deserializeStreamScoringResult(B.analysis.scoringResult)},metadata:B.metadata}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=kg.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return kg.fromJSON(I)}static serializeDifficultyVariant(A){return{difficulty:A.difficulty,beats:A.beats.map(B=>this.serializeVariantBeat(B)),isUnedited:A.isUnedited,editType:A.editType,editAmount:A.editAmount,patternsInserted:A.patternsInserted,conversionMetadata:A.conversionMetadata,enhancementMetadata:A.enhancementMetadata}}static deserializeDifficultyVariant(A){return{difficulty:A.difficulty,beats:A.beats.map(B=>this.deserializeVariantBeat(B)),isUnedited:A.isUnedited,editType:A.editType,editAmount:A.editAmount,patternsInserted:A.patternsInserted,conversionMetadata:A.conversionMetadata,enhancementMetadata:A.enhancementMetadata}}static serializeVariantBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError,sourceBand:A.sourceBand}}static deserializeVariantBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError,sourceBand:A.sourceBand}}static serializeGeneratedRhythmMap(A){return{audioId:A.audioId,duration:A.duration,beats:A.beats.map(B=>this.serializeGeneratedBeat(B)),gridDecisions:A.gridDecisions,quarterNoteInterval:A.quarterNoteInterval}}static deserializeGeneratedRhythmMap(A){return{audioId:A.audioId,duration:A.duration,beats:A.beats.map(B=>this.deserializeGeneratedBeat(B)),gridDecisions:A.gridDecisions,quarterNoteInterval:A.quarterNoteInterval}}static serializeGeneratedBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError}}static deserializeGeneratedBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError}}static serializeCompositeStream(A){return{beats:A.beats.map(B=>this.serializeCompositeBeat(B)),sections:A.sections,naturalDifficulty:A.naturalDifficulty,quarterNoteInterval:A.quarterNoteInterval,metadata:A.metadata}}static deserializeCompositeStream(A){return{beats:A.beats.map(B=>this.deserializeCompositeBeat(B)),sections:A.sections,naturalDifficulty:A.naturalDifficulty,quarterNoteInterval:A.quarterNoteInterval,metadata:A.metadata}}static serializeCompositeBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError,sourceBand:A.sourceBand,balancerAction:A.balancerAction}}static deserializeCompositeBeat(A){return{timestamp:A.timestamp,beatIndex:A.beatIndex,gridPosition:A.gridPosition,gridType:A.gridType,intensity:A.intensity,band:A.band,quantizationError:A.quantizationError,sourceBand:A.sourceBand,balancerAction:A.balancerAction}}static serializeTransientAnalysis(A){return{transients:A.transients.map(B=>this.serializeTransientResult(B)),bandTransients:Array.from(A.bandTransients.entries()).map(([B,I])=>({band:B,transients:I.map(Q=>this.serializeTransientResult(Q))})),metadata:{totalTransients:A.metadata.totalTransients,transientsPerBand:Array.from(A.metadata.transientsPerBand.entries()).map(([B,I])=>({band:B,count:I})),duration:A.metadata.duration,averageIntensity:A.metadata.averageIntensity,detectionMethodsUsed:A.metadata.detectionMethodsUsed}}}static deserializeTransientAnalysis(A){const B=new Map;for(const Q of A.bandTransients)B.set(Q.band,Q.transients.map(E=>this.deserializeTransientResult(E)));const I=new Map;for(const Q of A.metadata.transientsPerBand)I.set(Q.band,Q.count);return{transients:A.transients.map(Q=>this.deserializeTransientResult(Q)),bandTransients:B,metadata:{totalTransients:A.metadata.totalTransients,transientsPerBand:I,duration:A.metadata.duration,averageIntensity:A.metadata.averageIntensity,detectionMethodsUsed:A.metadata.detectionMethodsUsed}}}static serializeTransientResult(A){return{timestamp:A.timestamp,intensity:A.intensity,band:A.band,detectionMethod:A.detectionMethod,nearestBeat:A.nearestBeat}}static deserializeTransientResult(A){return{timestamp:A.timestamp,intensity:A.intensity,band:A.band,detectionMethod:A.detectionMethod,nearestBeat:A.nearestBeat}}static serializeQuantizedBandStreams(A){return{streams:{low:this.serializeGeneratedRhythmMap(A.streams.low),mid:this.serializeGeneratedRhythmMap(A.streams.mid),high:this.serializeGeneratedRhythmMap(A.streams.high)},metadata:{densityValidation:this.serializeDensityValidationResult(A.metadata.densityValidation),transientsFilteredByIntensity:A.metadata.transientsFilteredByIntensity,transientsFilteredByBand:A.metadata.transientsFilteredByBand}}}static serializeDensityValidationResult(A){return{isValid:A.isValid,bands:{low:this.serializeBandDensityValidationResult(A.bands.low),mid:this.serializeBandDensityValidationResult(A.bands.mid),high:this.serializeBandDensityValidationResult(A.bands.high)},maxRetryCount:A.maxRetryCount,maxSensitivityReduction:A.maxSensitivityReduction}}static serializeBandDensityValidationResult(A){return{band:A.band,isValid:A.isValid,minIntervalDetected:A.minIntervalDetected,requiredMinInterval:A.requiredMinInterval,retryCount:A.retryCount,sensitivityReduction:A.sensitivityReduction,finalIntensityThreshold:A.finalIntensityThreshold,transientsRemaining:A.transientsRemaining}}static deserializeQuantizedBandStreams(A){return{streams:{low:this.deserializeGeneratedRhythmMap(A.streams.low),mid:this.deserializeGeneratedRhythmMap(A.streams.mid),high:this.deserializeGeneratedRhythmMap(A.streams.high)},metadata:{densityValidation:this.deserializeDensityValidationResult(A.metadata.densityValidation),transientsFilteredByIntensity:A.metadata.transientsFilteredByIntensity,transientsFilteredByBand:A.metadata.transientsFilteredByBand}}}static deserializeDensityValidationResult(A){return{isValid:A.isValid,bands:{low:this.deserializeBandDensityValidationResult(A.bands.low),mid:this.deserializeBandDensityValidationResult(A.bands.mid),high:this.deserializeBandDensityValidationResult(A.bands.high)},maxRetryCount:A.maxRetryCount,maxSensitivityReduction:A.maxSensitivityReduction}}static deserializeBandDensityValidationResult(A){return{band:A.band,isValid:A.isValid,minIntervalDetected:A.minIntervalDetected,requiredMinInterval:A.requiredMinInterval,retryCount:A.retryCount,sensitivityReduction:A.sensitivityReduction,finalIntensityThreshold:A.finalIntensityThreshold,transientsRemaining:A.transientsRemaining}}static serializePhraseAnalysisResult(A){return{phrases:A.phrases.map(B=>this.serializeRhythmicPhrase(B)),phrasesByBand:Array.from(A.phrasesByBand.entries()).map(([B,I])=>({band:B,phrases:I.map(Q=>this.serializeRhythmicPhrase(Q))})),mostSignificantPhrases:A.mostSignificantPhrases.map(B=>this.serializeRhythmicPhrase(B)),phrasesBySize:Array.from(A.phrasesBySize.entries()).map(([B,I])=>({size:B,phrases:I.map(Q=>this.serializeRhythmicPhrase(Q))})),patternLibrary:A.patternLibrary.map(B=>this.serializeRhythmicPhrase(B)),bandAnalysis:{low:this.serializeBandPhraseAnalysis(A.bandAnalysis.low),mid:this.serializeBandPhraseAnalysis(A.bandAnalysis.mid),high:this.serializeBandPhraseAnalysis(A.bandAnalysis.high)}}}static deserializePhraseAnalysisResult(A){const B=new Map;for(const Q of A.phrasesByBand)B.set(Q.band,Q.phrases.map(E=>this.deserializeRhythmicPhrase(E)));const I=new Map;for(const Q of A.phrasesBySize)I.set(Q.size,Q.phrases.map(E=>this.deserializeRhythmicPhrase(E)));return{phrases:A.phrases.map(Q=>this.deserializeRhythmicPhrase(Q)),phrasesByBand:B,mostSignificantPhrases:A.mostSignificantPhrases.map(Q=>this.deserializeRhythmicPhrase(Q)),phrasesBySize:I,patternLibrary:A.patternLibrary.map(Q=>this.deserializeRhythmicPhrase(Q)),bandAnalysis:{low:this.deserializeBandPhraseAnalysis(A.bandAnalysis.low),mid:this.deserializeBandPhraseAnalysis(A.bandAnalysis.mid),high:this.deserializeBandPhraseAnalysis(A.bandAnalysis.high)}}}static serializeRhythmicPhrase(A){return{id:A.id,pattern:A.pattern.map(B=>this.serializeGeneratedBeat(B)),sizeInBeats:A.sizeInBeats,sourceBand:A.sourceBand,occurrences:A.occurrences,significance:A.significance,hasVariation:A.hasVariation,availableForReuse:A.availableForReuse}}static deserializeRhythmicPhrase(A){return{id:A.id,pattern:A.pattern.map(B=>this.deserializeGeneratedBeat(B)),sizeInBeats:A.sizeInBeats,sourceBand:A.sourceBand,occurrences:A.occurrences,significance:A.significance,hasVariation:A.hasVariation,availableForReuse:A.availableForReuse}}static serializeBandPhraseAnalysis(A){return{band:A.band,phrases:A.phrases.map(B=>this.serializeRhythmicPhrase(B)),phrasesBySize:Array.from(A.phrasesBySize.entries()).map(([B,I])=>({size:B,phrases:I.map(Q=>this.serializeRhythmicPhrase(Q))})),phrasesWithVariation:A.phrasesWithVariation.map(B=>this.serializeRhythmicPhrase(B))}}static deserializeBandPhraseAnalysis(A){const B=new Map;for(const I of A.phrasesBySize)B.set(I.size,I.phrases.map(Q=>this.deserializeRhythmicPhrase(Q)));return{band:A.band,phrases:A.phrases.map(I=>this.deserializeRhythmicPhrase(I)),phrasesBySize:B,phrasesWithVariation:A.phrasesWithVariation.map(I=>this.deserializeRhythmicPhrase(I))}}static serializeDensityAnalysisResult(A){return{bandMetrics:{low:this.serializeBandDensityMetrics(A.bandMetrics.low),mid:this.serializeBandDensityMetrics(A.bandMetrics.mid),high:this.serializeBandDensityMetrics(A.bandMetrics.high)},combinedMetrics:A.combinedMetrics,sections:A.sections,perBeatDensity:A.perBeatDensity}}static deserializeDensityAnalysisResult(A){return{bandMetrics:{low:this.deserializeBandDensityMetrics(A.bandMetrics.low),mid:this.deserializeBandDensityMetrics(A.bandMetrics.mid),high:this.deserializeBandDensityMetrics(A.bandMetrics.high)},combinedMetrics:A.combinedMetrics,sections:A.sections,perBeatDensity:A.perBeatDensity}}static serializeBandDensityMetrics(A){return{band:A.band,totalBeats:A.totalBeats,totalTransients:A.totalTransients,notesPerSecond:A.notesPerSecond,minNotesPerSecond:A.minNotesPerSecond,maxNotesPerSecond:A.maxNotesPerSecond,variance:A.variance,densityCategory:A.densityCategory,naturalDifficulty:A.naturalDifficulty,perBeatDensity:A.perBeatDensity}}static deserializeBandDensityMetrics(A){return{band:A.band,totalBeats:A.totalBeats,totalTransients:A.totalTransients,notesPerSecond:A.notesPerSecond,minNotesPerSecond:A.minNotesPerSecond,maxNotesPerSecond:A.maxNotesPerSecond,variance:A.variance,densityCategory:A.densityCategory,naturalDifficulty:A.naturalDifficulty,perBeatDensity:A.perBeatDensity}}static serializeStreamScoringResult(A){return{sectionScores:A.sectionScores,bandTotals:A.bandTotals,bandAverages:A.bandAverages,sectionWinners:A.sectionWinners,config:A.config}}static deserializeStreamScoringResult(A){return{sectionScores:A.sectionScores,bandTotals:A.bandTotals,bandAverages:A.bandAverages,sectionWinners:A.sectionWinners,config:A.config}}}const GN={minFrequency:80,maxFrequency:1e3,frameSize:2048,hopSize:512,voicingThreshold:.2,transitionPenalty:.5,selfTransitionProbability:.99,yinThreshold:.4,targetSampleRate:44100},ld=69,Dd=440,hN=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];class Wo{constructor(A={}){if(this.states=[],this.transitionMatrix=[],this.config={...GN,...A},this.config.minFrequency<=0)throw new Error(`minFrequency must be positive, got: ${this.config.minFrequency}`);if(this.config.maxFrequency<=this.config.minFrequency)throw new Error("maxFrequency must be greater than minFrequency");if(this.config.frameSize<=0||(this.config.frameSize&this.config.frameSize-1)!==0)throw new Error("frameSize must be a positive power of 2");if(this.config.hopSize<=0)throw new Error("hopSize must be positive");if(this.config.voicingThreshold<0||this.config.voicingThreshold>1)throw new Error("voicingThreshold must be between 0 and 1");this.initializeHMM()}getConfig(){return{...this.config}}initializeHMM(){const{minFrequency:A,maxFrequency:B}=this.config,I=this.frequencyToMidi(A),Q=this.frequencyToMidi(B);this.states=[];for(let E=I;E<=Q;E+=.5)this.states.push({frequency:this.midiToFrequency(E),midiNote:Math.round(E),index:this.states.length});this.states.push({frequency:0,midiNote:-1,index:this.states.length}),this.transitionMatrix=this.computeTransitionMatrix()}computeTransitionMatrix(){const A=this.states.length,B=[];for(let I=0;I<A;I++){B[I]=[];for(let Q=0;Q<A;Q++)B[I][Q]=this.transitionProbability(I,Q)}return B}transitionProbability(A,B){const{selfTransitionProbability:I,transitionPenalty:Q}=this.config,i=this.states.length-1;if(A===B)return Math.log(I);if(A===i||B===i)return Math.log((1-I)*.1);const e=this.states[A].midiNote,a=this.states[B].midiNote,C=Math.abs(a-e),o=(1-I)*Math.exp(-Q*C);return Math.log(o)}frequencyToMidi(A){return ld+12*Math.log2(A/Dd)}midiToFrequency(A){return Dd*Math.pow(2,(A-ld)/12)}midiToNoteName(A){const B=Math.floor(A/12)-1,I=Math.round(A)%12;return`${hN[I]}${B}`}detect(A){const I=XQ(A,this.config.targetSampleRate).data,Q=this.config.targetSampleRate,{frameSize:E,hopSize:i}=this.config,e=Math.floor((I.length-E)/i)+1,a=i/Q,C=[],o=[];for(let n=0;n<e;n++){const r=n*i,c=I.slice(r,r+E),{filtered:G,all:D}=this.analyzeFrame(c,Q);C.push(G),o.push(D)}const t=this.viterbiDecode(C),s=[];for(let n=0;n<e;n++){const r=t[n],c=this.states[r],G=C[n],D=o[n],h={timestamp:n*a,frequency:c.frequency,probability:this.getFrameProbability(r,G),isVoiced:c.frequency>0,midiNote:c.frequency>0?c.midiNote:null,noteName:c.frequency>0?this.midiToNoteName(c.midiNote):null};D.length>1&&(h.alternativeHypotheses=D.slice(0,5).map(w=>({frequency:w.frequency,probability:w.probability}))),s.push(h)}return s}detectSignal(A,B){const{frameSize:I,hopSize:Q}=this.config,E=Math.floor((A.length-I)/Q)+1,i=Q/B,e=[],a=[];for(let t=0;t<E;t++){const s=t*Q,n=A.slice(s,s+I),{filtered:r,all:c}=this.analyzeFrame(n,B);e.push(r),a.push(c)}const C=this.viterbiDecode(e),o=[];for(let t=0;t<E;t++){const s=C[t],n=this.states[s],r=e[t],c=a[t],G={timestamp:t*i,frequency:n.frequency,probability:this.getFrameProbability(s,r),isVoiced:n.frequency>0,midiNote:n.frequency>0?n.midiNote:null,noteName:n.frequency>0?this.midiToNoteName(n.midiNote):null};c.length>1&&(G.alternativeHypotheses=c.slice(0,5).map(D=>({frequency:D.frequency,probability:D.probability}))),o.push(G)}return o}detectSignalRaw(A,B){const{frameSize:I,hopSize:Q,voicingThreshold:E}=this.config,i=Math.floor((A.length-I)/Q)+1,e=Q/B,a=[];let C=0,o=0;const t={below01:0,b01_02:0,b02_03:0,b03_04:0,b04_05:0,above05:0};for(let s=0;s<i;s++){const n=s*Q,r=A.slice(n,n+I),{filtered:c,all:G}=this.analyzeFrame(r,B),D=s*e;if(c.length>0){C++;const h=c[0].probability;if(h<.1?t.below01++:h<.2?t.b01_02++:h<.3?t.b02_03++:h<.4?t.b03_04++:h<.5?t.b04_05++:t.above05++,h>=E){const w=c[0],y=Math.round(this.frequencyToMidi(w.frequency)),R={timestamp:D,frequency:w.frequency,probability:w.probability,isVoiced:!0,midiNote:y,noteName:this.midiToNoteName(y)};G.length>1&&(R.alternativeHypotheses=G.slice(0,5).map(p=>({frequency:p.frequency,probability:p.probability}))),a.push(R)}else a.push({timestamp:D,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null})}else o++,a.push({timestamp:D,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null})}return console.log("[PitchDetector.detectSignalRaw] Candidate quality:",{numFrames:i,framesWithCandidates:C,framesWithNoCandidates:o,candidateRate:(C/i*100).toFixed(1)+"%",voicingThreshold:E,probBuckets:t}),a}detectAt(A,B,I){const{frameSize:Q,hopSize:E}=this.config,i=Math.round(I*B),e=Math.max(0,i-Math.floor(Q/2)),a=Math.min(A.length,e+Q);if(a-e<Q)return{timestamp:I,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null};const C=A.slice(e,a),{filtered:o,all:t}=this.analyzeFrame(C,B);if(o.length===0)return{timestamp:I,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null};const s=o[0],n=Math.round(this.frequencyToMidi(s.frequency)),r={timestamp:I,frequency:s.frequency,probability:s.probability,isVoiced:s.probability>=this.config.voicingThreshold,midiNote:n,noteName:this.midiToNoteName(n)};return t.length>1&&(r.alternativeHypotheses=t.slice(0,5).map(c=>({frequency:c.frequency,probability:c.probability}))),r}analyzeFrame(A,B){const{minFrequency:I,maxFrequency:Q,yinThreshold:E}=this.config,i=Math.ceil(B/I),e=Math.max(2,Math.floor(B/Q)),a=Math.min(i,Math.floor(A.length/2)),C=this.computeDifferenceFunction(A,a),o=this.computeCMNDF(C,e,a),t=[];for(let r=e+1;r<a-1;r++)if(o[r]<o[r-1]&&o[r]<o[r+1]&&o[r]<E){const c=this.parabolicInterpolation(o,r),G=B/c,D=1-o[r];G>=I&&G<=Q&&t.push({lag:c,frequency:G,probability:D})}if(t.sort((r,c)=>c.probability-r.probability),t.length===0)return{filtered:[],all:[]};const s=[],n=t[0].probability;for(const r of t){let c=!1;for(const G of t){if(G.frequency<=r.frequency||G.probability<n*.9)continue;const D=G.frequency/r.frequency,h=Math.round(D),w=Math.abs(D-h);if(h>=2&&h<=5&&w<.1){c=!0;break}}c||s.push(r)}return s.length===0&&t.length>0&&s.push(t[0]),{filtered:s,all:t}}computeDifferenceFunction(A,B){const I=A.length,Q=new Float32Array(B+1);Q[0]=0;for(let E=1;E<=B;E++){let i=0;for(let e=0;e<I-E;e++){const a=A[e]-A[e+E];i+=a*a}Q[E]=i}return Q}computeCMNDF(A,B,I){const Q=new Float32Array(A.length);Q[0]=1;let E=0;for(let i=1;i<=I;i++)E+=A[i],Q[i]=A[i]*i/E;return Q}parabolicInterpolation(A,B){if(B<=0||B>=A.length-1)return B;const I=A[B-1],Q=A[B],E=A[B+1],i=(E-I)/(2*(2*Q-I-E));return Math.max(B-1,Math.min(B+1,B+i))}getFrameProbability(A,B){const Q=this.states.length-1;if(A===Q)return B.length===0?1:1-B[0].probability;const E=this.states[A].frequency;let i=0;for(const e of B)Math.abs(e.frequency-E)/E<.05&&(i=Math.max(i,e.probability));return i}viterbiDecode(A){const B=A.length,I=this.states.length;if(B===0)return[];const Q=[];Q[0]=[];for(let a=0;a<I;a++){const C=this.priorLogProbability(a,A[0]);Q[0][a]={logProbability:C,previousState:-1}}for(let a=1;a<B;a++){Q[a]=[];for(let C=0;C<I;C++){const o=this.observationLogProbability(C,A[a]);let t=0,s=-1/0;for(let n=0;n<I;n++){const r=this.transitionMatrix[n][C],c=Q[a-1][n].logProbability+r;c>s&&(s=c,t=n)}Q[a][C]={logProbability:s+o,previousState:t}}}const E=new Array(B);let i=0,e=Q[B-1][0].logProbability;for(let a=1;a<I;a++)Q[B-1][a].logProbability>e&&(e=Q[B-1][a].logProbability,i=a);E[B-1]=i;for(let a=B-2;a>=0;a--)E[a]=Q[a+1][E[a+1]].previousState;return E}priorLogProbability(A,B){const I=this.states.length,Q=I-1,E=Math.log(.7/(I-1)),i=Math.log(.3);return A===Q?i+this.observationLogProbability(A,B):E+this.observationLogProbability(A,B)}observationLogProbability(A,B){const Q=this.states.length-1;if(A===Q){if(B.length===0)return Math.log(.9);const e=B[0].probability;return Math.log(1-e*.8)}const E=this.states[A].frequency;let i=0;for(const e of B){const a=e.frequency/E,C=Math.abs(a-1);if(C<.05){const o=1-C/.05*.2,t=e.probability*o;t>i&&(i=t)}if(i===0){const o=Math.abs(a-.5),t=Math.abs(a-2);if(o<.05||t<.05){const s=e.probability*.4;s>i&&(i=s)}}}return Math.log(i===0?.01:i)}}const Xi="https://arweave.net/_HoMwkbF0JUamTiVGWNnCCC5yDjcPry-ENhzN6Xn3U0/model.json",dd={algorithm:"pitch_melodia",minFrequency:80,maxFrequency:2e4,frameSize:2048,hopSize:128,targetSampleRate:44100,crepeModelUrl:Xi},lN=69,DN=440,dN=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];class zQ{constructor(A){this.essentiaWASM=null,this.algorithms=null,this.crepeModel=null,this.initialized=!1,this.resolvedUrlCache=new Map,this.config={...dd,...A}}static async create(A={}){const B={...dd,...A};B.resolveUrl||(B.resolveUrl=TA.resolveUrl.bind(TA));const I=new zQ(B);return await I.initializeEssentia(),B.algorithm==="pitch_crepe"&&await I.loadCrepeModel(B.crepeModelUrl),I}getConfig(){return{...this.config}}async initializeEssentia(){if(this.initialized)return;const B=(await Promise.resolve().then(()=>Fb)).EssentiaWASM;await B.ready,this.essentiaWASM=B,this.algorithms=new B.EssentiaJS(!1),this.initialized=!0}async loadCrepeModel(A){if(this.config.enableModelCache!==!1)this.crepeModel=await xI.getOrFetchGraphModel(IB,A,{resolveUrl:this.config.resolveUrl,invalidateUrlCache:I=>TA.invalidateCacheForUrl(I),maxRetries:3,baseDelayMs:1e3});else{const I=await this.resolveUrlWithCache(A);let Q=null;for(let E=0;E<3;E++)try{this.crepeModel=await IB.loadGraphModel(I);break}catch(i){Q=i;const e=String(i).toLowerCase();if(!(e.includes("fetch")||e.includes("network")||e.includes("timeout")||e.includes("failed to fetch")||e.includes("networkerror"))||E===2)throw i;const C=1e3*Math.pow(2,E);console.warn(`[EssentiaPitchDetector] CREPE model load failed (attempt ${E+1}/3), retrying in ${C}ms...`,{originalUrl:A,resolvedUrl:I,error:String(i)}),await new Promise(o=>setTimeout(o,C))}if(!this.crepeModel)throw new Error(`Failed to load CREPE model from ${A}: ${Q?.message??String(Q)}`)}const B=IB.zeros([1,1024]);IB.tidy(()=>{this.crepeModel.execute(B)}),B.dispose(),console.log("[EssentiaPitchDetector] CREPE model loaded")}async resolveUrlWithCache(A){if(this.resolvedUrlCache.has(A))return this.resolvedUrlCache.get(A);if(!this.config.resolveUrl)return A;try{const B=await this.config.resolveUrl(A);return this.resolvedUrlCache.set(A,B),B!==A&&console.info("[EssentiaPitchDetector] Resolved URL to alternate gateway",{originalUrl:A,resolvedUrl:B}),B}catch(B){return console.warn("[EssentiaPitchDetector] URL resolution failed, using original URL",{url:A,error:String(B)}),this.resolvedUrlCache.set(A,A),A}}detectSignal(A,B){if(!this.initialized)throw new Error("EssentiaPitchDetector not initialized. Use create() factory method.");if(A.length===0)return[];switch(this.config.algorithm){case"predominant_melodia":return this.runPredominantMelodia(A,B);case"pitch_melodia":return this.runPitchMelodia(A,B);case"pitch_yin_probabilistic":return this.runPitchYinProbabilistic(A,B);case"multipitch_melodia":return this.runMultiPitchMelodia(A,B);case"multipitch_klapuri":return this.runMultiPitchKlapuri(A,B);case"pitch_crepe":return this.runCrepe(A,B);default:throw new Error(`Unknown algorithm: ${this.config.algorithm}`)}}runPredominantMelodia(A,B){const I=this.essentiaWASM.arrayToVector(A),Q=this.algorithms.PredominantPitchMelodia(I,10,3,this.config.frameSize,!1,.8,this.config.hopSize,1,40,this.config.maxFrequency,100,this.config.minFrequency,20,.9,.9,27.5625,55,B,100,!1,.2),E=Array.from(this.essentiaWASM.vectorToArray(Q.pitch)),i=Array.from(this.essentiaWASM.vectorToArray(Q.pitchConfidence)),e=this.config.hopSize/B;return this.mapSingleF0(E,i,e)}runPitchMelodia(A,B){const I=this.essentiaWASM.arrayToVector(A),Q=this.algorithms.PitchMelodia(I,10,3,this.config.frameSize,!1,.8,this.config.hopSize,1,40,this.config.maxFrequency,100,this.config.minFrequency,20,.9,.9,27.5625,55,B,100),E=Array.from(this.essentiaWASM.vectorToArray(Q.pitch)),i=Array.from(this.essentiaWASM.vectorToArray(Q.pitchConfidence)),e=this.config.hopSize/B;return this.mapSingleF0(E,i,e)}runPitchYinProbabilistic(A,B){const I=this.essentiaWASM.arrayToVector(A),Q=this.algorithms.PitchYinProbabilistic(I,this.config.frameSize,this.config.hopSize,.1,"negative",!1,B),E=Array.from(this.essentiaWASM.vectorToArray(Q.pitch)),i=Array.from(this.essentiaWASM.vectorToArray(Q.voicedProbabilities)),e=this.config.hopSize/B;return E.map((a,C)=>{const o=C*e;if(a>0&&i[C]>.5){const s=Math.round(this.frequencyToMidi(a));return{timestamp:o,frequency:a,probability:i[C],isVoiced:!0,midiNote:s,noteName:this.midiToNoteName(s)}}return{timestamp:o,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null}})}runMultiPitchMelodia(A,B){const I=this.essentiaWASM.arrayToVector(A),Q=this.algorithms.MultiPitchMelodia(I,10,3,this.config.frameSize,!1,.8,this.config.hopSize,1,40,this.config.maxFrequency,100,this.config.minFrequency,20,.9,.9,27.5625,55,B,100);return this.mapMultiPitch(Q.pitch,B)}runMultiPitchKlapuri(A,B){const I=this.essentiaWASM.arrayToVector(A),Q=this.algorithms.MultiPitchKlapuri(I,10,this.config.frameSize,.8,this.config.hopSize,1,40,this.config.maxFrequency,this.config.minFrequency,10,55,B);return this.mapMultiPitch(Q.pitch,B)}runCrepe(A,B){if(!this.crepeModel)throw new Error('CREPE model not loaded. Ensure algorithm is "pitch_crepe" and model URL is valid.');const I=16e3,Q=1024,E=256,i=this.resampleSignal(A,B,I),e=Math.floor((i.length-Q)/E)+1,a=E/I,C=32.7,o=[];for(let t=0;t<e;t++){const s=t*E,n=i.slice(s,s+Q),r=t*a;let c;try{const D=IB.tidy(()=>{const R=IB.tensor(n,[1,Q]);return this.crepeModel.execute(R).dataSync().slice()});let h=0,w=0,y=0;for(let R=0;R<360;R++){const p=D[R];h+=R*10*p,w+=p,p>y&&(y=p)}if(w>0&&y>.3){const R=h/w,p=C*Math.pow(2,R/1200),k=Math.round(this.frequencyToMidi(p));c={timestamp:r,frequency:p,probability:y,isVoiced:!0,midiNote:k,noteName:this.midiToNoteName(k)}}else c={timestamp:r,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null}}catch(G){console.error(`[EssentiaPitchDetector] CREPE inference error at frame ${t}:`,G),c={timestamp:r,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null}}o.push(c)}return o}mapSingleF0(A,B,I){return A.map((Q,E)=>{const i=E*I;if(Q>0){const e=Math.round(this.frequencyToMidi(Q));return{timestamp:i,frequency:Q,probability:B[E]??0,isVoiced:!0,midiNote:e,noteName:this.midiToNoteName(e)}}return{timestamp:i,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null}})}mapMultiPitch(A,B){const I=A.size(),Q=this.config.hopSize/B,E=[];for(let i=0;i<I;i++){const e=A.get(i),C=Array.from(this.essentiaWASM.vectorToArray(e)).filter(t=>t>0).sort((t,s)=>t-s),o=i*Q;if(C.length>0){const t=C[0],s=Math.round(this.frequencyToMidi(t)),n=C.slice(1).map(r=>({frequency:r,probability:1}));E.push({timestamp:o,frequency:t,probability:1,isVoiced:!0,midiNote:s,noteName:this.midiToNoteName(s),...n.length>0?{alternativeHypotheses:n}:{}})}else E.push({timestamp:o,frequency:0,probability:0,isVoiced:!1,midiNote:null,noteName:null})}return E}resampleSignal(A,B,I){if(B===I)return A;const Q=B/I,E=Math.round(A.length/Q),i=new Float32Array(E);for(let e=0;e<E;e++){const a=e*Q,C=Math.floor(a),o=Math.min(C+1,A.length-1),t=a-C;i[e]=A[C]*(1-t)+A[o]*t}return i}frequencyToMidi(A){return lN+12*Math.log2(A/DN)}midiToNoteName(A){const B=Math.floor(A/12)-1,I=Math.round(A)%12;return`${dN[I]}${B}`}}const wd={targetSampleRate:44100,pitchDetector:{},pitchAlgorithm:"pitch_melodia",crepeModelUrl:Xi};class Fd{constructor(A={}){this.essentiaDetector=null,this.config={...wd,...A,pitchDetector:{...wd.pitchDetector,...A.pitchDetector}},this.fullSpectrumDetector=new Wo({...this.config.pitchDetector,minFrequency:this.config.pitchDetector.minFrequency??80,maxFrequency:this.config.pitchDetector.maxFrequency??1e3,targetSampleRate:this.config.targetSampleRate,yinThreshold:this.config.pitchDetector.yinThreshold??.4})}getConfig(){return{...this.config}}async linkWithComposite(A,B){const I=this.config.targetSampleRate,E=XQ(B,I).data;let i,e;this.config.pitchAlgorithm==="pyin_legacy"?(i=this.fullSpectrumDetector.detectSignalRaw(E,I),e=this.fullSpectrumDetector.getConfig().hopSize/I):(this.essentiaDetector||(this.essentiaDetector=await zQ.create({algorithm:this.config.pitchAlgorithm,minFrequency:this.config.pitchDetector.minFrequency??80,maxFrequency:this.config.pitchDetector.maxFrequency??2e4,crepeModelUrl:this.config.crepeModelUrl,resolveUrl:this.config.resolveUrl}),console.log(`[PitchBeatLinker] Essentia detector initialized: ${this.config.pitchAlgorithm}`)),i=this.essentiaDetector.detectSignal(E,I),e=this.essentiaDetector.getConfig().hopSize/I),console.debug("[PitchBeatLinker] Full-spectrum analysis:",{detector:this.config.pitchAlgorithm==="pyin_legacy"?"pYIN":`essentia/${this.config.pitchAlgorithm}`,totalFrames:i.length,voicedFrames:i.filter(C=>C.isVoiced).length,signalLength:E.length,signalSampleRate:I});const a=[];for(const C of A.beats){const o=C.detectedTimestamp??C.timestamp,t=Math.round(o/e);let s=null,n=1/0,r=0;for(let c=0;c<i.length;c++){const G=i[c];if(!G.isVoiced)continue;const D=Math.abs(c-t);(D<n||D===n&&G.probability>r)&&(n=D,r=G.probability,s=G)}a.push({beatIndex:C.beatIndex,timestamp:C.timestamp,band:C.sourceBand,pitch:s,direction:"none",intervalFromPrevious:0})}return a}deriveVariantPitches(A,B){const I=[],Q=new Map;for(const E of B){const i=Math.round(E.timestamp*1e3);Q.set(i,E)}for(const E of A.beats){const i=Math.round(E.timestamp*1e3);let e=Q.get(i);if(!e)for(let a=-2;a<=2&&(e=Q.get(i+a),!e);a++);e?I.push({beatIndex:E.beatIndex,timestamp:E.timestamp,band:E.sourceBand,pitch:e.pitch,direction:e.direction,intervalFromPrevious:e.intervalFromPrevious,intervalCategory:e.intervalCategory}):I.push({beatIndex:E.beatIndex,timestamp:E.timestamp,band:E.sourceBand,pitch:null,direction:"none",intervalFromPrevious:0})}return I}deriveAllVariantPitches(A,B){return{easy:this.deriveVariantPitches(A.easy,B),medium:this.deriveVariantPitches(A.medium,B),hard:this.deriveVariantPitches(A.hard,B)}}}const wN={maxTimeGapForConsecutive:.5};function Md(g){return g===0?"unison":g<=2?"small":g<=4?"medium":g<=7?"large":"very_large"}function FN(g,A){return Math.abs(g-A)}function MN(g,A){return g===null||A===null?"none":g>A?"up":g<A?"down":"stable"}function RN(g){if(g.length===0)return"stable";let A=0,B=0,I=0;for(const e of g)e.direction==="up"?A++:e.direction==="down"?B++:I++;if(A===0&&B===0)return"stable";const Q=A-B,i=(A+B+I)*.2;return Math.abs(Q)<=i?"mixed":A>B?"ascending":B>A?"descending":"stable"}function Rd(g){const A=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],B=Math.floor(g/12)-1,I=g%12;return`${A[I]}${B}`}class yN{constructor(A={}){this.config={...wN,...A}}getConfig(){return{...this.config}}analyze(A){const B=this.analyzeBandPitches(A),{directionStats:I,intervalStats:Q,metadata:E}=this.calculateStatistics(B),i=this.buildMelodyContour(B);return{pitchByBeat:B,melodyContour:i,directionStats:I,intervalStats:Q,metadata:E}}analyzeBandPitches(A){if(A.length===0)return[];const B=[];for(let I=0;I<A.length;I++){const Q=A[I],E=I>0?A[I-1]:null,i=E!==null&&Q.timestamp-E.timestamp<=this.config.maxTimeGapForConsecutive;let e,a,C;if(!i||!E||!E.pitch||!Q.pitch)e="none",a=0,C="unison";else{const o=Q.pitch.midiNote,t=E.pitch.midiNote;o===null||t===null?(e="none",a=0,C="unison"):(e=MN(o,t),a=FN(o,t),C=Md(a))}B.push({...Q,direction:e,intervalFromPrevious:a,intervalCategory:C})}return B}buildMelodyContour(A){const B=A.filter(n=>n.pitch?.isVoiced);if(B.length===0)return{segments:[],direction:"stable",range:{minNote:"N/A",maxNote:"N/A",semitones:0},shortTermDirection:"stable",mediumTermDirection:"stable",longTermDirection:"stable"};const I=B.map(n=>n.pitch?.midiNote).filter(n=>n!==null);if(I.length===0)return{segments:[],direction:"stable",range:{minNote:"N/A",maxNote:"N/A",semitones:0},shortTermDirection:"stable",mediumTermDirection:"stable",longTermDirection:"stable"};const Q=Math.min(...I),E=Math.max(...I),i=Rd(Q),e=Rd(E),a=this.buildSegments(B),C=RN(a),o=this.calculateTimeWindowDirection(A,1,2),t=this.calculateTimeWindowDirection(A,4,8),s=this.calculateTimeWindowDirection(A,16,1/0);return{segments:a,direction:C,range:{minNote:i,maxNote:e,semitones:E-Q},shortTermDirection:o,mediumTermDirection:t,longTermDirection:s}}calculateTimeWindowDirection(A,B,I){if(A.length===0||B<1)return"stable";const Q=Math.min(I,Math.max(B,A.length)),E=A.slice(-Q);if(E.length===0)return"stable";let i=0,e=0,a=0;for(const C of E)C.direction==="up"?i++:C.direction==="down"?e++:C.direction==="stable"&&a++;return this.determineOverallDirectionFromCounts(i,e,a)}determineOverallDirectionFromCounts(A,B,I){const Q=A+B+I;if(Q===0)return"stable";const E=Q*.2,i=A-B;return Math.abs(i)<=E?"mixed":A>B?"ascending":B>A?"descending":"stable"}buildSegments(A){if(A.length===0)return[];const B=[];let I=null;for(const Q of A){const E=Q.direction;if(E==="none"){I&&(B.push({startTime:I.startTime,endTime:I.endTime,startPitch:I.startPitch,endPitch:I.endPitch,direction:I.direction,interval:Math.abs(I.endMidi-I.startMidi)}),I=null);continue}if(!I||I.direction!==E){I&&B.push({startTime:I.startTime,endTime:I.endTime,startPitch:I.startPitch,endPitch:I.endPitch,direction:I.direction,interval:Math.abs(I.endMidi-I.startMidi)});const i=Q.pitch?.noteName??"N/A",e=Q.pitch?.midiNote??60;I={startTime:Q.timestamp,endTime:Q.timestamp,startPitch:i,endPitch:i,direction:E,startMidi:e,endMidi:e}}else{const i=Q.pitch?.midiNote??I.endMidi,e=Q.pitch?.noteName??I.endPitch;I.endTime=Q.timestamp,I.endPitch=e,I.endMidi=i}}return I&&B.push({startTime:I.startTime,endTime:I.endTime,startPitch:I.startPitch,endPitch:I.endPitch,direction:I.direction,interval:Math.abs(I.endMidi-I.startMidi)}),B}calculateStatistics(A){const B={up:0,down:0,stable:0,none:0},I={unison:0,small:0,medium:0,large:0,very_large:0};let Q=0,E=0;for(const i of A)i.pitch?.isVoiced&&Q++,B[i.direction]++,i.direction!=="none"&&E++,i.intervalCategory&&I[i.intervalCategory]++;return{directionStats:B,intervalStats:I,metadata:{totalBeats:A.length,voicedBeats:Q,directionCalculatedBeats:E}}}}const uN={algorithm:"pitch_melodia",minFrequency:80,maxFrequency:2e4,sampleRate:44100,hopSize:1024,crepeModelUrl:Xi,resolveUrl:TA.resolveUrl.bind(TA),includeContour:!0,onProgress:void 0},mN=69,SN=440,bN=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];class pN{constructor(A={}){this.config={...uN,...A}}getConfig(){return{...this.config}}async analyze(A){const{onProgress:B}=this.config;B?.("fetching",0);const{signal:I,sampleRate:Q,duration:E}=await this.fetchAndDecodeAudio(A);B?.("fetching",1),B?.("detecting",0);const i=this.resolveDefaults(this.config),e=await this.detectPitch(I,Q,i);B?.("detecting",1);const a=this.computeSummary(e);let C,o,t;if(this.config.includeContour!==!1){const s=this.computeContour(e);C=s.contour,o=s.directionStats,t=s.intervalStats}return{pitchResults:e,contour:C,voicingRatio:a.voicingRatio,averageFrequency:a.averageFrequency,medianFrequency:a.medianFrequency,minFrequency:a.minFrequency,maxFrequency:a.maxFrequency,pitchRangeSemitones:a.pitchRangeSemitones,lowestNote:a.lowestNote,highestNote:a.highestNote,noteDistribution:a.noteDistribution,totalFrames:a.totalFrames,voicedFrames:a.voicedFrames,directionStats:o,intervalStats:t,analysis_metadata:{algorithm_used:this.config.algorithm,analyzed_at:new Date().toISOString(),duration_analyzed:E}}}async fetchAndDecodeAudio(A){const B=this.config.resolveUrl?await this.config.resolveUrl(A):A,I=await fetch(B);if(!I.ok)throw new Error(`Failed to fetch audio: ${I.statusText}`);const Q=await I.arrayBuffer(),E=globalThis.AudioContext||globalThis.window?.AudioContext;if(!E)throw new Error("AudioContext not available in this environment");const i=new E,e=await new Promise((C,o)=>{i.decodeAudioData(Q,C,o)}),a=new Float32Array(e.length);for(let C=0;C<e.numberOfChannels;C++){const o=e.getChannelData(C);for(let t=0;t<e.length;t++)a[t]+=o[t]/e.numberOfChannels}return{signal:a,sampleRate:e.sampleRate,duration:e.duration}}resolveDefaults(A){const B={...A};return A.maxFrequency===void 0&&(A.algorithm==="pyin_legacy"?B.maxFrequency=1e3:B.maxFrequency=2e4),B}async detectPitch(A,B,I){const Q=I.algorithm??"pitch_melodia";return Q==="pyin_legacy"?new Wo({minFrequency:I.minFrequency??80,maxFrequency:I.maxFrequency??1e3,targetSampleRate:I.sampleRate??44100,hopSize:I.hopSize??1024}).detectSignal(A,B):(await zQ.create({algorithm:Q,minFrequency:I.minFrequency??80,maxFrequency:I.maxFrequency??2e4,targetSampleRate:I.sampleRate??44100,hopSize:I.hopSize??1024,crepeModelUrl:I.crepeModelUrl,resolveUrl:I.resolveUrl})).detectSignal(A,B)}computeSummary(A){const B=A.length,I=A.filter(w=>w.isVoiced&&w.frequency>0),Q=I.length;if(Q===0)return{voicingRatio:0,averageFrequency:0,medianFrequency:0,minFrequency:0,maxFrequency:0,pitchRangeSemitones:0,lowestNote:null,highestNote:null,noteDistribution:[],totalFrames:B,voicedFrames:0};const E=I.map(w=>w.frequency),i=[...E].sort((w,y)=>w-y),e=E.reduce((w,y)=>w+y,0)/E.length,a=Math.floor(i.length/2),C=i.length%2!==0?i[a]:(i[a-1]+i[a])/2,o=i[0],t=i[i.length-1],s=this.frequencyToMidi(o),n=this.frequencyToMidi(t),r=Math.abs(n-s),c=this.midiToNoteName(Math.round(s)),G=this.midiToNoteName(Math.round(n)),D=new Map;for(const w of I)w.noteName&&D.set(w.noteName,(D.get(w.noteName)??0)+1);const h=Array.from(D.entries()).map(([w,y])=>({note:w,count:y,percentage:y/Q*100})).sort((w,y)=>y.count-w.count);return{voicingRatio:Q/B,averageFrequency:e,medianFrequency:C,minFrequency:o,maxFrequency:t,pitchRangeSemitones:r,lowestNote:c,highestNote:G,noteDistribution:h,totalFrames:B,voicedFrames:Q}}computeContour(A){const B=A.filter(D=>D.isVoiced&&D.midiNote!==null);if(B.length===0)return{contour:{direction:"stable",range:{minNote:"N/A",maxNote:"N/A",semitones:0},segments:[],shortTermDirection:"stable",mediumTermDirection:"stable",longTermDirection:"stable"},directionStats:{up:0,down:0,stable:0,none:A.length},intervalStats:{unison:0,small:0,medium:0,large:0,very_large:0}};const I=B.map(D=>D.midiNote).filter(D=>D!==null),Q=Math.min(...I),E=Math.max(...I),i=this.midiToNoteName(Q),e=this.midiToNoteName(E),a={up:0,down:0,stable:0,none:0},C={unison:0,small:0,medium:0,large:0,very_large:0},o=[];let t=null,s=null;for(const D of B){const h=D.midiNote;if(h===null){a.none++;continue}if(s===null){s=h;continue}let w;const y=Math.abs(h-s);h>s?(w="up",a.up++):h<s?(w="down",a.down++):(w="stable",a.stable++);const R=Md(y);C[R]++,!t||t.direction!==w?(t&&o.push({startTime:t.startTime,endTime:t.endTime,startNote:t.startNote,endNote:t.endNote,direction:t.direction,interval:Math.abs(t.endMidi-t.startMidi)}),t={startTime:D.timestamp,endTime:D.timestamp,startNote:this.midiToNoteName(s),endNote:this.midiToNoteName(h),direction:w,startMidi:s,endMidi:h}):(t.endTime=D.timestamp,t.endNote=this.midiToNoteName(h),t.endMidi=h),s=h}t&&o.push({startTime:t.startTime,endTime:t.endTime,startNote:t.startNote,endNote:t.endNote,direction:t.direction,interval:Math.abs(t.endMidi-t.startMidi)});const n=this.determineOverallDirection(o),r=this.calculateTimeWindowDirection(A,1,2),c=this.calculateTimeWindowDirection(A,4,8),G=this.calculateTimeWindowDirection(A,16,1/0);return{contour:{direction:n,range:{minNote:i,maxNote:e,semitones:E-Q},segments:o,shortTermDirection:r,mediumTermDirection:c,longTermDirection:G},directionStats:a,intervalStats:C}}determineOverallDirection(A){if(A.length===0)return"stable";let B=0,I=0,Q=0;for(const a of A)a.direction==="up"?B++:a.direction==="down"?I++:Q++;if(B===0&&I===0)return"stable";const E=B-I,e=(B+I+Q)*.2;return Math.abs(E)<=e?"mixed":B>I?"ascending":I>B?"descending":"stable"}calculateTimeWindowDirection(A,B,I){const Q=A.filter(t=>t.isVoiced&&t.midiNote!==null);if(Q.length===0||B<1)return"stable";const E=Math.min(I,Math.max(B,Q.length)),i=Q.slice(-E);if(i.length===0)return"stable";let e=0,a=0,C=0,o=null;for(const t of i){const s=t.midiNote;s!==null&&(o!==null&&(s>o?e++:s<o?a++:C++),o=s)}return this.determineOverallDirectionFromCounts(e,a,C)}determineOverallDirectionFromCounts(A,B,I){const Q=A+B+I;if(Q===0)return"stable";const E=Q*.2,i=A-B;return Math.abs(i)<=E?"mixed":A>B?"ascending":B>A?"descending":"stable"}frequencyToMidi(A){return mN+12*Math.log2(A/SN)}midiToNoteName(A){const B=Math.floor(A/12)-1,I=Math.round(A)%12;return`${bN[I]}${B}`}}const YN=[{id:"ddr_basic_single_up",name:"Single Up",controllerMode:"ddr",keys:["up"],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"ddr_basic_single_down",name:"Single Down",controllerMode:"ddr",keys:["down"],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"ddr_basic_single_left",name:"Single Left",controllerMode:"ddr",keys:["left"],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"ddr_basic_single_right",name:"Single Right",controllerMode:"ddr",keys:["right"],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"ddr_basic_alternating_up_down",name:"Alternating Up-Down",controllerMode:"ddr",keys:["up","down","up","down"],measures:1,tags:["basic","alternating","vertical"],category:"basic",difficulty:2},{id:"ddr_basic_alternating_left_right",name:"Alternating Left-Right",controllerMode:"ddr",keys:["left","right","left","right"],measures:1,tags:["basic","alternating","horizontal"],category:"basic",difficulty:2},{id:"ddr_basic_alternating_up_left",name:"Alternating Up-Left",controllerMode:"ddr",keys:["up","left","up","left"],measures:1,tags:["basic","alternating"],category:"basic",difficulty:2},{id:"ddr_basic_alternating_down_right",name:"Alternating Down-Right",controllerMode:"ddr",keys:["down","right","down","right"],measures:1,tags:["basic","alternating"],category:"basic",difficulty:2},{id:"ddr_basic_double_up",name:"Double Up",controllerMode:"ddr",keys:["up","up","down","down"],measures:1,tags:["basic","double","repeat"],category:"basic",difficulty:2},{id:"ddr_basic_double_left",name:"Double Left",controllerMode:"ddr",keys:["left","left","right","right"],measures:1,tags:["basic","double","repeat"],category:"basic",difficulty:2},{id:"ddr_basic_walk_forward",name:"Walk Forward",controllerMode:"ddr",keys:["left","right","left","right"],measures:1,tags:["basic","walk","horizontal"],category:"basic",difficulty:2},{id:"ddr_basic_walk_vertical",name:"Walk Vertical",controllerMode:"ddr",keys:["up","down","up","down"],measures:1,tags:["basic","walk","vertical"],category:"basic",difficulty:2}],UN=[{id:"ddr_roll_clockwise_full",name:"Clockwise Full Roll",controllerMode:"ddr",keys:["up","right","down","left"],measures:1,tags:["roll","clockwise","full"],category:"roll",difficulty:3},{id:"ddr_roll_clockwise_extended",name:"Clockwise Extended Roll",controllerMode:"ddr",keys:["up","right","down","left","up","right","down","left"],measures:2,tags:["roll","clockwise","extended"],category:"roll",difficulty:4},{id:"ddr_roll_counterclockwise_full",name:"Counter-Clockwise Full Roll",controllerMode:"ddr",keys:["up","left","down","right"],measures:1,tags:["roll","counterclockwise","full"],category:"roll",difficulty:3},{id:"ddr_roll_counterclockwise_extended",name:"Counter-Clockwise Extended Roll",controllerMode:"ddr",keys:["up","left","down","right","up","left","down","right"],measures:2,tags:["roll","counterclockwise","extended"],category:"roll",difficulty:4},{id:"ddr_roll_half_right",name:"Half Roll Right",controllerMode:"ddr",keys:["up","right","down"],measures:1,tags:["roll","half","right"],category:"roll",difficulty:3},{id:"ddr_roll_half_left",name:"Half Roll Left",controllerMode:"ddr",keys:["up","left","down"],measures:1,tags:["roll","half","left"],category:"roll",difficulty:3},{id:"ddr_roll_bottom_half_right",name:"Bottom Half Roll Right",controllerMode:"ddr",keys:["down","right","up"],measures:1,tags:["roll","half","bottom","right"],category:"roll",difficulty:3},{id:"ddr_roll_bottom_half_left",name:"Bottom Half Roll Left",controllerMode:"ddr",keys:["down","left","up"],measures:1,tags:["roll","half","bottom","left"],category:"roll",difficulty:3},{id:"ddr_roll_triangle_up",name:"Triangle Roll Up",controllerMode:"ddr",keys:["left","up","right"],measures:1,tags:["roll","triangle","top"],category:"roll",difficulty:3},{id:"ddr_roll_triangle_down",name:"Triangle Roll Down",controllerMode:"ddr",keys:["left","down","right"],measures:1,tags:["roll","triangle","bottom"],category:"roll",difficulty:3}],fN=[{id:"ddr_stream_vertical_8",name:"Vertical Stream 8",controllerMode:"ddr",keys:["up","down","up","down","up","down","up","down"],measures:2,tags:["stream","vertical","fast"],category:"stream",difficulty:5},{id:"ddr_stream_vertical_16",name:"Vertical Stream 16",controllerMode:"ddr",keys:["up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down"],measures:4,tags:["stream","vertical","extended"],category:"stream",difficulty:7},{id:"ddr_stream_horizontal_8",name:"Horizontal Stream 8",controllerMode:"ddr",keys:["left","right","left","right","left","right","left","right"],measures:2,tags:["stream","horizontal","fast"],category:"stream",difficulty:5},{id:"ddr_stream_diagonal_1",name:"Diagonal Stream 1",controllerMode:"ddr",keys:["up","right","up","right","up","right","up","right"],measures:2,tags:["stream","diagonal"],category:"stream",difficulty:5},{id:"ddr_stream_diagonal_2",name:"Diagonal Stream 2",controllerMode:"ddr",keys:["up","left","up","left","up","left","up","left"],measures:2,tags:["stream","diagonal"],category:"stream",difficulty:5},{id:"ddr_stream_diagonal_3",name:"Diagonal Stream 3",controllerMode:"ddr",keys:["down","right","down","right","down","right","down","right"],measures:2,tags:["stream","diagonal"],category:"stream",difficulty:5},{id:"ddr_stream_diagonal_4",name:"Diagonal Stream 4",controllerMode:"ddr",keys:["down","left","down","left","down","left","down","left"],measures:2,tags:["stream","diagonal"],category:"stream",difficulty:5},{id:"ddr_stream_mixed_box",name:"Box Stream",controllerMode:"ddr",keys:["up","right","down","left","up","right","down","left"],measures:2,tags:["stream","box","circular"],category:"stream",difficulty:6},{id:"ddr_stream_mixed_zigzag",name:"Zigzag Stream",controllerMode:"ddr",keys:["up","left","down","right","up","left","down","right"],measures:2,tags:["stream","zigzag"],category:"stream",difficulty:6}],kN=[{id:"ddr_jump_opposite_vertical",name:"Opposite Jump Vertical",controllerMode:"ddr",keys:["up","down","up","down"],measures:1,tags:["jump","opposite","vertical"],category:"jump",difficulty:4},{id:"ddr_jump_opposite_horizontal",name:"Opposite Jump Horizontal",controllerMode:"ddr",keys:["left","right","left","right"],measures:1,tags:["jump","opposite","horizontal"],category:"jump",difficulty:4},{id:"ddr_jump_diagonal_1",name:"Diagonal Jump 1",controllerMode:"ddr",keys:["up","left","up","left"],measures:1,tags:["jump","diagonal"],category:"jump",difficulty:4},{id:"ddr_jump_diagonal_2",name:"Diagonal Jump 2",controllerMode:"ddr",keys:["up","right","up","right"],measures:1,tags:["jump","diagonal"],category:"jump",difficulty:4},{id:"ddr_jump_diagonal_3",name:"Diagonal Jump 3",controllerMode:"ddr",keys:["down","left","down","left"],measures:1,tags:["jump","diagonal"],category:"jump",difficulty:4},{id:"ddr_jump_diagonal_4",name:"Diagonal Jump 4",controllerMode:"ddr",keys:["down","right","down","right"],measures:1,tags:["jump","diagonal"],category:"jump",difficulty:4},{id:"ddr_jump_cross_pattern",name:"Cross Pattern",controllerMode:"ddr",keys:["up","left","down","right"],measures:1,tags:["jump","cross"],category:"jump",difficulty:5},{id:"ddr_jump_x_pattern",name:"X Pattern",controllerMode:"ddr",keys:["up","right","down","left"],measures:1,tags:["jump","x"],category:"jump",difficulty:5},{id:"ddr_jump_leap_sequence",name:"Leap Sequence",controllerMode:"ddr",keys:["up","down","left","right","up","down"],measures:2,tags:["jump","leap","sequence"],category:"jump",difficulty:6},{id:"ddr_jump_corner_to_corner",name:"Corner to Corner",controllerMode:"ddr",keys:["up","right","down","left","up","right"],measures:2,tags:["jump","corner"],category:"jump",difficulty:6}],ZN=[{id:"ddr_transition_build_up",name:"Build Up",controllerMode:"ddr",keys:["left","left","up","up","right","right"],measures:2,tags:["transition","build-up","intensity"],category:"transition",difficulty:4},{id:"ddr_transition_wind_down",name:"Wind Down",controllerMode:"ddr",keys:["right","right","up","up","left","left"],measures:2,tags:["transition","wind-down"],category:"transition",difficulty:4},{id:"ddr_transition_fill_4",name:"Fill 4 Count",controllerMode:"ddr",keys:["up","right","down","left"],measures:1,tags:["transition","fill"],category:"transition",difficulty:3},{id:"ddr_transition_fill_8",name:"Fill 8 Count",controllerMode:"ddr",keys:["up","up","right","right","down","down","left","left"],measures:2,tags:["transition","fill","extended"],category:"transition",difficulty:4}],NN=[{id:"gh_basic_single_1",name:"Single Fret 1",controllerMode:"guitar_hero",keys:[1],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"gh_basic_single_2",name:"Single Fret 2",controllerMode:"guitar_hero",keys:[2],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"gh_basic_single_3",name:"Single Fret 3",controllerMode:"guitar_hero",keys:[3],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"gh_basic_single_4",name:"Single Fret 4",controllerMode:"guitar_hero",keys:[4],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"gh_basic_single_5",name:"Single Fret 5",controllerMode:"guitar_hero",keys:[5],measures:1,tags:["basic","single","beginner"],category:"basic",difficulty:1},{id:"gh_basic_ascending_full",name:"Ascending Full Run",controllerMode:"guitar_hero",keys:[1,2,3,4,5],measures:1,tags:["basic","ascending","full"],category:"basic",difficulty:3},{id:"gh_basic_ascending_quad",name:"Ascending Quad",controllerMode:"guitar_hero",keys:[1,2,3,4],measures:1,tags:["basic","ascending"],category:"basic",difficulty:2},{id:"gh_basic_ascending_tri_low",name:"Ascending Tri Low",controllerMode:"guitar_hero",keys:[1,2,3],measures:1,tags:["basic","ascending","tri"],category:"basic",difficulty:2},{id:"gh_basic_ascending_tri_mid",name:"Ascending Tri Mid",controllerMode:"guitar_hero",keys:[2,3,4],measures:1,tags:["basic","ascending","tri"],category:"basic",difficulty:2},{id:"gh_basic_ascending_tri_high",name:"Ascending Tri High",controllerMode:"guitar_hero",keys:[3,4,5],measures:1,tags:["basic","ascending","tri"],category:"basic",difficulty:2},{id:"gh_basic_descending_full",name:"Descending Full Run",controllerMode:"guitar_hero",keys:[5,4,3,2,1],measures:1,tags:["basic","descending","full"],category:"basic",difficulty:3},{id:"gh_basic_descending_quad",name:"Descending Quad",controllerMode:"guitar_hero",keys:[5,4,3,2],measures:1,tags:["basic","descending"],category:"basic",difficulty:2},{id:"gh_basic_descending_tri_high",name:"Descending Tri High",controllerMode:"guitar_hero",keys:[5,4,3],measures:1,tags:["basic","descending","tri"],category:"basic",difficulty:2},{id:"gh_basic_descending_tri_mid",name:"Descending Tri Mid",controllerMode:"guitar_hero",keys:[4,3,2],measures:1,tags:["basic","descending","tri"],category:"basic",difficulty:2},{id:"gh_basic_descending_tri_low",name:"Descending Tri Low",controllerMode:"guitar_hero",keys:[3,2,1],measures:1,tags:["basic","descending","tri"],category:"basic",difficulty:2},{id:"gh_basic_step_up",name:"Step Up",controllerMode:"guitar_hero",keys:[1,2,3,4],measures:1,tags:["basic","step","up"],category:"basic",difficulty:2},{id:"gh_basic_step_down",name:"Step Down",controllerMode:"guitar_hero",keys:[5,4,3,2],measures:1,tags:["basic","step","down"],category:"basic",difficulty:2}],WN=[{id:"gh_alternating_1_3",name:"Alternating 1-3",controllerMode:"guitar_hero",keys:[1,3,1,3],measures:1,tags:["alternating","skip"],category:"basic",difficulty:3},{id:"gh_alternating_2_4",name:"Alternating 2-4",controllerMode:"guitar_hero",keys:[2,4,2,4],measures:1,tags:["alternating","skip"],category:"basic",difficulty:3},{id:"gh_alternating_3_5",name:"Alternating 3-5",controllerMode:"guitar_hero",keys:[3,5,3,5],measures:1,tags:["alternating","skip"],category:"basic",difficulty:3},{id:"gh_alternating_1_2",name:"Alternating 1-2",controllerMode:"guitar_hero",keys:[1,2,1,2],measures:1,tags:["alternating","adjacent"],category:"basic",difficulty:2},{id:"gh_alternating_2_3",name:"Alternating 2-3",controllerMode:"guitar_hero",keys:[2,3,2,3],measures:1,tags:["alternating","adjacent"],category:"basic",difficulty:2},{id:"gh_alternating_3_4",name:"Alternating 3-4",controllerMode:"guitar_hero",keys:[3,4,3,4],measures:1,tags:["alternating","adjacent"],category:"basic",difficulty:2},{id:"gh_alternating_4_5",name:"Alternating 4-5",controllerMode:"guitar_hero",keys:[4,5,4,5],measures:1,tags:["alternating","adjacent"],category:"basic",difficulty:2},{id:"gh_alternating_tri_1_3_5",name:"Alternating 1-3-5",controllerMode:"guitar_hero",keys:[1,3,5,3],measures:1,tags:["alternating","tri","wide"],category:"basic",difficulty:4},{id:"gh_alternating_tri_1_2_3",name:"Alternating 1-2-3",controllerMode:"guitar_hero",keys:[1,2,3,2],measures:1,tags:["alternating","tri","tight"],category:"basic",difficulty:3},{id:"gh_alternating_tri_3_4_5",name:"Alternating 3-4-5",controllerMode:"guitar_hero",keys:[3,4,5,4],measures:1,tags:["alternating","tri","tight"],category:"basic",difficulty:3},{id:"gh_alternating_extended_1_3",name:"Extended Alternating 1-3",controllerMode:"guitar_hero",keys:[1,3,1,3,1,3,1,3],measures:2,tags:["alternating","extended"],category:"basic",difficulty:4},{id:"gh_alternating_extended_2_4",name:"Extended Alternating 2-4",controllerMode:"guitar_hero",keys:[2,4,2,4,2,4,2,4],measures:2,tags:["alternating","extended"],category:"basic",difficulty:4}],HN=[{id:"gh_chord_power_full",name:"Power Chord Full",controllerMode:"guitar_hero",keys:[1,3,5],measures:1,tags:["chord","power","full"],category:"chord",difficulty:4},{id:"gh_chord_power_low",name:"Power Chord Low",controllerMode:"guitar_hero",keys:[1,3],measures:1,tags:["chord","power","low"],category:"chord",difficulty:3},{id:"gh_chord_power_mid",name:"Power Chord Mid",controllerMode:"guitar_hero",keys:[2,4],measures:1,tags:["chord","power","mid"],category:"chord",difficulty:3},{id:"gh_chord_power_high",name:"Power Chord High",controllerMode:"guitar_hero",keys:[3,5],measures:1,tags:["chord","power","high"],category:"chord",difficulty:3},{id:"gh_chord_triad_low",name:"Triad Low",controllerMode:"guitar_hero",keys:[1,2,3],measures:1,tags:["chord","triad","low"],category:"chord",difficulty:4},{id:"gh_chord_triad_mid",name:"Triad Mid",controllerMode:"guitar_hero",keys:[2,3,4],measures:1,tags:["chord","triad","mid"],category:"chord",difficulty:4},{id:"gh_chord_triad_high",name:"Triad High",controllerMode:"guitar_hero",keys:[3,4,5],measures:1,tags:["chord","triad","high"],category:"chord",difficulty:4},{id:"gh_chord_progression_1",name:"Chord Progression 1",controllerMode:"guitar_hero",keys:[1,3,2,4,3,5],measures:2,tags:["chord","progression"],category:"chord",difficulty:5},{id:"gh_chord_progression_2",name:"Chord Progression 2",controllerMode:"guitar_hero",keys:[1,2,3,4,5,4,3,2],measures:2,tags:["chord","progression","wave"],category:"chord",difficulty:5}],JN=[{id:"gh_jump_wide_1_4",name:"Wide Jump 1-4",controllerMode:"guitar_hero",keys:[1,4,1,4],measures:1,tags:["jump","wide"],category:"jump",difficulty:4},{id:"gh_jump_wide_2_5",name:"Wide Jump 2-5",controllerMode:"guitar_hero",keys:[2,5,2,5],measures:1,tags:["jump","wide"],category:"jump",difficulty:4},{id:"gh_jump_wide_1_5",name:"Wide Jump 1-5",controllerMode:"guitar_hero",keys:[1,5,1,5],measures:1,tags:["jump","widest"],category:"jump",difficulty:5},{id:"gh_jump_shift_up",name:"Position Shift Up",controllerMode:"guitar_hero",keys:[1,2,4,5],measures:1,tags:["jump","shift","up"],category:"jump",difficulty:4},{id:"gh_jump_shift_down",name:"Position Shift Down",controllerMode:"guitar_hero",keys:[5,4,2,1],measures:1,tags:["jump","shift","down"],category:"jump",difficulty:4},{id:"gh_jump_leap_sequence",name:"Leap Sequence",controllerMode:"guitar_hero",keys:[1,3,5,3,1,3,5],measures:2,tags:["jump","leap","sequence"],category:"jump",difficulty:5},{id:"gh_jump_bounce",name:"Bounce Pattern",controllerMode:"guitar_hero",keys:[1,5,2,4,3],measures:1,tags:["jump","bounce"],category:"jump",difficulty:5},{id:"gh_jump_stretch_1_4_5",name:"Stretch 1-4-5",controllerMode:"guitar_hero",keys:[1,4,5,4],measures:1,tags:["jump","stretch"],category:"jump",difficulty:5},{id:"gh_jump_stretch_1_2_5",name:"Stretch 1-2-5",controllerMode:"guitar_hero",keys:[1,2,5,2],measures:1,tags:["jump","stretch"],category:"jump",difficulty:5}],xN=[{id:"gh_transition_build_up",name:"Build Up",controllerMode:"guitar_hero",keys:[1,1,2,2,3,3,4,4,5],measures:2,tags:["transition","build-up"],category:"transition",difficulty:4},{id:"gh_transition_wind_down",name:"Wind Down",controllerMode:"guitar_hero",keys:[5,5,4,4,3,3,2,2,1],measures:2,tags:["transition","wind-down"],category:"transition",difficulty:4},{id:"gh_transition_fill_ascending",name:"Fill Ascending",controllerMode:"guitar_hero",keys:[1,2,3,4,5],measures:1,tags:["transition","fill","ascending"],category:"transition",difficulty:3},{id:"gh_transition_fill_descending",name:"Fill Descending",controllerMode:"guitar_hero",keys:[5,4,3,2,1],measures:1,tags:["transition","fill","descending"],category:"transition",difficulty:3},{id:"gh_transition_fill_wave",name:"Fill Wave",controllerMode:"guitar_hero",keys:[1,3,5,3,1],measures:1,tags:["transition","fill","wave"],category:"transition",difficulty:4},{id:"gh_transition_end_resolve",name:"End Resolve",controllerMode:"guitar_hero",keys:[5,4,3,2,1,1],measures:2,tags:["transition","resolve","end"],category:"transition",difficulty:3}],qN=[...YN,...UN,...fN,...kN,...ZN],LN=[...NN,...WN,...HN,...JN,...xN];function yd(g,A){const B=new Map,I=["basic","roll","stream","jump","chord","transition"];for(const E of I){const i=A.filter(e=>e.category===E);i.length>0&&B.set(E,i)}const Q=new Map;for(let E=1;E<=10;E++){const i=A.filter(e=>e.difficulty===E);i.length>0&&Q.set(E,i)}return{controllerMode:g,patterns:A,byCategory:B,byDifficulty:Q}}const Ho=yd("ddr",qN),Jo=yd("guitar_hero",LN);function VN(g){return g==="ddr"?Ho:Jo}function vN(g,A){return g.byCategory.get(A)??[]}function XN(g,A=1,B=10){const I=[];for(let Q=A;Q<=B;Q++){const E=g.byDifficulty.get(Q);E&&I.push(...E)}return I}function KN(g,A){return g.patterns.filter(B=>A.every(I=>B.tags.includes(I)))}function zN(g,A){return g.patterns.filter(B=>B.keys.length===A)}function TN(g,A){const B=A?g.patterns.filter(A):g.patterns;if(B.length===0)return;const I=Math.floor(Math.random()*B.length);return B[I]}function ON(g,A){return g.patterns.find(B=>B.id===A)}function jN(g){const A={basic:0,roll:0,stream:0,jump:0,chord:0,transition:0},B={};for(let i=1;i<=10;i++)B[i]=0;let I=0,Q=1/0,E=0;for(const i of g.patterns)A[i.category]++,B[i.difficulty]++,I+=i.keys.length,Q=Math.min(Q,i.keys.length),E=Math.max(E,i.keys.length);return{totalPatterns:g.patterns.length,byCategory:A,byDifficulty:B,averageKeyCount:g.patterns.length>0?I/g.patterns.length:0,minKeys:Q===1/0?0:Q,maxKeys:E}}const ud={up:{ascending:{unison:"up",small:"up",medium:"right",large:"right",very_large:"left"},descending:{unison:"up",small:"left",medium:"right",large:"down",very_large:"down"},stable:"up"},right:{ascending:{unison:"right",small:"up",medium:"up",large:"up",very_large:"left"},descending:{unison:"right",small:"down",medium:"down",large:"down",very_large:"left"},stable:"right"},down:{ascending:{unison:"down",small:"left",medium:"right",large:"up",very_large:"up"},descending:{unison:"down",small:"down",medium:"left",large:"left",very_large:"right"},stable:"down"},left:{ascending:{unison:"left",small:"up",medium:"up",large:"up",very_large:"right"},descending:{unison:"left",small:"down",medium:"down",large:"down",very_large:"right"},stable:"left"}},PN={up:{ascending:"up",descending:"left",stable:"up"},right:{ascending:"up",descending:"down",stable:"right"},down:{ascending:"right",descending:"down",stable:"down"},left:{ascending:"up",descending:"down",stable:"left"}},md={1:{ascending:{unison:1,small:2,medium:2,large:3,very_large:3},descending:{unison:1,small:4,medium:4,large:3,very_large:3},stable:1},2:{ascending:{unison:2,small:3,medium:3,large:4,very_large:4},descending:{unison:2,small:1,medium:1,large:4,very_large:4},stable:2},3:{ascending:{unison:3,small:4,medium:4,large:5,very_large:5},descending:{unison:3,small:2,medium:2,large:1,very_large:1},stable:3},4:{ascending:{unison:4,small:5,medium:5,large:2,very_large:2},descending:{unison:4,small:3,medium:3,large:2,very_large:1},stable:4},5:{ascending:{unison:5,small:2,medium:2,large:3,very_large:3},descending:{unison:5,small:4,medium:4,large:3,very_large:2},stable:5}},_N={1:{ascending:2,descending:1,stable:1},2:{ascending:3,descending:1,stable:2},3:{ascending:4,descending:2,stable:3},4:{ascending:5,descending:3,stable:4},5:{ascending:5,descending:4,stable:5}};function Sd(g){return g.intervalCategory??"small"}function $N(g,A,B){const I=A??"left";if(g.direction==="stable"||g.intervalCategory==="unison")return I;if(B==="easy"){const e=PN[I];return g.direction==="up"?e.ascending:e.descending}const Q=ud[I],E=g.direction==="up"?"ascending":"descending",i=Sd(g);return Q[E][i]}function AW(g,A,B){const I=A??3;if(g.direction==="stable"||g.intervalCategory==="unison")return I;if(B==="easy"){const e=_N[I];return g.direction==="up"?e.ascending:e.descending}const Q=md[I],E=g.direction==="up"?"ascending":"descending",i=Sd(g);return Q[E][i]}function BW(g,A,B,I){const Q=new Map;if(A)for(const a of A){const C=Math.round(a.timestamp*1e3);Q.set(C,a)}const E=[],i=[];let e=null;for(const a of g){const C=Math.round(a.timestamp*1e3);let o=null;for(let n=-2;n<=2&&(o=Q.get(C+n)??null,!o);n++);let t=null,s=0;o&&o.direction!=="none"&&(t=B==="ddr"?$N(o,e,I):AW(o,e,I),s=o.pitch?.probability??.5),E.push(t),i.push(s),e=t}return{pitchKeys:E,probabilities:i}}function gW(g){const A=[];let B=-1;for(let I=0;I<=g.length;I++){const Q=I<g.length&&g[I]===null;if(Q&&B===-1)B=I;else if(!Q&&B!==-1){const E=B>0?g[B-1]:null,i=I<g.length?g[I]:null;A.push({startIndex:B,endIndex:I,length:I-B,previousKey:E,nextKey:i}),B=-1}}return A}function Ki(g,A,B,I,Q){if(g.keys.length===0||B+g.keys.length>A.length)return!1;const E=g.keys[0],i=g.keys[g.keys.length-1];if(I!==null&&E===I)return!1;if(Q!==null){if(typeof i=="string"&&typeof Q=="string"&&!TQ[i]?.includes(Q)&&i!==Q)return!1;if(typeof i=="number"&&typeof Q=="number"){if(I!==null&&typeof I=="number"){if(!(I<Q&&i>=I&&i<=Q||I>Q&&i<=I&&i>=Q)&&i!==Q)return!1}else if(Math.abs(i-Q)>1)return!1}}return!0}function IW(g,A,B,I,Q){const E=[],i=A.filter(s=>s.difficulty<=B&&s.keys.length>0);i.sort((s,n)=>n.keys.length-s.keys.length);const e=i[0]?.controllerMode??"ddr";let a=0,C=g.previousKey,o=null,t=null;for(;a<g.length;){const s=g.length-a;let n;const r=i.filter(c=>c.keys.length===s);if(n=xo(r,g,a,C,g.nextKey,o,t,I,Q),!n){const c=i.filter(G=>G.keys.length<=s);n=xo(c,g,a,C,null,o,t,I,Q)}if(!n){const c=i.filter(G=>G.keys.length<=s);n=xo(c,g,a,C,null,null,null,null,Q)}if(n)E.push({pattern:n,startIndex:g.startIndex+a,filledLength:n.keys.length}),C=n.keys[n.keys.length-1],o=n.id,t=n.category,a+=n.keys.length;else{const c=a===g.length-1?g.nextKey:null,G=bd(C,c);E.push({pattern:{id:"__interpolated__",name:"Interpolated",controllerMode:e,keys:[G],measures:0,tags:["interpolated"],category:"transition",difficulty:1},startIndex:g.startIndex+a,filledLength:1}),C=G,a++}}return E}function xo(g,A,B,I,Q,E,i,e,a=Math.random){const C=g.filter(n=>Ki(n,A,B,I,Q)&&n.id!==E&&n.category!==i&&!e?.has(n.id));if(C.length>0)return zi(C,a);const o=g.filter(n=>Ki(n,A,B,I,Q)&&n.id!==E&&!e?.has(n.id));if(o.length>0)return zi(o,a);const t=g.filter(n=>Ki(n,A,B,I,Q)&&n.id!==E);if(t.length>0)return zi(t,a);const s=g.filter(n=>Ki(n,A,B,I,Q));if(s.length>0)return zi(s,a)}function zi(g,A=Math.random){const B=g[0].keys.length,I=g.filter(Q=>Q.keys.length===B);return I[Math.floor(A()*I.length)]}function QW(g,A,B){const I=g.length,Q=[...g],E=new Array(I).fill(void 0);for(let i=0;i<A.length;i++){const e=B[i];for(const a of e)for(let C=0;C<a.filledLength;C++){const o=a.startIndex+C;if(o>=I)break;Q[o]=a.pattern.keys[C],E[o]=a.pattern.id}}for(let i=0;i<I;i++)if(Q[i]===null){const e=i>0?Q[i-1]:null,a=i<I-1?Q[i+1]:null;Q[i]=bd(e,a)}return{keys:Q,patternIds:E}}function EW(g,A){const B=[];if(A<=0||g.length===0)return{positions:B};let I=1;const Q=g[0];for(let E=1;E<g.length;E++)g[E]===Q?(I++,I>A&&B.push(E)):I=1;return{positions:B}}function iW(g,A,B,I=Math.random){const Q=A.filter(e=>e.difficulty<=B);if(Q.length===0)return g;const E=Q.filter(e=>e.keys[0]!==g),i=E.length>0?E:Q;return i[Math.floor(I()*i.length)].keys[0]}const TQ={up:["left","right"],down:["left","right"],left:["up","down"],right:["up","down"]};function bd(g,A){if(g===null)return A===null?typeof A=="string"?"left":3:typeof A=="string"?TQ[A][0]:Math.max(1,A-1);if(A===null)return g;if(typeof g=="string"&&typeof A=="string"){const B=g,I=A;if(B===I)return g;if(TQ[B].includes(I))return A;const E=TQ[B],i=TQ[I];for(const e of E)if(i.includes(e))return e;return E[0]}if(typeof g=="number"&&typeof A=="number"){const B=g,I=A;return B===I?g:I>B?Math.min(5,B+1):Math.max(1,B-1)}return g}class Ti{constructor(A){this.config=rC(A);const B=ah(this.config);if(!B.valid)throw new Error(`Invalid button mapping config: ${B.errors.join(", ")}`)}seededRandom(A){return this.config.seed?mg(Pg(this.config.seed,A)):Math.random()}getConfig(){return{...this.config}}mapVariant(A,B,I){const Q=this.mapButtons(A,I,A.difficulty),E=this.buildMetadata(Q,I),i=new Map,e=new Map,a=new Map;for(const C of Q)i.set(C.beatIndex,String(C.key)),e.set(C.beatIndex,C.source),a.set(C.beatIndex,C.patternId);return{variant:A,rhythmMetadata:B,buttonMetadata:E,keyAssignments:i,mappingSources:e,mappingPatternIds:a}}map(A,B,I){const Q=A.difficultyVariants[B];return this.mapVariant(Q,A.metadata,I)}mapAll(A,B){return{easy:this.map(A,"easy",B),medium:this.map(A,"medium",B),hard:this.map(A,"hard",B)}}mapButtons(A,B,I){const Q=A.beats;if(this.config.controllerMode==="tap")return Q.map((h,w)=>({beatIndex:w,timestamp:h.timestamp,key:"tap",source:"pattern",patternId:void 0,probability:void 0}));const E=[],i=this.getMaxPatternDifficulty(I);let e=0;const{pitchKeys:a,probabilities:C}=BW(Q,B,this.config.controllerMode,I),o=this.classifyPitchVsPattern(a,C,this.config.pitchInfluenceWeight),t=a.map((h,w)=>h!==null&&o[w]?h:null),s=this.config.controllerMode==="ddr"?Ho.patterns:Jo.patterns,n=gW(t),r=new Set,c=n.map((h,w)=>{const y=IW(h,s,i,r,()=>this.seededRandom(`run:${w}:pick:${e++}`));for(const R of y)R.pattern.id!=="__interpolated__"&&r.add(R.pattern.id);return y}),{keys:G,patternIds:D}=QW(t,n,c);for(let h=0;h<Q.length;h++){const w=t[h]!==null;E.push({beatIndex:h,timestamp:Q[h].timestamp,key:G[h],source:w?"pitch":"pattern",patternId:w?void 0:D[h],probability:C[h]})}return this.config.consecutiveSameKeyLimit>0&&this.applyConsecutiveLimit(E,s,i),E}classifyPitchVsPattern(A,B,I){const Q=new Array(A.length).fill(!1),E=A.map((a,C)=>({index:C,probability:B[C],hasPitch:a!==null})).filter(a=>a.hasPitch);E.sort((a,C)=>a.probability-C.probability);const i=Math.floor(E.length*(1-I)),e=new Set(E.slice(0,i).map(a=>a.index));for(let a=0;a<A.length;a++)A[a]===null||e.has(a)?Q[a]=!1:Q[a]=!0;return Q}getMaxPatternDifficulty(A){switch(A){case"easy":return 3;case"medium":return 6;case"hard":return 10;case"natural":return 10;case"custom":return 6}}applyConsecutiveLimit(A,B,I){const Q=A.map(e=>e.key),E=this.config.consecutiveSameKeyLimit,{positions:i}=EW(Q,E);for(let e=0;e<i.length;e++){const a=i[e],C=iW(A[a].key,B,I,()=>this.seededRandom(`var:${a}:${e}`));C!==A[a].key&&(A[a].key=C,A[a].source="pattern",A[a].patternId="consecutive_limit_fix")}}buildMetadata(A,B){const I=new Set,Q=new Map;let E=0,i=0;const e=new Set;for(const r of A){const c=String(r.key);I.add(c),Q.set(c,(Q.get(c)??0)+1),r.source==="pitch"?E++:i++,r.patternId&&e.add(r.patternId)}let a,C,o;if(B&&B.length>0){a={up:0,down:0,stable:0,none:0},C={unison:0,small:0,medium:0,large:0,very_large:0},o={low:0,mid:0,high:0};for(const r of B)a[r.direction]++,r.intervalCategory&&C[r.intervalCategory]++,r.band&&o[r.band]++}const t=[];let s,n=0;for(let r=0;r<A.length;r++){const c=A[r].patternId;c&&c===s||(s&&t.push({patternId:s,startIndex:n,length:r-n}),s=c,n=r)}return s&&t.push({patternId:s,startIndex:n,length:A.length-n}),{controllerMode:this.config.controllerMode,keysUsed:Array.from(I),pitchInfluencedBeats:E,patternInfluencedBeats:i,patternsUsed:Array.from(e),buttonDistribution:Q,directionStats:a,intervalStats:C,bandStats:o,patternPlacements:t}}static getDDRTransitions(){return ud}static getGuitarHeroTransitions(){return md}getAvailableButtons(){return this.config.controllerMode==="ddr"?["up","down","left","right"]:[1,2,3,4,5]}isValidButton(A){return this.config.controllerMode==="ddr"?["up","down","left","right"].includes(A):[1,2,3,4,5].includes(A)}}class Oi{convertToChartedBeatMap(A,B,I,Q,E,i){const e=[],a=[];for(let o=0;o<A.beats.length;o++){const t=A.beats[o],s=this.convertBeat(t,B,I.get(o),o,E?.get(o),i?.get(o));e.push(s),s.isDetected&&a.push(o)}const C=this.buildChartMetadata(A,Q,e,I);return{audioId:B.audioId,duration:B.duration,beats:e,detectedBeatIndices:a,downbeatConfig:B.downbeatConfig,quarterNoteInterval:B.quarterNoteInterval,bpm:B.quarterNoteBpm,chartMetadata:C}}convertWithOptions(A,B){const I={version:"1.0.0",algorithm:"procedural-generation",minBpm:60,maxBpm:200,sensitivity:1,filter:.5,noiseFloorThreshold:.01,hopSizeMs:10,fftSize:2048,dpAlpha:.5,melBands:40,highPassCutoff:80,gaussianSmoothMs:20,tempoCenter:.5,tempoWidth:.5,useOctaveResolution:!1,useTripleMeter:!1,generatedAt:new Date().toISOString()},Q={audioId:B.audioId,duration:B.duration,beats:[],detectedBeatIndices:[],quarterNoteInterval:B.quarterNoteInterval,quarterNoteBpm:60/B.quarterNoteInterval,downbeatConfig:B.downbeatConfig,originalMetadata:I};return this.convertToChartedBeatMap(A,Q,B.keyAssignments,B.metadata)}convertBeat(A,B,I,Q,E,i){const e=A.beatIndex,a=this.getParentBeat(B,e),C=this.calculateBeatInMeasure(a,A.gridPosition,A.gridType),o=this.isBeatDetected(A),t=this.mapGridToSubdivision(A.gridType),s=this.calculateConfidence(A,o);return{timestamp:A.timestamp,beatInMeasure:C,isDownbeat:(a?.isDownbeat??!1)&&A.gridPosition===0,measureNumber:a?.measureNumber??0,intensity:A.intensity,confidence:s,requiredKey:I,quarterNoteIndex:e,subdivisionPosition:A.gridPosition,isDetected:o,subdivisionType:t,sourceBand:A.sourceBand,quantizationError:A.quantizationError,mappingSource:E,patternId:i}}getParentBeat(A,B){return B<0||B>=A.beats.length?null:A.beats[B]}calculateBeatInMeasure(A,B,I){const Q=A?.beatInMeasure??0;return oh(Q,B,I)}isBeatDetected(A){return A.quantizationError===void 0?!0:A.quantizationError<10}mapGridToSubdivision(A){return Ch(A)}calculateConfidence(A,B){return B?Math.min(1,.8+A.intensity*.2):.8}buildChartMetadata(A,B,I,Q){const E=B.pitchInfluencedBeats??0,i=this.getUniqueKeys(I);return this.getSubdivisionTypesUsed(I),{difficulty:A.difficulty,keysUsed:i,pitchInfluencedBeats:E,patternsUsed:B.patternsUsed??[],rhythmMetadata:B.rhythmMetadata??this.createDefaultRhythmMetadata(A),pitchMetadata:B.pitchMetadata??null,generatedAt:B.generatedAt??new Date().toISOString(),seed:B.seed}}getUniqueKeys(A){const B=new Set;for(const I of A)I.requiredKey!==void 0&&B.add(I.requiredKey);return Array.from(B).sort()}getSubdivisionTypesUsed(A){const B=new Set;for(const I of A)B.add(I.subdivisionType);return Array.from(B)}createDefaultRhythmMetadata(A){return{difficulty:A.difficulty,bandsAnalyzed:["low","mid","high"],transientsDetected:A.beats.length,averageDensity:A.beats.length/180,naturalDifficulty:A.difficulty}}static fromMappedResult(A,B,I,Q,E,i,e){const a=E??new Map;if(a.size===0&&I.keysUsed.length>0)for(let n=0;n<A.beats.length;n++)a.set(n,I.keysUsed[n%I.keysUsed.length]);const C=new Oi,o={difficulty:Q.difficulty,bandsAnalyzed:Q.bandsAnalyzed,transientsDetected:Q.transientsDetected,averageDensity:Q.averageDensity,naturalDifficulty:Q.naturalDifficulty},t=I.directionStats?{melodyRange:null,directionStats:I.directionStats,intervalStats:I.intervalStats??null}:null,s={difficulty:A.difficulty,keysUsed:I.keysUsed,pitchInfluencedBeats:I.pitchInfluencedBeats,patternsUsed:I.patternsUsed,rhythmMetadata:o,pitchMetadata:t,generatedAt:new Date().toISOString()};return C.convertToChartedBeatMap(A,B,a,s,i,e)}}const OQ={difficulty:"medium",controllerMode:"ddr",rhythm:{},buttons:{},enableCache:!0,cacheMaxAge:1800*1e3};class ji{constructor(A={}){this.cache=new Map,this.cacheHits=0,this.cacheMisses=0,this.options={...OQ,...A,rhythm:{...OQ.rhythm,...A.rhythm},buttons:{...OQ.buttons,...A.buttons}},this.buttonConfig=rC({...this.options.buttons,difficulty:this.options.difficulty,controllerMode:this.options.controllerMode,seed:this.options.seed})}getOptions(){return{...this.options}}generateCacheKey(A,B,I){return`${A}:${B}:${I}`}createConfigFingerprint(A){const B=this.options.rhythm||{};return A==="rhythm"?JSON.stringify({measureStartOffset:B.measureStartOffset,minimumTransientIntensity:B.minimumTransientIntensity,seed:B.seed}):A==="pitch"?JSON.stringify({pitchAlgorithm:this.options.pitchAlgorithm,crepeModelUrl:this.options.crepeModelUrl,voicingThreshold:this.options.voicingThreshold}):"default"}getFromCache(A){if(!this.options.enableCache)return null;const B=this.cache.get(A);if(!B)return this.cacheMisses++,null;const I=Date.now()-B.timestamp,Q=this.options.cacheMaxAge??OQ.cacheMaxAge;return I>Q?(this.cache.delete(A),this.cacheMisses++,null):(this.cacheHits++,B.result)}setCache(A,B){this.options.enableCache&&this.cache.set(A,{result:B,timestamp:Date.now(),key:A})}clearCache(){this.cache.clear(),this.cacheHits=0,this.cacheMisses=0}pruneExpiredCache(){const A=Date.now(),B=this.options.cacheMaxAge??OQ.cacheMaxAge;for(const[I,Q]of this.cache.entries())A-Q.timestamp>B&&this.cache.delete(I)}getCacheStats(){const A=[];for(const[B]of this.cache.entries()){const I=B.split(":")[1];A.includes(I)||A.push(I)}return{entryCount:this.cache.size,cachedPhases:A,hits:this.cacheHits,misses:this.cacheMisses}}isCached(A,B){const I=this.createConfigFingerprint(B),Q=this.generateCacheKey(A,B,I);return this.cache.has(Q)}getCacheHitRatio(){const A=this.cacheHits+this.cacheMisses;return A===0?0:this.cacheHits/A}async generate(A,B,I,Q){const E=(s,n,r)=>{I?.({stage:s,progress:n,message:r})};Q?.throwIfAborted();const i=B.audioId;E("rhythm",0,"Starting rhythm generation...");let e=null;if(this.options.cachedRhythm)e=this.options.cachedRhythm,E("rhythm",1,"Using pre-generated rhythm from store");else{const s=this.generateCacheKey(i,"rhythm",this.createConfigFingerprint("rhythm"));e=this.getFromCache(s),e?E("rhythm",1,"Rhythm loaded from cache"):(e=await this.generateRhythm(A,B,(n,r,c)=>{E("rhythm",r,`[${n}] ${c}`)},Q),this.setCache(s,e),E("rhythm",1,"Rhythm generation complete"))}Q?.throwIfAborted();let a=null;if(this.buttonConfig.pitchInfluenceWeight>0&&this.buttonConfig.controllerMode!=="tap"){const s=this.generateCacheKey(i,"pitch",this.createConfigFingerprint("pitch"));a=this.getFromCache(s),a?E("pitch",1,"Pitch analysis loaded from cache"):(E("pitch",0,"Starting pitch analysis..."),a=await this.analyzePitch(A,e,(n,r)=>{E("pitch",n,r)}),this.setCache(s,a),E("pitch",1,"Pitch analysis complete"))}else E("pitch",1,"Pitch analysis skipped");Q?.throwIfAborted(),E("buttons",0,"Starting button mapping...");const C=this.mapButtons(e,a);E("buttons",1,"Button mapping complete"),Q?.throwIfAborted(),E("conversion",0,"Converting to playable chart...");const o=this.convertToChart(C,B);E("conversion",1,"Chart conversion complete"),Q?.throwIfAborted(),E("finalizing",0,"Finalizing level...");const t=this.buildLevel(o,C,e,a);return E("finalizing",1,"Level generation complete"),t}async generateAllDifficulties(A,B,I,Q){const E=["easy","medium","hard","natural"],i={easy:null,medium:null,hard:null,natural:null};Q?.throwIfAborted();const e=B.audioId;let a=null;if(this.options.cachedRhythm)a=this.options.cachedRhythm;else{const o=this.generateCacheKey(e,"rhythm",this.createConfigFingerprint("rhythm"));a=this.getFromCache(o),a||(a=await this.generateRhythm(A,B,void 0,Q),this.setCache(o,a))}Q?.throwIfAborted();let C=null;if(this.buttonConfig.pitchInfluenceWeight>0&&this.buttonConfig.controllerMode!=="tap"){const o=this.generateCacheKey(e,"pitch",this.createConfigFingerprint("pitch"));C=this.getFromCache(o),C||(C=await this.analyzePitch(A,a),this.setCache(o,C))}Q?.throwIfAborted();for(let o=0;o<E.length;o++){const t=E[o];I?.({stage:"buttons",progress:o/E.length,message:`Generating ${t} chart...`}),Q?.throwIfAborted();const s=new ji({...this.options,difficulty:t,buttons:{...this.options.buttons,difficulty:t}}),n=s.mapButtons(a,C),r=s.convertToChart(n,B),c=s.buildLevel(r,n,a,C);i[t]=c}return{easy:i.easy,medium:i.medium,hard:i.hard,natural:i.natural}}async generateAtDensity(A,B,I,Q,E){const i=(D,h,w)=>{Q?.({stage:D,progress:h,message:w})};E?.throwIfAborted();const e=B.audioId;i("rhythm",0,"Starting rhythm generation...");let a=null;if(this.options.cachedRhythm)a=this.options.cachedRhythm,i("rhythm",1,"Using pre-generated rhythm from store");else{const D=this.generateCacheKey(e,"rhythm",this.createConfigFingerprint("rhythm"));a=this.getFromCache(D),a?i("rhythm",1,"Rhythm loaded from cache"):(a=await this.generateRhythm(A,B,(h,w,y)=>{i("rhythm",w,`[${h}] ${y}`)},E,!0),this.setCache(D,a),i("rhythm",1,"Rhythm generation complete"))}E?.throwIfAborted(),i("buttons",0,"Generating density-based variant...");const C=this.collectGridDecisions(a.analysis.quantizationResult),t=new vi({seed:this.options.seed}).generateAtDensity(a.composite,I,B,a.analysis.phraseAnalysis,C);i("buttons",.3,"Density variant generated");let s=null;if(this.buttonConfig.pitchInfluenceWeight>0&&this.buttonConfig.controllerMode!=="tap"){const D=this.generateCacheKey(e,"pitch",this.createConfigFingerprint("pitch"));s=this.getFromCache(D),s?i("pitch",.5,"Pitch analysis loaded from cache"):(i("pitch",0,"Starting pitch analysis..."),s=await this.analyzePitch(A,a,(h,w)=>{i("pitch",h,w)}),this.setCache(D,s),i("pitch",1,"Pitch analysis complete"))}else i("pitch",1,"Pitch analysis skipped");E?.throwIfAborted(),i("buttons",.6,"Mapping buttons...");const r=new Ti(this.buttonConfig).mapVariant(t,a.metadata,s?.pitchByBeat);i("buttons",1,"Button mapping complete"),E?.throwIfAborted(),i("conversion",0,"Converting to playable chart...");const c=this.convertToChart(r,B);i("conversion",1,"Chart conversion complete"),E?.throwIfAborted(),i("finalizing",0,"Finalizing level...");const G=this.buildLevel(c,r,a,s,"custom");return i("finalizing",1,"Level generation complete"),G}async generateAtDensities(A,B,I,Q,E){const i=new Map;E?.throwIfAborted();const e=B.audioId;Q?.({stage:"rhythm",progress:0,message:"Starting rhythm generation..."});let a=null;if(this.options.cachedRhythm)a=this.options.cachedRhythm,Q?.({stage:"rhythm",progress:1,message:"Using pre-generated rhythm from store"});else{const s=this.generateCacheKey(e,"rhythm",this.createConfigFingerprint("rhythm"));a=this.getFromCache(s),a?Q?.({stage:"rhythm",progress:1,message:"Rhythm loaded from cache"}):(a=await this.generateRhythm(A,B,void 0,E,!0),this.setCache(s,a),Q?.({stage:"rhythm",progress:1,message:"Rhythm generation complete"}))}E?.throwIfAborted();let C=null;if(this.buttonConfig.pitchInfluenceWeight>0&&this.buttonConfig.controllerMode!=="tap"){const s=this.generateCacheKey(e,"pitch",this.createConfigFingerprint("pitch"));C=this.getFromCache(s),C?Q?.({stage:"pitch",progress:1,message:"Pitch analysis loaded from cache"}):(Q?.({stage:"pitch",progress:0,message:"Starting pitch analysis..."}),C=await this.analyzePitch(A,a),this.setCache(s,C),Q?.({stage:"pitch",progress:1,message:"Pitch analysis complete"}))}E?.throwIfAborted();const o=this.collectGridDecisions(a.analysis.quantizationResult),t=new vi({seed:this.options.seed});for(let s=0;s<I.length;s++){const{label:n,config:r}=I[s];Q?.({stage:"buttons",progress:s/I.length,message:`Generating ${n} density variant...`}),E?.throwIfAborted();const c=t.generateAtDensity(a.composite,r,B,a.analysis.phraseAnalysis,o),D=new Ti(this.buttonConfig).mapVariant(c,a.metadata,C?.pitchByBeat),h=this.convertToChart(D,B),w=this.buildLevel(h,D,a,C,"custom");i.set(n,w)}return Q?.({stage:"finalizing",progress:1,message:"All density variants generated"}),i}collectGridDecisions(A){const B=new Map,I=[...A.streams.low.gridDecisions,...A.streams.mid.gridDecisions,...A.streams.high.gridDecisions];for(const Q of I){const E=B.get(Q.beatIndex);(!E||Q.confidence>E.confidence)&&B.set(Q.beatIndex,Q)}return B}async generateRhythm(A,B,I,Q,E){return await new kg({...this.options.rhythm,skipDifficultyVariants:E}).generate(A,B,Q,I?(a,C,o)=>{I(a,C,o)}:void 0)}async analyzePitch(A,B,I){I?.(.1,"Linking pitch to beats...");const Q={};this.options.pitchAlgorithm!==void 0&&(Q.pitchAlgorithm=this.options.pitchAlgorithm),this.options.crepeModelUrl!==void 0&&(Q.crepeModelUrl=this.options.crepeModelUrl),this.options.resolveUrl!==void 0&&(Q.resolveUrl=this.options.resolveUrl),this.options.voicingThreshold!==void 0&&(Q.pitchDetector={...Q.pitchDetector,voicingThreshold:this.options.voicingThreshold});const E=new Fd(Q),i=await E.linkWithComposite(B.composite,A);I?.(.4,"Analyzing melody contour...");const a=new yN().analyze(i);I?.(.8,"Deriving variant pitches...");let C;if(!B.difficultyVariants)C=a.pitchByBeat;else{const o=this.options.difficulty==="custom"?"medium":this.options.difficulty;C=E.deriveVariantPitches(B.difficultyVariants[o],a.pitchByBeat)}return{...a,pitchByBeat:C}}mapButtons(A,B){const I=new Ti(this.buttonConfig),Q=this.options.difficulty==="custom"?"medium":this.options.difficulty;return I.map(A,Q,B?.pitchByBeat)}convertToChart(A,B){return Oi.fromMappedResult(A.variant,B,A.buttonMetadata,A.rhythmMetadata,A.keyAssignments,A.mappingSources,A.mappingPatternIds)}buildLevel(A,B,I,Q,E){const i={difficulty:E??this.options.difficulty,controllerMode:this.options.controllerMode,rhythmMetadata:B.rhythmMetadata,buttonMetadata:B.buttonMetadata,pitchMetadata:Q?{melodyRange:Q.melodyContour.range.minNote!=="N/A"?{min:Q.melodyContour.range.minNote,max:Q.melodyContour.range.maxNote}:null,directionStats:Q.directionStats,intervalStats:Q.intervalStats}:null,chartMetadata:{totalBeats:A.beats.length,detectedBeats:A.detectedBeatIndices.length,generatedBeats:A.beats.length-A.detectedBeatIndices.length},generationConfig:this.options};return{chart:A,variant:B.variant,rhythm:I,pitchAnalysis:Q,metadata:i}}}class eW{static toExportData(A,B={}){const{chart:I,metadata:Q,pitchAnalysis:E}=A,i=this.buildDetectedBeats(I),e=this.buildMergedBeats(I),a=this.buildInterpolatedMetadata(I),C=this.buildSubdivisionData(I),o=this.buildChartExportData(I,Q),t=this.buildGenerationMetadata(Q,E);return{version:1,format:"full-beatmap",audioId:I.audioId,audioTitle:B.includeAudioTitle?B.audioTitle:void 0,exportedAt:Date.now(),duration:I.duration,quarterNoteBpm:I.bpm,quarterNoteConfidence:I.chartMetadata.rhythmMetadata.averageDensity,detectedBeats:i,mergedBeats:e,interpolatedMetadata:a,subdivision:C,chart:o,trackReference:B.trackReference,generationSource:"procedural",generationMetadata:t}}static buildDetectedBeats(A){const B=[],I=new Set(A.detectedBeatIndices);for(let Q=0;Q<A.beats.length;Q++){const E=A.beats[Q];I.has(Q)&&B.push({timestamp:E.timestamp,beatInMeasure:E.beatInMeasure,isDownbeat:E.isDownbeat,measureNumber:E.measureNumber,intensity:E.intensity,confidence:E.confidence,requiredKey:E.requiredKey})}return B}static buildMergedBeats(A){const B=new Set(A.detectedBeatIndices);return A.beats.map((I,Q)=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,requiredKey:I.requiredKey,source:B.has(Q)?"detected":"interpolated",distanceToAnchor:I.quantizationError}))}static buildInterpolatedMetadata(A){const B=A.detectedBeatIndices.length,I=A.beats.length;return{quarterNoteInterval:A.quarterNoteInterval,quarterNoteBpm:A.bpm,quarterNoteConfidence:.9,detectedBeatCount:B,mergedBeatCount:I}}static buildSubdivisionData(A){const B=A.beats.map((e,a)=>this.chartedBeatToExportBeat(e,a,A.detectedBeatIndices)),I=new Set,Q=[];B.forEach((e,a)=>{I.add(e.subdivisionType),Q.push([a,e.subdivisionType])});const E={beatSubdivisions:Q,defaultSubdivision:"sixteenth"},i={originalBeatCount:A.beats.length-A.detectedBeatIndices.length,subdividedBeatCount:A.beats.length,averageDensityMultiplier:1,explicitBeatCount:A.detectedBeatIndices.length};return{config:E,beats:B,metadata:i}}static chartedBeatToExportBeat(A,B,I){const Q=I.includes(B);return{timestamp:A.timestamp,beatInMeasure:A.beatInMeasure,isDownbeat:A.isDownbeat,measureNumber:A.measureNumber,intensity:A.intensity,confidence:A.confidence,requiredKey:A.requiredKey,isDetected:Q,originalBeatIndex:Q?B:void 0,subdivisionType:A.subdivisionType,quarterNoteIndex:A.quarterNoteIndex,subdivisionPosition:A.subdivisionPosition,sourceBand:A.sourceBand,quantizationError:A.quantizationError}}static buildChartExportData(A,B){const I=A.beats.map(E=>E.requiredKey).filter(E=>E!==void 0),Q=[...new Set(I)];return Q.length===0?null:{style:B.controllerMode==="ddr"?"ddr":B.controllerMode==="guitar_hero"?"guitar":"tap",keyCount:Q.length,usedKeys:Q}}static buildGenerationMetadata(A,B){const I=A.rhythmMetadata;return{difficulty:A.difficulty,pitchInfluenceWeight:A.generationConfig.buttons?.pitchInfluenceWeight??1,patternsUsed:A.buttonMetadata.patternsUsed,controllerMode:A.controllerMode,seed:A.generationConfig.seed,generatedAt:new Date().toISOString(),directionStats:B?.directionStats??void 0,intervalStats:B?.intervalStats??void 0,rhythmMetadata:{difficulty:I.difficulty,bandsAnalyzed:I.bandsAnalyzed,transientsDetected:I.transientsDetected,averageDensity:I.averageDensity}}}static fromExportData(A){const B=this.validate(A);if(!B.success||!B.data)throw new Error(`Invalid export data: ${B.error??"Unknown error"}`);const I=B.data,Q=this.reconstructChart(I),E=this.reconstructVariant(I,Q),i=this.reconstructRhythm(I,Q),e=this.reconstructPitchAnalysis(I),a=this.reconstructMetadata(I,Q);return{chart:Q,variant:E,rhythm:i,pitchAnalysis:e,metadata:a}}static reconstructChart(A){if(!A.subdivision)return this.reconstructChartFromMergedBeats(A);const B=A.subdivision.beats.map((i,e)=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,quarterNoteIndex:i.quarterNoteIndex??0,subdivisionPosition:i.subdivisionPosition??0,isDetected:i.isDetected,subdivisionType:i.subdivisionType,sourceBand:i.sourceBand??"mid",quantizationError:i.quantizationError})),I=A.subdivision.beats.map((i,e)=>i.isDetected?e:-1).filter(i=>i>=0),Q=this.reconstructDownbeatConfig(B),E=this.reconstructChartMetadata(A);return{audioId:A.audioId,duration:A.duration,beats:B,detectedBeatIndices:I,downbeatConfig:Q,quarterNoteInterval:A.interpolatedMetadata.quarterNoteInterval,bpm:A.quarterNoteBpm,chartMetadata:E}}static reconstructChartFromMergedBeats(A){const B=A.mergedBeats.map((i,e)=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,quarterNoteIndex:e,subdivisionPosition:0,isDetected:i.source==="detected",subdivisionType:"quarter",sourceBand:"mid",quantizationError:i.distanceToAnchor})),I=A.mergedBeats.map((i,e)=>i.source==="detected"?e:-1).filter(i=>i>=0),Q=this.reconstructDownbeatConfig(B),E=this.reconstructChartMetadata(A);return{audioId:A.audioId,duration:A.duration,beats:B,detectedBeatIndices:I,downbeatConfig:Q,quarterNoteInterval:A.interpolatedMetadata.quarterNoteInterval,bpm:A.quarterNoteBpm,chartMetadata:E}}static reconstructDownbeatConfig(A){const B=A.filter(E=>E.isDownbeat);if(B.length===0)return{segments:[{startBeat:0,downbeatBeatIndex:0,timeSignature:{beatsPerMeasure:4}}]};const I={beatsPerMeasure:4};return{segments:[{startBeat:0,downbeatBeatIndex:B[0]?.measureNumber??0,timeSignature:I}]}}static reconstructChartMetadata(A){const B=A.generationMetadata;return{difficulty:this.parseDifficultyLevel(B?.difficulty),keysUsed:A.chart?.usedKeys??[],pitchInfluencedBeats:B?.pitchInfluenceWeight&&B.pitchInfluenceWeight>0?A.subdivision?.beats.filter(I=>I.requiredKey).length??0:0,patternsUsed:B?.patternsUsed??[],rhythmMetadata:{difficulty:B?.rhythmMetadata?.difficulty??"medium",bandsAnalyzed:B?.rhythmMetadata?.bandsAnalyzed??["low","mid","high"],transientsDetected:B?.rhythmMetadata?.transientsDetected??A.detectedBeats.length,averageDensity:B?.rhythmMetadata?.averageDensity??.5,naturalDifficulty:this.parseDifficultyLevel(B?.difficulty)},pitchMetadata:B?.directionStats||B?.intervalStats?{melodyRange:null,directionStats:B.directionStats??null,intervalStats:B.intervalStats??null}:null,generatedAt:B?.generatedAt??new Date().toISOString(),seed:B?.seed}}static parseDifficultyLevel(A){return A==="easy"||A==="medium"||A==="hard"||A==="natural"||A==="custom"?A:"medium"}static parseDifficultyPreset(A){return A==="easy"||A==="medium"||A==="hard"||A==="natural"||A==="custom"?A:"medium"}static reconstructVariant(A,B){const I=A.generationMetadata,Q=this.parseDifficultyLevel(I?.difficulty),E=B.beats.map(i=>({timestamp:i.timestamp,beatIndex:i.quarterNoteIndex,gridPosition:i.subdivisionPosition,gridType:this.subdivisionTypeToGridType(i.subdivisionType),intensity:i.intensity,band:i.sourceBand,quantizationError:i.quantizationError,sourceBand:i.sourceBand}));return{difficulty:Q,beats:E,isUnedited:!0,editType:"none",editAmount:0,patternsInserted:I?.patternsUsed}}static subdivisionTypeToGridType(A){switch(A){case"sixteenth":case"eighth":case"quarter":return"straight_16th";case"triplet8":case"triplet4":return"triplet_8th";default:return"straight_16th"}}static reconstructRhythm(A,B){const I=A.generationMetadata,Q=this.parseDifficultyLevel(I?.difficulty),E=t=>({audioId:A.audioId,duration:A.duration,beats:B.beats.filter(s=>s.sourceBand===t).map(s=>({timestamp:s.timestamp,beatIndex:s.quarterNoteIndex,gridPosition:s.subdivisionPosition,gridType:this.subdivisionTypeToGridType(s.subdivisionType),intensity:s.intensity,band:t,quantizationError:s.quantizationError})),gridDecisions:[],quarterNoteInterval:A.interpolatedMetadata.quarterNoteInterval}),i={low:E("low"),mid:E("mid"),high:E("high")},e=this.reconstructVariant(A,B),a=B.beats.map(t=>({timestamp:t.timestamp,beatIndex:t.quarterNoteIndex,gridPosition:t.subdivisionPosition,gridType:this.subdivisionTypeToGridType(t.subdivisionType),intensity:t.intensity,band:t.sourceBand,quantizationError:t.quantizationError,sourceBand:t.sourceBand})),C={low:0,mid:0,high:0};for(const t of a)C[t.sourceBand]++;const o={beats:a,sections:[],naturalDifficulty:Q,quarterNoteInterval:A.interpolatedMetadata.quarterNoteInterval,metadata:{totalBeats:a.length,sectionCount:0,beatsPerBand:C,sectionsPerBand:{low:0,mid:0,high:0}}};return{difficultyVariants:{easy:{...e,difficulty:"easy"},medium:{...e,difficulty:"medium"},hard:{...e,difficulty:"hard"},natural:{...e,difficulty:"natural"}},bandStreams:i,composite:o,analysis:{transientAnalysis:{transients:A.detectedBeats.map(t=>({timestamp:t.timestamp,intensity:t.intensity,band:"mid",detectionMethod:"energy"})),bandTransients:new Map([["low",[]],["mid",A.detectedBeats.map(t=>({timestamp:t.timestamp,intensity:t.intensity,band:"mid",detectionMethod:"energy"}))],["high",[]]]),metadata:{totalTransients:A.detectedBeats.length,transientsPerBand:new Map([["low",0],["mid",A.detectedBeats.length],["high",0]]),duration:A.duration,averageIntensity:.5,detectionMethodsUsed:["energy"]}},quantizationResult:{streams:i,metadata:{densityValidation:{isValid:!0,bands:{low:{band:"low",isValid:!0,minIntervalDetected:1/0,requiredMinInterval:.1,retryCount:0,sensitivityReduction:0,finalIntensityThreshold:0,transientsRemaining:0},mid:{band:"mid",isValid:!0,minIntervalDetected:.1,requiredMinInterval:.1,retryCount:0,sensitivityReduction:0,finalIntensityThreshold:0,transientsRemaining:A.detectedBeats.length},high:{band:"high",isValid:!0,minIntervalDetected:1/0,requiredMinInterval:.1,retryCount:0,sensitivityReduction:0,finalIntensityThreshold:0,transientsRemaining:0}},maxRetryCount:0,maxSensitivityReduction:0},transientsFilteredByIntensity:0,transientsFilteredByBand:{low:0,mid:0,high:0}}},phraseAnalysis:{phrases:[],phrasesByBand:new Map([["low",[]],["mid",[]],["high",[]]]),mostSignificantPhrases:[],phrasesBySize:new Map,patternLibrary:[],bandAnalysis:{low:{band:"low",phrases:[],phrasesBySize:new Map,phrasesWithVariation:[]},mid:{band:"mid",phrases:[],phrasesBySize:new Map,phrasesWithVariation:[]},high:{band:"high",phrases:[],phrasesBySize:new Map,phrasesWithVariation:[]}}},densityAnalysis:{sections:[],perBeatDensity:[],bandMetrics:{low:{band:"low",totalBeats:0,totalTransients:0,notesPerSecond:1,minNotesPerSecond:0,maxNotesPerSecond:2,variance:0,densityCategory:"moderate",naturalDifficulty:"medium",perBeatDensity:[]},mid:{band:"mid",totalBeats:A.mergedBeats.length,totalTransients:A.detectedBeats.length,notesPerSecond:1,minNotesPerSecond:0,maxNotesPerSecond:2,variance:0,densityCategory:"moderate",naturalDifficulty:Q,perBeatDensity:[]},high:{band:"high",totalBeats:0,totalTransients:0,notesPerSecond:1,minNotesPerSecond:0,maxNotesPerSecond:2,variance:0,densityCategory:"moderate",naturalDifficulty:"medium",perBeatDensity:[]}},combinedMetrics:{totalTransients:A.detectedBeats.length,notesPerSecond:1,densityCategory:"moderate",naturalDifficulty:Q}},scoringResult:{sectionScores:[],bandTotals:{low:0,mid:0,high:0},bandAverages:{low:0,mid:0,high:0},sectionWinners:[],config:{beatsPerSection:8,ioiVarianceWeight:.25,syncopationWeight:.25,phraseSignificanceWeight:.25,densityWeight:.25,offbeatGridPositions:{straight_16th:[1,3],triplet_8th:[1,2],straight_8th:[1]}}}},metadata:this.buildFullRhythmMetadata(A,Q)}}static buildFullRhythmMetadata(A,B){const I=A.generationMetadata;return{difficulty:this.parseDifficultyPreset(I?.difficulty),bandsAnalyzed:["low","mid","high"],transientsDetected:A.detectedBeats.length,transientsFilteredByIntensity:0,densityValidationRetries:0,phrasesDetected:0,averageDensity:I?.rhythmMetadata?.averageDensity??.5,naturalDifficulty:B,generationConfig:{difficulty:this.parseDifficultyPreset(I?.difficulty),outputMode:"composite",measureStartOffset:0,minimumTransientIntensity:.1,transientConfig:void 0,rhythmicBalanceConfig:{strongBeatEmphasis:"natural",downbeatProximityRange:2,fillEmptyMeasures:!0,addedBeatIntensity:.45,marginSeconds:.5},seed:I?.seed,verbose:!1,enableCache:!0,cacheMaxAge:1800*1e3,skipDifficultyVariants:!1},duration:A.duration,totalBeats:A.mergedBeats.length}}static buildMinimalMelodyContour(){return{segments:[],direction:"stable",range:{minNote:"N/A",maxNote:"N/A",semitones:0},shortTermDirection:"stable",mediumTermDirection:"stable",longTermDirection:"stable"}}static reconstructPitchAnalysis(A){const B=A.generationMetadata;return!B||!B.directionStats&&!B.intervalStats?null:{pitchByBeat:[],melodyContour:this.buildMinimalMelodyContour(),directionStats:B.directionStats??{up:0,down:0,stable:0,none:0},intervalStats:B.intervalStats??{unison:0,small:0,medium:0,large:0,very_large:0},metadata:{totalBeats:0,voicedBeats:0,directionCalculatedBeats:0}}}static reconstructMetadata(A,B){const I=A.generationMetadata,Q=this.parseDifficultyPreset(I?.difficulty),E=I?.controllerMode??"ddr";return{difficulty:Q,controllerMode:E,rhythmMetadata:this.buildFullRhythmMetadata(A,this.parseDifficultyLevel(I?.difficulty)),buttonMetadata:{controllerMode:E,keysUsed:A.chart?.usedKeys??[],pitchInfluencedBeats:I?.pitchInfluenceWeight&&I.pitchInfluenceWeight>0?B.beats.filter(i=>i.requiredKey).length:0,patternInfluencedBeats:B.beats.filter(i=>i.requiredKey).length-(I?.pitchInfluenceWeight&&I.pitchInfluenceWeight>0?B.beats.filter(i=>i.requiredKey).length:0),patternsUsed:I?.patternsUsed??[],buttonDistribution:new Map},pitchMetadata:I?.directionStats||I?.intervalStats?{melodyRange:null,directionStats:I.directionStats??null,intervalStats:I.intervalStats??null}:null,chartMetadata:{totalBeats:B.beats.length,detectedBeats:B.detectedBeatIndices.length,generatedBeats:B.beats.length-B.detectedBeatIndices.length},generationConfig:{difficulty:Q,controllerMode:E,rhythm:{},buttons:{pitchInfluenceWeight:I?.pitchInfluenceWeight??1},seed:I?.seed}}}static toJSON(A,B={}){const I=this.toExportData(A,B);return JSON.stringify(I,null,2)}static fromJSON(A){let B;try{B=JSON.parse(A)}catch(Q){throw new Error(`Invalid JSON: ${Q instanceof Error?Q.message:"Unknown error"}`)}const I=this.validate(B);if(!I.success||!I.data)throw new Error(`Invalid level data: ${I.error??"Unknown error"}`);return this.fromExportData(I.data)}static async saveToFile(A,B,I={}){const Q=await Promise.resolve().then(()=>Eg),E=this.toJSON(A,I);await Q.writeFile(B,E,"utf-8")}static async loadFromFile(A){const I=await(await Promise.resolve().then(()=>Eg)).readFile(A,"utf-8");return this.fromJSON(I)}static validate(A){const B=[];if(typeof A!="object"||A===null)return{success:!1,error:"Data must be a non-null object"};const I=A;if(I.version!==1)return{success:!1,error:`Unsupported version: ${I.version}. Expected 1.`};if(I.format!=="full-beatmap")return{success:!1,error:`Invalid format: ${I.format}. Expected 'full-beatmap'.`};const Q=["audioId","duration","quarterNoteBpm","detectedBeats","mergedBeats"];for(const E of Q)if(!(E in I))return{success:!1,error:`Missing required field: ${E}`};if(!Array.isArray(I.detectedBeats))return{success:!1,error:"detectedBeats must be an array"};if(!Array.isArray(I.mergedBeats))return{success:!1,error:"mergedBeats must be an array"};if(!I.subdivision||typeof I.subdivision!="object")B.push("No subdivision data - level may not be playable");else{const E=I.subdivision;if(!Array.isArray(E.beats))return{success:!1,error:"subdivision.beats must be an array"}}if(I.chart===null)B.push("No chart data - level has no key assignments");else if(I.chart!==void 0&&typeof I.chart!="object")return{success:!1,error:"chart must be an object or null"};return typeof I.interpolatedMetadata!="object"||I.interpolatedMetadata===null?{success:!1,error:"interpolatedMetadata must be an object"}:th(A)?{success:!0,data:A,warnings:B.length>0?B:void 0}:{success:!1,error:"Data does not match FullBeatMapExportData format",warnings:B.length>0?B:void 0}}static toPack(A,B={}){const I={};for(const[Q,E]of Object.entries(A))if(E){const i={includeAudioTitle:B.includeAudioTitle,audioTitle:B.audioTitle};I[Q]=this.toExportData(E,i)}return{version:1,format:"level-pack",exportedAt:Date.now(),trackReference:B.trackReference,difficulties:I}}static fromPack(A){const B={};for(const[I,Q]of Object.entries(A.difficulties))if(Q)try{B[I]=this.fromExportData(Q)}catch{}return B}static packToJSON(A,B={}){const I=this.toPack(A,B);return JSON.stringify(I,null,2)}static packFromJSON(A){let B;try{B=JSON.parse(A)}catch(I){throw new Error(`Invalid JSON: ${I instanceof Error?I.message:"Unknown error"}`)}if(!sh(B))throw new Error("Data does not match LevelPackExport format");return this.fromPack(B)}static isProcedural(A){return A.generationSource==="procedural"}static getSummary(A){const B=[`Audio: ${A.audioId}`,`Duration: ${A.duration.toFixed(1)}s`,`BPM: ${A.quarterNoteBpm.toFixed(1)}`,`Beats: ${A.mergedBeats.length} (${A.detectedBeats.length} detected)`];return A.chart&&(B.push(`Keys: ${A.chart.keyCount} (${A.chart.style})`),B.push(`Keys used: ${A.chart.usedKeys.join(", ")}`)),A.generationMetadata&&(B.push(`Difficulty: ${A.generationMetadata.difficulty}`),B.push(`Controller: ${A.generationMetadata.controllerMode}`),B.push(`Pitch influence: ${(A.generationMetadata.pitchInfluenceWeight*100).toFixed(0)}%`)),B.join(`
53
- `)}}function aW(g){return typeof g=="string"&&["humanoid","beast","undead","dragon","fiend","construct","elemental","monstrosity"].includes(g)}function CW(g){return typeof g=="string"&&["common","uncommon","elite","boss"].includes(g)}function oW(g){return typeof g=="string"&&["brute","archer","support"].includes(g)}function tW(g){return typeof g=="string"&&["easy","medium","hard","deadly"].includes(g)}class sW{static calculatePartyLevel(A){if(A.length===0)return 1;const B=A.map(I=>I.level||1);return Rh(B)}static calculatePartyStrength(A){if(A.length===0)return 0;const B=A.reduce((C,o)=>C+(o.hp?.max||10),0),I=this.getAverageAC(A),Q=this.getAverageDamage(A),E=A.length,i=I/10,e=B*i,a=Q*E*10;return e+a}static getXPBudget(A,B){if(A.length===0)return 0;const I=A.map(Q=>Q.level||1);return ei(I,B)}static getAverageAC(A){if(A.length===0)return 10;const B=A.reduce((I,Q)=>I+(Q.armor_class||10),0);return Math.round(B/A.length)}static getAverageHP(A){if(A.length===0)return 10;const B=A.reduce((I,Q)=>I+(Q.hp?.max||10),0);return Math.round(B/A.length)}static getPartySize(A){return A.length}static getAverageDamage(A){if(A.length===0)return 0;const B=A.reduce((I,Q)=>I+this.estimateCharacterDamage(Q),0);return Math.round(B/A.length)}static estimateCharacterDamage(A){const B=A.ability_scores,I=A.equipment?.weapons?.find(e=>e.equipped);let Q,E;if(I?.damage?.dice){Q=I.damage.dice;const e=I.weaponProperties?.includes("ranged")??!1,a=I.weaponProperties?.includes("finesse")??!1;E=Math.floor(((B?.[e||a?"DEX":"STR"]??10)-10)/2)}else Q="1",E=Math.floor(((B?.STR??10)-10)/2);let i;try{const e=RA.parseDiceFormula(Q);i=e.diceCount*((e.diceSides+1)/2)+e.modifier+E}catch{i=E+1}return Math.round(i*10)/10}static analyzeParty(A){const B=this.calculatePartyLevel(A),I=this.getPartySize(A),Q=this.getAverageAC(A),E=this.getAverageHP(A),i=this.getAverageDamage(A),e=this.calculatePartyStrength(A),a=this.getXPBudget(A,"easy"),C=this.getXPBudget(A,"medium"),o=this.getXPBudget(A,"hard"),t=this.getXPBudget(A,"deadly");return{averageLevel:B,partySize:I,averageAC:Q,averageHP:E,averageDamage:i,totalStrength:e,easyXP:a,mediumXP:C,hardXP:o,deadlyXP:t}}}const OA=[];for(let g=0;g<256;++g)OA.push((g+256).toString(16).slice(1));function nW(g,A=0){return(OA[g[A+0]]+OA[g[A+1]]+OA[g[A+2]]+OA[g[A+3]]+"-"+OA[g[A+4]]+OA[g[A+5]]+"-"+OA[g[A+6]]+OA[g[A+7]]+"-"+OA[g[A+8]]+OA[g[A+9]]+"-"+OA[g[A+10]]+OA[g[A+11]]+OA[g[A+12]]+OA[g[A+13]]+OA[g[A+14]]+OA[g[A+15]]).toLowerCase()}let qo;const rW=new Uint8Array(16);function cW(){if(!qo){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");qo=crypto.getRandomValues.bind(crypto)}return qo(rW)}const pd={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function GW(g,A,B){g=g||{};const I=g.random??g.rng?.()??cW();if(I.length<16)throw new Error("Random bytes length must be >= 16");return I[6]=I[6]&15|64,I[8]=I[8]&63|128,nW(I)}function hW(g,A,B){return pd.randomUUID&&!g?pd.randomUUID():GW(g)}class lW{constructor(A={}){this.options={validateAudioUrls:!1,strict:!1,audioUrlValidationTimeout:5e3,resolveImageUrls:!1,...A}}async parse(A){const B=A.name,I=A.image,Q=A.creator,E=A.description,i=A.genre,e=A.tags;let a=I;I&&this.options.resolveImageUrls&&(a=await TA.resolveUrl(I));const C=A.tracks||[],o=[];for(let t=0;t<C.length;t++)try{const s=await this.parseTrack(C[t],t);if(s){const n=await this.resolveTrackUrls(s);o.push(n)}}catch(s){if(this.options.strict)throw s;console.warn(`Failed to parse track at index ${t}:`,s)}return{name:B,description:E,image:a,creator:Q,genre:i,tags:e,version:A.version,playlist_type:A.playlist_type,original_playlist_tx_id:A.original_playlist_tx_id,playlist_artist:A.playlist_artist,platform:A.platform,tracks:o}}async resolveTrackUrls(A){if(!this.options.resolveImageUrls)return A;const B={...A};return B.image_url&&(B.image_url=await TA.resolveUrl(B.image_url)),B.image_thumb_url&&(B.image_thumb_url=await TA.resolveUrl(B.image_thumb_url)),B}async parseTrack(A,B){const I=iA.parseMetadata(A.metadata);if(!I&&this.options.strict)throw new Error(`Failed to parse metadata for track at index ${B}`);const Q=A.chain_name,E=A.token_address,i=A.token_id,e=A.tx_id,a=A.platform,C=A.id||(Q==="AR"?`AR-${e}`:`${Q}-${E}-${i}`),o=A.uuid||hW(),t=iA.extractTitle(I||{}),s=iA.extractArtist(I||{}),r=typeof A.artwork_url=="string"&&A.artwork_url||typeof A.image_url=="string"&&A.image_url||null||iA.extractImageUrl(I||{}),c=iA.extractImageThumbUrl(I||{}),D=typeof A.audio_url=="string"&&A.audio_url||null||iA.extractAudioUrl(I||{}),h=iA.extractAudioUrlLossless(I||{});if(!D){if(this.options.strict)throw new Error(`No audio URL found for track: ${t||C}`);return console.warn(`Track ${C} has no audio URL - marked as Unsummonable`),null}if(this.options.validateAudioUrls&&!await this.validateAudioUrl(D)){if(this.options.strict)throw new Error(`Audio URL validation failed for track: ${t||C}`);return console.warn(`Track ${C} has invalid audio URL - marked as Unsummonable`),null}if(!t||!s||!r){if(this.options.strict)throw new Error(`Missing required fields for track: ${JSON.stringify({title:t,artist:s,imageUrl:r})}`);return null}const w=typeof I?.description=="string"?I.description:void 0,y=typeof I?.album=="string"?I.album:void 0,R=I?.duration?Number(I.duration):0,p=iA.extractGenre(I||{}),k=iA.extractTags(I||{}),W=I?.bpm?Number(I.bpm):void 0,_=typeof I?.key=="string"?I.key:void 0,X=iA.convertAttributes(I?.attributes),z=yD(I||{}),QA={id:C,uuid:o,playlist_index:B,chain_name:Q,platform:a,title:t,artist:s,description:w,album:y,image_url:r,audio_url:D,audio_url_lossless:h&&h!==D?h:void 0,duration:R,genre:p,tags:k,bpm:W,key:_,attributes:X||void 0,extras:z};return c&&(QA.image_thumb_url=c),typeof I?.audio_ipfs_hash=="string"?QA.audio_ipfs_hash=A.audio_ipfs_hash||I.audio_ipfs_hash:A.audio_ipfs_hash&&(QA.audio_ipfs_hash=A.audio_ipfs_hash),typeof I?.artwork_ipfs_hash=="string"?QA.artwork_ipfs_hash=A.artwork_ipfs_hash||I.artwork_ipfs_hash:A.artwork_ipfs_hash&&(QA.artwork_ipfs_hash=A.artwork_ipfs_hash),Q!=="AR"?(QA.token_address=E,QA.token_id=i):e&&(QA.tx_id=e),QA}async validateAudioUrl(A){const B=new AbortController,I=setTimeout(()=>B.abort(),this.options.audioUrlValidationTimeout);try{const Q=await fetch(A,{method:"HEAD",signal:B.signal});return clearTimeout(I),Q.ok}catch(Q){return clearTimeout(I),Q instanceof Error&&Q.name==="AbortError"?console.warn(`Audio URL validation timed out after ${this.options.audioUrlValidationTimeout}ms: ${A}`):console.warn(`Audio URL validation failed for ${A}:`,Q),!1}}}class rg{static separateFrequencyBands(A,B){const I=A.length,Q=B/2/I,E=[],i=[],e=[];for(let a=0;a<I;a++){const C=a*Q,o=A[a]/255;C>=20&&C<400?E.push(o):C>=400&&C<4e3?i.push(o):C>=4e3&&C<=14e3&&e.push(o)}return{bass:E,mid:i,treble:e}}static calculateDominance(A,B){if(A.length===0)return 0;const Q=A.reduce((E,i)=>E+i,0)/A.length;return B===void 0?Q:Q/(B/1e3)}}const Pi={targetSampleRate:8e3,fftWindowSize:32,hopSizeMs:4,hopSizeMode:{mode:"standard"},melBands:40,melBandsMode:{mode:"standard"},highPassCutoff:.4,gaussianSmoothMs:20,gaussianSmoothMode:{mode:"standard"}};class Yd{constructor(A={}){const B=A.hopSizeMode?iC(A.hopSizeMode):A.hopSizeMs??Pi.hopSizeMs,I=A.melBandsMode?eC(A.melBandsMode):A.melBands??Pi.melBands,Q=A.gaussianSmoothMode?aC(A.gaussianSmoothMode):A.gaussianSmoothMs??Pi.gaussianSmoothMs;this.config={...Pi,...A,hopSizeMs:B,melBands:I,gaussianSmoothMs:Q}}calculate(A){const B=XQ(A,this.config.targetSampleRate),I=B.targetSampleRate,Q=A.duration,E=Math.round(this.config.fftWindowSize/1e3*I),i=Math.pow(2,Math.ceil(Math.log2(E))),e=Math.round(this.config.hopSizeMs/1e3*I),a=this.config.hopSizeMs/1e3,C=Yo(B.data,i,e,I),o=vD(this.config.melBands,i,I),t=C.numFrames,s=[];for(let w=0;w<t;w++){const y=C.frames[w],R=new Float32Array(this.config.melBands);for(let p=0;p<this.config.melBands;p++){const k=o[p];let W=0;for(let _=0;_<k.length&&_<y.length;_++)W+=y[_]*k[_];R[p]=20*Math.log10(W+1e-10)}s.push(R)}const n=new Float32Array(t);n[0]=0;for(let w=1;w<t;w++){let y=0;for(let R=0;R<this.config.melBands;R++){const p=s[w][R]-s[w-1][R];p>0&&(y+=p)}n[w]=y}const r=1/a,c=XD(n,this.config.highPassCutoff,r),G=po(c,this.config.gaussianSmoothMs,r),D=KD(G),h=new Float32Array(G.length);if(D>1e-10)for(let w=0;w<G.length;w++)h[w]=G[w]/D;else for(let w=0;w<G.length;w++)h[w]=G[w];return{envelope:h,numFrames:t,hopSizeSeconds:a,effectiveSampleRate:I,duration:Q}}getConfig(){return{...this.config}}findPeaks(A,B=.5){const I=[];for(let Q=1;Q<A.length-1;Q++)A[Q]>A[Q-1]&&A[Q]>A[Q+1]&&A[Q]>=B&&I.push(Q);return I}frameToTime(A,B){return A*B}timeToFrame(A,B){return Math.round(A/B)}}const DW={tempoCenter:.5,tempoWidth:1.4,minBpm:90,maxBpm:180,useOctaveResolution:!1,useTripleMeter:!1};class Ud{constructor(A={}){this.config={...DW,...A}}estimateTempo(A,B){if(A.length<10)return this.getDefaultEstimate();const I=Math.round(60/this.config.minBpm/B),Q=Math.round(60/this.config.maxBpm/B),E=Math.max(Q,2),i=Math.min(I,Math.floor(A.length/2));if(i<=E)return this.getDefaultEstimate();const e=this.computeAutocorrelation(A,E,i),a=this.applyPerceptualWeighting(e.values,e.minLag,B);let C=this.findPeak(a,0,a.length);if(this.config.useOctaveResolution){const y=this.calculateTPS2(e.values,C),R=Math.floor(C/2);R>=1&&R<a.length&&this.calculateTPS2(e.values,R)>y&&(C=R)}if(this.config.useTripleMeter){const y=this.calculateTPS3(e.values,C),R=Math.floor(C/3);R>=1&&R<a.length&&this.calculateTPS3(e.values,R)>y&&(C=R)}const o=this.lagToBpm(C+e.minLag,B),t=o/2,s=a[C]||0,n=Math.min(C*2,a.length-1),r=a[n]||0,c=Math.max(Math.floor(C/2),0),G=a[c]||0,D=1,h=Math.min(Math.max(Math.max(r,G)/(s+.001),0),1),w=60/o;return{primaryBpm:o,secondaryBpm:t,primaryWeight:D,secondaryWeight:h,targetIntervalSeconds:w}}getConfig(){return{...this.config}}computeAutocorrelation(A,B,I){const Q=I-B+1,E=new Float32Array(Q);let i=0;for(let e=0;e<A.length;e++)i+=A[e];i/=A.length;for(let e=B;e<=I;e++){let a=0,C=0;for(let o=e;o<A.length;o++)a+=(A[o]-i)*(A[o-e]-i),C++;E[e-B]=C>0?a/C:0}return{values:E,minLag:B,maxLag:I}}applyPerceptualWeighting(A,B,I){const Q=new Float32Array(A.length),E=this.config.tempoCenter/I,i=this.config.tempoWidth;for(let e=0;e<A.length;e++){const a=B+e;if(a<=0||E<=0){Q[e]=0;continue}const C=Math.log2(a/E),o=Math.exp(-.5*Math.pow(C/i,2));Q[e]=A[e]*o}return Q}findPeak(A,B,I){let Q=B,E=A[B];for(let i=B+1;i<I&&i<A.length;i++)A[i]>E&&(E=A[i],Q=i);return Q}lagToBpm(A,B){return A<=0||B<=0?120:60/(A*B)}calculateTPS2(A,B){const I=A[B]||0,Q=B*2,E=Q-1,i=Q+1,e=A[Q]||0,a=E>=0&&A[E]||0,C=i<A.length&&A[i]||0;return I+.5*e+.25*a+.25*C}calculateTPS3(A,B){const I=A[B]||0,Q=B*3,E=Q-1,i=Q+1,e=A[Q]||0,a=E>=0&&A[E]||0,C=i<A.length&&A[i]||0;return I+.33*e+.33*a+.33*C}getDefaultEstimate(){return{primaryBpm:120,secondaryBpm:60,primaryWeight:1,secondaryWeight:.5,targetIntervalSeconds:.5}}getTempoCandidates(A,B,I=5){if(A.length<10)return[{bpm:120,strength:1}];const Q=Math.round(60/this.config.minBpm/B),E=Math.round(60/this.config.maxBpm/B),i=Math.max(E,2),e=Math.min(Q,Math.floor(A.length/2));if(e<=i)return[{bpm:120,strength:1}];const a=this.computeAutocorrelation(A,i,e),C=this.applyPerceptualWeighting(a.values,a.minLag,B),o=[];for(let t=1;t<C.length-1;t++)if(C[t]>C[t-1]&&C[t]>C[t+1]){const s=this.lagToBpm(t+a.minLag,B);o.push({bpm:s,strength:C[t]})}return o.sort((t,s)=>s.strength-t.strength),o.slice(0,I)}}const _i=VA.for("BeatTracker"),dW={dpAlpha:680,sensitivity:1,minPredecessorRatio:.5,maxPredecessorRatio:2};class fd{constructor(A={}){this.config={...dW,...A}}getConfig(){return{...this.config}}trackBeats(A,B,I){const Q=A.length;if(Q<10)return{beats:[],beatFrames:[],cumulativeScores:new Float32Array(Q)};const E=Math.round(B.targetIntervalSeconds/I);if(_i.debug("BeatTracker: Beat tracking parameters",{targetIntervalSeconds:B.targetIntervalSeconds,hopSizeSeconds:I,periodFrames:E,envelopeLength:Q}),E<2||Q<E*2)return{beats:[],beatFrames:[],cumulativeScores:new Float32Array(Q)};const i=this.config.sensitivity,e=Math.round(this.config.dpAlpha/i),a=Math.max(10,Math.min(1e4,e));_i.debug("Beat tracking parameters",{sensitivity:i,dpAlpha:this.config.dpAlpha,effectiveDpAlpha:a});const C=Math.max(1,Math.round(E*this.config.minPredecessorRatio)),o=Math.round(E*this.config.maxPredecessorRatio),t=new Float32Array(o-C+1);for(let w=0;w<t.length;w++){const y=C+w,R=Math.log(y/E);t[w]=-a*R*R}const s=new Int32Array(Q);s.fill(-1);const n=new Float32Array(A);for(let w=o+1;w<Q;w++){let y=-1/0,R=-1;for(let p=0;p<t.length;p++){const k=w-(C+p);if(k>=0&&k<Q){const W=t[p]+n[k];W>y&&(y=W,R=k)}}n[w]=y+A[w],s[w]=R}_i.debug("BeatTracker: Starting backward pass",{maxCumulativeScore:Math.max(...n),envelopeLength:Q});let r=0,c=n[0];for(let w=1;w<Q;w++)n[w]>c&&(c=n[w],r=w);const G=[];let D=r;for(;D>=0&&s[D]>=0;)G.unshift(D),D=s[D];return D>=0&&A[D]>0&&G.unshift(D),_i.debug("BeatTracker: Backward pass complete",{beatsFound:G.length,bestEndFrame:r,firstBeatFrame:G[0],lastBeatFrame:G[G.length-1]}),{beats:this.convertToBeats(G,A,n,I,E),beatFrames:G,cumulativeScores:n}}convertToBeats(A,B,I,Q,E){if(A.length===0)return[];const i=[];let e=0;for(let o=0;o<B.length;o++)B[o]>e&&(e=B[o]);e<=0&&(e=1);let a=0;for(let o=0;o<A.length;o++)a+=B[A[o]];const C=a/A.length;for(let o=0;o<A.length;o++){const t=A[o],s=t*Q,n=Math.max(0,Math.min(1,B[t]/e)),r=B[t],c=C>0?r/C:.5,G=Math.max(0,Math.min(1,.5+.3*(c-1)));i.push({timestamp:s,beatInMeasure:0,isDownbeat:!1,measureNumber:0,intensity:n,confidence:G})}return i}trackBeatsWithOptions(A,B,I,Q={}){const E=this.trackBeats(A,B,I);if(Q.applyTrimming&&E.beatFrames.length>0&&this.applyDiscountedScoreTrimming(E,A.length,Math.round(B.targetIntervalSeconds/I)),Q.minScoreThreshold!==void 0){const i=E.beats.filter((e,a)=>{const C=E.beatFrames[a];return E.cumulativeScores[C]>=Q.minScoreThreshold});E.beats=i,E.beatFrames=E.beatFrames.filter((e,a)=>{const C=E.beatFrames[a];return E.cumulativeScores[C]>=Q.minScoreThreshold})}return E}applyDiscountedScoreTrimming(A,B,I){if(A.beatFrames.length<3)return;const Q=A.cumulativeScores,E=Q[B-1],i=new Float32Array(B);for(let s=0;s<B;s++)i[s]=Q[s]-s/B*E;let e=0,a=B-1;for(let s=0;s<B;s++)if(i[s]>0){e=s;break}for(let s=B-1;s>=0;s--)if(i[s]>0){a=s;break}const C=Math.floor(I/2),o=[],t=[];for(let s=0;s<A.beatFrames.length;s++){const n=A.beatFrames[s];n>=e-C&&n<=a+C&&(o.push(A.beats[s]),t.push(n))}A.beats=o,A.beatFrames=t}getTrackingStats(A,B){const{beats:I,beatFrames:Q}=A;if(I.length===0)return{numBeats:0,avgInterval:0,stdInterval:0,avgIntensity:0,avgConfidence:0,estimatedBpm:0};const E=[];for(let t=1;t<Q.length;t++)E.push((Q[t]-Q[t-1])*B);const i=E.length>0?E.reduce((t,s)=>t+s,0)/E.length:0,e=E.length>0?Math.sqrt(E.reduce((t,s)=>t+Math.pow(s-i,2),0)/E.length):0,a=I.reduce((t,s)=>t+s.intensity,0)/I.length,C=I.reduce((t,s)=>t+s.confidence,0)/I.length,o=i>0?60/i:0;return{numBeats:I.length,avgInterval:i,stdInterval:e,avgIntensity:a,avgConfidence:C,estimatedBpm:o}}}const kd=VA.for("BeatMapGenerator");class _B{constructor(A={}){this.state=null;const B={...fQ,...A},I=A.hopSizeMode,Q=A.melBandsMode,E=A.gaussianSmoothMode,i=I?iC(I):A.hopSizeMs??fQ.hopSizeMs,e=Q?eC(Q):A.melBands??fQ.melBands,a=E?aC(E):A.gaussianSmoothMs??fQ.gaussianSmoothMs;this.options={...B,hopSizeMs:i,melBands:e,gaussianSmoothMs:a,...I?{hopSizeMode:I}:{},...Q?{melBandsMode:Q}:{},...E?{gaussianSmoothMode:E}:{}}}getConfig(){return{...this.options}}async generateBeatMap(A,B,I,Q){this.state={cancelled:!1,progress:{phase:"loading",progress:0,message:"Loading audio..."}};try{this.updateProgress("loading",0,"Loading audio...");const E=await this.fetchAndDecode(A);if(this.state.cancelled)throw new Error("Generation cancelled");return await this.generateBeatMapFromBuffer(E,B,I,Q)}finally{this.state=null}}async generateBeatMapFromBuffer(A,B,I,Q){this.state||(this.state={cancelled:!1,progress:{phase:"preprocessing",progress:0,message:"Starting..."}}),I&&gC(I);try{const E=A.duration;if(this.updateProgress("preprocessing",5,"Preparing audio..."),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("ose_calculation",10,"Calculating onset strength envelope..."),this.state.cancelled)throw new Error("Generation cancelled");const e=new Yd({targetSampleRate:8e3,fftWindowSize:32,hopSizeMs:this.options.hopSizeMs,melBands:this.options.melBands,highPassCutoff:this.options.highPassCutoff,gaussianSmoothMs:this.options.gaussianSmoothMs}).calculate(A);if(this.updateProgress("ose_calculation",35,"Onset strength envelope calculated."),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("tempo_estimation",40,"Estimating tempo..."),this.state.cancelled)throw new Error("Generation cancelled");const C=new Ud({tempoCenter:this.options.tempoCenter,tempoWidth:this.options.tempoWidth,minBpm:this.options.minBpm,maxBpm:this.options.maxBpm,useOctaveResolution:this.options.useOctaveResolution}).estimateTempo(e.envelope,e.hopSizeSeconds);if(this.updateProgress("tempo_estimation",55,`Tempo estimated: ${Math.round(C.primaryBpm)} BPM`),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("beat_tracking",60,"Tracking beats..."),this.state.cancelled)throw new Error("Generation cancelled");const t=new fd({dpAlpha:this.options.dpAlpha,sensitivity:this.options.sensitivity}).trackBeats(e.envelope,C,e.hopSizeSeconds);if(this.updateProgress("beat_tracking",85,`${t.beats.length} beats detected.`),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("measure_labeling",87,"Applying measure labels..."),this.state.cancelled)throw new Error("Generation cancelled");const s=I??_E;I&&IC(I,t.beats.length);const n=this.applyMeasureLabels(t.beats,s);if(this.updateProgress("measure_labeling",97,"Measure labels applied."),this.state.cancelled)throw new Error("Generation cancelled");this.updateProgress("finalizing",98,"Finalizing beat map...");let r=n;const c=this.options.filter;c>0&&(r=this.filterBeatsByGridAlignment(r,C,c));const G=r.length;r=this.applyIntensityThreshold(r),kd.debug("BeatMapGenerator: Intensity filter applied",{beatsBefore:G,beatsAfter:r.length,beatsFiltered:G-r.length,noiseFloorThreshold:this.options.noiseFloorThreshold});const D={version:$G,algorithm:Ah,minBpm:this.options.minBpm,maxBpm:this.options.maxBpm,sensitivity:this.options.sensitivity,filter:this.options.filter,noiseFloorThreshold:this.options.noiseFloorThreshold,hopSizeMs:this.options.hopSizeMs,fftSize:this.options.fftSize,dpAlpha:this.options.dpAlpha,melBands:this.options.melBands,highPassCutoff:this.options.highPassCutoff,gaussianSmoothMs:this.options.gaussianSmoothMs,tempoCenter:this.options.tempoCenter,tempoWidth:this.options.tempoWidth,useOctaveResolution:this.options.useOctaveResolution??!1,useTripleMeter:this.options.useTripleMeter??!1,generatedAt:new Date().toISOString()},h={audioId:B,duration:E,beats:r,bpm:C.primaryBpm,metadata:D,...I?{downbeatConfig:I}:{}};return this.updateProgress("complete",100,"Beat map generation complete."),Q?.(this.state.progress),h}finally{this.state=null}}getProgress(){return this.state?.progress??null}cancel(){this.state&&(this.state.cancelled=!0,this.state.progress={phase:"error",progress:this.state.progress.progress,message:"Generation cancelled",error:"Cancelled by user"})}applyIntensityThreshold(A){const B=this.options.noiseFloorThreshold;return A.filter(I=>I.intensity>=B)}applyMeasureLabels(A,B){const I=this.computeMeasureOffsets(B.segments,A.length);return A.map((Q,E)=>{const i=this.findActiveSegmentIndex(B.segments,E),e=B.segments[i],{downbeatBeatIndex:a,timeSignature:C}=e,{beatsPerMeasure:o}=C,t=E-a,s=(t%o+o)%o,n=s===0,c=Math.max(0,Math.floor(t/o))+I[i];return{...Q,beatInMeasure:s,isDownbeat:n,measureNumber:c}})}computeMeasureOffsets(A,B){const I=[];for(let Q=0;Q<A.length;Q++)if(Q===0)I.push(0);else{const E=A[Q-1],{downbeatBeatIndex:i,timeSignature:e}=E,a=e.beatsPerMeasure,o=A[Q].startBeat-1-i,t=Math.max(0,Math.floor(o/a));I.push(t+1)}return I}findActiveSegmentIndex(A,B){let I=0;for(let Q=0;Q<A.length&&A[Q].startBeat<=B;Q++)I=Q;return I}filterBeatsByGridAlignment(A,B,I){if(I<=0)return A;const Q=60/B.primaryBpm,E=(1-I)*(Q/2),i=A.filter(e=>{const C=Math.round(e.timestamp/Q)*Q;return Math.abs(e.timestamp-C)<=E});return kd.debug("Filtered beats by grid alignment",{originalCount:A.length,filteredCount:i.length,threshold:I,maxDeviationMs:E*1e3}),i}updateProgress(A,B,I){this.state&&(this.state.progress={phase:A,progress:B,message:I})}async fetchAndDecode(A){const B=await fetch(A);if(!B.ok)throw new Error(`Failed to fetch audio: ${B.statusText}`);const I=await B.arrayBuffer(),Q=globalThis.AudioContext||window?.AudioContext;if(!Q)throw new Error("AudioContext not available in this environment");const E=new Q;return await new Promise((i,e)=>{E.decodeAudioData(I,i,e)})}static toJSON(A){const B={audioId:A.audioId,duration:A.duration,beats:A.beats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),bpm:A.bpm,metadata:A.metadata};return JSON.stringify(B,null,2)}static fromJSON(A){const B=JSON.parse(A);return{audioId:B.audioId,duration:B.duration,beats:B.beats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),bpm:B.bpm,metadata:B.metadata}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=_B.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return _B.fromJSON(I)}}function wW(g){return"mergedBeats"in g&&"detectedBeats"in g}function FW(g){return"subdivisionConfig"in g&&"subdivisionMetadata"in g}class Zd{constructor(A,B,I={},Q=8){this.beatMap=A,this.audioContext=B,this.options={...OG,...I},this.rollingBpmWindowSize=Q,this.normalizedBeatMap=this.createNormalizedBeatMap(A),this.state={isRunning:!1,currentTime:0,startTime:0,isPaused:!1,pauseTime:0,rafId:null,lastButtonPress:null,thresholds:this.resolveThresholds()},this.subscribers=new Set,this.scheduledBeats=this.initializeScheduledBeats()}createNormalizedBeatMap(A){if(FW(A)){let B=120;if(A.beats.length>=2){const Q=A.beats[1].timestamp-A.beats[0].timestamp;Q>0&&(B=60/Q)}const I={version:"1.0.0",algorithm:"subdivision",minBpm:B,maxBpm:B,sensitivity:1,filter:0,noiseFloorThreshold:0,hopSizeMs:4,fftSize:2048,dpAlpha:680,melBands:40,highPassCutoff:.4,gaussianSmoothMs:20,tempoCenter:.5,tempoWidth:1.4,useOctaveResolution:!1,useTripleMeter:!1,generatedAt:new Date().toISOString()};return{audioId:A.audioId,duration:A.duration,beats:A.beats,bpm:B,metadata:I,downbeatConfig:A.downbeatConfig}}else return wW(A)?this.options.useInterpolatedBeats?{audioId:A.audioId,duration:A.duration,beats:A.mergedBeats,bpm:A.quarterNoteBpm,metadata:A.originalMetadata}:{audioId:A.audioId,duration:A.duration,beats:A.detectedBeats,bpm:A.quarterNoteBpm,metadata:A.originalMetadata}:A}initializeScheduledBeats(){return this.normalizedBeatMap.beats.map((A,B)=>({beat:A,index:B,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1}))}getOptions(){return{...this.options}}getAccuracyThresholds(){return{...this.state.thresholds}}setDifficulty(A){A.preset!==void 0&&(this.options.difficultyPreset=A.preset),A.customThresholds!==void 0&&(this.options.customThresholds=A.customThresholds),this.state.thresholds=this.resolveThresholds()}subscribe(A){return this.subscribers.add(A),()=>{this.subscribers.delete(A)}}start(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate())}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.scheduledBeats=this.initializeScheduledBeats()}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate())}seek(A){const B=Math.max(0,Math.min(A,this.normalizedBeatMap.duration));this.state.pauseTime=B,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-B),this.scheduledBeats=this.initializeScheduledBeats()}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let A=0;if(this.options.compensateOutputLatency){const B=this.audioContext.outputLatency??0,I=this.audioContext.baseLatency??0;A+=B+I}return A+=this.options.userOffsetMs/1e3,A}getSyncState(){const A=this.audioContext.currentTime,B=this.getCurrentAudioTime(),I=this.audioContext.outputLatency??0,Q=this.audioContext.baseLatency??0,E=A-this.state.startTime,i=B-E-this.getTotalLatencyCompensation();return{audioContextTime:A,audioElementTime:B,drift:i,isSynchronized:Math.abs(i)<=this.options.timingTolerance,outputLatency:I,baseLatency:Q,userOffsetMs:this.options.userOffsetMs,totalCompensation:this.getTotalLatencyCompensation()}}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const A=this.getCurrentAudioTime(),B=this.getCurrentBpm();for(const I of this.scheduledBeats){const E=I.beat.timestamp-A;!I.upcomingEmitted&&E<=this.options.anticipationTime&&E>0&&(this.emitEvent(I,"upcoming",A,B,E),I.upcomingEmitted=!0),!I.exactEmitted&&Math.abs(E)<=this.options.timingTolerance&&(this.emitEvent(I,"exact",A,B,E),I.exactEmitted=!0),!I.passedEmitted&&E<-this.options.timingTolerance&&(this.emitEvent(I,"passed",A,B,E),I.passedEmitted=!0)}}emitEvent(A,B,I,Q,E){const i={beat:A.beat,currentBpm:Q,audioTime:I,timeUntilBeat:E,type:B};for(const e of this.subscribers)try{e(i)}catch(a){console.error("BeatStream callback error:",a)}}getUpcomingBeats(A){const B=this.getCurrentAudioTime(),I=[];for(const Q of this.normalizedBeatMap.beats){const E=Q.timestamp-B;if(E>=0&&E<=this.options.anticipationTime&&(I.push(Q),I.length>=A))break}return I}getBeatAtTime(A){const B=this.options.timingTolerance;for(const I of this.normalizedBeatMap.beats)if(Math.abs(I.timestamp-A)<=B)return I;return null}getCurrentBeat(){const A=this.getCurrentAudioTime();let B=null;for(const I of this.normalizedBeatMap.beats)if(I.timestamp<=A)B=I;else break;return B}getNextBeat(){const A=this.getCurrentAudioTime();for(const B of this.normalizedBeatMap.beats)if(B.timestamp>A)return B;return null}getCurrentBpm(){const A=this.getCurrentAudioTime(),B=[];for(const a of this.normalizedBeatMap.beats)a.timestamp<=A+.1&&B.push(a);if(B.length<2)return this.normalizedBeatMap.bpm;const I=Math.min(this.rollingBpmWindowSize,B.length),Q=B.slice(-I);let E=0;for(let a=1;a<Q.length;a++)E+=Q[a].timestamp-Q[a-1].timestamp;const e=60/(E/(Q.length-1));return Math.max(30,Math.min(300,e))}resolveThresholds(){return this.options.customThresholds&&Object.keys(this.options.customThresholds).length>0?{...$E(this.options.difficultyPreset||"medium"),...this.options.customThresholds}:$E(this.options.difficultyPreset||"medium")}checkButtonPress(A,B){let I=null,Q=1/0;for(const n of this.normalizedBeatMap.beats){const r=A-n.timestamp,c=Math.abs(r);c<Q&&(Q=c,I=n)}if(!I){const n={accuracy:"miss",offset:A,matchedBeat:null,absoluteOffset:A,keyMatch:!0,pressedKey:B,requiredKey:void 0};return this.state.lastButtonPress=n,n}const E=A-I.timestamp,i=Math.abs(E);let e;const a=this.state.thresholds;i<=a.perfect?e="perfect":i<=a.great?e="great":i<=a.good?e="good":i<=a.ok?e="ok":e="miss";const C=this.options.ignoreKeyRequirements??!1,o=I.requiredKey!==void 0;let t=!0;!C&&o&&(B===void 0?(e="miss",t=!1):B!==I.requiredKey&&(e="wrongKey",t=!1));const s={accuracy:e,offset:E,matchedBeat:I,absoluteOffset:i,keyMatch:t,pressedKey:B,requiredKey:I.requiredKey};return this.state.lastButtonPress=s,s}getLastBeatAccuracy(){return this.state.lastButtonPress}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.normalizedBeatMap.duration}getBeatMap(){return this.beatMap}getNormalizedBeatMap(){return this.normalizedBeatMap}setBeatMap(A){this.beatMap=A,this.normalizedBeatMap=this.createNormalizedBeatMap(A),this.scheduledBeats=this.initializeScheduledBeats()}dispose(){this.stop(),this.subscribers.clear()}}const iB=VA.for("BeatInterpolator");class jQ{constructor(A={}){this.options={...jG,...A}}getConfig(){return{...this.options}}interpolate(A){const{beats:B,audioId:I,duration:Q,metadata:E,bpm:i,downbeatConfig:e}=A;if(iB.debug("Starting beat interpolation",{audioId:I,detectedBeats:B.length,duration:Q,originalBpm:i}),B.length===0)return iB.warn("No beats to interpolate"),this.createEmptyInterpolatedBeatMap(A);if(B.length===1)return iB.warn("Only one beat, cannot determine quarter note"),this.createSingleBeatInterpolatedBeatMap(A);const a=this.detectQuarterNote(B);iB.debug("Quarter note detected",{intervalSeconds:a.intervalSeconds,bpm:a.bpm,confidence:a.confidence,method:a.method,denseSectionCount:a.denseSectionCount});const C=this.analyzeGaps(B,a.intervalSeconds);iB.debug("Gap analysis complete",{totalGaps:C.totalGaps,halfNoteGaps:C.halfNoteGaps,anomalies:C.anomalies.length,gridAlignmentScore:C.gridAlignmentScore});const o=this.generateGrid(A,a,C);let t=this.mergeBeats(B,o,e);const s=t.filter(p=>p.source==="interpolated").length,n=s>0?t.filter(p=>p.source==="interpolated").reduce((p,k)=>p+k.confidence,0)/s:0,r=this.calculateTempoDriftRatio(t),c=this.identifyTempoClusters(B),G=c.map(p=>Math.round(p.bpm)),D=this.filterOctaveMultiples(G),h=D.length>1;let w,y;if(this.options.enableMultiTempo&&h){const p=this.runMultiTempoAnalysis(B,c,Q);p&&(w=p.tempoSections,y=!0,w.length>1&&(t=this.reinterpolateBoundaryRegions(t,w,B,e)),iB.debug("Multi-tempo analysis complete",{sectionCount:w.length,tempos:w.map(k=>k.bpm)}))}const R={quarterNoteDetection:a,gapAnalysis:C,detectedBeatCount:B.length,interpolatedBeatCount:s,totalBeatCount:t.length,interpolationRatio:t.length>0?s/t.length:0,avgInterpolatedConfidence:n,tempoDriftRatio:r,detectedClusterTempos:D.length>0?D:void 0,hasMultipleTempos:h,tempoSections:w,hasMultiTempoApplied:y};return iB.debug("Interpolation complete",{detectedBeats:B.length,interpolatedBeats:s,totalBeats:t.length,interpolationRatio:R.interpolationRatio.toFixed(2),hasMultipleTempos:h,hasMultiTempoApplied:y}),{audioId:I,duration:Q,detectedBeats:[...B],mergedBeats:t,quarterNoteInterval:a.intervalSeconds,quarterNoteBpm:a.bpm,quarterNoteConfidence:a.confidence,originalMetadata:E,interpolationMetadata:R,downbeatConfig:e}}detectQuarterNote(A){const B=this.identifyDenseSections(A);iB.debug("Identified dense sections",{count:B.length,totalBeats:B.reduce((s,n)=>s+n.beatCount,0)});const I=this.calculateWeightedIntervals(A,B),Q=this.buildIntervalHistogram(I),E=this.findHistogramPeaks(Q);if(E.length===0){const s=this.calculateAverageInterval(A);return{intervalSeconds:s,bpm:60/s,confidence:.3,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:B.length,denseSectionBeats:B.reduce((n,r)=>n+r.beatCount,0)}}const i=E[0],e=E.slice(1,4).map(s=>s.interval),a=I.reduce((s,n)=>s+n.weight,0),C=a>0?i.weight/a:0,o=B.reduce((s,n)=>s+n.beatCount,0)/A.length,t=Math.min(1,C*.6+o*.4);return{intervalSeconds:i.interval,bpm:60/i.interval,confidence:t,histogramPeak:i.weight,secondaryPeaks:e,method:"histogram",denseSectionCount:B.length,denseSectionBeats:B.reduce((s,n)=>s+n.beatCount,0)}}identifyDenseSections(A){const B=this.options.denseSectionMinBeats,I=[];if(A.length<B)return I;const Q=[];for(let e=1;e<A.length;e++)Q.push(A[e].timestamp-A[e-1].timestamp);let E=0;for(let e=1;e<Q.length;e++){const a=Q.slice(E,e+1),C=a.reduce((s,n)=>s+n,0)/a.length,o=a.reduce((s,n)=>s+Math.pow(n-C,2),0)/a.length,t=Math.pow(C*.2,2);if(o>t){const s=e-E+1;if(s>=B){const n=Q.slice(E,e),r=n.reduce((G,D)=>G+D,0)/n.length,c=n.reduce((G,D)=>G+Math.pow(D-r,2),0)/n.length;I.push({startIndex:E,endIndex:e-1,beatCount:s,avgInterval:r,intervalVariance:c})}E=e}}const i=Q.length-E+1;if(i>=B){const e=Q.slice(E),a=e.reduce((o,t)=>o+t,0)/e.length,C=e.reduce((o,t)=>o+Math.pow(t-a,2),0)/e.length;I.push({startIndex:E,endIndex:Q.length-1,beatCount:i,avgInterval:a,intervalVariance:C})}return I}identifyTempoClusters(A){const B=this.identifyDenseSections(A);if(B.length===0)return[];const I=this.options.tempoSectionThreshold,Q=this.options.minClusterBeats,E=B.map(e=>({...e,bpm:60/e.avgInterval,isVerified:e.beatCount>=Q}));if(E.length===1)return E[0].isVerified?E:[];E.sort((e,a)=>e.startIndex-a.startIndex);const i=[E[0]];for(let e=1;e<E.length;e++){const a=E[e],C=i[i.length-1],o=C.endIndex+1>=a.startIndex,s=1-Math.min(C.avgInterval,a.avgInterval)/Math.max(C.avgInterval,a.avgInterval)<=I;if(o&&s){const n=[];for(let G=C.startIndex;G<=a.endIndex&&G<A.length-1;G++)n.push(A[G+1].timestamp-A[G].timestamp);const r=n.reduce((G,D)=>G+D,0)/n.length,c=n.reduce((G,D)=>G+Math.pow(D-r,2),0)/n.length;i[i.length-1]={startIndex:C.startIndex,endIndex:a.endIndex,beatCount:C.beatCount+a.beatCount,avgInterval:r,intervalVariance:c,bpm:60/r,isVerified:C.beatCount+a.beatCount>=Q}}else i.push(a)}return i.filter(e=>e.isVerified)}findConflictingClusters(A){if(A.length<2)return null;const B=this.options.tempoSectionThreshold,I=[];for(let Q=0;Q<A.length;Q++)for(let E=Q+1;E<A.length;E++){const i=A[Q],e=A[E];if(this.isOctaveMultiple(i.bpm,e.bpm))continue;1-Math.min(i.bpm,e.bpm)/Math.max(i.bpm,e.bpm)>B&&I.push({cluster1:i,cluster2:e})}return I.length>0?I:null}isOctaveMultiple(A,B,I=.1){const Q=[.5,2],E=A/B;return Q.some(i=>Math.abs(E-i)<=I)}filterOctaveMultiples(A){if(A.length<=1)return A;const B=[...A].sort((Q,E)=>Q-E),I=[];for(const Q of B)I.some(i=>this.isOctaveMultiple(Q,i))||I.push(Q);return I}findCrossingPoint(A,B,I){const Q=this.options.tempoSectionThreshold,E=this.options.tempoAdaptationRate,i=I.slice(A.startIndex,A.endIndex+1),e=I.slice(B.startIndex,B.endIndex+1),a=I.slice(A.endIndex+1,B.startIndex),C=i[i.length-1],o=e[0];if(a.length===0){const r=(C.timestamp+o.timestamp)/2;return iB.debug("No connecting beats between clusters → automatic boundary",{cluster1Bpm:A.bpm,cluster2Bpm:B.bpm,boundaryTimestamp:r}),{hasBoundary:!0,boundaryTimestamp:r,gapRatio:1,beatsInSection1:i,beatsInSection2:e}}const t=this.interpolateForwardsWithDrift(C.timestamp,A.avgInterval,a,E,o.timestamp),s=this.interpolateBackwardsWithDrift(o.timestamp,B.avgInterval,a,E,C.timestamp),n=this.measureGapAtCrossing(t,s,A.avgInterval,B.avgInterval);if(iB.debug("Crossing point analysis",{cluster1Bpm:A.bpm,cluster2Bpm:B.bpm,connectingBeats:a.length,gapRatio:n.gapRatio.toFixed(3),threshold:Q,hasBoundary:n.gapRatio>Q}),n.gapRatio>Q){const r=n.crossingTimestamp,{beatsInSection1:c,beatsInSection2:G}=this.assignBeatsToSections(a,A,B,r);return{hasBoundary:!0,boundaryTimestamp:r,gapRatio:n.gapRatio,beatsInSection1:[...i,...c],beatsInSection2:[...G,...e]}}else return{hasBoundary:!1,gapRatio:n.gapRatio,beatsInSection1:[...i,...a,...e],beatsInSection2:[]}}measureGapAtCrossing(A,B,I,Q){const E=A.endTimestamp,i=B.endTimestamp,e=(E+i)/2,a=A.beatPositions,C=B.beatPositions;let o=E,t=1/0;for(const D of a){const h=Math.abs(D-e);h<t&&(t=h,o=D)}let s=i,n=1/0;for(const D of C){const h=Math.abs(D-e);h<n&&(n=h,s=D)}a.length===0&&(o=E),C.length===0&&(s=i);const r=Math.abs(o-s),c=(I+Q)/2;return{gapRatio:c>0?r/c:0,crossingTimestamp:e,forwardsInterval:A.finalInterval,backwardsInterval:B.finalInterval}}assignBeatsToSections(A,B,I,Q){const E=[],i=[];A.length>0&&A[0].timestamp>Q,A[0],A[A.length-1];for(const e of A)e.timestamp<Q?E.push(e):i.push(e);return iB.debug("Assigned connecting beats to sections",{totalConnectingBeats:A.length,toSection1:E.length,toSection2:i.length,boundaryTimestamp:Q}),{beatsInSection1:E,beatsInSection2:i}}calculatePhaseAlignment(A,B,I,Q=.1){const E=A-B,i=Math.round(E/I),e=B+i*I,a=Math.abs(A-e),C=I*Q;return a<=C?1:0}runMultiTempoAnalysis(A,B,I){if(B.length<2)return null;const Q=this.findConflictingClusters(B);if(!Q||Q.length===0)return iB.debug("No conflicting tempo clusters found"),null;iB.debug("Found conflicting tempo clusters",{conflictCount:Q.length,conflicts:Q.map(o=>({c1:Math.round(o.cluster1.bpm),c2:Math.round(o.cluster2.bpm)}))});const E=[...B].sort((o,t)=>o.startIndex-t.startIndex),i=[];let e=0,a=0;for(let o=0;o<E.length-1;o++){const t=E[o],s=E[o+1];if(!Q.some(c=>c.cluster1===t&&c.cluster2===s||c.cluster1===s&&c.cluster2===t))continue;const r=this.findCrossingPoint(t,s,A);if(r.hasBoundary){const c=r.boundaryTimestamp;A[t.endIndex],i.push({start:e,end:c,bpm:Math.round(t.bpm*10)/10,intervalSeconds:t.avgInterval,beatCount:t.beatCount,startBeatIndex:a,endBeatIndex:t.endIndex}),e=c,a=s.startIndex}}const C=E[E.length-1];return i.push({start:e,end:I,bpm:Math.round(C.bpm*10)/10,intervalSeconds:C.avgInterval,beatCount:C.beatCount,startBeatIndex:a,endBeatIndex:C.endIndex}),i.length===1?(iB.debug("Multi-tempo analysis found no section boundaries"),null):{tempoSections:i}}reinterpolateBoundaryRegions(A,B,I,Q){return iB.debug("Boundary regions identified",{sectionCount:B.length,sections:B.map(E=>({bpm:E.bpm,start:E.start.toFixed(2),end:E.end.toFixed(2)}))}),A}canApplyMultiTempo(A){const{interpolationMetadata:B}=A,I=B.hasMultipleTempos,Q=B.hasMultiTempoApplied===!0;return I&&!Q}calculateWeightedIntervals(A,B){const I=[];for(let Q=1;Q<A.length;Q++){const E=A[Q].timestamp-A[Q-1].timestamp,i=B.some(t=>Q>t.startIndex&&Q<=t.endIndex),e=(A[Q-1].confidence+A[Q].confidence)/2,a=i?2:1;let C=1;if(Q>1){const t=A[Q-1].timestamp-A[Q-2].timestamp;C=.5+Math.min(E,t)/Math.max(E,t)*.5}if(Q<A.length-1){const t=A[Q+1].timestamp-A[Q].timestamp,s=Math.min(E,t)/Math.max(E,t);C=Math.max(C,.5+s*.5)}const o=e*a*C;I.push({intervalSeconds:E,weight:o,beatIndex:Q,isFromDenseSection:i,confidence:e})}return I}buildIntervalHistogram(A){const B=new Map,I=.005;for(const Q of A){const E=Math.round(Q.intervalSeconds/I)*I,i=B.get(E)||0;B.set(E,i+Q.weight)}return B}findHistogramPeaks(A){const B=[],I=Array.from(A.entries()).sort((i,e)=>i[0]-e[0]);for(let i=0;i<I.length;i++){const[e,a]=I[i],C=i>0?I[i-1][1]:0,o=i<I.length-1?I[i+1][1]:0;a>=C&&a>=o&&a>0&&B.push({interval:e,weight:a})}B.sort((i,e)=>e.weight-i.weight);const Q=.05,E=[];for(const i of B)E.some(a=>Math.abs(a.interval-i.interval)<Q)||E.push(i);return E}calculateAverageInterval(A){if(A.length<2)return .5;let B=0;for(let I=1;I<A.length;I++)B+=A[I].timestamp-A[I-1].timestamp;return B/(A.length-1)}analyzeGaps(A,B){const I=this.options.anomalyThreshold;let Q=0;const E=[],i=[];let e=0;for(let t=1;t<A.length;t++){const n=(A[t].timestamp-A[t-1].timestamp)/B,r=Math.round(n);r>1&&i.push(r),Math.abs(n-2)<.2&&Q++,(n<1-I||n>1+I&&Math.abs(n-2)>.3&&Math.abs(n-3)>.3&&Math.abs(n-4)>.3)&&E.push(t);const G=Math.round(A[t].timestamp/B)*B,D=Math.abs(A[t].timestamp-G)/B;e+=D}const a=A.length>1?e/(A.length-1):0,C=Math.max(0,1-a*2),o=i.length>0?i.reduce((t,s)=>t+s,0)/i.length:1;return{totalGaps:i.length,halfNoteGaps:Q,anomalies:E,avgGapSize:o,gridAlignmentScore:C}}generateGrid(A,B,I){return this.interpolateAdaptivePhaseLocked(A,B)}interpolateForwardsWithDrift(A,B,I,Q,E){const i=[];let e=B,a=A;const C=I.filter(r=>!(r.timestamp<=A||E!==void 0&&r.timestamp>E));let o=A;for(let r=0;r<C.length;r++){const c=C[r],G=c.timestamp-o,D=Math.round(G/e);if(D>0){const h=a+D*e,y=(c.timestamp-h)/D*Q;e=e+y;for(let R=1;R<D;R++){const p=o+R*e;if(E!==void 0&&p>E)break;i.push(p)}}a=c.timestamp,o=c.timestamp}const t=i.length,s=A+t*B,n=i.length>0?i[i.length-1]-s:0;return{finalInterval:e,beatPositions:i,phaseError:n,endTimestamp:o}}interpolateBackwardsWithDrift(A,B,I,Q,E){const i=[];let e=B,a=A;const C=I.filter(r=>!(r.timestamp>=A||E!==void 0&&r.timestamp<E)).reverse();let o=A;for(let r=0;r<C.length;r++){const c=C[r],G=o-c.timestamp,D=Math.round(G/e);if(D>0){const h=a-D*e,y=(c.timestamp-h)/D*Q;e=e+y;for(let R=1;R<D;R++){const p=o-R*e;if(E!==void 0&&p<E)break;i.unshift(p)}}a=c.timestamp,o=c.timestamp}const t=i.length,s=A-t*B,n=i.length>0?i[0]-s:0;return{finalInterval:e,beatPositions:i,phaseError:n,endTimestamp:o}}interpolateAdaptivePhaseLocked(A,B){const{beats:I,duration:Q}=A,E=B.intervalSeconds,i=this.options.gridSnapTolerance,e=this.options.tempoAdaptationRate,a=[],C=this.options.gridAlignmentWeight,o=this.options.anchorConfidenceWeight,t=this.options.paceConfidenceWeight;if(this.options.extrapolateStart){let r=I[0].timestamp-E;for(;r>=0;){const c=this.calculateConfidence(C,o*.5,t*B.confidence);a.unshift(this.createInterpolatedBeat(r,c,I[0].timestamp)),r-=E}}let s=I[0].timestamp,n=E;for(let r=0;r<I.length;r++){const c=I[r];if(a.push(this.createDetectedBeat(c)),r<I.length-1){const G=I[r+1],D=G.timestamp-c.timestamp,h=Math.round(D/n),w=s+h*n,y=G.timestamp-w;if(h>0){const R=y/h*e;n=n+R}for(let R=1;R<h;R++){const p=c.timestamp+R*n;if(p>=G.timestamp-i)break;const k=this.calculateConfidence(C,o*((c.confidence+G.confidence)/2),t*B.confidence);a.push(this.createInterpolatedBeat(p,k,c.timestamp))}s=G.timestamp}}if(this.options.extrapolateEnd){let r=I[I.length-1].timestamp+n;for(;r<=Q;){const c=this.calculateConfidence(C,o*.5,t*B.confidence);a.push(this.createInterpolatedBeat(r,c,I[I.length-1].timestamp)),r+=n}}return a.sort((r,c)=>r.timestamp-c.timestamp),a}mergeBeats(A,B,I){const Q=this.options.gridSnapTolerance,E=[],i=[...B].sort((o,t)=>o.timestamp-t.timestamp),e=[...A].sort((o,t)=>o.timestamp-t.timestamp),a=new Map;for(const o of e)a.set(o.timestamp,o);const C=new Set;for(const o of i){const t=this.findBeatNear(a,o.timestamp,Q);t&&!C.has(t.timestamp)?(E.push(this.createDetectedBeat(t)),C.add(t.timestamp)):o.source==="interpolated"&&E.push(o)}for(const o of e)C.has(o.timestamp)||E.push(this.createDetectedBeat(o));return E.sort((o,t)=>o.timestamp-t.timestamp),this.reassignBeatPositions(E,I),E}reassignBeatPositions(A,B){const I=B??_E,Q=this.computeMeasureOffsets(I.segments,A.length);for(let E=0;E<A.length;E++){const i=A[E],e=this.findActiveSegmentIndex(I.segments,E),a=I.segments[e],{downbeatBeatIndex:C,timeSignature:o}=a,{beatsPerMeasure:t}=o,s=E-C,n=(s%t+t)%t,r=n===0,G=Math.max(0,Math.floor(s/t))+Q[e];i.beatInMeasure=n,i.isDownbeat=r,i.measureNumber=G}}computeMeasureOffsets(A,B){const I=[];for(let Q=0;Q<A.length;Q++)if(Q===0)I.push(0);else{const E=A[Q-1],{downbeatBeatIndex:i,timeSignature:e}=E,a=e.beatsPerMeasure,o=A[Q].startBeat-1-i,t=Math.max(0,Math.floor(o/a));I.push(t+1)}return I}findActiveSegmentIndex(A,B){let I=0;for(let Q=0;Q<A.length&&A[Q].startBeat<=B;Q++)I=Q;return I}calculateConfidence(A,B,I){return Math.max(0,Math.min(1,A+B+I))}createInterpolatedBeat(A,B,I){return{timestamp:A,beatInMeasure:0,isDownbeat:!1,measureNumber:0,intensity:.5,confidence:B,source:"interpolated",distanceToAnchor:Math.abs(A-I),nearestAnchorTimestamp:I}}createDetectedBeat(A){return{...A,source:"detected",distanceToAnchor:0,nearestAnchorTimestamp:A.timestamp}}findBeatNear(A,B,I){const Q=A.get(B);if(Q)return Q;for(const[E,i]of A)if(Math.abs(E-B)<=I)return i;return null}findNearestAnchor(A,B,I){const Q=I>=0&&I<A.length?A[I]:A[0],E=I+1<A.length?A[I+1]:A[A.length-1],i=Math.abs(B-Q.timestamp),e=Math.abs(B-E.timestamp);return i<=e?{timestamp:Q.timestamp,confidence:Q.confidence}:{timestamp:E.timestamp,confidence:E.confidence}}calculateTempoDriftRatio(A){if(A.length<3)return 1;const B=[];for(let E=1;E<A.length;E++){const i=A[E].timestamp-A[E-1].timestamp;i>0&&B.push(60/i)}if(B.length===0)return 1;const I=Math.min(...B),Q=Math.max(...B);return I>0?Q/I:1}createEmptyInterpolatedBeatMap(A){return{audioId:A.audioId,duration:A.duration,detectedBeats:[],mergedBeats:[],quarterNoteInterval:.5,quarterNoteBpm:120,quarterNoteConfidence:0,originalMetadata:A.metadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:.5,bpm:120,confidence:0,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:0,denseSectionBeats:0},gapAnalysis:{totalGaps:0,halfNoteGaps:0,anomalies:[],avgGapSize:1,gridAlignmentScore:0},detectedBeatCount:0,interpolatedBeatCount:0,totalBeatCount:0,interpolationRatio:0,avgInterpolatedConfidence:0,tempoDriftRatio:1,detectedClusterTempos:void 0,hasMultipleTempos:!1,tempoSections:void 0,hasMultiTempoApplied:void 0},downbeatConfig:A.downbeatConfig}}createSingleBeatInterpolatedBeatMap(A){const B=A.beats[0],I={...B,source:"detected",distanceToAnchor:0,nearestAnchorTimestamp:B.timestamp};return{audioId:A.audioId,duration:A.duration,detectedBeats:[B],mergedBeats:[I],quarterNoteInterval:.5,quarterNoteBpm:120,quarterNoteConfidence:.1,originalMetadata:A.metadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:.5,bpm:120,confidence:.1,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:0,denseSectionBeats:0},gapAnalysis:{totalGaps:0,halfNoteGaps:0,anomalies:[],avgGapSize:1,gridAlignmentScore:0},detectedBeatCount:1,interpolatedBeatCount:0,totalBeatCount:1,interpolationRatio:0,avgInterpolatedConfidence:0,tempoDriftRatio:1,detectedClusterTempos:void 0,hasMultipleTempos:!1,tempoSections:void 0,hasMultiTempoApplied:void 0},downbeatConfig:A.downbeatConfig}}static toJSON(A){const B={audioId:A.audioId,duration:A.duration,detectedBeats:A.detectedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),mergedBeats:A.mergedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey},source:I.source,distanceToAnchor:I.distanceToAnchor,nearestAnchorTimestamp:I.nearestAnchorTimestamp})),quarterNoteInterval:A.quarterNoteInterval,quarterNoteBpm:A.quarterNoteBpm,quarterNoteConfidence:A.quarterNoteConfidence,originalMetadata:A.originalMetadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:A.interpolationMetadata.quarterNoteDetection.intervalSeconds,bpm:A.interpolationMetadata.quarterNoteDetection.bpm,confidence:A.interpolationMetadata.quarterNoteDetection.confidence,histogramPeak:A.interpolationMetadata.quarterNoteDetection.histogramPeak,secondaryPeaks:A.interpolationMetadata.quarterNoteDetection.secondaryPeaks,method:A.interpolationMetadata.quarterNoteDetection.method,denseSectionCount:A.interpolationMetadata.quarterNoteDetection.denseSectionCount,denseSectionBeats:A.interpolationMetadata.quarterNoteDetection.denseSectionBeats},gapAnalysis:{totalGaps:A.interpolationMetadata.gapAnalysis.totalGaps,halfNoteGaps:A.interpolationMetadata.gapAnalysis.halfNoteGaps,anomalies:A.interpolationMetadata.gapAnalysis.anomalies,avgGapSize:A.interpolationMetadata.gapAnalysis.avgGapSize,gridAlignmentScore:A.interpolationMetadata.gapAnalysis.gridAlignmentScore},detectedBeatCount:A.interpolationMetadata.detectedBeatCount,interpolatedBeatCount:A.interpolationMetadata.interpolatedBeatCount,totalBeatCount:A.interpolationMetadata.totalBeatCount,interpolationRatio:A.interpolationMetadata.interpolationRatio,avgInterpolatedConfidence:A.interpolationMetadata.avgInterpolatedConfidence,tempoDriftRatio:A.interpolationMetadata.tempoDriftRatio,detectedClusterTempos:A.interpolationMetadata.detectedClusterTempos,hasMultipleTempos:A.interpolationMetadata.hasMultipleTempos,tempoSections:A.interpolationMetadata.tempoSections?.map(I=>({start:I.start,end:I.end,bpm:I.bpm,intervalSeconds:I.intervalSeconds,beatCount:I.beatCount,startBeatIndex:I.startBeatIndex,endBeatIndex:I.endBeatIndex})),hasMultiTempoApplied:A.interpolationMetadata.hasMultiTempoApplied},downbeatConfig:A.downbeatConfig};return JSON.stringify(B,null,2)}static fromJSON(A){const B=JSON.parse(A);return{audioId:B.audioId,duration:B.duration,detectedBeats:B.detectedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),mergedBeats:B.mergedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey},source:I.source,distanceToAnchor:I.distanceToAnchor,nearestAnchorTimestamp:I.nearestAnchorTimestamp})),quarterNoteInterval:B.quarterNoteInterval,quarterNoteBpm:B.quarterNoteBpm,quarterNoteConfidence:B.quarterNoteConfidence,originalMetadata:B.originalMetadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:B.interpolationMetadata.quarterNoteDetection.intervalSeconds,bpm:B.interpolationMetadata.quarterNoteDetection.bpm,confidence:B.interpolationMetadata.quarterNoteDetection.confidence,histogramPeak:B.interpolationMetadata.quarterNoteDetection.histogramPeak,secondaryPeaks:B.interpolationMetadata.quarterNoteDetection.secondaryPeaks,method:B.interpolationMetadata.quarterNoteDetection.method,denseSectionCount:B.interpolationMetadata.quarterNoteDetection.denseSectionCount,denseSectionBeats:B.interpolationMetadata.quarterNoteDetection.denseSectionBeats},gapAnalysis:{totalGaps:B.interpolationMetadata.gapAnalysis.totalGaps,halfNoteGaps:B.interpolationMetadata.gapAnalysis.halfNoteGaps,anomalies:B.interpolationMetadata.gapAnalysis.anomalies,avgGapSize:B.interpolationMetadata.gapAnalysis.avgGapSize,gridAlignmentScore:B.interpolationMetadata.gapAnalysis.gridAlignmentScore},detectedBeatCount:B.interpolationMetadata.detectedBeatCount,interpolatedBeatCount:B.interpolationMetadata.interpolatedBeatCount,totalBeatCount:B.interpolationMetadata.totalBeatCount,interpolationRatio:B.interpolationMetadata.interpolationRatio,avgInterpolatedConfidence:B.interpolationMetadata.avgInterpolatedConfidence,tempoDriftRatio:B.interpolationMetadata.tempoDriftRatio,detectedClusterTempos:B.interpolationMetadata.detectedClusterTempos,hasMultipleTempos:B.interpolationMetadata.hasMultipleTempos??!1,tempoSections:B.interpolationMetadata.tempoSections?.map(I=>({start:I.start,end:I.end,bpm:I.bpm,intervalSeconds:I.intervalSeconds,beatCount:I.beatCount,startBeatIndex:I.startBeatIndex,endBeatIndex:I.endBeatIndex})),hasMultiTempoApplied:B.interpolationMetadata.hasMultiTempoApplied},downbeatConfig:B.downbeatConfig}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=jQ.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return jQ.fromJSON(I)}}function $i(g){const{mergedBeats:A,interpolationMetadata:B,downbeatConfig:I}=g,Q=[],E=[];for(let e=0;e<A.length;e++){const a=A[e],C={timestamp:a.timestamp,beatInMeasure:a.beatInMeasure,isDownbeat:a.isDownbeat,measureNumber:a.measureNumber,intensity:a.intensity,confidence:a.confidence};Q.push(C),a.source==="detected"&&E.push(e)}return{audioId:g.audioId,duration:g.duration,beats:Q,detectedBeatIndices:E,quarterNoteInterval:g.quarterNoteInterval,quarterNoteBpm:g.quarterNoteBpm,downbeatConfig:I??_E,tempoSections:B.tempoSections,originalMetadata:g.originalMetadata}}const OE=class OE{constructor(A={}){this.options={includeAdvancedMetrics:!1,sampleRate:44100,fftSize:2048,trebleBoost:1,bassBoost:1,midBoost:1,...A}}async extractSonicFingerprint(A){const B=await this.fetchAndDecode(A),I=B.duration,Q=I<3;let E;Q?E=[0]:E=[.05,.4,.7];const i=[];for(const D of E){const h=await this.analyzeAtPosition(B,D,Q);i.push(h)}const e=this.averageFrequencyBands(i);let a=rg.calculateDominance(e.bass,380),C=rg.calculateDominance(e.mid,3600),o=rg.calculateDominance(e.treble,1e4);a=a*this.options.bassBoost,C=C*this.options.midBoost,o=o*this.options.trebleBoost;const t=a+C+o;a=a/(t||1),C=C/(t||1),o=o/(t||1);const s=this.calculateAverageAmplitude(B),n=this.calculateRMS(B),c=this.calculatePeak(B)-n,G={bass_dominance:a,mid_dominance:C,treble_dominance:o,average_amplitude:s,analysis_metadata:{duration_analyzed:Q?I:I*E.length*.01,full_buffer_analyzed:Q,sample_positions:E,analyzed_at:new Date().toISOString()},rms_energy:n,dynamic_range:c};if(this.options.includeAdvancedMetrics){const D=[...e.bass,...e.mid,...e.treble];G.spectral_centroid=this.calculateSpectralCentroid(D),G.spectral_rolloff=this.calculateSpectralRolloff(D),G.zero_crossing_rate=this.calculateZeroCrossingRate(B)}return G}async analyzeTimeline(A,B){const I=await this.fetchAndDecode(A),Q=I.duration,E=I.sampleRate,i=[];let e=[];if(B.type==="interval"){const a=B.intervalSeconds;for(let C=0;C<Q;C+=a)e.push(C)}else{const a=B.count,C=Q/a;for(let o=0;o<a;o++)e.push(o*C)}for(const a of e){const C=Q-a,o=Math.min(1,C);if(o<=0)break;const t=Math.floor(a*E),s=Math.floor((a+o)*E),n=this.extractAudioSegment(I,t,s),r=this.performFFT(n,this.options.fftSize),c=new Uint8Array(r.length);for(let W=0;W<r.length;W++)c[W]=Math.min(255,Math.round(r[W]));const G=rg.separateFrequencyBands(c,E),D=rg.calculateDominance(G.bass)*this.options.bassBoost,h=rg.calculateDominance(G.mid)*this.options.midBoost,w=rg.calculateDominance(G.treble)*this.options.trebleBoost,y=D+h+w||1,R=this.calculateRMS(I,t,s),p=this.calculatePeak(I,t,s),k=[...G.bass,...G.mid,...G.treble];i.push({timestamp:a,duration:o,bass:D/y,mid:h/y,treble:w/y,amplitude:R,rms_energy:R,peak:p,dynamic_range:p-R,spectral_centroid:this.calculateSpectralCentroid(k),spectral_rolloff:this.calculateSpectralRolloff(k),zero_crossing_rate:this.calculateZeroCrossingRate(I,t,s)})}return i}async generateBeatMap(A,B,I,Q,E){return new _B(I).generateBeatMap(A,B,Q,E)}async generateBeatMapFromBuffer(A,B,I,Q,E){return new _B(I).generateBeatMapFromBuffer(A,B,Q,E)}createBeatStream(A,B,I){return new Zd(A,B,I)}interpolateBeatMap(A,B){return new jQ(B).interpolate(A)}async generateBeatMapWithInterpolation(A,B,I,Q,E,i){const e=await this.generateBeatMap(A,B,I,Q,i);return this.interpolateBeatMap(e,E)}async generateBeatMapWithInterpolationFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapFromBuffer(A,B,I,Q,i);return this.interpolateBeatMap(e,E)}static beatMapToJSON(A){return _B.toJSON(A)}static beatMapFromJSON(A){return _B.fromJSON(A)}static async saveBeatMapToFile(A,B){return _B.saveToFile(A,B)}static async loadBeatMapFromFile(A){return _B.loadFromFile(A)}async generateRhythm(A,B,I,Q,E,i){const e=await this.fetchAndDecode(A);return this.generateRhythmFromBuffer(e,B,I,Q,E,i)}async generateRhythmFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapWithInterpolationFromBuffer(A,B,Q,E),a=$i(e);return new kg(I).generate(A,a,void 0,i)}async generateRhythmLevel(A,B,I,Q,E,i){const e=await this.fetchAndDecode(A);return this.generateRhythmLevelFromBuffer(e,B,I,Q,E,i)}async generateRhythmLevelFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapWithInterpolationFromBuffer(A,B,Q,E),a=$i(e);return new ji(I).generate(A,a,i)}async generateRhythmLevelWithPreset(A,B,I,Q,E,i){const e=OE.LEVEL_PRESETS[I],a={difficulty:e.difficulty,controllerMode:e.controllerMode,buttons:e.buttons};return this.generateRhythmLevel(A,B,a,Q,E,i)}async generateRhythmLevelWithPresetFromBuffer(A,B,I,Q,E,i){const e=OE.LEVEL_PRESETS[I],a={difficulty:e.difficulty,controllerMode:e.controllerMode,buttons:e.buttons};return this.generateRhythmLevelFromBuffer(A,B,a,Q,E,i)}async fetchAndDecodeAudio(A){return this.fetchAndDecode(A)}async fetchAndDecode(A){const B=await fetch(A);if(!B.ok)throw new Error(`Failed to fetch audio: ${B.statusText}`);const I=await B.arrayBuffer(),Q=globalThis.AudioContext||window?.AudioContext;if(!Q)throw new Error("AudioContext not available in this environment");const E=new Q;return await new Promise((i,e)=>{E.decodeAudioData(I,i,e)})}async analyzeAtPosition(A,B,I){const Q=A.sampleRate,E=A.duration;let i,e;if(I)i=0,e=A.length;else{const t=E*B;i=Math.floor(t*Q),e=Math.min(i+Q,A.length)}const a=this.extractAudioSegment(A,i,e),C=this.performFFT(a,this.options.fftSize),o=new Uint8Array(C.length);for(let t=0;t<C.length;t++)o[t]=Math.min(255,Math.round(C[t]));return rg.separateFrequencyBands(o,Q)}extractAudioSegment(A,B,I){const Q=I-B,E=new Float32Array(Q);for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i);for(let a=0;a<Q;a++)E[a]+=e[B+a]/A.numberOfChannels}return E}performFFT(A,B){const I=Math.pow(2,Math.ceil(Math.log2(Math.min(A.length,B)))),Q=new Float32Array(I),E=new Float32Array(I);for(let C=0;C<Math.min(A.length,I);C++){const o=.5-.5*Math.cos(2*Math.PI*C/(I-1));Q[C]=A[C]*o}this.fft(Q,E);const i=new Array(I/2);for(let C=0;C<i.length;C++){const o=Q[C],t=E[C];i[C]=Math.sqrt(o*o+t*t)}const e=Math.max(...i);return i.map(C=>{const o=C/(e||1);return 20*Math.log10(Math.max(o,.001))})}fft(A,B){const I=A.length;if(I<=1)return;let Q=0;for(let i=0;i<I-1;i++){i<Q&&([A[i],A[Q]]=[A[Q],A[i]],[B[i],B[Q]]=[B[Q],B[i]]);let e=I/2;for(;Q>=e;)Q-=e,e/=2;Q+=e}let E=2;for(;E<=I;){const i=-2*Math.PI/E,e=Math.cos(i),a=Math.sin(i);for(let C=0;C<I;C+=E){let o=1,t=0;for(let s=0;s<E/2;s++){const n=C+s,r=C+s+E/2,c=A[r]*o-B[r]*t,G=A[r]*t+B[r]*o;A[r]=A[n]-c,B[r]=B[n]-G,A[n]+=c,B[n]+=G;const D=o*e-t*a;t=o*a+t*e,o=D}}E*=2}}averageFrequencyBands(A){if(A.length===0)return{bass:[],mid:[],treble:[]};if(A.length===1)return A[0];const B=Math.max(...A.map(i=>Math.max(i.bass.length,i.mid.length,i.treble.length))),I=[],Q=[],E=[];for(let i=0;i<B;i++){const e=A.map(o=>o.bass[i]||0),a=A.map(o=>o.mid[i]||0),C=A.map(o=>o.treble[i]||0);I.push(e.reduce((o,t)=>o+t,0)/e.length),Q.push(a.reduce((o,t)=>o+t,0)/a.length),E.push(C.reduce((o,t)=>o+t,0)/C.length)}return{bass:I,mid:Q,treble:E}}calculateAverageAmplitude(A){let B=0,I=0;for(let Q=0;Q<A.numberOfChannels;Q++){const E=A.getChannelData(Q);for(let i=0;i<E.length;i++)B+=Math.abs(E[i]),I++}return I>0?B/I:0}calculateRMS(A,B=0,I=A.length){let Q=0,E=0;for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i),a=Math.min(I,e.length);for(let C=B;C<a;C++)Q+=e[C]*e[C],E++}return E>0?Math.sqrt(Q/E):0}calculatePeak(A,B=0,I=A.length){let Q=0;for(let E=0;E<A.numberOfChannels;E++){const i=A.getChannelData(E),e=Math.min(I,i.length);for(let a=B;a<e;a++){const C=Math.abs(i[a]);C>Q&&(Q=C)}}return Q}calculateSpectralCentroid(A){let B=0,I=0;for(let Q=0;Q<A.length;Q++)B+=Q*A[Q],I+=A[Q];return I>0?B/I:0}calculateSpectralRolloff(A){const I=A.reduce((E,i)=>E+i,0)*.85;let Q=0;for(let E=0;E<A.length;E++)if(Q+=A[E],Q>=I)return E/A.length;return 1}calculateZeroCrossingRate(A,B=0,I=A.length){let Q=0,E=0;for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i),a=Math.min(I,e.length),C=Math.max(0,B);for(let o=Math.max(1,C+1);o<a;o++)(e[o-1]>=0&&e[o]<0||e[o-1]<0&&e[o]>=0)&&Q++,E++}return E>0?Q/E:0}};OE.LEVEL_PRESETS={casual:{difficulty:"easy",controllerMode:"ddr",buttons:{pitchInfluenceWeight:.3},description:"Easy difficulty, low pitch influence - for relaxed gameplay"},standard:{difficulty:"medium",controllerMode:"ddr",buttons:{pitchInfluenceWeight:.7},description:"Medium difficulty, high pitch influence - balanced experience"},challenge:{difficulty:"hard",controllerMode:"ddr",buttons:{pitchInfluenceWeight:1},description:"Hard difficulty, full pitch influence - for skilled players"},insane:{difficulty:"hard",controllerMode:"ddr",buttons:{pitchInfluenceWeight:1,consecutiveSameKeyLimit:4},description:"Maximum difficulty with strict limits - for experts only"}};let Lo=OE;const MW=["60s","70s","80s","90s","acidjazz","alternative","alternativerock","ambient","atmospheric","blues","bluesrock","bossanova","bossa","celtic","chanson","chillout","choir","classical","classicrock","club","comedy","country","cuban","dance","darkambient","darkwave","deephouse","disco","downtempo","drumandbass","dub","dubstep","easylistening","edm","electronic","electro","electropop","ethno","eurodance","experimental","folk","funk","fusion","groove","grunge","hardcore","hardrock","hiphop","house","indie","indiepop","indierock","industrial","instrumentalpop","instrumentalrock","jazz","jazzfusion","latin","lounge","metal","minimal","newage"," orchestrall","pop","popfolk","poprock","postrock","progressive","psychedelic","punkrock","rap","reggae","rnb","rock","rockandroll","ska","soul","soundtrack","synthpop","techno","trance","trip","triphop","underground","world","worldbeat","worldmusic"],Vo=["Blues---Boogie Woogie","Blues---Chicago Blues","Blues---Country Blues","Blues---Delta Blues","Blues---Electric Blues","Blues---Harmonica Blues","Blues---Jump Blues","Blues---Louisiana Blues","Blues---Modern Electric Blues","Blues---Piano Blues","Blues---Rhythm & Blues","Blues---Texas Blues","Brass & Military---Brass Band","Brass & Military---Marches","Brass & Military---Military","Children's-- - Educational","Children's-- - Nursery Rhymes","Children's-- - Story","Classical---Baroque","Classical---Choral","Classical---Classical","Classical---Contemporary","Classical---Impressionist","Classical---Medieval","Classical---Modern","Classical---Neo-Classical","Classical---Neo-Romantic","Classical---Opera","Classical---Post-Modern","Classical---Renaissance","Classical---Romantic","Electronic---Abstract","Electronic---Acid","Electronic---Acid House","Electronic---Acid Jazz","Electronic---Ambient","Electronic---Bassline","Electronic---Beatdown","Electronic---Berlin-School","Electronic---Big Beat","Electronic---Bleep","Electronic---Breakbeat","Electronic---Breakcore","Electronic---Breaks","Electronic---Broken Beat","Electronic---Chillwave","Electronic---Chiptune","Electronic---Dance-pop","Electronic---Dark Ambient","Electronic---Darkwave","Electronic---Deep House","Electronic---Deep Techno","Electronic---Disco","Electronic---Disco Polo","Electronic---Donk","Electronic---Downtempo","Electronic---Drone","Electronic---Drum n Bass","Electronic---Dub","Electronic---Dub Techno","Electronic---Dubstep","Electronic---Dungeon Synth","Electronic---EBM","Electronic---Electro","Electronic---Electro House","Electronic---Electroclash","Electronic---Euro House","Electronic---Euro-Disco","Electronic---Eurobeat","Electronic---Eurodance","Electronic---Experimental","Electronic---Freestyle","Electronic---Future Jazz","Electronic---Gabber","Electronic---Garage House","Electronic---Ghetto","Electronic---Ghetto House","Electronic---Glitch","Electronic---Goa Trance","Electronic---Grime","Electronic---Halftime","Electronic---Hands Up","Electronic---Happy Hardcore","Electronic---Hard House","Electronic---Hard Techno","Electronic---Hard Trance","Electronic---Hardcore","Electronic---Hardstyle","Electronic---Hi NRG","Electronic---Hip Hop","Electronic---Hip-House","Electronic---House","Electronic---IDM","Electronic---Illbient","Electronic---Industrial","Electronic---Italo House","Electronic---Italo-Disco","Electronic---Italodance","Electronic---Jazzdance","Electronic---Juke","Electronic---Jumpstyle","Electronic---Jungle","Electronic---Latin","Electronic---Leftfield","Electronic---Makina","Electronic---Minimal","Electronic---Minimal Techno","Electronic---Modern Classical","Electronic---Musique Concrète","Electronic---Neofolk","Electronic---New Age","Electronic---New Beat","Electronic---New Wave","Electronic---Noise","Electronic---Nu-Disco","Electronic---Power Electronics","Electronic---Progressive Breaks","Electronic---Progressive House","Electronic---Progressive Trance","Electronic---Psy-Trance","Electronic---Rhythmic Noise","Electronic---Schranz","Electronic---Sound Collage","Electronic---Speed Garage","Electronic---Speedcore","Electronic---Synth-pop","Electronic---Synthwave","Electronic---Tech House","Electronic---Tech Trance","Electronic---Techno","Electronic---Trance","Electronic---Tribal","Electronic---Tribal House","Electronic---Trip Hop","Electronic---Tropical House","Electronic---UK Garage","Electronic---Vaporwave","Folk, World, & Country---African","Folk, World, & Country---Bluegrass","Folk, World, & Country---Cajun","Folk, World, & Country---Canzone Napoletana","Folk, World, & Country---Catalan Music","Folk, World, & Country---Celtic","Folk, World, & Country---Country","Folk, World, & Country---Fado","Folk, World, & Country---Flamenco","Folk, World, & Country---Folk","Folk, World, & Country---Gospel","Folk, World, & Country---Highlife","Folk, World, & Country---Hillbilly","Folk, World, & Country---Hindustani","Folk, World, & Country---Honky Tonk","Folk, World, & Country---Indian Classical","Folk, World, & Country---Laïkó","Folk, World, & Country---Nordic","Folk, World, & Country---Pacific","Folk, World, & Country---Polka","Folk, World, & Country---Raï","Folk, World, & Country---Romani","Folk, World, & Country---Soukous","Folk, World, & Country---Séga","Folk, World, & Country---Volksmusik","Folk, World, & Country---Zouk","Folk, World, & Country---Éntekhno","Funk / Soul---Afrobeat","Funk / Soul---Boogie","Funk / Soul---Contemporary R&B","Funk / Soul---Disco","Funk / Soul---Free Funk","Funk / Soul---Funk","Funk / Soul---Gospel","Funk / Soul---Neo Soul","Funk / Soul---New Jack Swing","Funk / Soul---P.Funk","Funk / Soul---Psychedelic","Funk / Soul---Rhythm & Blues","Funk / Soul---Soul","Funk / Soul---Swingbeat","Funk / Soul---UK Street Soul","Hip Hop---Bass Music","Hip Hop---Boom Bap","Hip Hop---Bounce","Hip Hop---Britcore","Hip Hop---Cloud Rap","Hip Hop---Conscious","Hip Hop---Crunk","Hip Hop---Cut-up/DJ","Hip Hop---DJ Battle Tool","Hip Hop---Electro","Hip Hop---G-Funk","Hip Hop---Gangsta","Hip Hop---Grime","Hip Hop---Hardcore Hip-Hop","Hip Hop---Horrorcore","Hip Hop---Instrumental","Hip Hop---Jazzy Hip-Hop","Hip Hop---Miami Bass","Hip Hop---Pop Rap","Hip Hop---Ragga HipHop","Hip Hop---RnB/Swing","Hip Hop---Screw","Hip Hop---Thug Rap","Hip Hop---Trap","Hip Hop---Trip Hop","Hip Hop---Turntablism","Jazz---Afro-Cuban Jazz","Jazz---Afrobeat","Jazz---Avant-garde Jazz","Jazz---Big Band","Jazz---Bop","Jazz---Bossa Nova","Jazz---Contemporary Jazz","Jazz---Cool Jazz","Jazz---Dixieland","Jazz---Easy Listening","Jazz---Free Improvisation","Jazz---Free Jazz","Jazz---Fusion","Jazz---Gypsy Jazz","Jazz---Hard Bop","Jazz---Jazz-Funk","Jazz---Jazz-Rock","Jazz---Latin Jazz","Jazz---Modal","Jazz---Post Bop","Jazz---Ragtime","Jazz---Smooth Jazz","Jazz---Soul-Jazz","Jazz---Space-Age","Jazz---Swing","Latin---Afro-Cuban","Latin---Baião","Latin---Batucada","Latin---Beguine","Latin---Bolero","Latin---Boogaloo","Latin---Bossanova","Latin---Cha-Cha","Latin---Charanga","Latin---Compas","Latin---Cubano","Latin---Cumbia","Latin---Descarga","Latin---Forró","Latin---Guaguancó","Latin---Guajira","Latin---Guaracha","Latin---MPB","Latin---Mambo","Latin---Mariachi","Latin---Merengue","Latin---Norteño","Latin---Nueva Cancion","Latin---Pachanga","Latin---Porro","Latin---Ranchera","Latin---Reggaeton","Latin---Rumba","Latin---Salsa","Latin---Samba","Latin---Son","Latin---Son Montuno","Latin---Tango","Latin---Tejano","Latin---Vallenato","Non-Music---Audiobook","Non-Music---Comedy","Non-Music---Dialogue","Non-Music---Education","Non-Music---Field Recording","Non-Music---Interview","Non-Music---Monolog","Non-Music---Poetry","Non-Music---Political","Non-Music---Promotional","Non-Music---Radioplay","Non-Music---Religious","Non-Music---Spoken Word","Pop---Ballad","Pop---Bollywood","Pop---Bubblegum","Pop---Chanson","Pop---City Pop","Pop---Europop","Pop---Indie Pop","Pop---J-pop","Pop---K-pop","Pop---Kayōkyoku","Pop---Light Music","Pop---Music Hall","Pop---Novelty","Pop---Parody","Pop---Schlager","Pop---Vocal","Reggae---Calypso","Reggae---Dancehall","Reggae---Dub","Reggae---Lovers Rock","Reggae---Ragga","Reggae---Reggae","Reggae---Reggae-Pop","Reggae---Rocksteady","Reggae---Roots Reggae","Reggae---Ska","Reggae---Soca","Rock---AOR","Rock---Acid Rock","Rock---Acoustic","Rock---Alternative Rock","Rock---Arena Rock","Rock---Art Rock","Rock---Atmospheric Black Metal","Rock---Avantgarde","Rock---Beat","Rock---Black Metal","Rock---Blues Rock","Rock---Brit Pop","Rock---Classic Rock","Rock---Coldwave","Rock---Country Rock","Rock---Crust","Rock---Death Metal","Rock---Deathcore","Rock---Deathrock","Rock---Depressive Black Metal","Rock---Doo Wop","Rock---Doom Metal","Rock---Dream Pop","Rock---Emo","Rock---Ethereal","Rock---Experimental","Rock---Folk Metal","Rock---Folk Rock","Rock---Funeral Doom Metal","Rock---Funk Metal","Rock---Garage Rock","Rock---Glam","Rock---Goregrind","Rock---Goth Rock","Rock---Gothic Metal","Rock---Grindcore","Rock---Grunge","Rock---Hard Rock","Rock---Hardcore","Rock---Heavy Metal","Rock---Indie Rock","Rock---Industrial","Rock---Krautrock","Rock---Lo-Fi","Rock---Lounge","Rock---Math Rock","Rock---Melodic Death Metal","Rock---Melodic Hardcore","Rock---Metalcore","Rock---Mod","Rock---Neofolk","Rock---New Wave","Rock---No Wave","Rock---Noise","Rock---Noisecore","Rock---Nu Metal","Rock---Oi","Rock---Parody","Rock---Pop Punk","Rock---Pop Rock","Rock---Pornogrind","Rock---Post Rock","Rock---Post-Hardcore","Rock---Post-Metal","Rock---Post-Punk","Rock---Power Metal","Rock---Power Pop","Rock---Power Violence","Rock---Prog Rock","Rock---Progressive Metal","Rock---Psychedelic Rock","Rock---Psychobilly","Rock---Pub Rock","Rock---Punk","Rock---Rock & Roll","Rock---Rockabilly","Rock---Shoegaze","Rock---Ska","Rock---Sludge Metal","Rock---Soft Rock","Rock---Southern Rock","Rock---Space Rock","Rock---Speed Metal","Rock---Stoner Rock","Rock---Surf","Rock---Symphonic Rock","Rock---Technical Death Metal","Rock---Thrash","Rock---Twist","Rock---Viking Metal","Rock---Yé-Yé","Stage & Screen---Musical","Stage & Screen---Score","Stage & Screen---Soundtrack","Stage & Screen---Theme"],RW=["Blues","Classical","Country","Disco","Hip-Hop","Jazz","Metal","Pop","Reggae","Rock"],yW=["guitar","classical","slow","techno","strings","drums","electronic","rock","fast","piano","ambient","beat","violin","vocal","synth","female","indian","opera","male","singing","vocals","no vocals","harpsichord","loud","quiet","flute","woman","male vocal","no vocal","pop","soft","sitar","solo","man","classic","choir","voice","new age","dance","male voice","female vocal","beats","harp","cello","no voice","weird","country","metal","female voice","choral"],uW=["happy","not happy"],mW=["action","adventure","advertising","background","ballad","calm","children","christmas","commercial","cool","corporate","dark","deep","documentary","drama","dramatic","dream","emotional","energetic","epic","fast","film","fun","funny","game","groovy","happy","heavy","holiday","hopeful","inspiring","love","meditative","melancholic","melodic","motivational","movie","nature","nostalgic","party","peaceful","positive","relaxing","retro","romantic","sad","scary","science","sea","sentimental","serene","sexy","space","sport","summer","suspense","trailer","travel","upbeat","uplifting","video","war"],SW=["danceable","non-danceable"],bW=["voice","instrumental"],pW=["acoustic","electronic"];function PQ(g){return typeof g=="object"&&g!==null&&"embedding"in g&&"classifier"in g}function vo(g){return typeof g=="object"&&g!==null&&"modelUrl"in g}function Ae(g,A){if(A)return A;const B=g.toLowerCase();return B.includes("effnet")||B.includes("discogs")?"effnet":B.includes("vggish")?"vggish":B.includes("tempocnn")||B.includes("tempo")&&!B.includes("temple")?"tempocnn":"musicnn"}function Nd(g,A){if(A)return A;const B=g.toLowerCase();return B.includes("jamendo")?"jamendo":B.includes("discogs400")||B.includes("discogs")?"discogs400":B.includes("tzanetakis")?"tzanetakis":B.includes("mtt_musicnn")?"mtt_musicnn":"jamendo"}function Wd(g){switch(g){case"jamendo":return MW;case"discogs400":return Vo;case"tzanetakis":return RW;case"mtt_musicnn":return yW;default:return Vo}}function qI(g){return PQ(g)?`${g.embedding} -> ${g.classifier}`:g.modelUrl}function YW(g){if(!g||g.length===0)return[];const A=g[0].length;if(A===0)return[];const B=[];for(let I=0;I<A;I++){const Q=g.reduce((E,i)=>E+(i[I]??0),0);B.push(Q/g.length)}return B}const Be={genre:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",classifier:"https://arweave.net/ZY-GSfMe7crJUITAtHITcoLCNfNWVP1HMwywivZ_LAQ/model.json",embeddingType:"effnet",classifierType:"discogs400"},mood:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",classifier:"https://arweave.net/BUXf3AoFuIsrNDkV2hW6BhiwSVTuFllWOUQv5mu6qQ8/model.json",embeddingType:"effnet"},danceability:{modelUrl:"https://turbo-gateway.com/nX9KX1OVhEaT1dStNcsRiZKCQTWuHjAMl4MWprIFyZU/model.json",modelType:"musicnn"}},Xo={discogs400:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/ZY-GSfMe7crJUITAtHITcoLCNfNWVP1HMwywivZ_LAQ/model.json",classifierType:"discogs400"},jamendo:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/MuhF5mek1BJPZLoPNY1TBTPUUBEXbVmMfAGgBp-_MyA/model.json",classifierType:"jamendo"},tzanetakis:{modelUrl:"https://arweave.net/7MQD4W5yJeUUK2tRg8TEdomew-ZY7s0K91nk35FxleM/model.json",modelType:"musicnn",genreType:"tzanetakis"},musicnn:{modelUrl:"https://arweave.net/KCZQ1geu4ymxp8axAql95FDY98VjnOzSymdkCiM9BXo/model.json",modelType:"musicnn",genreType:"mtt_musicnn"}},Ko={jamendo:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/BUXf3AoFuIsrNDkV2hW6BhiwSVTuFllWOUQv5mu6qQ8/model.json",classifierType:"jamendo"},happyMusicnn:{modelUrl:"https://arweave.net/kUIS-Xxr4k3MZ4K2gHdvMgZxK7aBYce_FPGIkGA_hjM/model.json",modelType:"musicnn"}},zo={default:{modelUrl:"https://arweave.net/nX9KX1OVhEaT1dStNcsRiZKCQTWuHjAMl4MWprIFyZU/model.json",modelType:"musicnn"}},UW={genre:Object.keys(Xo),mood:Object.keys(Ko),danceability:Object.keys(zo)};function fW(g){const A={};return g.genre&&(A.genre=Xo[g.genre]),g.mood&&(A.mood=Ko[g.mood]),g.danceability&&(A.danceability=zo[g.danceability]),A}class kW{constructor(A={}){this.essentiaWASM=null,this.essentiaModel=null,this.extractor=null,this.initialized=!1,this.embeddingModelCache=new Map,this.classifierModelCache=new Map,this.resolvedUrlCache=new Map,this.extractors=new Map;const B=A.preset?fW(A.preset):{};this.options={models:{genre:Be.genre,mood:Be.mood,danceability:Be.danceability,...B,...A.models},topN:5,threshold:.05,cacheEmbeddings:!0,enableModelCache:!0,...A},this.options.resolveUrl||(this.options.resolveUrl=TA.resolveUrl.bind(TA)),delete this.options.preset}async initializeEssentia(){if(this.initialized)return;const B=(await Promise.resolve().then(()=>Fb)).EssentiaWASM;await B.ready,this.essentiaWASM=B;const I=await Promise.resolve().then(()=>$z);this.essentiaModel=I,this.extractor=new I.EssentiaTFInputExtractor(this.essentiaWASM,"musicnn");const Q=new I.EssentiaTFInputExtractor(this.essentiaWASM,"vggish");this.extractors.set("vggish",Q),this.initialized=!0}async resolveUrlWithCache(A){if(this.resolvedUrlCache.has(A))return this.resolvedUrlCache.get(A);if(!this.options.resolveUrl)return A;try{const B=await this.options.resolveUrl(A);return this.resolvedUrlCache.set(A,B),B!==A&&console.info("[MusicClassifier] Resolved URL to alternate gateway",{originalUrl:A,resolvedUrl:B}),B}catch(B){return console.warn("[MusicClassifier] URL resolution failed, using original URL",{url:A,error:String(B)}),this.resolvedUrlCache.set(A,A),A}}async loadModelWithRetry(A,B=3,I=1e3){if(this.options.enableModelCache)return xI.getOrFetchGraphModel(IB,A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i),maxRetries:B,baseDelayMs:I});let Q=null;const E=await this.resolveUrlWithCache(A);for(let i=0;i<B;i++)try{return await IB.loadGraphModel(E)}catch(e){Q=e;const a=String(e).toLowerCase();if(!(a.includes("fetch")||a.includes("network")||a.includes("timeout")||a.includes("failed to fetch")||a.includes("networkerror"))||i===B-1)throw e;const o=I*Math.pow(2,i);console.warn(`[MusicClassifier] Model load failed (attempt ${i+1}/${B}), retrying in ${o}ms...`,{originalUrl:A,resolvedUrl:E,error:String(e)}),await new Promise(t=>setTimeout(t,o))}throw Q}async initializeEssentiaModelWithRetry(A,B,I=3,Q=1e3){for(let E=0;E<I;E++){const i=await this.resolveUrlWithCache(A),e=B==="vggish"?this.essentiaModel.TensorflowVGGish:this.essentiaModel.TensorflowMusiCNN,a=new e(IB,i);try{return await a.initialize(),a}catch(C){const o=String(C).toLowerCase();if(!(o.includes("fetch")||o.includes("network")||o.includes("timeout")||o.includes("failed to fetch")||o.includes("networkerror")||o.includes("404"))||E===I-1)throw C;TA.invalidateCacheForUrl(A);const s=Q*Math.pow(2,E);console.warn(`[MusicClassifier] Essentia model init failed (attempt ${E+1}/${I}), re-resolving and retrying in ${s}ms...`,{url:A,resolvedUrl:i,error:String(C)}),await new Promise(n=>setTimeout(n,s))}}throw new Error(`[MusicClassifier] Essentia model failed to load after ${I} retries: ${A}`)}async getEmbeddingModel(A,B){if(this.embeddingModelCache.has(A))return this.embeddingModelCache.get(A);const I=Ae(A,B);let Q;if(I==="effnet")Q=await this.loadModelWithRetry(A);else if(I==="vggish"){if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");if(this.options.enableModelCache){const E=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i)});Q=new this.essentiaModel.TensorflowVGGish(IB,E),await Q.initialize()}else Q=await this.initializeEssentiaModelWithRetry(A,"vggish")}else{if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");if(this.options.enableModelCache){const E=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i)});Q=new this.essentiaModel.TensorflowMusiCNN(IB,E),await Q.initialize()}else Q=await this.initializeEssentiaModelWithRetry(A,"musicnn")}return this.options.cacheEmbeddings&&this.embeddingModelCache.set(A,Q),Q}clearEmbeddingCache(){for(const A of this.embeddingModelCache.values())A.dispose?A.dispose():A.terminate&&A.terminate();this.embeddingModelCache.clear()}clearClassifierCache(){for(const A of this.classifierModelCache.values())A.terminate&&A.terminate();this.classifierModelCache.clear()}clearAllCaches(){this.clearEmbeddingCache(),this.clearClassifierCache(),this.resolvedUrlCache.clear()}getFeaturesForArchitecture(A,B){switch(B){case"effnet":return this.extractor.computeFrameWise(A,256).melSpectrum;case"vggish":const I=this.extractors.get("vggish");return I?I.computeFrameWise(A,160):(console.warn("VGGish extractor not initialized, falling back to default 96-band extractor."),this.extractor.computeFrameWise(A,256));case"tempocnn":return console.warn("TempoCNN architecture requested but using default 96-band extractor. Results may be suboptimal for TempoCNN models."),this.extractor.computeFrameWise(A,256);default:return this.extractor.computeFrameWise(A,256)}}sliceAudioSignal(A,B){if(!this.options.analysisDurationSeconds&&this.options.analysisStartPosition===void 0)return A;const I=A.length,Q=I/B,E=Math.min(this.options.analysisDurationSeconds??Q,Q),i=this.options.analysisStartPosition??.5,e=Math.floor(E*B),a=I-e,C=Math.max(0,Math.floor(i*a)),o=Math.min(C+e,I);return console.info(`[MusicClassifier] Analyzing segment: ${E.toFixed(1)}s starting at ${i} (samples ${C}-${o} of ${I}, ${(C/B).toFixed(1)}s-${(o/B).toFixed(1)}s of ${Q.toFixed(1)}s)`),A.slice(C,o)}async analyze(A){try{await this.initializeEssentia();const I=await(await fetch(A)).arrayBuffer(),Q=new(window.AudioContext||window.webkitAudioContext),E=await Q.decodeAudioData(I),i=await this.extractor.downsampleAudioBuffer(E,Q.sampleRate),e=this.sliceAudioSignal(i,16e3),a=Math.max(0,Math.floor((e.length-512)/256)+1),C=[],o={genres:[],moods:[],mood_tags:[],vibe_metrics:{}};if(this.options.models?.genre){const t=this.options.models.genre,s=PQ(t)?t.classifier:t.modelUrl;let n;PQ(t)&&t.classifierType?n=t.classifierType:vo(t)&&t.genreType?n=t.genreType:n=Nd(s);const r=Wd(n);o.genres=await this.runModelPrediction(t,e,r),o.primary_genre=o.genres.length>0?o.genres[0].name:"Unknown",C.push(qI(t))}if(this.options.models?.mood){const t=this.options.models.mood,s=vo(t)?uW:mW;o.moods=await this.runModelPrediction(t,e,s),o.mood_tags=o.moods.slice(0,3).map(n=>n.name),C.push(qI(t))}if(this.options.models?.danceability){const t=this.options.models.danceability,n=(await this.runModelPrediction(t,e,SW)).find(r=>r.name==="danceable");o.vibe_metrics.danceability=n?.confidence??0,C.push(qI(t))}if(this.options.models?.voice){const t=this.options.models.voice,n=(await this.runModelPrediction(t,e,bW)).find(r=>r.name==="instrumental");o.vibe_metrics.instrumental_probability=n?.confidence??0,C.push(qI(t))}if(this.options.models?.acoustic){const t=this.options.models.acoustic,n=(await this.runModelPrediction(t,e,pW)).find(r=>r.name==="electronic");o.vibe_metrics.electronic_probability=n?.confidence??0,C.push(qI(t))}if(o.moods){const t=o.moods.find(n=>n.name==="energetic"||n.name==="upbeat"||n.name==="epic"),s=o.moods.find(n=>n.name==="happy"||n.name==="positive"||n.name==="uplifting");t&&(o.vibe_metrics.energy=t.confidence),s&&(o.vibe_metrics.valence=s.confidence)}return{genres:o.genres||[],moods:o.moods||[],primary_genre:o.primary_genre||"Unknown",mood_tags:o.mood_tags||[],vibe_metrics:o.vibe_metrics,analysis_metadata:{models_used:C,model_used:C.length>0?C[0]:void 0,frames_analyzed:a,duration_analyzed:e.length/16e3,analyzed_at:new Date().toISOString()}}}catch(B){throw console.error("Music analysis failed:",B),B}}async runClassifierOnEmbeddings(A,B){if(B.length===0||B[0].length===0)return console.warn("Empty embeddings provided to classifier"),[];const I=await this.loadModelWithRetry(A),Q=IB.tensor2d(B);let E=[];try{const i=I.predict(Q),e=await i.data(),a=i.shape,C=a[a.length-1],o=Math.max(1,Math.floor(e.length/C)),t=new Array(C).fill(0);for(let s=0;s<o;s++)for(let n=0;n<C;n++)t[n]+=e[s*C+n];E=t.map(s=>s/o),i.dispose()}finally{Q.dispose(),I.dispose()}return E}async predictWithTwoStepModel(A,B){const I=Ae(A.embedding,A.embeddingType),Q=this.getFeaturesForArchitecture(B,I);if(Q.length===0)return console.warn("No features extracted from audio signal"),[];const E=await this.getEmbeddingModel(A.embedding,A.embeddingType);let i;return I==="effnet"?i=await this.runEffnetEmbedding(E,Q):I==="vggish"?i=await this.runEssentiaEmbedding(E,Q):i=await this.runEssentiaEmbedding(E,Q),i.length===0?(console.warn("No embeddings produced by embedding model"),[]):await this.runClassifierOnEmbeddings(A.classifier,i)}getModelInputNames(A){const B=[];try{if(A.inputs&&Array.isArray(A.inputs))for(const I of A.inputs)I&&I.name&&B.push(I.name);if(B.length===0&&A.executor){const I=A.executor;if(I._signature&&I._signature.inputs)for(const Q of Object.keys(I._signature.inputs))B.push(Q)}if(B.length===0&&A.inputNodes){const I=A.inputNodes;Array.isArray(I)&&B.push(...I)}if(B.length===0&&A.metadata){const I=A.metadata;if(I.signature&&I.signature.inputs)for(const Q of Object.keys(I.signature.inputs))B.push(Q)}if(B.length===0){const I=A;if(I.graph&&I.graph.inputs)for(const Q of Object.keys(I.graph.inputs))B.push(Q)}}catch(I){console.warn("[MusicClassifier] Error discovering input names:",I)}return B}async runEffnetEmbedding(A,B){const I=B.length,Q=64,E=128,i=96;if(I===0)return[];(B[0]?.length??0)!==i&&console.warn(`[MusicClassifier] EffNet expects ${i}-band mel features but got ${B[0]?.length}-band frames. Results will be unreliable.`);const e=[],a=Math.max(0,I-E),C=new Array(i).fill(0),o=new Float32Array(Q*E*i);for(let s=0;s<Q;s++){const n=Math.round(s*a/(Q-1));for(let r=0;r<E;r++){const c=B[n+r]??C,G=(s*E+r)*i;for(let D=0;D<i;D++)o[G+D]=c[D]??0}}const t=IB.tensor3d(o,[Q,E,i]);try{const s=this.getModelInputNames(A);if(s.length===0)throw new Error("Could not discover model input names");const n={};for(const y of s)y.includes("melspectrogram")||y.includes("mel")||y.includes("spectrogram")?n[y]=t:y.includes("saver")||y.includes("filename")?n[y]=IB.scalar(""):n[y]=t;const r=A.execute(n);let c;if(Array.isArray(r)?c=r[0]:r&&typeof r=="object"&&!("data"in r)?c=Object.values(r)[0]:c=r,!c||typeof c.data!="function")throw new Error("Model execution did not return a valid tensor");const G=await c.data(),D=c.shape,h=D[D.length-1],w=Math.max(1,Math.floor(G.length/h));for(let y=0;y<w;y++)e.push(Array.from(G.slice(y*h,(y+1)*h)));c.dispose();for(const[y,R]of Object.entries(n))R!==t&&R.dispose()}finally{t.dispose()}return e}async runEssentiaEmbedding(A,B){const I=await A.predict(B,!0);return!I||I.length===0?[]:I}async runModelPrediction(A,B,I){let Q;if(PQ(A))Q=await this.predictWithTwoStepModel(A,B);else{const E=Ae(A.modelUrl,A.modelType),i=this.getFeaturesForArchitecture(B,E);Q=await this.predictWithModel(A.modelUrl,i)}return this.mapPredictions(Q,I)}async predictWithModel(A,B){if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");let I;if(this.options.enableModelCache){const e=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:a=>TA.invalidateCacheForUrl(a)});I=new this.essentiaModel.TensorflowMusiCNN(IB,e),await I.initialize()}else I=await this.initializeEssentiaModelWithRetry(A,"musicnn");const Q=await I.predict(B,!0);if(!Q||Q.length===0)return[];const E=Q[0].length,i=[];for(let e=0;e<E;e++){const a=Q.reduce((C,o)=>C+o[e],0);i.push(a/Q.length)}return I.terminate&&I.terminate(),i}mapPredictions(A,B){return A.length>0&&A.length!==B.length&&console.warn(`[MusicClassifier] Model returned ${A.length} outputs but ${B.length} labels are configured — tags may be mislabeled. Check that the label list matches the model.`),A.map((I,Q)=>({name:B[Q]||`unknown_${Q}`,confidence:I})).sort((I,Q)=>Q.confidence-I.confidence).filter(I=>I.confidence>=this.options.threshold).slice(0,this.options.topN)}}class ZW{constructor(A={}){this.canvas=null,this.context=null,typeof document<"u"&&(this.canvas=document.createElement("canvas"),this.canvas.width=100,this.canvas.height=100,this.context=this.canvas.getContext("2d",{willReadFrequently:!0}))}async extractPalette(A){try{if(!this.context||!this.canvas)throw new Error("Canvas not supported in this environment");const B=await TA.resolveUrl(A),I=await this.loadImage(B);this.context.drawImage(I,0,0,100,100);const Q=this.context.getImageData(0,0,100,100),E=this.getPixels(Q);if(E.length===0)return this.getFallbackPalette();let i=this.kMeans(E,4);i.length<4&&(i=this.medianCut(E,4));const e=this.sortColorsByFrequency(E,i);return this.analyzePalette(e)}catch(B){return console.warn("Color extraction failed, using fallback:",B),this.getFallbackPalette()}}loadImage(A){return new Promise((B,I)=>{const Q=new Image;Q.crossOrigin="Anonymous",Q.onload=()=>B(Q),Q.onerror=E=>I(E),Q.src=A})}getPixels(A){const B=[];for(let I=0;I<A.data.length;I+=4){const Q=A.data[I],E=A.data[I+1],i=A.data[I+2];A.data[I+3]>=128&&B.push([Q,E,i])}return B}kMeans(A,B){if(A.length===0)return[];if(A.length<B)return A;let I=[];const Q=new Set;for(;I.length<B&&I.length<A.length;){const E=Math.floor(Math.random()*A.length);Q.has(E)||(Q.add(E),I.push([...A[E]]))}for(let E=0;E<10;E++){const i=Array.from({length:B},()=>[]);for(const a of A){let C=1/0,o=0;for(let t=0;t<I.length;t++){const s=this.distance(a,I[t]);s<C&&(C=s,o=t)}i[o].push(a)}const e=[];for(let a=0;a<B;a++)if(i[a].length===0)e.push(I[a]);else{const C=i[a].reduce((o,t)=>[o[0]+t[0],o[1]+t[1],o[2]+t[2]],[0,0,0]);e.push([Math.round(C[0]/i[a].length),Math.round(C[1]/i[a].length),Math.round(C[2]/i[a].length)])}if(e.every((a,C)=>a[0]===I[C][0]&&a[1]===I[C][1]&&a[2]===I[C][2]))break;I=e}return I}medianCut(A,B){if(A.length===0)return[];const I=[],Q=[{pixels:[...A],depth:0}];for(;Q.length<B&&Q.length>0;){let E=Q[0],i=0,e=0;for(let o=0;o<Q.length;o++){const t=this.getBoxRange(Q[o].pixels);t>e&&(e=t,E=Q[o],i=o)}if(E.pixels.length<=1)break;const a=this.sortPixelsByChannel(E.pixels,E.depth%3),C=Math.floor(a.length/2);Q.splice(i,1),Q.push({pixels:a.slice(0,C),depth:E.depth+1},{pixels:a.slice(C),depth:E.depth+1})}for(const E of Q)if(E.pixels.length>0){const i=E.pixels.reduce((e,a)=>[e[0]+a[0],e[1]+a[1],e[2]+a[2]],[0,0,0]);I.push([Math.round(i[0]/E.pixels.length),Math.round(i[1]/E.pixels.length),Math.round(i[2]/E.pixels.length)])}return I.slice(0,B)}getBoxRange(A){if(A.length===0)return 0;let B=1/0,I=-1/0,Q=1/0,E=-1/0,i=1/0,e=-1/0;for(const[a,C,o]of A)B=Math.min(B,a),I=Math.max(I,a),Q=Math.min(Q,C),E=Math.max(E,C),i=Math.min(i,o),e=Math.max(e,o);return Math.max(I-B,E-Q,e-i)}sortPixelsByChannel(A,B){return[...A].sort((I,Q)=>I[B]-Q[B])}distance(A,B){return Math.sqrt(Math.pow(A[0]-B[0],2)+Math.pow(A[1]-B[1],2)+Math.pow(A[2]-B[2],2))}sortColorsByFrequency(A,B){const I=new Map;for(const Q of A){let E=B[0],i=1/0;for(const a of B){const C=this.distance(Q,a);C<i&&(i=C,E=a)}const e=`${E[0]},${E[1]},${E[2]}`;I.set(e,(I.get(e)||0)+1)}return B.sort((Q,E)=>{const i=`${Q[0]},${Q[1]},${Q[2]}`,e=`${E[0]},${E[1]},${E[2]}`;return(I.get(e)||0)-(I.get(i)||0)})}analyzePalette(A){const B=A.map(C=>this.rgbToHex(C[0],C[1],C[2]));if(B.length===0)return this.getFallbackPalette();const I=B[0],Q=B[1],E=B[2],i=this.getBrightness(A[0]),e=this.getSaturation(A[0]),a=e<.1;return{colors:B,primary_color:I,secondary_color:Q,accent_color:E,brightness:i/255,saturation:e,is_monochrome:a}}getBrightness(A){return(A[0]*299+A[1]*587+A[2]*114)/1e3}getSaturation(A){const B=Math.max(A[0],A[1],A[2]),I=Math.min(A[0],A[1],A[2]);return B===0?0:(B-I)/B}rgbToHex(A,B,I){return"#"+[A,B,I].map(Q=>{const E=Q.toString(16);return E.length===1?"0"+E:E}).join("").toUpperCase()}getFallbackPalette(){return{colors:["#000000","#333333","#666666"],primary_color:"#000000",secondary_color:"#333333",accent_color:"#666666",brightness:0,saturation:0,is_monochrome:!0}}}const To=VA.for("BeatSubdivider"),NW={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};class LI{constructor(A){this.options={...NW,...A},To.debug("BeatSubdivider initialized",{options:this.options})}subdivide(A,B=CC){if(To.debug("Starting per-beat subdivision",{audioId:A.audioId,beatCount:A.beats.length,explicitAssignments:B.beatSubdivisions.size,defaultSubdivision:B.defaultSubdivision}),sC(B),PG(B,A.beats.length),A.beats.length===0)return this.createEmptySubdividedBeatMap(A,B);const I=new Set;let Q=0;const E=!!A.tempoSections&&A.tempoSections.length>1;let i=0;const e=c=>{let G=0;for(let D=c-1;D>=0&&(B.beatSubdivisions.get(D)??B.defaultSubdivision)==="triplet4";D--)G++;return G%2},a=[];for(let c=0;c<A.beats.length;c++){const G=B.beatSubdivisions.get(c)??B.defaultSubdivision;B.beatSubdivisions.has(c)&&i++,I.add(G);const D=tC(G);Q=Math.max(Q,D);const h=A.beats[c],w=c<A.beats.length-1?A.beats[c+1]:null,y=A.detectedBeatIndices.includes(c);if(G==="rest"||G==="half"&&h.beatInMeasure%2!==0)continue;const R=G==="dotted4"&&h.beatInMeasure%2!==0;if(G==="offbeat8"||G==="triplet4"&&e(c)===1||R||a.push({...h,isDetected:y,originalBeatIndex:c,subdivisionType:G}),w){const k=this.createInterpolatedBeatsForSubdivision(h,w,c,G,A,E,B,e(c));a.push(...k)}}const C=this.buildDetectedBeatIndices(a),o=A.beats.length,t=a.length,s=o>0?t/o:1,n={originalBeatCount:o,subdividedBeatCount:t,averageDensityMultiplier:s,explicitBeatCount:i,subdivisionsUsed:Array.from(I),hasMultipleTempos:E,maxDensity:Q},r={audioId:A.audioId,duration:A.duration,beats:a,detectedBeatIndices:C,subdivisionConfig:B,downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections,subdivisionMetadata:n};return To.debug("Per-beat subdivision complete",{originalBeats:o,subdividedBeats:t,densityMultiplier:s,subdivisionsUsed:Array.from(I)}),r}createInterpolatedBeatsForSubdivision(A,B,I,Q,E,i,e,a=0){const C=[],o=E.quarterNoteInterval,t=i?this.getQuarterNoteIntervalForTimestamp(E,A.timestamp):o;switch(Q){case"quarter":break;case"half":break;case"eighth":C.push(this.createInterpolatedBeat(A,B,.5,"eighth",t,this.options));break;case"sixteenth":for(let n=.25;n<1;n+=.25)C.push(this.createInterpolatedBeat(A,B,n,"sixteenth",t,this.options));break;case"triplet8":for(const n of[1/3,2/3])C.push(this.createInterpolatedBeat(A,B,n,"triplet8",t,this.options));break;case"triplet4":{a===0?C.push(this.createInterpolatedBeat(A,B,2/3,"triplet4",t,this.options)):(e.beatSubdivisions.get(I-1)??e.defaultSubdivision)==="triplet4"&&C.push(this.createInterpolatedBeat(A,B,1/3,"triplet4",t,this.options));break}case"dotted4":A.beatInMeasure%2!==0&&C.push(this.createInterpolatedBeat(A,B,.5,"dotted4",t,this.options));break;case"dotted8":C.push(this.createInterpolatedBeat(A,B,3/4,"dotted8",t,this.options));break;case"swing":C.push(this.createInterpolatedBeat(A,B,2/3,"swing",t,this.options));break;case"offbeat8":C.push(this.createInterpolatedBeat(A,B,.5,"offbeat8",t,this.options));break;case"rest":break;default:const s=Q;throw new Error(`Unknown subdivision type: ${s}`)}return C}createEmptySubdividedBeatMap(A,B){return{audioId:A.audioId,duration:A.duration,beats:[],detectedBeatIndices:[],subdivisionConfig:B,downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections,subdivisionMetadata:{originalBeatCount:0,subdividedBeatCount:0,averageDensityMultiplier:1,explicitBeatCount:0,subdivisionsUsed:[B.defaultSubdivision],hasMultipleTempos:!!A.tempoSections&&A.tempoSections.length>1,maxDensity:1}}}createInterpolatedBeat(A,B,I,Q,E,i){const e=B.timestamp-A.timestamp,a=A.timestamp+e*I,C=A.beatInMeasure+I,o=(A.intensity+B.intensity)/2,t=(A.confidence+B.confidence)/2;return{timestamp:a,beatInMeasure:C,isDownbeat:!1,measureNumber:A.measureNumber,intensity:o,confidence:t,isDetected:!1,subdivisionType:Q}}findClosestBeat(A,B){let I=A[0],Q=Math.abs(A[0].timestamp-B);for(const E of A){const i=Math.abs(E.timestamp-B);i<Q&&(Q=i,I=E)}return I}calculateBeatInMeasure(A,B,I){return(A-B)/I%4}calculateMeasureNumber(A,B,I,Q){const i=(A-B)/I,e=Q.segments[0]?.timeSignature?.beatsPerMeasure??4;return Math.floor(i/e)}buildDetectedBeatIndices(A){const B=[];for(let I=0;I<A.length;I++)A[I].isDetected&&B.push(I);return B}getTempoSectionForTimestamp(A,B){if(!(!A||A.length===0)){for(const I of A)if(B>=I.start&&B<I.end)return I;return A[A.length-1]}}getQuarterNoteIntervalForTimestamp(A,B){const I=this.getTempoSectionForTimestamp(A.tempoSections,B);return I?I.intervalSeconds:A.quarterNoteInterval}getTempoSectionForBeatIndex(A,B){if(!(!A||A.length===0)){for(const I of A)if(B>=I.startBeatIndex&&B<=I.endBeatIndex)return I;return A[A.length-1]}}static toJSON(A){const B=Array.from(A.subdivisionConfig.beatSubdivisions.entries()),I={audioId:A.audioId,duration:A.duration,beats:A.beats.map(Q=>({timestamp:Q.timestamp,beatInMeasure:Q.beatInMeasure,isDownbeat:Q.isDownbeat,measureNumber:Q.measureNumber,intensity:Q.intensity,confidence:Q.confidence,requiredKey:Q.requiredKey,isDetected:Q.isDetected,originalBeatIndex:Q.originalBeatIndex,subdivisionType:Q.subdivisionType})),detectedBeatIndices:A.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:B,defaultSubdivision:A.subdivisionConfig.defaultSubdivision},downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections?.map(Q=>({start:Q.start,end:Q.end,bpm:Q.bpm,intervalSeconds:Q.intervalSeconds,beatCount:Q.beatCount,startBeatIndex:Q.startBeatIndex,endBeatIndex:Q.endBeatIndex})),subdivisionMetadata:{originalBeatCount:A.subdivisionMetadata.originalBeatCount,subdividedBeatCount:A.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:A.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:A.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:A.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:A.subdivisionMetadata.hasMultipleTempos,maxDensity:A.subdivisionMetadata.maxDensity}};return JSON.stringify(I)}static fromJSON(A){const B=JSON.parse(A),I=new Map(B.subdivisionConfig.beatSubdivisions||[]);return{audioId:B.audioId,duration:B.duration,beats:B.beats.map(Q=>({timestamp:Q.timestamp,beatInMeasure:Q.beatInMeasure,isDownbeat:Q.isDownbeat,measureNumber:Q.measureNumber,intensity:Q.intensity,confidence:Q.confidence,requiredKey:Q.requiredKey,isDetected:Q.isDetected,originalBeatIndex:Q.originalBeatIndex,subdivisionType:Q.subdivisionType})),detectedBeatIndices:B.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:I,defaultSubdivision:B.subdivisionConfig.defaultSubdivision},downbeatConfig:B.downbeatConfig,tempoSections:B.tempoSections?.map(Q=>({start:Q.start,end:Q.end,bpm:Q.bpm,intervalSeconds:Q.intervalSeconds,beatCount:Q.beatCount,startBeatIndex:Q.startBeatIndex,endBeatIndex:Q.endBeatIndex})),subdivisionMetadata:{originalBeatCount:B.subdivisionMetadata.originalBeatCount,subdividedBeatCount:B.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:B.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:B.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:B.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:B.subdivisionMetadata.hasMultipleTempos,maxDensity:B.subdivisionMetadata.maxDensity}}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=LI.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return LI.fromJSON(I)}}class WW{constructor(A){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0,this.previousDirection="neutral",this.options={...gh,...A}}setDifficulty(A){const B=ih(A.preset,A.customPenalties);this.options.hotnessLossOnMiss=B.hotnessLossOnMiss,this.options.hotnessLossOnBreak=B.hotnessLossOnBreak}recordHit(A,B,I=0,Q="perfect"){if(this.hitCount++,this.lastBpm=B,Q==="miss"||Q==="wrongKey")return this.recordMiss(I);const E=this.pocketDirection;this.recentOffsets.push(A),this.recentOffsets.length>this.options.averagingWindowSize&&this.recentOffsets.shift(),this.updateRunningAverage(),this.pocketDirection=this.determineDirection(this.establishedOffset);const i=this.calculatePocketWindow(B),e=Math.abs(A-this.establishedOffset),a=this.calculateConsistency(e,i),C=this.hasPocket()&&e<=i;this.hasPocket()&&(C?(this.hotness=this.hotness+this.options.hotnessGainPerHit,this.streakLength++):this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnBreak),this.maxGrooveStreak=Math.max(this.maxGrooveStreak,this.streakLength));let o;const t=E!==this.pocketDirection,s=t||this.hotness===0;if(this.hotness>0&&!s)this.grooveStartTime===null&&(this.grooveStartTime=I??this.hitCount*(60/B)),this.maxHotness=Math.max(this.maxHotness,this.hotness),this.hotnessSamples.push(this.hotness),this.grooveHitCount++;else if(s&&this.grooveStartTime!==null&&this.grooveHitCount>0){const n=I??this.hitCount*(60/B),r=this.hotnessSamples.length>0?this.hotnessSamples.reduce((c,G)=>c+G,0)/this.hotnessSamples.length:0;o={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:r,duration:n-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:n},this.streakLength=0,this.hotness=0,this.resetGrooveStats()}return t&&(this.streakLength=0,this.hotness=0,this.resetGrooveStats()),this.previousDirection=this.pocketDirection,{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:a,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,inPocket:C,pocketWindow:i,endedGrooveStats:o}}recordMiss(A){const I=this.hotness>0&&this.grooveStartTime!==null&&this.grooveHitCount>0;this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnMiss),this.streakLength=0;const Q=this.calculatePocketWindow(this.lastBpm);let E;if(I&&this.hotness===0){const i=A??this.hitCount*(60/this.lastBpm),e=this.hotnessSamples.length>0?this.hotnessSamples.reduce((a,C)=>a+C,0)/this.hotnessSamples.length:0;E={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:e,duration:i-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:i},this.resetGrooveStats()}return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:0,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,inPocket:!1,pocketWindow:Q,endedGrooveStats:E}}getState(){const A=this.hotnessSamples.length>0?this.hotnessSamples.reduce((B,I)=>B+I,0)/this.hotnessSamples.length:0;return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,hitCount:this.hitCount,pocketWindow:this.calculatePocketWindow(this.lastBpm),grooveStartTime:this.grooveStartTime,grooveDuration:this.grooveStartTime!==null?this.grooveHitCount*(60/this.lastBpm):0,maxHotness:this.maxHotness,avgHotness:A,grooveHitCount:this.grooveHitCount}}reset(){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.resetGrooveStats()}getGrooveStats(A){if(this.grooveStartTime===null||this.grooveHitCount===0)return null;const B=A??this.hitCount*(60/this.lastBpm),I=this.hotnessSamples.length>0?this.hotnessSamples.reduce((Q,E)=>Q+E,0)/this.hotnessSamples.length:0;return{maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:I,duration:B-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:B}}resetGrooveStats(){this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0}hasPocket(){return this.hitCount>=this.options.minHitsForPocket}calculatePocketWindow(A){const I=Bh(this.hotness)/1e3,Q=120/A;return I*Q}updateRunningAverage(){this.recentOffsets.length!==0&&(this.establishedOffset=this.recentOffsets.reduce((A,B)=>A+B,0)/this.recentOffsets.length)}calculateConsistency(A,B){if(!this.hasPocket())return 0;const I=A/B;return I>=1?0:1-I*I}determineDirection(A){return Math.abs(A)<this.options.neutralDeadZone?"neutral":A<0?"push":"pull"}}function HW(g,A=CC,B){const I=$i(g);return new LI(B).subdivide(I,A)}function gI(g){return"mergedBeats"in g?g.mergedBeats:g.beats}function Oo(g,A){return"mergedBeats"in g?{...g,mergedBeats:A}:{...g,beats:A}}function JW(g,A,B){const I=gI(g);if(A<0||A>=I.length)throw new Error(`beatIndex ${A} is out of bounds (beats array length: ${I.length})`);const Q=I.map((E,i)=>{if(i!==A)return E;if(B===null){const{requiredKey:e,...a}=E;return a}return{...E,requiredKey:B}});return Oo(g,Q)}function xW(g,A){const B=gI(g),I=B.length;for(const i of A)if(i.beatIndex<0||i.beatIndex>=I)throw new Error(`beatIndex ${i.beatIndex} is out of bounds (beats array length: ${I})`);const Q=new Map;for(const{beatIndex:i,key:e}of A)Q.set(i,e);const E=B.map((i,e)=>{const a=Q.get(e);if(a===void 0)return i;if(a===null){const{requiredKey:C,...o}=i;return o}return{...i,requiredKey:a}});return Oo(g,E)}function qW(g){const A=gI(g),B=new Map;for(let I=0;I<A.length;I++){const Q=A[I];Q.requiredKey!==void 0&&B.set(I,Q.requiredKey)}return B}function LW(g){const B=gI(g).map(I=>{if(I.requiredKey===void 0)return I;const{requiredKey:Q,...E}=I;return E});return Oo(g,B)}function VW(g){return gI(g).some(B=>B.requiredKey!==void 0)}function vW(g){return gI(g).filter(B=>B.requiredKey!==void 0).length}function XW(g){const A=gI(g),B=new Set;for(const I of A)I.requiredKey!==void 0&&B.add(I.requiredKey);return Array.from(B).sort()}const vA=VA.for("SubdivisionPlaybackController");class KW{constructor(A,B,I={}){this.unifiedMap=A,this.audioContext=B,this.options={..._G,...I};const Q={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};this.subdivider=new LI(Q),this.state={isRunning:!1,isPaused:!1,currentTime:0,startTime:0,pauseTime:0,rafId:null,currentSubdivision:this.options.initialSubdivision,pendingSubdivision:null,pendingMeasureNumber:null,transitionTimestamp:null,scheduledBeats:new Map,thresholds:$E("medium")},this.subscribers=new Set,this.regenerateBeats(),vA.debug("SubdivisionPlaybackController initialized",{audioId:A.audioId,duration:A.duration,initialSubdivision:this.state.currentSubdivision,beatCount:A.beats.length})}get subdivision(){return this.state.currentSubdivision}get beatMap(){return this.unifiedMap}getOptions(){return{...this.options}}subscribe(A){return this.subscribers.add(A),()=>{this.subscribers.delete(A)}}setSubdivision(A){if(!Bi(A))throw new Error(`Invalid subdivision type: ${A}`);if(this.state.currentSubdivision===A)return;const B=this.state.currentSubdivision;switch(vA.debug("Subdivision change requested",{from:B,to:A,transitionMode:this.options.transitionMode}),this.options.transitionMode){case"immediate":this.applySubdivisionChange(A,B);break;case"next-downbeat":this.state.pendingSubdivision=A,this.state.pendingMeasureNumber=null,vA.debug("Subdivision change deferred until next downbeat",{pendingType:A,transitionMode:this.options.transitionMode});break;case"next-measure":{const Q=this.getCurrentBeat()?.measureNumber??null;this.state.pendingSubdivision=A,this.state.pendingMeasureNumber=Q,vA.debug("Subdivision change deferred until next measure",{pendingType:A,transitionMode:this.options.transitionMode,currentMeasureNumber:Q});break}default:this.applySubdivisionChange(A,B)}}setTransitionMode(A){if(!["immediate","next-downbeat","next-measure"].includes(A))throw new Error(`Invalid transition mode: ${A}`);const B=this.options.transitionMode;B!==A&&(vA.debug("Transition mode change requested",{from:B,to:A}),this.options={...this.options,transitionMode:A},A==="immediate"&&this.state.pendingSubdivision&&(vA.debug("Applying pending subdivision immediately due to mode switch",{pendingSubdivision:this.state.pendingSubdivision}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision)),vA.debug("Transition mode changed",{from:B,to:A}))}applySubdivisionChange(A,B){this.state.currentSubdivision=A,this.state.pendingSubdivision=null,this.state.pendingMeasureNumber=null;const I=this.state.transitionTimestamp;if(this.state.transitionTimestamp=null,this.regenerateBeats(I),this.options.onSubdivisionChange)try{this.options.onSubdivisionChange(B,A)}catch(Q){vA.error("Error in onSubdivisionChange callback",Q)}vA.debug("Subdivision changed",{from:B,to:A,transitionTimestamp:I})}regenerateBeats(A=null){const B={beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},I=this.subdivider.subdivide(this.unifiedMap,B),Q=new Map;if(A!==null)for(const[E,i]of this.state.scheduledBeats)i.beat.timestamp<A&&Q.set(E,i);this.state.scheduledBeats.clear();for(const[E,i]of Q)this.state.scheduledBeats.set(E,i);for(const E of I.beats){if(A!==null&&E.timestamp<A)continue;const i=this.getBeatKey(E);this.state.scheduledBeats.has(i)||this.state.scheduledBeats.set(i,{beat:E,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1})}vA.debug("Beats regenerated",{subdivision:this.state.currentSubdivision,beatCount:I.beats.length,preservedBeatCount:Q.size,totalScheduledBeats:this.state.scheduledBeats.size,fromTimestamp:A})}getBeatKey(A){return`${A.timestamp.toFixed(6)}-${A.subdivisionType}`}play(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate(),vA.debug("Playback started",{startTime:this.state.startTime,pauseTime:this.state.pauseTime}))}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.regenerateBeats(),vA.debug("Playback stopped")}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),vA.debug("Playback paused",{pauseTime:this.state.pauseTime}))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate(),vA.debug("Playback resumed",{startTime:this.state.startTime}))}seek(A){const B=Math.max(0,Math.min(A,this.unifiedMap.duration));this.state.pauseTime=B,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-B),this.regenerateBeats(),vA.debug("Seeked to time",{time:B})}getBeatsInRange(A,B){const I=[];for(const[,Q]of this.state.scheduledBeats){const E=Q.beat;E.timestamp>=A&&E.timestamp<=B&&I.push(E)}return I.sort((Q,E)=>Q.timestamp-E.timestamp),I}getUpcomingBeats(A){const B=this.getCurrentAudioTime(),I=[];for(const[,Q]of this.state.scheduledBeats){const E=Q.beat,i=E.timestamp-B;if(i>=0&&i<=this.options.anticipationTime&&(I.push(E),I.length>=A))break}return I.sort((Q,E)=>Q.timestamp-E.timestamp),I}getBeatAtTime(A){const B=this.options.timingTolerance;for(const[,I]of this.state.scheduledBeats)if(Math.abs(I.beat.timestamp-A)<=B)return I.beat;return null}getCurrentBeat(){const A=this.getCurrentAudioTime();let B=null;for(const[,I]of this.state.scheduledBeats)if(I.beat.timestamp<=A)B=I.beat;else break;return B}getNextBeat(){const A=this.getCurrentAudioTime(),B=Array.from(this.state.scheduledBeats.values()).map(I=>I.beat).sort((I,Q)=>I.timestamp-Q.timestamp);for(const I of B)if(I.timestamp>A)return I;return null}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let A=0;if(this.options.compensateOutputLatency){const B=this.audioContext.outputLatency??0,I=this.audioContext.baseLatency??0;A+=B+I}return A+=this.options.userOffsetMs/1e3,A}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const A=this.getCurrentAudioTime();this.checkPendingSubdivisionChange(A);for(const B of this.state.scheduledBeats.values()){const Q=B.beat.timestamp-A;!B.upcomingEmitted&&Q<=this.options.anticipationTime&&Q>0&&(this.emitEvent(B.beat,"upcoming",A,Q),B.upcomingEmitted=!0),!B.exactEmitted&&Math.abs(Q)<=this.options.timingTolerance&&(this.emitEvent(B.beat,"exact",A,Q),B.exactEmitted=!0),!B.passedEmitted&&Q<-this.options.timingTolerance&&(this.emitEvent(B.beat,"passed",A,Q),B.passedEmitted=!0)}}checkPendingSubdivisionChange(A){if(!this.state.pendingSubdivision)return;const B=this.getCurrentBeat();if(!B)return;let I=!1;if(this.options.transitionMode==="next-downbeat")I=B.isDownbeat;else if(this.options.transitionMode==="next-measure"){const Q=this.state.pendingMeasureNumber;Q!==null?I=B.measureNumber>Q&&Math.floor(B.beatInMeasure)===0:I=B.beatInMeasure===0}I&&(this.state.transitionTimestamp=B.timestamp,vA.debug("Applying pending subdivision change",{newSubdivision:this.state.pendingSubdivision,transitionMode:this.options.transitionMode,currentMeasureNumber:B.measureNumber,pendingMeasureNumber:this.state.pendingMeasureNumber,beatInMeasure:B.beatInMeasure,transitionTimestamp:this.state.transitionTimestamp}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision))}emitEvent(A,B,I,Q){const E={beat:A,currentSubdivision:this.state.currentSubdivision,audioTime:I,timeUntilBeat:Q,type:B};for(const i of this.subscribers)try{i(E)}catch(e){vA.error("SubdivisionPlaybackController callback error:",e)}}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.unifiedMap.duration}checkButtonPress(A,B){const I=B??this.state.thresholds,Q=Array.from(this.state.scheduledBeats.values()).map(o=>o.beat);let E=null,i=1/0;for(const o of Q){const t=A-o.timestamp,s=Math.abs(t);s<i&&(i=s,E=o)}if(!E)return{accuracy:"miss",offset:A,matchedBeat:null,absoluteOffset:A,keyMatch:!0,pressedKey:void 0,requiredKey:void 0};const e=A-E.timestamp,a=Math.abs(e);let C;return a<=I.perfect?C="perfect":a<=I.great?C="great":a<=I.good?C="good":a<=I.ok?C="ok":C="miss",{accuracy:C,offset:e,matchedBeat:E,absoluteOffset:a,keyMatch:!0,pressedKey:void 0,requiredKey:void 0}}setBeatMap(A){this.unifiedMap=A,this.regenerateBeats(),vA.debug("Beat map updated",{audioId:A.audioId,beatCount:A.beats.length})}forceUpdate(){this.update()}getSubdividedBeatMap(){const A=[];for(const[,Q]of this.state.scheduledBeats)A.push(Q.beat);A.sort((Q,E)=>Q.timestamp-E.timestamp);const B=new Set(A.map(Q=>Q.subdivisionType)),I=A.filter(Q=>Q.isDetected).length;return{audioId:this.unifiedMap.audioId,duration:this.unifiedMap.duration,beats:A,detectedBeatIndices:[],subdivisionConfig:{beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},downbeatConfig:this.unifiedMap.downbeatConfig,subdivisionMetadata:{originalBeatCount:I,subdividedBeatCount:A.length,averageDensityMultiplier:A.length/Math.max(1,this.unifiedMap.beats.length),explicitBeatCount:0,subdivisionsUsed:Array.from(B),hasMultipleTempos:!1,maxDensity:Math.max(...Array.from(B).map(Q=>Q==="eighth"?2:Q==="sixteenth"?4:Q==="triplet8"||Q==="triplet4"?3:1))}}}dispose(){this.stop(),this.subscribers.clear(),this.state.scheduledBeats.clear(),vA.debug("SubdivisionPlaybackController disposed")}}const zW=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],TW={stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5};class jo{constructor(A){this.config={level_thresholds:A?.level_thresholds||zW,xp_per_second:A?.xp_per_second??1,xp_per_track_completion:A?.xp_per_track_completion??50,activity_bonuses:{...TW,...A?.activity_bonuses},track_mastery_threshold:A?.track_mastery_threshold??10,mastery_bonus_xp:A?.mastery_bonus_xp??100}}calculateSessionXP(A,B){let I=A.duration_seconds*this.config.xp_per_second;if(A.activity_type&&A.activity_type in this.config.activity_bonuses){const Q=this.config.activity_bonuses[A.activity_type];I*=Q}return A.environmental_context&&(I=this.applyEnvironmentalBonus(I,A.environmental_context)),A.gaming_context&&(I=this.applyGamingBonus(I,A.gaming_context)),A.rhythm_game_context&&(I=this.applyRhythmGameBonus(I,A.rhythm_game_context)),B&&A.duration_seconds>=B.duration*.95&&(I+=this.config.xp_per_track_completion),Math.floor(I)}applyEnvironmentalBonus(A,B){let I=1;return B.weather?.isNight&&(I*=this.config.activity_bonuses.night_time),B.weather&&(B.weather.weatherType==="Thunderstorm"&&(I*=this.config.activity_bonuses.extreme_weather),(B.weather.weatherType==="Rain"||B.weather.weatherType==="Snow")&&(I*=this.config.activity_bonuses.extreme_weather)),B.geolocation?.altitude&&B.geolocation.altitude>=2e3&&(I*=this.config.activity_bonuses.high_altitude),A*I}applyGamingBonus(A,B){if(!B.isActivelyGaming)return A;let I=1;if(I+=.25,B.currentGame){if(B.currentGame.genre){const Q=B.currentGame.genre;(Q.includes("Action")||Q.includes("FPS"))&&(I+=.15),Q.includes("RPG")&&(I+=.2),Q.includes("Strategy")&&(I+=.1)}if(B.currentGame.partySize&&B.currentGame.partySize>1&&(I+=.15),B.currentGame.sessionDuration){const Q=B.currentGame.sessionDuration/60;Q>=1&&(I+=Math.min(.2,Q*.05))}}return A*I}applyRhythmGameBonus(A,B){if(!B.isActive)return A;let I=this.config.activity_bonuses.rhythm_game_base;if(B.currentCombo>0){const Q=B.currentCombo/B.maxComboCap;I+=Q*this.config.activity_bonuses.rhythm_game_combo}if(B.grooveHotness>0){const Q=B.grooveHotness/100;I+=Q*this.config.activity_bonuses.rhythm_game_groove}return A*I}calculateTotalModifier(A,B,I){let Q=1;return A&&(Q*=this.calculateEnvironmentalModifier(A)),B&&B.isActivelyGaming&&(Q*=this.calculateGamingModifier(B)),I&&I.isActive&&(Q*=this.calculateRhythmGameModifier(I)),Math.min(Q,3)}calculateEnvironmentalModifier(A){let B=1;return A.weather?.weatherType==="Thunderstorm"&&(B*=this.config.activity_bonuses.extreme_weather),A.weather?.isNight&&(B*=this.config.activity_bonuses.night_time),A.geolocation?.altitude&&A.geolocation.altitude>=2e3&&(B*=this.config.activity_bonuses.high_altitude),Math.min(B,3)}calculateGamingModifier(A){let B=1;if(B+=.25,A.currentGame){if(A.currentGame.genre){const I=A.currentGame.genre;(I.includes("Action")||I.includes("FPS"))&&(B+=.15),I.includes("RPG")&&(B+=.2),I.includes("Strategy")&&(B+=.1)}if(A.currentGame.partySize&&A.currentGame.partySize>1&&(B+=.15),A.currentGame.sessionDuration){const I=A.currentGame.sessionDuration/60;I>=1&&(B+=Math.min(.2,I*.05))}}return Math.min(B,1.75)}calculateRhythmGameModifier(A){let B=this.config.activity_bonuses.rhythm_game_base;if(A.currentCombo>0){const I=A.currentCombo/A.maxComboCap;B+=I*this.config.activity_bonuses.rhythm_game_combo}if(A.grooveHotness>0){const I=A.grooveHotness/100;B+=I*this.config.activity_bonuses.rhythm_game_groove}return Math.min(B,2.25)}getXPThresholdForLevel(A){if(A<1||A>20)throw new Error("Level must be between 1 and 20");return this.config.level_thresholds[A-1]}getXPToNextLevel(A){if(A<1||A>19)throw new Error("Current level must be between 1 and 19");const B=this.config.level_thresholds[A-1];return this.config.level_thresholds[A]-B}getLevelFromXP(A){for(let B=20;B>=1;B--)if(A>=this.config.level_thresholds[B-1])return B;return 1}isTrackMastered(A){return A>=this.config.track_mastery_threshold}getMasteryBonusXP(){return this.config.mastery_bonus_xp}getConfig(){return{...this.config}}}class OW{constructor(A){this.activeSessions=new Map,this.sessionHistory=[],this.sessionCounter=0,this.xpCalculator=A||new jo}startSession(A,B,I){const Q=`session-${this.sessionCounter++}-${A}-${Date.now()}`;return this.activeSessions.set(Q,{track_uuid:A,start_time:Date.now(),track:B,environmental_context:I?.environmental_context,gaming_context:I?.gaming_context}),Q}endSession(A,B,I){const Q=this.activeSessions.get(A);if(!Q)return null;const E=Date.now(),i=B||Math.max(1,Math.ceil((E-Q.start_time)/1e3)),e={track_uuid:Q.track_uuid,start_time:Q.start_time,end_time:E,duration_seconds:Math.round(i),base_xp_earned:0,bonus_xp:0,activity_type:I,environmental_context:Q.environmental_context,gaming_context:Q.gaming_context,total_xp_earned:0};return e.base_xp_earned=Math.floor(e.duration_seconds*this.xpCalculator.getConfig().xp_per_second),e.total_xp_earned=this.xpCalculator.calculateSessionXP(e,Q.track),e.bonus_xp=e.total_xp_earned-e.base_xp_earned,this.sessionHistory.push(e),this.activeSessions.delete(A),e}getActiveSession(A){return this.activeSessions.get(A)||null}getActiveSessionDuration(A){const B=this.activeSessions.get(A);return B?(Date.now()-B.start_time)/1e3:null}updateSessionContext(A,B){const I=this.activeSessions.get(A);return I?(B.environmental_context&&(I.environmental_context=B.environmental_context),B.gaming_context&&(I.gaming_context=B.gaming_context),!0):!1}getSessionHistory(){return[...this.sessionHistory]}getSessionsForTrack(A){return this.sessionHistory.filter(B=>B.track_uuid===A)}getTotalListeningTime(){return this.sessionHistory.reduce((A,B)=>A+B.duration_seconds,0)}getTotalXPEarned(){return this.sessionHistory.reduce((A,B)=>A+B.total_xp_earned,0)}getTrackListeningTime(A){return this.getSessionsForTrack(A).reduce((B,I)=>B+I.duration_seconds,0)}getTrackListenCount(A){return this.getSessionsForTrack(A).length}isTrackMastered(A,B=10){return this.getTrackListenCount(A)>=B}getSessionsInRange(A,B){return this.sessionHistory.filter(I=>I.start_time>=A&&I.end_time<=B)}getAverageSessionLength(){return this.sessionHistory.length===0?0:this.getTotalListeningTime()/this.sessionHistory.length}getLongestSession(){return this.sessionHistory.length===0?null:this.sessionHistory.reduce((A,B)=>B.duration_seconds>A.duration_seconds?B:A)}getTrackXPTotal(A){return this.getSessionsForTrack(A).reduce((B,I)=>B+I.total_xp_earned,0)}clearTrackSessions(A){const B=this.sessionHistory.length;return this.sessionHistory=this.sessionHistory.filter(I=>I.track_uuid!==A),B-this.sessionHistory.length}clearHistory(){this.sessionHistory=[]}clearActiveSessions(){this.activeSessions.clear()}getActiveSessionCount(){return this.activeSessions.size}getActiveSessionIds(){return Array.from(this.activeSessions.keys())}}const Hd=[4,8,12,16,19];class $B{static setUncappedConfig(A){this.uncappedConfig=A}static getUncappedConfig(){return this.uncappedConfig}static setStatManager(A){this.statManager=A}static getStatManager(){return this.statManager}static processLevelUp(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=E?1/0:20;if(B<1||B>i)throw new Error(`Level must be between 1 and ${i}`);const e=ag[A.class];if(!e)throw new Error(`Unknown class: ${A.class}`);const a=this.calculateHPIncrease(e.hit_die,A.ability_modifiers.CON,I),C=A.hp.max+a,o=this.getProficiencyBonus(B,E),t=o-A.proficiency_bonus,s={newLevel:B,hitPointIncrease:a,newHitPointsTotal:C,proficiencyBonusIncrease:t,newProficiencyBonus:o},n=E||Hd.includes(B);if(this.statManager&&n){const c=this.statManager.processLevelUp(A,B);c&&c.increases.length>0&&(s.abilityScoreIncreases=c.increases.map(G=>({ability:G.ability,increase:G.delta})),c.increases.length===1&&(s.abilityScoreIncrease={ability:c.increases[0].ability,increase:c.increases[0].delta}))}else Hd.includes(B)&&(s.abilityScoreIncrease={ability:"STR",increase:2});if(this.isSpellcaster(A.class)){const c=this.calculateSpellSlots(B),G={};for(const[D,h]of Object.entries(c))G[Number(D)]={total:h,used:0};s.newSpellSlots=G}const r=this.getClassFeaturesForLevel(A,A.class,B);if(r.length>0){s.classFeatures=r.map(c=>c.id),s.featureEffects=[];for(const c of r)if(c.effects&&c.effects.length>0){const G={...A,level:B},D=vQ.applyFeatureEffects(G,c);D.applied&&s.featureEffects.push({featureId:c.id,featureName:c.name,effectsApplied:D.count})}}return s}static getClassFeaturesForLevel(A,B,I){const Q=KA.getInstance(),E=Q.getFeaturesForLevel(B,I),i={...A,level:I},e=[];for(const a of E){const C=Q.validatePrerequisites(a,i);C.valid?e.push(a):(console.warn(`Feature "${a.name}" (level ${a.level}) failed prerequisite validation at level ${I}:`,C.errors),a.source==="default"&&e.push(a))}return e}static applyLevelUp(A,B){const I={...A};I.level=B.newLevel,I.hp.max=B.newHitPointsTotal,I.hp.current=Math.min(I.hp.current+B.hitPointIncrease,I.hp.max),I.proficiency_bonus=B.newProficiencyBonus;const Q=I.gameMode==="uncapped"?1/0:20;if(B.abilityScoreIncreases&&B.abilityScoreIncreases.length>0)for(const E of B.abilityScoreIncreases){const i=E.ability;I.ability_scores[i]=Math.min(Q,I.ability_scores[i]+E.increase);const e=I.ability_scores[i];I.ability_modifiers[i]=Math.floor((e-10)/2)}else if(B.abilityScoreIncrease){const E=B.abilityScoreIncrease.ability;I.ability_scores[E]=Math.min(Q,I.ability_scores[E]+B.abilityScoreIncrease.increase);const i=I.ability_scores[E];I.ability_modifiers[E]=Math.floor((i-10)/2)}return B.newSpellSlots&&I.spells&&(I.spells.spell_slots=B.newSpellSlots),B.classFeatures&&(I.class_features=[...new Set([...I.class_features,...B.classFeatures])]),this.reapplyEquipmentEffects(I),I}static calculateHPIncrease(A,B,I){let Q;return I?Q=new qB(I).randomInt(1,A+1):Q=Math.floor(Math.random()*A)+1,Math.max(1,Q+B)}static isSpellcaster(A){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(A)}static calculateSpellSlots(A){const B={1:0};for(let I=1;I<=9;I++)B[I]=this.getSpellSlotCount(A,I);return B}static getSpellSlotCount(A,B){return B>Math.ceil(A/2)||A<1?0:A<3?B===1?2:0:A<5?B===1?3:B===2?2:0:A<7?B===1?4:B===2?3:0:A<9?B===1||B===2?4:B===3?2:0:Math.min(5,Math.ceil(A/2))}static getXPThreshold(A,B=!1){if(A<1)throw new Error("Level must be at least 1");if(!B){if(A>20)throw new Error("Standard mode only supports levels 1-20");return HQ[A]}if(this.uncappedConfig?.xpFormula)return this.uncappedConfig.xpFormula(A);if(A<=20)return HQ[A];let I=355e3;for(let Q=21;Q<=A;Q++)I+=(Q-1)*Q*500;return I}static getProficiencyBonus(A,B=!1){if(!B){if(A<1||A>20)throw new Error("Standard proficiency bonus only defined for levels 1-20");return SC[A]}return this.uncappedConfig?.proficiencyBonusFormula?this.uncappedConfig.proficiencyBonusFormula(A):A<=4?2:A<=8?3:A<=12?4:A<=16?5:2+Math.floor((A-1)/4)}static calculateLevel(A,B=!1){if(!B){for(let Q=20;Q>=1;Q--)if(A>=this.getXPThreshold(Q,!1))return Q;return 1}let I=1;for(;A>=this.getXPThreshold(I+1,!0);)I++;return I}static getXPToNextLevel(A,B=!1){if(!B&&A>=20)return 0;const I=this.getXPThreshold(A,B);return this.getXPThreshold(A+1,B)-I}static getProgressPercentage(A,B,I=!1){if(!I&&A>=20)return 100;const Q=this.getXPThreshold(A,I),E=this.getXPThreshold(A+1,I),i=B-Q,e=E-Q;return Math.floor(i/e*100)}static processLevelUpWithoutStats(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=E?1/0:20;if(B<1||B>i)throw new Error(`Level must be between 1 and ${i}`);const e=ag[A.class];if(!e)throw new Error(`Unknown class: ${A.class}`);const a=this.calculateHPIncrease(e.hit_die,A.ability_modifiers.CON,I),C=A.hp.max+a,o=this.getProficiencyBonus(B,E),t=o-A.proficiency_bonus,s={newLevel:B,hitPointIncrease:a,newHitPointsTotal:C,proficiencyBonusIncrease:t,newProficiencyBonus:o};if(this.isSpellcaster(A.class)){const r=this.calculateSpellSlots(B),c={};for(const[G,D]of Object.entries(r))c[Number(G)]={total:D,used:0};s.newSpellSlots=c}const n=this.getClassFeaturesForLevel(A,A.class,B);if(n.length>0){s.classFeatures=n.map(r=>r.id),s.featureEffects=[];for(const r of n)if(r.effects&&r.effects.length>0){const c={...A,level:B},G=vQ.applyFeatureEffects(c,r);G.applied&&s.featureEffects.push({featureId:r.id,featureName:r.name,effectsApplied:G.count})}}return s}static applyAutomaticBenefitsOnly(A,B){const I={...A};return I.level=B.newLevel,I.hp.max=B.newHitPointsTotal,I.hp.current=Math.min(I.hp.current+B.hitPointIncrease,I.hp.max),I.proficiency_bonus=B.newProficiencyBonus,B.newSpellSlots&&I.spells&&(I.spells.spell_slots=B.newSpellSlots),B.classFeatures&&(I.class_features=[...new Set([...I.class_features,...B.classFeatures])]),this.reapplyEquipmentEffects(I),I}static applyStatIncreasesOnly(A,B){const I={...A};I.ability_scores={...I.ability_scores},I.ability_modifiers={...I.ability_modifiers};const E=(I.gameMode||"standard")==="uncapped"?1/0:20;for(const i of B){const e=i.ability;I.ability_scores[e]=Math.min(E,I.ability_scores[e]+i.amount),I.ability_modifiers[e]=Math.floor((I.ability_scores[e]-10)/2)}return I}static reapplyEquipmentEffects(A){A.equipment&&PA.reapplyEquipmentEffects(A)}}class Jd{constructor(){this.name="D&D 5e Standard"}selectIncreases(A,B,I){if(I?.forcedAbilities&&I.forcedAbilities.length>0){const Q=[];for(const E of I.forcedAbilities)A.ability_scores[E]>=20||Q.push({ability:E,amount:B/I.forcedAbilities.length});return Q}return[]}requiresManualInput(A){return!A?.forcedAbilities||A.forcedAbilities.length===0}}class xd{constructor(){this.name="D&D 5e Smart"}selectIncreases(A,B,I){const Q=new Set(I?.excludedAbilities||[]),E=["STR","DEX","CON","INT","WIS","CHA"].filter(C=>!Q.has(C)&&A.ability_scores[C]<20);if(E.length===0)return[];const e=ag[A.class]?.primary_ability||"STR";if(!Q.has(e)&&A.ability_scores[e]<18&&E.includes(e)){if(B===2&&this.shouldSplit(A,e)){const C=this.findLowestStat(A,E,e);return[{ability:e,amount:1},{ability:C,amount:1}]}return[{ability:e,amount:B}]}if(B===2&&E.length>=2){const C=this.findLowestStat(A,E),o=this.findLowestStat(A,E.filter(t=>t!==C));return[{ability:C,amount:1},{ability:o,amount:1}]}return[{ability:this.findLowestStat(A,E),amount:B}]}requiresManualInput(){return!1}shouldSplit(A,B){const I=A.ability_scores[B];if(I<15)return!1;const Q=Object.values(A.ability_scores);return Math.min(...Q)<I-4}findLowestStat(A,B,I){let Q=B[0],E=A.ability_scores[Q];for(const i of B){if(i===I)continue;const e=A.ability_scores[i];e<E&&(Q=i,E=e)}return Q}}class qd{constructor(){this.name="Balanced"}selectIncreases(A,B,I){const Q=new Set(I?.excludedAbilities||[]),E=["STR","DEX","CON","INT","WIS","CHA"].filter(a=>!Q.has(a)).filter(a=>A.ability_scores[a]<20).sort((a,C)=>A.ability_scores[a]-A.ability_scores[C]);if(E.length===0)return[];if(I?.forcedAbilities&&I.forcedAbilities.length>=2)return[{ability:I.forcedAbilities[0],amount:1},{ability:I.forcedAbilities[1],amount:1}];const i=[];let e=B;for(let a=0;a<E.length&&e>0;a++)i.push({ability:E[a],amount:1}),e--;return i}requiresManualInput(){return!1}}class Ld{constructor(){this.name="Primary Only"}selectIncreases(A,B,I){const E=ag[A.class]?.primary_ability||"STR";return I?.excludedAbilities?.includes(E)?[{ability:this.findLowestStat(A,I?.excludedAbilities),amount:B}]:A.ability_scores[E]>=20?[{ability:this.findLowestStat(A,[E]),amount:B}]:[{ability:E,amount:B}]}requiresManualInput(){return!1}findLowestStat(A,B){const I=["STR","DEX","CON","INT","WIS","CHA"].filter(i=>!B?.includes(i)).filter(i=>A.ability_scores[i]<20);if(I.length===0)return"STR";let Q=I[0],E=A.ability_scores[Q];for(const i of I){const e=A.ability_scores[i];e<E&&(Q=i,E=e)}return Q}}class Vd{constructor(){this.name="Random"}selectIncreases(A,B,I){const Q=["STR","DEX","CON","INT","WIS","CHA"].filter(i=>!I?.excludedAbilities?.includes(i)).filter(i=>A.ability_scores[i]<20);if(Q.length===0)return[];const E=[...Q].sort(()=>Math.random()-.5);return B===2&&E.length>=2?[{ability:E[0],amount:1},{ability:E[1],amount:1}]:[{ability:E[0],amount:B}]}requiresManualInput(){return!1}}class vd{constructor(){this.name="Manual"}selectIncreases(A,B,I){return[]}requiresManualInput(){return!0}}class jW{constructor(A,B){this.fn=A,this.name=B||"Custom Function"}selectIncreases(A,B,I){return this.fn(A,B,I)}requiresManualInput(){return!1}}function Po(g){if(typeof g!="string"&&typeof g!="function")return g;if(typeof g=="function")return new jW(g);switch(g){case"dnD5e":return new Jd;case"dnD5e_smart":return new xd;case"balanced":return new qd;case"primary_only":return new Ld;case"random":return new Vd;case"manual":return new vd;default:const A=g;throw new Error(`Unknown strategy type: ${A}`)}}const Xd=[4,8,12,16,19],Kd={strategy:"dnD5e",autoApply:!0};class zd{constructor(A){this.config=this.mergeWithDefaults(A),this.strategy=Po(this.config.strategy)}increaseStats(A,B,I="manual"){const Q={...A},E=[],i=[];Q.ability_scores={...Q.ability_scores},Q.ability_modifiers={...Q.ability_modifiers};const a=(Q.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;for(const{ability:C,amount:o}of B){const t=Q.ability_scores[C],s=t+o,n=Math.min(s,a),r=n-t;r>0?(Q.ability_scores[C]=n,Q.ability_modifiers[C]=Math.floor((n-10)/2),E.push({ability:C,oldValue:t,newValue:n,delta:r})):s>a&&i.push({ability:C,attemptedValue:s,cappedAt:a})}return{character:Q,increases:E,capped:i,source:I,timestamp:Date.now()}}decreaseStats(A,B,I="event"){const Q=B.map(({ability:E,amount:i})=>({ability:E,amount:-i}));return this.increaseStats(A,Q,I)}setStat(A,B,I,Q="manual"){const E=A.ability_scores[B],i=I-E;return this.increaseStats(A,[{ability:B,amount:i}],Q)}processLevelUp(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=this.config.statIncreaseLevels?.length?this.config.statIncreaseLevels:Xd;if(!(E||i.includes(B)))return null;const C=this.strategy.selectIncreases(A,2,I);return C.length===0?null:this.increaseStats(A,C,"level_up")}updateConfig(A){const B=this.config.strategy;this.config=this.mergeWithDefaults(A),A.strategy&&A.strategy!==B&&(this.strategy=Po(this.config.strategy))}getConfig(){return this.config}canIncrease(A,B,I=1){const Q=A.ability_scores[B],i=(A.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;return Q+I<=i}getStatCap(A,B){return(A.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20}validateDnD5eStatSelection(A,B,I=2){const Q=B.reduce((o,t)=>o+t.amount,0);if(Q!==I)return{error:`Total stat increase must be ${I}, got ${Q}`,reason:"invalid_amount",allowedPatterns:[`+${I} to one ability`,"+1 to two abilities"]};const E=[{count:1,amount:I},{count:2,amount:1}];if(!E.some(o=>B.length===o.count&&B.every(t=>t.amount===o.amount)))return{error:"Invalid pattern: must be +2 to one ability or +1 to two abilities",reason:"wrong_pattern",allowedPatterns:E.map(o=>o.count===1?`+${o.amount} to one ability`:"+1 to two abilities")};const e=this.getStatCap(A,"STR");for(const o of B){const t=A.ability_scores[o.ability];if(t+o.amount>e&&e!==1/0)return{error:`${o.ability} would exceed stat cap of ${e}`,reason:"exceeds_cap",allowedPatterns:[`Choose different abilities or use ${e-t} points`]}}const a=["STR","DEX","CON","INT","WIS","CHA"];for(const o of B)if(!a.includes(o.ability))return{error:`Invalid ability: ${o.ability}`,reason:"invalid_ability",allowedPatterns:a};const C=new Set;for(const o of B){if(C.has(o.ability))return{error:`Duplicate ability: ${o.ability}`,reason:"duplicate_ability",allowedPatterns:["Each ability can only be increased once"]};C.add(o.ability)}return{valid:!0}}mergeWithDefaults(A){return{maxStatCap:A?.maxStatCap??20,strategy:A?.strategy??Kd.strategy,autoApply:A?.autoApply??Kd.autoApply,statIncreaseLevels:A?.statIncreaseLevels??Xd}}}const fG=class fG{static getPlaysThreshold(A){const B=this.customThresholds.get(A);return B?.playsThreshold!==void 0&&B.playsThreshold!==null?B.playsThreshold:Math.floor(rh*Math.pow(cC,A))}static getXPThreshold(A){const B=this.customThresholds.get(A);return B?.xpThreshold!==void 0&&B.xpThreshold!==null?B.xpThreshold:Math.floor(ch*Math.pow(cC,A))}static setCustomThresholds(A,B){this.customThresholds.set(A,{...B})}static clearCustomThresholds(A){A!==void 0?this.customThresholds.delete(A):this.customThresholds.clear()}static hasCustomThresholds(A){return this.customThresholds.has(A)}static getCustomThresholds(A){return this.customThresholds.get(A)}static isMastered(A,B,I){const Q=this.getPlaysThreshold(I),E=this.getXPThreshold(I);return A>=Q&&B>=E}static canPrestige(A,B,I){return A>=kQ?!1:this.isMastered(B,I,A)}static isJustMastered(A,B,I,Q,E){const i=this.isMastered(A,I,E),e=this.isMastered(B,Q,E);return!i&&e}static calculateMasteryBonus(A){return A?$h:0}static getPrestigeInfo(A,B,I){const Q=this.getPlaysThreshold(A),E=this.getXPThreshold(A),i=this.isMastered(B,I,A),e=A>=kQ;return{prestigeLevel:A,currentPlays:B,currentXP:I,playsThreshold:Q,xpThreshold:E,playsProgress:Math.min(1,B/Q),xpProgress:Math.min(1,I/E),isMastered:i,canPrestige:i&&!e,isMaxPrestige:e}}static toRomanNumeral(A){return nh[A]}static getNextPrestigeLevel(A){return A>=kQ?null:Gh(A+1)}static createSuccessResult(A,B){const I=this.toRomanNumeral(B);return{success:!0,newPrestigeLevel:B,previousPrestigeLevel:A,message:`Successfully prestiged to level ${I}! New mastery requirements: ${this.getPlaysThreshold(B)} plays, ${this.getXPThreshold(B).toLocaleString()} XP`}}static createFailureResult(A,B){return{success:!1,newPrestigeLevel:B,previousPrestigeLevel:B,message:`Prestige failed: ${A}`}}static getAllThresholds(){const A=[];for(let B=0;B<=kQ;B++){const I=B;A.push({level:I,plays:this.getPlaysThreshold(I),xp:this.getXPThreshold(I)})}return A}};fG.customThresholds=new Map;let jA=fG;class PW{constructor(A){this.xpCalculator=new jo,this.statManager=A,this.statManager&&$B.setStatManager(this.statManager)}addXP(A,B,I="custom"){const Q={...A,xp:{...A.xp}};if(Q.xp.current+=B,!this.statManager){const t=(Q.gameMode||"standard")==="standard"?"dnD5e":"dnD5e_smart";this.statManager=new zd({strategy:t}),$B.setStatManager(this.statManager)}let E=!1,i;const e=[],a=Q.gameMode==="uncapped",C=$B.calculateLevel(Q.xp.current,a);if(C>Q.level){E=!0,i=C;for(let o=Q.level+1;o<=C;o++)if(this.isStatIncreaseLevel(o,a)&&this.statManager.getConfig().strategy==="dnD5e"){Q.pendingStatIncreases=(Q.pendingStatIncreases||0)+1;const n=$B.processLevelUpWithoutStats(Q,o),r=$B.applyAutomaticBenefitsOnly(Q,n),c={fromLevel:o-1,toLevel:o,hpIncrease:n.hitPointIncrease,newMaxHP:n.newHitPointsTotal,proficiencyIncrease:n.proficiencyBonusIncrease,newProficiency:n.newProficiencyBonus,statIncreases:void 0,featuresGained:n.classFeatures,newSpellSlots:n.newSpellSlots};e.push(c),Q.level=o,Q.hp=r.hp,Q.proficiency_bonus=r.proficiency_bonus,Q.class_features=r.class_features,r.spells&&(Q.spells=r.spells)}else{const n={...Q.ability_scores},r=[...Q.class_features],c=$B.processLevelUp(Q,o),G=$B.applyLevelUp(Q,c),D={fromLevel:o-1,toLevel:o,hpIncrease:c.hitPointIncrease,newMaxHP:c.newHitPointsTotal,proficiencyIncrease:c.proficiencyBonusIncrease,newProficiency:c.newProficiencyBonus,statIncreases:c.abilityScoreIncreases?.map(h=>({ability:h.ability,oldValue:n[h.ability],newValue:n[h.ability]+h.increase,delta:h.increase})),featuresGained:c.classFeatures?.filter(h=>!r.includes(h)),newSpellSlots:c.newSpellSlots};e.push(D),Q.level=G.level,Q.hp=G.hp,Q.ability_scores=G.ability_scores,Q.ability_modifiers=G.ability_modifiers,Q.proficiency_bonus=G.proficiency_bonus,Q.class_features=G.class_features,G.spells&&(Q.spells=G.spells)}}return!a&&Q.level>=20?Q.xp.next_level=0:Q.xp.next_level=$B.getXPThreshold(Q.level+1,a),{character:Q,xpEarned:B,leveledUp:E,newLevel:i,levelUpDetails:e.length>0?e:void 0}}addRhythmXP(A,B,I="rhythm_game"){const Q=this.addXP(A,B.finalXP,I);return{character:Q.character,xpEarned:Q.xpEarned,leveledUp:Q.leveledUp,newLevel:Q.newLevel,levelUpDetails:Q.levelUpDetails}}isStatIncreaseLevel(A,B){return B?!0:[4,8,12,16,19].includes(A)}updateCharacterFromSession(A,B,I,Q){const E=Q?.previousListenCount??0,i=Q?.previousXP??0,e=Q?.prestigeLevel??A.prestige_level??0,a=this.xpCalculator.calculateSessionXP(B,I);let C=!1,o=0;if(I){const n=E+1,r=i+a;jA.isJustMastered(E,n,i,r,e)&&(C=!0,o=jA.calculateMasteryBonus(!0))}const t=a+o;return{...this.addXP(A,t,"listening"),masteredTrack:C,masteryBonusXP:o}}applyPendingStatIncrease(A,B,I){const Q=A.pendingStatIncreases||0;if(Q===0)throw new Error("No pending stat increases to apply");let E;if(I&&I.length>0){if(I.length>1)throw new Error("Only one secondary stat allowed for +1/+1 distribution");E=[{ability:B,amount:1},{ability:I[0],amount:1}]}else E=[{ability:B,amount:2}];const i=this.statManager.validateDnD5eStatSelection(A,E);if("error"in i)throw new Error(i.error);const e=$B.applyStatIncreasesOnly(A,E);e.pendingStatIncreases=Q-1,e.pendingStatIncreases===0&&delete e.pendingStatIncreases;const a=E.map(C=>({ability:C.ability,oldValue:A.ability_scores[C.ability],newValue:e.ability_scores[C.ability],delta:C.amount}));return{character:e,statIncreases:a,remainingPending:e.pendingStatIncreases||0,timestamp:Date.now()}}hasPendingStatIncreases(A){return!!(A.pendingStatIncreases&&A.pendingStatIncreases>0)}getPendingStatIncreaseCount(A){return A.pendingStatIncreases||0}resetCharacterForPrestige(A,B,I,Q,E){const i=A.prestige_level??0;if(i>=10)return jA.createFailureResult("Already at maximum prestige level",i);const e=B.getTrackListenCount(I),a=B.getTrackXPTotal(I);if(!jA.canPrestige(i,e,a)){const n=jA.getPlaysThreshold(i),r=jA.getXPThreshold(i);return e<n&&a<r?jA.createFailureResult(`Need ${n-e} more plays and ${(r-a).toLocaleString()} more XP to prestige`,i):e<n?jA.createFailureResult(`Need ${n-e} more plays to prestige (XP requirement met)`,i):jA.createFailureResult(`Need ${(r-a).toLocaleString()} more XP to prestige (plays requirement met)`,i)}const C=A.equipment?{weapons:[...A.equipment.weapons],armor:[...A.equipment.armor],items:[...A.equipment.items],totalWeight:A.equipment.totalWeight,equippedWeight:A.equipment.equippedWeight}:null;B.clearTrackSessions(I);const o=jA.getNextPrestigeLevel(i);if(o===null)return jA.createFailureResult("Cannot prestige beyond maximum level",i);const t=qi.generate(A.seed,Q,E,{level:1,gameMode:A.gameMode,forceName:A.name});return C&&(t.equipment=C),t.prestige_level=o,{...jA.createSuccessResult(i,o),character:t}}canPrestige(A,B,I){const Q=A.prestige_level??0,E=B.getTrackListenCount(I),i=B.getTrackXPTotal(I);return jA.canPrestige(Q,E,i)}getPrestigeInfo(A,B,I){const Q=A.prestige_level??0,E=B.getTrackListenCount(I),i=B.getTrackXPTotal(I);return jA.getPrestigeInfo(Q,E,i)}}class _W{constructor(A){this.sessionTotals=null,this.sessionStartTime=null,this.config=di(A)}calculateButtonPressXP(A,B){const I=B?.comboLength??0,Q=B?.grooveHotness,E=this.getBaseXP(A),i=E*this.config.xpRatio,e=this.config.combo.enabled?this.getComboMultiplier(I):1;let a=0;this.config.groove.perHitMultiplier&&Q!==void 0&&Q>0&&(a=Q/100*this.config.groove.perHitScale);const C=Math.min(e+a,this.config.maxMultiplier),o=E*C,t=i*C,s=Math.max(0,t);return{scorePoints:E,baseXP:i,comboMultiplier:e,grooveMultiplier:a,totalMultiplier:C,finalScore:o,finalXP:s,breakdown:{accuracy:A,comboLength:I,grooveHotness:Q}}}calculateComboEndBonus(A){if(!this.config.combo.endBonus.enabled||A<=0)return{comboLength:A,bonusScore:0,bonusXP:0};const B=this.config.combo.endBonus.formula?this.config.combo.endBonus.formula(A):A*2,I=B*this.config.xpRatio;return{comboLength:A,bonusScore:B,bonusXP:I}}calculateGrooveEndBonus(A){if(!this.config.groove.endBonus.enabled)return{bonusScore:0,bonusXP:0};if(A.maxStreak<2)return{bonusScore:0,bonusXP:0};const{maxStreakWeight:I,avgHotnessWeight:Q,durationWeight:E}=this.config.groove.endBonus,i=A.maxStreak*I,e=A.avgHotness*Q,a=A.duration*E,C=i+e+a,o=C*this.config.xpRatio;return{bonusScore:C,bonusXP:o}}getBaseXP(A){return this.config.baseXP[A]}getComboMultiplier(A){if(!this.config.combo.enabled)return 1;const B=this.config.combo.formula?this.config.combo.formula(A):this.defaultComboFormula(A);return Math.min(B,this.config.combo.cap)}defaultComboFormula(A){return 1+A/25}startSession(){this.sessionTotals=this.createEmptyTotals(),this.sessionStartTime=Date.now()}recordHit(A,B){this.sessionTotals||this.startSession();const I=this.calculateButtonPressXP(A,B);this.sessionTotals.totalScore+=I.finalScore,this.sessionTotals.totalXP+=I.finalXP;const Q=B?.comboLength??0;return Q>this.sessionTotals.maxCombo&&(this.sessionTotals.maxCombo=Q),this.sessionTotals.accuracyDistribution[A]++,this.recalculateAccuracyPercentage(),I}getSessionTotals(){return!this.sessionTotals||!this.sessionStartTime?null:{...this.sessionTotals,duration:(Date.now()-this.sessionStartTime)/1e3}}endSession(){const A=this.getSessionTotals();return this.sessionTotals=null,this.sessionStartTime=null,A}createEmptyTotals(){return{totalScore:0,totalXP:0,maxCombo:0,accuracyDistribution:{perfect:0,great:0,good:0,ok:0,miss:0,wrongKey:0},accuracyPercentage:0,duration:0}}recalculateAccuracyPercentage(){if(!this.sessionTotals)return;const{perfect:A,great:B,good:I,ok:Q,miss:E,wrongKey:i}=this.sessionTotals.accuracyDistribution,e=A+B+I+Q+E+i;if(e===0){this.sessionTotals.accuracyPercentage=0;return}const a=A+B+I+Q;this.sessionTotals.accuracyPercentage=a/e*100}getConfig(){return this.config}updateConfig(A){this.config=di({...this.config,...A})}}class $W{static enchant(A,B,I,Q){return si.validateModification(I).valid?this.addModificationToEquipment(A,B,I,Q):A}static applyTemplate(A,B,I,Q){const e=IA.getInstance().get("equipment.templates").find(t=>t.id===I);if(!e)return A;const a={id:`template_${I}_${Date.now()}`,name:e.name||`Template: ${I}`,properties:e.properties||[],addsFeatures:e.addsFeatures,addsSkills:e.addsSkills,addsSpells:e.addsSpells,appliedAt:new Date().toISOString(),source:"template"},C=this.addModificationToEquipment(A,B,a,Q),o=this.findItem(C,B);return o&&(o.templateId=I),C}static curse(A,B,I,Q){return si.validateModification(I).valid?this.addModificationToEquipment(A,B,I,Q):A}static upgrade(A,B,I,Q){return this.enchant(A,B,I,Q)}static removeModification(A,B,I,Q){const E={weapons:A.weapons.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),armor:A.armor.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),items:A.items.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),totalWeight:A.totalWeight,equippedWeight:A.equippedWeight},i=this.findItem(E,B);if(!i||!i.modifications)return A;const e=i.equipped,a=i.instanceId,C=this.getEquipmentData(B);e&&Q&&C&&PA.unequipItem(Q,B,a);const o=i.modifications.findIndex(t=>t.id===I);if(o!==-1&&i.modifications.splice(o,1),e&&Q&&C){PA.equipItem(Q,C,a);for(const t of i.modifications)this.applyModificationEffects(Q,C,t,a)}return E}static getModificationHistory(A,B){const I=this.findItem(A,B);return!I||!I.modifications?[]:I.modifications.map(Q=>({...Q}))}static getCombinedEffects(A,B,I){const Q=this.getEquipmentData(B);if(!Q)return[];const E=Q.properties?[...Q.properties]:[],i=this.findItem(A,B);if(i&&i.modifications)for(const e of i.modifications)e.properties&&E.push(...e.properties);return E}static hasTemplate(A,B,I){const Q=this.findItem(A,B);return Q?Q.templateId===I?!0:Q.modifications?Q.modifications.some(E=>E.source==="template"&&E.name.includes(I)):!1:!1}static getAppliedTemplates(A,B){const I=[],Q=this.findItem(A,B);if(!Q)return I;if(Q.templateId&&I.push(Q.templateId),Q.modifications){for(const E of Q.modifications)if(E.source==="template"){const i=E.id.match(/template_([^_]+)/);i&&I.push(i[1])}}return I}static removeAllModifications(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications||Q.modifications.length===0)return A;const E=Q.modifications.map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static disenchant(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications)return A;const E=Q.modifications.filter(e=>e.source==="enchantment"||e.source==="upgrade").map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static liftCurse(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications)return A;const E=Q.modifications.filter(e=>e.source==="curse").map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static addModificationToEquipment(A,B,I,Q){const E={weapons:A.weapons.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),armor:A.armor.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),items:A.items.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),totalWeight:A.totalWeight,equippedWeight:A.equippedWeight},i=this.findItem(E,B);if(!i)return A;const e=this.getEquipmentData(B);if(!e)return A;i.modifications||(i.modifications=[]),i.instanceId||(i.instanceId=`${B}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const a=i.equipped,C=i.instanceId;if(a&&Q&&PA.unequipItem(Q,B,C),i.modifications.push(I),a&&Q){PA.equipItem(Q,e,C);for(const o of i.modifications)this.applyModificationEffects(Q,e,o,C)}return E}static findItem(A,B){return A.weapons.find(Q=>Q.name===B)||A.armor.find(Q=>Q.name===B)||A.items.find(Q=>Q.name===B)}static getEquipmentData(A){return IA.getInstance().get("equipment").find(Q=>Q.name===A)}static applyModificationEffects(A,B,I,Q){const E={...B,properties:[...B.properties||[],...I.properties],grantsFeatures:I.addsFeatures,grantsSkills:I.addsSkills,grantsSpells:I.addsSpells};PA.equipItem(A,E,Q)}static createModification(A,B,I,Q){return{id:A,name:B,properties:I,appliedAt:new Date().toISOString(),source:Q}}static createFeatureModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsFeatures:Q,appliedAt:new Date().toISOString(),source:E}}static createSkillModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsSkills:Q,appliedAt:new Date().toISOString(),source:E}}static createSpellModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsSpells:Q,appliedAt:new Date().toISOString(),source:E}}static generateModificationId(A="mod"){return`${A}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}static getModificationSources(A,B){const I=this.findItem(A,B);if(!I||!I.modifications)return[];const Q=new Set(I.modifications.map(E=>E.source));return Array.from(Q)}static countModificationsBySource(A,B){const I=this.findItem(A,B);if(!I||!I.modifications)return{};const Q={};for(const E of I.modifications)Q[E.source]=(Q[E.source]||0)+1;return Q}static countModificationsForSource(A,B,I){return this.countModificationsBySource(A,B)[I]||0}static isCursed(A,B){return this.countModificationsForSource(A,B,"curse")>0}static isEnchanted(A,B){return this.getModificationSources(A,B).some(Q=>Q==="enchantment"||Q==="upgrade")}static getItemSummary(A,B){const I=this.findItem(A,B);return I?{name:I.name,quantity:I.quantity,equipped:I.equipped,instanceId:I.instanceId,templateId:I.templateId,modificationCount:I.modifications?.length||0,isCursed:this.isCursed(A,B),isEnchanted:this.isEnchanted(A,B),sources:this.getModificationSources(A,B),effects:this.getCombinedEffects(A,B)}:null}}const ge={plus_one_weapon:{rarity:"uncommon",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+1"]},plus_two_weapon:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+2"]},plus_three_weapon:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+3"]},flaming_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],tags:["magic","fire","flaming"]},frost_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],tags:["magic","cold","frost"]},shocking_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage",condition:{type:"on_hit",value:!0}}],tags:["magic","lightning","shocking"]},vicious_weapon_template:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"extra_damage_on_hit",value:"1d8",description:"Deals 1d8 extra damage, but wielder takes 1d8 necrotic",condition:{type:"on_hit",value:!0}}],tags:["magic","vicious","necrotic"]},plus_one_armor:{rarity:"rare",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 AC bonus",stackable:!0}],tags:["magic","enhanced","+1","armor"]},plus_two_armor:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus",stackable:!0}],tags:["magic","enhanced","+2","armor"]}};class Td{static openBox(A,B,I){if(!A.boxContents)return{success:!1,items:[],gold:0,consumeBox:!1,error:{code:"NO_BOX_CONTENTS",message:`Box "${A.name}" has no contents defined.`}};if(I!==void 0&&A.boxContents.openRequirements){const C=this.checkRequirements(A,I);if(C)return{success:!1,items:[],gold:0,consumeBox:!1,error:C}}const Q=A.boxContents,E=[];let i=0;for(const C of Q.drops){const o=this.selectFromPool(C.pool,B);if(o){if(o.gold!==void 0&&o.gold>0)i+=o.gold;else if(o.itemName){const t=this.getEquipmentData(o.itemName);if(t){const s=o.quantity??1;if(t.type==="box")E.push({...t});else for(let n=0;n<s;n++)E.push(t)}else console.warn(`BoxOpener: Item "${o.itemName}" not found in equipment registry. Skipping drop.`)}}}const e=Q.consumeOnOpen!==!1;let a;return I!==void 0&&Q.openRequirements&&(a=Q.openRequirements.map(C=>({name:C.itemName,quantity:C.quantity??1}))),{success:!0,items:E,gold:i,consumeBox:e,consumedItems:a}}static selectFromPool(A,B){if(!A||A.length===0)return;const I=A.map(Q=>[Q,Q.weight]);return B.weightedChoice(I)}static getEquipmentData(A){return IA.getInstance().get("equipment").find(Q=>Q.name===A)}static isBox(A){return A.type==="box"&&A.boxContents!==void 0}static checkRequirements(A,B){if(!A.boxContents?.openRequirements)return null;const I=A.boxContents.openRequirements;for(const Q of I){const E=Q.quantity??1,i=B.find(e=>e.name===Q.itemName);if(!i)return{code:"MISSING_ITEM",message:`Missing required item: ${Q.itemName}`,requirement:Q};if(i.quantity<E)return{code:"INSUFFICIENT_QUANTITY",message:`Insufficient ${Q.itemName}: have ${i.quantity}, need ${E}`,requirement:Q}}return null}static canOpen(A,B){return A.boxContents?.openRequirements?this.checkRequirements(A,B)===null:!0}static getRequirementsDescription(A){if(!A.boxContents?.openRequirements||A.boxContents.openRequirements.length===0)return null;const B=A.boxContents.openRequirements,I=[];for(const Q of B){const E=Q.quantity??1;E===1?I.push(Q.itemName):I.push(`${E} ${Q.itemName}`)}return`Requires: ${I.join(", ")}`}static previewContents(A){if(!A.boxContents)return{possibleItems:[],possibleGold:{min:0,max:0},totalDrops:0};const B=new Set;let I=0,Q=0;for(const i of A.boxContents.drops)for(const e of i.pool)e.itemName&&B.add(e.itemName),e.gold!==void 0&&(I=Math.min(I,e.gold),Q=Math.max(Q,e.gold));const E={possibleItems:Array.from(B),possibleGold:{min:I,max:Q},totalDrops:A.boxContents.drops.length};return A.boxContents.openRequirements&&A.boxContents.openRequirements.length>0&&(E.openRequirements=A.boxContents.openRequirements),E}}const VI={common:0,uncommon:1,rare:2,very_rare:3,legendary:4};class AH{static spawnFromList(A,B){const Q=IA.getInstance().get("equipment");let E=A;return B&&(E=B.shuffle([...A])),E.map(i=>Q.find(e=>e.name===i))}static spawnByRarity(A,B,I){const i=IA.getInstance().get("equipment").filter(t=>t.rarity===A&&(t.spawnWeight??1)>0);if(i.length===0)return[];const e=Math.min(B,i.length),a=[],C=I?()=>I.random():Math.random,o=[...i];for(let t=o.length-1;t>0;t--){const s=Math.floor(C()*(t+1));[o[t],o[s]]=[o[s],o[t]]}for(let t=0;t<e;t++)a.push(o[t]);return a}static spawnByTags(A,B,I,Q){const e=IA.getInstance().get("equipment").filter(t=>{if(Q?.excludeZeroWeight&&(t.spawnWeight??1)<=0||Q?.includeTypes&&Q.includeTypes.length>0&&!Q.includeTypes.includes(t.type))return!1;const s=VI[t.rarity];return Q?.minRarity&&s<VI[Q.minRarity]||Q?.maxRarity&&s>VI[Q.maxRarity]?!1:t.tags&&A.some(n=>t.tags.includes(n))});if(e.length===0)return[];const a=Math.min(B,e.length),C=[],o=I?()=>I.random():Math.random;for(let t=0;t<a;t++){const s=e.map(G=>[G,G.spawnWeight??1]);let n=s.reduce((G,[,D])=>G+D,0),r=o()*n,c;for(const[G,D]of s)if(r-=D,r<=0){c=G;break}if(c){C.push(c);const G=e.indexOf(c);G>-1&&e.splice(G,1)}}return C}static spawnRandom(A,B,I){const i=IA.getInstance().get("equipment").filter(a=>{const C=a.spawnWeight??1;if(I?.excludeZeroWeight&&C<=0||I?.includeTypes&&I.includeTypes.length>0&&!I.includeTypes.includes(a.type))return!1;const o=VI[a.rarity];return I?.minRarity&&o<VI[I.minRarity]||I?.maxRarity&&o>VI[I.maxRarity]?!1:C>0});if(i.length===0)return[];const e=[];for(let a=0;a<A&&i.length>0;a++){const C=i.map(c=>[c,c.spawnWeight??1]),o=C.reduce((c,[,G])=>c+G,0);let t=B.random()*o,s=0;for(let c=0;c<C.length;c++)if(t-=C[c][1],t<=0){s=c;break}const n=C[s][0];e.push(n);const r=i.indexOf(n);r>-1&&i.splice(r,1)}return e}static spawnFromTemplate(A,B){const I=IA.getInstance();let Q;const E=I.get("equipment.templates");if(Array.isArray(E)?Q=E.find(e=>e.id===A||e.name===A):E&&typeof E=="object"&&(Q=E[A]),!Q&&A in ge&&(Q=ge[A]),!Q)return null;let i;return B?i=I.get("equipment").find(a=>a.name===B):i=this.getDefaultItemForTemplate(Q),i?{...i,...Q,name:`${i.name} (${A.replace(/_/g," ")})`,templateId:A,properties:[...i.properties||[],...Q.properties||[]],tags:[...i.tags||[],...Q.tags||[]]}:null}static spawnTreasureHoard(A,B){const I=[];let Q=0;const E=this.getTreasureItemCount(A,B);for(const[i,e]of Object.entries(E))for(let a=0;a<e;a++){const C=i,o=this.spawnByRarity(C,1,B);o.length>0&&(I.push(o[0]),Q+=this.estimateItemValue(o[0]))}if(I.length===0){const i=this.spawnByRarity("common",1,B);i.length>0&&(I.push(i[0]),Q+=this.estimateItemValue(i[0]))}return{items:I,totalValue:Q,cr:A}}static addToCharacter(A,B,I=!1){A.equipment||(A.equipment={weapons:[],armor:[],items:[],totalWeight:0,equippedWeight:0});for(const Q of B){const E={name:Q.name,quantity:1,equipped:I,instanceId:`${Q.name}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`};switch(Q.type){case"weapon":A.equipment.weapons.push(E);break;case"armor":A.equipment.armor.push(E);break;case"item":case"box":A.equipment.items.push(E);break}A.equipment.totalWeight+=Q.weight,I&&(A.equipment.equippedWeight+=Q.weight)}return A}static openBoxForCharacter(A,B,I){if(!A.equipment?.items||A.equipment.items.findIndex(t=>t.name===B)===-1)return null;const e=IA.getInstance().get("equipment").find(t=>t.name===B);if(!e||e.type!=="box")return null;const a=A.equipment.items,C=Td.openBox(e,I,a);if(!C.success)return{character:A,result:C};if(C.consumedItems&&C.consumedItems.length>0)for(const t of C.consumedItems)A=this.consumeItemFromCharacter(A,t.name,t.quantity).character;if(C.consumeBox&&A.equipment?.items){const t=A.equipment.items.findIndex(s=>s.name===B);t!==-1&&(A.equipment.items.splice(t,1),A.equipment.totalWeight-=e.weight)}const o=C.items;return A=this.addToCharacter(A,o,!1),{character:A,result:C}}static consumeItemFromCharacter(A,B,I){if(!A.equipment?.items)return{success:!1,character:A};const Q=A.equipment.items.findIndex(o=>o.name===B);if(Q===-1)return{success:!1,character:A};const E=A.equipment.items[Q];if(E.quantity<I)return{success:!1,character:A};const C=IA.getInstance().get("equipment").find(o=>o.name===B)?.weight??0;return E.quantity-=I,A.equipment.totalWeight-=C*I,E.quantity<=0&&A.equipment.items.splice(Q,1),{success:!0,character:A}}static getDefaultItemForTemplate(A){const I=IA.getInstance().get("equipment");let Q="item";return A.tags&&(A.tags.includes("weapon")?Q="weapon":A.tags.includes("armor")&&(Q="armor")),I.find(E=>E.type===Q)}static getTreasureItemCount(A,B){const I={},Q=()=>B.random();return A<5?(I.common=Math.floor(Q()*3)+1,I.uncommon=Q()>.7?1:0):A<11?(I.common=Math.floor(Q()*2)+1,I.uncommon=Math.floor(Q()*2)+1,I.rare=Q()>.6?1:0):A<17?(I.uncommon=Math.floor(Q()*2)+1,I.rare=Math.floor(Q()*2)+1,I.very_rare=Q()>.7?1:0):(I.rare=Math.floor(Q()*2)+1,I.very_rare=Math.floor(Q()*2)+1,I.legendary=Q()>.5?1:0),I}static estimateItemValue(A){let I={common:50,uncommon:400,rare:4e3,very_rare:4e4,legendary:2e5}[A.rarity]||50;return A.type==="weapon"?I*=1.2:A.type==="armor"&&(I*=1.5),A.weight>20&&(I*=1.1),Math.round(I)}}const _o="geolocation_cache";class Od{constructor(A,B){if(this.cache=null,this.cacheTTL=300*1e3,this.cacheStats={hits:0,misses:0},this.logger=VA.for("GeolocationProvider"),typeof A=="number"||A===void 0)this.cacheTTL=(A??5)*60*1e3,this.useLocalStorage=(B??!0)&&this.isLocalStorageAvailable();else{const I=A;this.cacheTTL=I.cacheTTL??300*1e3,this.useLocalStorage=(I.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const A="__localStorage_test__";return localStorage.setItem(A,A),localStorage.removeItem(A),!0}catch{return!1}}loadFromLocalStorage(){try{const A=localStorage.getItem(_o);if(A){const B=JSON.parse(A),I=Date.now(),Q=Object.values(B)[0];Q&&I-Q.timestamp<this.cacheTTL&&(this.cache=Q)}}catch(A){this.logger.warn("Failed to load geolocation cache from localStorage",{error:A})}}saveToLocalStorage(){if(!(!this.useLocalStorage||!this.cache))try{const A={position:this.cache};localStorage.setItem(_o,JSON.stringify(A))}catch(A){this.logger.warn("Failed to save geolocation cache to localStorage",{error:A})}}isCacheEntryValid(A){return A?Date.now()-A.timestamp<this.cacheTTL:!1}getCacheAge(){return this.cache?Date.now()-this.cache.timestamp:null}async getCurrentPosition(A=!1){return!A&&this.isCacheEntryValid(this.cache)?(this.cacheStats.hits++,this.cache.data):(this.cacheStats.misses++,typeof navigator>"u"||!navigator.geolocation?null:new Promise(B=>{navigator.geolocation.getCurrentPosition(I=>{const Q={latitude:I.coords.latitude,longitude:I.coords.longitude,altitude:I.coords.altitude,accuracy:I.coords.accuracy,heading:I.coords.heading,speed:I.coords.speed,timestamp:I.timestamp};this.cache={data:Q,timestamp:Date.now()},this.saveToLocalStorage(),B(Q)},I=>{this.logger.warn("Geolocation error",{error:I.message}),B(null)},{enableHighAccuracy:!0,timeout:5e3,maximumAge:0})}))}invalidateCache(){if(this.cache=null,this.useLocalStorage)try{localStorage.removeItem(_o)}catch(A){this.logger.warn("Failed to clear geolocation cache from localStorage",{error:A})}}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}isCacheExpired(){return!this.isCacheEntryValid(this.cache)}getCachedPosition(){return this.cache?.data??null}getBiome(A,B,I=null){const Q=Math.abs(A),E=this.normalizeLongitude(B),i=this.isCoastal(A,B),e=i?"_coastal":"";if(I!==null&&!isNaN(I)){const a=this.getElevationBiome(I,e);if(a)return a}if(Q>66.5)return`tundra${e}`;if(this.isInSwampRegion(A,E))return`swamp${e}`;if(Q>=15&&Q<=45&&this.isInDesertRegion(A,E))return i?"coastal_desert":"desert";if(Q<=23.5){if(this.isInJungleRegion(A,E))return`jungle${e}`;if(this.isInSavannaRegion(A,E))return`savanna${e}`;if(A>0){if(E>=290&&E<=310)return`forest${e}`;if(E>=10&&E<=30)return`forest${e}`;if(E>=70&&E<=120)return`forest${e}`}else{if(E>=10&&E<=30)return`forest${e}`;if(E>=100&&E<=140)return`forest${e}`}return`forest${e}`}if(A>0&&Q>=30&&Q<=50&&(E>=235&&E<=290||E>=0&&E<=40||E>=110&&E<=145))return i?"coastal_urban":"urban";if(Q>23.5&&Q<=66.5)if(A>0){if(this.isInTaigaRegion(A,E))return`taiga${e}`;if(E>=235&&E<=290)return Q>=45?`forest${e}`:`plains${e}`;if(E>=0&&E<=40)return`forest${e}`;if(E>40&&E<=180)return Q>=50?`mountain${e}`:`plains${e}`}else{if(E>=280&&E<=320)return`plains${e}`;if(E>=15&&E<=40)return`plains${e}`;if(E>=110&&E<=180)return`plains${e}`}return`plains${e}`}getElevationBiome(A,B){return A>3500?`mountain${B}`:A>1500?`mountain${B}`:A<0?`valley${B}`:null}normalizeLongitude(A){let B=A%360;return B<0&&(B+=360),B}isInJungleRegion(A,B){return Math.abs(A)>15?!1:A>=-15&&A<=5&&B>=290&&B<=310||A>=-5&&A<=5&&B>=10&&B<=30||A>=-10&&A<=10&&B>=95&&B<=140}isInSwampRegion(A,B){return A>=25&&A<=26&&B>=279&&B<=281||A>=-20&&A<=-18&&B>=22&&B<=24||A>=21&&A<=22&&B>=89&&B<=90||A>=-20&&A<=-15&&B>=300&&B<=305}isInTaigaRegion(A,B){return A<50||A>70?!1:A>=50&&A<=70&&B>=230&&B<=300||A>=60&&A<=70&&B>=5&&B<=30||A>=55&&A<=70&&B>=30&&B<=180}isInSavannaRegion(A,B){return Math.abs(A)>20?!1:A>=-5&&A<=15&&B>=30&&B<=40||A>=-20&&A<=-15&&B>=15&&B<=35||A>=-25&&A<=-5&&B>=300&&B<=315||A>=-20&&A<=-10&&B>=130&&B<=135}isInDesertRegion(A,B){return A>15&&A<30&&(B>=345||B<=40)||A>15&&A<30&&B>=35&&B<=55||A>28&&A<35&&B>=35&&B<=40||A>25&&A<35&&B>=50&&B<=65||A>23&&A<30&&B>=68&&B<=75||A>35&&A<45&&B>=100&&B<=115||A<-20&&A>-30&&B>=115&&B<=145||A<-20&&A>-25&&B>=290&&B<=292||A>25&&A<35&&B>=245&&B<=250||A<-20&&A>-30&&B>=20&&B<=30}isCoastal(A,B){const I=Math.abs(A),Q=this.normalizeLongitude(B);return!!(this.isInSmallIslandRegion(A,Q)||this.isInNarrowLandmass(A,Q)||I>60||A>30&&A<45&&Q>=0&&Q<=25||A>30&&A<40&&Q>=25&&Q<=35||A>15&&A<30&&Q>=35&&Q<=43||A>24&&A<30&&Q>=48&&Q<=55||A>41&&A<47&&Q>=27&&Q<=42||A>36&&A<47&&Q>=46&&Q<=55||A>53&&A<66&&Q>=15&&Q<=30||A>50&&A<60&&Q>=0&&Q<=10||A>15&&A<25&&Q>=65&&Q<=75||A>15&&A<23&&Q>=80&&Q<=90||A>35&&A<43&&Q>=130&&Q<=142||A>10&&A<25&&Q>=105&&Q<=122||A>20&&A<30&&Q>=265&&Q<=280||A>15&&A<25&&Q>=275&&Q<=300)}isInSmallIslandRegion(A,B){return A>=50&&A<=60&&B>=358&&B<=360||A>=50&&A<=60&&B>=0&&B<=10||A>=30&&A<=46&&B>=128&&B<=146||A>=4&&A<=22&&B>=116&&B<=127||A>=-10&&A<=6&&B>=94&&B<=142||A>=-47&&A<=-34&&B>=165&&B<=179||A>=-26&&A<=-12&&B>=43&&B<=51||A>=63&&A<=67&&B>=338&&B<=344||A>=10&&A<=23&&B>=275&&B<=300||A>=5&&A<=10&&B>=79&&B<=82||A>=18&&A<=23&&B>=204&&B<=206||A>=-18&&A<=-12&&B>=176&&B<=181}isInNarrowLandmass(A,B){return A>=7&&A<=18&&B>=275&&B<=290||A>=33&&A<=43&&B>=124&&B<=132||A>=36&&A<=47&&B>=8&&B<=19||A>=36&&A<=44&&B>=358&&B<=360||A>=36&&A<=44&&B>=0&&B<=10||A>=55&&A<=71&&B>=4&&B<=32||A>=24&&A<=31&&B>=268&&B<=272||A>=55&&A<=62&&B>=210&&B<=220||A>=50&&A<=62&&B>=156&&B<=163}}class BH{constructor(){this.logger=VA.for("MotionDetector"),this.isListening=!1,this.lastMotion=null,this.motionCallback=null,this.lastProcessedTime=0,this.minSampleIntervalMs=83,this.deltaBuffer=[],this.bufferSize=24,this.confirmedActivity="unknown",this.candidateActivity=null,this.candidateStartTime=0,this.confirmationMs=1500,this.handleMotion=A=>{if(!this.isListening||!this.motionCallback)return;const B=Date.now(),I={acceleration:{x:A.acceleration?.x??null,y:A.acceleration?.y??null,z:A.acceleration?.z??null},accelerationIncludingGravity:{x:A.accelerationIncludingGravity?.x??0,y:A.accelerationIncludingGravity?.y??0,z:A.accelerationIncludingGravity?.z??0},rotationRate:{alpha:A.rotationRate?.alpha??null,beta:A.rotationRate?.beta??null,gamma:A.rotationRate?.gamma??null},interval:A.interval,timestamp:B};if(this.lastMotion=I,B-this.lastProcessedTime<this.minSampleIntervalMs)return;this.lastProcessedTime=B;const Q=I.accelerationIncludingGravity;if(Q.x!=null&&Q.y!=null&&Q.z!=null&&!(Q.x===0&&Q.y===0&&Q.z===0)){const E=Math.sqrt(Q.x**2+Q.y**2+Q.z**2),i=Math.abs(E-9.8);this.updateSmoothing(i)}this.logger.debug("Motion event received",{acceleration:I.accelerationIncludingGravity,rotation:I.rotationRate,activity:this.detectActivity(I)}),this.motionCallback(I)}}startMonitoring(A){if(typeof window>"u"||!("DeviceMotionEvent"in window)){this.logger.warn("DeviceMotionEvent not supported");return}this.motionCallback=A,this.isListening=!0,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null,window.addEventListener("devicemotion",this.handleMotion)}stopMonitoring(){typeof window<"u"&&window.removeEventListener("devicemotion",this.handleMotion),this.isListening=!1,this.motionCallback=null,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null}getLastMotion(){return this.lastMotion}detectActivity(A){const B=A.accelerationIncludingGravity;if(B.x==null||B.y==null||B.z==null||B.x===0&&B.y===0&&B.z===0)return"unknown";if(this.deltaBuffer.length>=10)return this.confirmedActivity;const I=Math.sqrt(B.x**2+B.y**2+B.z**2),Q=Math.abs(I-9.8);return Q<.5?"stationary":Q<2?"walking":Q<5?"running":"driving"}updateSmoothing(A){this.deltaBuffer.push(A),this.deltaBuffer.length>this.bufferSize&&this.deltaBuffer.shift();const B=this.deltaBuffer.reduce((Q,E)=>Q+E,0)/this.deltaBuffer.length;let I;if(B<.5?I="stationary":B<2?I="walking":B<5?I="running":I="driving",I===this.confirmedActivity){this.candidateActivity=null;return}if(I!==this.candidateActivity){this.candidateActivity=I,this.candidateStartTime=Date.now();return}Date.now()-this.candidateStartTime>=this.confirmationMs&&(this.confirmedActivity=I,this.candidateActivity=null)}}const jd=wA({id:K(),main:O(),description:O(),icon:O().optional()}),Pd=wA({temp:K(),feels_like:K().optional(),temp_min:K().optional(),temp_max:K().optional(),pressure:K(),humidity:K().nonnegative(),sea_level:K().optional(),grnd_level:K().optional(),temp_kf:K().optional()}),_d=wA({speed:K().nonnegative(),deg:K().min(0).max(360).optional(),gust:K().optional()}),$d=wA({all:K().min(0).max(100)}),gH=wA({type:K().optional(),id:K().optional(),country:O().optional(),sunrise:K().nonnegative(),sunset:K().nonnegative(),pod:O().optional()}),Aw=wA({"1h":K().optional(),"3h":K().optional()}),Bw=wA({"1h":K().optional(),"3h":K().optional()}),gw=wA({lon:K().min(-180).max(180),lat:K().min(-90).max(90)}),IH=wA({coord:gw.optional(),weather:zA(jd).min(1),base:O().optional(),main:Pd,visibility:K().nonnegative().optional(),wind:_d.optional(),rain:Aw.optional(),snow:Bw.optional(),clouds:$d.optional(),dt:K().nonnegative(),sys:gH,timezone:K().optional(),id:K().optional(),name:O().optional(),cod:K().optional()}),QH=wA({dt:K().nonnegative(),main:Pd,weather:zA(jd).min(1),clouds:$d.optional(),wind:_d.optional(),visibility:K().nonnegative().optional(),pop:K().min(0).max(1).optional(),rain:Aw.optional(),snow:Bw.optional(),sys:wA({pod:O().optional()}).optional(),dt_txt:O().optional()}),EH=wA({cod:TC([O(),K()]),message:K().optional(),cnt:K().nonnegative().optional(),list:zA(QH).min(1),city:wA({id:K(),name:O(),coord:gw,country:O().optional(),population:K().optional(),timezone:K().optional(),sunrise:K().optional(),sunset:K().optional()}).optional()}),Iw="weather_api_cache";class iH{constructor(A,B,I){if(this.baseUrl="https://api.openweathermap.org/data/2.5/weather",this.forecastUrl="https://api.openweathermap.org/data/2.5/forecast",this.cache=new Map,this.forecastCache=new Map,this.cacheTTL=720*1e3,this.forecastCacheTTL=3600*1e3,this.cacheStats={hits:0,misses:0},this.logger=VA.for("WeatherAPIClient"),this.lastKnownLocation=null,this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[],this.maxRecentSamples=100,typeof A=="string"||A===void 0)this.apiKey=A??"",this.cacheTTL=(B??12)*60*1e3,this.useLocalStorage=(I??!0)&&this.isLocalStorageAvailable();else{const Q=A;this.apiKey=Q.apiKey??"",this.cacheTTL=Q.cacheTTL??720*1e3,this.useLocalStorage=(Q.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const A="__localStorage_test__";return localStorage.setItem(A,A),localStorage.removeItem(A),!0}catch{return!1}}loadFromLocalStorage(){try{const A=localStorage.getItem(Iw);if(A){const B=JSON.parse(A),I=Date.now();for(const[Q,E]of Object.entries(B))I-E.timestamp<this.cacheTTL&&this.cache.set(Q,E)}}catch(A){this.logger.warn("Failed to load weather cache from localStorage",{error:A})}}saveToLocalStorage(){if(this.useLocalStorage)try{const A={};for(const[B,I]of this.cache.entries())A[B]=I;localStorage.setItem(Iw,JSON.stringify(A))}catch(A){this.logger.warn("Failed to save weather cache to localStorage",{error:A})}}startTiming(){const A=performance.now();return B=>{performance.now()-A}}recordWeatherApiCall(A,B){const I=Date.now();B?(this.weatherApiMetrics.successCount++,this.weatherApiMetrics.totalTime+=A,this.weatherApiMetrics.minTime=Math.min(this.weatherApiMetrics.minTime,A),this.weatherApiMetrics.maxTime=Math.max(this.weatherApiMetrics.maxTime,A),this.recentWeatherApiTimes.push(A),this.recentWeatherApiTimes.length>this.maxRecentSamples&&this.recentWeatherApiTimes.shift()):this.weatherApiMetrics.errorCount++,this.weatherApiMetrics.lastCallTimestamp=I}recordForecastApiCall(A,B){const I=Date.now();B?(this.forecastApiMetrics.successCount++,this.forecastApiMetrics.totalTime+=A,this.forecastApiMetrics.minTime=Math.min(this.forecastApiMetrics.minTime,A),this.forecastApiMetrics.maxTime=Math.max(this.forecastApiMetrics.maxTime,A),this.recentForecastApiTimes.push(A),this.recentForecastApiTimes.length>this.maxRecentSamples&&this.recentForecastApiTimes.shift()):this.forecastApiMetrics.errorCount++,this.forecastApiMetrics.lastCallTimestamp=I}calculatePercentile(A,B){if(A.length===0)return 0;const I=[...A].sort((E,i)=>E-i),Q=Math.ceil(B/100*I.length)-1;return I[Math.max(0,Q)]}getPerformanceStatistics(A,B){const I=A.successCount+A.errorCount,Q=A.successCount>0?A.totalTime/A.successCount:0,E=I>0?A.successCount/I*100:0;return{average:Math.round(Q),min:A.minTime===1/0?0:Math.round(A.minTime),max:Math.round(A.maxTime),totalCalls:I,successRate:Math.round(E*10)/10}}getWeatherApiMetrics(){return{...this.weatherApiMetrics}}getWeatherApiStatistics(){return{...this.getPerformanceStatistics(this.weatherApiMetrics,this.recentWeatherApiTimes),p95:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,99))}}getForecastApiMetrics(){return{...this.forecastApiMetrics}}getForecastApiStatistics(){return{...this.getPerformanceStatistics(this.forecastApiMetrics,this.recentForecastApiTimes),p95:Math.round(this.calculatePercentile(this.recentForecastApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentForecastApiTimes,99))}}resetPerformanceMetrics(){this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[]}getCacheKey(A,B){const I=Math.round(A*1e4)/1e4,Q=Math.round(B*1e4)/1e4;return`${I},${Q}`}isCacheEntryValid(A){return Date.now()-A.timestamp<this.cacheTTL}calculateMoonPhase(A){const B=new Date("2024-01-11T11:57:00Z"),I=29.530588853*24*60*60*1e3,E=(A.getTime()-B.getTime())/I;return Math.abs(E%1)}getSolarInfo(A,B,I){const Q=I??new Date;return{...this.calculateSolarInfo(A,B,Q),fromApi:!1,timestamp:Date.now()}}calculateSolarInfo(A,B,I){const Q=this.calculateSunriseSunset(A,B,I,!0),E=this.calculateSunriseSunset(A,B,I,!1),i=this.calculateSolarNoon(A,B,I),e=this.calculateTwilight(A,B,I,"civil",!0),a=this.calculateTwilight(A,B,I,"civil",!1),{altitude:C,azimuth:o}=this.calculateSunPosition(A,B,I),t=!isNaN(E.getTime())&&!isNaN(Q.getTime())?(E.getTime()-Q.getTime())/(1e3*60*60):0,s=this.determineDayStage(I,Q,E,e,a);return{currentTime:I,stage:s,sunrise:Q,sunset:E,solarNoon:i,civilDawn:e,civilDusk:a,sunAltitude:C,sunAzimuth:o,dayLengthHours:t}}toRadians(A){return A*(Math.PI/180)}toDegrees(A){return A*(180/Math.PI)}calculateJulianDate(A){const B=A.getUTCFullYear(),I=A.getUTCMonth()+1,Q=A.getUTCDate(),E=A.getUTCHours()+A.getUTCMinutes()/60+A.getUTCSeconds()/3600;let i=Math.floor((14-I)/12),e=B+4800-i,a=I+12*i-3;return Q+Math.floor((153*a+2)/5)+365*e+Math.floor(e/4)-Math.floor(e/100)+Math.floor(e/400)-32045+(E-12)/24}calculateJulianCentury(A){return(A-2451545)/36525}calculateSolarDeclination(A){const B=(280.46646+A*(36000.76983+A*3032e-7))%360,I=357.52911+A*(35999.05029-1537e-7*A),Q=Math.sin(this.toRadians(I))*(1.914602-A*(.004817+14e-6*A))+Math.sin(this.toRadians(2*I))*(.019993-101e-6*A)+Math.sin(this.toRadians(3*I))*289e-6,E=B+Q,e=23+(26+(21.448-A*(46.815+A*(59e-5-A*.001813)))/60)/60+.00256*Math.cos(this.toRadians(125.04-1934.136*A)),a=E-.00569-.00478*Math.sin(this.toRadians(125.04-1934.136*A));return this.toDegrees(Math.asin(Math.sin(this.toRadians(e))*Math.sin(this.toRadians(a))))}calculateEquationOfTime(A){const B=(280.46646+A*(36000.76983+A*3032e-7))%360,I=357.52911+A*(35999.05029-1537e-7*A),Q=.016708634-A*(42037e-9+1267e-10*A);return Math.tan(this.toRadians(23.44/2))**2,4*this.toDegrees(Math.atan2(Math.sin(this.toRadians(2*B))*Math.cos(this.toRadians(I))*2*Q-Math.sin(this.toRadians(I))*2*Q+Math.sin(this.toRadians(B))*Math.cos(this.toRadians(I))*2*Q*Math.cos(this.toRadians(B)),1-Math.sin(this.toRadians(B))*Math.sin(this.toRadians(B))*Q*Q*Math.cos(this.toRadians(I))*Math.cos(this.toRadians(I))))}calculateHourAngle(A,B,I=90.833){const Q=this.toRadians(A),E=this.toRadians(B),i=this.toRadians(I),e=(Math.cos(i)-Math.sin(Q)*Math.sin(E))/(Math.cos(Q)*Math.cos(E));return e>1?NaN:e<-1?NaN:this.toDegrees(Math.acos(e))}calculateSunriseSunset(A,B,I,Q){const E=new Date(I);E.setUTCHours(12,0,0,0);const i=this.calculateJulianDate(E),e=this.calculateJulianCentury(i),a=this.calculateSolarDeclination(e),C=this.calculateHourAngle(A,a);if(isNaN(C))return new Date(NaN);const o=this.calculateEquationOfTime(e),t=(Q?-C:C)/15,n=12-B/15-o/60+t,r=Math.floor(n),c=Math.floor((n-r)*60),G=Math.floor(((n-r)*60-c)*60),D=new Date(I);return D.setUTCHours(r,c,G,0),D}calculateSolarNoon(A,B,I){const Q=new Date(I);Q.setUTCHours(12,0,0,0);const E=this.calculateJulianDate(Q),i=this.calculateJulianCentury(E),e=this.calculateEquationOfTime(i),a=12-B/15-e/60,C=Math.floor(a),o=Math.floor((a-C)*60),t=Math.floor(((a-C)*60-o)*60),s=new Date(I);return s.setUTCHours(C,o,t,0),s}calculateTwilight(A,B,I,Q,E){const e={civil:96,nautical:102,astronomical:108}[Q],a=new Date(I);a.setUTCHours(12,0,0,0);const C=this.calculateJulianDate(a),o=this.calculateJulianCentury(C),t=this.calculateSolarDeclination(o),s=this.calculateHourAngle(A,t,e);if(isNaN(s))return new Date(NaN);const n=this.calculateEquationOfTime(o),r=(E?-s:s)/15,G=12-B/15-n/60+r,D=Math.floor(G),h=Math.floor((G-D)*60),w=Math.floor(((G-D)*60-h)*60),y=new Date(I);return y.setUTCHours(D,h,w,0),y}calculateSunPosition(A,B,I){const Q=this.calculateJulianDate(I),E=this.calculateJulianCentury(Q),i=this.calculateSolarDeclination(E),e=this.calculateEquationOfTime(E),o=(I.getUTCHours()+I.getUTCMinutes()/60+I.getUTCSeconds()/3600+B/15+e/60-12)*15,t=this.toRadians(A),s=this.toRadians(i),n=this.toRadians(o),r=Math.sin(t)*Math.sin(s)+Math.cos(t)*Math.cos(s)*Math.cos(n),c=this.toDegrees(Math.asin(r)),G=(Math.sin(s)-Math.sin(t)*r)/(Math.cos(t)*Math.cos(this.toRadians(c)));let D=this.toDegrees(Math.acos(Math.max(-1,Math.min(1,G))));return o>0&&(D=360-D),{altitude:c,azimuth:D}}determineDayStage(A,B,I,Q,E){const i=A.getTime();if(isNaN(B.getTime())||isNaN(I.getTime())){const t=A.getUTCMonth();return t>=4&&t<=7?"day":"night"}const e=Q&&!isNaN(Q.getTime())?Q.getTime():B.getTime(),a=E&&!isNaN(E.getTime())?E.getTime():I.getTime(),C=B.getTime(),o=I.getTime();return i<e||i>a?"night":i>=e&&i<C?"dawn":i>o&&i<=a?"dusk":"day"}async getWeather(A,B){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;this.lastKnownLocation={latitude:A,longitude:B};const I=this.getCacheKey(A,B),Q=this.cache.get(I);if(Q&&this.isCacheEntryValid(Q))return this.cacheStats.hits++,Q.data;this.cacheStats.misses++;const E=performance.now();let i=!1;try{const e=`${this.baseUrl}?lat=${A}&lon=${B}&appid=${this.apiKey}&units=metric`,a=await fetch(e);if(!a.ok)throw new Error(`Weather API error: ${a.statusText}`);const C=await a.json(),o=IH.safeParse(C);if(!o.success)return this.logger.error("Weather API response validation failed",{errors:o.error.issues,response:C}),null;const t=o.data,s=Date.now()/1e3,n=s<t.sys.sunrise||s>t.sys.sunset,r={temperature:t.main.temp,humidity:t.main.humidity,pressure:t.main.pressure,weatherType:t.weather[0]?.main||"Clear",windSpeed:t.wind?.speed??0,windDirection:t.wind?.deg??0,isNight:n,moonPhase:this.calculateMoonPhase(new Date),timestamp:Date.now()};return this.cache.set(I,{data:r,timestamp:Date.now()}),this.saveToLocalStorage(),i=!0,r}catch(e){return this.logger.error("Failed to fetch weather",{error:e}),null}finally{const e=performance.now()-E;this.recordWeatherApiCall(e,i)}}invalidateCache(){this.cache.clear(),this.saveToLocalStorage()}invalidateLocation(A,B){const I=this.getCacheKey(A,B);this.cache.delete(I),this.saveToLocalStorage()}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}clearExpiredEntries(){let A=0;for(const[B,I]of this.cache.entries())this.isCacheEntryValid(I)||(this.cache.delete(B),A++);return A>0&&this.saveToLocalStorage(),A}getCacheSize(){return this.cache.size}getLastKnownLocation(){return this.lastKnownLocation?{...this.lastKnownLocation}:null}isForecastCacheEntryValid(A){return Date.now()-A.timestamp<this.forecastCacheTTL}getForecastCacheKey(A,B,I){const Q=Math.round(A*1e4)/1e4,E=Math.round(B*1e4)/1e4;return`forecast_${Q},${E}_${I}h`}async getForecast(A,B,I=24){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;const Q=Math.min(I,120),E=this.getForecastCacheKey(A,B,Q),i=this.forecastCache.get(E);if(i&&this.isForecastCacheEntryValid(i))return i.data.slice(0,Math.ceil(Q/3));const e=performance.now();let a=!1;try{const C=Math.ceil(Q/3),o=`${this.forecastUrl}?lat=${A}&lon=${B}&appid=${this.apiKey}&units=metric&cnt=${C}`,t=await fetch(o);if(!t.ok)throw new Error(`Weather Forecast API error: ${t.statusText}`);const s=await t.json(),n=EH.safeParse(s);if(!n.success)return this.logger.error("Weather Forecast API response validation failed",{errors:n.error.issues,response:s}),null;const c=n.data.list.map(G=>({temperature:G.main.temp,humidity:G.main.humidity,pressure:G.main.pressure,weatherType:G.weather[0]?.main||"Clear",windSpeed:G.wind?.speed??0,windDirection:G.wind?.deg??0,timestamp:Date.now(),forecastTime:new Date(G.dt*1e3),probabilityOfPrecipitation:G.pop??0}));return this.forecastCache.set(E,{data:c,timestamp:Date.now()}),a=!0,c}catch(C){return this.logger.error("Failed to fetch weather forecast",{error:C}),null}finally{const C=performance.now()-e;this.recordForecastApiCall(C,a)}}async getUpcomingWeather(A,B,I=12){const Q=await this.getForecast(A,B,I);if(!Q||Q.length===0)return null;let E=!1,i=!1,e=0,a=0;const C=[];for(const n of Q)C.push(n.weatherType),(n.weatherType.toLowerCase().includes("rain")||n.weatherType.toLowerCase().includes("drizzle"))&&(E=!0,e=Math.max(e,n.probabilityOfPrecipitation)),n.weatherType.toLowerCase().includes("snow")&&(i=!0,a=Math.max(a,n.probabilityOfPrecipitation));const o={thunderstorm:5,snow:4,rain:3,drizzle:2,clouds:1,clear:0,mist:1,fog:1,haze:1,smoke:1,dust:1,sand:1,ash:1,squall:2,tornado:5};let t="Clear",s=-1;for(const n of new Set(C)){const r=o[n.toLowerCase()]??0;r>s&&(s=r,t=n)}return{willRain:E,willSnow:i,rainProbability:e,snowProbability:a,worstWeatherType:t}}detectSevereWeather(A){const B=A.weatherType.toLowerCase(),I=A.windSpeed*3.6;if(B.includes("snow")||B.includes("blizzard")){const Q=B.includes("blizzard")||B.includes("heavy")||A.windSpeed>8,E=I>25;if(B.includes("blizzard")||Q&&E)return{type:"Blizzard",xpBonus:.5,severity:I>50?"extreme":"high",message:"⚠️ Blizzard conditions detected! Stay safe and warm.",detectedAt:Date.now()}}if(I>118){const E=(this.lastKnownLocation?this.isTropicalRegion(this.lastKnownLocation.latitude):!1)?"Hurricane":"Typhoon";return{type:E,xpBonus:.75,severity:I>177?"extreme":"high",message:`🌀 ${E} conditions detected! Please seek shelter.`,detectedAt:Date.now()}}return B.includes("tornado")?{type:"Tornado",xpBonus:1,severity:"extreme",message:"🌪️ TORNADO WARNING! Take immediate shelter!",detectedAt:Date.now()}:B.includes("thunderstorm")&&I>60?{type:"Tornado",xpBonus:.5,severity:"high",message:"⛈️ Extreme thunderstorm with high winds! Exercise caution.",detectedAt:Date.now()}:null}isTropicalRegion(A){return Math.abs(A)<23.5}getSafetyWarning(A){switch(A.type){case"Blizzard":return A.severity==="extreme"?"🚨 EXTREME BLIZZARD: Stay indoors, avoid travel. Keep emergency supplies ready.":"⚠️ Blizzard: Dress warmly, avoid unnecessary travel. Check on neighbors.";case"Hurricane":case"Typhoon":return A.severity==="extreme"?"🚨 EXTREME CYCLONE: Seek shelter immediately! Follow evacuation orders.":"⚠️ Hurricane/Typhoon: Secure property, prepare emergency kit, follow local alerts.";case"Tornado":return"🚨 TORNADO: Take shelter in basement or interior room immediately! Stay away from windows.";default:return"⚠️ Severe weather detected. Stay informed and stay safe."}}invalidateForecastCache(){this.forecastCache.clear()}invalidateForecastLocation(A,B){for(const[I]of this.forecastCache.entries()){const Q=this.getCacheKey(A,B);I.includes(Q)&&this.forecastCache.delete(I)}}clearExpiredForecastEntries(){let A=0;for(const[B,I]of this.forecastCache.entries())this.isForecastCacheEntryValid(I)||(this.forecastCache.delete(B),A++);return A}}const L={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m"},$o={useColors:!0,compact:!1,showTimestamp:!0,maxFailures:5};function At(g){return g.useColors===!1?!1:typeof process<"u"&&process.stdout?.isTTY===!0}function T(g,A,B){return B?`${A}${g}${L.reset}`:g}function eH(g,A){switch(g){case"healthy":return T("●",L.green,A);case"degraded":return T("●",L.yellow,A);case"failed":return T("●",L.red,A);default:return T("○",L.dim,A)}}function aH(g,A){return g?T("✓ Granted",L.green,A):T("✗ Denied",L.red,A)}function CH(g,A){return g?T("Available",L.green,A):T("Unavailable",L.red,A)}function Bt(g){const A=new Date(g),B=A.getHours().toString().padStart(2,"0"),I=A.getMinutes().toString().padStart(2,"0"),Q=A.getSeconds().toString().padStart(2,"0");return`${B}:${I}:${Q}`}function _Q(g){return g<1e3?`${g}ms`:g<6e4?`${(g/1e3).toFixed(1)}s`:g<36e5?`${Math.floor(g/6e4)}m ${Math.floor(g%6e4/1e3)}s`:`${Math.floor(g/36e5)}h ${Math.floor(g%36e5/6e4)}m`}function Qw(g,A){const B=g+A;return B===0?"N/A":`${(g/B*100).toFixed(1)}%`}function Ie(g,A){const B=[],I=g.totalCalls>0;if(B.push(` Calls: ${g.totalCalls}`),B.push(` Success: ${I?`${g.successRate.toFixed(1)}%`:"N/A"}`),I){const Q=g.average<500?L.green:g.average<1500?L.yellow:L.red;B.push(` Avg Time: ${T(`${g.average}ms`,Q,A)}`),B.push(` Min/Avg/Max: ${g.min}/${g.average}/${g.max}ms`),B.push(` P95/P99: ${g.p95}/${g.p99}ms`)}else B.push(" Avg Time: N/A");return B}function tB(g,A="─"){return A.repeat(g)}function gt(g,A,B){const I=Math.max(0,A-g.length-2),Q=Math.floor(I/2),E=I-Q,i=tB(A),e=B?L.bright+L.cyan+g+L.reset:g;return`
53
+ `)}}function aW(g){return typeof g=="string"&&["humanoid","beast","undead","dragon","fiend","construct","elemental","monstrosity"].includes(g)}function CW(g){return typeof g=="string"&&["common","uncommon","elite","boss"].includes(g)}function oW(g){return typeof g=="string"&&["brute","archer","support"].includes(g)}function tW(g){return typeof g=="string"&&["easy","medium","hard","deadly"].includes(g)}class sW{static calculatePartyLevel(A){if(A.length===0)return 1;const B=A.map(I=>I.level||1);return Rh(B)}static calculatePartyStrength(A){if(A.length===0)return 0;const B=A.reduce((C,o)=>C+(o.hp?.max||10),0),I=this.getAverageAC(A),Q=this.getAverageDamage(A),E=A.length,i=I/10,e=B*i,a=Q*E*10;return e+a}static getXPBudget(A,B){if(A.length===0)return 0;const I=A.map(Q=>Q.level||1);return ei(I,B)}static getAverageAC(A){if(A.length===0)return 10;const B=A.reduce((I,Q)=>I+(Q.armor_class||10),0);return Math.round(B/A.length)}static getAverageHP(A){if(A.length===0)return 10;const B=A.reduce((I,Q)=>I+(Q.hp?.max||10),0);return Math.round(B/A.length)}static getPartySize(A){return A.length}static getAverageDamage(A){if(A.length===0)return 0;const B=A.reduce((I,Q)=>I+this.estimateCharacterDamage(Q),0);return Math.round(B/A.length)}static estimateCharacterDamage(A){const B=A.ability_scores,I=A.equipment?.weapons?.find(e=>e.equipped);let Q,E;if(I?.damage?.dice){Q=I.damage.dice;const e=I.weaponProperties?.includes("ranged")??!1,a=I.weaponProperties?.includes("finesse")??!1;E=Math.floor(((B?.[e||a?"DEX":"STR"]??10)-10)/2)}else Q="1",E=Math.floor(((B?.STR??10)-10)/2);let i;try{const e=RA.parseDiceFormula(Q);i=e.diceCount*((e.diceSides+1)/2)+e.modifier+E}catch{i=E+1}return Math.round(i*10)/10}static analyzeParty(A){const B=this.calculatePartyLevel(A),I=this.getPartySize(A),Q=this.getAverageAC(A),E=this.getAverageHP(A),i=this.getAverageDamage(A),e=this.calculatePartyStrength(A),a=this.getXPBudget(A,"easy"),C=this.getXPBudget(A,"medium"),o=this.getXPBudget(A,"hard"),t=this.getXPBudget(A,"deadly");return{averageLevel:B,partySize:I,averageAC:Q,averageHP:E,averageDamage:i,totalStrength:e,easyXP:a,mediumXP:C,hardXP:o,deadlyXP:t}}}const OA=[];for(let g=0;g<256;++g)OA.push((g+256).toString(16).slice(1));function nW(g,A=0){return(OA[g[A+0]]+OA[g[A+1]]+OA[g[A+2]]+OA[g[A+3]]+"-"+OA[g[A+4]]+OA[g[A+5]]+"-"+OA[g[A+6]]+OA[g[A+7]]+"-"+OA[g[A+8]]+OA[g[A+9]]+"-"+OA[g[A+10]]+OA[g[A+11]]+OA[g[A+12]]+OA[g[A+13]]+OA[g[A+14]]+OA[g[A+15]]).toLowerCase()}let qo;const rW=new Uint8Array(16);function cW(){if(!qo){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");qo=crypto.getRandomValues.bind(crypto)}return qo(rW)}const pd={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function GW(g,A,B){g=g||{};const I=g.random??g.rng?.()??cW();if(I.length<16)throw new Error("Random bytes length must be >= 16");return I[6]=I[6]&15|64,I[8]=I[8]&63|128,nW(I)}function hW(g,A,B){return pd.randomUUID&&!g?pd.randomUUID():GW(g)}class lW{constructor(A={}){this.options={validateAudioUrls:!1,strict:!1,audioUrlValidationTimeout:5e3,resolveImageUrls:!1,...A}}async parse(A){const B=A.name,I=A.image,Q=A.creator,E=A.description,i=A.genre,e=A.tags;let a=I;I&&this.options.resolveImageUrls&&(a=await TA.resolveUrl(I));const C=A.tracks||[],o=[];for(let t=0;t<C.length;t++)try{const s=await this.parseTrack(C[t],t);if(s){const n=await this.resolveTrackUrls(s);o.push(n)}}catch(s){if(this.options.strict)throw s;console.warn(`Failed to parse track at index ${t}:`,s)}return{name:B,description:E,image:a,creator:Q,genre:i,tags:e,version:A.version,playlist_type:A.playlist_type,original_playlist_tx_id:A.original_playlist_tx_id,playlist_artist:A.playlist_artist,platform:A.platform,tracks:o}}async resolveTrackUrls(A){if(!this.options.resolveImageUrls)return A;const B={...A};return B.image_url&&(B.image_url=await TA.resolveUrl(B.image_url)),B.image_thumb_url&&(B.image_thumb_url=await TA.resolveUrl(B.image_thumb_url)),B}async parseTrack(A,B){const I=iA.parseMetadata(A.metadata);if(!I&&this.options.strict)throw new Error(`Failed to parse metadata for track at index ${B}`);const Q=A.chain_name,E=A.token_address,i=A.token_id,e=A.tx_id,a=A.platform,C=A.id||(Q==="AR"?`AR-${e}`:`${Q}-${E}-${i}`),o=A.uuid||hW(),t=iA.extractTitle(I||{}),s=iA.extractArtist(I||{}),r=typeof A.artwork_url=="string"&&A.artwork_url||typeof A.image_url=="string"&&A.image_url||null||iA.extractImageUrl(I||{}),c=iA.extractImageThumbUrl(I||{}),D=typeof A.audio_url=="string"&&A.audio_url||null||iA.extractAudioUrl(I||{}),h=iA.extractAudioUrlLossless(I||{});if(!D){if(this.options.strict)throw new Error(`No audio URL found for track: ${t||C}`);return console.warn(`Track ${C} has no audio URL - marked as Unsummonable`),null}if(this.options.validateAudioUrls&&!await this.validateAudioUrl(D)){if(this.options.strict)throw new Error(`Audio URL validation failed for track: ${t||C}`);return console.warn(`Track ${C} has invalid audio URL - marked as Unsummonable`),null}if(!t||!s||!r){if(this.options.strict)throw new Error(`Missing required fields for track: ${JSON.stringify({title:t,artist:s,imageUrl:r})}`);return null}const w=typeof I?.description=="string"?I.description:void 0,y=typeof I?.album=="string"?I.album:void 0,R=I?.duration?Number(I.duration):0,p=iA.extractGenre(I||{}),k=iA.extractTags(I||{}),W=I?.bpm?Number(I.bpm):void 0,_=typeof I?.key=="string"?I.key:void 0,X=iA.convertAttributes(I?.attributes),z=yD(I||{}),QA={id:C,uuid:o,playlist_index:B,chain_name:Q,platform:a,title:t,artist:s,description:w,album:y,image_url:r,audio_url:D,audio_url_lossless:h&&h!==D?h:void 0,duration:R,genre:p,tags:k,bpm:W,key:_,attributes:X||void 0,extras:z};return c&&(QA.image_thumb_url=c),typeof I?.audio_ipfs_hash=="string"?QA.audio_ipfs_hash=A.audio_ipfs_hash||I.audio_ipfs_hash:A.audio_ipfs_hash&&(QA.audio_ipfs_hash=A.audio_ipfs_hash),typeof I?.artwork_ipfs_hash=="string"?QA.artwork_ipfs_hash=A.artwork_ipfs_hash||I.artwork_ipfs_hash:A.artwork_ipfs_hash&&(QA.artwork_ipfs_hash=A.artwork_ipfs_hash),Q!=="AR"?(QA.token_address=E,QA.token_id=i):e&&(QA.tx_id=e),QA}async validateAudioUrl(A){const B=new AbortController,I=setTimeout(()=>B.abort(),this.options.audioUrlValidationTimeout);try{const Q=await fetch(A,{method:"HEAD",signal:B.signal});return clearTimeout(I),Q.ok}catch(Q){return clearTimeout(I),Q instanceof Error&&Q.name==="AbortError"?console.warn(`Audio URL validation timed out after ${this.options.audioUrlValidationTimeout}ms: ${A}`):console.warn(`Audio URL validation failed for ${A}:`,Q),!1}}}class rg{static separateFrequencyBands(A,B){const I=A.length,Q=B/2/I,E=[],i=[],e=[];for(let a=0;a<I;a++){const C=a*Q,o=A[a]/255;C>=20&&C<400?E.push(o):C>=400&&C<4e3?i.push(o):C>=4e3&&C<=14e3&&e.push(o)}return{bass:E,mid:i,treble:e}}static calculateDominance(A,B){if(A.length===0)return 0;const Q=A.reduce((E,i)=>E+i,0)/A.length;return B===void 0?Q:Q/(B/1e3)}}const Pi={targetSampleRate:8e3,fftWindowSize:32,hopSizeMs:4,hopSizeMode:{mode:"standard"},melBands:40,melBandsMode:{mode:"standard"},highPassCutoff:.4,gaussianSmoothMs:20,gaussianSmoothMode:{mode:"standard"}};class Yd{constructor(A={}){const B=A.hopSizeMode?iC(A.hopSizeMode):A.hopSizeMs??Pi.hopSizeMs,I=A.melBandsMode?eC(A.melBandsMode):A.melBands??Pi.melBands,Q=A.gaussianSmoothMode?aC(A.gaussianSmoothMode):A.gaussianSmoothMs??Pi.gaussianSmoothMs;this.config={...Pi,...A,hopSizeMs:B,melBands:I,gaussianSmoothMs:Q}}calculate(A){const B=XQ(A,this.config.targetSampleRate),I=B.targetSampleRate,Q=A.duration,E=Math.round(this.config.fftWindowSize/1e3*I),i=Math.pow(2,Math.ceil(Math.log2(E))),e=Math.round(this.config.hopSizeMs/1e3*I),a=this.config.hopSizeMs/1e3,C=Yo(B.data,i,e,I),o=vD(this.config.melBands,i,I),t=C.numFrames,s=[];for(let w=0;w<t;w++){const y=C.frames[w],R=new Float32Array(this.config.melBands);for(let p=0;p<this.config.melBands;p++){const k=o[p];let W=0;for(let _=0;_<k.length&&_<y.length;_++)W+=y[_]*k[_];R[p]=20*Math.log10(W+1e-10)}s.push(R)}const n=new Float32Array(t);n[0]=0;for(let w=1;w<t;w++){let y=0;for(let R=0;R<this.config.melBands;R++){const p=s[w][R]-s[w-1][R];p>0&&(y+=p)}n[w]=y}const r=1/a,c=XD(n,this.config.highPassCutoff,r),G=po(c,this.config.gaussianSmoothMs,r),D=KD(G),h=new Float32Array(G.length);if(D>1e-10)for(let w=0;w<G.length;w++)h[w]=G[w]/D;else for(let w=0;w<G.length;w++)h[w]=G[w];return{envelope:h,numFrames:t,hopSizeSeconds:a,effectiveSampleRate:I,duration:Q}}getConfig(){return{...this.config}}findPeaks(A,B=.5){const I=[];for(let Q=1;Q<A.length-1;Q++)A[Q]>A[Q-1]&&A[Q]>A[Q+1]&&A[Q]>=B&&I.push(Q);return I}frameToTime(A,B){return A*B}timeToFrame(A,B){return Math.round(A/B)}}const DW={tempoCenter:.5,tempoWidth:1.4,minBpm:90,maxBpm:180,useOctaveResolution:!1,useTripleMeter:!1};class Ud{constructor(A={}){this.config={...DW,...A}}estimateTempo(A,B){if(A.length<10)return this.getDefaultEstimate();const I=Math.round(60/this.config.minBpm/B),Q=Math.round(60/this.config.maxBpm/B),E=Math.max(Q,2),i=Math.min(I,Math.floor(A.length/2));if(i<=E)return this.getDefaultEstimate();const e=this.computeAutocorrelation(A,E,i),a=this.applyPerceptualWeighting(e.values,e.minLag,B);let C=this.findPeak(a,0,a.length);if(this.config.useOctaveResolution){const y=this.calculateTPS2(e.values,C),R=Math.floor(C/2);R>=1&&R<a.length&&this.calculateTPS2(e.values,R)>y&&(C=R)}if(this.config.useTripleMeter){const y=this.calculateTPS3(e.values,C),R=Math.floor(C/3);R>=1&&R<a.length&&this.calculateTPS3(e.values,R)>y&&(C=R)}const o=this.lagToBpm(C+e.minLag,B),t=o/2,s=a[C]||0,n=Math.min(C*2,a.length-1),r=a[n]||0,c=Math.max(Math.floor(C/2),0),G=a[c]||0,D=1,h=Math.min(Math.max(Math.max(r,G)/(s+.001),0),1),w=60/o;return{primaryBpm:o,secondaryBpm:t,primaryWeight:D,secondaryWeight:h,targetIntervalSeconds:w}}getConfig(){return{...this.config}}computeAutocorrelation(A,B,I){const Q=I-B+1,E=new Float32Array(Q);let i=0;for(let e=0;e<A.length;e++)i+=A[e];i/=A.length;for(let e=B;e<=I;e++){let a=0,C=0;for(let o=e;o<A.length;o++)a+=(A[o]-i)*(A[o-e]-i),C++;E[e-B]=C>0?a/C:0}return{values:E,minLag:B,maxLag:I}}applyPerceptualWeighting(A,B,I){const Q=new Float32Array(A.length),E=this.config.tempoCenter/I,i=this.config.tempoWidth;for(let e=0;e<A.length;e++){const a=B+e;if(a<=0||E<=0){Q[e]=0;continue}const C=Math.log2(a/E),o=Math.exp(-.5*Math.pow(C/i,2));Q[e]=A[e]*o}return Q}findPeak(A,B,I){let Q=B,E=A[B];for(let i=B+1;i<I&&i<A.length;i++)A[i]>E&&(E=A[i],Q=i);return Q}lagToBpm(A,B){return A<=0||B<=0?120:60/(A*B)}calculateTPS2(A,B){const I=A[B]||0,Q=B*2,E=Q-1,i=Q+1,e=A[Q]||0,a=E>=0&&A[E]||0,C=i<A.length&&A[i]||0;return I+.5*e+.25*a+.25*C}calculateTPS3(A,B){const I=A[B]||0,Q=B*3,E=Q-1,i=Q+1,e=A[Q]||0,a=E>=0&&A[E]||0,C=i<A.length&&A[i]||0;return I+.33*e+.33*a+.33*C}getDefaultEstimate(){return{primaryBpm:120,secondaryBpm:60,primaryWeight:1,secondaryWeight:.5,targetIntervalSeconds:.5}}getTempoCandidates(A,B,I=5){if(A.length<10)return[{bpm:120,strength:1}];const Q=Math.round(60/this.config.minBpm/B),E=Math.round(60/this.config.maxBpm/B),i=Math.max(E,2),e=Math.min(Q,Math.floor(A.length/2));if(e<=i)return[{bpm:120,strength:1}];const a=this.computeAutocorrelation(A,i,e),C=this.applyPerceptualWeighting(a.values,a.minLag,B),o=[];for(let t=1;t<C.length-1;t++)if(C[t]>C[t-1]&&C[t]>C[t+1]){const s=this.lagToBpm(t+a.minLag,B);o.push({bpm:s,strength:C[t]})}return o.sort((t,s)=>s.strength-t.strength),o.slice(0,I)}}const _i=VA.for("BeatTracker"),dW={dpAlpha:680,sensitivity:1,minPredecessorRatio:.5,maxPredecessorRatio:2};class fd{constructor(A={}){this.config={...dW,...A}}getConfig(){return{...this.config}}trackBeats(A,B,I){const Q=A.length;if(Q<10)return{beats:[],beatFrames:[],cumulativeScores:new Float32Array(Q)};const E=Math.round(B.targetIntervalSeconds/I);if(_i.debug("BeatTracker: Beat tracking parameters",{targetIntervalSeconds:B.targetIntervalSeconds,hopSizeSeconds:I,periodFrames:E,envelopeLength:Q}),E<2||Q<E*2)return{beats:[],beatFrames:[],cumulativeScores:new Float32Array(Q)};const i=this.config.sensitivity,e=Math.round(this.config.dpAlpha/i),a=Math.max(10,Math.min(1e4,e));_i.debug("Beat tracking parameters",{sensitivity:i,dpAlpha:this.config.dpAlpha,effectiveDpAlpha:a});const C=Math.max(1,Math.round(E*this.config.minPredecessorRatio)),o=Math.round(E*this.config.maxPredecessorRatio),t=new Float32Array(o-C+1);for(let w=0;w<t.length;w++){const y=C+w,R=Math.log(y/E);t[w]=-a*R*R}const s=new Int32Array(Q);s.fill(-1);const n=new Float32Array(A);for(let w=o+1;w<Q;w++){let y=-1/0,R=-1;for(let p=0;p<t.length;p++){const k=w-(C+p);if(k>=0&&k<Q){const W=t[p]+n[k];W>y&&(y=W,R=k)}}n[w]=y+A[w],s[w]=R}_i.debug("BeatTracker: Starting backward pass",{maxCumulativeScore:Math.max(...n),envelopeLength:Q});let r=0,c=n[0];for(let w=1;w<Q;w++)n[w]>c&&(c=n[w],r=w);const G=[];let D=r;for(;D>=0&&s[D]>=0;)G.unshift(D),D=s[D];return D>=0&&A[D]>0&&G.unshift(D),_i.debug("BeatTracker: Backward pass complete",{beatsFound:G.length,bestEndFrame:r,firstBeatFrame:G[0],lastBeatFrame:G[G.length-1]}),{beats:this.convertToBeats(G,A,n,I,E),beatFrames:G,cumulativeScores:n}}convertToBeats(A,B,I,Q,E){if(A.length===0)return[];const i=[];let e=0;for(let o=0;o<B.length;o++)B[o]>e&&(e=B[o]);e<=0&&(e=1);let a=0;for(let o=0;o<A.length;o++)a+=B[A[o]];const C=a/A.length;for(let o=0;o<A.length;o++){const t=A[o],s=t*Q,n=Math.max(0,Math.min(1,B[t]/e)),r=B[t],c=C>0?r/C:.5,G=Math.max(0,Math.min(1,.5+.3*(c-1)));i.push({timestamp:s,beatInMeasure:0,isDownbeat:!1,measureNumber:0,intensity:n,confidence:G})}return i}trackBeatsWithOptions(A,B,I,Q={}){const E=this.trackBeats(A,B,I);if(Q.applyTrimming&&E.beatFrames.length>0&&this.applyDiscountedScoreTrimming(E,A.length,Math.round(B.targetIntervalSeconds/I)),Q.minScoreThreshold!==void 0){const i=E.beats.filter((e,a)=>{const C=E.beatFrames[a];return E.cumulativeScores[C]>=Q.minScoreThreshold});E.beats=i,E.beatFrames=E.beatFrames.filter((e,a)=>{const C=E.beatFrames[a];return E.cumulativeScores[C]>=Q.minScoreThreshold})}return E}applyDiscountedScoreTrimming(A,B,I){if(A.beatFrames.length<3)return;const Q=A.cumulativeScores,E=Q[B-1],i=new Float32Array(B);for(let s=0;s<B;s++)i[s]=Q[s]-s/B*E;let e=0,a=B-1;for(let s=0;s<B;s++)if(i[s]>0){e=s;break}for(let s=B-1;s>=0;s--)if(i[s]>0){a=s;break}const C=Math.floor(I/2),o=[],t=[];for(let s=0;s<A.beatFrames.length;s++){const n=A.beatFrames[s];n>=e-C&&n<=a+C&&(o.push(A.beats[s]),t.push(n))}A.beats=o,A.beatFrames=t}getTrackingStats(A,B){const{beats:I,beatFrames:Q}=A;if(I.length===0)return{numBeats:0,avgInterval:0,stdInterval:0,avgIntensity:0,avgConfidence:0,estimatedBpm:0};const E=[];for(let t=1;t<Q.length;t++)E.push((Q[t]-Q[t-1])*B);const i=E.length>0?E.reduce((t,s)=>t+s,0)/E.length:0,e=E.length>0?Math.sqrt(E.reduce((t,s)=>t+Math.pow(s-i,2),0)/E.length):0,a=I.reduce((t,s)=>t+s.intensity,0)/I.length,C=I.reduce((t,s)=>t+s.confidence,0)/I.length,o=i>0?60/i:0;return{numBeats:I.length,avgInterval:i,stdInterval:e,avgIntensity:a,avgConfidence:C,estimatedBpm:o}}}const kd=VA.for("BeatMapGenerator");class _B{constructor(A={}){this.state=null;const B={...fQ,...A},I=A.hopSizeMode,Q=A.melBandsMode,E=A.gaussianSmoothMode,i=I?iC(I):A.hopSizeMs??fQ.hopSizeMs,e=Q?eC(Q):A.melBands??fQ.melBands,a=E?aC(E):A.gaussianSmoothMs??fQ.gaussianSmoothMs;this.options={...B,hopSizeMs:i,melBands:e,gaussianSmoothMs:a,...I?{hopSizeMode:I}:{},...Q?{melBandsMode:Q}:{},...E?{gaussianSmoothMode:E}:{}}}getConfig(){return{...this.options}}async generateBeatMap(A,B,I,Q){this.state={cancelled:!1,progress:{phase:"loading",progress:0,message:"Loading audio..."}};try{this.updateProgress("loading",0,"Loading audio...");const E=await this.fetchAndDecode(A);if(this.state.cancelled)throw new Error("Generation cancelled");return await this.generateBeatMapFromBuffer(E,B,I,Q)}finally{this.state=null}}async generateBeatMapFromBuffer(A,B,I,Q){this.state||(this.state={cancelled:!1,progress:{phase:"preprocessing",progress:0,message:"Starting..."}}),I&&gC(I);try{const E=A.duration;if(this.updateProgress("preprocessing",5,"Preparing audio..."),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("ose_calculation",10,"Calculating onset strength envelope..."),this.state.cancelled)throw new Error("Generation cancelled");const e=new Yd({targetSampleRate:8e3,fftWindowSize:32,hopSizeMs:this.options.hopSizeMs,melBands:this.options.melBands,highPassCutoff:this.options.highPassCutoff,gaussianSmoothMs:this.options.gaussianSmoothMs}).calculate(A);if(this.updateProgress("ose_calculation",35,"Onset strength envelope calculated."),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("tempo_estimation",40,"Estimating tempo..."),this.state.cancelled)throw new Error("Generation cancelled");const C=new Ud({tempoCenter:this.options.tempoCenter,tempoWidth:this.options.tempoWidth,minBpm:this.options.minBpm,maxBpm:this.options.maxBpm,useOctaveResolution:this.options.useOctaveResolution}).estimateTempo(e.envelope,e.hopSizeSeconds);if(this.updateProgress("tempo_estimation",55,`Tempo estimated: ${Math.round(C.primaryBpm)} BPM`),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("beat_tracking",60,"Tracking beats..."),this.state.cancelled)throw new Error("Generation cancelled");const t=new fd({dpAlpha:this.options.dpAlpha,sensitivity:this.options.sensitivity}).trackBeats(e.envelope,C,e.hopSizeSeconds);if(this.updateProgress("beat_tracking",85,`${t.beats.length} beats detected.`),this.state.cancelled)throw new Error("Generation cancelled");if(this.updateProgress("measure_labeling",87,"Applying measure labels..."),this.state.cancelled)throw new Error("Generation cancelled");const s=I??_E;I&&IC(I,t.beats.length);const n=this.applyMeasureLabels(t.beats,s);if(this.updateProgress("measure_labeling",97,"Measure labels applied."),this.state.cancelled)throw new Error("Generation cancelled");this.updateProgress("finalizing",98,"Finalizing beat map...");let r=n;const c=this.options.filter;c>0&&(r=this.filterBeatsByGridAlignment(r,C,c));const G=r.length;r=this.applyIntensityThreshold(r),kd.debug("BeatMapGenerator: Intensity filter applied",{beatsBefore:G,beatsAfter:r.length,beatsFiltered:G-r.length,noiseFloorThreshold:this.options.noiseFloorThreshold});const D={version:$G,algorithm:Ah,minBpm:this.options.minBpm,maxBpm:this.options.maxBpm,sensitivity:this.options.sensitivity,filter:this.options.filter,noiseFloorThreshold:this.options.noiseFloorThreshold,hopSizeMs:this.options.hopSizeMs,fftSize:this.options.fftSize,dpAlpha:this.options.dpAlpha,melBands:this.options.melBands,highPassCutoff:this.options.highPassCutoff,gaussianSmoothMs:this.options.gaussianSmoothMs,tempoCenter:this.options.tempoCenter,tempoWidth:this.options.tempoWidth,useOctaveResolution:this.options.useOctaveResolution??!1,useTripleMeter:this.options.useTripleMeter??!1,generatedAt:new Date().toISOString()},h={audioId:B,duration:E,beats:r,bpm:C.primaryBpm,metadata:D,...I?{downbeatConfig:I}:{}};return this.updateProgress("complete",100,"Beat map generation complete."),Q?.(this.state.progress),h}finally{this.state=null}}getProgress(){return this.state?.progress??null}cancel(){this.state&&(this.state.cancelled=!0,this.state.progress={phase:"error",progress:this.state.progress.progress,message:"Generation cancelled",error:"Cancelled by user"})}applyIntensityThreshold(A){const B=this.options.noiseFloorThreshold;return A.filter(I=>I.intensity>=B)}applyMeasureLabels(A,B){const I=this.computeMeasureOffsets(B.segments,A.length);return A.map((Q,E)=>{const i=this.findActiveSegmentIndex(B.segments,E),e=B.segments[i],{downbeatBeatIndex:a,timeSignature:C}=e,{beatsPerMeasure:o}=C,t=E-a,s=(t%o+o)%o,n=s===0,c=Math.max(0,Math.floor(t/o))+I[i];return{...Q,beatInMeasure:s,isDownbeat:n,measureNumber:c}})}computeMeasureOffsets(A,B){const I=[];for(let Q=0;Q<A.length;Q++)if(Q===0)I.push(0);else{const E=A[Q-1],{downbeatBeatIndex:i,timeSignature:e}=E,a=e.beatsPerMeasure,o=A[Q].startBeat-1-i,t=Math.max(0,Math.floor(o/a));I.push(t+1)}return I}findActiveSegmentIndex(A,B){let I=0;for(let Q=0;Q<A.length&&A[Q].startBeat<=B;Q++)I=Q;return I}filterBeatsByGridAlignment(A,B,I){if(I<=0)return A;const Q=60/B.primaryBpm,E=(1-I)*(Q/2),i=A.filter(e=>{const C=Math.round(e.timestamp/Q)*Q;return Math.abs(e.timestamp-C)<=E});return kd.debug("Filtered beats by grid alignment",{originalCount:A.length,filteredCount:i.length,threshold:I,maxDeviationMs:E*1e3}),i}updateProgress(A,B,I){this.state&&(this.state.progress={phase:A,progress:B,message:I})}async fetchAndDecode(A){const B=await fetch(A);if(!B.ok)throw new Error(`Failed to fetch audio: ${B.statusText}`);const I=await B.arrayBuffer(),Q=globalThis.AudioContext||window?.AudioContext;if(!Q)throw new Error("AudioContext not available in this environment");const E=new Q;return await new Promise((i,e)=>{E.decodeAudioData(I,i,e)})}static toJSON(A){const B={audioId:A.audioId,duration:A.duration,beats:A.beats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),bpm:A.bpm,metadata:A.metadata};return JSON.stringify(B,null,2)}static fromJSON(A){const B=JSON.parse(A);return{audioId:B.audioId,duration:B.duration,beats:B.beats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),bpm:B.bpm,metadata:B.metadata}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=_B.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return _B.fromJSON(I)}}function wW(g){return"mergedBeats"in g&&"detectedBeats"in g}function FW(g){return"subdivisionConfig"in g&&"subdivisionMetadata"in g}class Zd{constructor(A,B,I={},Q=8){this.beatMap=A,this.audioContext=B,this.options={...OG,...I},this.rollingBpmWindowSize=Q,this.normalizedBeatMap=this.createNormalizedBeatMap(A),this.state={isRunning:!1,currentTime:0,startTime:0,isPaused:!1,pauseTime:0,rafId:null,lastButtonPress:null,thresholds:this.resolveThresholds()},this.subscribers=new Set,this.scheduledBeats=this.initializeScheduledBeats()}createNormalizedBeatMap(A){if(FW(A)){let B=120;if(A.beats.length>=2){const Q=A.beats[1].timestamp-A.beats[0].timestamp;Q>0&&(B=60/Q)}const I={version:"1.0.0",algorithm:"subdivision",minBpm:B,maxBpm:B,sensitivity:1,filter:0,noiseFloorThreshold:0,hopSizeMs:4,fftSize:2048,dpAlpha:680,melBands:40,highPassCutoff:.4,gaussianSmoothMs:20,tempoCenter:.5,tempoWidth:1.4,useOctaveResolution:!1,useTripleMeter:!1,generatedAt:new Date().toISOString()};return{audioId:A.audioId,duration:A.duration,beats:A.beats,bpm:B,metadata:I,downbeatConfig:A.downbeatConfig}}else return wW(A)?this.options.useInterpolatedBeats?{audioId:A.audioId,duration:A.duration,beats:A.mergedBeats,bpm:A.quarterNoteBpm,metadata:A.originalMetadata}:{audioId:A.audioId,duration:A.duration,beats:A.detectedBeats,bpm:A.quarterNoteBpm,metadata:A.originalMetadata}:A}initializeScheduledBeats(){return this.normalizedBeatMap.beats.map((A,B)=>({beat:A,index:B,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1}))}getOptions(){return{...this.options}}getAccuracyThresholds(){return{...this.state.thresholds}}setDifficulty(A){A.preset!==void 0&&(this.options.difficultyPreset=A.preset),A.customThresholds!==void 0&&(this.options.customThresholds=A.customThresholds),this.state.thresholds=this.resolveThresholds()}subscribe(A){return this.subscribers.add(A),()=>{this.subscribers.delete(A)}}start(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate())}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.scheduledBeats=this.initializeScheduledBeats()}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate())}seek(A){const B=Math.max(0,Math.min(A,this.normalizedBeatMap.duration));this.state.pauseTime=B,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-B),this.scheduledBeats=this.initializeScheduledBeats()}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let A=0;if(this.options.compensateOutputLatency){const B=this.audioContext.outputLatency??0,I=this.audioContext.baseLatency??0;A+=B+I}return A+=this.options.userOffsetMs/1e3,A}getSyncState(){const A=this.audioContext.currentTime,B=this.getCurrentAudioTime(),I=this.audioContext.outputLatency??0,Q=this.audioContext.baseLatency??0,E=A-this.state.startTime,i=B-E-this.getTotalLatencyCompensation();return{audioContextTime:A,audioElementTime:B,drift:i,isSynchronized:Math.abs(i)<=this.options.timingTolerance,outputLatency:I,baseLatency:Q,userOffsetMs:this.options.userOffsetMs,totalCompensation:this.getTotalLatencyCompensation()}}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const A=this.getCurrentAudioTime(),B=this.getCurrentBpm();for(const I of this.scheduledBeats){const E=I.beat.timestamp-A;!I.upcomingEmitted&&E<=this.options.anticipationTime&&E>0&&(this.emitEvent(I,"upcoming",A,B,E),I.upcomingEmitted=!0),!I.exactEmitted&&Math.abs(E)<=this.options.timingTolerance&&(this.emitEvent(I,"exact",A,B,E),I.exactEmitted=!0),!I.passedEmitted&&E<-this.options.timingTolerance&&(this.emitEvent(I,"passed",A,B,E),I.passedEmitted=!0)}}emitEvent(A,B,I,Q,E){const i={beat:A.beat,currentBpm:Q,audioTime:I,timeUntilBeat:E,type:B};for(const e of this.subscribers)try{e(i)}catch(a){console.error("BeatStream callback error:",a)}}getUpcomingBeats(A){const B=this.getCurrentAudioTime(),I=[];for(const Q of this.normalizedBeatMap.beats){const E=Q.timestamp-B;if(E>=0&&E<=this.options.anticipationTime&&(I.push(Q),I.length>=A))break}return I}getBeatAtTime(A){const B=this.options.timingTolerance;for(const I of this.normalizedBeatMap.beats)if(Math.abs(I.timestamp-A)<=B)return I;return null}getCurrentBeat(){const A=this.getCurrentAudioTime();let B=null;for(const I of this.normalizedBeatMap.beats)if(I.timestamp<=A)B=I;else break;return B}getNextBeat(){const A=this.getCurrentAudioTime();for(const B of this.normalizedBeatMap.beats)if(B.timestamp>A)return B;return null}getCurrentBpm(){const A=this.getCurrentAudioTime(),B=[];for(const a of this.normalizedBeatMap.beats)a.timestamp<=A+.1&&B.push(a);if(B.length<2)return this.normalizedBeatMap.bpm;const I=Math.min(this.rollingBpmWindowSize,B.length),Q=B.slice(-I);let E=0;for(let a=1;a<Q.length;a++)E+=Q[a].timestamp-Q[a-1].timestamp;const e=60/(E/(Q.length-1));return Math.max(30,Math.min(300,e))}resolveThresholds(){return this.options.customThresholds&&Object.keys(this.options.customThresholds).length>0?{...$E(this.options.difficultyPreset||"medium"),...this.options.customThresholds}:$E(this.options.difficultyPreset||"medium")}checkButtonPress(A,B){let I=null,Q=1/0;for(const n of this.normalizedBeatMap.beats){const r=A-n.timestamp,c=Math.abs(r);c<Q&&(Q=c,I=n)}if(!I){const n={accuracy:"miss",offset:A,matchedBeat:null,absoluteOffset:A,keyMatch:!0,pressedKey:B,requiredKey:void 0};return this.state.lastButtonPress=n,n}const E=A-I.timestamp,i=Math.abs(E);let e;const a=this.state.thresholds;i<=a.perfect?e="perfect":i<=a.great?e="great":i<=a.good?e="good":i<=a.ok?e="ok":e="miss";const C=this.options.ignoreKeyRequirements??!1,o=I.requiredKey!==void 0;let t=!0;!C&&o&&(B===void 0?(e="miss",t=!1):B!==I.requiredKey&&(e="wrongKey",t=!1));const s={accuracy:e,offset:E,matchedBeat:I,absoluteOffset:i,keyMatch:t,pressedKey:B,requiredKey:I.requiredKey};return this.state.lastButtonPress=s,s}getLastBeatAccuracy(){return this.state.lastButtonPress}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.normalizedBeatMap.duration}getBeatMap(){return this.beatMap}getNormalizedBeatMap(){return this.normalizedBeatMap}setBeatMap(A){this.beatMap=A,this.normalizedBeatMap=this.createNormalizedBeatMap(A),this.scheduledBeats=this.initializeScheduledBeats()}dispose(){this.stop(),this.subscribers.clear()}}const iB=VA.for("BeatInterpolator");class jQ{constructor(A={}){this.options={...jG,...A}}getConfig(){return{...this.options}}interpolate(A){const{beats:B,audioId:I,duration:Q,metadata:E,bpm:i,downbeatConfig:e}=A;if(iB.debug("Starting beat interpolation",{audioId:I,detectedBeats:B.length,duration:Q,originalBpm:i}),B.length===0)return iB.warn("No beats to interpolate"),this.createEmptyInterpolatedBeatMap(A);if(B.length===1)return iB.warn("Only one beat, cannot determine quarter note"),this.createSingleBeatInterpolatedBeatMap(A);const a=this.detectQuarterNote(B);iB.debug("Quarter note detected",{intervalSeconds:a.intervalSeconds,bpm:a.bpm,confidence:a.confidence,method:a.method,denseSectionCount:a.denseSectionCount});const C=this.analyzeGaps(B,a.intervalSeconds);iB.debug("Gap analysis complete",{totalGaps:C.totalGaps,halfNoteGaps:C.halfNoteGaps,anomalies:C.anomalies.length,gridAlignmentScore:C.gridAlignmentScore});const o=this.generateGrid(A,a,C);let t=this.mergeBeats(B,o,e);const s=t.filter(p=>p.source==="interpolated").length,n=s>0?t.filter(p=>p.source==="interpolated").reduce((p,k)=>p+k.confidence,0)/s:0,r=this.calculateTempoDriftRatio(t),c=this.identifyTempoClusters(B),G=c.map(p=>Math.round(p.bpm)),D=this.filterOctaveMultiples(G),h=D.length>1;let w,y;if(this.options.enableMultiTempo&&h){const p=this.runMultiTempoAnalysis(B,c,Q);p&&(w=p.tempoSections,y=!0,w.length>1&&(t=this.reinterpolateBoundaryRegions(t,w,B,e)),iB.debug("Multi-tempo analysis complete",{sectionCount:w.length,tempos:w.map(k=>k.bpm)}))}const R={quarterNoteDetection:a,gapAnalysis:C,detectedBeatCount:B.length,interpolatedBeatCount:s,totalBeatCount:t.length,interpolationRatio:t.length>0?s/t.length:0,avgInterpolatedConfidence:n,tempoDriftRatio:r,detectedClusterTempos:D.length>0?D:void 0,hasMultipleTempos:h,tempoSections:w,hasMultiTempoApplied:y};return iB.debug("Interpolation complete",{detectedBeats:B.length,interpolatedBeats:s,totalBeats:t.length,interpolationRatio:R.interpolationRatio.toFixed(2),hasMultipleTempos:h,hasMultiTempoApplied:y}),{audioId:I,duration:Q,detectedBeats:[...B],mergedBeats:t,quarterNoteInterval:a.intervalSeconds,quarterNoteBpm:a.bpm,quarterNoteConfidence:a.confidence,originalMetadata:E,interpolationMetadata:R,downbeatConfig:e}}detectQuarterNote(A){const B=this.identifyDenseSections(A);iB.debug("Identified dense sections",{count:B.length,totalBeats:B.reduce((s,n)=>s+n.beatCount,0)});const I=this.calculateWeightedIntervals(A,B),Q=this.buildIntervalHistogram(I),E=this.findHistogramPeaks(Q);if(E.length===0){const s=this.calculateAverageInterval(A);return{intervalSeconds:s,bpm:60/s,confidence:.3,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:B.length,denseSectionBeats:B.reduce((n,r)=>n+r.beatCount,0)}}const i=E[0],e=E.slice(1,4).map(s=>s.interval),a=I.reduce((s,n)=>s+n.weight,0),C=a>0?i.weight/a:0,o=B.reduce((s,n)=>s+n.beatCount,0)/A.length,t=Math.min(1,C*.6+o*.4);return{intervalSeconds:i.interval,bpm:60/i.interval,confidence:t,histogramPeak:i.weight,secondaryPeaks:e,method:"histogram",denseSectionCount:B.length,denseSectionBeats:B.reduce((s,n)=>s+n.beatCount,0)}}identifyDenseSections(A){const B=this.options.denseSectionMinBeats,I=[];if(A.length<B)return I;const Q=[];for(let e=1;e<A.length;e++)Q.push(A[e].timestamp-A[e-1].timestamp);let E=0;for(let e=1;e<Q.length;e++){const a=Q.slice(E,e+1),C=a.reduce((s,n)=>s+n,0)/a.length,o=a.reduce((s,n)=>s+Math.pow(n-C,2),0)/a.length,t=Math.pow(C*.2,2);if(o>t){const s=e-E+1;if(s>=B){const n=Q.slice(E,e),r=n.reduce((G,D)=>G+D,0)/n.length,c=n.reduce((G,D)=>G+Math.pow(D-r,2),0)/n.length;I.push({startIndex:E,endIndex:e-1,beatCount:s,avgInterval:r,intervalVariance:c})}E=e}}const i=Q.length-E+1;if(i>=B){const e=Q.slice(E),a=e.reduce((o,t)=>o+t,0)/e.length,C=e.reduce((o,t)=>o+Math.pow(t-a,2),0)/e.length;I.push({startIndex:E,endIndex:Q.length-1,beatCount:i,avgInterval:a,intervalVariance:C})}return I}identifyTempoClusters(A){const B=this.identifyDenseSections(A);if(B.length===0)return[];const I=this.options.tempoSectionThreshold,Q=this.options.minClusterBeats,E=B.map(e=>({...e,bpm:60/e.avgInterval,isVerified:e.beatCount>=Q}));if(E.length===1)return E[0].isVerified?E:[];E.sort((e,a)=>e.startIndex-a.startIndex);const i=[E[0]];for(let e=1;e<E.length;e++){const a=E[e],C=i[i.length-1],o=C.endIndex+1>=a.startIndex,s=1-Math.min(C.avgInterval,a.avgInterval)/Math.max(C.avgInterval,a.avgInterval)<=I;if(o&&s){const n=[];for(let G=C.startIndex;G<=a.endIndex&&G<A.length-1;G++)n.push(A[G+1].timestamp-A[G].timestamp);const r=n.reduce((G,D)=>G+D,0)/n.length,c=n.reduce((G,D)=>G+Math.pow(D-r,2),0)/n.length;i[i.length-1]={startIndex:C.startIndex,endIndex:a.endIndex,beatCount:C.beatCount+a.beatCount,avgInterval:r,intervalVariance:c,bpm:60/r,isVerified:C.beatCount+a.beatCount>=Q}}else i.push(a)}return i.filter(e=>e.isVerified)}findConflictingClusters(A){if(A.length<2)return null;const B=this.options.tempoSectionThreshold,I=[];for(let Q=0;Q<A.length;Q++)for(let E=Q+1;E<A.length;E++){const i=A[Q],e=A[E];if(this.isOctaveMultiple(i.bpm,e.bpm))continue;1-Math.min(i.bpm,e.bpm)/Math.max(i.bpm,e.bpm)>B&&I.push({cluster1:i,cluster2:e})}return I.length>0?I:null}isOctaveMultiple(A,B,I=.1){const Q=[.5,2],E=A/B;return Q.some(i=>Math.abs(E-i)<=I)}filterOctaveMultiples(A){if(A.length<=1)return A;const B=[...A].sort((Q,E)=>Q-E),I=[];for(const Q of B)I.some(i=>this.isOctaveMultiple(Q,i))||I.push(Q);return I}findCrossingPoint(A,B,I){const Q=this.options.tempoSectionThreshold,E=this.options.tempoAdaptationRate,i=I.slice(A.startIndex,A.endIndex+1),e=I.slice(B.startIndex,B.endIndex+1),a=I.slice(A.endIndex+1,B.startIndex),C=i[i.length-1],o=e[0];if(a.length===0){const r=(C.timestamp+o.timestamp)/2;return iB.debug("No connecting beats between clusters → automatic boundary",{cluster1Bpm:A.bpm,cluster2Bpm:B.bpm,boundaryTimestamp:r}),{hasBoundary:!0,boundaryTimestamp:r,gapRatio:1,beatsInSection1:i,beatsInSection2:e}}const t=this.interpolateForwardsWithDrift(C.timestamp,A.avgInterval,a,E,o.timestamp),s=this.interpolateBackwardsWithDrift(o.timestamp,B.avgInterval,a,E,C.timestamp),n=this.measureGapAtCrossing(t,s,A.avgInterval,B.avgInterval);if(iB.debug("Crossing point analysis",{cluster1Bpm:A.bpm,cluster2Bpm:B.bpm,connectingBeats:a.length,gapRatio:n.gapRatio.toFixed(3),threshold:Q,hasBoundary:n.gapRatio>Q}),n.gapRatio>Q){const r=n.crossingTimestamp,{beatsInSection1:c,beatsInSection2:G}=this.assignBeatsToSections(a,A,B,r);return{hasBoundary:!0,boundaryTimestamp:r,gapRatio:n.gapRatio,beatsInSection1:[...i,...c],beatsInSection2:[...G,...e]}}else return{hasBoundary:!1,gapRatio:n.gapRatio,beatsInSection1:[...i,...a,...e],beatsInSection2:[]}}measureGapAtCrossing(A,B,I,Q){const E=A.endTimestamp,i=B.endTimestamp,e=(E+i)/2,a=A.beatPositions,C=B.beatPositions;let o=E,t=1/0;for(const D of a){const h=Math.abs(D-e);h<t&&(t=h,o=D)}let s=i,n=1/0;for(const D of C){const h=Math.abs(D-e);h<n&&(n=h,s=D)}a.length===0&&(o=E),C.length===0&&(s=i);const r=Math.abs(o-s),c=(I+Q)/2;return{gapRatio:c>0?r/c:0,crossingTimestamp:e,forwardsInterval:A.finalInterval,backwardsInterval:B.finalInterval}}assignBeatsToSections(A,B,I,Q){const E=[],i=[];A.length>0&&A[0].timestamp>Q,A[0],A[A.length-1];for(const e of A)e.timestamp<Q?E.push(e):i.push(e);return iB.debug("Assigned connecting beats to sections",{totalConnectingBeats:A.length,toSection1:E.length,toSection2:i.length,boundaryTimestamp:Q}),{beatsInSection1:E,beatsInSection2:i}}calculatePhaseAlignment(A,B,I,Q=.1){const E=A-B,i=Math.round(E/I),e=B+i*I,a=Math.abs(A-e),C=I*Q;return a<=C?1:0}runMultiTempoAnalysis(A,B,I){if(B.length<2)return null;const Q=this.findConflictingClusters(B);if(!Q||Q.length===0)return iB.debug("No conflicting tempo clusters found"),null;iB.debug("Found conflicting tempo clusters",{conflictCount:Q.length,conflicts:Q.map(o=>({c1:Math.round(o.cluster1.bpm),c2:Math.round(o.cluster2.bpm)}))});const E=[...B].sort((o,t)=>o.startIndex-t.startIndex),i=[];let e=0,a=0;for(let o=0;o<E.length-1;o++){const t=E[o],s=E[o+1];if(!Q.some(c=>c.cluster1===t&&c.cluster2===s||c.cluster1===s&&c.cluster2===t))continue;const r=this.findCrossingPoint(t,s,A);if(r.hasBoundary){const c=r.boundaryTimestamp;A[t.endIndex],i.push({start:e,end:c,bpm:Math.round(t.bpm*10)/10,intervalSeconds:t.avgInterval,beatCount:t.beatCount,startBeatIndex:a,endBeatIndex:t.endIndex}),e=c,a=s.startIndex}}const C=E[E.length-1];return i.push({start:e,end:I,bpm:Math.round(C.bpm*10)/10,intervalSeconds:C.avgInterval,beatCount:C.beatCount,startBeatIndex:a,endBeatIndex:C.endIndex}),i.length===1?(iB.debug("Multi-tempo analysis found no section boundaries"),null):{tempoSections:i}}reinterpolateBoundaryRegions(A,B,I,Q){return iB.debug("Boundary regions identified",{sectionCount:B.length,sections:B.map(E=>({bpm:E.bpm,start:E.start.toFixed(2),end:E.end.toFixed(2)}))}),A}canApplyMultiTempo(A){const{interpolationMetadata:B}=A,I=B.hasMultipleTempos,Q=B.hasMultiTempoApplied===!0;return I&&!Q}calculateWeightedIntervals(A,B){const I=[];for(let Q=1;Q<A.length;Q++){const E=A[Q].timestamp-A[Q-1].timestamp,i=B.some(t=>Q>t.startIndex&&Q<=t.endIndex),e=(A[Q-1].confidence+A[Q].confidence)/2,a=i?2:1;let C=1;if(Q>1){const t=A[Q-1].timestamp-A[Q-2].timestamp;C=.5+Math.min(E,t)/Math.max(E,t)*.5}if(Q<A.length-1){const t=A[Q+1].timestamp-A[Q].timestamp,s=Math.min(E,t)/Math.max(E,t);C=Math.max(C,.5+s*.5)}const o=e*a*C;I.push({intervalSeconds:E,weight:o,beatIndex:Q,isFromDenseSection:i,confidence:e})}return I}buildIntervalHistogram(A){const B=new Map,I=.005;for(const Q of A){const E=Math.round(Q.intervalSeconds/I)*I,i=B.get(E)||0;B.set(E,i+Q.weight)}return B}findHistogramPeaks(A){const B=[],I=Array.from(A.entries()).sort((i,e)=>i[0]-e[0]);for(let i=0;i<I.length;i++){const[e,a]=I[i],C=i>0?I[i-1][1]:0,o=i<I.length-1?I[i+1][1]:0;a>=C&&a>=o&&a>0&&B.push({interval:e,weight:a})}B.sort((i,e)=>e.weight-i.weight);const Q=.05,E=[];for(const i of B)E.some(a=>Math.abs(a.interval-i.interval)<Q)||E.push(i);return E}calculateAverageInterval(A){if(A.length<2)return .5;let B=0;for(let I=1;I<A.length;I++)B+=A[I].timestamp-A[I-1].timestamp;return B/(A.length-1)}analyzeGaps(A,B){const I=this.options.anomalyThreshold;let Q=0;const E=[],i=[];let e=0;for(let t=1;t<A.length;t++){const n=(A[t].timestamp-A[t-1].timestamp)/B,r=Math.round(n);r>1&&i.push(r),Math.abs(n-2)<.2&&Q++,(n<1-I||n>1+I&&Math.abs(n-2)>.3&&Math.abs(n-3)>.3&&Math.abs(n-4)>.3)&&E.push(t);const G=Math.round(A[t].timestamp/B)*B,D=Math.abs(A[t].timestamp-G)/B;e+=D}const a=A.length>1?e/(A.length-1):0,C=Math.max(0,1-a*2),o=i.length>0?i.reduce((t,s)=>t+s,0)/i.length:1;return{totalGaps:i.length,halfNoteGaps:Q,anomalies:E,avgGapSize:o,gridAlignmentScore:C}}generateGrid(A,B,I){return this.interpolateAdaptivePhaseLocked(A,B)}interpolateForwardsWithDrift(A,B,I,Q,E){const i=[];let e=B,a=A;const C=I.filter(r=>!(r.timestamp<=A||E!==void 0&&r.timestamp>E));let o=A;for(let r=0;r<C.length;r++){const c=C[r],G=c.timestamp-o,D=Math.round(G/e);if(D>0){const h=a+D*e,y=(c.timestamp-h)/D*Q;e=e+y;for(let R=1;R<D;R++){const p=o+R*e;if(E!==void 0&&p>E)break;i.push(p)}}a=c.timestamp,o=c.timestamp}const t=i.length,s=A+t*B,n=i.length>0?i[i.length-1]-s:0;return{finalInterval:e,beatPositions:i,phaseError:n,endTimestamp:o}}interpolateBackwardsWithDrift(A,B,I,Q,E){const i=[];let e=B,a=A;const C=I.filter(r=>!(r.timestamp>=A||E!==void 0&&r.timestamp<E)).reverse();let o=A;for(let r=0;r<C.length;r++){const c=C[r],G=o-c.timestamp,D=Math.round(G/e);if(D>0){const h=a-D*e,y=(c.timestamp-h)/D*Q;e=e+y;for(let R=1;R<D;R++){const p=o-R*e;if(E!==void 0&&p<E)break;i.unshift(p)}}a=c.timestamp,o=c.timestamp}const t=i.length,s=A-t*B,n=i.length>0?i[0]-s:0;return{finalInterval:e,beatPositions:i,phaseError:n,endTimestamp:o}}interpolateAdaptivePhaseLocked(A,B){const{beats:I,duration:Q}=A,E=B.intervalSeconds,i=this.options.gridSnapTolerance,e=this.options.tempoAdaptationRate,a=[],C=this.options.gridAlignmentWeight,o=this.options.anchorConfidenceWeight,t=this.options.paceConfidenceWeight;if(this.options.extrapolateStart){let r=I[0].timestamp-E;for(;r>=0;){const c=this.calculateConfidence(C,o*.5,t*B.confidence);a.unshift(this.createInterpolatedBeat(r,c,I[0].timestamp)),r-=E}}let s=I[0].timestamp,n=E;for(let r=0;r<I.length;r++){const c=I[r];if(a.push(this.createDetectedBeat(c)),r<I.length-1){const G=I[r+1],D=G.timestamp-c.timestamp,h=Math.round(D/n),w=s+h*n,y=G.timestamp-w;if(h>0){const R=y/h*e;n=n+R}for(let R=1;R<h;R++){const p=c.timestamp+R*n;if(p>=G.timestamp-i)break;const k=this.calculateConfidence(C,o*((c.confidence+G.confidence)/2),t*B.confidence);a.push(this.createInterpolatedBeat(p,k,c.timestamp))}s=G.timestamp}}if(this.options.extrapolateEnd){let r=I[I.length-1].timestamp+n;for(;r<=Q;){const c=this.calculateConfidence(C,o*.5,t*B.confidence);a.push(this.createInterpolatedBeat(r,c,I[I.length-1].timestamp)),r+=n}}return a.sort((r,c)=>r.timestamp-c.timestamp),a}mergeBeats(A,B,I){const Q=this.options.gridSnapTolerance,E=[],i=[...B].sort((o,t)=>o.timestamp-t.timestamp),e=[...A].sort((o,t)=>o.timestamp-t.timestamp),a=new Map;for(const o of e)a.set(o.timestamp,o);const C=new Set;for(const o of i){const t=this.findBeatNear(a,o.timestamp,Q);t&&!C.has(t.timestamp)?(E.push(this.createDetectedBeat(t)),C.add(t.timestamp)):o.source==="interpolated"&&E.push(o)}for(const o of e)C.has(o.timestamp)||E.push(this.createDetectedBeat(o));return E.sort((o,t)=>o.timestamp-t.timestamp),this.reassignBeatPositions(E,I),E}reassignBeatPositions(A,B){const I=B??_E,Q=this.computeMeasureOffsets(I.segments,A.length);for(let E=0;E<A.length;E++){const i=A[E],e=this.findActiveSegmentIndex(I.segments,E),a=I.segments[e],{downbeatBeatIndex:C,timeSignature:o}=a,{beatsPerMeasure:t}=o,s=E-C,n=(s%t+t)%t,r=n===0,G=Math.max(0,Math.floor(s/t))+Q[e];i.beatInMeasure=n,i.isDownbeat=r,i.measureNumber=G}}computeMeasureOffsets(A,B){const I=[];for(let Q=0;Q<A.length;Q++)if(Q===0)I.push(0);else{const E=A[Q-1],{downbeatBeatIndex:i,timeSignature:e}=E,a=e.beatsPerMeasure,o=A[Q].startBeat-1-i,t=Math.max(0,Math.floor(o/a));I.push(t+1)}return I}findActiveSegmentIndex(A,B){let I=0;for(let Q=0;Q<A.length&&A[Q].startBeat<=B;Q++)I=Q;return I}calculateConfidence(A,B,I){return Math.max(0,Math.min(1,A+B+I))}createInterpolatedBeat(A,B,I){return{timestamp:A,beatInMeasure:0,isDownbeat:!1,measureNumber:0,intensity:.5,confidence:B,source:"interpolated",distanceToAnchor:Math.abs(A-I),nearestAnchorTimestamp:I}}createDetectedBeat(A){return{...A,source:"detected",distanceToAnchor:0,nearestAnchorTimestamp:A.timestamp}}findBeatNear(A,B,I){const Q=A.get(B);if(Q)return Q;for(const[E,i]of A)if(Math.abs(E-B)<=I)return i;return null}findNearestAnchor(A,B,I){const Q=I>=0&&I<A.length?A[I]:A[0],E=I+1<A.length?A[I+1]:A[A.length-1],i=Math.abs(B-Q.timestamp),e=Math.abs(B-E.timestamp);return i<=e?{timestamp:Q.timestamp,confidence:Q.confidence}:{timestamp:E.timestamp,confidence:E.confidence}}calculateTempoDriftRatio(A){if(A.length<3)return 1;const B=[];for(let E=1;E<A.length;E++){const i=A[E].timestamp-A[E-1].timestamp;i>0&&B.push(60/i)}if(B.length===0)return 1;const I=Math.min(...B),Q=Math.max(...B);return I>0?Q/I:1}createEmptyInterpolatedBeatMap(A){return{audioId:A.audioId,duration:A.duration,detectedBeats:[],mergedBeats:[],quarterNoteInterval:.5,quarterNoteBpm:120,quarterNoteConfidence:0,originalMetadata:A.metadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:.5,bpm:120,confidence:0,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:0,denseSectionBeats:0},gapAnalysis:{totalGaps:0,halfNoteGaps:0,anomalies:[],avgGapSize:1,gridAlignmentScore:0},detectedBeatCount:0,interpolatedBeatCount:0,totalBeatCount:0,interpolationRatio:0,avgInterpolatedConfidence:0,tempoDriftRatio:1,detectedClusterTempos:void 0,hasMultipleTempos:!1,tempoSections:void 0,hasMultiTempoApplied:void 0},downbeatConfig:A.downbeatConfig}}createSingleBeatInterpolatedBeatMap(A){const B=A.beats[0],I={...B,source:"detected",distanceToAnchor:0,nearestAnchorTimestamp:B.timestamp};return{audioId:A.audioId,duration:A.duration,detectedBeats:[B],mergedBeats:[I],quarterNoteInterval:.5,quarterNoteBpm:120,quarterNoteConfidence:.1,originalMetadata:A.metadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:.5,bpm:120,confidence:.1,histogramPeak:0,secondaryPeaks:[],method:"tempo-detector-fallback",denseSectionCount:0,denseSectionBeats:0},gapAnalysis:{totalGaps:0,halfNoteGaps:0,anomalies:[],avgGapSize:1,gridAlignmentScore:0},detectedBeatCount:1,interpolatedBeatCount:0,totalBeatCount:1,interpolationRatio:0,avgInterpolatedConfidence:0,tempoDriftRatio:1,detectedClusterTempos:void 0,hasMultipleTempos:!1,tempoSections:void 0,hasMultiTempoApplied:void 0},downbeatConfig:A.downbeatConfig}}static toJSON(A){const B={audioId:A.audioId,duration:A.duration,detectedBeats:A.detectedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),mergedBeats:A.mergedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey},source:I.source,distanceToAnchor:I.distanceToAnchor,nearestAnchorTimestamp:I.nearestAnchorTimestamp})),quarterNoteInterval:A.quarterNoteInterval,quarterNoteBpm:A.quarterNoteBpm,quarterNoteConfidence:A.quarterNoteConfidence,originalMetadata:A.originalMetadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:A.interpolationMetadata.quarterNoteDetection.intervalSeconds,bpm:A.interpolationMetadata.quarterNoteDetection.bpm,confidence:A.interpolationMetadata.quarterNoteDetection.confidence,histogramPeak:A.interpolationMetadata.quarterNoteDetection.histogramPeak,secondaryPeaks:A.interpolationMetadata.quarterNoteDetection.secondaryPeaks,method:A.interpolationMetadata.quarterNoteDetection.method,denseSectionCount:A.interpolationMetadata.quarterNoteDetection.denseSectionCount,denseSectionBeats:A.interpolationMetadata.quarterNoteDetection.denseSectionBeats},gapAnalysis:{totalGaps:A.interpolationMetadata.gapAnalysis.totalGaps,halfNoteGaps:A.interpolationMetadata.gapAnalysis.halfNoteGaps,anomalies:A.interpolationMetadata.gapAnalysis.anomalies,avgGapSize:A.interpolationMetadata.gapAnalysis.avgGapSize,gridAlignmentScore:A.interpolationMetadata.gapAnalysis.gridAlignmentScore},detectedBeatCount:A.interpolationMetadata.detectedBeatCount,interpolatedBeatCount:A.interpolationMetadata.interpolatedBeatCount,totalBeatCount:A.interpolationMetadata.totalBeatCount,interpolationRatio:A.interpolationMetadata.interpolationRatio,avgInterpolatedConfidence:A.interpolationMetadata.avgInterpolatedConfidence,tempoDriftRatio:A.interpolationMetadata.tempoDriftRatio,detectedClusterTempos:A.interpolationMetadata.detectedClusterTempos,hasMultipleTempos:A.interpolationMetadata.hasMultipleTempos,tempoSections:A.interpolationMetadata.tempoSections?.map(I=>({start:I.start,end:I.end,bpm:I.bpm,intervalSeconds:I.intervalSeconds,beatCount:I.beatCount,startBeatIndex:I.startBeatIndex,endBeatIndex:I.endBeatIndex})),hasMultiTempoApplied:A.interpolationMetadata.hasMultiTempoApplied},downbeatConfig:A.downbeatConfig};return JSON.stringify(B,null,2)}static fromJSON(A){const B=JSON.parse(A);return{audioId:B.audioId,duration:B.duration,detectedBeats:B.detectedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey}})),mergedBeats:B.mergedBeats.map(I=>({timestamp:I.timestamp,beatInMeasure:I.beatInMeasure,isDownbeat:I.isDownbeat,measureNumber:I.measureNumber,intensity:I.intensity,confidence:I.confidence,...I.requiredKey!==void 0&&{requiredKey:I.requiredKey},source:I.source,distanceToAnchor:I.distanceToAnchor,nearestAnchorTimestamp:I.nearestAnchorTimestamp})),quarterNoteInterval:B.quarterNoteInterval,quarterNoteBpm:B.quarterNoteBpm,quarterNoteConfidence:B.quarterNoteConfidence,originalMetadata:B.originalMetadata,interpolationMetadata:{quarterNoteDetection:{intervalSeconds:B.interpolationMetadata.quarterNoteDetection.intervalSeconds,bpm:B.interpolationMetadata.quarterNoteDetection.bpm,confidence:B.interpolationMetadata.quarterNoteDetection.confidence,histogramPeak:B.interpolationMetadata.quarterNoteDetection.histogramPeak,secondaryPeaks:B.interpolationMetadata.quarterNoteDetection.secondaryPeaks,method:B.interpolationMetadata.quarterNoteDetection.method,denseSectionCount:B.interpolationMetadata.quarterNoteDetection.denseSectionCount,denseSectionBeats:B.interpolationMetadata.quarterNoteDetection.denseSectionBeats},gapAnalysis:{totalGaps:B.interpolationMetadata.gapAnalysis.totalGaps,halfNoteGaps:B.interpolationMetadata.gapAnalysis.halfNoteGaps,anomalies:B.interpolationMetadata.gapAnalysis.anomalies,avgGapSize:B.interpolationMetadata.gapAnalysis.avgGapSize,gridAlignmentScore:B.interpolationMetadata.gapAnalysis.gridAlignmentScore},detectedBeatCount:B.interpolationMetadata.detectedBeatCount,interpolatedBeatCount:B.interpolationMetadata.interpolatedBeatCount,totalBeatCount:B.interpolationMetadata.totalBeatCount,interpolationRatio:B.interpolationMetadata.interpolationRatio,avgInterpolatedConfidence:B.interpolationMetadata.avgInterpolatedConfidence,tempoDriftRatio:B.interpolationMetadata.tempoDriftRatio,detectedClusterTempos:B.interpolationMetadata.detectedClusterTempos,hasMultipleTempos:B.interpolationMetadata.hasMultipleTempos??!1,tempoSections:B.interpolationMetadata.tempoSections?.map(I=>({start:I.start,end:I.end,bpm:I.bpm,intervalSeconds:I.intervalSeconds,beatCount:I.beatCount,startBeatIndex:I.startBeatIndex,endBeatIndex:I.endBeatIndex})),hasMultiTempoApplied:B.interpolationMetadata.hasMultiTempoApplied},downbeatConfig:B.downbeatConfig}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=jQ.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return jQ.fromJSON(I)}}function $i(g){const{mergedBeats:A,interpolationMetadata:B,downbeatConfig:I}=g,Q=[],E=[];for(let e=0;e<A.length;e++){const a=A[e],C={timestamp:a.timestamp,beatInMeasure:a.beatInMeasure,isDownbeat:a.isDownbeat,measureNumber:a.measureNumber,intensity:a.intensity,confidence:a.confidence};Q.push(C),a.source==="detected"&&E.push(e)}return{audioId:g.audioId,duration:g.duration,beats:Q,detectedBeatIndices:E,quarterNoteInterval:g.quarterNoteInterval,quarterNoteBpm:g.quarterNoteBpm,downbeatConfig:I??_E,tempoSections:B.tempoSections,originalMetadata:g.originalMetadata}}const OE=class OE{constructor(A={}){this.options={includeAdvancedMetrics:!1,sampleRate:44100,fftSize:2048,trebleBoost:1,bassBoost:1,midBoost:1,...A}}async extractSonicFingerprint(A){const B=await this.fetchAndDecode(A),I=B.duration,Q=I<3;let E;Q?E=[0]:E=[.05,.4,.7];const i=[];for(const D of E){const h=await this.analyzeAtPosition(B,D,Q);i.push(h)}const e=this.averageFrequencyBands(i);let a=rg.calculateDominance(e.bass,380),C=rg.calculateDominance(e.mid,3600),o=rg.calculateDominance(e.treble,1e4);a=a*this.options.bassBoost,C=C*this.options.midBoost,o=o*this.options.trebleBoost;const t=a+C+o;a=a/(t||1),C=C/(t||1),o=o/(t||1);const s=this.calculateAverageAmplitude(B),n=this.calculateRMS(B),c=this.calculatePeak(B)-n,G={bass_dominance:a,mid_dominance:C,treble_dominance:o,average_amplitude:s,analysis_metadata:{duration_analyzed:Q?I:I*E.length*.01,full_buffer_analyzed:Q,sample_positions:E,analyzed_at:new Date().toISOString()},rms_energy:n,dynamic_range:c};if(this.options.includeAdvancedMetrics){const D=[...e.bass,...e.mid,...e.treble];G.spectral_centroid=this.calculateSpectralCentroid(D),G.spectral_rolloff=this.calculateSpectralRolloff(D),G.zero_crossing_rate=this.calculateZeroCrossingRate(B)}return G}async analyzeTimeline(A,B){const I=await this.fetchAndDecode(A),Q=I.duration,E=I.sampleRate,i=[];let e=[];if(B.type==="interval"){const a=B.intervalSeconds;for(let C=0;C<Q;C+=a)e.push(C)}else{const a=B.count,C=Q/a;for(let o=0;o<a;o++)e.push(o*C)}for(const a of e){const C=Q-a,o=Math.min(1,C);if(o<=0)break;const t=Math.floor(a*E),s=Math.floor((a+o)*E),n=this.extractAudioSegment(I,t,s),r=this.performFFT(n,this.options.fftSize),c=new Uint8Array(r.length);for(let W=0;W<r.length;W++)c[W]=Math.min(255,Math.round(r[W]));const G=rg.separateFrequencyBands(c,E),D=rg.calculateDominance(G.bass)*this.options.bassBoost,h=rg.calculateDominance(G.mid)*this.options.midBoost,w=rg.calculateDominance(G.treble)*this.options.trebleBoost,y=D+h+w||1,R=this.calculateRMS(I,t,s),p=this.calculatePeak(I,t,s),k=[...G.bass,...G.mid,...G.treble];i.push({timestamp:a,duration:o,bass:D/y,mid:h/y,treble:w/y,amplitude:R,rms_energy:R,peak:p,dynamic_range:p-R,spectral_centroid:this.calculateSpectralCentroid(k),spectral_rolloff:this.calculateSpectralRolloff(k),zero_crossing_rate:this.calculateZeroCrossingRate(I,t,s)})}return i}async generateBeatMap(A,B,I,Q,E){return new _B(I).generateBeatMap(A,B,Q,E)}async generateBeatMapFromBuffer(A,B,I,Q,E){return new _B(I).generateBeatMapFromBuffer(A,B,Q,E)}createBeatStream(A,B,I){return new Zd(A,B,I)}interpolateBeatMap(A,B){return new jQ(B).interpolate(A)}async generateBeatMapWithInterpolation(A,B,I,Q,E,i){const e=await this.generateBeatMap(A,B,I,Q,i);return this.interpolateBeatMap(e,E)}async generateBeatMapWithInterpolationFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapFromBuffer(A,B,I,Q,i);return this.interpolateBeatMap(e,E)}static beatMapToJSON(A){return _B.toJSON(A)}static beatMapFromJSON(A){return _B.fromJSON(A)}static async saveBeatMapToFile(A,B){return _B.saveToFile(A,B)}static async loadBeatMapFromFile(A){return _B.loadFromFile(A)}async generateRhythm(A,B,I,Q,E,i){const e=await this.fetchAndDecode(A);return this.generateRhythmFromBuffer(e,B,I,Q,E,i)}async generateRhythmFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapWithInterpolationFromBuffer(A,B,Q,E),a=$i(e);return new kg(I).generate(A,a,void 0,i)}async generateRhythmLevel(A,B,I,Q,E,i){const e=await this.fetchAndDecode(A);return this.generateRhythmLevelFromBuffer(e,B,I,Q,E,i)}async generateRhythmLevelFromBuffer(A,B,I,Q,E,i){const e=await this.generateBeatMapWithInterpolationFromBuffer(A,B,Q,E),a=$i(e);return new ji(I).generate(A,a,i)}async generateRhythmLevelWithPreset(A,B,I,Q,E,i){const e=OE.LEVEL_PRESETS[I],a={difficulty:e.difficulty,controllerMode:e.controllerMode,buttons:e.buttons};return this.generateRhythmLevel(A,B,a,Q,E,i)}async generateRhythmLevelWithPresetFromBuffer(A,B,I,Q,E,i){const e=OE.LEVEL_PRESETS[I],a={difficulty:e.difficulty,controllerMode:e.controllerMode,buttons:e.buttons};return this.generateRhythmLevelFromBuffer(A,B,a,Q,E,i)}async fetchAndDecodeAudio(A){return this.fetchAndDecode(A)}async fetchAndDecode(A){const B=await fetch(A);if(!B.ok)throw new Error(`Failed to fetch audio: ${B.statusText}`);const I=await B.arrayBuffer(),Q=globalThis.AudioContext||window?.AudioContext;if(!Q)throw new Error("AudioContext not available in this environment");const E=new Q;return await new Promise((i,e)=>{E.decodeAudioData(I,i,e)})}async analyzeAtPosition(A,B,I){const Q=A.sampleRate,E=A.duration;let i,e;if(I)i=0,e=A.length;else{const t=E*B;i=Math.floor(t*Q),e=Math.min(i+Q,A.length)}const a=this.extractAudioSegment(A,i,e),C=this.performFFT(a,this.options.fftSize),o=new Uint8Array(C.length);for(let t=0;t<C.length;t++)o[t]=Math.min(255,Math.round(C[t]));return rg.separateFrequencyBands(o,Q)}extractAudioSegment(A,B,I){const Q=I-B,E=new Float32Array(Q);for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i);for(let a=0;a<Q;a++)E[a]+=e[B+a]/A.numberOfChannels}return E}performFFT(A,B){const I=Math.pow(2,Math.ceil(Math.log2(Math.min(A.length,B)))),Q=new Float32Array(I),E=new Float32Array(I);for(let C=0;C<Math.min(A.length,I);C++){const o=.5-.5*Math.cos(2*Math.PI*C/(I-1));Q[C]=A[C]*o}this.fft(Q,E);const i=new Array(I/2);for(let C=0;C<i.length;C++){const o=Q[C],t=E[C];i[C]=Math.sqrt(o*o+t*t)}const e=Math.max(...i);return i.map(C=>{const o=C/(e||1);return 20*Math.log10(Math.max(o,.001))})}fft(A,B){const I=A.length;if(I<=1)return;let Q=0;for(let i=0;i<I-1;i++){i<Q&&([A[i],A[Q]]=[A[Q],A[i]],[B[i],B[Q]]=[B[Q],B[i]]);let e=I/2;for(;Q>=e;)Q-=e,e/=2;Q+=e}let E=2;for(;E<=I;){const i=-2*Math.PI/E,e=Math.cos(i),a=Math.sin(i);for(let C=0;C<I;C+=E){let o=1,t=0;for(let s=0;s<E/2;s++){const n=C+s,r=C+s+E/2,c=A[r]*o-B[r]*t,G=A[r]*t+B[r]*o;A[r]=A[n]-c,B[r]=B[n]-G,A[n]+=c,B[n]+=G;const D=o*e-t*a;t=o*a+t*e,o=D}}E*=2}}averageFrequencyBands(A){if(A.length===0)return{bass:[],mid:[],treble:[]};if(A.length===1)return A[0];const B=Math.max(...A.map(i=>Math.max(i.bass.length,i.mid.length,i.treble.length))),I=[],Q=[],E=[];for(let i=0;i<B;i++){const e=A.map(o=>o.bass[i]||0),a=A.map(o=>o.mid[i]||0),C=A.map(o=>o.treble[i]||0);I.push(e.reduce((o,t)=>o+t,0)/e.length),Q.push(a.reduce((o,t)=>o+t,0)/a.length),E.push(C.reduce((o,t)=>o+t,0)/C.length)}return{bass:I,mid:Q,treble:E}}calculateAverageAmplitude(A){let B=0,I=0;for(let Q=0;Q<A.numberOfChannels;Q++){const E=A.getChannelData(Q);for(let i=0;i<E.length;i++)B+=Math.abs(E[i]),I++}return I>0?B/I:0}calculateRMS(A,B=0,I=A.length){let Q=0,E=0;for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i),a=Math.min(I,e.length);for(let C=B;C<a;C++)Q+=e[C]*e[C],E++}return E>0?Math.sqrt(Q/E):0}calculatePeak(A,B=0,I=A.length){let Q=0;for(let E=0;E<A.numberOfChannels;E++){const i=A.getChannelData(E),e=Math.min(I,i.length);for(let a=B;a<e;a++){const C=Math.abs(i[a]);C>Q&&(Q=C)}}return Q}calculateSpectralCentroid(A){let B=0,I=0;for(let Q=0;Q<A.length;Q++)B+=Q*A[Q],I+=A[Q];return I>0?B/I:0}calculateSpectralRolloff(A){const I=A.reduce((E,i)=>E+i,0)*.85;let Q=0;for(let E=0;E<A.length;E++)if(Q+=A[E],Q>=I)return E/A.length;return 1}calculateZeroCrossingRate(A,B=0,I=A.length){let Q=0,E=0;for(let i=0;i<A.numberOfChannels;i++){const e=A.getChannelData(i),a=Math.min(I,e.length),C=Math.max(0,B);for(let o=Math.max(1,C+1);o<a;o++)(e[o-1]>=0&&e[o]<0||e[o-1]<0&&e[o]>=0)&&Q++,E++}return E>0?Q/E:0}};OE.LEVEL_PRESETS={casual:{difficulty:"easy",controllerMode:"ddr",buttons:{pitchInfluenceWeight:.3},description:"Easy difficulty, low pitch influence - for relaxed gameplay"},standard:{difficulty:"medium",controllerMode:"ddr",buttons:{pitchInfluenceWeight:.7},description:"Medium difficulty, high pitch influence - balanced experience"},challenge:{difficulty:"hard",controllerMode:"ddr",buttons:{pitchInfluenceWeight:1},description:"Hard difficulty, full pitch influence - for skilled players"},insane:{difficulty:"hard",controllerMode:"ddr",buttons:{pitchInfluenceWeight:1,consecutiveSameKeyLimit:4},description:"Maximum difficulty with strict limits - for experts only"}};let Lo=OE;const MW=["60s","70s","80s","90s","acidjazz","alternative","alternativerock","ambient","atmospheric","blues","bluesrock","bossanova","bossa","celtic","chanson","chillout","choir","classical","classicrock","club","comedy","country","cuban","dance","darkambient","darkwave","deephouse","disco","downtempo","drumandbass","dub","dubstep","easylistening","edm","electronic","electro","electropop","ethno","eurodance","experimental","folk","funk","fusion","groove","grunge","hardcore","hardrock","hiphop","house","indie","indiepop","indierock","industrial","instrumentalpop","instrumentalrock","jazz","jazzfusion","latin","lounge","metal","minimal","newage"," orchestrall","pop","popfolk","poprock","postrock","progressive","psychedelic","punkrock","rap","reggae","rnb","rock","rockandroll","ska","soul","soundtrack","synthpop","techno","trance","trip","triphop","underground","world","worldbeat","worldmusic"],Vo=["Blues---Boogie Woogie","Blues---Chicago Blues","Blues---Country Blues","Blues---Delta Blues","Blues---Electric Blues","Blues---Harmonica Blues","Blues---Jump Blues","Blues---Louisiana Blues","Blues---Modern Electric Blues","Blues---Piano Blues","Blues---Rhythm & Blues","Blues---Texas Blues","Brass & Military---Brass Band","Brass & Military---Marches","Brass & Military---Military","Children's-- - Educational","Children's-- - Nursery Rhymes","Children's-- - Story","Classical---Baroque","Classical---Choral","Classical---Classical","Classical---Contemporary","Classical---Impressionist","Classical---Medieval","Classical---Modern","Classical---Neo-Classical","Classical---Neo-Romantic","Classical---Opera","Classical---Post-Modern","Classical---Renaissance","Classical---Romantic","Electronic---Abstract","Electronic---Acid","Electronic---Acid House","Electronic---Acid Jazz","Electronic---Ambient","Electronic---Bassline","Electronic---Beatdown","Electronic---Berlin-School","Electronic---Big Beat","Electronic---Bleep","Electronic---Breakbeat","Electronic---Breakcore","Electronic---Breaks","Electronic---Broken Beat","Electronic---Chillwave","Electronic---Chiptune","Electronic---Dance-pop","Electronic---Dark Ambient","Electronic---Darkwave","Electronic---Deep House","Electronic---Deep Techno","Electronic---Disco","Electronic---Disco Polo","Electronic---Donk","Electronic---Downtempo","Electronic---Drone","Electronic---Drum n Bass","Electronic---Dub","Electronic---Dub Techno","Electronic---Dubstep","Electronic---Dungeon Synth","Electronic---EBM","Electronic---Electro","Electronic---Electro House","Electronic---Electroclash","Electronic---Euro House","Electronic---Euro-Disco","Electronic---Eurobeat","Electronic---Eurodance","Electronic---Experimental","Electronic---Freestyle","Electronic---Future Jazz","Electronic---Gabber","Electronic---Garage House","Electronic---Ghetto","Electronic---Ghetto House","Electronic---Glitch","Electronic---Goa Trance","Electronic---Grime","Electronic---Halftime","Electronic---Hands Up","Electronic---Happy Hardcore","Electronic---Hard House","Electronic---Hard Techno","Electronic---Hard Trance","Electronic---Hardcore","Electronic---Hardstyle","Electronic---Hi NRG","Electronic---Hip Hop","Electronic---Hip-House","Electronic---House","Electronic---IDM","Electronic---Illbient","Electronic---Industrial","Electronic---Italo House","Electronic---Italo-Disco","Electronic---Italodance","Electronic---Jazzdance","Electronic---Juke","Electronic---Jumpstyle","Electronic---Jungle","Electronic---Latin","Electronic---Leftfield","Electronic---Makina","Electronic---Minimal","Electronic---Minimal Techno","Electronic---Modern Classical","Electronic---Musique Concrète","Electronic---Neofolk","Electronic---New Age","Electronic---New Beat","Electronic---New Wave","Electronic---Noise","Electronic---Nu-Disco","Electronic---Power Electronics","Electronic---Progressive Breaks","Electronic---Progressive House","Electronic---Progressive Trance","Electronic---Psy-Trance","Electronic---Rhythmic Noise","Electronic---Schranz","Electronic---Sound Collage","Electronic---Speed Garage","Electronic---Speedcore","Electronic---Synth-pop","Electronic---Synthwave","Electronic---Tech House","Electronic---Tech Trance","Electronic---Techno","Electronic---Trance","Electronic---Tribal","Electronic---Tribal House","Electronic---Trip Hop","Electronic---Tropical House","Electronic---UK Garage","Electronic---Vaporwave","Folk, World, & Country---African","Folk, World, & Country---Bluegrass","Folk, World, & Country---Cajun","Folk, World, & Country---Canzone Napoletana","Folk, World, & Country---Catalan Music","Folk, World, & Country---Celtic","Folk, World, & Country---Country","Folk, World, & Country---Fado","Folk, World, & Country---Flamenco","Folk, World, & Country---Folk","Folk, World, & Country---Gospel","Folk, World, & Country---Highlife","Folk, World, & Country---Hillbilly","Folk, World, & Country---Hindustani","Folk, World, & Country---Honky Tonk","Folk, World, & Country---Indian Classical","Folk, World, & Country---Laïkó","Folk, World, & Country---Nordic","Folk, World, & Country---Pacific","Folk, World, & Country---Polka","Folk, World, & Country---Raï","Folk, World, & Country---Romani","Folk, World, & Country---Soukous","Folk, World, & Country---Séga","Folk, World, & Country---Volksmusik","Folk, World, & Country---Zouk","Folk, World, & Country---Éntekhno","Funk / Soul---Afrobeat","Funk / Soul---Boogie","Funk / Soul---Contemporary R&B","Funk / Soul---Disco","Funk / Soul---Free Funk","Funk / Soul---Funk","Funk / Soul---Gospel","Funk / Soul---Neo Soul","Funk / Soul---New Jack Swing","Funk / Soul---P.Funk","Funk / Soul---Psychedelic","Funk / Soul---Rhythm & Blues","Funk / Soul---Soul","Funk / Soul---Swingbeat","Funk / Soul---UK Street Soul","Hip Hop---Bass Music","Hip Hop---Boom Bap","Hip Hop---Bounce","Hip Hop---Britcore","Hip Hop---Cloud Rap","Hip Hop---Conscious","Hip Hop---Crunk","Hip Hop---Cut-up/DJ","Hip Hop---DJ Battle Tool","Hip Hop---Electro","Hip Hop---G-Funk","Hip Hop---Gangsta","Hip Hop---Grime","Hip Hop---Hardcore Hip-Hop","Hip Hop---Horrorcore","Hip Hop---Instrumental","Hip Hop---Jazzy Hip-Hop","Hip Hop---Miami Bass","Hip Hop---Pop Rap","Hip Hop---Ragga HipHop","Hip Hop---RnB/Swing","Hip Hop---Screw","Hip Hop---Thug Rap","Hip Hop---Trap","Hip Hop---Trip Hop","Hip Hop---Turntablism","Jazz---Afro-Cuban Jazz","Jazz---Afrobeat","Jazz---Avant-garde Jazz","Jazz---Big Band","Jazz---Bop","Jazz---Bossa Nova","Jazz---Contemporary Jazz","Jazz---Cool Jazz","Jazz---Dixieland","Jazz---Easy Listening","Jazz---Free Improvisation","Jazz---Free Jazz","Jazz---Fusion","Jazz---Gypsy Jazz","Jazz---Hard Bop","Jazz---Jazz-Funk","Jazz---Jazz-Rock","Jazz---Latin Jazz","Jazz---Modal","Jazz---Post Bop","Jazz---Ragtime","Jazz---Smooth Jazz","Jazz---Soul-Jazz","Jazz---Space-Age","Jazz---Swing","Latin---Afro-Cuban","Latin---Baião","Latin---Batucada","Latin---Beguine","Latin---Bolero","Latin---Boogaloo","Latin---Bossanova","Latin---Cha-Cha","Latin---Charanga","Latin---Compas","Latin---Cubano","Latin---Cumbia","Latin---Descarga","Latin---Forró","Latin---Guaguancó","Latin---Guajira","Latin---Guaracha","Latin---MPB","Latin---Mambo","Latin---Mariachi","Latin---Merengue","Latin---Norteño","Latin---Nueva Cancion","Latin---Pachanga","Latin---Porro","Latin---Ranchera","Latin---Reggaeton","Latin---Rumba","Latin---Salsa","Latin---Samba","Latin---Son","Latin---Son Montuno","Latin---Tango","Latin---Tejano","Latin---Vallenato","Non-Music---Audiobook","Non-Music---Comedy","Non-Music---Dialogue","Non-Music---Education","Non-Music---Field Recording","Non-Music---Interview","Non-Music---Monolog","Non-Music---Poetry","Non-Music---Political","Non-Music---Promotional","Non-Music---Radioplay","Non-Music---Religious","Non-Music---Spoken Word","Pop---Ballad","Pop---Bollywood","Pop---Bubblegum","Pop---Chanson","Pop---City Pop","Pop---Europop","Pop---Indie Pop","Pop---J-pop","Pop---K-pop","Pop---Kayōkyoku","Pop---Light Music","Pop---Music Hall","Pop---Novelty","Pop---Parody","Pop---Schlager","Pop---Vocal","Reggae---Calypso","Reggae---Dancehall","Reggae---Dub","Reggae---Lovers Rock","Reggae---Ragga","Reggae---Reggae","Reggae---Reggae-Pop","Reggae---Rocksteady","Reggae---Roots Reggae","Reggae---Ska","Reggae---Soca","Rock---AOR","Rock---Acid Rock","Rock---Acoustic","Rock---Alternative Rock","Rock---Arena Rock","Rock---Art Rock","Rock---Atmospheric Black Metal","Rock---Avantgarde","Rock---Beat","Rock---Black Metal","Rock---Blues Rock","Rock---Brit Pop","Rock---Classic Rock","Rock---Coldwave","Rock---Country Rock","Rock---Crust","Rock---Death Metal","Rock---Deathcore","Rock---Deathrock","Rock---Depressive Black Metal","Rock---Doo Wop","Rock---Doom Metal","Rock---Dream Pop","Rock---Emo","Rock---Ethereal","Rock---Experimental","Rock---Folk Metal","Rock---Folk Rock","Rock---Funeral Doom Metal","Rock---Funk Metal","Rock---Garage Rock","Rock---Glam","Rock---Goregrind","Rock---Goth Rock","Rock---Gothic Metal","Rock---Grindcore","Rock---Grunge","Rock---Hard Rock","Rock---Hardcore","Rock---Heavy Metal","Rock---Indie Rock","Rock---Industrial","Rock---Krautrock","Rock---Lo-Fi","Rock---Lounge","Rock---Math Rock","Rock---Melodic Death Metal","Rock---Melodic Hardcore","Rock---Metalcore","Rock---Mod","Rock---Neofolk","Rock---New Wave","Rock---No Wave","Rock---Noise","Rock---Noisecore","Rock---Nu Metal","Rock---Oi","Rock---Parody","Rock---Pop Punk","Rock---Pop Rock","Rock---Pornogrind","Rock---Post Rock","Rock---Post-Hardcore","Rock---Post-Metal","Rock---Post-Punk","Rock---Power Metal","Rock---Power Pop","Rock---Power Violence","Rock---Prog Rock","Rock---Progressive Metal","Rock---Psychedelic Rock","Rock---Psychobilly","Rock---Pub Rock","Rock---Punk","Rock---Rock & Roll","Rock---Rockabilly","Rock---Shoegaze","Rock---Ska","Rock---Sludge Metal","Rock---Soft Rock","Rock---Southern Rock","Rock---Space Rock","Rock---Speed Metal","Rock---Stoner Rock","Rock---Surf","Rock---Symphonic Rock","Rock---Technical Death Metal","Rock---Thrash","Rock---Twist","Rock---Viking Metal","Rock---Yé-Yé","Stage & Screen---Musical","Stage & Screen---Score","Stage & Screen---Soundtrack","Stage & Screen---Theme"],RW=["Blues","Classical","Country","Disco","Hip-Hop","Jazz","Metal","Pop","Reggae","Rock"],yW=["guitar","classical","slow","techno","strings","drums","electronic","rock","fast","piano","ambient","beat","violin","vocal","synth","female","indian","opera","male","singing","vocals","no vocals","harpsichord","loud","quiet","flute","woman","male vocal","no vocal","pop","soft","sitar","solo","man","classic","choir","voice","new age","dance","male voice","female vocal","beats","harp","cello","no voice","weird","country","metal","female voice","choral"],uW=["happy","not happy"],mW=["action","adventure","advertising","background","ballad","calm","children","christmas","commercial","cool","corporate","dark","deep","documentary","drama","dramatic","dream","emotional","energetic","epic","fast","film","fun","funny","game","groovy","happy","heavy","holiday","hopeful","inspiring","love","meditative","melancholic","melodic","motivational","movie","nature","nostalgic","party","peaceful","positive","relaxing","retro","romantic","sad","scary","science","sea","sentimental","serene","sexy","space","sport","summer","suspense","trailer","travel","upbeat","uplifting","video","war"],SW=["danceable","non-danceable"],bW=["voice","instrumental"],pW=["acoustic","electronic"];function PQ(g){return typeof g=="object"&&g!==null&&"embedding"in g&&"classifier"in g}function vo(g){return typeof g=="object"&&g!==null&&"modelUrl"in g}function Ae(g,A){if(A)return A;const B=g.toLowerCase();return B.includes("effnet")||B.includes("discogs")?"effnet":B.includes("vggish")?"vggish":B.includes("tempocnn")||B.includes("tempo")&&!B.includes("temple")?"tempocnn":"musicnn"}function Nd(g,A){if(A)return A;const B=g.toLowerCase();return B.includes("jamendo")?"jamendo":B.includes("discogs400")||B.includes("discogs")?"discogs400":B.includes("tzanetakis")?"tzanetakis":B.includes("mtt_musicnn")?"mtt_musicnn":"jamendo"}function Wd(g){switch(g){case"jamendo":return MW;case"discogs400":return Vo;case"tzanetakis":return RW;case"mtt_musicnn":return yW;default:return Vo}}function qI(g){return PQ(g)?`${g.embedding} -> ${g.classifier}`:g.modelUrl}function YW(g){if(!g||g.length===0)return[];const A=g[0].length;if(A===0)return[];const B=[];for(let I=0;I<A;I++){const Q=g.reduce((E,i)=>E+(i[I]??0),0);B.push(Q/g.length)}return B}const Be={genre:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",classifier:"https://arweave.net/ZY-GSfMe7crJUITAtHITcoLCNfNWVP1HMwywivZ_LAQ/model.json",embeddingType:"effnet",classifierType:"discogs400"},mood:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",classifier:"https://arweave.net/BUXf3AoFuIsrNDkV2hW6BhiwSVTuFllWOUQv5mu6qQ8/model.json",embeddingType:"effnet"},danceability:{modelUrl:"https://turbo-gateway.com/nX9KX1OVhEaT1dStNcsRiZKCQTWuHjAMl4MWprIFyZU/model.json",modelType:"musicnn"}},Xo={discogs400:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/ZY-GSfMe7crJUITAtHITcoLCNfNWVP1HMwywivZ_LAQ/model.json",classifierType:"discogs400"},jamendo:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/MuhF5mek1BJPZLoPNY1TBTPUUBEXbVmMfAGgBp-_MyA/model.json",classifierType:"jamendo"},tzanetakis:{modelUrl:"https://arweave.net/7MQD4W5yJeUUK2tRg8TEdomew-ZY7s0K91nk35FxleM/model.json",modelType:"musicnn",genreType:"tzanetakis"},musicnn:{modelUrl:"https://arweave.net/KCZQ1geu4ymxp8axAql95FDY98VjnOzSymdkCiM9BXo/model.json",modelType:"musicnn",genreType:"mtt_musicnn"}},Ko={jamendo:{embedding:"https://arweave.net/tVO0RIu2Ly_Di5cZccw_wB3x6Vs_2KSqxhl8bdhhimE/model.json",embeddingType:"effnet",classifier:"https://arweave.net/BUXf3AoFuIsrNDkV2hW6BhiwSVTuFllWOUQv5mu6qQ8/model.json",classifierType:"jamendo"},happyMusicnn:{modelUrl:"https://arweave.net/kUIS-Xxr4k3MZ4K2gHdvMgZxK7aBYce_FPGIkGA_hjM/model.json",modelType:"musicnn"}},zo={default:{modelUrl:"https://arweave.net/nX9KX1OVhEaT1dStNcsRiZKCQTWuHjAMl4MWprIFyZU/model.json",modelType:"musicnn"}},UW={genre:Object.keys(Xo),mood:Object.keys(Ko),danceability:Object.keys(zo)};function fW(g){const A={};return g.genre&&(A.genre=Xo[g.genre]),g.mood&&(A.mood=Ko[g.mood]),g.danceability&&(A.danceability=zo[g.danceability]),A}class kW{constructor(A={}){this.essentiaWASM=null,this.essentiaModel=null,this.extractor=null,this.initialized=!1,this.embeddingModelCache=new Map,this.classifierModelCache=new Map,this.resolvedUrlCache=new Map,this.extractors=new Map;const B=A.preset?fW(A.preset):{};this.options={models:{genre:Be.genre,mood:Be.mood,danceability:Be.danceability,...B,...A.models},topN:5,threshold:.05,cacheEmbeddings:!0,enableModelCache:!0,...A},this.options.resolveUrl||(this.options.resolveUrl=TA.resolveUrl.bind(TA)),delete this.options.preset}async initializeEssentia(){if(this.initialized)return;const B=(await Promise.resolve().then(()=>Fb)).EssentiaWASM;await B.ready,this.essentiaWASM=B;const I=await Promise.resolve().then(()=>$z);this.essentiaModel=I,this.extractor=new I.EssentiaTFInputExtractor(this.essentiaWASM,"musicnn");const Q=new I.EssentiaTFInputExtractor(this.essentiaWASM,"vggish");this.extractors.set("vggish",Q),this.initialized=!0}async resolveUrlWithCache(A){if(this.resolvedUrlCache.has(A))return this.resolvedUrlCache.get(A);if(!this.options.resolveUrl)return A;try{const B=await this.options.resolveUrl(A);return this.resolvedUrlCache.set(A,B),B!==A&&console.info("[MusicClassifier] Resolved URL to alternate gateway",{originalUrl:A,resolvedUrl:B}),B}catch(B){return console.warn("[MusicClassifier] URL resolution failed, using original URL",{url:A,error:String(B)}),this.resolvedUrlCache.set(A,A),A}}async loadModelWithRetry(A,B=3,I=1e3){if(this.options.enableModelCache)return xI.getOrFetchGraphModel(IB,A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i),maxRetries:B,baseDelayMs:I});let Q=null;const E=await this.resolveUrlWithCache(A);for(let i=0;i<B;i++)try{return await IB.loadGraphModel(E)}catch(e){Q=e;const a=String(e).toLowerCase();if(!(a.includes("fetch")||a.includes("network")||a.includes("timeout")||a.includes("failed to fetch")||a.includes("networkerror"))||i===B-1)throw e;const o=I*Math.pow(2,i);console.warn(`[MusicClassifier] Model load failed (attempt ${i+1}/${B}), retrying in ${o}ms...`,{originalUrl:A,resolvedUrl:E,error:String(e)}),await new Promise(t=>setTimeout(t,o))}throw Q}async initializeEssentiaModelWithRetry(A,B,I=3,Q=1e3){for(let E=0;E<I;E++){const i=await this.resolveUrlWithCache(A),e=B==="vggish"?this.essentiaModel.TensorflowVGGish:this.essentiaModel.TensorflowMusiCNN,a=new e(IB,i);try{return await a.initialize(),a}catch(C){const o=String(C).toLowerCase();if(!(o.includes("fetch")||o.includes("network")||o.includes("timeout")||o.includes("failed to fetch")||o.includes("networkerror")||o.includes("404"))||E===I-1)throw C;TA.invalidateCacheForUrl(A);const s=Q*Math.pow(2,E);console.warn(`[MusicClassifier] Essentia model init failed (attempt ${E+1}/${I}), re-resolving and retrying in ${s}ms...`,{url:A,resolvedUrl:i,error:String(C)}),await new Promise(n=>setTimeout(n,s))}}throw new Error(`[MusicClassifier] Essentia model failed to load after ${I} retries: ${A}`)}async getEmbeddingModel(A,B){if(this.embeddingModelCache.has(A))return this.embeddingModelCache.get(A);const I=Ae(A,B);let Q;if(I==="effnet")Q=await this.loadModelWithRetry(A);else if(I==="vggish"){if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");if(this.options.enableModelCache){const E=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i)});Q=new this.essentiaModel.TensorflowVGGish(IB,E),await Q.initialize()}else Q=await this.initializeEssentiaModelWithRetry(A,"vggish")}else{if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");if(this.options.enableModelCache){const E=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:i=>TA.invalidateCacheForUrl(i)});Q=new this.essentiaModel.TensorflowMusiCNN(IB,E),await Q.initialize()}else Q=await this.initializeEssentiaModelWithRetry(A,"musicnn")}return this.options.cacheEmbeddings&&this.embeddingModelCache.set(A,Q),Q}clearEmbeddingCache(){for(const A of this.embeddingModelCache.values())A.dispose?A.dispose():A.terminate&&A.terminate();this.embeddingModelCache.clear()}clearClassifierCache(){for(const A of this.classifierModelCache.values())A.terminate&&A.terminate();this.classifierModelCache.clear()}clearAllCaches(){this.clearEmbeddingCache(),this.clearClassifierCache(),this.resolvedUrlCache.clear()}getFeaturesForArchitecture(A,B){switch(B){case"effnet":return this.extractor.computeFrameWise(A,256).melSpectrum;case"vggish":const I=this.extractors.get("vggish");return I?I.computeFrameWise(A,160):(console.warn("VGGish extractor not initialized, falling back to default 96-band extractor."),this.extractor.computeFrameWise(A,256));case"tempocnn":return console.warn("TempoCNN architecture requested but using default 96-band extractor. Results may be suboptimal for TempoCNN models."),this.extractor.computeFrameWise(A,256);default:return this.extractor.computeFrameWise(A,256)}}sliceAudioSignal(A,B){if(!this.options.analysisDurationSeconds&&this.options.analysisStartPosition===void 0)return A;const I=A.length,Q=I/B,E=Math.min(this.options.analysisDurationSeconds??Q,Q),i=this.options.analysisStartPosition??.5,e=Math.floor(E*B),a=I-e,C=Math.max(0,Math.floor(i*a)),o=Math.min(C+e,I);return console.info(`[MusicClassifier] Analyzing segment: ${E.toFixed(1)}s starting at ${i} (samples ${C}-${o} of ${I}, ${(C/B).toFixed(1)}s-${(o/B).toFixed(1)}s of ${Q.toFixed(1)}s)`),A.slice(C,o)}async analyze(A){try{await this.initializeEssentia();let B;if(typeof A=="string"){const C=await(await fetch(A)).arrayBuffer(),o=typeof self<"u"?self:typeof globalThis<"u"?globalThis:{},t=o.AudioContext||o.webkitAudioContext,s=o.OfflineAudioContext||o.webkitOfflineAudioContext,n=t?new t:s?new s(1,44100,44100):(()=>{throw new Error("No Web Audio API available in this context")})(),r=await n.decodeAudioData(C);B=await this.extractor.downsampleAudioBuffer(r,n.sampleRate)}else B=A.audioSignal;const I=this.sliceAudioSignal(B,16e3),Q=Math.max(0,Math.floor((I.length-512)/256)+1),E=[],i={genres:[],moods:[],mood_tags:[],vibe_metrics:{}};if(this.options.models?.genre){const e=this.options.models.genre,a=PQ(e)?e.classifier:e.modelUrl;let C;PQ(e)&&e.classifierType?C=e.classifierType:vo(e)&&e.genreType?C=e.genreType:C=Nd(a);const o=Wd(C);i.genres=await this.runModelPrediction(e,I,o),i.primary_genre=i.genres.length>0?i.genres[0].name:"Unknown",E.push(qI(e))}if(this.options.models?.mood){const e=this.options.models.mood,a=vo(e)?uW:mW;i.moods=await this.runModelPrediction(e,I,a),i.mood_tags=i.moods.slice(0,3).map(C=>C.name),E.push(qI(e))}if(this.options.models?.danceability){const e=this.options.models.danceability,C=(await this.runModelPrediction(e,I,SW)).find(o=>o.name==="danceable");i.vibe_metrics.danceability=C?.confidence??0,E.push(qI(e))}if(this.options.models?.voice){const e=this.options.models.voice,C=(await this.runModelPrediction(e,I,bW)).find(o=>o.name==="instrumental");i.vibe_metrics.instrumental_probability=C?.confidence??0,E.push(qI(e))}if(this.options.models?.acoustic){const e=this.options.models.acoustic,C=(await this.runModelPrediction(e,I,pW)).find(o=>o.name==="electronic");i.vibe_metrics.electronic_probability=C?.confidence??0,E.push(qI(e))}if(i.moods){const e=i.moods.find(C=>C.name==="energetic"||C.name==="upbeat"||C.name==="epic"),a=i.moods.find(C=>C.name==="happy"||C.name==="positive"||C.name==="uplifting");e&&(i.vibe_metrics.energy=e.confidence),a&&(i.vibe_metrics.valence=a.confidence)}return{genres:i.genres||[],moods:i.moods||[],primary_genre:i.primary_genre||"Unknown",mood_tags:i.mood_tags||[],vibe_metrics:i.vibe_metrics,analysis_metadata:{models_used:E,model_used:E.length>0?E[0]:void 0,frames_analyzed:Q,duration_analyzed:I.length/16e3,analyzed_at:new Date().toISOString()}}}catch(B){throw console.error("Music analysis failed:",B),B}}async runClassifierOnEmbeddings(A,B){if(B.length===0||B[0].length===0)return console.warn("Empty embeddings provided to classifier"),[];const I=await this.loadModelWithRetry(A),Q=IB.tensor2d(B);let E=[];try{const i=I.predict(Q),e=await i.data(),a=i.shape,C=a[a.length-1],o=Math.max(1,Math.floor(e.length/C)),t=new Array(C).fill(0);for(let s=0;s<o;s++)for(let n=0;n<C;n++)t[n]+=e[s*C+n];E=t.map(s=>s/o),i.dispose()}finally{Q.dispose(),I.dispose()}return E}async predictWithTwoStepModel(A,B){const I=Ae(A.embedding,A.embeddingType),Q=this.getFeaturesForArchitecture(B,I);if(Q.length===0)return console.warn("No features extracted from audio signal"),[];const E=await this.getEmbeddingModel(A.embedding,A.embeddingType);let i;return I==="effnet"?i=await this.runEffnetEmbedding(E,Q):I==="vggish"?i=await this.runEssentiaEmbedding(E,Q):i=await this.runEssentiaEmbedding(E,Q),i.length===0?(console.warn("No embeddings produced by embedding model"),[]):await this.runClassifierOnEmbeddings(A.classifier,i)}getModelInputNames(A){const B=[];try{if(A.inputs&&Array.isArray(A.inputs))for(const I of A.inputs)I&&I.name&&B.push(I.name);if(B.length===0&&A.executor){const I=A.executor;if(I._signature&&I._signature.inputs)for(const Q of Object.keys(I._signature.inputs))B.push(Q)}if(B.length===0&&A.inputNodes){const I=A.inputNodes;Array.isArray(I)&&B.push(...I)}if(B.length===0&&A.metadata){const I=A.metadata;if(I.signature&&I.signature.inputs)for(const Q of Object.keys(I.signature.inputs))B.push(Q)}if(B.length===0){const I=A;if(I.graph&&I.graph.inputs)for(const Q of Object.keys(I.graph.inputs))B.push(Q)}}catch(I){console.warn("[MusicClassifier] Error discovering input names:",I)}return B}async runEffnetEmbedding(A,B){const I=B.length,Q=64,E=128,i=96;if(I===0)return[];(B[0]?.length??0)!==i&&console.warn(`[MusicClassifier] EffNet expects ${i}-band mel features but got ${B[0]?.length}-band frames. Results will be unreliable.`);const e=[],a=Math.max(0,I-E),C=new Array(i).fill(0),o=new Float32Array(Q*E*i);for(let s=0;s<Q;s++){const n=Math.round(s*a/(Q-1));for(let r=0;r<E;r++){const c=B[n+r]??C,G=(s*E+r)*i;for(let D=0;D<i;D++)o[G+D]=c[D]??0}}const t=IB.tensor3d(o,[Q,E,i]);try{const s=this.getModelInputNames(A);if(s.length===0)throw new Error("Could not discover model input names");const n={};for(const y of s)y.includes("melspectrogram")||y.includes("mel")||y.includes("spectrogram")?n[y]=t:y.includes("saver")||y.includes("filename")?n[y]=IB.scalar(""):n[y]=t;const r=A.execute(n);let c;if(Array.isArray(r)?c=r[0]:r&&typeof r=="object"&&!("data"in r)?c=Object.values(r)[0]:c=r,!c||typeof c.data!="function")throw new Error("Model execution did not return a valid tensor");const G=await c.data(),D=c.shape,h=D[D.length-1],w=Math.max(1,Math.floor(G.length/h));for(let y=0;y<w;y++)e.push(Array.from(G.slice(y*h,(y+1)*h)));c.dispose();for(const[y,R]of Object.entries(n))R!==t&&R.dispose()}finally{t.dispose()}return e}async runEssentiaEmbedding(A,B){const I=await A.predict(B,!0);return!I||I.length===0?[]:I}async runModelPrediction(A,B,I){let Q;if(PQ(A))Q=await this.predictWithTwoStepModel(A,B);else{const E=Ae(A.modelUrl,A.modelType),i=this.getFeaturesForArchitecture(B,E);Q=await this.predictWithModel(A.modelUrl,i)}return this.mapPredictions(Q,I)}async predictWithModel(A,B){if(!this.essentiaModel)throw new Error("Essentia not initialized. Call initializeEssentia() first.");let I;if(this.options.enableModelCache){const e=await xI.getOrFetchEssentiaUrl(A,{resolveUrl:this.options.resolveUrl,invalidateUrlCache:a=>TA.invalidateCacheForUrl(a)});I=new this.essentiaModel.TensorflowMusiCNN(IB,e),await I.initialize()}else I=await this.initializeEssentiaModelWithRetry(A,"musicnn");const Q=await I.predict(B,!0);if(!Q||Q.length===0)return[];const E=Q[0].length,i=[];for(let e=0;e<E;e++){const a=Q.reduce((C,o)=>C+o[e],0);i.push(a/Q.length)}return I.terminate&&I.terminate(),i}mapPredictions(A,B){return A.length>0&&A.length!==B.length&&console.warn(`[MusicClassifier] Model returned ${A.length} outputs but ${B.length} labels are configured — tags may be mislabeled. Check that the label list matches the model.`),A.map((I,Q)=>({name:B[Q]||`unknown_${Q}`,confidence:I})).sort((I,Q)=>Q.confidence-I.confidence).filter(I=>I.confidence>=this.options.threshold).slice(0,this.options.topN)}}class ZW{constructor(A={}){this.canvas=null,this.context=null,typeof document<"u"&&(this.canvas=document.createElement("canvas"),this.canvas.width=100,this.canvas.height=100,this.context=this.canvas.getContext("2d",{willReadFrequently:!0}))}async extractPalette(A){try{if(!this.context||!this.canvas)throw new Error("Canvas not supported in this environment");const B=await TA.resolveUrl(A),I=await this.loadImage(B);this.context.drawImage(I,0,0,100,100);const Q=this.context.getImageData(0,0,100,100),E=this.getPixels(Q);if(E.length===0)return this.getFallbackPalette();let i=this.kMeans(E,4);i.length<4&&(i=this.medianCut(E,4));const e=this.sortColorsByFrequency(E,i);return this.analyzePalette(e)}catch(B){return console.warn("Color extraction failed, using fallback:",B),this.getFallbackPalette()}}loadImage(A){return new Promise((B,I)=>{const Q=new Image;Q.crossOrigin="Anonymous",Q.onload=()=>B(Q),Q.onerror=E=>I(E),Q.src=A})}getPixels(A){const B=[];for(let I=0;I<A.data.length;I+=4){const Q=A.data[I],E=A.data[I+1],i=A.data[I+2];A.data[I+3]>=128&&B.push([Q,E,i])}return B}kMeans(A,B){if(A.length===0)return[];if(A.length<B)return A;let I=[];const Q=new Set;for(;I.length<B&&I.length<A.length;){const E=Math.floor(Math.random()*A.length);Q.has(E)||(Q.add(E),I.push([...A[E]]))}for(let E=0;E<10;E++){const i=Array.from({length:B},()=>[]);for(const a of A){let C=1/0,o=0;for(let t=0;t<I.length;t++){const s=this.distance(a,I[t]);s<C&&(C=s,o=t)}i[o].push(a)}const e=[];for(let a=0;a<B;a++)if(i[a].length===0)e.push(I[a]);else{const C=i[a].reduce((o,t)=>[o[0]+t[0],o[1]+t[1],o[2]+t[2]],[0,0,0]);e.push([Math.round(C[0]/i[a].length),Math.round(C[1]/i[a].length),Math.round(C[2]/i[a].length)])}if(e.every((a,C)=>a[0]===I[C][0]&&a[1]===I[C][1]&&a[2]===I[C][2]))break;I=e}return I}medianCut(A,B){if(A.length===0)return[];const I=[],Q=[{pixels:[...A],depth:0}];for(;Q.length<B&&Q.length>0;){let E=Q[0],i=0,e=0;for(let o=0;o<Q.length;o++){const t=this.getBoxRange(Q[o].pixels);t>e&&(e=t,E=Q[o],i=o)}if(E.pixels.length<=1)break;const a=this.sortPixelsByChannel(E.pixels,E.depth%3),C=Math.floor(a.length/2);Q.splice(i,1),Q.push({pixels:a.slice(0,C),depth:E.depth+1},{pixels:a.slice(C),depth:E.depth+1})}for(const E of Q)if(E.pixels.length>0){const i=E.pixels.reduce((e,a)=>[e[0]+a[0],e[1]+a[1],e[2]+a[2]],[0,0,0]);I.push([Math.round(i[0]/E.pixels.length),Math.round(i[1]/E.pixels.length),Math.round(i[2]/E.pixels.length)])}return I.slice(0,B)}getBoxRange(A){if(A.length===0)return 0;let B=1/0,I=-1/0,Q=1/0,E=-1/0,i=1/0,e=-1/0;for(const[a,C,o]of A)B=Math.min(B,a),I=Math.max(I,a),Q=Math.min(Q,C),E=Math.max(E,C),i=Math.min(i,o),e=Math.max(e,o);return Math.max(I-B,E-Q,e-i)}sortPixelsByChannel(A,B){return[...A].sort((I,Q)=>I[B]-Q[B])}distance(A,B){return Math.sqrt(Math.pow(A[0]-B[0],2)+Math.pow(A[1]-B[1],2)+Math.pow(A[2]-B[2],2))}sortColorsByFrequency(A,B){const I=new Map;for(const Q of A){let E=B[0],i=1/0;for(const a of B){const C=this.distance(Q,a);C<i&&(i=C,E=a)}const e=`${E[0]},${E[1]},${E[2]}`;I.set(e,(I.get(e)||0)+1)}return B.sort((Q,E)=>{const i=`${Q[0]},${Q[1]},${Q[2]}`,e=`${E[0]},${E[1]},${E[2]}`;return(I.get(e)||0)-(I.get(i)||0)})}analyzePalette(A){const B=A.map(C=>this.rgbToHex(C[0],C[1],C[2]));if(B.length===0)return this.getFallbackPalette();const I=B[0],Q=B[1],E=B[2],i=this.getBrightness(A[0]),e=this.getSaturation(A[0]),a=e<.1;return{colors:B,primary_color:I,secondary_color:Q,accent_color:E,brightness:i/255,saturation:e,is_monochrome:a}}getBrightness(A){return(A[0]*299+A[1]*587+A[2]*114)/1e3}getSaturation(A){const B=Math.max(A[0],A[1],A[2]),I=Math.min(A[0],A[1],A[2]);return B===0?0:(B-I)/B}rgbToHex(A,B,I){return"#"+[A,B,I].map(Q=>{const E=Q.toString(16);return E.length===1?"0"+E:E}).join("").toUpperCase()}getFallbackPalette(){return{colors:["#000000","#333333","#666666"],primary_color:"#000000",secondary_color:"#333333",accent_color:"#666666",brightness:0,saturation:0,is_monochrome:!0}}}const To=VA.for("BeatSubdivider"),NW={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};class LI{constructor(A){this.options={...NW,...A},To.debug("BeatSubdivider initialized",{options:this.options})}subdivide(A,B=CC){if(To.debug("Starting per-beat subdivision",{audioId:A.audioId,beatCount:A.beats.length,explicitAssignments:B.beatSubdivisions.size,defaultSubdivision:B.defaultSubdivision}),sC(B),PG(B,A.beats.length),A.beats.length===0)return this.createEmptySubdividedBeatMap(A,B);const I=new Set;let Q=0;const E=!!A.tempoSections&&A.tempoSections.length>1;let i=0;const e=c=>{let G=0;for(let D=c-1;D>=0&&(B.beatSubdivisions.get(D)??B.defaultSubdivision)==="triplet4";D--)G++;return G%2},a=[];for(let c=0;c<A.beats.length;c++){const G=B.beatSubdivisions.get(c)??B.defaultSubdivision;B.beatSubdivisions.has(c)&&i++,I.add(G);const D=tC(G);Q=Math.max(Q,D);const h=A.beats[c],w=c<A.beats.length-1?A.beats[c+1]:null,y=A.detectedBeatIndices.includes(c);if(G==="rest"||G==="half"&&h.beatInMeasure%2!==0)continue;const R=G==="dotted4"&&h.beatInMeasure%2!==0;if(G==="offbeat8"||G==="triplet4"&&e(c)===1||R||a.push({...h,isDetected:y,originalBeatIndex:c,subdivisionType:G}),w){const k=this.createInterpolatedBeatsForSubdivision(h,w,c,G,A,E,B,e(c));a.push(...k)}}const C=this.buildDetectedBeatIndices(a),o=A.beats.length,t=a.length,s=o>0?t/o:1,n={originalBeatCount:o,subdividedBeatCount:t,averageDensityMultiplier:s,explicitBeatCount:i,subdivisionsUsed:Array.from(I),hasMultipleTempos:E,maxDensity:Q},r={audioId:A.audioId,duration:A.duration,beats:a,detectedBeatIndices:C,subdivisionConfig:B,downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections,subdivisionMetadata:n};return To.debug("Per-beat subdivision complete",{originalBeats:o,subdividedBeats:t,densityMultiplier:s,subdivisionsUsed:Array.from(I)}),r}createInterpolatedBeatsForSubdivision(A,B,I,Q,E,i,e,a=0){const C=[],o=E.quarterNoteInterval,t=i?this.getQuarterNoteIntervalForTimestamp(E,A.timestamp):o;switch(Q){case"quarter":break;case"half":break;case"eighth":C.push(this.createInterpolatedBeat(A,B,.5,"eighth",t,this.options));break;case"sixteenth":for(let n=.25;n<1;n+=.25)C.push(this.createInterpolatedBeat(A,B,n,"sixteenth",t,this.options));break;case"triplet8":for(const n of[1/3,2/3])C.push(this.createInterpolatedBeat(A,B,n,"triplet8",t,this.options));break;case"triplet4":{a===0?C.push(this.createInterpolatedBeat(A,B,2/3,"triplet4",t,this.options)):(e.beatSubdivisions.get(I-1)??e.defaultSubdivision)==="triplet4"&&C.push(this.createInterpolatedBeat(A,B,1/3,"triplet4",t,this.options));break}case"dotted4":A.beatInMeasure%2!==0&&C.push(this.createInterpolatedBeat(A,B,.5,"dotted4",t,this.options));break;case"dotted8":C.push(this.createInterpolatedBeat(A,B,3/4,"dotted8",t,this.options));break;case"swing":C.push(this.createInterpolatedBeat(A,B,2/3,"swing",t,this.options));break;case"offbeat8":C.push(this.createInterpolatedBeat(A,B,.5,"offbeat8",t,this.options));break;case"rest":break;default:const s=Q;throw new Error(`Unknown subdivision type: ${s}`)}return C}createEmptySubdividedBeatMap(A,B){return{audioId:A.audioId,duration:A.duration,beats:[],detectedBeatIndices:[],subdivisionConfig:B,downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections,subdivisionMetadata:{originalBeatCount:0,subdividedBeatCount:0,averageDensityMultiplier:1,explicitBeatCount:0,subdivisionsUsed:[B.defaultSubdivision],hasMultipleTempos:!!A.tempoSections&&A.tempoSections.length>1,maxDensity:1}}}createInterpolatedBeat(A,B,I,Q,E,i){const e=B.timestamp-A.timestamp,a=A.timestamp+e*I,C=A.beatInMeasure+I,o=(A.intensity+B.intensity)/2,t=(A.confidence+B.confidence)/2;return{timestamp:a,beatInMeasure:C,isDownbeat:!1,measureNumber:A.measureNumber,intensity:o,confidence:t,isDetected:!1,subdivisionType:Q}}findClosestBeat(A,B){let I=A[0],Q=Math.abs(A[0].timestamp-B);for(const E of A){const i=Math.abs(E.timestamp-B);i<Q&&(Q=i,I=E)}return I}calculateBeatInMeasure(A,B,I){return(A-B)/I%4}calculateMeasureNumber(A,B,I,Q){const i=(A-B)/I,e=Q.segments[0]?.timeSignature?.beatsPerMeasure??4;return Math.floor(i/e)}buildDetectedBeatIndices(A){const B=[];for(let I=0;I<A.length;I++)A[I].isDetected&&B.push(I);return B}getTempoSectionForTimestamp(A,B){if(!(!A||A.length===0)){for(const I of A)if(B>=I.start&&B<I.end)return I;return A[A.length-1]}}getQuarterNoteIntervalForTimestamp(A,B){const I=this.getTempoSectionForTimestamp(A.tempoSections,B);return I?I.intervalSeconds:A.quarterNoteInterval}getTempoSectionForBeatIndex(A,B){if(!(!A||A.length===0)){for(const I of A)if(B>=I.startBeatIndex&&B<=I.endBeatIndex)return I;return A[A.length-1]}}static toJSON(A){const B=Array.from(A.subdivisionConfig.beatSubdivisions.entries()),I={audioId:A.audioId,duration:A.duration,beats:A.beats.map(Q=>({timestamp:Q.timestamp,beatInMeasure:Q.beatInMeasure,isDownbeat:Q.isDownbeat,measureNumber:Q.measureNumber,intensity:Q.intensity,confidence:Q.confidence,requiredKey:Q.requiredKey,isDetected:Q.isDetected,originalBeatIndex:Q.originalBeatIndex,subdivisionType:Q.subdivisionType})),detectedBeatIndices:A.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:B,defaultSubdivision:A.subdivisionConfig.defaultSubdivision},downbeatConfig:A.downbeatConfig,tempoSections:A.tempoSections?.map(Q=>({start:Q.start,end:Q.end,bpm:Q.bpm,intervalSeconds:Q.intervalSeconds,beatCount:Q.beatCount,startBeatIndex:Q.startBeatIndex,endBeatIndex:Q.endBeatIndex})),subdivisionMetadata:{originalBeatCount:A.subdivisionMetadata.originalBeatCount,subdividedBeatCount:A.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:A.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:A.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:A.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:A.subdivisionMetadata.hasMultipleTempos,maxDensity:A.subdivisionMetadata.maxDensity}};return JSON.stringify(I)}static fromJSON(A){const B=JSON.parse(A),I=new Map(B.subdivisionConfig.beatSubdivisions||[]);return{audioId:B.audioId,duration:B.duration,beats:B.beats.map(Q=>({timestamp:Q.timestamp,beatInMeasure:Q.beatInMeasure,isDownbeat:Q.isDownbeat,measureNumber:Q.measureNumber,intensity:Q.intensity,confidence:Q.confidence,requiredKey:Q.requiredKey,isDetected:Q.isDetected,originalBeatIndex:Q.originalBeatIndex,subdivisionType:Q.subdivisionType})),detectedBeatIndices:B.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:I,defaultSubdivision:B.subdivisionConfig.defaultSubdivision},downbeatConfig:B.downbeatConfig,tempoSections:B.tempoSections?.map(Q=>({start:Q.start,end:Q.end,bpm:Q.bpm,intervalSeconds:Q.intervalSeconds,beatCount:Q.beatCount,startBeatIndex:Q.startBeatIndex,endBeatIndex:Q.endBeatIndex})),subdivisionMetadata:{originalBeatCount:B.subdivisionMetadata.originalBeatCount,subdividedBeatCount:B.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:B.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:B.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:B.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:B.subdivisionMetadata.hasMultipleTempos,maxDensity:B.subdivisionMetadata.maxDensity}}}static async saveToFile(A,B){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:I}=await Promise.resolve().then(()=>Eg),Q=LI.toJSON(A);await I(B,Q,"utf-8")}static async loadFromFile(A){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:B}=await Promise.resolve().then(()=>Eg),I=await B(A,"utf-8");return LI.fromJSON(I)}}class WW{constructor(A){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0,this.previousDirection="neutral",this.options={...gh,...A}}setDifficulty(A){const B=ih(A.preset,A.customPenalties);this.options.hotnessLossOnMiss=B.hotnessLossOnMiss,this.options.hotnessLossOnBreak=B.hotnessLossOnBreak}recordHit(A,B,I=0,Q="perfect"){if(this.hitCount++,this.lastBpm=B,Q==="miss"||Q==="wrongKey")return this.recordMiss(I);const E=this.pocketDirection;this.recentOffsets.push(A),this.recentOffsets.length>this.options.averagingWindowSize&&this.recentOffsets.shift(),this.updateRunningAverage(),this.pocketDirection=this.determineDirection(this.establishedOffset);const i=this.calculatePocketWindow(B),e=Math.abs(A-this.establishedOffset),a=this.calculateConsistency(e,i),C=this.hasPocket()&&e<=i;this.hasPocket()&&(C?(this.hotness=this.hotness+this.options.hotnessGainPerHit,this.streakLength++):this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnBreak),this.maxGrooveStreak=Math.max(this.maxGrooveStreak,this.streakLength));let o;const t=E!==this.pocketDirection,s=t||this.hotness===0;if(this.hotness>0&&!s)this.grooveStartTime===null&&(this.grooveStartTime=I??this.hitCount*(60/B)),this.maxHotness=Math.max(this.maxHotness,this.hotness),this.hotnessSamples.push(this.hotness),this.grooveHitCount++;else if(s&&this.grooveStartTime!==null&&this.grooveHitCount>0){const n=I??this.hitCount*(60/B),r=this.hotnessSamples.length>0?this.hotnessSamples.reduce((c,G)=>c+G,0)/this.hotnessSamples.length:0;o={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:r,duration:n-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:n},this.streakLength=0,this.hotness=0,this.resetGrooveStats()}return t&&(this.streakLength=0,this.hotness=0,this.resetGrooveStats()),this.previousDirection=this.pocketDirection,{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:a,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,inPocket:C,pocketWindow:i,endedGrooveStats:o}}recordMiss(A){const I=this.hotness>0&&this.grooveStartTime!==null&&this.grooveHitCount>0;this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnMiss),this.streakLength=0;const Q=this.calculatePocketWindow(this.lastBpm);let E;if(I&&this.hotness===0){const i=A??this.hitCount*(60/this.lastBpm),e=this.hotnessSamples.length>0?this.hotnessSamples.reduce((a,C)=>a+C,0)/this.hotnessSamples.length:0;E={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:e,duration:i-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:i},this.resetGrooveStats()}return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:0,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,inPocket:!1,pocketWindow:Q,endedGrooveStats:E}}getState(){const A=this.hotnessSamples.length>0?this.hotnessSamples.reduce((B,I)=>B+I,0)/this.hotnessSamples.length:0;return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,hotness:this.hotness,tier:gi(this.hotness),streakLength:this.streakLength,hitCount:this.hitCount,pocketWindow:this.calculatePocketWindow(this.lastBpm),grooveStartTime:this.grooveStartTime,grooveDuration:this.grooveStartTime!==null?this.grooveHitCount*(60/this.lastBpm):0,maxHotness:this.maxHotness,avgHotness:A,grooveHitCount:this.grooveHitCount}}reset(){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.resetGrooveStats()}getGrooveStats(A){if(this.grooveStartTime===null||this.grooveHitCount===0)return null;const B=A??this.hitCount*(60/this.lastBpm),I=this.hotnessSamples.length>0?this.hotnessSamples.reduce((Q,E)=>Q+E,0)/this.hotnessSamples.length:0;return{maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:I,duration:B-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:B}}resetGrooveStats(){this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0}hasPocket(){return this.hitCount>=this.options.minHitsForPocket}calculatePocketWindow(A){const I=Bh(this.hotness)/1e3,Q=120/A;return I*Q}updateRunningAverage(){this.recentOffsets.length!==0&&(this.establishedOffset=this.recentOffsets.reduce((A,B)=>A+B,0)/this.recentOffsets.length)}calculateConsistency(A,B){if(!this.hasPocket())return 0;const I=A/B;return I>=1?0:1-I*I}determineDirection(A){return Math.abs(A)<this.options.neutralDeadZone?"neutral":A<0?"push":"pull"}}function HW(g,A=CC,B){const I=$i(g);return new LI(B).subdivide(I,A)}function gI(g){return"mergedBeats"in g?g.mergedBeats:g.beats}function Oo(g,A){return"mergedBeats"in g?{...g,mergedBeats:A}:{...g,beats:A}}function JW(g,A,B){const I=gI(g);if(A<0||A>=I.length)throw new Error(`beatIndex ${A} is out of bounds (beats array length: ${I.length})`);const Q=I.map((E,i)=>{if(i!==A)return E;if(B===null){const{requiredKey:e,...a}=E;return a}return{...E,requiredKey:B}});return Oo(g,Q)}function xW(g,A){const B=gI(g),I=B.length;for(const i of A)if(i.beatIndex<0||i.beatIndex>=I)throw new Error(`beatIndex ${i.beatIndex} is out of bounds (beats array length: ${I})`);const Q=new Map;for(const{beatIndex:i,key:e}of A)Q.set(i,e);const E=B.map((i,e)=>{const a=Q.get(e);if(a===void 0)return i;if(a===null){const{requiredKey:C,...o}=i;return o}return{...i,requiredKey:a}});return Oo(g,E)}function qW(g){const A=gI(g),B=new Map;for(let I=0;I<A.length;I++){const Q=A[I];Q.requiredKey!==void 0&&B.set(I,Q.requiredKey)}return B}function LW(g){const B=gI(g).map(I=>{if(I.requiredKey===void 0)return I;const{requiredKey:Q,...E}=I;return E});return Oo(g,B)}function VW(g){return gI(g).some(B=>B.requiredKey!==void 0)}function vW(g){return gI(g).filter(B=>B.requiredKey!==void 0).length}function XW(g){const A=gI(g),B=new Set;for(const I of A)I.requiredKey!==void 0&&B.add(I.requiredKey);return Array.from(B).sort()}const vA=VA.for("SubdivisionPlaybackController");class KW{constructor(A,B,I={}){this.unifiedMap=A,this.audioContext=B,this.options={..._G,...I};const Q={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};this.subdivider=new LI(Q),this.state={isRunning:!1,isPaused:!1,currentTime:0,startTime:0,pauseTime:0,rafId:null,currentSubdivision:this.options.initialSubdivision,pendingSubdivision:null,pendingMeasureNumber:null,transitionTimestamp:null,scheduledBeats:new Map,thresholds:$E("medium")},this.subscribers=new Set,this.regenerateBeats(),vA.debug("SubdivisionPlaybackController initialized",{audioId:A.audioId,duration:A.duration,initialSubdivision:this.state.currentSubdivision,beatCount:A.beats.length})}get subdivision(){return this.state.currentSubdivision}get beatMap(){return this.unifiedMap}getOptions(){return{...this.options}}subscribe(A){return this.subscribers.add(A),()=>{this.subscribers.delete(A)}}setSubdivision(A){if(!Bi(A))throw new Error(`Invalid subdivision type: ${A}`);if(this.state.currentSubdivision===A)return;const B=this.state.currentSubdivision;switch(vA.debug("Subdivision change requested",{from:B,to:A,transitionMode:this.options.transitionMode}),this.options.transitionMode){case"immediate":this.applySubdivisionChange(A,B);break;case"next-downbeat":this.state.pendingSubdivision=A,this.state.pendingMeasureNumber=null,vA.debug("Subdivision change deferred until next downbeat",{pendingType:A,transitionMode:this.options.transitionMode});break;case"next-measure":{const Q=this.getCurrentBeat()?.measureNumber??null;this.state.pendingSubdivision=A,this.state.pendingMeasureNumber=Q,vA.debug("Subdivision change deferred until next measure",{pendingType:A,transitionMode:this.options.transitionMode,currentMeasureNumber:Q});break}default:this.applySubdivisionChange(A,B)}}setTransitionMode(A){if(!["immediate","next-downbeat","next-measure"].includes(A))throw new Error(`Invalid transition mode: ${A}`);const B=this.options.transitionMode;B!==A&&(vA.debug("Transition mode change requested",{from:B,to:A}),this.options={...this.options,transitionMode:A},A==="immediate"&&this.state.pendingSubdivision&&(vA.debug("Applying pending subdivision immediately due to mode switch",{pendingSubdivision:this.state.pendingSubdivision}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision)),vA.debug("Transition mode changed",{from:B,to:A}))}applySubdivisionChange(A,B){this.state.currentSubdivision=A,this.state.pendingSubdivision=null,this.state.pendingMeasureNumber=null;const I=this.state.transitionTimestamp;if(this.state.transitionTimestamp=null,this.regenerateBeats(I),this.options.onSubdivisionChange)try{this.options.onSubdivisionChange(B,A)}catch(Q){vA.error("Error in onSubdivisionChange callback",Q)}vA.debug("Subdivision changed",{from:B,to:A,transitionTimestamp:I})}regenerateBeats(A=null){const B={beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},I=this.subdivider.subdivide(this.unifiedMap,B),Q=new Map;if(A!==null)for(const[E,i]of this.state.scheduledBeats)i.beat.timestamp<A&&Q.set(E,i);this.state.scheduledBeats.clear();for(const[E,i]of Q)this.state.scheduledBeats.set(E,i);for(const E of I.beats){if(A!==null&&E.timestamp<A)continue;const i=this.getBeatKey(E);this.state.scheduledBeats.has(i)||this.state.scheduledBeats.set(i,{beat:E,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1})}vA.debug("Beats regenerated",{subdivision:this.state.currentSubdivision,beatCount:I.beats.length,preservedBeatCount:Q.size,totalScheduledBeats:this.state.scheduledBeats.size,fromTimestamp:A})}getBeatKey(A){return`${A.timestamp.toFixed(6)}-${A.subdivisionType}`}play(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate(),vA.debug("Playback started",{startTime:this.state.startTime,pauseTime:this.state.pauseTime}))}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.regenerateBeats(),vA.debug("Playback stopped")}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),vA.debug("Playback paused",{pauseTime:this.state.pauseTime}))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate(),vA.debug("Playback resumed",{startTime:this.state.startTime}))}seek(A){const B=Math.max(0,Math.min(A,this.unifiedMap.duration));this.state.pauseTime=B,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-B),this.regenerateBeats(),vA.debug("Seeked to time",{time:B})}getBeatsInRange(A,B){const I=[];for(const[,Q]of this.state.scheduledBeats){const E=Q.beat;E.timestamp>=A&&E.timestamp<=B&&I.push(E)}return I.sort((Q,E)=>Q.timestamp-E.timestamp),I}getUpcomingBeats(A){const B=this.getCurrentAudioTime(),I=[];for(const[,Q]of this.state.scheduledBeats){const E=Q.beat,i=E.timestamp-B;if(i>=0&&i<=this.options.anticipationTime&&(I.push(E),I.length>=A))break}return I.sort((Q,E)=>Q.timestamp-E.timestamp),I}getBeatAtTime(A){const B=this.options.timingTolerance;for(const[,I]of this.state.scheduledBeats)if(Math.abs(I.beat.timestamp-A)<=B)return I.beat;return null}getCurrentBeat(){const A=this.getCurrentAudioTime();let B=null;for(const[,I]of this.state.scheduledBeats)if(I.beat.timestamp<=A)B=I.beat;else break;return B}getNextBeat(){const A=this.getCurrentAudioTime(),B=Array.from(this.state.scheduledBeats.values()).map(I=>I.beat).sort((I,Q)=>I.timestamp-Q.timestamp);for(const I of B)if(I.timestamp>A)return I;return null}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let A=0;if(this.options.compensateOutputLatency){const B=this.audioContext.outputLatency??0,I=this.audioContext.baseLatency??0;A+=B+I}return A+=this.options.userOffsetMs/1e3,A}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const A=this.getCurrentAudioTime();this.checkPendingSubdivisionChange(A);for(const B of this.state.scheduledBeats.values()){const Q=B.beat.timestamp-A;!B.upcomingEmitted&&Q<=this.options.anticipationTime&&Q>0&&(this.emitEvent(B.beat,"upcoming",A,Q),B.upcomingEmitted=!0),!B.exactEmitted&&Math.abs(Q)<=this.options.timingTolerance&&(this.emitEvent(B.beat,"exact",A,Q),B.exactEmitted=!0),!B.passedEmitted&&Q<-this.options.timingTolerance&&(this.emitEvent(B.beat,"passed",A,Q),B.passedEmitted=!0)}}checkPendingSubdivisionChange(A){if(!this.state.pendingSubdivision)return;const B=this.getCurrentBeat();if(!B)return;let I=!1;if(this.options.transitionMode==="next-downbeat")I=B.isDownbeat;else if(this.options.transitionMode==="next-measure"){const Q=this.state.pendingMeasureNumber;Q!==null?I=B.measureNumber>Q&&Math.floor(B.beatInMeasure)===0:I=B.beatInMeasure===0}I&&(this.state.transitionTimestamp=B.timestamp,vA.debug("Applying pending subdivision change",{newSubdivision:this.state.pendingSubdivision,transitionMode:this.options.transitionMode,currentMeasureNumber:B.measureNumber,pendingMeasureNumber:this.state.pendingMeasureNumber,beatInMeasure:B.beatInMeasure,transitionTimestamp:this.state.transitionTimestamp}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision))}emitEvent(A,B,I,Q){const E={beat:A,currentSubdivision:this.state.currentSubdivision,audioTime:I,timeUntilBeat:Q,type:B};for(const i of this.subscribers)try{i(E)}catch(e){vA.error("SubdivisionPlaybackController callback error:",e)}}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.unifiedMap.duration}checkButtonPress(A,B){const I=B??this.state.thresholds,Q=Array.from(this.state.scheduledBeats.values()).map(o=>o.beat);let E=null,i=1/0;for(const o of Q){const t=A-o.timestamp,s=Math.abs(t);s<i&&(i=s,E=o)}if(!E)return{accuracy:"miss",offset:A,matchedBeat:null,absoluteOffset:A,keyMatch:!0,pressedKey:void 0,requiredKey:void 0};const e=A-E.timestamp,a=Math.abs(e);let C;return a<=I.perfect?C="perfect":a<=I.great?C="great":a<=I.good?C="good":a<=I.ok?C="ok":C="miss",{accuracy:C,offset:e,matchedBeat:E,absoluteOffset:a,keyMatch:!0,pressedKey:void 0,requiredKey:void 0}}setBeatMap(A){this.unifiedMap=A,this.regenerateBeats(),vA.debug("Beat map updated",{audioId:A.audioId,beatCount:A.beats.length})}forceUpdate(){this.update()}getSubdividedBeatMap(){const A=[];for(const[,Q]of this.state.scheduledBeats)A.push(Q.beat);A.sort((Q,E)=>Q.timestamp-E.timestamp);const B=new Set(A.map(Q=>Q.subdivisionType)),I=A.filter(Q=>Q.isDetected).length;return{audioId:this.unifiedMap.audioId,duration:this.unifiedMap.duration,beats:A,detectedBeatIndices:[],subdivisionConfig:{beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},downbeatConfig:this.unifiedMap.downbeatConfig,subdivisionMetadata:{originalBeatCount:I,subdividedBeatCount:A.length,averageDensityMultiplier:A.length/Math.max(1,this.unifiedMap.beats.length),explicitBeatCount:0,subdivisionsUsed:Array.from(B),hasMultipleTempos:!1,maxDensity:Math.max(...Array.from(B).map(Q=>Q==="eighth"?2:Q==="sixteenth"?4:Q==="triplet8"||Q==="triplet4"?3:1))}}}dispose(){this.stop(),this.subscribers.clear(),this.state.scheduledBeats.clear(),vA.debug("SubdivisionPlaybackController disposed")}}const zW=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],TW={stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5};class jo{constructor(A){this.config={level_thresholds:A?.level_thresholds||zW,xp_per_second:A?.xp_per_second??1,xp_per_track_completion:A?.xp_per_track_completion??50,activity_bonuses:{...TW,...A?.activity_bonuses},track_mastery_threshold:A?.track_mastery_threshold??10,mastery_bonus_xp:A?.mastery_bonus_xp??100}}calculateSessionXP(A,B){let I=A.duration_seconds*this.config.xp_per_second;if(A.activity_type&&A.activity_type in this.config.activity_bonuses){const Q=this.config.activity_bonuses[A.activity_type];I*=Q}return A.environmental_context&&(I=this.applyEnvironmentalBonus(I,A.environmental_context)),A.gaming_context&&(I=this.applyGamingBonus(I,A.gaming_context)),A.rhythm_game_context&&(I=this.applyRhythmGameBonus(I,A.rhythm_game_context)),B&&A.duration_seconds>=B.duration*.95&&(I+=this.config.xp_per_track_completion),Math.floor(I)}applyEnvironmentalBonus(A,B){let I=1;return B.weather?.isNight&&(I*=this.config.activity_bonuses.night_time),B.weather&&(B.weather.weatherType==="Thunderstorm"&&(I*=this.config.activity_bonuses.extreme_weather),(B.weather.weatherType==="Rain"||B.weather.weatherType==="Snow")&&(I*=this.config.activity_bonuses.extreme_weather)),B.geolocation?.altitude&&B.geolocation.altitude>=2e3&&(I*=this.config.activity_bonuses.high_altitude),A*I}applyGamingBonus(A,B){if(!B.isActivelyGaming)return A;let I=1;if(I+=.25,B.currentGame){if(B.currentGame.genre){const Q=B.currentGame.genre;(Q.includes("Action")||Q.includes("FPS"))&&(I+=.15),Q.includes("RPG")&&(I+=.2),Q.includes("Strategy")&&(I+=.1)}if(B.currentGame.partySize&&B.currentGame.partySize>1&&(I+=.15),B.currentGame.sessionDuration){const Q=B.currentGame.sessionDuration/60;Q>=1&&(I+=Math.min(.2,Q*.05))}}return A*I}applyRhythmGameBonus(A,B){if(!B.isActive)return A;let I=this.config.activity_bonuses.rhythm_game_base;if(B.currentCombo>0){const Q=B.currentCombo/B.maxComboCap;I+=Q*this.config.activity_bonuses.rhythm_game_combo}if(B.grooveHotness>0){const Q=B.grooveHotness/100;I+=Q*this.config.activity_bonuses.rhythm_game_groove}return A*I}calculateTotalModifier(A,B,I){let Q=1;return A&&(Q*=this.calculateEnvironmentalModifier(A)),B&&B.isActivelyGaming&&(Q*=this.calculateGamingModifier(B)),I&&I.isActive&&(Q*=this.calculateRhythmGameModifier(I)),Math.min(Q,3)}calculateEnvironmentalModifier(A){let B=1;return A.weather?.weatherType==="Thunderstorm"&&(B*=this.config.activity_bonuses.extreme_weather),A.weather?.isNight&&(B*=this.config.activity_bonuses.night_time),A.geolocation?.altitude&&A.geolocation.altitude>=2e3&&(B*=this.config.activity_bonuses.high_altitude),Math.min(B,3)}calculateGamingModifier(A){let B=1;if(B+=.25,A.currentGame){if(A.currentGame.genre){const I=A.currentGame.genre;(I.includes("Action")||I.includes("FPS"))&&(B+=.15),I.includes("RPG")&&(B+=.2),I.includes("Strategy")&&(B+=.1)}if(A.currentGame.partySize&&A.currentGame.partySize>1&&(B+=.15),A.currentGame.sessionDuration){const I=A.currentGame.sessionDuration/60;I>=1&&(B+=Math.min(.2,I*.05))}}return Math.min(B,1.75)}calculateRhythmGameModifier(A){let B=this.config.activity_bonuses.rhythm_game_base;if(A.currentCombo>0){const I=A.currentCombo/A.maxComboCap;B+=I*this.config.activity_bonuses.rhythm_game_combo}if(A.grooveHotness>0){const I=A.grooveHotness/100;B+=I*this.config.activity_bonuses.rhythm_game_groove}return Math.min(B,2.25)}getXPThresholdForLevel(A){if(A<1||A>20)throw new Error("Level must be between 1 and 20");return this.config.level_thresholds[A-1]}getXPToNextLevel(A){if(A<1||A>19)throw new Error("Current level must be between 1 and 19");const B=this.config.level_thresholds[A-1];return this.config.level_thresholds[A]-B}getLevelFromXP(A){for(let B=20;B>=1;B--)if(A>=this.config.level_thresholds[B-1])return B;return 1}isTrackMastered(A){return A>=this.config.track_mastery_threshold}getMasteryBonusXP(){return this.config.mastery_bonus_xp}getConfig(){return{...this.config}}}class OW{constructor(A){this.activeSessions=new Map,this.sessionHistory=[],this.sessionCounter=0,this.xpCalculator=A||new jo}startSession(A,B,I){const Q=`session-${this.sessionCounter++}-${A}-${Date.now()}`;return this.activeSessions.set(Q,{track_uuid:A,start_time:Date.now(),track:B,environmental_context:I?.environmental_context,gaming_context:I?.gaming_context}),Q}endSession(A,B,I){const Q=this.activeSessions.get(A);if(!Q)return null;const E=Date.now(),i=B||Math.max(1,Math.ceil((E-Q.start_time)/1e3)),e={track_uuid:Q.track_uuid,start_time:Q.start_time,end_time:E,duration_seconds:Math.round(i),base_xp_earned:0,bonus_xp:0,activity_type:I,environmental_context:Q.environmental_context,gaming_context:Q.gaming_context,total_xp_earned:0};return e.base_xp_earned=Math.floor(e.duration_seconds*this.xpCalculator.getConfig().xp_per_second),e.total_xp_earned=this.xpCalculator.calculateSessionXP(e,Q.track),e.bonus_xp=e.total_xp_earned-e.base_xp_earned,this.sessionHistory.push(e),this.activeSessions.delete(A),e}getActiveSession(A){return this.activeSessions.get(A)||null}getActiveSessionDuration(A){const B=this.activeSessions.get(A);return B?(Date.now()-B.start_time)/1e3:null}updateSessionContext(A,B){const I=this.activeSessions.get(A);return I?(B.environmental_context&&(I.environmental_context=B.environmental_context),B.gaming_context&&(I.gaming_context=B.gaming_context),!0):!1}getSessionHistory(){return[...this.sessionHistory]}getSessionsForTrack(A){return this.sessionHistory.filter(B=>B.track_uuid===A)}getTotalListeningTime(){return this.sessionHistory.reduce((A,B)=>A+B.duration_seconds,0)}getTotalXPEarned(){return this.sessionHistory.reduce((A,B)=>A+B.total_xp_earned,0)}getTrackListeningTime(A){return this.getSessionsForTrack(A).reduce((B,I)=>B+I.duration_seconds,0)}getTrackListenCount(A){return this.getSessionsForTrack(A).length}isTrackMastered(A,B=10){return this.getTrackListenCount(A)>=B}getSessionsInRange(A,B){return this.sessionHistory.filter(I=>I.start_time>=A&&I.end_time<=B)}getAverageSessionLength(){return this.sessionHistory.length===0?0:this.getTotalListeningTime()/this.sessionHistory.length}getLongestSession(){return this.sessionHistory.length===0?null:this.sessionHistory.reduce((A,B)=>B.duration_seconds>A.duration_seconds?B:A)}getTrackXPTotal(A){return this.getSessionsForTrack(A).reduce((B,I)=>B+I.total_xp_earned,0)}clearTrackSessions(A){const B=this.sessionHistory.length;return this.sessionHistory=this.sessionHistory.filter(I=>I.track_uuid!==A),B-this.sessionHistory.length}clearHistory(){this.sessionHistory=[]}clearActiveSessions(){this.activeSessions.clear()}getActiveSessionCount(){return this.activeSessions.size}getActiveSessionIds(){return Array.from(this.activeSessions.keys())}}const Hd=[4,8,12,16,19];class $B{static setUncappedConfig(A){this.uncappedConfig=A}static getUncappedConfig(){return this.uncappedConfig}static setStatManager(A){this.statManager=A}static getStatManager(){return this.statManager}static processLevelUp(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=E?1/0:20;if(B<1||B>i)throw new Error(`Level must be between 1 and ${i}`);const e=ag[A.class];if(!e)throw new Error(`Unknown class: ${A.class}`);const a=this.calculateHPIncrease(e.hit_die,A.ability_modifiers.CON,I),C=A.hp.max+a,o=this.getProficiencyBonus(B,E),t=o-A.proficiency_bonus,s={newLevel:B,hitPointIncrease:a,newHitPointsTotal:C,proficiencyBonusIncrease:t,newProficiencyBonus:o},n=E||Hd.includes(B);if(this.statManager&&n){const c=this.statManager.processLevelUp(A,B);c&&c.increases.length>0&&(s.abilityScoreIncreases=c.increases.map(G=>({ability:G.ability,increase:G.delta})),c.increases.length===1&&(s.abilityScoreIncrease={ability:c.increases[0].ability,increase:c.increases[0].delta}))}else Hd.includes(B)&&(s.abilityScoreIncrease={ability:"STR",increase:2});if(this.isSpellcaster(A.class)){const c=this.calculateSpellSlots(B),G={};for(const[D,h]of Object.entries(c))G[Number(D)]={total:h,used:0};s.newSpellSlots=G}const r=this.getClassFeaturesForLevel(A,A.class,B);if(r.length>0){s.classFeatures=r.map(c=>c.id),s.featureEffects=[];for(const c of r)if(c.effects&&c.effects.length>0){const G={...A,level:B},D=vQ.applyFeatureEffects(G,c);D.applied&&s.featureEffects.push({featureId:c.id,featureName:c.name,effectsApplied:D.count})}}return s}static getClassFeaturesForLevel(A,B,I){const Q=KA.getInstance(),E=Q.getFeaturesForLevel(B,I),i={...A,level:I},e=[];for(const a of E){const C=Q.validatePrerequisites(a,i);C.valid?e.push(a):(console.warn(`Feature "${a.name}" (level ${a.level}) failed prerequisite validation at level ${I}:`,C.errors),a.source==="default"&&e.push(a))}return e}static applyLevelUp(A,B){const I={...A};I.level=B.newLevel,I.hp.max=B.newHitPointsTotal,I.hp.current=Math.min(I.hp.current+B.hitPointIncrease,I.hp.max),I.proficiency_bonus=B.newProficiencyBonus;const Q=I.gameMode==="uncapped"?1/0:20;if(B.abilityScoreIncreases&&B.abilityScoreIncreases.length>0)for(const E of B.abilityScoreIncreases){const i=E.ability;I.ability_scores[i]=Math.min(Q,I.ability_scores[i]+E.increase);const e=I.ability_scores[i];I.ability_modifiers[i]=Math.floor((e-10)/2)}else if(B.abilityScoreIncrease){const E=B.abilityScoreIncrease.ability;I.ability_scores[E]=Math.min(Q,I.ability_scores[E]+B.abilityScoreIncrease.increase);const i=I.ability_scores[E];I.ability_modifiers[E]=Math.floor((i-10)/2)}return B.newSpellSlots&&I.spells&&(I.spells.spell_slots=B.newSpellSlots),B.classFeatures&&(I.class_features=[...new Set([...I.class_features,...B.classFeatures])]),this.reapplyEquipmentEffects(I),I}static calculateHPIncrease(A,B,I){let Q;return I?Q=new qB(I).randomInt(1,A+1):Q=Math.floor(Math.random()*A)+1,Math.max(1,Q+B)}static isSpellcaster(A){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(A)}static calculateSpellSlots(A){const B={1:0};for(let I=1;I<=9;I++)B[I]=this.getSpellSlotCount(A,I);return B}static getSpellSlotCount(A,B){return B>Math.ceil(A/2)||A<1?0:A<3?B===1?2:0:A<5?B===1?3:B===2?2:0:A<7?B===1?4:B===2?3:0:A<9?B===1||B===2?4:B===3?2:0:Math.min(5,Math.ceil(A/2))}static getXPThreshold(A,B=!1){if(A<1)throw new Error("Level must be at least 1");if(!B){if(A>20)throw new Error("Standard mode only supports levels 1-20");return HQ[A]}if(this.uncappedConfig?.xpFormula)return this.uncappedConfig.xpFormula(A);if(A<=20)return HQ[A];let I=355e3;for(let Q=21;Q<=A;Q++)I+=(Q-1)*Q*500;return I}static getProficiencyBonus(A,B=!1){if(!B){if(A<1||A>20)throw new Error("Standard proficiency bonus only defined for levels 1-20");return SC[A]}return this.uncappedConfig?.proficiencyBonusFormula?this.uncappedConfig.proficiencyBonusFormula(A):A<=4?2:A<=8?3:A<=12?4:A<=16?5:2+Math.floor((A-1)/4)}static calculateLevel(A,B=!1){if(!B){for(let Q=20;Q>=1;Q--)if(A>=this.getXPThreshold(Q,!1))return Q;return 1}let I=1;for(;A>=this.getXPThreshold(I+1,!0);)I++;return I}static getXPToNextLevel(A,B=!1){if(!B&&A>=20)return 0;const I=this.getXPThreshold(A,B);return this.getXPThreshold(A+1,B)-I}static getProgressPercentage(A,B,I=!1){if(!I&&A>=20)return 100;const Q=this.getXPThreshold(A,I),E=this.getXPThreshold(A+1,I),i=B-Q,e=E-Q;return Math.floor(i/e*100)}static processLevelUpWithoutStats(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=E?1/0:20;if(B<1||B>i)throw new Error(`Level must be between 1 and ${i}`);const e=ag[A.class];if(!e)throw new Error(`Unknown class: ${A.class}`);const a=this.calculateHPIncrease(e.hit_die,A.ability_modifiers.CON,I),C=A.hp.max+a,o=this.getProficiencyBonus(B,E),t=o-A.proficiency_bonus,s={newLevel:B,hitPointIncrease:a,newHitPointsTotal:C,proficiencyBonusIncrease:t,newProficiencyBonus:o};if(this.isSpellcaster(A.class)){const r=this.calculateSpellSlots(B),c={};for(const[G,D]of Object.entries(r))c[Number(G)]={total:D,used:0};s.newSpellSlots=c}const n=this.getClassFeaturesForLevel(A,A.class,B);if(n.length>0){s.classFeatures=n.map(r=>r.id),s.featureEffects=[];for(const r of n)if(r.effects&&r.effects.length>0){const c={...A,level:B},G=vQ.applyFeatureEffects(c,r);G.applied&&s.featureEffects.push({featureId:r.id,featureName:r.name,effectsApplied:G.count})}}return s}static applyAutomaticBenefitsOnly(A,B){const I={...A};return I.level=B.newLevel,I.hp.max=B.newHitPointsTotal,I.hp.current=Math.min(I.hp.current+B.hitPointIncrease,I.hp.max),I.proficiency_bonus=B.newProficiencyBonus,B.newSpellSlots&&I.spells&&(I.spells.spell_slots=B.newSpellSlots),B.classFeatures&&(I.class_features=[...new Set([...I.class_features,...B.classFeatures])]),this.reapplyEquipmentEffects(I),I}static applyStatIncreasesOnly(A,B){const I={...A};I.ability_scores={...I.ability_scores},I.ability_modifiers={...I.ability_modifiers};const E=(I.gameMode||"standard")==="uncapped"?1/0:20;for(const i of B){const e=i.ability;I.ability_scores[e]=Math.min(E,I.ability_scores[e]+i.amount),I.ability_modifiers[e]=Math.floor((I.ability_scores[e]-10)/2)}return I}static reapplyEquipmentEffects(A){A.equipment&&PA.reapplyEquipmentEffects(A)}}class Jd{constructor(){this.name="D&D 5e Standard"}selectIncreases(A,B,I){if(I?.forcedAbilities&&I.forcedAbilities.length>0){const Q=[];for(const E of I.forcedAbilities)A.ability_scores[E]>=20||Q.push({ability:E,amount:B/I.forcedAbilities.length});return Q}return[]}requiresManualInput(A){return!A?.forcedAbilities||A.forcedAbilities.length===0}}class xd{constructor(){this.name="D&D 5e Smart"}selectIncreases(A,B,I){const Q=new Set(I?.excludedAbilities||[]),E=["STR","DEX","CON","INT","WIS","CHA"].filter(C=>!Q.has(C)&&A.ability_scores[C]<20);if(E.length===0)return[];const e=ag[A.class]?.primary_ability||"STR";if(!Q.has(e)&&A.ability_scores[e]<18&&E.includes(e)){if(B===2&&this.shouldSplit(A,e)){const C=this.findLowestStat(A,E,e);return[{ability:e,amount:1},{ability:C,amount:1}]}return[{ability:e,amount:B}]}if(B===2&&E.length>=2){const C=this.findLowestStat(A,E),o=this.findLowestStat(A,E.filter(t=>t!==C));return[{ability:C,amount:1},{ability:o,amount:1}]}return[{ability:this.findLowestStat(A,E),amount:B}]}requiresManualInput(){return!1}shouldSplit(A,B){const I=A.ability_scores[B];if(I<15)return!1;const Q=Object.values(A.ability_scores);return Math.min(...Q)<I-4}findLowestStat(A,B,I){let Q=B[0],E=A.ability_scores[Q];for(const i of B){if(i===I)continue;const e=A.ability_scores[i];e<E&&(Q=i,E=e)}return Q}}class qd{constructor(){this.name="Balanced"}selectIncreases(A,B,I){const Q=new Set(I?.excludedAbilities||[]),E=["STR","DEX","CON","INT","WIS","CHA"].filter(a=>!Q.has(a)).filter(a=>A.ability_scores[a]<20).sort((a,C)=>A.ability_scores[a]-A.ability_scores[C]);if(E.length===0)return[];if(I?.forcedAbilities&&I.forcedAbilities.length>=2)return[{ability:I.forcedAbilities[0],amount:1},{ability:I.forcedAbilities[1],amount:1}];const i=[];let e=B;for(let a=0;a<E.length&&e>0;a++)i.push({ability:E[a],amount:1}),e--;return i}requiresManualInput(){return!1}}class Ld{constructor(){this.name="Primary Only"}selectIncreases(A,B,I){const E=ag[A.class]?.primary_ability||"STR";return I?.excludedAbilities?.includes(E)?[{ability:this.findLowestStat(A,I?.excludedAbilities),amount:B}]:A.ability_scores[E]>=20?[{ability:this.findLowestStat(A,[E]),amount:B}]:[{ability:E,amount:B}]}requiresManualInput(){return!1}findLowestStat(A,B){const I=["STR","DEX","CON","INT","WIS","CHA"].filter(i=>!B?.includes(i)).filter(i=>A.ability_scores[i]<20);if(I.length===0)return"STR";let Q=I[0],E=A.ability_scores[Q];for(const i of I){const e=A.ability_scores[i];e<E&&(Q=i,E=e)}return Q}}class Vd{constructor(){this.name="Random"}selectIncreases(A,B,I){const Q=["STR","DEX","CON","INT","WIS","CHA"].filter(i=>!I?.excludedAbilities?.includes(i)).filter(i=>A.ability_scores[i]<20);if(Q.length===0)return[];const E=[...Q].sort(()=>Math.random()-.5);return B===2&&E.length>=2?[{ability:E[0],amount:1},{ability:E[1],amount:1}]:[{ability:E[0],amount:B}]}requiresManualInput(){return!1}}class vd{constructor(){this.name="Manual"}selectIncreases(A,B,I){return[]}requiresManualInput(){return!0}}class jW{constructor(A,B){this.fn=A,this.name=B||"Custom Function"}selectIncreases(A,B,I){return this.fn(A,B,I)}requiresManualInput(){return!1}}function Po(g){if(typeof g!="string"&&typeof g!="function")return g;if(typeof g=="function")return new jW(g);switch(g){case"dnD5e":return new Jd;case"dnD5e_smart":return new xd;case"balanced":return new qd;case"primary_only":return new Ld;case"random":return new Vd;case"manual":return new vd;default:const A=g;throw new Error(`Unknown strategy type: ${A}`)}}const Xd=[4,8,12,16,19],Kd={strategy:"dnD5e",autoApply:!0};class zd{constructor(A){this.config=this.mergeWithDefaults(A),this.strategy=Po(this.config.strategy)}increaseStats(A,B,I="manual"){const Q={...A},E=[],i=[];Q.ability_scores={...Q.ability_scores},Q.ability_modifiers={...Q.ability_modifiers};const a=(Q.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;for(const{ability:C,amount:o}of B){const t=Q.ability_scores[C],s=t+o,n=Math.min(s,a),r=n-t;r>0?(Q.ability_scores[C]=n,Q.ability_modifiers[C]=Math.floor((n-10)/2),E.push({ability:C,oldValue:t,newValue:n,delta:r})):s>a&&i.push({ability:C,attemptedValue:s,cappedAt:a})}return{character:Q,increases:E,capped:i,source:I,timestamp:Date.now()}}decreaseStats(A,B,I="event"){const Q=B.map(({ability:E,amount:i})=>({ability:E,amount:-i}));return this.increaseStats(A,Q,I)}setStat(A,B,I,Q="manual"){const E=A.ability_scores[B],i=I-E;return this.increaseStats(A,[{ability:B,amount:i}],Q)}processLevelUp(A,B,I){const E=(A.gameMode||"standard")==="uncapped",i=this.config.statIncreaseLevels?.length?this.config.statIncreaseLevels:Xd;if(!(E||i.includes(B)))return null;const C=this.strategy.selectIncreases(A,2,I);return C.length===0?null:this.increaseStats(A,C,"level_up")}updateConfig(A){const B=this.config.strategy;this.config=this.mergeWithDefaults(A),A.strategy&&A.strategy!==B&&(this.strategy=Po(this.config.strategy))}getConfig(){return this.config}canIncrease(A,B,I=1){const Q=A.ability_scores[B],i=(A.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;return Q+I<=i}getStatCap(A,B){return(A.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20}validateDnD5eStatSelection(A,B,I=2){const Q=B.reduce((o,t)=>o+t.amount,0);if(Q!==I)return{error:`Total stat increase must be ${I}, got ${Q}`,reason:"invalid_amount",allowedPatterns:[`+${I} to one ability`,"+1 to two abilities"]};const E=[{count:1,amount:I},{count:2,amount:1}];if(!E.some(o=>B.length===o.count&&B.every(t=>t.amount===o.amount)))return{error:"Invalid pattern: must be +2 to one ability or +1 to two abilities",reason:"wrong_pattern",allowedPatterns:E.map(o=>o.count===1?`+${o.amount} to one ability`:"+1 to two abilities")};const e=this.getStatCap(A,"STR");for(const o of B){const t=A.ability_scores[o.ability];if(t+o.amount>e&&e!==1/0)return{error:`${o.ability} would exceed stat cap of ${e}`,reason:"exceeds_cap",allowedPatterns:[`Choose different abilities or use ${e-t} points`]}}const a=["STR","DEX","CON","INT","WIS","CHA"];for(const o of B)if(!a.includes(o.ability))return{error:`Invalid ability: ${o.ability}`,reason:"invalid_ability",allowedPatterns:a};const C=new Set;for(const o of B){if(C.has(o.ability))return{error:`Duplicate ability: ${o.ability}`,reason:"duplicate_ability",allowedPatterns:["Each ability can only be increased once"]};C.add(o.ability)}return{valid:!0}}mergeWithDefaults(A){return{maxStatCap:A?.maxStatCap??20,strategy:A?.strategy??Kd.strategy,autoApply:A?.autoApply??Kd.autoApply,statIncreaseLevels:A?.statIncreaseLevels??Xd}}}const fG=class fG{static getPlaysThreshold(A){const B=this.customThresholds.get(A);return B?.playsThreshold!==void 0&&B.playsThreshold!==null?B.playsThreshold:Math.floor(rh*Math.pow(cC,A))}static getXPThreshold(A){const B=this.customThresholds.get(A);return B?.xpThreshold!==void 0&&B.xpThreshold!==null?B.xpThreshold:Math.floor(ch*Math.pow(cC,A))}static setCustomThresholds(A,B){this.customThresholds.set(A,{...B})}static clearCustomThresholds(A){A!==void 0?this.customThresholds.delete(A):this.customThresholds.clear()}static hasCustomThresholds(A){return this.customThresholds.has(A)}static getCustomThresholds(A){return this.customThresholds.get(A)}static isMastered(A,B,I){const Q=this.getPlaysThreshold(I),E=this.getXPThreshold(I);return A>=Q&&B>=E}static canPrestige(A,B,I){return A>=kQ?!1:this.isMastered(B,I,A)}static isJustMastered(A,B,I,Q,E){const i=this.isMastered(A,I,E),e=this.isMastered(B,Q,E);return!i&&e}static calculateMasteryBonus(A){return A?$h:0}static getPrestigeInfo(A,B,I){const Q=this.getPlaysThreshold(A),E=this.getXPThreshold(A),i=this.isMastered(B,I,A),e=A>=kQ;return{prestigeLevel:A,currentPlays:B,currentXP:I,playsThreshold:Q,xpThreshold:E,playsProgress:Math.min(1,B/Q),xpProgress:Math.min(1,I/E),isMastered:i,canPrestige:i&&!e,isMaxPrestige:e}}static toRomanNumeral(A){return nh[A]}static getNextPrestigeLevel(A){return A>=kQ?null:Gh(A+1)}static createSuccessResult(A,B){const I=this.toRomanNumeral(B);return{success:!0,newPrestigeLevel:B,previousPrestigeLevel:A,message:`Successfully prestiged to level ${I}! New mastery requirements: ${this.getPlaysThreshold(B)} plays, ${this.getXPThreshold(B).toLocaleString()} XP`}}static createFailureResult(A,B){return{success:!1,newPrestigeLevel:B,previousPrestigeLevel:B,message:`Prestige failed: ${A}`}}static getAllThresholds(){const A=[];for(let B=0;B<=kQ;B++){const I=B;A.push({level:I,plays:this.getPlaysThreshold(I),xp:this.getXPThreshold(I)})}return A}};fG.customThresholds=new Map;let jA=fG;class PW{constructor(A){this.xpCalculator=new jo,this.statManager=A,this.statManager&&$B.setStatManager(this.statManager)}addXP(A,B,I="custom"){const Q={...A,xp:{...A.xp}};if(Q.xp.current+=B,!this.statManager){const t=(Q.gameMode||"standard")==="standard"?"dnD5e":"dnD5e_smart";this.statManager=new zd({strategy:t}),$B.setStatManager(this.statManager)}let E=!1,i;const e=[],a=Q.gameMode==="uncapped",C=$B.calculateLevel(Q.xp.current,a);if(C>Q.level){E=!0,i=C;for(let o=Q.level+1;o<=C;o++)if(this.isStatIncreaseLevel(o,a)&&this.statManager.getConfig().strategy==="dnD5e"){Q.pendingStatIncreases=(Q.pendingStatIncreases||0)+1;const n=$B.processLevelUpWithoutStats(Q,o),r=$B.applyAutomaticBenefitsOnly(Q,n),c={fromLevel:o-1,toLevel:o,hpIncrease:n.hitPointIncrease,newMaxHP:n.newHitPointsTotal,proficiencyIncrease:n.proficiencyBonusIncrease,newProficiency:n.newProficiencyBonus,statIncreases:void 0,featuresGained:n.classFeatures,newSpellSlots:n.newSpellSlots};e.push(c),Q.level=o,Q.hp=r.hp,Q.proficiency_bonus=r.proficiency_bonus,Q.class_features=r.class_features,r.spells&&(Q.spells=r.spells)}else{const n={...Q.ability_scores},r=[...Q.class_features],c=$B.processLevelUp(Q,o),G=$B.applyLevelUp(Q,c),D={fromLevel:o-1,toLevel:o,hpIncrease:c.hitPointIncrease,newMaxHP:c.newHitPointsTotal,proficiencyIncrease:c.proficiencyBonusIncrease,newProficiency:c.newProficiencyBonus,statIncreases:c.abilityScoreIncreases?.map(h=>({ability:h.ability,oldValue:n[h.ability],newValue:n[h.ability]+h.increase,delta:h.increase})),featuresGained:c.classFeatures?.filter(h=>!r.includes(h)),newSpellSlots:c.newSpellSlots};e.push(D),Q.level=G.level,Q.hp=G.hp,Q.ability_scores=G.ability_scores,Q.ability_modifiers=G.ability_modifiers,Q.proficiency_bonus=G.proficiency_bonus,Q.class_features=G.class_features,G.spells&&(Q.spells=G.spells)}}return!a&&Q.level>=20?Q.xp.next_level=0:Q.xp.next_level=$B.getXPThreshold(Q.level+1,a),{character:Q,xpEarned:B,leveledUp:E,newLevel:i,levelUpDetails:e.length>0?e:void 0}}addRhythmXP(A,B,I="rhythm_game"){const Q=this.addXP(A,B.finalXP,I);return{character:Q.character,xpEarned:Q.xpEarned,leveledUp:Q.leveledUp,newLevel:Q.newLevel,levelUpDetails:Q.levelUpDetails}}isStatIncreaseLevel(A,B){return B?!0:[4,8,12,16,19].includes(A)}updateCharacterFromSession(A,B,I,Q){const E=Q?.previousListenCount??0,i=Q?.previousXP??0,e=Q?.prestigeLevel??A.prestige_level??0,a=this.xpCalculator.calculateSessionXP(B,I);let C=!1,o=0;if(I){const n=E+1,r=i+a;jA.isJustMastered(E,n,i,r,e)&&(C=!0,o=jA.calculateMasteryBonus(!0))}const t=a+o;return{...this.addXP(A,t,"listening"),masteredTrack:C,masteryBonusXP:o}}applyPendingStatIncrease(A,B,I){const Q=A.pendingStatIncreases||0;if(Q===0)throw new Error("No pending stat increases to apply");let E;if(I&&I.length>0){if(I.length>1)throw new Error("Only one secondary stat allowed for +1/+1 distribution");E=[{ability:B,amount:1},{ability:I[0],amount:1}]}else E=[{ability:B,amount:2}];const i=this.statManager.validateDnD5eStatSelection(A,E);if("error"in i)throw new Error(i.error);const e=$B.applyStatIncreasesOnly(A,E);e.pendingStatIncreases=Q-1,e.pendingStatIncreases===0&&delete e.pendingStatIncreases;const a=E.map(C=>({ability:C.ability,oldValue:A.ability_scores[C.ability],newValue:e.ability_scores[C.ability],delta:C.amount}));return{character:e,statIncreases:a,remainingPending:e.pendingStatIncreases||0,timestamp:Date.now()}}hasPendingStatIncreases(A){return!!(A.pendingStatIncreases&&A.pendingStatIncreases>0)}getPendingStatIncreaseCount(A){return A.pendingStatIncreases||0}resetCharacterForPrestige(A,B,I,Q,E){const i=A.prestige_level??0;if(i>=10)return jA.createFailureResult("Already at maximum prestige level",i);const e=B.getTrackListenCount(I),a=B.getTrackXPTotal(I);if(!jA.canPrestige(i,e,a)){const n=jA.getPlaysThreshold(i),r=jA.getXPThreshold(i);return e<n&&a<r?jA.createFailureResult(`Need ${n-e} more plays and ${(r-a).toLocaleString()} more XP to prestige`,i):e<n?jA.createFailureResult(`Need ${n-e} more plays to prestige (XP requirement met)`,i):jA.createFailureResult(`Need ${(r-a).toLocaleString()} more XP to prestige (plays requirement met)`,i)}const C=A.equipment?{weapons:[...A.equipment.weapons],armor:[...A.equipment.armor],items:[...A.equipment.items],totalWeight:A.equipment.totalWeight,equippedWeight:A.equipment.equippedWeight}:null;B.clearTrackSessions(I);const o=jA.getNextPrestigeLevel(i);if(o===null)return jA.createFailureResult("Cannot prestige beyond maximum level",i);const t=qi.generate(A.seed,Q,E,{level:1,gameMode:A.gameMode,forceName:A.name});return C&&(t.equipment=C),t.prestige_level=o,{...jA.createSuccessResult(i,o),character:t}}canPrestige(A,B,I){const Q=A.prestige_level??0,E=B.getTrackListenCount(I),i=B.getTrackXPTotal(I);return jA.canPrestige(Q,E,i)}getPrestigeInfo(A,B,I){const Q=A.prestige_level??0,E=B.getTrackListenCount(I),i=B.getTrackXPTotal(I);return jA.getPrestigeInfo(Q,E,i)}}class _W{constructor(A){this.sessionTotals=null,this.sessionStartTime=null,this.config=di(A)}calculateButtonPressXP(A,B){const I=B?.comboLength??0,Q=B?.grooveHotness,E=this.getBaseXP(A),i=E*this.config.xpRatio,e=this.config.combo.enabled?this.getComboMultiplier(I):1;let a=0;this.config.groove.perHitMultiplier&&Q!==void 0&&Q>0&&(a=Q/100*this.config.groove.perHitScale);const C=Math.min(e+a,this.config.maxMultiplier),o=E*C,t=i*C,s=Math.max(0,t);return{scorePoints:E,baseXP:i,comboMultiplier:e,grooveMultiplier:a,totalMultiplier:C,finalScore:o,finalXP:s,breakdown:{accuracy:A,comboLength:I,grooveHotness:Q}}}calculateComboEndBonus(A){if(!this.config.combo.endBonus.enabled||A<=0)return{comboLength:A,bonusScore:0,bonusXP:0};const B=this.config.combo.endBonus.formula?this.config.combo.endBonus.formula(A):A*2,I=B*this.config.xpRatio;return{comboLength:A,bonusScore:B,bonusXP:I}}calculateGrooveEndBonus(A){if(!this.config.groove.endBonus.enabled)return{bonusScore:0,bonusXP:0};if(A.maxStreak<2)return{bonusScore:0,bonusXP:0};const{maxStreakWeight:I,avgHotnessWeight:Q,durationWeight:E}=this.config.groove.endBonus,i=A.maxStreak*I,e=A.avgHotness*Q,a=A.duration*E,C=i+e+a,o=C*this.config.xpRatio;return{bonusScore:C,bonusXP:o}}getBaseXP(A){return this.config.baseXP[A]}getComboMultiplier(A){if(!this.config.combo.enabled)return 1;const B=this.config.combo.formula?this.config.combo.formula(A):this.defaultComboFormula(A);return Math.min(B,this.config.combo.cap)}defaultComboFormula(A){return 1+A/25}startSession(){this.sessionTotals=this.createEmptyTotals(),this.sessionStartTime=Date.now()}recordHit(A,B){this.sessionTotals||this.startSession();const I=this.calculateButtonPressXP(A,B);this.sessionTotals.totalScore+=I.finalScore,this.sessionTotals.totalXP+=I.finalXP;const Q=B?.comboLength??0;return Q>this.sessionTotals.maxCombo&&(this.sessionTotals.maxCombo=Q),this.sessionTotals.accuracyDistribution[A]++,this.recalculateAccuracyPercentage(),I}getSessionTotals(){return!this.sessionTotals||!this.sessionStartTime?null:{...this.sessionTotals,duration:(Date.now()-this.sessionStartTime)/1e3}}endSession(){const A=this.getSessionTotals();return this.sessionTotals=null,this.sessionStartTime=null,A}createEmptyTotals(){return{totalScore:0,totalXP:0,maxCombo:0,accuracyDistribution:{perfect:0,great:0,good:0,ok:0,miss:0,wrongKey:0},accuracyPercentage:0,duration:0}}recalculateAccuracyPercentage(){if(!this.sessionTotals)return;const{perfect:A,great:B,good:I,ok:Q,miss:E,wrongKey:i}=this.sessionTotals.accuracyDistribution,e=A+B+I+Q+E+i;if(e===0){this.sessionTotals.accuracyPercentage=0;return}const a=A+B+I+Q;this.sessionTotals.accuracyPercentage=a/e*100}getConfig(){return this.config}updateConfig(A){this.config=di({...this.config,...A})}}class $W{static enchant(A,B,I,Q){return si.validateModification(I).valid?this.addModificationToEquipment(A,B,I,Q):A}static applyTemplate(A,B,I,Q){const e=IA.getInstance().get("equipment.templates").find(t=>t.id===I);if(!e)return A;const a={id:`template_${I}_${Date.now()}`,name:e.name||`Template: ${I}`,properties:e.properties||[],addsFeatures:e.addsFeatures,addsSkills:e.addsSkills,addsSpells:e.addsSpells,appliedAt:new Date().toISOString(),source:"template"},C=this.addModificationToEquipment(A,B,a,Q),o=this.findItem(C,B);return o&&(o.templateId=I),C}static curse(A,B,I,Q){return si.validateModification(I).valid?this.addModificationToEquipment(A,B,I,Q):A}static upgrade(A,B,I,Q){return this.enchant(A,B,I,Q)}static removeModification(A,B,I,Q){const E={weapons:A.weapons.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),armor:A.armor.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),items:A.items.map(t=>({...t,modifications:t.modifications?[...t.modifications]:[]})),totalWeight:A.totalWeight,equippedWeight:A.equippedWeight},i=this.findItem(E,B);if(!i||!i.modifications)return A;const e=i.equipped,a=i.instanceId,C=this.getEquipmentData(B);e&&Q&&C&&PA.unequipItem(Q,B,a);const o=i.modifications.findIndex(t=>t.id===I);if(o!==-1&&i.modifications.splice(o,1),e&&Q&&C){PA.equipItem(Q,C,a);for(const t of i.modifications)this.applyModificationEffects(Q,C,t,a)}return E}static getModificationHistory(A,B){const I=this.findItem(A,B);return!I||!I.modifications?[]:I.modifications.map(Q=>({...Q}))}static getCombinedEffects(A,B,I){const Q=this.getEquipmentData(B);if(!Q)return[];const E=Q.properties?[...Q.properties]:[],i=this.findItem(A,B);if(i&&i.modifications)for(const e of i.modifications)e.properties&&E.push(...e.properties);return E}static hasTemplate(A,B,I){const Q=this.findItem(A,B);return Q?Q.templateId===I?!0:Q.modifications?Q.modifications.some(E=>E.source==="template"&&E.name.includes(I)):!1:!1}static getAppliedTemplates(A,B){const I=[],Q=this.findItem(A,B);if(!Q)return I;if(Q.templateId&&I.push(Q.templateId),Q.modifications){for(const E of Q.modifications)if(E.source==="template"){const i=E.id.match(/template_([^_]+)/);i&&I.push(i[1])}}return I}static removeAllModifications(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications||Q.modifications.length===0)return A;const E=Q.modifications.map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static disenchant(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications)return A;const E=Q.modifications.filter(e=>e.source==="enchantment"||e.source==="upgrade").map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static liftCurse(A,B,I){const Q=this.findItem(A,B);if(!Q||!Q.modifications)return A;const E=Q.modifications.filter(e=>e.source==="curse").map(e=>e.id);let i=A;for(const e of E)i=this.removeModification(i,B,e,I);return i}static addModificationToEquipment(A,B,I,Q){const E={weapons:A.weapons.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),armor:A.armor.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),items:A.items.map(o=>({...o,modifications:o.modifications?[...o.modifications]:[]})),totalWeight:A.totalWeight,equippedWeight:A.equippedWeight},i=this.findItem(E,B);if(!i)return A;const e=this.getEquipmentData(B);if(!e)return A;i.modifications||(i.modifications=[]),i.instanceId||(i.instanceId=`${B}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const a=i.equipped,C=i.instanceId;if(a&&Q&&PA.unequipItem(Q,B,C),i.modifications.push(I),a&&Q){PA.equipItem(Q,e,C);for(const o of i.modifications)this.applyModificationEffects(Q,e,o,C)}return E}static findItem(A,B){return A.weapons.find(Q=>Q.name===B)||A.armor.find(Q=>Q.name===B)||A.items.find(Q=>Q.name===B)}static getEquipmentData(A){return IA.getInstance().get("equipment").find(Q=>Q.name===A)}static applyModificationEffects(A,B,I,Q){const E={...B,properties:[...B.properties||[],...I.properties],grantsFeatures:I.addsFeatures,grantsSkills:I.addsSkills,grantsSpells:I.addsSpells};PA.equipItem(A,E,Q)}static createModification(A,B,I,Q){return{id:A,name:B,properties:I,appliedAt:new Date().toISOString(),source:Q}}static createFeatureModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsFeatures:Q,appliedAt:new Date().toISOString(),source:E}}static createSkillModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsSkills:Q,appliedAt:new Date().toISOString(),source:E}}static createSpellModification(A,B,I,Q,E){return{id:A,name:B,properties:I,addsSpells:Q,appliedAt:new Date().toISOString(),source:E}}static generateModificationId(A="mod"){return`${A}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}static getModificationSources(A,B){const I=this.findItem(A,B);if(!I||!I.modifications)return[];const Q=new Set(I.modifications.map(E=>E.source));return Array.from(Q)}static countModificationsBySource(A,B){const I=this.findItem(A,B);if(!I||!I.modifications)return{};const Q={};for(const E of I.modifications)Q[E.source]=(Q[E.source]||0)+1;return Q}static countModificationsForSource(A,B,I){return this.countModificationsBySource(A,B)[I]||0}static isCursed(A,B){return this.countModificationsForSource(A,B,"curse")>0}static isEnchanted(A,B){return this.getModificationSources(A,B).some(Q=>Q==="enchantment"||Q==="upgrade")}static getItemSummary(A,B){const I=this.findItem(A,B);return I?{name:I.name,quantity:I.quantity,equipped:I.equipped,instanceId:I.instanceId,templateId:I.templateId,modificationCount:I.modifications?.length||0,isCursed:this.isCursed(A,B),isEnchanted:this.isEnchanted(A,B),sources:this.getModificationSources(A,B),effects:this.getCombinedEffects(A,B)}:null}}const ge={plus_one_weapon:{rarity:"uncommon",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+1"]},plus_two_weapon:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+2"]},plus_three_weapon:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+3"]},flaming_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],tags:["magic","fire","flaming"]},frost_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],tags:["magic","cold","frost"]},shocking_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage",condition:{type:"on_hit",value:!0}}],tags:["magic","lightning","shocking"]},vicious_weapon_template:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"extra_damage_on_hit",value:"1d8",description:"Deals 1d8 extra damage, but wielder takes 1d8 necrotic",condition:{type:"on_hit",value:!0}}],tags:["magic","vicious","necrotic"]},plus_one_armor:{rarity:"rare",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 AC bonus",stackable:!0}],tags:["magic","enhanced","+1","armor"]},plus_two_armor:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus",stackable:!0}],tags:["magic","enhanced","+2","armor"]}};class Td{static openBox(A,B,I){if(!A.boxContents)return{success:!1,items:[],gold:0,consumeBox:!1,error:{code:"NO_BOX_CONTENTS",message:`Box "${A.name}" has no contents defined.`}};if(I!==void 0&&A.boxContents.openRequirements){const C=this.checkRequirements(A,I);if(C)return{success:!1,items:[],gold:0,consumeBox:!1,error:C}}const Q=A.boxContents,E=[];let i=0;for(const C of Q.drops){const o=this.selectFromPool(C.pool,B);if(o){if(o.gold!==void 0&&o.gold>0)i+=o.gold;else if(o.itemName){const t=this.getEquipmentData(o.itemName);if(t){const s=o.quantity??1;if(t.type==="box")E.push({...t});else for(let n=0;n<s;n++)E.push(t)}else console.warn(`BoxOpener: Item "${o.itemName}" not found in equipment registry. Skipping drop.`)}}}const e=Q.consumeOnOpen!==!1;let a;return I!==void 0&&Q.openRequirements&&(a=Q.openRequirements.map(C=>({name:C.itemName,quantity:C.quantity??1}))),{success:!0,items:E,gold:i,consumeBox:e,consumedItems:a}}static selectFromPool(A,B){if(!A||A.length===0)return;const I=A.map(Q=>[Q,Q.weight]);return B.weightedChoice(I)}static getEquipmentData(A){return IA.getInstance().get("equipment").find(Q=>Q.name===A)}static isBox(A){return A.type==="box"&&A.boxContents!==void 0}static checkRequirements(A,B){if(!A.boxContents?.openRequirements)return null;const I=A.boxContents.openRequirements;for(const Q of I){const E=Q.quantity??1,i=B.find(e=>e.name===Q.itemName);if(!i)return{code:"MISSING_ITEM",message:`Missing required item: ${Q.itemName}`,requirement:Q};if(i.quantity<E)return{code:"INSUFFICIENT_QUANTITY",message:`Insufficient ${Q.itemName}: have ${i.quantity}, need ${E}`,requirement:Q}}return null}static canOpen(A,B){return A.boxContents?.openRequirements?this.checkRequirements(A,B)===null:!0}static getRequirementsDescription(A){if(!A.boxContents?.openRequirements||A.boxContents.openRequirements.length===0)return null;const B=A.boxContents.openRequirements,I=[];for(const Q of B){const E=Q.quantity??1;E===1?I.push(Q.itemName):I.push(`${E} ${Q.itemName}`)}return`Requires: ${I.join(", ")}`}static previewContents(A){if(!A.boxContents)return{possibleItems:[],possibleGold:{min:0,max:0},totalDrops:0};const B=new Set;let I=0,Q=0;for(const i of A.boxContents.drops)for(const e of i.pool)e.itemName&&B.add(e.itemName),e.gold!==void 0&&(I=Math.min(I,e.gold),Q=Math.max(Q,e.gold));const E={possibleItems:Array.from(B),possibleGold:{min:I,max:Q},totalDrops:A.boxContents.drops.length};return A.boxContents.openRequirements&&A.boxContents.openRequirements.length>0&&(E.openRequirements=A.boxContents.openRequirements),E}}const VI={common:0,uncommon:1,rare:2,very_rare:3,legendary:4};class AH{static spawnFromList(A,B){const Q=IA.getInstance().get("equipment");let E=A;return B&&(E=B.shuffle([...A])),E.map(i=>Q.find(e=>e.name===i))}static spawnByRarity(A,B,I){const i=IA.getInstance().get("equipment").filter(t=>t.rarity===A&&(t.spawnWeight??1)>0);if(i.length===0)return[];const e=Math.min(B,i.length),a=[],C=I?()=>I.random():Math.random,o=[...i];for(let t=o.length-1;t>0;t--){const s=Math.floor(C()*(t+1));[o[t],o[s]]=[o[s],o[t]]}for(let t=0;t<e;t++)a.push(o[t]);return a}static spawnByTags(A,B,I,Q){const e=IA.getInstance().get("equipment").filter(t=>{if(Q?.excludeZeroWeight&&(t.spawnWeight??1)<=0||Q?.includeTypes&&Q.includeTypes.length>0&&!Q.includeTypes.includes(t.type))return!1;const s=VI[t.rarity];return Q?.minRarity&&s<VI[Q.minRarity]||Q?.maxRarity&&s>VI[Q.maxRarity]?!1:t.tags&&A.some(n=>t.tags.includes(n))});if(e.length===0)return[];const a=Math.min(B,e.length),C=[],o=I?()=>I.random():Math.random;for(let t=0;t<a;t++){const s=e.map(G=>[G,G.spawnWeight??1]);let n=s.reduce((G,[,D])=>G+D,0),r=o()*n,c;for(const[G,D]of s)if(r-=D,r<=0){c=G;break}if(c){C.push(c);const G=e.indexOf(c);G>-1&&e.splice(G,1)}}return C}static spawnRandom(A,B,I){const i=IA.getInstance().get("equipment").filter(a=>{const C=a.spawnWeight??1;if(I?.excludeZeroWeight&&C<=0||I?.includeTypes&&I.includeTypes.length>0&&!I.includeTypes.includes(a.type))return!1;const o=VI[a.rarity];return I?.minRarity&&o<VI[I.minRarity]||I?.maxRarity&&o>VI[I.maxRarity]?!1:C>0});if(i.length===0)return[];const e=[];for(let a=0;a<A&&i.length>0;a++){const C=i.map(c=>[c,c.spawnWeight??1]),o=C.reduce((c,[,G])=>c+G,0);let t=B.random()*o,s=0;for(let c=0;c<C.length;c++)if(t-=C[c][1],t<=0){s=c;break}const n=C[s][0];e.push(n);const r=i.indexOf(n);r>-1&&i.splice(r,1)}return e}static spawnFromTemplate(A,B){const I=IA.getInstance();let Q;const E=I.get("equipment.templates");if(Array.isArray(E)?Q=E.find(e=>e.id===A||e.name===A):E&&typeof E=="object"&&(Q=E[A]),!Q&&A in ge&&(Q=ge[A]),!Q)return null;let i;return B?i=I.get("equipment").find(a=>a.name===B):i=this.getDefaultItemForTemplate(Q),i?{...i,...Q,name:`${i.name} (${A.replace(/_/g," ")})`,templateId:A,properties:[...i.properties||[],...Q.properties||[]],tags:[...i.tags||[],...Q.tags||[]]}:null}static spawnTreasureHoard(A,B){const I=[];let Q=0;const E=this.getTreasureItemCount(A,B);for(const[i,e]of Object.entries(E))for(let a=0;a<e;a++){const C=i,o=this.spawnByRarity(C,1,B);o.length>0&&(I.push(o[0]),Q+=this.estimateItemValue(o[0]))}if(I.length===0){const i=this.spawnByRarity("common",1,B);i.length>0&&(I.push(i[0]),Q+=this.estimateItemValue(i[0]))}return{items:I,totalValue:Q,cr:A}}static addToCharacter(A,B,I=!1){A.equipment||(A.equipment={weapons:[],armor:[],items:[],totalWeight:0,equippedWeight:0});for(const Q of B){const E={name:Q.name,quantity:1,equipped:I,instanceId:`${Q.name}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`};switch(Q.type){case"weapon":A.equipment.weapons.push(E);break;case"armor":A.equipment.armor.push(E);break;case"item":case"box":A.equipment.items.push(E);break}A.equipment.totalWeight+=Q.weight,I&&(A.equipment.equippedWeight+=Q.weight)}return A}static openBoxForCharacter(A,B,I){if(!A.equipment?.items||A.equipment.items.findIndex(t=>t.name===B)===-1)return null;const e=IA.getInstance().get("equipment").find(t=>t.name===B);if(!e||e.type!=="box")return null;const a=A.equipment.items,C=Td.openBox(e,I,a);if(!C.success)return{character:A,result:C};if(C.consumedItems&&C.consumedItems.length>0)for(const t of C.consumedItems)A=this.consumeItemFromCharacter(A,t.name,t.quantity).character;if(C.consumeBox&&A.equipment?.items){const t=A.equipment.items.findIndex(s=>s.name===B);t!==-1&&(A.equipment.items.splice(t,1),A.equipment.totalWeight-=e.weight)}const o=C.items;return A=this.addToCharacter(A,o,!1),{character:A,result:C}}static consumeItemFromCharacter(A,B,I){if(!A.equipment?.items)return{success:!1,character:A};const Q=A.equipment.items.findIndex(o=>o.name===B);if(Q===-1)return{success:!1,character:A};const E=A.equipment.items[Q];if(E.quantity<I)return{success:!1,character:A};const C=IA.getInstance().get("equipment").find(o=>o.name===B)?.weight??0;return E.quantity-=I,A.equipment.totalWeight-=C*I,E.quantity<=0&&A.equipment.items.splice(Q,1),{success:!0,character:A}}static getDefaultItemForTemplate(A){const I=IA.getInstance().get("equipment");let Q="item";return A.tags&&(A.tags.includes("weapon")?Q="weapon":A.tags.includes("armor")&&(Q="armor")),I.find(E=>E.type===Q)}static getTreasureItemCount(A,B){const I={},Q=()=>B.random();return A<5?(I.common=Math.floor(Q()*3)+1,I.uncommon=Q()>.7?1:0):A<11?(I.common=Math.floor(Q()*2)+1,I.uncommon=Math.floor(Q()*2)+1,I.rare=Q()>.6?1:0):A<17?(I.uncommon=Math.floor(Q()*2)+1,I.rare=Math.floor(Q()*2)+1,I.very_rare=Q()>.7?1:0):(I.rare=Math.floor(Q()*2)+1,I.very_rare=Math.floor(Q()*2)+1,I.legendary=Q()>.5?1:0),I}static estimateItemValue(A){let I={common:50,uncommon:400,rare:4e3,very_rare:4e4,legendary:2e5}[A.rarity]||50;return A.type==="weapon"?I*=1.2:A.type==="armor"&&(I*=1.5),A.weight>20&&(I*=1.1),Math.round(I)}}const _o="geolocation_cache";class Od{constructor(A,B){if(this.cache=null,this.cacheTTL=300*1e3,this.cacheStats={hits:0,misses:0},this.logger=VA.for("GeolocationProvider"),typeof A=="number"||A===void 0)this.cacheTTL=(A??5)*60*1e3,this.useLocalStorage=(B??!0)&&this.isLocalStorageAvailable();else{const I=A;this.cacheTTL=I.cacheTTL??300*1e3,this.useLocalStorage=(I.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const A="__localStorage_test__";return localStorage.setItem(A,A),localStorage.removeItem(A),!0}catch{return!1}}loadFromLocalStorage(){try{const A=localStorage.getItem(_o);if(A){const B=JSON.parse(A),I=Date.now(),Q=Object.values(B)[0];Q&&I-Q.timestamp<this.cacheTTL&&(this.cache=Q)}}catch(A){this.logger.warn("Failed to load geolocation cache from localStorage",{error:A})}}saveToLocalStorage(){if(!(!this.useLocalStorage||!this.cache))try{const A={position:this.cache};localStorage.setItem(_o,JSON.stringify(A))}catch(A){this.logger.warn("Failed to save geolocation cache to localStorage",{error:A})}}isCacheEntryValid(A){return A?Date.now()-A.timestamp<this.cacheTTL:!1}getCacheAge(){return this.cache?Date.now()-this.cache.timestamp:null}async getCurrentPosition(A=!1){return!A&&this.isCacheEntryValid(this.cache)?(this.cacheStats.hits++,this.cache.data):(this.cacheStats.misses++,typeof navigator>"u"||!navigator.geolocation?null:new Promise(B=>{navigator.geolocation.getCurrentPosition(I=>{const Q={latitude:I.coords.latitude,longitude:I.coords.longitude,altitude:I.coords.altitude,accuracy:I.coords.accuracy,heading:I.coords.heading,speed:I.coords.speed,timestamp:I.timestamp};this.cache={data:Q,timestamp:Date.now()},this.saveToLocalStorage(),B(Q)},I=>{this.logger.warn("Geolocation error",{error:I.message}),B(null)},{enableHighAccuracy:!0,timeout:5e3,maximumAge:0})}))}invalidateCache(){if(this.cache=null,this.useLocalStorage)try{localStorage.removeItem(_o)}catch(A){this.logger.warn("Failed to clear geolocation cache from localStorage",{error:A})}}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}isCacheExpired(){return!this.isCacheEntryValid(this.cache)}getCachedPosition(){return this.cache?.data??null}getBiome(A,B,I=null){const Q=Math.abs(A),E=this.normalizeLongitude(B),i=this.isCoastal(A,B),e=i?"_coastal":"";if(I!==null&&!isNaN(I)){const a=this.getElevationBiome(I,e);if(a)return a}if(Q>66.5)return`tundra${e}`;if(this.isInSwampRegion(A,E))return`swamp${e}`;if(Q>=15&&Q<=45&&this.isInDesertRegion(A,E))return i?"coastal_desert":"desert";if(Q<=23.5){if(this.isInJungleRegion(A,E))return`jungle${e}`;if(this.isInSavannaRegion(A,E))return`savanna${e}`;if(A>0){if(E>=290&&E<=310)return`forest${e}`;if(E>=10&&E<=30)return`forest${e}`;if(E>=70&&E<=120)return`forest${e}`}else{if(E>=10&&E<=30)return`forest${e}`;if(E>=100&&E<=140)return`forest${e}`}return`forest${e}`}if(A>0&&Q>=30&&Q<=50&&(E>=235&&E<=290||E>=0&&E<=40||E>=110&&E<=145))return i?"coastal_urban":"urban";if(Q>23.5&&Q<=66.5)if(A>0){if(this.isInTaigaRegion(A,E))return`taiga${e}`;if(E>=235&&E<=290)return Q>=45?`forest${e}`:`plains${e}`;if(E>=0&&E<=40)return`forest${e}`;if(E>40&&E<=180)return Q>=50?`mountain${e}`:`plains${e}`}else{if(E>=280&&E<=320)return`plains${e}`;if(E>=15&&E<=40)return`plains${e}`;if(E>=110&&E<=180)return`plains${e}`}return`plains${e}`}getElevationBiome(A,B){return A>3500?`mountain${B}`:A>1500?`mountain${B}`:A<0?`valley${B}`:null}normalizeLongitude(A){let B=A%360;return B<0&&(B+=360),B}isInJungleRegion(A,B){return Math.abs(A)>15?!1:A>=-15&&A<=5&&B>=290&&B<=310||A>=-5&&A<=5&&B>=10&&B<=30||A>=-10&&A<=10&&B>=95&&B<=140}isInSwampRegion(A,B){return A>=25&&A<=26&&B>=279&&B<=281||A>=-20&&A<=-18&&B>=22&&B<=24||A>=21&&A<=22&&B>=89&&B<=90||A>=-20&&A<=-15&&B>=300&&B<=305}isInTaigaRegion(A,B){return A<50||A>70?!1:A>=50&&A<=70&&B>=230&&B<=300||A>=60&&A<=70&&B>=5&&B<=30||A>=55&&A<=70&&B>=30&&B<=180}isInSavannaRegion(A,B){return Math.abs(A)>20?!1:A>=-5&&A<=15&&B>=30&&B<=40||A>=-20&&A<=-15&&B>=15&&B<=35||A>=-25&&A<=-5&&B>=300&&B<=315||A>=-20&&A<=-10&&B>=130&&B<=135}isInDesertRegion(A,B){return A>15&&A<30&&(B>=345||B<=40)||A>15&&A<30&&B>=35&&B<=55||A>28&&A<35&&B>=35&&B<=40||A>25&&A<35&&B>=50&&B<=65||A>23&&A<30&&B>=68&&B<=75||A>35&&A<45&&B>=100&&B<=115||A<-20&&A>-30&&B>=115&&B<=145||A<-20&&A>-25&&B>=290&&B<=292||A>25&&A<35&&B>=245&&B<=250||A<-20&&A>-30&&B>=20&&B<=30}isCoastal(A,B){const I=Math.abs(A),Q=this.normalizeLongitude(B);return!!(this.isInSmallIslandRegion(A,Q)||this.isInNarrowLandmass(A,Q)||I>60||A>30&&A<45&&Q>=0&&Q<=25||A>30&&A<40&&Q>=25&&Q<=35||A>15&&A<30&&Q>=35&&Q<=43||A>24&&A<30&&Q>=48&&Q<=55||A>41&&A<47&&Q>=27&&Q<=42||A>36&&A<47&&Q>=46&&Q<=55||A>53&&A<66&&Q>=15&&Q<=30||A>50&&A<60&&Q>=0&&Q<=10||A>15&&A<25&&Q>=65&&Q<=75||A>15&&A<23&&Q>=80&&Q<=90||A>35&&A<43&&Q>=130&&Q<=142||A>10&&A<25&&Q>=105&&Q<=122||A>20&&A<30&&Q>=265&&Q<=280||A>15&&A<25&&Q>=275&&Q<=300)}isInSmallIslandRegion(A,B){return A>=50&&A<=60&&B>=358&&B<=360||A>=50&&A<=60&&B>=0&&B<=10||A>=30&&A<=46&&B>=128&&B<=146||A>=4&&A<=22&&B>=116&&B<=127||A>=-10&&A<=6&&B>=94&&B<=142||A>=-47&&A<=-34&&B>=165&&B<=179||A>=-26&&A<=-12&&B>=43&&B<=51||A>=63&&A<=67&&B>=338&&B<=344||A>=10&&A<=23&&B>=275&&B<=300||A>=5&&A<=10&&B>=79&&B<=82||A>=18&&A<=23&&B>=204&&B<=206||A>=-18&&A<=-12&&B>=176&&B<=181}isInNarrowLandmass(A,B){return A>=7&&A<=18&&B>=275&&B<=290||A>=33&&A<=43&&B>=124&&B<=132||A>=36&&A<=47&&B>=8&&B<=19||A>=36&&A<=44&&B>=358&&B<=360||A>=36&&A<=44&&B>=0&&B<=10||A>=55&&A<=71&&B>=4&&B<=32||A>=24&&A<=31&&B>=268&&B<=272||A>=55&&A<=62&&B>=210&&B<=220||A>=50&&A<=62&&B>=156&&B<=163}}class BH{constructor(){this.logger=VA.for("MotionDetector"),this.isListening=!1,this.lastMotion=null,this.motionCallback=null,this.lastProcessedTime=0,this.minSampleIntervalMs=83,this.deltaBuffer=[],this.bufferSize=24,this.confirmedActivity="unknown",this.candidateActivity=null,this.candidateStartTime=0,this.confirmationMs=1500,this.handleMotion=A=>{if(!this.isListening||!this.motionCallback)return;const B=Date.now(),I={acceleration:{x:A.acceleration?.x??null,y:A.acceleration?.y??null,z:A.acceleration?.z??null},accelerationIncludingGravity:{x:A.accelerationIncludingGravity?.x??0,y:A.accelerationIncludingGravity?.y??0,z:A.accelerationIncludingGravity?.z??0},rotationRate:{alpha:A.rotationRate?.alpha??null,beta:A.rotationRate?.beta??null,gamma:A.rotationRate?.gamma??null},interval:A.interval,timestamp:B};if(this.lastMotion=I,B-this.lastProcessedTime<this.minSampleIntervalMs)return;this.lastProcessedTime=B;const Q=I.accelerationIncludingGravity;if(Q.x!=null&&Q.y!=null&&Q.z!=null&&!(Q.x===0&&Q.y===0&&Q.z===0)){const E=Math.sqrt(Q.x**2+Q.y**2+Q.z**2),i=Math.abs(E-9.8);this.updateSmoothing(i)}this.logger.debug("Motion event received",{acceleration:I.accelerationIncludingGravity,rotation:I.rotationRate,activity:this.detectActivity(I)}),this.motionCallback(I)}}startMonitoring(A){if(typeof window>"u"||!("DeviceMotionEvent"in window)){this.logger.warn("DeviceMotionEvent not supported");return}this.motionCallback=A,this.isListening=!0,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null,window.addEventListener("devicemotion",this.handleMotion)}stopMonitoring(){typeof window<"u"&&window.removeEventListener("devicemotion",this.handleMotion),this.isListening=!1,this.motionCallback=null,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null}getLastMotion(){return this.lastMotion}detectActivity(A){const B=A.accelerationIncludingGravity;if(B.x==null||B.y==null||B.z==null||B.x===0&&B.y===0&&B.z===0)return"unknown";if(this.deltaBuffer.length>=10)return this.confirmedActivity;const I=Math.sqrt(B.x**2+B.y**2+B.z**2),Q=Math.abs(I-9.8);return Q<.5?"stationary":Q<2?"walking":Q<5?"running":"driving"}updateSmoothing(A){this.deltaBuffer.push(A),this.deltaBuffer.length>this.bufferSize&&this.deltaBuffer.shift();const B=this.deltaBuffer.reduce((Q,E)=>Q+E,0)/this.deltaBuffer.length;let I;if(B<.5?I="stationary":B<2?I="walking":B<5?I="running":I="driving",I===this.confirmedActivity){this.candidateActivity=null;return}if(I!==this.candidateActivity){this.candidateActivity=I,this.candidateStartTime=Date.now();return}Date.now()-this.candidateStartTime>=this.confirmationMs&&(this.confirmedActivity=I,this.candidateActivity=null)}}const jd=wA({id:K(),main:O(),description:O(),icon:O().optional()}),Pd=wA({temp:K(),feels_like:K().optional(),temp_min:K().optional(),temp_max:K().optional(),pressure:K(),humidity:K().nonnegative(),sea_level:K().optional(),grnd_level:K().optional(),temp_kf:K().optional()}),_d=wA({speed:K().nonnegative(),deg:K().min(0).max(360).optional(),gust:K().optional()}),$d=wA({all:K().min(0).max(100)}),gH=wA({type:K().optional(),id:K().optional(),country:O().optional(),sunrise:K().nonnegative(),sunset:K().nonnegative(),pod:O().optional()}),Aw=wA({"1h":K().optional(),"3h":K().optional()}),Bw=wA({"1h":K().optional(),"3h":K().optional()}),gw=wA({lon:K().min(-180).max(180),lat:K().min(-90).max(90)}),IH=wA({coord:gw.optional(),weather:zA(jd).min(1),base:O().optional(),main:Pd,visibility:K().nonnegative().optional(),wind:_d.optional(),rain:Aw.optional(),snow:Bw.optional(),clouds:$d.optional(),dt:K().nonnegative(),sys:gH,timezone:K().optional(),id:K().optional(),name:O().optional(),cod:K().optional()}),QH=wA({dt:K().nonnegative(),main:Pd,weather:zA(jd).min(1),clouds:$d.optional(),wind:_d.optional(),visibility:K().nonnegative().optional(),pop:K().min(0).max(1).optional(),rain:Aw.optional(),snow:Bw.optional(),sys:wA({pod:O().optional()}).optional(),dt_txt:O().optional()}),EH=wA({cod:TC([O(),K()]),message:K().optional(),cnt:K().nonnegative().optional(),list:zA(QH).min(1),city:wA({id:K(),name:O(),coord:gw,country:O().optional(),population:K().optional(),timezone:K().optional(),sunrise:K().optional(),sunset:K().optional()}).optional()}),Iw="weather_api_cache";class iH{constructor(A,B,I){if(this.baseUrl="https://api.openweathermap.org/data/2.5/weather",this.forecastUrl="https://api.openweathermap.org/data/2.5/forecast",this.cache=new Map,this.forecastCache=new Map,this.cacheTTL=720*1e3,this.forecastCacheTTL=3600*1e3,this.cacheStats={hits:0,misses:0},this.logger=VA.for("WeatherAPIClient"),this.lastKnownLocation=null,this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[],this.maxRecentSamples=100,typeof A=="string"||A===void 0)this.apiKey=A??"",this.cacheTTL=(B??12)*60*1e3,this.useLocalStorage=(I??!0)&&this.isLocalStorageAvailable();else{const Q=A;this.apiKey=Q.apiKey??"",this.cacheTTL=Q.cacheTTL??720*1e3,this.useLocalStorage=(Q.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const A="__localStorage_test__";return localStorage.setItem(A,A),localStorage.removeItem(A),!0}catch{return!1}}loadFromLocalStorage(){try{const A=localStorage.getItem(Iw);if(A){const B=JSON.parse(A),I=Date.now();for(const[Q,E]of Object.entries(B))I-E.timestamp<this.cacheTTL&&this.cache.set(Q,E)}}catch(A){this.logger.warn("Failed to load weather cache from localStorage",{error:A})}}saveToLocalStorage(){if(this.useLocalStorage)try{const A={};for(const[B,I]of this.cache.entries())A[B]=I;localStorage.setItem(Iw,JSON.stringify(A))}catch(A){this.logger.warn("Failed to save weather cache to localStorage",{error:A})}}startTiming(){const A=performance.now();return B=>{performance.now()-A}}recordWeatherApiCall(A,B){const I=Date.now();B?(this.weatherApiMetrics.successCount++,this.weatherApiMetrics.totalTime+=A,this.weatherApiMetrics.minTime=Math.min(this.weatherApiMetrics.minTime,A),this.weatherApiMetrics.maxTime=Math.max(this.weatherApiMetrics.maxTime,A),this.recentWeatherApiTimes.push(A),this.recentWeatherApiTimes.length>this.maxRecentSamples&&this.recentWeatherApiTimes.shift()):this.weatherApiMetrics.errorCount++,this.weatherApiMetrics.lastCallTimestamp=I}recordForecastApiCall(A,B){const I=Date.now();B?(this.forecastApiMetrics.successCount++,this.forecastApiMetrics.totalTime+=A,this.forecastApiMetrics.minTime=Math.min(this.forecastApiMetrics.minTime,A),this.forecastApiMetrics.maxTime=Math.max(this.forecastApiMetrics.maxTime,A),this.recentForecastApiTimes.push(A),this.recentForecastApiTimes.length>this.maxRecentSamples&&this.recentForecastApiTimes.shift()):this.forecastApiMetrics.errorCount++,this.forecastApiMetrics.lastCallTimestamp=I}calculatePercentile(A,B){if(A.length===0)return 0;const I=[...A].sort((E,i)=>E-i),Q=Math.ceil(B/100*I.length)-1;return I[Math.max(0,Q)]}getPerformanceStatistics(A,B){const I=A.successCount+A.errorCount,Q=A.successCount>0?A.totalTime/A.successCount:0,E=I>0?A.successCount/I*100:0;return{average:Math.round(Q),min:A.minTime===1/0?0:Math.round(A.minTime),max:Math.round(A.maxTime),totalCalls:I,successRate:Math.round(E*10)/10}}getWeatherApiMetrics(){return{...this.weatherApiMetrics}}getWeatherApiStatistics(){return{...this.getPerformanceStatistics(this.weatherApiMetrics,this.recentWeatherApiTimes),p95:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,99))}}getForecastApiMetrics(){return{...this.forecastApiMetrics}}getForecastApiStatistics(){return{...this.getPerformanceStatistics(this.forecastApiMetrics,this.recentForecastApiTimes),p95:Math.round(this.calculatePercentile(this.recentForecastApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentForecastApiTimes,99))}}resetPerformanceMetrics(){this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[]}getCacheKey(A,B){const I=Math.round(A*1e4)/1e4,Q=Math.round(B*1e4)/1e4;return`${I},${Q}`}isCacheEntryValid(A){return Date.now()-A.timestamp<this.cacheTTL}calculateMoonPhase(A){const B=new Date("2024-01-11T11:57:00Z"),I=29.530588853*24*60*60*1e3,E=(A.getTime()-B.getTime())/I;return Math.abs(E%1)}getSolarInfo(A,B,I){const Q=I??new Date;return{...this.calculateSolarInfo(A,B,Q),fromApi:!1,timestamp:Date.now()}}calculateSolarInfo(A,B,I){const Q=this.calculateSunriseSunset(A,B,I,!0),E=this.calculateSunriseSunset(A,B,I,!1),i=this.calculateSolarNoon(A,B,I),e=this.calculateTwilight(A,B,I,"civil",!0),a=this.calculateTwilight(A,B,I,"civil",!1),{altitude:C,azimuth:o}=this.calculateSunPosition(A,B,I),t=!isNaN(E.getTime())&&!isNaN(Q.getTime())?(E.getTime()-Q.getTime())/(1e3*60*60):0,s=this.determineDayStage(I,Q,E,e,a);return{currentTime:I,stage:s,sunrise:Q,sunset:E,solarNoon:i,civilDawn:e,civilDusk:a,sunAltitude:C,sunAzimuth:o,dayLengthHours:t}}toRadians(A){return A*(Math.PI/180)}toDegrees(A){return A*(180/Math.PI)}calculateJulianDate(A){const B=A.getUTCFullYear(),I=A.getUTCMonth()+1,Q=A.getUTCDate(),E=A.getUTCHours()+A.getUTCMinutes()/60+A.getUTCSeconds()/3600;let i=Math.floor((14-I)/12),e=B+4800-i,a=I+12*i-3;return Q+Math.floor((153*a+2)/5)+365*e+Math.floor(e/4)-Math.floor(e/100)+Math.floor(e/400)-32045+(E-12)/24}calculateJulianCentury(A){return(A-2451545)/36525}calculateSolarDeclination(A){const B=(280.46646+A*(36000.76983+A*3032e-7))%360,I=357.52911+A*(35999.05029-1537e-7*A),Q=Math.sin(this.toRadians(I))*(1.914602-A*(.004817+14e-6*A))+Math.sin(this.toRadians(2*I))*(.019993-101e-6*A)+Math.sin(this.toRadians(3*I))*289e-6,E=B+Q,e=23+(26+(21.448-A*(46.815+A*(59e-5-A*.001813)))/60)/60+.00256*Math.cos(this.toRadians(125.04-1934.136*A)),a=E-.00569-.00478*Math.sin(this.toRadians(125.04-1934.136*A));return this.toDegrees(Math.asin(Math.sin(this.toRadians(e))*Math.sin(this.toRadians(a))))}calculateEquationOfTime(A){const B=(280.46646+A*(36000.76983+A*3032e-7))%360,I=357.52911+A*(35999.05029-1537e-7*A),Q=.016708634-A*(42037e-9+1267e-10*A);return Math.tan(this.toRadians(23.44/2))**2,4*this.toDegrees(Math.atan2(Math.sin(this.toRadians(2*B))*Math.cos(this.toRadians(I))*2*Q-Math.sin(this.toRadians(I))*2*Q+Math.sin(this.toRadians(B))*Math.cos(this.toRadians(I))*2*Q*Math.cos(this.toRadians(B)),1-Math.sin(this.toRadians(B))*Math.sin(this.toRadians(B))*Q*Q*Math.cos(this.toRadians(I))*Math.cos(this.toRadians(I))))}calculateHourAngle(A,B,I=90.833){const Q=this.toRadians(A),E=this.toRadians(B),i=this.toRadians(I),e=(Math.cos(i)-Math.sin(Q)*Math.sin(E))/(Math.cos(Q)*Math.cos(E));return e>1?NaN:e<-1?NaN:this.toDegrees(Math.acos(e))}calculateSunriseSunset(A,B,I,Q){const E=new Date(I);E.setUTCHours(12,0,0,0);const i=this.calculateJulianDate(E),e=this.calculateJulianCentury(i),a=this.calculateSolarDeclination(e),C=this.calculateHourAngle(A,a);if(isNaN(C))return new Date(NaN);const o=this.calculateEquationOfTime(e),t=(Q?-C:C)/15,n=12-B/15-o/60+t,r=Math.floor(n),c=Math.floor((n-r)*60),G=Math.floor(((n-r)*60-c)*60),D=new Date(I);return D.setUTCHours(r,c,G,0),D}calculateSolarNoon(A,B,I){const Q=new Date(I);Q.setUTCHours(12,0,0,0);const E=this.calculateJulianDate(Q),i=this.calculateJulianCentury(E),e=this.calculateEquationOfTime(i),a=12-B/15-e/60,C=Math.floor(a),o=Math.floor((a-C)*60),t=Math.floor(((a-C)*60-o)*60),s=new Date(I);return s.setUTCHours(C,o,t,0),s}calculateTwilight(A,B,I,Q,E){const e={civil:96,nautical:102,astronomical:108}[Q],a=new Date(I);a.setUTCHours(12,0,0,0);const C=this.calculateJulianDate(a),o=this.calculateJulianCentury(C),t=this.calculateSolarDeclination(o),s=this.calculateHourAngle(A,t,e);if(isNaN(s))return new Date(NaN);const n=this.calculateEquationOfTime(o),r=(E?-s:s)/15,G=12-B/15-n/60+r,D=Math.floor(G),h=Math.floor((G-D)*60),w=Math.floor(((G-D)*60-h)*60),y=new Date(I);return y.setUTCHours(D,h,w,0),y}calculateSunPosition(A,B,I){const Q=this.calculateJulianDate(I),E=this.calculateJulianCentury(Q),i=this.calculateSolarDeclination(E),e=this.calculateEquationOfTime(E),o=(I.getUTCHours()+I.getUTCMinutes()/60+I.getUTCSeconds()/3600+B/15+e/60-12)*15,t=this.toRadians(A),s=this.toRadians(i),n=this.toRadians(o),r=Math.sin(t)*Math.sin(s)+Math.cos(t)*Math.cos(s)*Math.cos(n),c=this.toDegrees(Math.asin(r)),G=(Math.sin(s)-Math.sin(t)*r)/(Math.cos(t)*Math.cos(this.toRadians(c)));let D=this.toDegrees(Math.acos(Math.max(-1,Math.min(1,G))));return o>0&&(D=360-D),{altitude:c,azimuth:D}}determineDayStage(A,B,I,Q,E){const i=A.getTime();if(isNaN(B.getTime())||isNaN(I.getTime())){const t=A.getUTCMonth();return t>=4&&t<=7?"day":"night"}const e=Q&&!isNaN(Q.getTime())?Q.getTime():B.getTime(),a=E&&!isNaN(E.getTime())?E.getTime():I.getTime(),C=B.getTime(),o=I.getTime();return i<e||i>a?"night":i>=e&&i<C?"dawn":i>o&&i<=a?"dusk":"day"}async getWeather(A,B){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;this.lastKnownLocation={latitude:A,longitude:B};const I=this.getCacheKey(A,B),Q=this.cache.get(I);if(Q&&this.isCacheEntryValid(Q))return this.cacheStats.hits++,Q.data;this.cacheStats.misses++;const E=performance.now();let i=!1;try{const e=`${this.baseUrl}?lat=${A}&lon=${B}&appid=${this.apiKey}&units=metric`,a=await fetch(e);if(!a.ok)throw new Error(`Weather API error: ${a.statusText}`);const C=await a.json(),o=IH.safeParse(C);if(!o.success)return this.logger.error("Weather API response validation failed",{errors:o.error.issues,response:C}),null;const t=o.data,s=Date.now()/1e3,n=s<t.sys.sunrise||s>t.sys.sunset,r={temperature:t.main.temp,humidity:t.main.humidity,pressure:t.main.pressure,weatherType:t.weather[0]?.main||"Clear",windSpeed:t.wind?.speed??0,windDirection:t.wind?.deg??0,isNight:n,moonPhase:this.calculateMoonPhase(new Date),timestamp:Date.now()};return this.cache.set(I,{data:r,timestamp:Date.now()}),this.saveToLocalStorage(),i=!0,r}catch(e){return this.logger.error("Failed to fetch weather",{error:e}),null}finally{const e=performance.now()-E;this.recordWeatherApiCall(e,i)}}invalidateCache(){this.cache.clear(),this.saveToLocalStorage()}invalidateLocation(A,B){const I=this.getCacheKey(A,B);this.cache.delete(I),this.saveToLocalStorage()}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}clearExpiredEntries(){let A=0;for(const[B,I]of this.cache.entries())this.isCacheEntryValid(I)||(this.cache.delete(B),A++);return A>0&&this.saveToLocalStorage(),A}getCacheSize(){return this.cache.size}getLastKnownLocation(){return this.lastKnownLocation?{...this.lastKnownLocation}:null}isForecastCacheEntryValid(A){return Date.now()-A.timestamp<this.forecastCacheTTL}getForecastCacheKey(A,B,I){const Q=Math.round(A*1e4)/1e4,E=Math.round(B*1e4)/1e4;return`forecast_${Q},${E}_${I}h`}async getForecast(A,B,I=24){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;const Q=Math.min(I,120),E=this.getForecastCacheKey(A,B,Q),i=this.forecastCache.get(E);if(i&&this.isForecastCacheEntryValid(i))return i.data.slice(0,Math.ceil(Q/3));const e=performance.now();let a=!1;try{const C=Math.ceil(Q/3),o=`${this.forecastUrl}?lat=${A}&lon=${B}&appid=${this.apiKey}&units=metric&cnt=${C}`,t=await fetch(o);if(!t.ok)throw new Error(`Weather Forecast API error: ${t.statusText}`);const s=await t.json(),n=EH.safeParse(s);if(!n.success)return this.logger.error("Weather Forecast API response validation failed",{errors:n.error.issues,response:s}),null;const c=n.data.list.map(G=>({temperature:G.main.temp,humidity:G.main.humidity,pressure:G.main.pressure,weatherType:G.weather[0]?.main||"Clear",windSpeed:G.wind?.speed??0,windDirection:G.wind?.deg??0,timestamp:Date.now(),forecastTime:new Date(G.dt*1e3),probabilityOfPrecipitation:G.pop??0}));return this.forecastCache.set(E,{data:c,timestamp:Date.now()}),a=!0,c}catch(C){return this.logger.error("Failed to fetch weather forecast",{error:C}),null}finally{const C=performance.now()-e;this.recordForecastApiCall(C,a)}}async getUpcomingWeather(A,B,I=12){const Q=await this.getForecast(A,B,I);if(!Q||Q.length===0)return null;let E=!1,i=!1,e=0,a=0;const C=[];for(const n of Q)C.push(n.weatherType),(n.weatherType.toLowerCase().includes("rain")||n.weatherType.toLowerCase().includes("drizzle"))&&(E=!0,e=Math.max(e,n.probabilityOfPrecipitation)),n.weatherType.toLowerCase().includes("snow")&&(i=!0,a=Math.max(a,n.probabilityOfPrecipitation));const o={thunderstorm:5,snow:4,rain:3,drizzle:2,clouds:1,clear:0,mist:1,fog:1,haze:1,smoke:1,dust:1,sand:1,ash:1,squall:2,tornado:5};let t="Clear",s=-1;for(const n of new Set(C)){const r=o[n.toLowerCase()]??0;r>s&&(s=r,t=n)}return{willRain:E,willSnow:i,rainProbability:e,snowProbability:a,worstWeatherType:t}}detectSevereWeather(A){const B=A.weatherType.toLowerCase(),I=A.windSpeed*3.6;if(B.includes("snow")||B.includes("blizzard")){const Q=B.includes("blizzard")||B.includes("heavy")||A.windSpeed>8,E=I>25;if(B.includes("blizzard")||Q&&E)return{type:"Blizzard",xpBonus:.5,severity:I>50?"extreme":"high",message:"⚠️ Blizzard conditions detected! Stay safe and warm.",detectedAt:Date.now()}}if(I>118){const E=(this.lastKnownLocation?this.isTropicalRegion(this.lastKnownLocation.latitude):!1)?"Hurricane":"Typhoon";return{type:E,xpBonus:.75,severity:I>177?"extreme":"high",message:`🌀 ${E} conditions detected! Please seek shelter.`,detectedAt:Date.now()}}return B.includes("tornado")?{type:"Tornado",xpBonus:1,severity:"extreme",message:"🌪️ TORNADO WARNING! Take immediate shelter!",detectedAt:Date.now()}:B.includes("thunderstorm")&&I>60?{type:"Tornado",xpBonus:.5,severity:"high",message:"⛈️ Extreme thunderstorm with high winds! Exercise caution.",detectedAt:Date.now()}:null}isTropicalRegion(A){return Math.abs(A)<23.5}getSafetyWarning(A){switch(A.type){case"Blizzard":return A.severity==="extreme"?"🚨 EXTREME BLIZZARD: Stay indoors, avoid travel. Keep emergency supplies ready.":"⚠️ Blizzard: Dress warmly, avoid unnecessary travel. Check on neighbors.";case"Hurricane":case"Typhoon":return A.severity==="extreme"?"🚨 EXTREME CYCLONE: Seek shelter immediately! Follow evacuation orders.":"⚠️ Hurricane/Typhoon: Secure property, prepare emergency kit, follow local alerts.";case"Tornado":return"🚨 TORNADO: Take shelter in basement or interior room immediately! Stay away from windows.";default:return"⚠️ Severe weather detected. Stay informed and stay safe."}}invalidateForecastCache(){this.forecastCache.clear()}invalidateForecastLocation(A,B){for(const[I]of this.forecastCache.entries()){const Q=this.getCacheKey(A,B);I.includes(Q)&&this.forecastCache.delete(I)}}clearExpiredForecastEntries(){let A=0;for(const[B,I]of this.forecastCache.entries())this.isForecastCacheEntryValid(I)||(this.forecastCache.delete(B),A++);return A}}const L={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m"},$o={useColors:!0,compact:!1,showTimestamp:!0,maxFailures:5};function At(g){return g.useColors===!1?!1:typeof process<"u"&&process.stdout?.isTTY===!0}function T(g,A,B){return B?`${A}${g}${L.reset}`:g}function eH(g,A){switch(g){case"healthy":return T("●",L.green,A);case"degraded":return T("●",L.yellow,A);case"failed":return T("●",L.red,A);default:return T("○",L.dim,A)}}function aH(g,A){return g?T("✓ Granted",L.green,A):T("✗ Denied",L.red,A)}function CH(g,A){return g?T("Available",L.green,A):T("Unavailable",L.red,A)}function Bt(g){const A=new Date(g),B=A.getHours().toString().padStart(2,"0"),I=A.getMinutes().toString().padStart(2,"0"),Q=A.getSeconds().toString().padStart(2,"0");return`${B}:${I}:${Q}`}function _Q(g){return g<1e3?`${g}ms`:g<6e4?`${(g/1e3).toFixed(1)}s`:g<36e5?`${Math.floor(g/6e4)}m ${Math.floor(g%6e4/1e3)}s`:`${Math.floor(g/36e5)}h ${Math.floor(g%36e5/6e4)}m`}function Qw(g,A){const B=g+A;return B===0?"N/A":`${(g/B*100).toFixed(1)}%`}function Ie(g,A){const B=[],I=g.totalCalls>0;if(B.push(` Calls: ${g.totalCalls}`),B.push(` Success: ${I?`${g.successRate.toFixed(1)}%`:"N/A"}`),I){const Q=g.average<500?L.green:g.average<1500?L.yellow:L.red;B.push(` Avg Time: ${T(`${g.average}ms`,Q,A)}`),B.push(` Min/Avg/Max: ${g.min}/${g.average}/${g.max}ms`),B.push(` P95/P99: ${g.p95}/${g.p99}ms`)}else B.push(" Avg Time: N/A");return B}function tB(g,A="─"){return A.repeat(g)}function gt(g,A,B){const I=Math.max(0,A-g.length-2),Q=Math.floor(I/2),E=I-Q,i=tB(A),e=B?L.bright+L.cyan+g+L.reset:g;return`
54
54
  ${i}
55
55
  ${" ".repeat(Q)}${e}${" ".repeat(E)}
56
56
  ${i}