@libraz/libsonare 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/index.d.ts +1 -2842
- package/dist/index.js +3602 -1934
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +1 -1
- package/dist/sonare-rt.js +1 -1
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +4816 -483
- package/dist/worklet.js +747 -440
- package/dist/worklet.js.map +1 -1
- package/package.json +2 -1
- package/src/analysis_helpers.ts +152 -0
- package/src/audio.ts +493 -0
- package/src/codes.ts +56 -0
- package/src/effects_mastering.ts +964 -0
- package/src/feature_core.ts +248 -0
- package/src/feature_music.ts +419 -0
- package/src/feature_pitch.ts +80 -0
- package/src/feature_resample.ts +21 -0
- package/src/feature_spectral.ts +330 -0
- package/src/feature_spectrogram.ts +454 -0
- package/src/features.ts +84 -0
- package/src/index.ts +341 -4963
- package/src/live_audio.ts +45 -0
- package/src/metering.ts +380 -0
- package/src/mixer.ts +523 -0
- package/src/module_state.ts +14 -0
- package/src/opfs_clip_pages.ts +188 -0
- package/src/project.ts +1614 -0
- package/src/public_types.ts +177 -2
- package/src/quick_analysis.ts +508 -0
- package/src/realtime_engine.ts +667 -0
- package/src/realtime_voice_changer.ts +275 -0
- package/src/scale.ts +42 -0
- package/src/sonare.js.d.ts +302 -4
- package/src/stream_analyzer.ts +275 -0
- package/src/stream_types.ts +26 -1
- package/src/streaming_mixing.ts +18 -0
- package/src/streaming_processors.ts +335 -0
- package/src/validation.ts +82 -0
- package/src/web_midi.ts +367 -0
package/dist/worklet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/worklet.ts"],"sourcesContent":["/**\n * sonare - Audio Analysis Library\n *\n * @example\n * ```typescript\n * import { init, detectBpm, detectKey, analyze } from '@libraz/libsonare';\n *\n * await init();\n *\n * // Detect BPM from audio samples\n * const bpm = detectBpm(samples, sampleRate);\n *\n * // Detect musical key\n * const key = detectKey(samples, sampleRate);\n *\n * // Full analysis\n * const result = analyze(samples, sampleRate);\n * ```\n */\n\nimport type {\n AcousticResult,\n AnalysisResult,\n AutomationCurve,\n ChordAnalysisResult,\n ChordDetectionOptions,\n ChordQuality,\n ChromaResult,\n CqtResult,\n EqBand,\n EqMatchOptions,\n EqSpectrumSnapshot,\n GoniometerPoint,\n HpssResult,\n Key,\n KeyCandidate,\n KeyDetectionOptions,\n KeyProfileName,\n LufsResult,\n MasteringChainConfig,\n MasteringChainResult,\n MasteringPreset,\n MasteringProcessorParams,\n MasteringResult,\n MasteringStereoChainResult,\n MasteringStereoResult,\n MelodyResult,\n MelPowerResult,\n MelSpectrogramResult,\n MeterTap,\n MfccResult,\n MixerProcessResult,\n MixMeterSnapshot,\n MixOptions,\n MixResult,\n PairAnalysis,\n PairProcessor,\n PanLaw,\n PanMode,\n PitchResult,\n RealtimeVoiceChangerConfigInput,\n RealtimeVoiceChangerPodConfig,\n RirResult,\n RirSynthOptions,\n RoomEstimateOptions,\n RoomEstimateResult,\n RoomMorphOptions,\n Section,\n SectionType,\n SendTiming,\n SoloProcessor,\n StereoAnalysis,\n StftPowerResult,\n StftResult,\n StreamingEqualizerConfig,\n StreamingPlatform,\n StreamingRetuneConfig,\n TempogramMode,\n VoicePresetId,\n} from './public_types';\nimport { KeyProfile as KeyProfileValues, Mode, PitchClass } from './public_types';\nimport type {\n ProgressCallback,\n SonareModule,\n WasmAcousticResult,\n WasmAnalysisResult,\n WasmChordAnalysisResult,\n WasmCyclicTempogramResult,\n WasmDecomposeResult,\n WasmEngineAutomationPoint,\n WasmEngineBounceOptions,\n WasmEngineBounceResult,\n WasmEngineCaptureStatus,\n WasmEngineClip,\n WasmEngineFreezeOptions,\n WasmEngineFreezeResult,\n WasmEngineGraphSpec,\n WasmEngineMarker,\n WasmEngineMeterTelemetry,\n WasmEngineMetronomeConfig,\n WasmEngineParameterInfo,\n WasmEngineProcessWithMonitorResult,\n WasmEngineTelemetry,\n WasmEngineTransportState,\n WasmFourierTempogramResult,\n WasmFrameResult,\n WasmHpssWithResidualResult,\n WasmKeyCandidateResult,\n WasmLufsResult,\n WasmMatrix2dResult,\n WasmNnlsChromaResult,\n WasmRealtimeEngine,\n WasmStreamAnalyzer,\n WasmTempogramResult,\n WasmTrimResult,\n} from './sonare.js';\nimport type {\n AnalyzerStats,\n FrameBuffer,\n StreamConfig,\n StreamFramesI16,\n StreamFramesU8,\n} from './stream_types';\n\nexport type {\n AcousticResult,\n AnalysisResult,\n AutomationCurve,\n Beat,\n Chord,\n ChordAnalysisResult,\n ChordDetectionOptions,\n ChromaResult,\n CqtResult,\n Dynamics,\n EqBand,\n EqBandPhase,\n EqBandType,\n EqCoeffMode,\n EqMatchOptions,\n EqSpectrumSnapshot,\n EqStereoPlacement,\n GoniometerPoint,\n HpssResult,\n Key,\n KeyCandidate,\n KeyDetectionOptions,\n KeyProfileName,\n LufsResult,\n MasteringChainConfig,\n MasteringChainResult,\n MasteringPreset,\n MasteringProcessorParams,\n MasteringResult,\n MasteringStereoChainResult,\n MasteringStereoResult,\n MelodyPoint,\n MelodyResult,\n MelPowerResult,\n MelSpectrogramResult,\n MeterTap,\n MfccResult,\n MixerProcessResult,\n MixMeterSnapshot,\n MixOptions,\n MixResult,\n PairAnalysis,\n PairProcessor,\n PanLaw,\n PanMode,\n PitchResult,\n RealtimeVoiceChangerConfigInput,\n RealtimeVoiceChangerPodConfig,\n RhythmFeatures,\n RirResult,\n RirSynthOptions,\n RoomEstimateOptions,\n RoomEstimateResult,\n RoomGeometryOptions,\n RoomMorphOptions,\n Section,\n SendTiming,\n SoloProcessor,\n StereoAnalysis,\n StftPowerResult,\n StftResult,\n StreamingEqualizerConfig,\n StreamingPlatform,\n StreamingRetuneConfig,\n TempogramMode,\n Timbre,\n TimeSignature,\n VoicePresetId,\n} from './public_types';\nexport {\n ChordQuality,\n KeyProfile,\n Mode,\n PitchClass,\n SectionType,\n} from './public_types';\nexport type { ProgressCallback } from './sonare.js';\nexport type {\n AnalyzerStats,\n BarChord,\n ChordChange,\n FrameBuffer,\n PatternScore,\n ProgressiveEstimate,\n StreamConfig,\n StreamFramesI16,\n StreamFramesU8,\n} from './stream_types';\n\nexport type EngineClip = WasmEngineClip;\nexport type EngineParameterInfo = WasmEngineParameterInfo;\nexport type EngineAutomationPoint = WasmEngineAutomationPoint;\nexport type EngineMarker = WasmEngineMarker;\nexport type EngineMetronomeConfig = WasmEngineMetronomeConfig;\nexport type EngineGraphSpec = WasmEngineGraphSpec;\nexport type EngineCaptureStatus = WasmEngineCaptureStatus;\nexport type EngineBounceOptions = WasmEngineBounceOptions;\nexport type EngineBounceResult = WasmEngineBounceResult;\nexport type EngineFreezeOptions = WasmEngineFreezeOptions;\nexport type EngineFreezeResult = WasmEngineFreezeResult;\nexport type EngineTelemetry = WasmEngineTelemetry;\nexport type EngineMeterTelemetry = WasmEngineMeterTelemetry;\nexport type EngineTransportState = WasmEngineTransportState;\n\n/** Row-major 2-D matrix as a flat buffer plus its dimensions. */\nexport type Matrix2dResult = WasmMatrix2dResult;\n/** NMF factor matrices { w, h } from {@link decompose}. */\nexport type DecomposeResult = WasmDecomposeResult;\n/** Harmonic / percussive / residual signals from {@link hpssWithResidual}. */\nexport type HpssWithResidualResult = WasmHpssWithResidualResult;\n\nexport const EXPECTED_ENGINE_ABI_VERSION = 2;\n\nexport interface EngineCapabilities {\n engineAbiVersion: number;\n expectedEngineAbiVersion: number;\n abiCompatible: boolean;\n sharedArrayBuffer: boolean;\n atomics: boolean;\n audioWorklet: boolean;\n mode: 'sab' | 'postMessage';\n}\n\nexport interface MixerRealtimeBuffer {\n leftInputs: Float32Array[];\n rightInputs: Float32Array[];\n outLeft: Float32Array;\n outRight: Float32Array;\n process: (numSamples?: number) => void;\n}\n\n/**\n * Zero-copy realtime buffer pair for {@link RealtimeVoiceChanger} mono\n * processing. The `input` / `output` `Float32Array`s are typed-memory views\n * onto the WASM heap — write samples into `input`, call `process()`, then\n * read from `output`. The views are owned by the {@link RealtimeVoiceChanger}\n * and remain valid until `delete()` is called on it.\n */\nexport interface RealtimeVoiceChangerMonoBuffer {\n input: Float32Array;\n output: Float32Array;\n process: () => void;\n}\n\n/**\n * Zero-copy realtime buffer pair for {@link RealtimeVoiceChanger} interleaved\n * multi-channel processing. Layout is L0,R0,L1,R1,... for stereo. The views\n * are owned by the {@link RealtimeVoiceChanger}.\n */\nexport interface RealtimeVoiceChangerInterleavedBuffer {\n input: Float32Array;\n output: Float32Array;\n channels: number;\n process: () => void;\n}\n\n/**\n * Zero-copy realtime buffer for {@link RealtimeVoiceChanger} planar stereo\n * processing. Each entry in `channels` is a heap-backed `Float32Array` for one\n * channel (matching AudioWorklet's native layout). Process happens in place:\n * write samples into each channel view, call `process()`, then read back from\n * the same views.\n */\nexport interface RealtimeVoiceChangerPlanarBuffer {\n channels: Float32Array[];\n process: () => void;\n}\n\nfunction automationCurveCode(curve: AutomationCurve): number {\n switch (curve) {\n case 'linear':\n return 0;\n case 'exponential':\n return 1;\n case 'hold':\n return 2;\n case 's-curve':\n return 3;\n default:\n throw new Error(`Invalid automation curve: ${curve}`);\n }\n}\n\nfunction panLawCode(panLaw: PanLaw | number): number {\n if (typeof panLaw === 'number') {\n return panLaw;\n }\n switch (panLaw) {\n case 'const4.5dB':\n return 1;\n case 'const6dB':\n return 2;\n case 'linear0dB':\n return 3;\n default:\n return 0;\n }\n}\n\nfunction panModeCode(panMode: PanMode | number): number {\n if (typeof panMode === 'number') {\n return panMode;\n }\n switch (panMode) {\n case 'stereoPan':\n case 'stereo-pan':\n return 1;\n case 'dualPan':\n case 'dual-pan':\n return 2;\n default:\n return 0;\n }\n}\n\nfunction meterTapCode(tap: MeterTap | number): number {\n return tap === 'preFader' || tap === 0 ? 0 : 1;\n}\n\nfunction sendTimingCode(timing: SendTiming | number): number {\n return timing === 'preFader' || timing === 0 ? 0 : 1;\n}\n\n// ============================================================================\n// Module State\n// ============================================================================\n\nlet module: SonareModule | null = null;\nlet initPromise: Promise<void> | null = null;\n\n// ============================================================================\n// Input validation helpers (empty + NaN/Inf guards for sample buffers)\n// ============================================================================\n\n/**\n * Per-call validation options accepted by guarded wrappers. Empty-buffer\n * checks are always performed; pass `{ validate: false }` to opt out of the\n * O(n) NaN/Inf scan on hot paths.\n */\nexport interface ValidateOptions {\n validate?: boolean;\n}\n\nfunction assertNonEmptySamples(\n fnName: string,\n samples: ArrayLike<number>,\n argName = 'samples',\n): void {\n if (samples.length === 0) {\n throw new RangeError(`${fnName}: ${argName} must not be empty`);\n }\n}\n\nfunction assertFiniteSamples(\n fnName: string,\n samples: ArrayLike<number>,\n validate: boolean,\n argName = 'samples',\n): void {\n if (!validate) {\n return;\n }\n for (let i = 0; i < samples.length; i++) {\n const v = samples[i] as number;\n if (!Number.isFinite(v)) {\n throw new RangeError(`${fnName}: ${argName} contains NaN or Inf at index ${i}`);\n }\n }\n}\n\nfunction assertSamples(\n fnName: string,\n samples: ArrayLike<number>,\n validate: boolean,\n argName = 'samples',\n): void {\n assertNonEmptySamples(fnName, samples, argName);\n assertFiniteSamples(fnName, samples, validate, argName);\n}\n\nfunction assertFiniteScalar(fnName: string, value: number, argName: string): void {\n if (!Number.isFinite(value)) {\n throw new RangeError(`${fnName}: ${argName} must be a finite number`);\n }\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the WASM module.\n * Must be called before using any analysis functions.\n *\n * @param options - Optional module configuration\n * @returns Promise that resolves when initialization is complete\n */\nexport async function init(options?: {\n locateFile?: (path: string, prefix: string) => string;\n}): Promise<void> {\n if (module) {\n return;\n }\n\n if (initPromise) {\n return initPromise;\n }\n\n initPromise = (async () => {\n try {\n const createModule = (await import('./sonare.js')).default;\n module = await createModule(options);\n } catch (error) {\n initPromise = null;\n throw error;\n }\n })();\n\n return initPromise;\n}\n\n/**\n * Check if the module is initialized.\n */\nexport function isInitialized(): boolean {\n return module !== null;\n}\n\n/**\n * Get the library version.\n */\nexport function version(): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.version();\n}\n\nexport function engineAbiVersion(): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.engineAbiVersion();\n}\n\nexport function voiceChangerAbiVersion(): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.voiceChangerAbiVersion();\n}\n\n// Canonical ordinal order of the built-in voice-character presets, matching the\n// C ABI SonareVoiceCharacterPreset enum and SONARE_REALTIME_VOICE_CHANGER_PRESET_IDS.\nconst VOICE_PRESET_ORDINALS: readonly VoicePresetId[] = [\n 'neutral-monitor',\n 'bright-idol',\n 'soft-whisper',\n 'deep-narrator',\n 'robot-mascot',\n 'dark-villain',\n];\n\nfunction resolveVoicePresetOrdinal(preset: VoicePresetId | number): number {\n if (typeof preset === 'number') {\n return preset;\n }\n const ordinal = VOICE_PRESET_ORDINALS.indexOf(preset);\n if (ordinal < 0) {\n throw new Error(`Unknown voice character preset: ${preset}`);\n }\n return ordinal;\n}\n\n/**\n * Map a voice-character preset ordinal (or canonical id) to its canonical id\n * string (e.g. `'bright-idol'`). Returns `null` for an out-of-range ordinal.\n */\nexport function voiceCharacterPresetId(preset: VoicePresetId | number): string | null {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.voiceCharacterPresetId(resolveVoicePresetOrdinal(preset));\n}\n\n/**\n * Return the canonical (normalized) flat POD config for a built-in voice\n * preset, skipping the JSON round-trip. Accepts a canonical preset id or its\n * integer ordinal. Returns `null` for an out-of-range ordinal.\n */\nexport function realtimeVoiceChangerPresetConfig(\n preset: VoicePresetId | number,\n): RealtimeVoiceChangerPodConfig | null {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.realtimeVoiceChangerPresetConfig(resolveVoicePresetOrdinal(preset));\n}\n\nexport function engineCapabilities(): EngineCapabilities {\n const abiVersion = engineAbiVersion();\n const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';\n const atomics = typeof globalThis.Atomics === 'object';\n const audioWorklet =\n typeof AudioWorkletNode !== 'undefined' ||\n typeof (globalThis as typeof globalThis & { AudioWorkletProcessor?: unknown })\n .AudioWorkletProcessor !== 'undefined';\n return {\n engineAbiVersion: abiVersion,\n expectedEngineAbiVersion: EXPECTED_ENGINE_ABI_VERSION,\n abiCompatible: abiVersion === EXPECTED_ENGINE_ABI_VERSION,\n sharedArrayBuffer,\n atomics,\n audioWorklet,\n mode: sharedArrayBuffer && atomics ? 'sab' : 'postMessage',\n };\n}\n\nexport class RealtimeEngine {\n private native: WasmRealtimeEngine;\n\n constructor(\n sampleRate = 48000,\n maxBlockSize = 128,\n commandCapacity = 1024,\n telemetryCapacity = 1024,\n ) {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const capabilities = engineCapabilities();\n if (!capabilities.abiCompatible) {\n throw new Error(\n `Engine ABI mismatch: wasm=${capabilities.engineAbiVersion}, expected=${capabilities.expectedEngineAbiVersion}`,\n );\n }\n this.native = new module.RealtimeEngine(\n sampleRate,\n maxBlockSize,\n commandCapacity,\n telemetryCapacity,\n );\n }\n\n prepare(\n sampleRate: number,\n maxBlockSize: number,\n commandCapacity = 1024,\n telemetryCapacity = 1024,\n ): void {\n this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);\n }\n\n /** Queue a sample-accurate parameter change (engine kSetParam). */\n setParameter(paramId: number, value: number, renderFrame = -1): void {\n this.native.setParameter(paramId, value, renderFrame);\n }\n\n /** Queue a smoothed parameter change (engine kSetParamSmoothed). */\n setParameterSmoothed(paramId: number, value: number, renderFrame = -1): void {\n this.native.setParameterSmoothed(paramId, value, renderFrame);\n }\n\n /** Read back the current transport state snapshot. */\n getTransportState(): EngineTransportState {\n return this.native.getTransportState();\n }\n\n play(renderFrame = -1): void {\n this.native.play(renderFrame);\n }\n\n stop(renderFrame = -1): void {\n this.native.stop(renderFrame);\n }\n\n seekSample(timelineSample: number, renderFrame = -1): void {\n this.native.seekSample(timelineSample, renderFrame);\n }\n\n seekPpq(ppq: number, renderFrame = -1): void {\n this.native.seekPpq(ppq, renderFrame);\n }\n\n setTempo(bpm: number): void {\n this.native.setTempo(bpm);\n }\n\n setTimeSignature(numerator: number, denominator: number): void {\n this.native.setTimeSignature(numerator, denominator);\n }\n\n setLoop(startPpq: number, endPpq: number, enabled = true): void {\n this.native.setLoop(startPpq, endPpq, enabled);\n }\n\n addParameter(info: EngineParameterInfo): void {\n this.native.addParameter(info);\n }\n\n parameterCount(): number {\n return this.native.parameterCount();\n }\n\n parameterInfoByIndex(index: number): EngineParameterInfo {\n return this.native.parameterInfoByIndex(index);\n }\n\n parameterInfo(id: number): EngineParameterInfo {\n return this.native.parameterInfo(id);\n }\n\n setAutomationLane(paramId: number, points: EngineAutomationPoint[]): void {\n this.native.setAutomationLane(paramId, points);\n }\n\n automationLaneCount(): number {\n return this.native.automationLaneCount();\n }\n\n setMarkers(markers: EngineMarker[]): void {\n this.native.setMarkers(markers);\n }\n\n markerCount(): number {\n return this.native.markerCount();\n }\n\n markerByIndex(index: number): EngineMarker {\n return this.native.markerByIndex(index);\n }\n\n marker(id: number): EngineMarker {\n return this.native.marker(id);\n }\n\n seekMarker(markerId: number, renderFrame = -1): void {\n this.native.seekMarker(markerId, renderFrame);\n }\n\n setLoopFromMarkers(startMarkerId: number, endMarkerId: number): void {\n this.native.setLoopFromMarkers(startMarkerId, endMarkerId);\n }\n\n setMetronome(config: EngineMetronomeConfig): void {\n this.native.setMetronome(config);\n }\n\n metronome(): Required<EngineMetronomeConfig> {\n return this.native.metronome();\n }\n\n countInEndSample(startSample: number, bars: number): number {\n return Number(this.native.countInEndSample(startSample, bars));\n }\n\n setGraph(spec: EngineGraphSpec): void {\n this.native.setGraph(spec);\n }\n\n graphNodeCount(): number {\n return this.native.graphNodeCount();\n }\n\n graphConnectionCount(): number {\n return this.native.graphConnectionCount();\n }\n\n setClips(clips: EngineClip[]): void {\n this.native.setClips(clips);\n }\n\n clipCount(): number {\n return this.native.clipCount();\n }\n\n setCaptureBuffer(numChannels: number, capacityFrames: number): void {\n this.native.setCaptureBuffer(numChannels, capacityFrames);\n }\n\n armCapture(armed = true): void {\n this.native.armCapture(armed);\n }\n\n setCapturePunch(startSample: number, endSample: number, enabled = true): void {\n this.native.setCapturePunch(startSample, endSample, enabled);\n }\n\n resetCapture(): void {\n this.native.resetCapture();\n }\n\n captureStatus(): EngineCaptureStatus {\n return this.native.captureStatus();\n }\n\n capturedAudio(): Float32Array[] {\n return this.native.capturedAudio();\n }\n\n process(channels: Float32Array[]): Float32Array[] {\n return this.native.process(channels);\n }\n\n /**\n * Allocates persistent per-channel WASM-heap scratch for the zero-copy\n * `getChannelBuffer` / `processPrepared` realtime path. Call once (off the\n * audio thread) before driving `processPrepared` from an AudioWorklet so the\n * render callback never allocates on the C++/JS heap.\n */\n prepareChannels(numChannels: number, maxFrames: number): void {\n this.native.prepareChannels(numChannels, maxFrames);\n }\n\n /**\n * Returns a Float32Array view onto the persistent WASM-heap scratch for one\n * channel (valid for up to `numFrames`). Fill it, call `processPrepared`, then\n * read the same view back. Re-acquire after WASM memory growth.\n */\n getChannelBuffer(channel: number, numFrames: number): Float32Array {\n return this.native.getChannelBuffer(channel, numFrames);\n }\n\n /**\n * Runs the engine in place over the prepared per-channel scratch buffers.\n * Allocation-free: safe to call on the AudioWorklet render thread after\n * `prepareChannels`.\n */\n processPrepared(numFrames: number): void {\n this.native.processPrepared(numFrames);\n }\n\n processWithMonitor(channels: Float32Array[]): WasmEngineProcessWithMonitorResult {\n return this.native.processWithMonitor(channels);\n }\n\n renderOffline(channels: Float32Array[], blockSize = 128): Float32Array[] {\n return this.native.renderOffline(channels, blockSize);\n }\n\n bounceOffline(options: EngineBounceOptions): EngineBounceResult {\n return this.native.bounceOffline(options);\n }\n\n freezeOffline(options: EngineFreezeOptions): EngineFreezeResult {\n return this.native.freezeOffline(options);\n }\n\n drainTelemetry(maxRecords = 1024): EngineTelemetry[] {\n return this.native.drainTelemetry(maxRecords);\n }\n\n drainMeterTelemetry(maxRecords = 1024): EngineMeterTelemetry[] {\n return this.native.drainMeterTelemetry(maxRecords);\n }\n\n destroy(): void {\n this.native.delete();\n }\n}\n\n// ============================================================================\n// Quick API (High-level Analysis)\n// ============================================================================\n\n/**\n * Detect BPM from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Detected BPM\n */\nexport function detectBpm(samples: Float32Array, sampleRate = 22050): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.detectBpm(samples, sampleRate);\n}\n\n/**\n * Detect musical key from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Detected key\n */\nexport function detectKey(\n samples: Float32Array,\n sampleRate = 22050,\n options: KeyDetectionOptions = {},\n): Key {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result = module._detectKeyWithOptions(\n samples,\n sampleRate,\n options.nFft ?? 4096,\n options.hopLength ?? 512,\n options.useHpss ?? false,\n options.loudnessWeighted ?? false,\n options.highPassHz ?? 0,\n keyModeValues(options.modes),\n keyProfileValue(options.profile),\n options.genreHint ?? '',\n );\n return {\n root: result.root as PitchClass,\n mode: result.mode as Mode,\n confidence: result.confidence,\n name: result.name,\n shortName: result.shortName,\n };\n}\n\nfunction convertKeyCandidate(wasm: WasmKeyCandidateResult): KeyCandidate {\n return {\n key: {\n root: wasm.key.root as PitchClass,\n mode: wasm.key.mode as Mode,\n confidence: wasm.key.confidence,\n name: wasm.key.name,\n shortName: wasm.key.shortName,\n },\n correlation: wasm.correlation,\n };\n}\n\nfunction keyModeValues(modes: KeyDetectionOptions['modes'] | undefined): number[] {\n if (!modes) {\n return [];\n }\n if (modes === 'major-minor') {\n return [Mode.Major, Mode.Minor];\n }\n if (modes === 'all' || modes === 'modal') {\n return [\n Mode.Major,\n Mode.Minor,\n Mode.Dorian,\n Mode.Phrygian,\n Mode.Lydian,\n Mode.Mixolydian,\n Mode.Locrian,\n ];\n }\n const names = {\n major: Mode.Major,\n minor: Mode.Minor,\n dorian: Mode.Dorian,\n phrygian: Mode.Phrygian,\n lydian: Mode.Lydian,\n mixolydian: Mode.Mixolydian,\n locrian: Mode.Locrian,\n } as const;\n return modes.map((mode) => (typeof mode === 'number' ? mode : names[mode]));\n}\n\nfunction keyProfileValue(profile: KeyDetectionOptions['profile'] | undefined): number {\n if (profile === undefined) {\n return -1;\n }\n if (typeof profile === 'number') {\n return profile;\n }\n const names: Record<KeyProfileName, number> = {\n ks: KeyProfileValues.KrumhanslSchmuckler,\n krumhansl: KeyProfileValues.KrumhanslSchmuckler,\n temperley: KeyProfileValues.Temperley,\n shaath: KeyProfileValues.Shaath,\n keyfinder: KeyProfileValues.Shaath,\n 'faraldo-edmt': KeyProfileValues.FaraldoEDMT,\n edmt: KeyProfileValues.FaraldoEDMT,\n 'faraldo-edma': KeyProfileValues.FaraldoEDMA,\n edma: KeyProfileValues.FaraldoEDMA,\n 'faraldo-edmm': KeyProfileValues.FaraldoEDMM,\n edmm: KeyProfileValues.FaraldoEDMM,\n 'bellman-budge': KeyProfileValues.BellmanBudge,\n bellman: KeyProfileValues.BellmanBudge,\n };\n return names[profile];\n}\n\nexport function detectKeyCandidates(\n samples: Float32Array,\n sampleRate = 22050,\n options: KeyDetectionOptions = {},\n): KeyCandidate[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module\n ._detectKeyCandidates(\n samples,\n sampleRate,\n options.nFft ?? 4096,\n options.hopLength ?? 512,\n options.useHpss ?? false,\n options.loudnessWeighted ?? false,\n options.highPassHz ?? 0,\n keyModeValues(options.modes),\n keyProfileValue(options.profile),\n options.genreHint ?? '',\n )\n .map(convertKeyCandidate);\n}\n\n/**\n * Detect onset times from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Array of onset times in seconds\n */\nexport function detectOnsets(samples: Float32Array, sampleRate = 22050): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.detectOnsets(samples, sampleRate);\n}\n\n/**\n * Detect beat times from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Array of beat times in seconds\n */\nexport function detectBeats(samples: Float32Array, sampleRate = 22050): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.detectBeats(samples, sampleRate);\n}\n\n/**\n * Detect downbeat times from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Array of downbeat times in seconds\n */\nexport function detectDownbeats(samples: Float32Array, sampleRate = 22050): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.detectDownbeats(samples, sampleRate);\n}\n\nfunction convertChordAnalysisResult(wasm: WasmChordAnalysisResult): ChordAnalysisResult {\n return {\n chords: wasm.chords.map((c) => ({\n root: c.root as PitchClass,\n bass: c.bass as PitchClass,\n quality: c.quality as ChordQuality,\n start: c.start,\n end: c.end,\n confidence: c.confidence,\n name: c.name,\n })),\n };\n}\n\n/**\n * Detect chords from audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param options - Optional chord detection settings\n * @returns Detected chord segments\n */\nexport function detectChords(\n samples: Float32Array,\n sampleRate = 22050,\n options: ChordDetectionOptions = {},\n): ChordAnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result = module.detectChords(\n samples,\n sampleRate,\n options.minDuration ?? 0.3,\n options.smoothingWindow ?? 2.0,\n options.threshold ?? 0.5,\n options.useTriadsOnly ?? false,\n options.nFft ?? 2048,\n options.hopLength ?? 512,\n options.useBeatSync ?? true,\n options.useHmm ?? false,\n options.hmmBeamWidth ?? 24,\n options.useKeyContext ?? false,\n options.keyRoot ?? PitchClass.C,\n options.keyMode ?? Mode.Major,\n options.detectInversions ?? false,\n chordChromaMethodValue(options.chromaMethod ?? 'stft'),\n );\n return convertChordAnalysisResult(result);\n}\n\nfunction chordChromaMethodValue(method: 'stft' | 'nnls'): number {\n if (method === 'stft') {\n return 0;\n }\n if (method === 'nnls') {\n return 1;\n }\n throw new Error(`Invalid chord chroma method: ${method}`);\n}\n\n// Helper to convert WASM result to typed result\nfunction convertAnalysisResult(wasm: WasmAnalysisResult): AnalysisResult {\n const beatTimes = new Float32Array(wasm.beats.length);\n for (let i = 0; i < wasm.beats.length; i++) {\n beatTimes[i] = wasm.beats[i].time;\n }\n return {\n bpm: wasm.bpm,\n bpmConfidence: wasm.bpmConfidence,\n key: {\n root: wasm.key.root as PitchClass,\n mode: wasm.key.mode as Mode,\n confidence: wasm.key.confidence,\n name: wasm.key.name,\n shortName: wasm.key.shortName,\n },\n timeSignature: wasm.timeSignature,\n beatTimes,\n beats: wasm.beats,\n chords: wasm.chords.map((c) => ({\n root: c.root as PitchClass,\n bass: c.bass as PitchClass,\n quality: c.quality as ChordQuality,\n start: c.start,\n end: c.end,\n confidence: c.confidence,\n name: c.name,\n })),\n sections: wasm.sections.map((s) => ({\n type: s.type as SectionType,\n start: s.start,\n end: s.end,\n energyLevel: s.energyLevel,\n confidence: s.confidence,\n name: s.name,\n })),\n timbre: wasm.timbre,\n dynamics: wasm.dynamics,\n rhythm: wasm.rhythm,\n form: wasm.form,\n };\n}\n\n/**\n * Perform complete music analysis.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Complete analysis result\n */\nexport function analyze(samples: Float32Array, sampleRate = 22050): AnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result = module.analyze(samples, sampleRate);\n return convertAnalysisResult(result);\n}\n\nexport function analyzeImpulseResponse(\n samples: Float32Array,\n sampleRate = 48000,\n nOctaveBands = 6,\n): AcousticResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result: WasmAcousticResult = module.analyzeImpulseResponse(\n samples,\n sampleRate,\n nOctaveBands,\n );\n return result;\n}\n\nexport function detectAcoustic(\n samples: Float32Array,\n sampleRate = 48000,\n nOctaveBands = 6,\n nThirdOctaveSubbands = 24,\n minDecayDb = 30.0,\n noiseFloorMarginDb = 10.0,\n): AcousticResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result: WasmAcousticResult = module.detectAcoustic(\n samples,\n sampleRate,\n nOctaveBands,\n nThirdOctaveSubbands,\n minDecayDb,\n noiseFloorMarginDb,\n );\n return result;\n}\n\n/**\n * Synthesize a room impulse response from shoebox geometry. `hasError` is true\n * when the source/listener falls outside the room (the RIR is then empty).\n */\nexport function synthesizeRir(options: RirSynthOptions = {}): RirResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (typeof module.synthesizeRir !== 'function') {\n throw new Error('libsonare was built without acoustic-simulation support');\n }\n return module.synthesizeRir(options);\n}\n\n/**\n * Estimate an equivalent room (volume/dimensions/absorption/DRR) from a\n * recording or impulse response.\n */\nexport function estimateRoom(\n samples: Float32Array,\n sampleRate = 48000,\n options: RoomEstimateOptions = {},\n): RoomEstimateResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (typeof module.estimateRoom !== 'function') {\n throw new Error('libsonare was built without acoustic-simulation support');\n }\n return module.estimateRoom(samples, sampleRate, options);\n}\n\n/**\n * Morph a recording's reverberation toward a target room (creative FX, not\n * dereverberation). Returns the morphed samples (input length plus the target\n * room's reverb tail).\n */\nexport function roomMorph(\n samples: Float32Array,\n sampleRate: number,\n options: RoomMorphOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (typeof module.roomMorph !== 'function') {\n throw new Error('libsonare was built without acoustic-simulation support');\n }\n return module.roomMorph(samples, sampleRate, options);\n}\n\n/**\n * Perform complete music analysis with progress reporting.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param onProgress - Progress callback (progress: 0-1, stage: string)\n * @returns Complete analysis result\n */\nexport function analyzeWithProgress(\n samples: Float32Array,\n sampleRate = 22050,\n onProgress: ProgressCallback,\n): AnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n const result = module.analyzeWithProgress(samples, sampleRate, onProgress);\n return convertAnalysisResult(result);\n}\n\nexport interface BpmCandidate {\n bpm: number;\n confidence: number;\n}\n\nexport interface BpmAnalysisResult {\n bpm: number;\n confidence: number;\n candidates: BpmCandidate[];\n autocorrelation: Float32Array;\n tempogram: Float32Array;\n}\n\nexport interface RhythmAnalysisResult {\n timeSignature: { numerator: number; denominator: number; confidence: number };\n syncopation: number;\n grooveType: string;\n patternRegularity: number;\n tempoStability: number;\n bpm: number;\n beatIntervals: Float32Array;\n}\n\nexport interface DynamicsAnalysisResult {\n dynamicRangeDb: number;\n peakDb: number;\n rmsDb: number;\n crestFactor: number;\n loudnessRangeDb: number;\n isCompressed: boolean;\n /** Loudness curve timestamps (seconds), parallel to {@link loudnessRmsDb}. */\n loudnessTimes: Float32Array;\n /** Loudness curve RMS values (dB), parallel to {@link loudnessTimes}. */\n loudnessRmsDb: Float32Array;\n}\n\n/** Timbre metrics for one analysis window. Entries are ordered by time in `timbreOverTime`. */\nexport interface TimbreFrame {\n brightness: number;\n warmth: number;\n density: number;\n roughness: number;\n complexity: number;\n}\n\nexport interface TimbreAnalysisResult extends TimbreFrame {\n spectralCentroid: Float32Array;\n spectralFlatness: Float32Array;\n spectralRolloff: Float32Array;\n /** Time-varying timbre metrics, one entry per analysis window. */\n timbreOverTime: TimbreFrame[];\n}\n\n/**\n * Detailed BPM analysis (BPM, confidence, alternate candidates, autocorrelation,\n * tempogram). Matches the Node `analyzeBpm` / Python `analyze_bpm` surface.\n */\nexport function analyzeBpm(\n samples: Float32Array,\n sampleRate = 22050,\n bpmMin = 30.0,\n bpmMax = 300.0,\n startBpm = 120.0,\n nFft = 2048,\n hopLength = 512,\n maxCandidates = 5,\n): BpmAnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.analyzeBpm(\n samples,\n sampleRate,\n bpmMin,\n bpmMax,\n startBpm,\n nFft,\n hopLength,\n maxCandidates,\n );\n}\n\n/**\n * Detailed rhythm analysis (time signature, groove, syncopation, beat intervals).\n */\nexport function analyzeRhythm(\n samples: Float32Array,\n sampleRate = 22050,\n bpmMin = 60.0,\n bpmMax = 200.0,\n startBpm = 120.0,\n nFft = 2048,\n hopLength = 512,\n): RhythmAnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.analyzeRhythm(samples, sampleRate, bpmMin, bpmMax, startBpm, nFft, hopLength);\n}\n\n/**\n * Dynamics analysis (RMS, peak, crest factor, LRA, loudness curve).\n */\nexport function analyzeDynamics(\n samples: Float32Array,\n sampleRate = 22050,\n windowSec = 0.4,\n hopLength = 512,\n compressionThreshold = 6.0,\n): DynamicsAnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.analyzeDynamics(samples, sampleRate, windowSec, hopLength, compressionThreshold);\n}\n\n/**\n * Timbre analysis (brightness/warmth/density/roughness/complexity plus spectral\n * features and per-window timbre frames).\n */\nexport function analyzeTimbre(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n nMels = 128,\n nMfcc = 13,\n windowSec = 0.5,\n): TimbreAnalysisResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.analyzeTimbre(samples, sampleRate, nFft, hopLength, nMels, nMfcc, windowSec);\n}\n\n/**\n * Whether this WASM build was compiled with FFmpeg support. Mirrors Node /\n * Python `hasFfmpegSupport`. In the published WASM binding this currently\n * always returns `false` (FFmpeg is not bundled into the .wasm), but the API\n * exists so caller code can branch on capabilities portably.\n */\nexport function hasFfmpegSupport(): boolean {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hasFfmpegSupport();\n}\n\n// ============================================================================\n// Effects\n// ============================================================================\n\n/**\n * Perform Harmonic-Percussive Source Separation (HPSS).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param kernelHarmonic - Horizontal median filter size for harmonic (default: 31)\n * @param kernelPercussive - Vertical median filter size for percussive (default: 31)\n * @returns Separated harmonic and percussive components\n */\nexport function hpss(\n samples: Float32Array,\n sampleRate = 22050,\n kernelHarmonic = 31,\n kernelPercussive = 31,\n): HpssResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hpss(samples, sampleRate, kernelHarmonic, kernelPercussive);\n}\n\n/**\n * Extract harmonic component from audio.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @returns Harmonic component\n */\nexport function harmonic(\n samples: Float32Array,\n sampleRate: number,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('harmonic', samples, options.validate !== false);\n return module.harmonic(samples, sampleRate);\n}\n\n/**\n * Extract percussive component from audio.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @returns Percussive component\n */\nexport function percussive(\n samples: Float32Array,\n sampleRate: number,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('percussive', samples, options.validate !== false);\n return module.percussive(samples, sampleRate);\n}\n\n/**\n * Time-stretch audio without changing pitch.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param rate - Time stretch rate (0.5 = double duration, 2.0 = half duration)\n * @returns Time-stretched audio\n */\nexport function timeStretch(\n samples: Float32Array,\n sampleRate: number,\n rate: number,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('timeStretch', samples, options.validate !== false);\n return module.timeStretch(samples, sampleRate, rate);\n}\n\n/**\n * Pitch-shift audio without changing duration.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param semitones - Pitch shift in semitones (+12 = one octave up, -12 = one octave down)\n * @returns Pitch-shifted audio\n */\nexport function pitchShift(\n samples: Float32Array,\n sampleRate: number,\n semitones: number,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('pitchShift', samples, options.validate !== false);\n return module.pitchShift(samples, sampleRate, semitones);\n}\n\n/**\n * Pitch-correct audio from a current MIDI note to a target MIDI note.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param currentMidi - Detected/current MIDI note number\n * @param targetMidi - Desired MIDI note number\n * @returns Pitch-corrected audio\n */\nexport function pitchCorrectToMidi(\n samples: Float32Array,\n sampleRate = 22050,\n currentMidi = 69.0,\n targetMidi = 69.0,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('pitchCorrectToMidi', samples, options.validate !== false);\n return module.pitchCorrectToMidi(samples, sampleRate, currentMidi, targetMidi);\n}\n\n/**\n * Time-stretch a note region between two sample offsets without changing pitch.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param onsetSample - Note onset position in samples\n * @param offsetSample - Note offset position in samples\n * @param stretchRatio - Stretch ratio (0.5 = double duration, 2.0 = half duration)\n * @returns Audio with the note region stretched\n */\nexport function noteStretch(\n samples: Float32Array,\n sampleRate = 22050,\n onsetSample = 0,\n offsetSample = 0,\n stretchRatio = 1.0,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('noteStretch', samples, options.validate !== false);\n return module.noteStretch(samples, sampleRate, onsetSample, offsetSample, stretchRatio);\n}\n\n/**\n * Apply a voice change by shifting pitch and formants independently.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param pitchSemitones - Pitch shift in semitones\n * @param formantFactor - Formant scaling factor (1.0 = unchanged)\n * @returns Voice-changed audio\n */\nexport function voiceChange(\n samples: Float32Array,\n sampleRate = 22050,\n pitchSemitones = 0.0,\n formantFactor = 1.0,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('voiceChange', samples, options.validate !== false);\n return module.voiceChange(samples, sampleRate, pitchSemitones, formantFactor);\n}\n\n/** Options for the offline {@link voiceChangeRealtime} convenience wrapper. */\nexport interface VoiceChangeRealtimeOptions extends ValidateOptions {\n sampleRate?: number;\n /** Voice-changer preset id or full config object. */\n preset?: RealtimeVoiceChangerConfigInput;\n /** Channel count (1 = mono, 2 = interleaved stereo). */\n channels?: 1 | 2;\n /** Block size for the internal render loop (default 512). */\n blockSize?: number;\n}\n\n/**\n * Applies the realtime voice-changer chain to a whole buffer in one call.\n *\n * Constructs and prepares a {@link RealtimeVoiceChanger}, runs the block loop\n * for the caller, then disposes it — matching the Python `voice_change_realtime`\n * and Node `voiceChangeRealtime` convenience wrappers. For mono, `samples` is a\n * plain mono buffer; for stereo, `samples` is interleaved (L0,R0,L1,R1,...).\n *\n * @returns The processed buffer (same layout/length as the input).\n */\nexport function voiceChangeRealtime(\n samples: Float32Array,\n options: VoiceChangeRealtimeOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('voiceChangeRealtime', samples, options.validate !== false);\n const channels = options.channels ?? 1;\n if (channels !== 1 && channels !== 2) {\n throw new Error('voiceChangeRealtime: channels must be 1 or 2.');\n }\n // 48000 matches the Python voice_change_realtime and Node voiceChangeRealtime\n // convenience wrappers (and the RealtimeVoiceChanger default).\n const sampleRate = options.sampleRate ?? 48000;\n const blockSize = Math.max(1, Math.floor(options.blockSize ?? 512));\n const changer = new RealtimeVoiceChanger(options.preset ?? 'neutral-monitor');\n try {\n changer.prepare(sampleRate, blockSize, channels);\n const out = new Float32Array(samples.length);\n if (channels === 1) {\n for (let offset = 0; offset < samples.length; offset += blockSize) {\n const block = samples.subarray(offset, Math.min(offset + blockSize, samples.length));\n out.set(changer.processMono(block), offset);\n }\n } else {\n const frameStride = blockSize * 2;\n for (let offset = 0; offset < samples.length; offset += frameStride) {\n const block = samples.subarray(offset, Math.min(offset + frameStride, samples.length));\n out.set(changer.processInterleaved(block, 2), offset);\n }\n }\n return out;\n } finally {\n changer.delete();\n }\n}\n\n/**\n * Normalize audio to target peak level.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param targetDb - Target peak level in dB (default: 0 dB = full scale)\n * @returns Normalized audio\n */\nexport function normalize(\n samples: Float32Array,\n sampleRate: number,\n targetDb = 0.0,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('normalize', samples, options.validate !== false);\n return module.normalize(samples, sampleRate, targetDb);\n}\n\n/**\n * Apply mastering loudness normalization with a true-peak ceiling.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param targetLufs - Target integrated LUFS (default: -14)\n * @param ceilingDb - True/sample peak ceiling in dBFS (default: -1)\n * @param truePeakOversample - Oversampling factor used for peak estimation\n * @returns Processed audio and loudness metadata\n */\nexport function mastering(\n samples: Float32Array,\n sampleRate = 22050,\n targetLufs = -14.0,\n ceilingDb = -1.0,\n truePeakOversample = 4,\n): MasteringResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mastering(samples, sampleRate, targetLufs, ceilingDb, truePeakOversample);\n}\n\nexport function masteringProcessorNames(): SoloProcessor[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringProcessorNames() as SoloProcessor[];\n}\n\nexport function masteringPairProcessorNames(): PairProcessor[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringPairProcessorNames() as PairProcessor[];\n}\n\nexport function masteringPairAnalysisNames(): PairAnalysis[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringPairAnalysisNames() as PairAnalysis[];\n}\n\nexport function masteringStereoAnalysisNames(): StereoAnalysis[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringStereoAnalysisNames() as StereoAnalysis[];\n}\n\nexport function masteringProcess(\n processorName: SoloProcessor,\n samples: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): MasteringResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringProcess(processorName, samples, sampleRate, params);\n}\n\nexport function masteringProcessStereo(\n processorName: SoloProcessor,\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): MasteringStereoResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return module.masteringProcessStereo(processorName, left, right, sampleRate, params);\n}\n\nexport function masteringPairProcess(\n processorName: PairProcessor,\n source: Float32Array,\n reference: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): MasteringResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringPairProcess(processorName, source, reference, sampleRate, params);\n}\n\nexport function masteringPairAnalyze(\n analysisName: PairAnalysis,\n source: Float32Array,\n reference: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringPairAnalyze(analysisName, source, reference, sampleRate, params);\n}\n\nexport function masteringStereoAnalyze(\n analysisName: StereoAnalysis,\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringStereoAnalyze(analysisName, left, right, sampleRate, params);\n}\n\nexport function masteringAssistantSuggest(\n samples: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringAssistantSuggest(samples, sampleRate, params);\n}\n\nexport function masteringAudioProfile(\n samples: Float32Array,\n sampleRate = 22050,\n params: MasteringProcessorParams = {},\n): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringAudioProfile(samples, sampleRate, params);\n}\n\nexport function masteringStreamingPreview(\n samples: Float32Array,\n sampleRate = 22050,\n platforms: StreamingPlatform[] = [],\n): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringStreamingPreview(samples, sampleRate, platforms);\n}\n\n// ============================================================================\n// Mastering repair (declick, denoise_classical, declip, decrackle, dehum,\n// dereverb_classical, trim_silence) — hand-written bindings.\n// ============================================================================\n\n/** Options for `masteringRepairDeclick`. */\nexport interface DeclickOptions {\n threshold?: number;\n neighborRatio?: number;\n maxClickSamples?: number;\n lpcOrder?: number;\n residualRatio?: number;\n}\n\n/** Algorithms accepted by `masteringRepairDenoiseClassical`. */\nexport type DenoiseClassicalMode = 'logMmse' | 'mmseStsa' | 'spectralSubtraction';\n\n/** Noise PSD estimators accepted by `masteringRepairDenoiseClassical`. */\nexport type DenoiseClassicalNoiseEstimator = 'quantile' | 'mcra' | 'imcra';\n\n/** Options for `masteringRepairDenoiseClassical`. */\nexport interface DenoiseClassicalOptions {\n mode?: DenoiseClassicalMode;\n noiseEstimator?: DenoiseClassicalNoiseEstimator;\n nFft?: number;\n hopLength?: number;\n ddAlpha?: number;\n gainFloor?: number;\n overSubtraction?: number;\n spectralFloor?: number;\n noiseEstimationQuantile?: number;\n speechPresenceGain?: boolean;\n gainSmoothing?: boolean;\n}\n\n/** Offline LPC-based declicker. */\nexport function masteringRepairDeclick(\n samples: Float32Array,\n sampleRate: number,\n options: DeclickOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDeclick(samples, sampleRate, options);\n}\n\n/** Offline STFT-domain classical denoiser (LogMMSE / MMSE-STSA / SpectralSubtraction). */\nexport function masteringRepairDenoiseClassical(\n samples: Float32Array,\n sampleRate: number,\n options: DenoiseClassicalOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDenoiseClassical(samples, sampleRate, options);\n}\n\n/** Options for `masteringRepairDeclip`. */\nexport interface DeclipOptions {\n clipThreshold?: number;\n lpcOrder?: number;\n iterations?: number;\n lpcBlend?: number;\n}\n\n/** Algorithms accepted by `masteringRepairDecrackle`. */\nexport type DecrackleMode = 'median' | 'waveletShrinkage';\n\n/** Options for `masteringRepairDecrackle`. */\nexport interface DecrackleOptions {\n threshold?: number;\n mode?: DecrackleMode;\n levels?: number;\n}\n\n/** Options for `masteringRepairDehum`. */\nexport interface DehumOptions {\n fundamentalHz?: number;\n harmonics?: number;\n q?: number;\n adaptive?: boolean;\n searchRangeHz?: number;\n adaptation?: number;\n frameSize?: number;\n pllBandwidth?: number;\n}\n\n/** Options for `masteringRepairDereverbClassical`. */\nexport interface DereverbClassicalOptions {\n threshold?: number;\n attenuation?: number;\n nFft?: number;\n hopLength?: number;\n t60Sec?: number;\n lateDelayMs?: number;\n overSubtraction?: number;\n spectralFloor?: number;\n wpeEnabled?: boolean;\n wpeIterations?: number;\n wpeTaps?: number;\n wpeStrength?: number;\n}\n\n/** Trimming modes accepted by `masteringRepairTrimSilence`. */\nexport type TrimSilenceMode = 'peak' | 'lufsGated';\n\n/** Options for `masteringRepairTrimSilence`. */\nexport interface TrimSilenceOptions {\n threshold?: number;\n paddingSamples?: number;\n mode?: TrimSilenceMode;\n gateLufs?: number;\n windowMs?: number;\n}\n\n/** Offline LPC-based declipper. */\nexport function masteringRepairDeclip(\n samples: Float32Array,\n sampleRate: number,\n options: DeclipOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDeclip(samples, sampleRate, options);\n}\n\n/** Offline crackle suppressor (median or wavelet-shrinkage). */\nexport function masteringRepairDecrackle(\n samples: Float32Array,\n sampleRate: number,\n options: DecrackleOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDecrackle(samples, sampleRate, options);\n}\n\n/** Offline mains-hum remover. */\nexport function masteringRepairDehum(\n samples: Float32Array,\n sampleRate: number,\n options: DehumOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDehum(samples, sampleRate, options);\n}\n\n/** Offline classical dereverberator (spectral subtraction + optional WPE). */\nexport function masteringRepairDereverbClassical(\n samples: Float32Array,\n sampleRate: number,\n options: DereverbClassicalOptions = {},\n): Float32Array {\n return requireModule().masteringRepairDereverbClassical(samples, sampleRate, options);\n}\n\n/** Offline silence trimmer (peak threshold or LUFS-gated). */\nexport function masteringRepairTrimSilence(\n samples: Float32Array,\n sampleRate: number,\n options: TrimSilenceOptions = {},\n): Float32Array {\n return requireModule().masteringRepairTrimSilence(samples, sampleRate, options);\n}\n\n// ============================================================================\n// Mastering — offline dynamics processors (compressor / gate / transient_shaper)\n// ============================================================================\n\n/** Compressor sidechain detector mode. */\nexport type CompressorDetector = 'peak' | 'rms' | 'log_rms';\n\n/** Options for `masteringDynamicsCompressor`. */\nexport interface CompressorOptions extends ValidateOptions {\n thresholdDb?: number;\n ratio?: number;\n attackMs?: number;\n releaseMs?: number;\n kneeDb?: number;\n makeupGainDb?: number;\n autoMakeup?: boolean;\n detector?: CompressorDetector | number;\n sidechainHpfEnabled?: boolean;\n sidechainHpfHz?: number;\n pdrTimeMs?: number;\n pdrReleaseScale?: number;\n}\n\n/** Options for `masteringDynamicsGate`. */\nexport interface GateOptions extends ValidateOptions {\n thresholdDb?: number;\n attackMs?: number;\n releaseMs?: number;\n rangeDb?: number;\n holdMs?: number;\n closeThresholdDb?: number;\n keyHpfHz?: number;\n}\n\n/** Options for `masteringDynamicsTransientShaper`. */\nexport interface TransientShaperOptions extends ValidateOptions {\n attackGainDb?: number;\n sustainGainDb?: number;\n fastAttackMs?: number;\n fastReleaseMs?: number;\n slowAttackMs?: number;\n slowReleaseMs?: number;\n sensitivity?: number;\n maxGainDb?: number;\n gainSmoothingMs?: number;\n lookaheadMs?: number;\n}\n\n/** Result envelope returned by offline mastering dynamics processors. */\nexport interface DynamicsResult {\n samples: Float32Array;\n latencySamples: number;\n}\n\nconst COMPRESSOR_DETECTOR_MAP: Record<CompressorDetector, number> = {\n peak: 0,\n rms: 1,\n log_rms: 2,\n};\n\n/** Offline feed-forward compressor (soft knee, optional auto-makeup / sidechain HPF). */\nexport function masteringDynamicsCompressor(\n samples: Float32Array,\n sampleRate: number,\n options: CompressorOptions = {},\n): DynamicsResult {\n assertSamples('masteringDynamicsCompressor', samples, options.validate !== false);\n const detector =\n typeof options.detector === 'string'\n ? COMPRESSOR_DETECTOR_MAP[options.detector]\n : options.detector;\n const opts: Record<string, unknown> = { ...options };\n if (detector !== undefined) {\n opts.detector = detector;\n }\n return requireModule().masteringDynamicsCompressor(samples, sampleRate, opts);\n}\n\n/** Offline noise gate (hysteresis, hold, optional key HPF). */\nexport function masteringDynamicsGate(\n samples: Float32Array,\n sampleRate: number,\n options: GateOptions = {},\n): DynamicsResult {\n assertSamples('masteringDynamicsGate', samples, options.validate !== false);\n return requireModule().masteringDynamicsGate(samples, sampleRate, options);\n}\n\n/** Offline transient shaper (envelope-difference attack/sustain control). */\nexport function masteringDynamicsTransientShaper(\n samples: Float32Array,\n sampleRate: number,\n options: TransientShaperOptions = {},\n): DynamicsResult {\n assertSamples('masteringDynamicsTransientShaper', samples, options.validate !== false);\n return requireModule().masteringDynamicsTransientShaper(samples, sampleRate, options);\n}\n\n/**\n * Apply a configurable mastering chain in WASM.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param config - Chain stage configuration\n * @returns Processed audio, loudness metadata, and applied stage names\n */\nexport function masteringChain(\n samples: Float32Array,\n sampleRate = 22050,\n config: MasteringChainConfig,\n): MasteringChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringChain(samples, sampleRate, config as Record<string, unknown>);\n}\n\n/**\n * Apply a configurable stereo mastering chain in WASM.\n *\n * @param left - Left channel samples\n * @param right - Right channel samples\n * @param sampleRate - Sample rate in Hz\n * @param config - Chain stage configuration\n * @returns Processed stereo audio, loudness metadata, and applied stage names\n */\nexport function masteringChainStereo(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n config: MasteringChainConfig,\n): MasteringStereoChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return module.masteringChainStereo(left, right, sampleRate, config as Record<string, unknown>);\n}\n\n/**\n * Apply a configurable mastering chain in WASM with progress reporting.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param config - Chain stage configuration\n * @param onProgress - Progress callback (progress: 0-1, stage: string)\n * @returns Processed audio, loudness metadata, and applied stage names\n */\nexport function masteringChainWithProgress(\n samples: Float32Array,\n sampleRate = 22050,\n config: MasteringChainConfig,\n onProgress: ProgressCallback,\n): MasteringChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringChainWithProgress(\n samples,\n sampleRate,\n config as Record<string, unknown>,\n onProgress,\n );\n}\n\n/**\n * Apply a configurable stereo mastering chain in WASM with progress reporting.\n *\n * @param left - Left channel samples\n * @param right - Right channel samples\n * @param sampleRate - Sample rate in Hz\n * @param config - Chain stage configuration\n * @param onProgress - Progress callback (progress: 0-1, stage: string)\n * @returns Processed stereo audio, loudness metadata, and applied stage names\n */\nexport function masteringChainStereoWithProgress(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n config: MasteringChainConfig,\n onProgress: ProgressCallback,\n): MasteringStereoChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return module.masteringChainStereoWithProgress(\n left,\n right,\n sampleRate,\n config as Record<string, unknown>,\n onProgress,\n );\n}\n\n/**\n * List built-in mastering preset identifiers.\n *\n * @returns Preset names in display order (e.g. \"pop\", \"edm\", \"aiMusic\")\n */\nexport function masteringPresetNames(): MasteringPreset[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masteringPresetNames() as MasteringPreset[];\n}\n\n/**\n * Apply a named mastering preset chain to mono audio.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param presetName - Preset identifier from {@link masteringPresetNames}\n * @param overrides - Optional flat overrides (dot-notation, e.g. `'loudness.targetLufs'`) applied on top of the preset. Pass `null` for preset defaults.\n * @returns Processed audio, loudness metadata, and applied stage names\n */\nexport function masterAudio(\n samples: Float32Array,\n sampleRate = 22050,\n presetName: MasteringPreset,\n overrides: Record<string, number | boolean> | null = null,\n): MasteringChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masterAudio(presetName, samples, sampleRate, overrides);\n}\n\n/**\n * Apply a named mastering preset chain to stereo audio.\n *\n * @param left - Left channel samples\n * @param right - Right channel samples\n * @param sampleRate - Sample rate in Hz\n * @param presetName - Preset identifier from {@link masteringPresetNames}\n * @param overrides - Optional flat overrides (dot-notation, e.g. `'loudness.targetLufs'`) applied on top of the preset. Pass `null` for preset defaults.\n * @returns Processed stereo audio, loudness metadata, and applied stage names\n */\nexport function masterAudioStereo(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n presetName: MasteringPreset,\n overrides: Record<string, number | boolean> | null = null,\n): MasteringStereoChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return module.masterAudioStereo(presetName, left, right, sampleRate, overrides);\n}\n\n/**\n * Mono `masterAudio` with per-stage progress reporting. `onProgress` is invoked\n * with `(progress, stage)` between each chain stage (progress is in [0,1]).\n */\nexport function masterAudioWithProgress(\n samples: Float32Array,\n sampleRate = 22050,\n presetName: MasteringPreset,\n onProgress: ProgressCallback,\n overrides: Record<string, number | boolean> | null = null,\n): MasteringChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.masterAudioWithProgress(presetName, samples, sampleRate, overrides, onProgress);\n}\n\n/**\n * Stereo `masterAudio` with per-stage progress reporting.\n */\nexport function masterAudioStereoWithProgress(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n presetName: MasteringPreset,\n onProgress: ProgressCallback,\n overrides: Record<string, number | boolean> | null = null,\n): MasteringStereoChainResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return module.masterAudioStereoWithProgress(\n presetName,\n left,\n right,\n sampleRate,\n overrides,\n onProgress,\n );\n}\n\nexport function mixingScenePresetNames(): string[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mixingScenePresetNames();\n}\n\n/**\n * Get a built-in mixing scene preset serialized as JSON. This is the canonical\n * name shared with the Node and Python bindings; the returned JSON loads\n * directly into a {@link Mixer} via {@link Mixer.fromSceneJson}.\n *\n * @param presetName - Preset name (see {@link mixingScenePresetNames})\n * @returns Scene JSON string\n */\nexport function mixingScenePresetJson(presetName: string): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mixingScenePresetJson(presetName);\n}\n\nexport function mixStereo(\n leftChannels: Float32Array[],\n rightChannels: Float32Array[],\n sampleRate = 48000,\n options: MixOptions = {},\n): MixResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n if (leftChannels.length === 0 || leftChannels.length !== rightChannels.length) {\n throw new Error('leftChannels and rightChannels must have the same non-zero length.');\n }\n return module.mixStereo(\n leftChannels,\n rightChannels,\n sampleRate,\n options as Record<string, unknown>,\n );\n}\n\n// ============================================================================\n// StreamingMasteringChain Class\n// ============================================================================\n\n/**\n * Block-by-block streaming variant of {@link masteringChain}.\n *\n * Maintains processor state across {@link processMono}/{@link processStereo}\n * calls. Only ProcessorBase-backed stages are supported. Configurations that\n * enable `repair.denoise` or `loudness` throw at construction.\n *\n * Call {@link delete} (or use a `try/finally`) to release the underlying WASM\n * object — the embind handle is not garbage-collected automatically.\n *\n * @example\n * ```typescript\n * const chain = new StreamingMasteringChain({ eq: { tiltDb: 1.0 } });\n * try {\n * chain.prepare(44100, 512, 1);\n * const out = chain.processMono(blockSamples);\n * } finally {\n * chain.delete();\n * }\n * ```\n */\nexport class StreamingMasteringChain {\n private chain: import('./sonare.js').WasmStreamingMasteringChain;\n\n constructor(config: MasteringChainConfig) {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n this.chain = module.createStreamingMasteringChain(config as Record<string, unknown>);\n }\n\n /**\n * Initialize processors for the given sample rate and block layout.\n *\n * @param sampleRate - Sample rate in Hz\n * @param maxBlockSize - Maximum block size per process call\n * @param numChannels - 1 (mono) or 2 (stereo)\n */\n prepare(sampleRate: number, maxBlockSize: number, numChannels: number): void {\n this.chain.prepare(sampleRate, maxBlockSize, numChannels);\n }\n\n /**\n * Process one mono block, returning the processed samples (same length).\n */\n processMono(samples: Float32Array): Float32Array {\n return this.chain.processMono(samples);\n }\n\n /**\n * Process one stereo block, returning the processed channels.\n */\n processStereo(\n left: Float32Array,\n right: Float32Array,\n ): { left: Float32Array; right: Float32Array } {\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return this.chain.processStereo(left, right);\n }\n\n /** Reset all processor state without rebuilding. */\n reset(): void {\n this.chain.reset();\n }\n\n /** Total reported latency in samples across all active processors. */\n latencySamples(): number {\n return this.chain.latencySamples();\n }\n\n /** Ordered stage names that will run (e.g. `\"eq.tilt\"`). */\n stageNames(): string[] {\n return this.chain.stageNames();\n }\n\n /** Release the underlying WASM object. Safe to call only once. */\n delete(): void {\n this.chain.delete();\n }\n}\n\n// ============================================================================\n// StreamingEqualizer Class\n// ============================================================================\n\n/**\n * Block-by-block streaming equalizer wrapping the unified C++\n * `EqualizerProcessor` (up to 24 bands, RBJ/Vicanek biquads, dynamic EQ,\n * linear-phase FIR, mid/side processing, and auto-gain).\n *\n * State is maintained across {@link processMono}/{@link processStereo} calls.\n * Call {@link delete} (or use `try/finally`) to release the underlying WASM\n * object — the embind handle is not garbage-collected automatically.\n *\n * @example\n * ```typescript\n * const eq = new StreamingEqualizer({ sampleRate: 48000, maxBlockSize: 512 });\n * try {\n * eq.setBand(0, { type: 'HighShelf', frequencyHz: 8000, gainDb: 6, enabled: true });\n * const out = eq.processStereo(left, right);\n * const snapshot = eq.spectrum();\n * } finally {\n * eq.delete();\n * }\n * ```\n */\nexport class StreamingEqualizer {\n private eq: import('./sonare.js').WasmStreamingEqualizer;\n\n constructor(config: StreamingEqualizerConfig = {}) {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n this.eq = module.createEqualizer(config as Record<string, unknown>);\n }\n\n /**\n * Configure the band at `index` (0..23). Omitted fields use C++ defaults.\n */\n setBand(index: number, band: EqBand): void {\n this.eq.setBand(index, band as Record<string, unknown>);\n }\n\n /** Disable and reset every band. */\n clear(): void {\n this.eq.clear();\n }\n\n /**\n * Set the global phase mode: 1=ZeroLatency, 2=NaturalPhase, 3=LinearPhase.\n */\n setPhaseMode(mode: number): void {\n this.eq.setPhaseMode(mode);\n }\n\n /** Enable or disable output auto-gain compensation. */\n setAutoGain(enabled: boolean): void {\n this.eq.setAutoGain(enabled);\n }\n\n /** Set all-band EQ gain scale as a 0.0..2.0 multiplier. */\n setGainScale(scale: number): void {\n this.eq.setGainScale(scale);\n }\n\n /** Set post-EQ output gain in dB. */\n setOutputGainDb(gainDb: number): void {\n this.eq.setOutputGainDb(gainDb);\n }\n\n /** Set post-EQ stereo balance in -1.0..1.0; mono input ignores pan. */\n setOutputPan(pan: number): void {\n this.eq.setOutputPan(pan);\n }\n\n /**\n * Provide a mono external sidechain key for dynamic bands that opt into\n * `external_sidechain`. The samples are copied into an owned buffer.\n */\n setSidechainMono(samples: Float32Array): void {\n this.eq.setSidechainMono(samples);\n }\n\n /**\n * Provide a stereo external sidechain key. Both channels must match length.\n */\n setSidechainStereo(left: Float32Array, right: Float32Array): void {\n if (left.length !== right.length) {\n throw new Error('Sidechain channel lengths must match.');\n }\n this.eq.setSidechainStereo(left, right);\n }\n\n /** Release any borrowed external sidechain buffers. */\n clearSidechain(): void {\n this.eq.clearSidechain();\n }\n\n /** Auto-gain applied on the most recent block, in dB. */\n lastAutoGainDb(): number {\n return this.eq.lastAutoGainDb();\n }\n\n /** Reported processing latency in samples (non-zero for linear-phase bands). */\n latencySamples(): number {\n return this.eq.latencySamples();\n }\n\n /**\n * Process one mono block, returning the equalized samples (same length).\n */\n processMono(samples: Float32Array): Float32Array {\n return this.eq.processMono(samples);\n }\n\n /**\n * Process one stereo block, returning the equalized channels.\n */\n processStereo(\n left: Float32Array,\n right: Float32Array,\n ): { left: Float32Array; right: Float32Array } {\n if (left.length !== right.length) {\n throw new Error('Stereo channel lengths must match.');\n }\n return this.eq.processStereo(left, right);\n }\n\n /**\n * Read the latest pre/post spectrum snapshot for metering. `seq` increments\n * each time a new snapshot is published.\n */\n spectrum(): EqSpectrumSnapshot {\n return this.eq.spectrum();\n }\n\n /**\n * Configure bands so the source spectrum matches the reference spectrum.\n *\n * @param source - Source audio (mono samples)\n * @param reference - Reference audio (mono samples)\n * @param options - `sampleRate` (default 48000) and `maxBands` (default 8)\n */\n match(source: Float32Array, reference: Float32Array, options: EqMatchOptions = {}): void {\n this.eq.match(source, reference, options as Record<string, unknown>);\n }\n\n /** Release the underlying WASM object. Safe to call only once. */\n delete(): void {\n this.eq.delete();\n }\n}\n\n// ============================================================================\n// StreamingRetune Class\n// ============================================================================\n\n/**\n * Block-by-block mono voice retune / pitch shifter.\n *\n * State is maintained across {@link processMono} calls. Call {@link prepare}\n * before processing, and call {@link delete} (or use `try/finally`) to release\n * the underlying WASM object.\n */\nexport class StreamingRetune {\n private retune: import('./sonare.js').WasmStreamingRetune;\n\n constructor(config: StreamingRetuneConfig = {}) {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n this.retune = module.createStreamingRetune(config as Record<string, unknown>);\n }\n\n /**\n * Allocate and initialize native state for the given sample rate and maximum\n * process block size.\n */\n prepare(sampleRate: number, maxBlockSize: number): void {\n this.retune.prepare(sampleRate, maxBlockSize);\n }\n\n /** Reset delay, grain, and overlap-add state without changing config. */\n reset(): void {\n this.retune.reset();\n }\n\n /**\n * Update retune settings. Changing `grainSize` takes effect after the next\n * {@link prepare} call.\n */\n setConfig(config: StreamingRetuneConfig): void {\n this.retune.setConfig(config as Record<string, unknown>);\n }\n\n /** Current native config. */\n config(): Required<StreamingRetuneConfig> {\n return this.retune.config();\n }\n\n /** Resolved grain size in samples after {@link prepare}. */\n grainSize(): number {\n return this.retune.grainSize();\n }\n\n /** Process one mono block, returning the shifted samples (same length). */\n processMono(samples: Float32Array): Float32Array {\n return this.retune.processMono(samples);\n }\n\n /** Release the underlying WASM object. Safe to call only once. */\n delete(): void {\n this.retune.delete();\n }\n}\n\n// ============================================================================\n// RealtimeVoiceChanger Class\n// ============================================================================\n\nexport class RealtimeVoiceChanger {\n private changer: import('./sonare.js').WasmRealtimeVoiceChanger;\n\n constructor(config: RealtimeVoiceChangerConfigInput = 'neutral-monitor') {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n this.changer = module.createRealtimeVoiceChanger(config as Record<string, unknown> | string);\n }\n\n prepare(sampleRate: number, maxBlockSize = 128, channels = 1): void {\n this.changer.prepare(sampleRate, maxBlockSize, channels);\n }\n\n reset(): void {\n this.changer.reset();\n }\n\n setConfig(config: RealtimeVoiceChangerConfigInput): void {\n this.changer.setConfig(config as Record<string, unknown> | string);\n }\n\n configJson(): string {\n return this.changer.configJson();\n }\n\n latencySamples(): number {\n return this.changer.latencySamples();\n }\n\n processMono(samples: Float32Array): Float32Array {\n return this.changer.processMono(samples);\n }\n\n processMonoInto(samples: Float32Array, output: Float32Array): void {\n this.changer.processMonoInto(samples, output);\n }\n\n processInterleaved(samples: Float32Array, channels: number): Float32Array {\n return this.changer.processInterleaved(samples, channels);\n }\n\n processInterleavedInto(samples: Float32Array, channels: number, output: Float32Array): void {\n this.changer.processInterleavedInto(samples, channels, output);\n }\n\n /**\n * Acquire a typed-memory view onto the WASM heap for mono input.\n *\n * Write your input samples into the returned `Float32Array` directly (e.g.\n * via `input.set(source)`); no copy crosses the JS↔C++ bridge until\n * {@link processPreparedMono} is called. The view is owned by this\n * RealtimeVoiceChanger and becomes invalid after {@link delete}; it may\n * also be invalidated if you later call this method with a larger\n * `numSamples` value (the underlying buffer may be reallocated).\n */\n getMonoInputBuffer(numSamples: number): Float32Array {\n return this.changer.getMonoInputBuffer(numSamples);\n }\n\n /** Mono output view counterpart to {@link getMonoInputBuffer}. */\n getMonoOutputBuffer(numSamples: number): Float32Array {\n return this.changer.getMonoOutputBuffer(numSamples);\n }\n\n /**\n * Process the previously-acquired mono input buffer in place. The output\n * appears in the buffer returned by {@link getMonoOutputBuffer}. No JS↔C++\n * sample-level crossings happen on this call — it just hands control to\n * the underlying DSP on already-on-heap data.\n */\n processPreparedMono(numSamples: number): void {\n this.changer.processPreparedMono(numSamples);\n }\n\n /** Interleaved input view (layout L0,R0,L1,R1,...). */\n getInterleavedInputBuffer(numFrames: number, numChannels: number): Float32Array {\n return this.changer.getInterleavedInputBuffer(numFrames, numChannels);\n }\n\n /** Interleaved output view counterpart. */\n getInterleavedOutputBuffer(numFrames: number, numChannels: number): Float32Array {\n return this.changer.getInterleavedOutputBuffer(numFrames, numChannels);\n }\n\n /**\n * Process the previously-acquired interleaved buffer in place. Output\n * appears in the buffer returned by {@link getInterleavedOutputBuffer}.\n */\n processPreparedInterleaved(numFrames: number, numChannels: number): void {\n this.changer.processPreparedInterleaved(numFrames, numChannels);\n }\n\n /**\n * Planar-channel input/output view (one Float32Array per channel). Matches\n * AudioWorklet's native layout; processing happens in place.\n */\n getPlanarChannelBuffer(channel: number, numFrames: number): Float32Array {\n return this.changer.getPlanarChannelBuffer(channel, numFrames);\n }\n\n /**\n * Process the previously-acquired planar channel buffers in place. Each\n * channel must have been obtained from {@link getPlanarChannelBuffer}\n * with the same `numFrames`. Output replaces input in the same buffers.\n */\n processPreparedPlanar(numFrames: number): void {\n this.changer.processPreparedPlanar(numFrames);\n }\n\n /**\n * Convenience factory for the mono zero-copy path: returns the input/output\n * heap views plus a `process()` thunk wired to the same `numSamples`. The\n * views are reused across calls and become invalid after {@link delete}.\n */\n createRealtimeMonoBuffer(numSamples: number): RealtimeVoiceChangerMonoBuffer {\n const input = this.getMonoInputBuffer(numSamples);\n const output = this.getMonoOutputBuffer(numSamples);\n return {\n input,\n output,\n process: () => this.processPreparedMono(numSamples),\n };\n }\n\n /** Same as {@link createRealtimeMonoBuffer} but for interleaved I/O. */\n createRealtimeInterleavedBuffer(\n numFrames: number,\n numChannels: number,\n ): RealtimeVoiceChangerInterleavedBuffer {\n const input = this.getInterleavedInputBuffer(numFrames, numChannels);\n const output = this.getInterleavedOutputBuffer(numFrames, numChannels);\n return {\n input,\n output,\n channels: numChannels,\n process: () => this.processPreparedInterleaved(numFrames, numChannels),\n };\n }\n\n /**\n * Convenience factory for the planar zero-copy path. Acquires one\n * heap-backed Float32Array per channel and returns a `process()` thunk\n * wired to the same `numFrames`. Buffers are reused across calls and\n * become invalid after {@link delete}.\n */\n createRealtimePlanarBuffer(\n numFrames: number,\n numChannels: number,\n ): RealtimeVoiceChangerPlanarBuffer {\n const channels: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channels.push(this.getPlanarChannelBuffer(ch, numFrames));\n }\n return {\n channels,\n process: () => this.processPreparedPlanar(numFrames),\n };\n }\n\n delete(): void {\n this.changer.delete();\n }\n}\n\nexport function realtimeVoiceChangerPresetNames(): VoicePresetId[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.realtimeVoiceChangerPresetNames() as VoicePresetId[];\n}\n\nexport function realtimeVoiceChangerPresetJson(name: VoicePresetId): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.realtimeVoiceChangerPresetJson(name);\n}\n\nexport function validateRealtimeVoiceChangerPresetJson(json: string): {\n ok: boolean;\n normalizedJson?: string;\n error?: string;\n} {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.validateRealtimeVoiceChangerPresetJson(json);\n}\n\n// ============================================================================\n// Mixer Class (scene-based persistent mixer)\n// ============================================================================\n\n/**\n * Persistent, scene-based stereo mixer.\n *\n * Build one from a scene JSON string (e.g. {@link mixingScenePresetJson} or a\n * hand-authored scene), then feed per-strip stereo blocks through\n * {@link processStereo} to get the routed stereo master. Strips, sends, buses,\n * and inserts are described entirely by the scene; the routing graph is\n * compiled lazily on the first {@link processStereo} call (or eagerly via\n * {@link compile}).\n *\n * Call {@link delete} (or use a `try/finally`) to release the underlying WASM\n * object — the embind handle is not garbage-collected automatically.\n *\n * @example\n * ```typescript\n * const mixer = Mixer.fromSceneJson(mixingScenePresetJson('basicStereo'), 48000, 512);\n * try {\n * const out = mixer.processStereo([stripL], [stripR]);\n * } finally {\n * mixer.delete();\n * }\n * ```\n */\nexport class Mixer {\n private mixer: import('./sonare.js').WasmMixer;\n\n private constructor(mixer: import('./sonare.js').WasmMixer) {\n this.mixer = mixer;\n }\n\n /**\n * Build a mixer from a scene JSON string.\n *\n * @param json - Scene JSON (strips, buses, sends, connections, inserts)\n * @param sampleRate - Sample rate in Hz (default: 48000)\n * @param blockSize - Maximum block size per {@link processStereo} call (default: 512)\n */\n static fromSceneJson(json: string, sampleRate = 48000, blockSize = 512): Mixer {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return new Mixer(module.createMixerFromSceneJson(json, sampleRate, blockSize));\n }\n\n /** Rebuild and compile the routing graph from the current scene topology. */\n compile(): void {\n this.mixer.compile();\n }\n\n /**\n * Mix one block of per-strip stereo audio into the stereo master.\n *\n * @param leftChannels - `leftChannels[i]` is the left channel of strip `i`\n * @param rightChannels - `rightChannels[i]` is the right channel of strip `i`\n * @returns Mixed stereo master (`left`, `right`, `sampleRate`)\n */\n processStereo(leftChannels: Float32Array[], rightChannels: Float32Array[]): MixerProcessResult {\n if (leftChannels.length !== rightChannels.length) {\n throw new Error('leftChannels and rightChannels must have the same length.');\n }\n return this.mixer.processStereo(leftChannels, rightChannels);\n }\n\n /**\n * Mix one block into caller-owned output arrays.\n *\n * This avoids allocating the result object and result `Float32Array`s. It is\n * intended for realtime bridges such as AudioWorklet; the input channel count\n * must match the scene strip count and all arrays must have the same length.\n */\n processStereoInto(\n leftChannels: Float32Array[],\n rightChannels: Float32Array[],\n outLeft: Float32Array,\n outRight: Float32Array,\n ): void {\n if (leftChannels.length !== rightChannels.length) {\n throw new Error('leftChannels and rightChannels must have the same length.');\n }\n if (outLeft.length !== outRight.length) {\n throw new Error('outLeft and outRight must have the same length.');\n }\n this.mixer.processStereoInto(leftChannels, rightChannels, outLeft, outRight);\n }\n\n /**\n * Create reusable WASM-heap input/output views for realtime-style processing.\n *\n * Fill `leftInputs[i]` / `rightInputs[i]`, call `process()`, then read\n * `outLeft` / `outRight`. The views are owned by this mixer and become invalid\n * after {@link delete}.\n */\n createRealtimeBuffer(): MixerRealtimeBuffer {\n const stripCount = this.stripCount();\n const leftInputs: Float32Array[] = [];\n const rightInputs: Float32Array[] = [];\n for (let index = 0; index < stripCount; index++) {\n leftInputs.push(this.mixer.inputLeftView(index));\n rightInputs.push(this.mixer.inputRightView(index));\n }\n const outLeft = this.mixer.outputLeftView();\n const outRight = this.mixer.outputRightView();\n return {\n leftInputs,\n rightInputs,\n outLeft,\n outRight,\n process: (numSamples = outLeft.length) => this.mixer.processPreparedStereo(numSamples),\n };\n }\n\n /** Number of strips in the mixer (e.g. strips loaded from the scene). */\n stripCount(): number {\n return this.mixer.stripCount();\n }\n\n /**\n * Schedule sample-accurate insert-parameter automation on a strip's insert.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param insertIndex - Index into the strip's combined insert sequence\n * (`[pre-inserts... post-inserts...]`)\n * @param paramId - Processor-specific parameter id\n * @param samplePos - Absolute samples from the start of processing (the mixer\n * advances an internal position from 0 on the first {@link processStereo}\n * call; recompiling resets it to 0)\n * @param value - Target parameter value\n * @param curve - Interpolation curve (default: `'linear'`)\n * @throws If the strip index is out of range or the schedule call fails\n * (unknown curve, out-of-range insert index, or full event lane)\n */\n scheduleInsertAutomation(\n stripIndex: number,\n insertIndex: number,\n paramId: number,\n samplePos: number,\n value: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleInsertAutomation(\n stripIndex,\n insertIndex,\n paramId,\n samplePos,\n value,\n automationCurveCode(curve),\n );\n }\n\n /**\n * Resolve a strip's index in `[0, stripCount())` from its scene id, or `null`\n * when no strip with that id exists (matches the Node binding's `number | null`).\n */\n stripById(id: string): number | null {\n const index = this.mixer.stripById(id);\n return index < 0 ? null : index;\n }\n\n /**\n * Add a bus to the mixer topology. `role` is one of `'master'`, `'aux'`, or\n * `'submix'` (defaults to `'aux'`). Marks the routing graph dirty; call\n * {@link compile} (or {@link processStereo}) to rebuild.\n */\n addBus(id: string, role = 'aux'): void {\n this.mixer.addBus(id, role);\n }\n\n /** Remove a bus by id. Marks the routing graph dirty. */\n removeBus(id: string): void {\n this.mixer.removeBus(id);\n }\n\n /** Number of buses in the mixer topology. */\n busCount(): number {\n return this.mixer.busCount();\n }\n\n /**\n * Add a VCA group with the given gain offset (dB). `members` is a list of\n * strip ids governed by the group (may be empty).\n */\n addVcaGroup(id: string, gainDb = 0.0, members: string[] = []): void {\n this.mixer.addVcaGroup(id, gainDb, members);\n }\n\n /** Remove a VCA group by id. */\n removeVcaGroup(id: string): void {\n this.mixer.removeVcaGroup(id);\n }\n\n /** Number of VCA groups in the mixer topology. */\n vcaGroupCount(): number {\n return this.mixer.vcaGroupCount();\n }\n\n /** Set the strip's input trim in dB. */\n setInputTrimDb(stripIndex: number, db: number): void {\n this.mixer.setInputTrimDb(stripIndex, db);\n }\n\n /** Set the strip's fader level in dB. */\n setFaderDb(stripIndex: number, db: number): void {\n this.mixer.setFaderDb(stripIndex, db);\n }\n\n /** Set the strip's pan position. */\n setPan(stripIndex: number, pan: number, panMode: PanMode | number = 0): void {\n this.mixer.setPan(stripIndex, pan, panModeCode(panMode));\n }\n\n /** Set the strip's stereo width. */\n setWidth(stripIndex: number, width: number): void {\n this.mixer.setWidth(stripIndex, width);\n }\n\n /** Set the strip's mute state. */\n setMuted(stripIndex: number, muted: boolean): void {\n this.mixer.setMuted(stripIndex, muted);\n }\n\n /**\n * Set a strip's solo state. Takes effect on the next process without a\n * graph recompile.\n */\n setSoloed(stripIndex: number, soloed: boolean): void {\n this.mixer.setSoloed(stripIndex, soloed);\n }\n\n /**\n * Mark a strip solo-safe so it is never implied-muted by another strip's\n * solo. Takes effect on the next process without a graph recompile.\n */\n setSoloSafe(stripIndex: number, soloSafe: boolean): void {\n this.mixer.setSoloSafe(stripIndex, soloSafe);\n }\n\n /** Invert the polarity of the left and/or right channel of a strip. */\n setPolarityInvert(stripIndex: number, invertLeft: boolean, invertRight: boolean): void {\n this.mixer.setPolarityInvert(stripIndex, invertLeft, invertRight);\n }\n\n /** Set the strip's pan law. */\n setPanLaw(stripIndex: number, panLaw: PanLaw | number): void {\n this.mixer.setPanLaw(stripIndex, panLawCode(panLaw));\n }\n\n /**\n * Set a per-strip channel delay in samples. This changes the strip's reported\n * latency; recompile to re-run latency compensation.\n */\n setChannelDelaySamples(stripIndex: number, delaySamples: number): void {\n this.mixer.setChannelDelaySamples(stripIndex, delaySamples);\n }\n\n /** Set the strip's live VCA gain offset in dB (not persisted to the scene). */\n setVcaOffsetDb(stripIndex: number, offsetDb: number): void {\n this.mixer.setVcaOffsetDb(stripIndex, offsetDb);\n }\n\n /** Set independent left/right pan positions (dual-pan mode). */\n setDualPan(stripIndex: number, leftPan: number, rightPan: number): void {\n this.mixer.setDualPan(stripIndex, leftPan, rightPan);\n }\n\n /**\n * Add a send to a strip after construction.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param id - Send id\n * @param destinationBusId - Destination bus id\n * @param sendDb - Initial send level in dB\n * @param timing - `'preFader'` or `'postFader'` (default: `'postFader'`)\n * @returns The new send's index\n */\n addSend(\n stripIndex: number,\n id: string,\n destinationBusId: string,\n sendDb: number,\n timing: SendTiming | number = 'postFader',\n ): number {\n return this.mixer.addSend(stripIndex, id, destinationBusId, sendDb, sendTimingCode(timing));\n }\n\n /** Set the send level (in dB) for an existing send by index. */\n setSendDb(stripIndex: number, sendIndex: number, sendDb: number): void {\n this.mixer.setSendDb(stripIndex, sendIndex, sendDb);\n }\n\n /**\n * Read a strip's meter snapshot at the given tap point.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param tap - `'preFader'` or `'postFader'` (default: `'postFader'`)\n */\n meterTap(stripIndex: number, tap: MeterTap = 'postFader'): MixMeterSnapshot {\n return this.mixer.meterTap(stripIndex, meterTapCode(tap));\n }\n\n /**\n * Read a strip's meter snapshot. Alias of {@link meterTap}, provided for\n * cross-binding (Node/Python) parity.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param tap - `'preFader'` or `'postFader'` (default: `'postFader'`)\n */\n stripMeter(stripIndex: number, tap: MeterTap = 'postFader'): MixMeterSnapshot {\n return this.mixer.stripMeter(stripIndex, meterTapCode(tap));\n }\n\n /**\n * Schedule sample-accurate fader automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param faderDb - Target fader level in dB\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleFaderAutomation(\n stripIndex: number,\n samplePos: number,\n faderDb: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleFaderAutomation(stripIndex, samplePos, faderDb, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate pan automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param pan - Target pan position\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n schedulePanAutomation(\n stripIndex: number,\n samplePos: number,\n pan: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.schedulePanAutomation(stripIndex, samplePos, pan, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate width automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param width - Target stereo width\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleWidthAutomation(\n stripIndex: number,\n samplePos: number,\n width: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleWidthAutomation(stripIndex, samplePos, width, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate send-level automation on a strip's send.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param sendIndex - Send index in the strip's add order\n * @param samplePos - Absolute samples from the start of processing\n * @param db - Target send level in dB\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleSendAutomation(\n stripIndex: number,\n sendIndex: number,\n samplePos: number,\n db: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleSendAutomation(\n stripIndex,\n sendIndex,\n samplePos,\n db,\n automationCurveCode(curve),\n );\n }\n\n /**\n * Read up to `maxPoints` of a strip's most recent goniometer samples\n * (oldest to newest).\n */\n readGoniometerLatest(stripIndex: number, maxPoints: number): GoniometerPoint[] {\n return this.mixer.readGoniometerLatest(stripIndex, maxPoints);\n }\n\n /** Serialize the current scene (strips, buses, sends, connections) to JSON. */\n toSceneJson(): string {\n return this.mixer.toSceneJson();\n }\n\n /** Release the underlying WASM object. Safe to call only once. */\n delete(): void {\n this.mixer.delete();\n }\n\n /** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */\n destroy(): void {\n this.delete();\n }\n}\n\n/**\n * Trim silence from beginning and end of audio.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz\n * @param thresholdDb - Silence threshold in dB (default: -60 dB)\n * @returns Trimmed audio\n */\nexport function trim(samples: Float32Array, sampleRate: number, thresholdDb = -60.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.trim(samples, sampleRate, thresholdDb);\n}\n\n// ============================================================================\n// Features - Spectrogram\n// ============================================================================\n\n/**\n * Compute Short-Time Fourier Transform (STFT).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns STFT result with magnitude and power spectrograms\n */\nexport function stft(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): StftResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.stft(samples, sampleRate, nFft, hopLength);\n}\n\n/**\n * Compute STFT and return magnitude in decibels.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns STFT result with dB values\n */\nexport function stftDb(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): { nBins: number; nFrames: number; db: Float32Array } {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.stftDb(samples, sampleRate, nFft, hopLength);\n}\n\n// ============================================================================\n// Features - Mel Spectrogram\n// ============================================================================\n\n/**\n * Compute Mel spectrogram.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param nMels - Number of Mel bands (default: 128)\n * @returns Mel spectrogram result\n */\nexport function melSpectrogram(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n nMels = 128,\n): MelSpectrogramResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.melSpectrogram(samples, sampleRate, nFft, hopLength, nMels);\n}\n\n/**\n * Compute MFCC (Mel-Frequency Cepstral Coefficients).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param nMels - Number of Mel bands (default: 128)\n * @param nMfcc - Number of MFCC coefficients (default: 20)\n * @returns MFCC result\n */\nexport function mfcc(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n nMels = 128,\n nMfcc = 20,\n): MfccResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mfcc(samples, sampleRate, nFft, hopLength, nMels, nMfcc);\n}\n\n// ============================================================================\n// Features - Inverse reconstruction\n// ============================================================================\n\n/**\n * Approximate inverse of a Mel filterbank: Mel power spectrogram -> STFT power\n * spectrogram. Mirrors `feature::mel_to_stft`.\n *\n * @param melPower - Mel power spectrogram [nMels x nFrames] row-major\n * @param nMels - Number of Mel bands\n * @param nFrames - Number of time frames\n * @param sampleRate - Sample rate in Hz\n * @param nFft - FFT size (default: 2048)\n * @param fmin - Lower Mel band edge in Hz (default: 0)\n * @param fmax - Upper Mel band edge in Hz (default: sr/2 when 0)\n * @returns STFT power spectrogram result\n */\nexport function melToStft(\n melPower: Float32Array,\n nMels: number,\n nFrames: number,\n sampleRate = 22050,\n nFft = 2048,\n fmin = 0,\n fmax = 0,\n): StftPowerResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.melToStft(melPower, nMels, nFrames, sampleRate, nFft, fmin, fmax);\n}\n\n/**\n * Reconstruct audio from a Mel power spectrogram via Griffin-Lim. Mirrors\n * `feature::mel_to_audio`.\n *\n * @param melPower - Mel power spectrogram [nMels x nFrames] row-major\n * @param nMels - Number of Mel bands\n * @param nFrames - Number of time frames\n * @param sampleRate - Sample rate in Hz\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum Mel frequency in Hz (default: 0)\n * @param fmax - Maximum Mel frequency in Hz (default: 0 = sr/2)\n * @param nIter - Griffin-Lim iterations (default: 32)\n * @returns Reconstructed audio samples (mono, float32)\n */\nexport function melToAudio(\n melPower: Float32Array,\n nMels: number,\n nFrames: number,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n fmin = 0,\n fmax = 0,\n nIter = 32,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.melToAudio(\n melPower,\n nMels,\n nFrames,\n sampleRate,\n nFft,\n hopLength,\n fmin,\n fmax,\n nIter,\n );\n}\n\n/**\n * Invert MFCC coefficients back to a Mel power spectrogram. Mirrors\n * `feature::mfcc_to_mel`.\n *\n * @param mfccCoefficients - MFCC matrix [nMfcc x nFrames] row-major\n * @param nMfcc - Number of MFCC coefficients\n * @param nFrames - Number of time frames\n * @param nMels - Number of Mel bins to reconstruct (default: 128)\n * @returns Mel power spectrogram result\n */\nexport function mfccToMel(\n mfccCoefficients: Float32Array,\n nMfcc: number,\n nFrames: number,\n nMels = 128,\n): MelPowerResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mfccToMel(mfccCoefficients, nMfcc, nFrames, nMels);\n}\n\n/**\n * Reconstruct audio directly from MFCC coefficients via Griffin-Lim. Mirrors\n * `feature::mfcc_to_audio`.\n *\n * @param mfccCoefficients - MFCC matrix [nMfcc x nFrames] row-major\n * @param nMfcc - Number of MFCC coefficients\n * @param nFrames - Number of time frames\n * @param nMels - Number of Mel bins (default: 128)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum Mel frequency in Hz (default: 0)\n * @param fmax - Maximum Mel frequency in Hz (default: 0 = sr/2)\n * @param nIter - Griffin-Lim iterations (default: 32)\n * @returns Reconstructed audio samples (mono, float32)\n */\nexport function mfccToAudio(\n mfccCoefficients: Float32Array,\n nMfcc: number,\n nFrames: number,\n nMels = 128,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n fmin = 0,\n fmax = 0,\n nIter = 32,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.mfccToAudio(\n mfccCoefficients,\n nMfcc,\n nFrames,\n nMels,\n sampleRate,\n nFft,\n hopLength,\n fmin,\n fmax,\n nIter,\n );\n}\n\n// ============================================================================\n// Features - Chroma\n// ============================================================================\n\n/**\n * Compute chromagram (pitch class distribution).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns Chroma features result\n */\nexport function chroma(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): ChromaResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.chroma(samples, sampleRate, nFft, hopLength);\n}\n\n// ============================================================================\n// Features - Spectral\n// ============================================================================\n\n/**\n * Compute spectral centroid (center of mass of spectrum).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns Spectral centroid in Hz for each frame\n */\nexport function spectralCentroid(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.spectralCentroid(samples, sampleRate, nFft, hopLength);\n}\n\n/**\n * Compute spectral contrast (librosa.feature.spectral_contrast).\n *\n * @returns Matrix2d of shape (nBands + 1) x nFrames.\n */\nexport function spectralContrast(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n nBands = 6,\n fmin = 200.0,\n quantile = 0.02,\n): WasmMatrix2dResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.spectralContrast(samples, sampleRate, nFft, hopLength, nBands, fmin, quantile);\n}\n\n/**\n * Fit per-frame polynomial coefficients (librosa.feature.poly_features).\n *\n * @returns Matrix2d of shape (order + 1) x nFrames.\n */\nexport function polyFeatures(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n order = 1,\n): WasmMatrix2dResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.polyFeatures(samples, sampleRate, nFft, hopLength, order);\n}\n\n/**\n * Locate zero-crossing indices of a signal (librosa.zero_crossings).\n */\nexport function zeroCrossings(\n samples: Float32Array,\n threshold = 1e-10,\n refMagnitude = false,\n pad = true,\n zeroPos = true,\n): Int32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.zeroCrossings(samples, threshold, refMagnitude, pad, zeroPos);\n}\n\n/**\n * Estimate the global tuning offset from a set of frequencies\n * (librosa.pitch_tuning). Returns a deviation in fractions of a bin.\n */\nexport function pitchTuning(\n frequencies: Float32Array,\n resolution = 0.01,\n binsPerOctave = 12,\n): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.pitchTuning(frequencies, resolution, binsPerOctave);\n}\n\n/**\n * Estimate the tuning offset of an audio signal (librosa.estimate_tuning).\n */\nexport function estimateTuning(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n resolution = 0.01,\n binsPerOctave = 12,\n): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.estimateTuning(samples, sampleRate, nFft, hopLength, resolution, binsPerOctave);\n}\n\n/**\n * Non-negative matrix factorisation of a flattened [nFeatures x nFrames]\n * spectrogram (librosa.decompose.decompose). Returns the W and H factors.\n */\nexport function decompose(\n s: Float32Array,\n nFeatures: number,\n nFrames: number,\n nComponents: number,\n nIter = 50,\n beta = 2.0,\n): WasmDecomposeResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.decompose(s, nFeatures, nFrames, nComponents, nIter, beta);\n}\n\n/**\n * Nearest-neighbour filtering of a flattened [nFeatures x nFrames] spectrogram\n * (librosa.decompose.nn_filter).\n */\nexport function nnFilter(\n s: Float32Array,\n nFeatures: number,\n nFrames: number,\n aggregate = 'mean',\n k = 7,\n width = 1,\n): WasmMatrix2dResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.nnFilter(s, nFeatures, nFrames, aggregate, k, width);\n}\n\n/**\n * Reorder/concatenate a signal by interval slices (librosa.effects.remix).\n *\n * @param intervals - Flat (start, end) sample pairs (even length).\n */\nexport function remix(\n samples: Float32Array,\n intervals: Int32Array | ArrayLike<number>,\n sampleRate = 22050,\n alignZeros = false,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n // Sample indices must reach the native side as exact 32-bit integers. Passing\n // a Float32Array (or a number[] holding fractional/large values) would round\n // boundaries above 2^24 and misalign the slice. Coerce to an Int32Array,\n // truncating toward zero, so callers can hand us any numeric array safely.\n const intervalsI32 =\n intervals instanceof Int32Array ? intervals : Int32Array.from(intervals, (v) => Math.trunc(v));\n return module.remix(samples, intervalsI32, sampleRate, alignZeros);\n}\n\n/**\n * Phase-vocoder time-scale modification (rate > 1 faster, < 1 slower).\n */\nexport function phaseVocoder(\n samples: Float32Array,\n rate: number,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.phaseVocoder(samples, sampleRate, rate, nFft, hopLength);\n}\n\n/**\n * HPSS into harmonic / percussive / residual signals.\n */\nexport function hpssWithResidual(\n samples: Float32Array,\n sampleRate = 22050,\n kernelHarmonic = 31,\n kernelPercussive = 31,\n): WasmHpssWithResidualResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hpssWithResidual(samples, sampleRate, kernelHarmonic, kernelPercussive);\n}\n\n/**\n * Channel-weighted multichannel integrated loudness + LRA (ITU-R BS.1770 /\n * EBU R128) from an interleaved buffer of `frames * channels` samples. The\n * per-channel frame count is derived from the buffer length and `channels`.\n */\nexport function lufsInterleaved(\n samples: Float32Array,\n channels: number,\n sampleRate = 22050,\n): WasmLufsResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.lufsInterleaved(samples, channels, sampleRate);\n}\n\n/**\n * Standards-compliant EBU R128 loudness range (LRA) in LU.\n */\nexport function ebur128LoudnessRange(samples: Float32Array, sampleRate = 22050): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.ebur128LoudnessRange(samples, sampleRate);\n}\n\n/**\n * Compute spectral bandwidth.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns Spectral bandwidth in Hz for each frame\n */\nexport function spectralBandwidth(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.spectralBandwidth(samples, sampleRate, nFft, hopLength);\n}\n\n/**\n * Compute spectral rolloff frequency.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param rollPercent - Percentage threshold (default: 0.85)\n * @returns Rolloff frequency in Hz for each frame\n */\nexport function spectralRolloff(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n rollPercent = 0.85,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.spectralRolloff(samples, sampleRate, nFft, hopLength, rollPercent);\n}\n\n/**\n * Compute spectral flatness.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns Spectral flatness for each frame (0 = tonal, 1 = noise-like)\n */\nexport function spectralFlatness(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.spectralFlatness(samples, sampleRate, nFft, hopLength);\n}\n\n/**\n * Compute zero crossing rate.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param frameLength - Frame length (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns Zero crossing rate for each frame\n */\nexport function zeroCrossingRate(\n samples: Float32Array,\n sampleRate = 22050,\n frameLength = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.zeroCrossingRate(samples, sampleRate, frameLength, hopLength);\n}\n\n/**\n * Compute RMS energy.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param frameLength - Frame length (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @returns RMS energy for each frame\n */\nexport function rmsEnergy(\n samples: Float32Array,\n sampleRate = 22050,\n frameLength = 2048,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.rmsEnergy(samples, sampleRate, frameLength, hopLength);\n}\n\n// ============================================================================\n// Features - Pitch\n// ============================================================================\n\n/**\n * Detect pitch using YIN algorithm.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param frameLength - Frame length (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum frequency in Hz (default: 65)\n * @param fmax - Maximum frequency in Hz (default: 2093)\n * @param threshold - YIN threshold (default: 0.3)\n * @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)\n * @returns Pitch detection result\n */\nexport function pitchYin(\n samples: Float32Array,\n sampleRate = 22050,\n frameLength = 2048,\n hopLength = 512,\n fmin = 65.0,\n fmax = 2093.0,\n threshold = 0.3,\n fillNa = false,\n): PitchResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.pitchYin(\n samples,\n sampleRate,\n frameLength,\n hopLength,\n fmin,\n fmax,\n threshold,\n fillNa,\n );\n}\n\n/**\n * Detect pitch using pYIN algorithm (probabilistic YIN with HMM smoothing).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param frameLength - Frame length (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum frequency in Hz (default: 65)\n * @param fmax - Maximum frequency in Hz (default: 2093)\n * @param threshold - YIN threshold (default: 0.3)\n * @param fillNa - If true, return 0 for unvoiced f0 frames; otherwise keep NaN (default: false)\n * @returns Pitch detection result\n */\nexport function pitchPyin(\n samples: Float32Array,\n sampleRate = 22050,\n frameLength = 2048,\n hopLength = 512,\n fmin = 65.0,\n fmax = 2093.0,\n threshold = 0.3,\n fillNa = false,\n): PitchResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.pitchPyin(\n samples,\n sampleRate,\n frameLength,\n hopLength,\n fmin,\n fmax,\n threshold,\n fillNa,\n );\n}\n\n// ============================================================================\n// Core - Unit Conversion\n// ============================================================================\n\n/**\n * Convert frequency in Hz to Mel scale.\n *\n * @param hz - Frequency in Hz\n * @returns Mel frequency\n */\nexport function hzToMel(hz: number): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hzToMel(hz);\n}\n\n/**\n * Convert Mel scale to frequency in Hz.\n *\n * @param mel - Mel frequency\n * @returns Frequency in Hz\n */\nexport function melToHz(mel: number): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.melToHz(mel);\n}\n\n/**\n * Convert frequency in Hz to MIDI note number.\n *\n * @param hz - Frequency in Hz\n * @returns MIDI note number (A4 = 440 Hz = 69)\n */\nexport function hzToMidi(hz: number): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hzToMidi(hz);\n}\n\n/**\n * Convert MIDI note number to frequency in Hz.\n *\n * @param midi - MIDI note number\n * @returns Frequency in Hz\n */\nexport function midiToHz(midi: number): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.midiToHz(midi);\n}\n\n/**\n * Convert frequency in Hz to note name.\n *\n * @param hz - Frequency in Hz\n * @returns Note name (e.g., \"A4\", \"C#5\")\n */\nexport function hzToNote(hz: number): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.hzToNote(hz);\n}\n\n/**\n * Convert note name to frequency in Hz.\n *\n * @param note - Note name (e.g., \"A4\", \"C#5\")\n * @returns Frequency in Hz\n */\nexport function noteToHz(note: string): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.noteToHz(note);\n}\n\n/**\n * Convert frame index to time in seconds.\n *\n * @param frames - Frame index\n * @param sr - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length in samples (default: 512)\n * @returns Time in seconds\n */\nexport function framesToTime(frames: number, sr = 22050, hopLength = 512): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.framesToTime(frames, sr, hopLength);\n}\n\n/**\n * Convert time in seconds to frame index.\n *\n * @param time - Time in seconds\n * @param sr - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length in samples (default: 512)\n * @returns Frame index\n */\nexport function timeToFrames(time: number, sr = 22050, hopLength = 512): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.timeToFrames(time, sr, hopLength);\n}\n\nexport function framesToSamples(frames: number, hopLength = 512, nFft = 0): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.framesToSamples(frames, hopLength, nFft);\n}\n\nexport function samplesToFrames(samples: number, hopLength = 512, nFft = 0): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.samplesToFrames(samples, hopLength, nFft);\n}\n\nexport function powerToDb(\n values: Float32Array,\n ref = 1.0,\n amin = 1e-10,\n topDb = 80.0,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.powerToDb(values, ref, amin, topDb);\n}\n\nexport function amplitudeToDb(\n values: Float32Array,\n ref = 1.0,\n amin = 1e-5,\n topDb = 80.0,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.amplitudeToDb(values, ref, amin, topDb);\n}\n\nexport function dbToPower(values: Float32Array, ref = 1.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.dbToPower(values, ref);\n}\n\nexport function dbToAmplitude(values: Float32Array, ref = 1.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.dbToAmplitude(values, ref);\n}\n\nexport function preemphasis(samples: Float32Array, coef = 0.97, zi?: number): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.preemphasis(samples, coef, zi ?? null);\n}\n\nexport function deemphasis(samples: Float32Array, coef = 0.97, zi?: number): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.deemphasis(samples, coef, zi ?? null);\n}\n\nexport function trimSilence(\n samples: Float32Array,\n topDb = 60.0,\n frameLength = 2048,\n hopLength = 512,\n): WasmTrimResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.trimSilence(samples, topDb, frameLength, hopLength);\n}\n\nexport function splitSilence(\n samples: Float32Array,\n topDb = 60.0,\n frameLength = 2048,\n hopLength = 512,\n): Int32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.splitSilence(samples, topDb, frameLength, hopLength);\n}\n\nexport function frameSignal(\n samples: Float32Array,\n frameLength: number,\n hopLength: number,\n): WasmFrameResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.frameSignal(samples, frameLength, hopLength);\n}\n\nexport function padCenter(values: Float32Array, targetSize: number, padValue = 0.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.padCenter(values, targetSize, padValue);\n}\n\nexport function fixLength(values: Float32Array, targetSize: number, padValue = 0.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.fixLength(values, targetSize, padValue);\n}\n\nexport function fixFrames(frames: Int32Array, xMin = 0, xMax = -1, pad = true): Int32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.fixFrames(frames, xMin, xMax, pad);\n}\n\nexport function peakPick(\n values: Float32Array,\n preMax: number,\n postMax: number,\n preAvg: number,\n postAvg: number,\n delta: number,\n wait: number,\n): Int32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.peakPick(values, preMax, postMax, preAvg, postAvg, delta, wait);\n}\n\nexport function vectorNormalize(values: Float32Array, normType = 0, threshold = 0.0): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.vectorNormalize(values, normType, threshold);\n}\n\nexport function pcen(\n values: Float32Array,\n nBins: number,\n nFrames: number,\n options: Record<string, number> = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.pcen(values, nBins, nFrames, options);\n}\n\nexport function tonnetz(chromagram: Float32Array, nChroma: number, nFrames: number): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.tonnetz(chromagram, nChroma, nFrames);\n}\n\nexport function tempogram(\n onsetEnvelope: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n winLength = 384,\n mode: TempogramMode = 'autocorrelation',\n): WasmTempogramResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.tempogram(onsetEnvelope, sampleRate, hopLength, winLength, mode);\n}\n\nexport function cyclicTempogram(\n onsetEnvelope: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n winLength = 384,\n bpmMin = 60.0,\n nBins = 60,\n): WasmCyclicTempogramResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.cyclicTempogram(onsetEnvelope, sampleRate, hopLength, winLength, bpmMin, nBins);\n}\n\nexport function plp(\n onsetEnvelope: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n tempoMin = 30.0,\n tempoMax = 300.0,\n winLength = 384,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.plp(onsetEnvelope, sampleRate, hopLength, tempoMin, tempoMax, winLength);\n}\n\n/**\n * Compute NNLS (non-negative least squares) chromagram.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns NNLS chroma result\n */\nexport function nnlsChroma(samples: Float32Array, sampleRate = 22050): WasmNnlsChromaResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.nnlsChroma(samples, sampleRate);\n}\n\n/**\n * Compute the Constant-Q Transform magnitude.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum frequency in Hz (default: 32.70319566257483, C1)\n * @param nBins - Number of frequency bins (default: 84)\n * @param binsPerOctave - Bins per octave (default: 12)\n * @returns CQT magnitude result\n */\nexport function cqt(\n samples: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n fmin = 32.70319566257483,\n nBins = 84,\n binsPerOctave = 12,\n): CqtResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.cqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave);\n}\n\n/**\n * Compute the Variable-Q Transform magnitude (gamma controls Q).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length (default: 512)\n * @param fmin - Minimum frequency in Hz (default: 32.70319566257483, C1)\n * @param nBins - Number of frequency bins (default: 84)\n * @param binsPerOctave - Bins per octave (default: 12)\n * @param gamma - Bandwidth offset; 0 is equivalent to CQT (default: 0)\n * @returns VQT magnitude result (same shape as CQT)\n */\nexport function vqt(\n samples: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n fmin = 32.70319566257483,\n nBins = 84,\n binsPerOctave = 12,\n gamma = 0,\n): CqtResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);\n}\n\n/**\n * Detect song-structure sections (intro/verse/chorus/...).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param minSectionSec - Minimum section duration in seconds (default: 4.0)\n * @returns Array of detected sections\n */\nexport function analyzeSections(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n minSectionSec = 4.0,\n): Section[] {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module\n .analyzeSections(samples, sampleRate, nFft, hopLength, minSectionSec)\n .map((s) => ({ ...s, type: s.type as SectionType }));\n}\n\n/**\n * Extract the melody contour from monophonic audio via YIN.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param fmin - Minimum frequency in Hz (default: 65.0)\n * @param fmax - Maximum frequency in Hz (default: 2093.0)\n * @param frameLength - Frame length in samples (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param threshold - YIN threshold; lower is stricter (default: 0.1)\n * @returns Melody contour with per-frame pitch points and summary stats\n */\nexport function analyzeMelody(\n samples: Float32Array,\n sampleRate = 22050,\n fmin = 65.0,\n fmax = 2093.0,\n frameLength = 2048,\n hopLength = 256,\n threshold = 0.1,\n): MelodyResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.analyzeMelody(samples, sampleRate, fmin, fmax, frameLength, hopLength, threshold);\n}\n\n/**\n * Compute the onset strength envelope.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param nFft - FFT size (default: 2048)\n * @param hopLength - Hop length (default: 512)\n * @param nMels - Number of Mel bands (default: 128)\n * @returns Onset envelope for each frame\n */\nexport function onsetEnvelope(\n samples: Float32Array,\n sampleRate = 22050,\n nFft = 2048,\n hopLength = 512,\n nMels = 128,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.onsetEnvelope(samples, sampleRate, nFft, hopLength, nMels);\n}\n\n/**\n * Compute the Fourier tempogram from an onset envelope.\n *\n * @param onsetEnvelope - Onset strength envelope (float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length (default: 512)\n * @param winLength - Window length in frames (default: 384)\n * @returns Fourier tempogram result\n */\nexport function fourierTempogram(\n onsetEnvelope: Float32Array,\n sampleRate = 22050,\n hopLength = 512,\n winLength = 384,\n): WasmFourierTempogramResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.fourierTempogram(onsetEnvelope, sampleRate, hopLength, winLength);\n}\n\n/**\n * Compute tempogram ratio features.\n *\n * @param tempogramData - Tempogram data (float32)\n * @param winLength - Window length in frames (default: 384)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @param hopLength - Hop length (default: 512)\n * @returns Tempogram ratio features\n */\nexport function tempogramRatio(\n tempogramData: Float32Array,\n winLength = 384,\n sampleRate = 22050,\n hopLength = 512,\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.tempogramRatio(tempogramData, winLength, sampleRate, hopLength);\n}\n\n/**\n * Measure loudness (EBU R128 / ITU-R BS.1770).\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Loudness measurement result\n */\nexport function lufs(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): LufsResult {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('lufs', samples, options.validate !== false);\n return module.lufs(samples, sampleRate);\n}\n\n/**\n * Compute the momentary loudness (LUFS) over time.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Momentary LUFS values over time\n */\nexport function momentaryLufs(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('momentaryLufs', samples, options.validate !== false);\n return module.momentaryLufs(samples, sampleRate);\n}\n\n/**\n * Compute the short-term loudness (LUFS) over time.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleRate - Sample rate in Hz (default: 22050)\n * @returns Short-term LUFS values over time\n */\nexport function shortTermLufs(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n assertSamples('shortTermLufs', samples, options.validate !== false);\n return module.shortTermLufs(samples, sampleRate);\n}\n\n// ============================================================================\n// Metering — basic / true-peak / clipping / dynamic range\n// ============================================================================\n\n/** One contiguous run of clipped samples reported by `meteringDetectClipping`. */\nexport interface ClippingRegion {\n startSample: number;\n endSample: number;\n length: number;\n peak: number;\n}\n\n/** Aggregated clipping report. */\nexport interface ClippingReport {\n clippedSamples: number;\n clippingRatio: number;\n maxClippedPeak: number;\n regions: ClippingRegion[];\n}\n\n/** Sliding-window dynamic range report. */\nexport interface DynamicRangeReport {\n dynamicRangeDb: number;\n lowPercentileDb: number;\n highPercentileDb: number;\n windowRmsDb: Float32Array;\n}\n\nfunction requireModule() {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module;\n}\n\nexport function meteringPeakDb(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n assertSamples('meteringPeakDb', samples, options.validate !== false);\n return requireModule().meteringPeakDb(samples, sampleRate);\n}\n\nexport function meteringRmsDb(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n assertSamples('meteringRmsDb', samples, options.validate !== false);\n return requireModule().meteringRmsDb(samples, sampleRate);\n}\n\nexport function meteringCrestFactorDb(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n assertSamples('meteringCrestFactorDb', samples, options.validate !== false);\n return requireModule().meteringCrestFactorDb(samples, sampleRate);\n}\n\nexport function meteringDcOffset(\n samples: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n assertSamples('meteringDcOffset', samples, options.validate !== false);\n return requireModule().meteringDcOffset(samples, sampleRate);\n}\n\n/**\n * Inter-sample (true) peak in dBFS. `oversampleFactor` must be a power of two\n * in [1, 16]; pass 0 to use the library default (4).\n */\nexport function meteringTruePeakDb(\n samples: Float32Array,\n sampleRate = 22050,\n oversampleFactor = 4,\n options: ValidateOptions = {},\n): number {\n assertSamples('meteringTruePeakDb', samples, options.validate !== false);\n return requireModule().meteringTruePeakDb(samples, sampleRate, oversampleFactor);\n}\n\n/**\n * Detect contiguous runs of clipped samples.\n *\n * @param threshold Linear absolute threshold (default 0.999).\n * @param minRegionSamples Minimum run length to report (default 1).\n */\nexport function meteringDetectClipping(\n samples: Float32Array,\n sampleRate = 22050,\n threshold = 0.999,\n minRegionSamples = 1,\n options: ValidateOptions = {},\n): ClippingReport {\n assertSamples('meteringDetectClipping', samples, options.validate !== false);\n return requireModule().meteringDetectClipping(samples, sampleRate, threshold, minRegionSamples);\n}\n\n/**\n * Sliding-window dynamic range. Pass 0 for any parameter to use the library\n * default (window=3 s, hop=1 s, low=0.10, high=0.95).\n */\nexport function meteringDynamicRange(\n samples: Float32Array,\n sampleRate = 22050,\n windowSec = 0,\n hopSec = 0,\n lowPercentile = 0,\n highPercentile = 0,\n options: ValidateOptions = {},\n): DynamicRangeReport {\n assertSamples('meteringDynamicRange', samples, options.validate !== false);\n return requireModule().meteringDynamicRange(\n samples,\n sampleRate,\n windowSec,\n hopSec,\n lowPercentile,\n highPercentile,\n );\n}\n\n// ============================================================================\n// Metering — stereo / phase-scope / spectrum\n// ============================================================================\n\n/** Mid/side vectorscope point series for a (left, right) stereo pair. */\nexport interface VectorscopeReport {\n mid: Float32Array;\n side: Float32Array;\n}\n\n/** Phase-scope (Lissajous) point series plus summary stats. */\nexport interface PhaseScopeReport {\n mid: Float32Array;\n side: Float32Array;\n radius: Float32Array;\n angleRad: Float32Array;\n correlation: number;\n averageAbsAngleRad: number;\n maxRadius: number;\n}\n\n/** Options for `meteringSpectrum`. */\nexport interface SpectrumOptions {\n /** FFT size. Pass 0 / omit for the library default (2048). */\n nFft?: number;\n /** Apply fractional-octave smoothing to magnitude. */\n applyOctaveSmoothing?: boolean;\n /** Smoothing fraction (e.g. 3 = 1/3-octave). 0 / omit = library default (3). */\n octaveFraction?: number;\n /** Linear reference for the dB conversion. 0 / omit = 1.0. */\n dbRef?: number;\n /** Linear floor used to avoid log(0). 0 / omit = library default. */\n dbAmin?: number;\n}\n\n/** Single-frame magnitude / power / dB spectrum returned by `meteringSpectrum`. */\nexport interface SpectrumReport {\n frequencies: Float32Array;\n magnitude: Float32Array;\n power: Float32Array;\n db: Float32Array;\n nFft: number;\n sampleRate: number;\n}\n\n/** Pearson correlation in [-1, 1] between two equal-length channels. */\nexport function meteringStereoCorrelation(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n const validate = options.validate !== false;\n assertSamples('meteringStereoCorrelation', left, validate, 'left');\n assertSamples('meteringStereoCorrelation', right, validate, 'right');\n return requireModule().meteringStereoCorrelation(left, right, sampleRate);\n}\n\n/** Side / mid energy ratio: 0 = pure mono, ~1 = wide stereo. */\nexport function meteringStereoWidth(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): number {\n const validate = options.validate !== false;\n assertSamples('meteringStereoWidth', left, validate, 'left');\n assertSamples('meteringStereoWidth', right, validate, 'right');\n return requireModule().meteringStereoWidth(left, right, sampleRate);\n}\n\n/** Per-sample mid/side point series (one entry per input frame). */\nexport function meteringVectorscope(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): VectorscopeReport {\n const validate = options.validate !== false;\n assertSamples('meteringVectorscope', left, validate, 'left');\n assertSamples('meteringVectorscope', right, validate, 'right');\n return requireModule().meteringVectorscope(left, right, sampleRate);\n}\n\n/** Phase-scope point series plus summary stats. */\nexport function meteringPhaseScope(\n left: Float32Array,\n right: Float32Array,\n sampleRate = 22050,\n options: ValidateOptions = {},\n): PhaseScopeReport {\n const validate = options.validate !== false;\n assertSamples('meteringPhaseScope', left, validate, 'left');\n assertSamples('meteringPhaseScope', right, validate, 'right');\n return requireModule().meteringPhaseScope(left, right, sampleRate);\n}\n\n/** Single-frame spectrum view (uses the first `nFft` samples of `samples`). */\nexport function meteringSpectrum(\n samples: Float32Array,\n sampleRate = 22050,\n options?: SpectrumOptions & ValidateOptions,\n): SpectrumReport {\n const validate = options?.validate !== false;\n assertSamples('meteringSpectrum', samples, validate);\n return requireModule().meteringSpectrum(samples, sampleRate, options ?? {});\n}\n\n// ============================================================================\n// Editing — 12-TET scale quantizer\n// ============================================================================\n\n/**\n * Snap a MIDI value to the nearest pitch class enabled by `modeMask`.\n *\n * `modeMask` is a 12-bit mask. For natural C major use `0b101010110101`.\n * `referenceMidi` defaults to A4 (69) when passed as 0.\n */\nexport function scaleQuantizeMidi(\n root: number,\n modeMask: number,\n midi: number,\n referenceMidi = 0,\n): number {\n assertFiniteScalar('scaleQuantizeMidi', midi, 'midi');\n assertFiniteScalar('scaleQuantizeMidi', referenceMidi, 'referenceMidi');\n return requireModule().scaleQuantizeMidi(root, modeMask, midi, referenceMidi);\n}\n\nexport function scaleCorrectionSemitones(\n root: number,\n modeMask: number,\n midi: number,\n referenceMidi = 0,\n): number {\n assertFiniteScalar('scaleCorrectionSemitones', midi, 'midi');\n assertFiniteScalar('scaleCorrectionSemitones', referenceMidi, 'referenceMidi');\n return requireModule().scaleCorrectionSemitones(root, modeMask, midi, referenceMidi);\n}\n\nexport function scalePitchClassEnabled(\n root: number,\n modeMask: number,\n pitchClass: number,\n): boolean {\n return requireModule().scalePitchClassEnabled(root, modeMask, pitchClass);\n}\n\n// ============================================================================\n// Core - Resample\n// ============================================================================\n\n/**\n * Resample audio to a different sample rate.\n *\n * @param samples - Audio samples (mono, float32)\n * @param srcSr - Source sample rate in Hz\n * @param targetSr - Target sample rate in Hz\n * @returns Resampled audio\n */\nexport function resample(samples: Float32Array, srcSr: number, targetSr: number): Float32Array {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.resample(samples, srcSr, targetSr);\n}\n\n// ============================================================================\n// Audio Class\n// ============================================================================\n\n/**\n * Wrapper around audio data that exposes all analysis and feature functions as instance methods.\n *\n * @example\n * ```typescript\n * import { init, Audio } from '@libraz/libsonare';\n *\n * await init();\n *\n * const audio = Audio.fromBuffer(samples, 44100);\n * console.log('BPM:', audio.detectBpm());\n * console.log('Key:', audio.detectKey().name);\n *\n * const mel = audio.melSpectrogram();\n * ```\n */\nexport class Audio {\n private _samples: Float32Array;\n private _sampleRate: number;\n\n private constructor(samples: Float32Array, sampleRate: number) {\n this._samples = samples;\n this._sampleRate = sampleRate;\n }\n\n /** Create an Audio instance from raw sample data. */\n static fromBuffer(samples: Float32Array, sampleRate: number): Audio {\n return new Audio(samples, sampleRate);\n }\n\n /** The raw audio samples. */\n get data(): Float32Array {\n return this._samples;\n }\n\n /** Number of samples. */\n get length(): number {\n return this._samples.length;\n }\n\n /** Sample rate in Hz. */\n get sampleRate(): number {\n return this._sampleRate;\n }\n\n /** Duration in seconds. */\n get duration(): number {\n return this._samples.length / this._sampleRate;\n }\n\n // -- Analysis --\n\n detectBpm(): number {\n return detectBpm(this._samples, this._sampleRate);\n }\n\n detectKey(options: KeyDetectionOptions = {}): Key {\n return detectKey(this._samples, this._sampleRate, options);\n }\n\n detectKeyCandidates(options: KeyDetectionOptions = {}): KeyCandidate[] {\n return detectKeyCandidates(this._samples, this._sampleRate, options);\n }\n\n detectOnsets(): Float32Array {\n return detectOnsets(this._samples, this._sampleRate);\n }\n\n detectBeats(): Float32Array {\n return detectBeats(this._samples, this._sampleRate);\n }\n\n detectDownbeats(): Float32Array {\n return detectDownbeats(this._samples, this._sampleRate);\n }\n\n detectChords(options: ChordDetectionOptions = {}): ChordAnalysisResult {\n return detectChords(this._samples, this._sampleRate, options);\n }\n\n analyze(): AnalysisResult {\n return analyze(this._samples, this._sampleRate);\n }\n\n analyzeWithProgress(onProgress: ProgressCallback): AnalysisResult {\n return analyzeWithProgress(this._samples, this._sampleRate, onProgress);\n }\n\n // -- Effects --\n\n hpss(kernelHarmonic = 31, kernelPercussive = 31): HpssResult {\n return hpss(this._samples, this._sampleRate, kernelHarmonic, kernelPercussive);\n }\n\n harmonic(): Float32Array {\n return harmonic(this._samples, this._sampleRate);\n }\n\n percussive(): Float32Array {\n return percussive(this._samples, this._sampleRate);\n }\n\n timeStretch(rate: number): Float32Array {\n return timeStretch(this._samples, this._sampleRate, rate);\n }\n\n pitchShift(semitones: number): Float32Array {\n return pitchShift(this._samples, this._sampleRate, semitones);\n }\n\n pitchCorrectToMidi(currentMidi = 69.0, targetMidi = 69.0): Float32Array {\n return pitchCorrectToMidi(this._samples, this._sampleRate, currentMidi, targetMidi);\n }\n\n noteStretch(onsetSample = 0, offsetSample = 0, stretchRatio = 1.0): Float32Array {\n return noteStretch(this._samples, this._sampleRate, onsetSample, offsetSample, stretchRatio);\n }\n\n voiceChange(pitchSemitones = 0.0, formantFactor = 1.0): Float32Array {\n return voiceChange(this._samples, this._sampleRate, pitchSemitones, formantFactor);\n }\n\n normalize(targetDb = 0.0): Float32Array {\n return normalize(this._samples, this._sampleRate, targetDb);\n }\n\n mastering(targetLufs = -14.0, ceilingDb = -1.0, truePeakOversample = 4): MasteringResult {\n return mastering(this._samples, this._sampleRate, targetLufs, ceilingDb, truePeakOversample);\n }\n\n masteringChain(config: MasteringChainConfig): MasteringChainResult {\n return masteringChain(this._samples, this._sampleRate, config);\n }\n\n masterAudio(\n presetName: MasteringPreset,\n overrides: Record<string, number | boolean> | null = null,\n ): MasteringChainResult {\n return masterAudio(this._samples, this._sampleRate, presetName, overrides);\n }\n\n masteringProcess(\n processorName: SoloProcessor,\n params: MasteringProcessorParams = {},\n ): MasteringResult {\n return masteringProcess(processorName, this._samples, this._sampleRate, params);\n }\n\n trim(thresholdDb = -60.0): Float32Array {\n return trim(this._samples, this._sampleRate, thresholdDb);\n }\n\n // -- Features --\n\n stft(nFft = 2048, hopLength = 512): StftResult {\n return stft(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n stftDb(nFft = 2048, hopLength = 512): { nBins: number; nFrames: number; db: Float32Array } {\n return stftDb(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n melSpectrogram(nFft = 2048, hopLength = 512, nMels = 128): MelSpectrogramResult {\n return melSpectrogram(this._samples, this._sampleRate, nFft, hopLength, nMels);\n }\n\n mfcc(nFft = 2048, hopLength = 512, nMels = 128, nMfcc = 20): MfccResult {\n return mfcc(this._samples, this._sampleRate, nFft, hopLength, nMels, nMfcc);\n }\n\n chroma(nFft = 2048, hopLength = 512): ChromaResult {\n return chroma(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n nnlsChroma(): WasmNnlsChromaResult {\n return nnlsChroma(this._samples, this._sampleRate);\n }\n\n onsetEnvelope(nFft = 2048, hopLength = 512, nMels = 128): Float32Array {\n return onsetEnvelope(this._samples, this._sampleRate, nFft, hopLength, nMels);\n }\n\n lufs(): LufsResult {\n return lufs(this._samples, this._sampleRate);\n }\n\n momentaryLufs(): Float32Array {\n return momentaryLufs(this._samples, this._sampleRate);\n }\n\n shortTermLufs(): Float32Array {\n return shortTermLufs(this._samples, this._sampleRate);\n }\n\n spectralCentroid(nFft = 2048, hopLength = 512): Float32Array {\n return spectralCentroid(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n spectralBandwidth(nFft = 2048, hopLength = 512): Float32Array {\n return spectralBandwidth(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n spectralRolloff(nFft = 2048, hopLength = 512, rollPercent = 0.85): Float32Array {\n return spectralRolloff(this._samples, this._sampleRate, nFft, hopLength, rollPercent);\n }\n\n spectralFlatness(nFft = 2048, hopLength = 512): Float32Array {\n return spectralFlatness(this._samples, this._sampleRate, nFft, hopLength);\n }\n\n zeroCrossingRate(frameLength = 2048, hopLength = 512): Float32Array {\n return zeroCrossingRate(this._samples, this._sampleRate, frameLength, hopLength);\n }\n\n rmsEnergy(frameLength = 2048, hopLength = 512): Float32Array {\n return rmsEnergy(this._samples, this._sampleRate, frameLength, hopLength);\n }\n\n pitchYin(\n frameLength = 2048,\n hopLength = 512,\n fmin = 65.0,\n fmax = 2093.0,\n threshold = 0.3,\n fillNa = false,\n ): PitchResult {\n return pitchYin(\n this._samples,\n this._sampleRate,\n frameLength,\n hopLength,\n fmin,\n fmax,\n threshold,\n fillNa,\n );\n }\n\n pitchPyin(\n frameLength = 2048,\n hopLength = 512,\n fmin = 65.0,\n fmax = 2093.0,\n threshold = 0.3,\n fillNa = false,\n ): PitchResult {\n return pitchPyin(\n this._samples,\n this._sampleRate,\n frameLength,\n hopLength,\n fmin,\n fmax,\n threshold,\n fillNa,\n );\n }\n\n resample(targetSr: number): Float32Array {\n return resample(this._samples, this._sampleRate, targetSr);\n }\n}\n\n// ============================================================================\n// StreamAnalyzer Class\n// ============================================================================\n\n/**\n * Real-time streaming audio analyzer.\n *\n * @example\n * ```typescript\n * import { init, StreamAnalyzer } from '@libraz/libsonare';\n *\n * await init();\n *\n * const analyzer = new StreamAnalyzer({ sampleRate: 44100 });\n *\n * // In audio processing callback\n * analyzer.process(samples);\n *\n * // Get current analysis state\n * const stats = analyzer.stats();\n * console.log('BPM:', stats.estimate.bpm);\n * console.log('Key:', stats.estimate.key);\n * console.log('Chord progression:', stats.estimate.chordProgression);\n * ```\n */\nexport class StreamAnalyzer {\n private analyzer: WasmStreamAnalyzer;\n\n /**\n * Create a new StreamAnalyzer.\n *\n * @param config - Configuration options\n */\n constructor(config: StreamConfig) {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n this.analyzer = new module.StreamAnalyzer(\n config.sampleRate ?? 44100,\n config.nFft ?? 2048,\n config.hopLength ?? 512,\n config.nMels ?? 128,\n config.fmin ?? 0,\n config.fmax ?? 0,\n config.tuningRefHz ?? 440,\n config.computeMagnitude ?? true,\n config.computeMel ?? true,\n config.computeChroma ?? true,\n config.computeOnset ?? true,\n config.computeSpectral ?? true,\n config.emitEveryNFrames ?? 1,\n config.magnitudeDownsample ?? 1,\n config.keyUpdateIntervalSec ?? 5,\n config.bpmUpdateIntervalSec ?? 10,\n config.window ?? 0,\n config.outputFormat ?? 0,\n );\n }\n\n /**\n * Process audio samples.\n *\n * @param samples - Audio samples (mono, float32)\n */\n process(samples: Float32Array): void {\n this.analyzer.process(samples);\n }\n\n /**\n * Process audio samples with explicit sample offset.\n *\n * @param samples - Audio samples (mono, float32)\n * @param sampleOffset - Cumulative sample count at start of this chunk\n */\n processWithOffset(samples: Float32Array, sampleOffset: number): void {\n this.analyzer.processWithOffset(samples, sampleOffset);\n }\n\n /**\n * Get the number of frames available to read.\n */\n availableFrames(): number {\n return this.analyzer.availableFrames();\n }\n\n /**\n * Read processed frames as Structure of Arrays.\n *\n * @param maxFrames - Maximum number of frames to read\n * @returns Frame buffer with analysis results\n */\n readFrames(maxFrames: number): FrameBuffer {\n return this.analyzer.readFramesSoa(maxFrames);\n }\n\n readFramesU8(maxFrames: number): StreamFramesU8 {\n return this.analyzer.readFramesU8(maxFrames) as StreamFramesU8;\n }\n\n readFramesI16(maxFrames: number): StreamFramesI16 {\n return this.analyzer.readFramesI16(maxFrames) as StreamFramesI16;\n }\n\n /**\n * Reset the analyzer state.\n *\n * @param baseSampleOffset - Starting sample offset (default 0)\n */\n reset(baseSampleOffset = 0): void {\n this.analyzer.reset(baseSampleOffset);\n }\n\n /**\n * Get current statistics and progressive estimates.\n *\n * @returns Analyzer statistics including BPM, key, and chord progression\n */\n stats(): AnalyzerStats {\n const s = this.analyzer.stats();\n return {\n totalFrames: s.totalFrames,\n totalSamples: s.totalSamples,\n durationSeconds: s.durationSeconds,\n estimate: {\n bpm: s.estimate.bpm,\n bpmConfidence: s.estimate.bpmConfidence,\n bpmCandidateCount: s.estimate.bpmCandidateCount,\n key: s.estimate.key as PitchClass,\n keyMinor: s.estimate.keyMinor,\n keyConfidence: s.estimate.keyConfidence,\n chordRoot: s.estimate.chordRoot as PitchClass,\n chordQuality: s.estimate.chordQuality as ChordQuality,\n chordConfidence: s.estimate.chordConfidence,\n chordStartTime: s.estimate.chordStartTime,\n chordProgression: s.estimate.chordProgression.map((c) => ({\n root: c.root as PitchClass,\n quality: c.quality as ChordQuality,\n startTime: c.startTime,\n confidence: c.confidence,\n })),\n barChordProgression: s.estimate.barChordProgression.map((c) => ({\n barIndex: c.barIndex,\n root: c.root as PitchClass,\n quality: c.quality as ChordQuality,\n startTime: c.startTime,\n confidence: c.confidence,\n })),\n currentBar: s.estimate.currentBar,\n barDuration: s.estimate.barDuration,\n votedPattern: (s.estimate.votedPattern || []).map((c) => ({\n barIndex: c.barIndex,\n root: c.root as PitchClass,\n quality: c.quality as ChordQuality,\n startTime: c.startTime,\n confidence: c.confidence,\n })),\n patternLength: s.estimate.patternLength,\n detectedPatternName: s.estimate.detectedPatternName || '',\n detectedPatternScore: s.estimate.detectedPatternScore || 0,\n allPatternScores: (s.estimate.allPatternScores || []).map((p) => ({\n name: p.name,\n score: p.score,\n })),\n accumulatedSeconds: s.estimate.accumulatedSeconds,\n usedFrames: s.estimate.usedFrames,\n updated: s.estimate.updated,\n },\n };\n }\n\n /**\n * Get total frames processed.\n */\n frameCount(): number {\n return this.analyzer.frameCount();\n }\n\n /**\n * Get current time position in seconds.\n */\n currentTime(): number {\n return this.analyzer.currentTime();\n }\n\n /**\n * Get the sample rate.\n */\n sampleRate(): number {\n return this.analyzer.sampleRate();\n }\n\n /**\n * Set the expected total duration for pattern lock timing.\n *\n * @param durationSeconds - Total duration in seconds\n */\n setExpectedDuration(durationSeconds: number): void {\n this.analyzer.setExpectedDuration(durationSeconds);\n }\n\n /**\n * Set normalization gain for loud/compressed audio.\n *\n * @param gain - Gain factor to apply (e.g., 0.5 for -6dB reduction)\n */\n setNormalizationGain(gain: number): void {\n this.analyzer.setNormalizationGain(gain);\n }\n\n /**\n * Set tuning reference frequency for non-standard tuning.\n *\n * @param refHz - Reference frequency for A4 (default 440 Hz)\n * @example\n * // If audio is 1 semitone sharp (A4 = 466.16 Hz)\n * analyzer.setTuningRefHz(466.16);\n * // If audio is 1 semitone flat (A4 = 415.30 Hz)\n * analyzer.setTuningRefHz(415.30);\n */\n setTuningRefHz(refHz: number): void {\n this.analyzer.setTuningRefHz(refHz);\n }\n\n /**\n * Release resources. Call when done using the analyzer.\n */\n dispose(): void {\n this.analyzer.delete();\n }\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport { PitchClass as Pitch } from './public_types';\n","import type {\n EngineAutomationPoint,\n EngineClip,\n EngineMarker,\n EngineMeterTelemetry,\n EngineMetronomeConfig,\n EngineParameterInfo,\n EngineTelemetry,\n MixerRealtimeBuffer,\n RealtimeVoiceChangerConfigInput,\n} from './index';\nimport { engineCapabilities, Mixer, RealtimeEngine, RealtimeVoiceChanger } from './index';\nimport type { AutomationCurve } from './public_types';\nimport type { SonareRtModule } from './sonare-rt';\n\n// With code-splitting disabled, the worklet bundle carries its own copy of the\n// module singleton (a real AudioWorkletGlobalScope cannot resolve sibling\n// chunks, so the bundle must be self-contained). Re-export the lifecycle so that\n// realm can initialize its own wasm instance, independent of the main-thread\n// `index` module.\nexport { init, isInitialized } from './index';\n\nexport interface SonareWorkletProcessorOptions {\n sceneJson: string;\n sampleRate?: number;\n blockSize?: number;\n stripCount?: number;\n meterIntervalFrames?: number;\n meterSharedBuffer?: SharedArrayBuffer;\n meterRingCapacity?: number;\n spectrumIntervalFrames?: number;\n spectrumBands?: number;\n spectrumSharedBuffer?: SharedArrayBuffer;\n spectrumRingCapacity?: number;\n}\n\nexport interface SonareRealtimeEngineWorkletProcessorOptions {\n runtimeTarget?: 'embind' | 'sonare-rt';\n rtModuleUrl?: string;\n rtWasmBinary?: ArrayBuffer | Uint8Array;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n meterIntervalFrames?: number;\n commandSharedBuffer?: SharedArrayBuffer;\n commandRingCapacity?: number;\n telemetrySharedBuffer?: SharedArrayBuffer;\n telemetryRingCapacity?: number;\n meterSharedBuffer?: SharedArrayBuffer;\n meterRingCapacity?: number;\n}\n\nexport interface SonareRealtimeVoiceChangerWorkletProcessorOptions {\n preset?: RealtimeVoiceChangerConfigInput;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n}\n\nexport interface SonareRealtimeVoiceChangerSetConfigMessage {\n type: 'setConfig';\n preset: RealtimeVoiceChangerConfigInput;\n}\n\nexport interface SonareRealtimeVoiceChangerResetMessage {\n type: 'reset';\n}\n\nexport interface SonareRealtimeVoiceChangerDestroyMessage {\n type: 'destroy';\n}\n\nexport type SonareRealtimeVoiceChangerMessage =\n | SonareRealtimeVoiceChangerSetConfigMessage\n | SonareRealtimeVoiceChangerResetMessage\n | SonareRealtimeVoiceChangerDestroyMessage;\n\nexport interface SonareRealtimeEngineNodeCapabilities {\n mode: 'sab' | 'postMessage';\n runtimeTarget: 'embind' | 'sonare-rt';\n sharedArrayBuffer: boolean;\n atomics: boolean;\n audioWorklet: boolean;\n engineAbiVersion?: number;\n expectedEngineAbiVersion?: number;\n abiCompatible?: boolean;\n degradedReason?: string;\n}\n\nexport interface SonareRealtimeEngineNodeOptions\n extends SonareRealtimeEngineWorkletProcessorOptions {\n processorName?: string;\n moduleUrl?: string | URL;\n rtModuleUrl?: string;\n mode?: 'auto' | 'sab' | 'postMessage';\n engineAbiVersion?: number;\n expectedEngineAbiVersion?: number;\n requireAbiCompatible?: boolean;\n nodeFactory?: (\n context: BaseAudioContext,\n processorName: string,\n options: AudioWorkletNodeOptions,\n ) => AudioWorkletNode;\n}\n\nexport interface SonareRtRealtimeEngineRuntimeOptions {\n module: SonareRtModule;\n memory: WebAssembly.Memory;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n commandSharedBuffer?: SharedArrayBuffer;\n commandRingCapacity?: number;\n telemetrySharedBuffer?: SharedArrayBuffer;\n telemetryRingCapacity?: number;\n}\n\nexport interface SonareEngineOptions extends SonareRealtimeEngineNodeOptions {\n offlineEngine?: RealtimeEngine;\n offlineBlockSize?: number;\n offlineChannelCount?: number;\n}\n\nexport interface SonareEngineTransportFacade {\n play(sampleTime?: number): boolean;\n stop(sampleTime?: number): boolean;\n seekPpq(ppq: number, sampleTime?: number): boolean;\n seekSeconds(seconds: number, sampleTime?: number): boolean;\n setTempo(bpm: number): void;\n setLoop(startPpq: number, endPpq: number, enabled?: boolean): boolean;\n}\n\ntype SuspendableAudioContext = BaseAudioContext & {\n suspend?: () => Promise<void>;\n resume?: () => Promise<void>;\n};\n\ntype WorkletInput = readonly (readonly Float32Array[])[];\ntype WorkletOutput = Float32Array[][];\n\nexport interface SonareWorkletScheduleInsertAutomationMessage {\n type: 'scheduleInsertAutomation';\n stripIndex: number;\n insertIndex: number;\n paramId: number;\n value: number;\n samplePos?: number;\n curve?: AutomationCurve;\n}\n\nexport interface SonareWorkletSetMeterIntervalMessage {\n type: 'setMeterInterval';\n frames: number;\n}\n\nexport interface SonareWorkletDestroyMessage {\n type: 'destroy';\n}\n\nexport type SonareWorkletMessage =\n | SonareWorkletScheduleInsertAutomationMessage\n | SonareWorkletSetMeterIntervalMessage\n | SonareWorkletDestroyMessage;\n\nexport interface SonareWorkletMeterSnapshot {\n type: 'meter';\n frame: number;\n peakDbL: number;\n peakDbR: number;\n rmsDbL: number;\n rmsDbR: number;\n correlation: number;\n}\n\nexport interface SonareWorkletSpectrumSnapshot {\n type: 'spectrum';\n frame: number;\n bands: Float32Array;\n}\n\nexport type SonareWorkletTransportMessage =\n | SonareWorkletMeterSnapshot\n | SonareWorkletSpectrumSnapshot\n | SonareEngineTelemetryRecord;\n\nexport const SONARE_METER_RING_HEADER_INTS = 4;\n// Record layout: [frameLo, peakDbL, peakDbR, rmsDbL, rmsDbR, correlation, frameHi].\n// The sample-frame index is monotonically increasing and quickly exceeds the\n// 2^24 exact-integer range of a single Float32 slot (~349 s at 48 kHz), so it is\n// stored split across two Float32 lanes (low 24 bits + high bits) for exact\n// reconstruction. See encodeFrameLo/encodeFrameHi/decodeFrame.\nexport const SONARE_METER_RING_RECORD_FLOATS = 7;\nexport const SONARE_SPECTRUM_RING_HEADER_INTS = 5;\n\n/** Base for splitting a frame index into two exactly-representable Float32 lanes. */\nconst SONARE_FRAME_LANE_BASE = 0x1000000; // 2^24\n\n/** Low 24 bits of a frame index (exact in Float32). */\nexport function encodeFrameLo(frame: number): number {\n const f = Math.max(0, Math.floor(frame));\n return f % SONARE_FRAME_LANE_BASE;\n}\n\n/** High bits of a frame index above 2^24 (exact in Float32 up to ~2^48). */\nexport function encodeFrameHi(frame: number): number {\n const f = Math.max(0, Math.floor(frame));\n return Math.floor(f / SONARE_FRAME_LANE_BASE);\n}\n\n/** Reconstruct a frame index from its low/high Float32 lanes. */\nexport function decodeFrame(lo: number, hi: number): number {\n return hi * SONARE_FRAME_LANE_BASE + lo;\n}\nexport const SONARE_ENGINE_RING_HEADER_INTS = 5;\nexport const SONARE_ENGINE_COMMAND_RECORD_BYTES = 32;\nexport const SONARE_ENGINE_TELEMETRY_RECORD_BYTES = 48;\n\nexport enum SonareEngineCommandType {\n SetParam = 0,\n SetParamSmoothed = 1,\n TransportPlay = 2,\n TransportStop = 3,\n TransportSeekSample = 4,\n TransportSeekPpq = 5,\n SetTempoMap = 6,\n SetLoop = 7,\n SwapGraph = 8,\n SwapAutomation = 9,\n SetSoloMute = 10,\n AddClip = 11,\n RemoveClip = 12,\n ArmRecord = 13,\n Punch = 14,\n SetMetronome = 15,\n SetMarker = 16,\n SeekMarker = 17,\n}\n\nexport enum SonareEngineTelemetryType {\n ProcessBlock = 0,\n Error = 1,\n}\n\nexport enum SonareEngineTelemetryError {\n None = 0,\n CommandQueueOverflow = 1,\n PendingCommandOverflow = 2,\n BoundaryOverflow = 3,\n TelemetryOverflow = 4,\n CaptureOverflow = 5,\n MaxBlockExceeded = 6,\n UnknownTarget = 7,\n NonRealtimeSafeParameter = 8,\n NotPrepared = 9,\n NonQueueableCommand = 10,\n AutomationBindTargetOverflow = 11,\n StaleAutomationLanes = 12,\n SmoothedParameterCapacity = 13,\n}\n\ninterface WorkletTransport {\n postMessage?: (message: SonareWorkletTransportMessage) => void;\n onMeter?: (meter: SonareWorkletMeterSnapshot) => void;\n onSpectrum?: (spectrum: SonareWorkletSpectrumSnapshot) => void;\n}\n\ninterface ResolvedMetronomeConfig {\n beatGain: number;\n accentGain: number;\n clickSamples: number;\n}\n\n// Fallback metronome gains/click length used by the worklet consumer until the\n// host posts a 'syncMetronome' config. Aligned with the embind setMetronome\n// defaults (src/wasm/bindings.cpp) so offline and realtime metronomes match.\nconst DEFAULT_METRONOME_CONFIG: ResolvedMetronomeConfig = {\n beatGain: 0.35,\n accentGain: 0.7,\n clickSamples: 96,\n};\n\nfunction resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedMetronomeConfig {\n return {\n beatGain: config.beatGain ?? DEFAULT_METRONOME_CONFIG.beatGain,\n accentGain: config.accentGain ?? DEFAULT_METRONOME_CONFIG.accentGain,\n clickSamples: config.clickSamples ?? DEFAULT_METRONOME_CONFIG.clickSamples,\n };\n}\n\nexport interface SonareMeterRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n}\n\nexport interface SonareMeterRingReadResult {\n nextReadIndex: number;\n meters: SonareWorkletMeterSnapshot[];\n}\n\nexport interface SonareSpectrumRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n bands: number;\n}\n\nexport interface SonareSpectrumRingReadResult {\n nextReadIndex: number;\n spectra: SonareWorkletSpectrumSnapshot[];\n}\n\nexport interface SonareEngineCommandRecord {\n type: SonareEngineCommandType | number;\n targetId?: number;\n sampleTime?: number | bigint;\n argFloat?: number;\n argInt?: number | bigint;\n}\n\n// Out-of-band control messages posted from the main-thread SonareEngine facade\n// to the worklet engine processor over node.port. Unlike SonareEngineCommandRecord\n// (a small POD POSTed/ringed every block) these carry bulk/structured payloads\n// (clip audio buffers, marker lists, metronome config) that cannot fit the\n// fixed-size SAB command record, so they are applied OUTSIDE process() — the\n// audio-thread equivalent of the engine's control-thread RtPublisher setters.\nexport interface SonareEngineSyncClipsMessage {\n type: 'syncClips';\n clips: EngineClip[];\n}\n\nexport interface SonareEngineSyncMarkersMessage {\n type: 'syncMarkers';\n markers: EngineMarker[];\n}\n\nexport interface SonareEngineSyncMetronomeMessage {\n type: 'syncMetronome';\n config: EngineMetronomeConfig;\n}\n\nexport interface SonareEngineSyncAutomationMessage {\n type: 'syncAutomation';\n paramId: number;\n points: EngineAutomationPoint[];\n}\n\nexport type SonareEngineSyncMessage =\n | SonareEngineSyncClipsMessage\n | SonareEngineSyncMarkersMessage\n | SonareEngineSyncMetronomeMessage\n | SonareEngineSyncAutomationMessage;\n\nexport interface SonareEngineTelemetryRecord {\n type: SonareEngineTelemetryType | number;\n error: SonareEngineTelemetryError | number;\n renderFrame: number;\n timelineSample: number;\n audibleTimelineSample: number;\n graphLatencySamplesQ8: number;\n value: number;\n}\n\nexport interface SonareEngineCommandRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n view: DataView;\n capacity: number;\n}\n\nexport interface SonareEngineTelemetryRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n view: DataView;\n capacity: number;\n}\n\nexport interface SonareEngineTelemetryRingReadResult {\n nextReadIndex: number;\n telemetry: SonareEngineTelemetryRecord[];\n}\n\ninterface SharedMeterRingWriter {\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n}\n\ninterface SharedSpectrumRingWriter {\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n bands: number;\n recordFloats: number;\n}\n\ninterface WorkletPort {\n postMessage?: (message: unknown) => void;\n onmessage?: (event: { data: unknown }) => void;\n addEventListener?: (type: 'message', listener: (event: { data: unknown }) => void) => void;\n start?: () => void;\n}\n\nfunction toDb(value: number): number {\n return value > 0 ? 20 * Math.log10(value) : Number.NEGATIVE_INFINITY;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isWorkletMessage(value: unknown): value is SonareWorkletMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return (\n value.type === 'scheduleInsertAutomation' ||\n value.type === 'setMeterInterval' ||\n value.type === 'destroy'\n );\n}\n\nfunction isEngineCommandRecord(value: unknown): value is SonareEngineCommandRecord {\n return isRecord(value) && typeof value.type === 'number';\n}\n\nfunction isEngineSyncMessage(value: unknown): value is SonareEngineSyncMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return (\n value.type === 'syncClips' ||\n value.type === 'syncMarkers' ||\n value.type === 'syncMetronome' ||\n value.type === 'syncAutomation'\n );\n}\n\nfunction isRealtimeVoiceChangerMessage(value: unknown): value is SonareRealtimeVoiceChangerMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return value.type === 'setConfig' || value.type === 'reset' || value.type === 'destroy';\n}\n\nfunction isEngineTelemetryRecord(value: unknown): value is SonareEngineTelemetryRecord {\n return (\n isRecord(value) &&\n typeof value.type === 'number' &&\n typeof value.error === 'number' &&\n typeof value.renderFrame === 'number' &&\n typeof value.timelineSample === 'number' &&\n typeof value.audibleTimelineSample === 'number' &&\n typeof value.graphLatencySamplesQ8 === 'number' &&\n typeof value.value === 'number'\n );\n}\n\nfunction isMeterSnapshot(value: unknown): value is SonareWorkletMeterSnapshot {\n return (\n isRecord(value) &&\n value.type === 'meter' &&\n typeof value.frame === 'number' &&\n typeof value.peakDbL === 'number' &&\n typeof value.peakDbR === 'number' &&\n typeof value.rmsDbL === 'number' &&\n typeof value.rmsDbR === 'number' &&\n typeof value.correlation === 'number'\n );\n}\n\nexport function sonareMeterRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_METER_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_METER_RING_RECORD_FLOATS * Float32Array.BYTES_PER_ELEMENT\n );\n}\n\nexport function createSonareMeterRingBuffer(capacity = 128): SonareMeterRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(sonareMeterRingBufferByteLength(clampedCapacity));\n const ring = meterRingFromSharedBuffer(sharedBuffer, clampedCapacity);\n Atomics.store(ring.header, 0, 0);\n Atomics.store(ring.header, 1, clampedCapacity);\n Atomics.store(ring.header, 2, SONARE_METER_RING_RECORD_FLOATS);\n Atomics.store(ring.header, 3, 0);\n return { sharedBuffer, header: ring.header, records: ring.records, capacity: ring.capacity };\n}\n\nexport function readSonareMeterRingBuffer(\n ring: SonareMeterRingBuffer,\n readIndex = 0,\n): SonareMeterRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const meters: SonareWorkletMeterSnapshot[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n const offset = (index % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n meters.push({\n type: 'meter',\n frame: decodeFrame(ring.records[offset], ring.records[offset + 6]),\n peakDbL: ring.records[offset + 1],\n peakDbR: ring.records[offset + 2],\n rmsDbL: ring.records[offset + 3],\n rmsDbR: ring.records[offset + 4],\n correlation: ring.records[offset + 5],\n });\n }\n return { nextReadIndex: writeIndex, meters };\n}\n\nexport function sonareSpectrumRingBufferByteLength(capacity: number, bands = 16): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const clampedBands = Math.max(1, Math.floor(bands));\n // Record layout: [frameLo, frameHi, bandCount, band0, band1, ...]. frame is\n // split across two Float32 lanes for exact reconstruction beyond 2^24.\n return (\n SONARE_SPECTRUM_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * (3 + clampedBands) * Float32Array.BYTES_PER_ELEMENT\n );\n}\n\nexport function createSonareSpectrumRingBuffer(\n capacity = 128,\n bands = 16,\n): SonareSpectrumRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const clampedBands = Math.max(1, Math.floor(bands));\n const sharedBuffer = new SharedArrayBuffer(\n sonareSpectrumRingBufferByteLength(clampedCapacity, clampedBands),\n );\n const ring = spectrumRingFromSharedBuffer(sharedBuffer, clampedCapacity, clampedBands);\n Atomics.store(ring.header, 0, 0);\n Atomics.store(ring.header, 1, clampedCapacity);\n Atomics.store(ring.header, 2, ring.recordFloats);\n Atomics.store(ring.header, 3, clampedBands);\n Atomics.store(ring.header, 4, 0);\n return {\n sharedBuffer,\n header: ring.header,\n records: ring.records,\n capacity: ring.capacity,\n bands: ring.bands,\n };\n}\n\nexport function readSonareSpectrumRingBuffer(\n ring: SonareSpectrumRingBuffer,\n readIndex = 0,\n): SonareSpectrumRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const recordFloats = Atomics.load(ring.header, 2) || 3 + ring.bands;\n const bands = Atomics.load(ring.header, 3) || ring.bands;\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const spectra: SonareWorkletSpectrumSnapshot[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n const offset = (index % ring.capacity) * recordFloats;\n const values = new Float32Array(bands);\n values.set(ring.records.subarray(offset + 3, offset + 3 + bands));\n spectra.push({\n type: 'spectrum',\n frame: decodeFrame(ring.records[offset], ring.records[offset + 1]),\n bands: values,\n });\n }\n return { nextReadIndex: writeIndex, spectra };\n}\n\nexport function sonareEngineCommandRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_ENGINE_COMMAND_RECORD_BYTES\n );\n}\n\nexport function sonareEngineTelemetryRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_ENGINE_TELEMETRY_RECORD_BYTES\n );\n}\n\nexport function createSonareEngineCommandRingBuffer(capacity = 128): SonareEngineCommandRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(\n sonareEngineCommandRingBufferByteLength(clampedCapacity),\n );\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n clampedCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n}\n\nexport function createSonareEngineTelemetryRingBuffer(\n capacity = 128,\n): SonareEngineTelemetryRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(\n sonareEngineTelemetryRingBufferByteLength(clampedCapacity),\n );\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n clampedCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n}\n\nexport function pushSonareEngineCommandRingBuffer(\n ring: SonareEngineCommandRingBuffer,\n command: SonareEngineCommandRecord,\n): boolean {\n const writeIndex = Atomics.load(ring.header, 0);\n const readIndex = Atomics.load(ring.header, 1);\n if (writeIndex - readIndex >= ring.capacity) {\n Atomics.add(ring.header, 4, 1);\n return false;\n }\n writeEngineCommandRecord(\n ring.view,\n recordOffset(writeIndex, ring.capacity, SONARE_ENGINE_COMMAND_RECORD_BYTES),\n command,\n );\n Atomics.store(ring.header, 0, writeIndex + 1);\n return true;\n}\n\nexport function popSonareEngineCommandRingBuffer(\n ring: SonareEngineCommandRingBuffer,\n): SonareEngineCommandRecord | null {\n const readIndex = Atomics.load(ring.header, 1);\n const writeIndex = Atomics.load(ring.header, 0);\n if (readIndex >= writeIndex) {\n return null;\n }\n const command = readEngineCommandRecord(\n ring.view,\n recordOffset(readIndex, ring.capacity, SONARE_ENGINE_COMMAND_RECORD_BYTES),\n );\n Atomics.store(ring.header, 1, readIndex + 1);\n return command;\n}\n\nexport function writeSonareEngineTelemetryRingBuffer(\n ring: SonareEngineTelemetryRingBuffer,\n telemetry: SonareEngineTelemetryRecord,\n): void {\n const writeIndex = Atomics.load(ring.header, 0);\n writeEngineTelemetryRecord(\n ring.view,\n recordOffset(writeIndex, ring.capacity, SONARE_ENGINE_TELEMETRY_RECORD_BYTES),\n telemetry,\n );\n Atomics.store(ring.header, 0, writeIndex + 1);\n if (writeIndex + 1 > ring.capacity) {\n Atomics.store(ring.header, 4, writeIndex + 1 - ring.capacity);\n }\n}\n\nexport function readSonareEngineTelemetryRingBuffer(\n ring: SonareEngineTelemetryRingBuffer,\n readIndex = 0,\n): SonareEngineTelemetryRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const telemetry: SonareEngineTelemetryRecord[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n telemetry.push(\n readEngineTelemetryRecord(\n ring.view,\n recordOffset(index, ring.capacity, SONARE_ENGINE_TELEMETRY_RECORD_BYTES),\n ),\n );\n }\n return { nextReadIndex: writeIndex, telemetry };\n}\n\nfunction meterRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n): SharedMeterRingWriter {\n const headerBytes = SONARE_METER_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_METER_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 1);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const minBytes = sonareMeterRingBufferByteLength(capacity);\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('meterSharedBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 1, capacity);\n Atomics.store(header, 2, SONARE_METER_RING_RECORD_FLOATS);\n return {\n header,\n records: new Float32Array(\n sharedBuffer,\n headerBytes,\n capacity * SONARE_METER_RING_RECORD_FLOATS,\n ),\n capacity,\n };\n}\n\nfunction spectrumRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n fallbackBands?: number,\n): SharedSpectrumRingWriter {\n const headerBytes = SONARE_SPECTRUM_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_SPECTRUM_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 1);\n const existingBands = Atomics.load(header, 3);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const bands = Math.max(1, Math.floor(existingBands || fallbackBands || 16));\n const recordFloats = 3 + bands;\n const minBytes = sonareSpectrumRingBufferByteLength(capacity, bands);\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('spectrumSharedBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 1, capacity);\n Atomics.store(header, 2, recordFloats);\n Atomics.store(header, 3, bands);\n return {\n header,\n records: new Float32Array(sharedBuffer, headerBytes, capacity * recordFloats),\n capacity,\n bands,\n recordFloats,\n };\n}\n\nfunction engineRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n recordBytes: number,\n fallbackCapacity?: number,\n): { header: Int32Array; view: DataView; capacity: number } {\n const headerBytes = SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_ENGINE_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 2);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const minBytes = headerBytes + capacity * recordBytes;\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('engine SharedArrayBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 2, capacity);\n Atomics.store(header, 3, recordBytes);\n return {\n header,\n view: new DataView(sharedBuffer, headerBytes, capacity * recordBytes),\n capacity,\n };\n}\n\nfunction recordOffset(index: number, capacity: number, recordBytes: number): number {\n return (index % capacity) * recordBytes;\n}\n\nfunction toBigInt64(value: number | bigint | undefined, fallback: bigint): bigint {\n if (typeof value === 'bigint') {\n return value;\n }\n if (typeof value === 'number') {\n return BigInt(Math.trunc(value));\n }\n return fallback;\n}\n\nfunction writeEngineCommandRecord(\n view: DataView,\n offset: number,\n command: SonareEngineCommandRecord,\n): void {\n view.setUint32(offset, command.type, true);\n view.setUint32(offset + 4, command.targetId ?? 0, true);\n view.setBigInt64(offset + 8, toBigInt64(command.sampleTime, -1n), true);\n // argFloat occupies a full 8-byte Float64 slot (replacing the old Float32 +\n // 4-byte pad) so PPQ scalars carried here keep full double precision over the\n // SAB transport, matching the engine's double-precision seek/loop contract.\n view.setFloat64(offset + 16, command.argFloat ?? 0, true);\n view.setBigInt64(offset + 24, toBigInt64(command.argInt, 0n), true);\n}\n\nfunction readEngineCommandRecord(view: DataView, offset: number): SonareEngineCommandRecord {\n return {\n type: view.getUint32(offset, true),\n targetId: view.getUint32(offset + 4, true),\n sampleTime: Number(view.getBigInt64(offset + 8, true)),\n argFloat: view.getFloat64(offset + 16, true),\n argInt: Number(view.getBigInt64(offset + 24, true)),\n };\n}\n\nfunction writeEngineTelemetryRecord(\n view: DataView,\n offset: number,\n telemetry: SonareEngineTelemetryRecord,\n): void {\n view.setUint32(offset, telemetry.type, true);\n view.setUint32(offset + 4, telemetry.error, true);\n view.setBigInt64(offset + 8, BigInt(Math.trunc(telemetry.renderFrame)), true);\n view.setBigInt64(offset + 16, BigInt(Math.trunc(telemetry.timelineSample)), true);\n view.setBigInt64(offset + 24, BigInt(Math.trunc(telemetry.audibleTimelineSample)), true);\n view.setInt32(offset + 32, telemetry.graphLatencySamplesQ8, true);\n view.setUint32(offset + 36, telemetry.value, true);\n view.setBigInt64(offset + 40, 0n, true);\n}\n\nfunction readEngineTelemetryRecord(view: DataView, offset: number): SonareEngineTelemetryRecord {\n return {\n type: view.getUint32(offset, true),\n error: view.getUint32(offset + 4, true),\n renderFrame: Number(view.getBigInt64(offset + 8, true)),\n timelineSample: Number(view.getBigInt64(offset + 16, true)),\n audibleTimelineSample: Number(view.getBigInt64(offset + 24, true)),\n graphLatencySamplesQ8: view.getInt32(offset + 32, true),\n value: view.getUint32(offset + 36, true),\n };\n}\n\nfunction telemetryFromEngine(telemetry: EngineTelemetry): SonareEngineTelemetryRecord {\n return {\n type: telemetry.type,\n error: telemetry.error,\n renderFrame: telemetry.renderFrame,\n timelineSample: telemetry.timelineSample,\n audibleTimelineSample: telemetry.audibleTimelineSample,\n graphLatencySamplesQ8: telemetry.graphLatencySamplesQ8,\n value: telemetry.value,\n };\n}\n\nfunction meterFromEngine(meter: EngineMeterTelemetry): SonareWorkletMeterSnapshot {\n return {\n type: 'meter',\n frame: meter.renderFrame,\n peakDbL: meter.peakDbL,\n peakDbR: meter.peakDbR,\n rmsDbL: meter.rmsDbL,\n rmsDbR: meter.rmsDbR,\n correlation: meter.correlation,\n };\n}\n\nfunction magnitudeToDb(value: number): number {\n return value > 1.0e-12 ? 20 * Math.log10(value) : -120;\n}\n\n/**\n * AudioWorklet-style mixer bridge backed by the package's single `sonare.wasm`.\n *\n * The WASM module must already be initialized via `init()` before constructing\n * this bridge. Each AudioWorklet input is treated as one stereo strip:\n * `inputs[strip][0]` is left and `inputs[strip][1]` is right. Missing channels\n * are replaced with preallocated silence.\n */\nexport class SonareWorkletProcessor {\n readonly sampleRate: number;\n readonly blockSize: number;\n private mixer: Mixer;\n private realtime: MixerRealtimeBuffer;\n private closed = false;\n private processedFrames = 0;\n private lastMeterFrame = 0;\n private meterIntervalFrames: number;\n private spectrumIntervalFrames: number;\n private lastSpectrumFrame = 0;\n private transport?: WorkletTransport;\n private meterRing?: SharedMeterRingWriter;\n private spectrumRing?: SharedSpectrumRingWriter;\n private spectrumBands: Float32Array;\n\n constructor(options: SonareWorkletProcessorOptions, transport?: WorkletTransport) {\n if (!options.sceneJson) {\n throw new Error('sceneJson is required.');\n }\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.spectrumIntervalFrames = Math.max(0, Math.floor(options.spectrumIntervalFrames ?? 0));\n this.transport = transport;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.meterRing = options.meterSharedBuffer\n ? meterRingFromSharedBuffer(options.meterSharedBuffer, options.meterRingCapacity)\n : undefined;\n this.spectrumRing = options.spectrumSharedBuffer\n ? spectrumRingFromSharedBuffer(\n options.spectrumSharedBuffer,\n options.spectrumRingCapacity,\n options.spectrumBands,\n )\n : undefined;\n const spectrumBandCount = this.spectrumRing?.bands ?? Math.max(1, options.spectrumBands ?? 16);\n this.spectrumBands = new Float32Array(spectrumBandCount);\n this.mixer = Mixer.fromSceneJson(options.sceneJson, this.sampleRate, this.blockSize);\n this.mixer.compile();\n const sceneStripCount = this.mixer.stripCount();\n const stripCount = options.stripCount ?? sceneStripCount;\n if (stripCount !== sceneStripCount) {\n throw new Error('stripCount must match the scene strip count.');\n }\n this.realtime = this.mixer.createRealtimeBuffer();\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const leftOut = output?.[0];\n const rightOut = output?.[1];\n if (!leftOut) {\n return true;\n }\n const frames = leftOut.length;\n // The mixer's realtime heap buffers are sized to blockSize. A render quantum\n // that differs from blockSize (e.g. a future browser using a quantum other\n // than 128, or a misconfigured blockSize) must NOT return false here:\n // returning false permanently terminates the AudioWorkletProcessor and\n // silently kills the node mid-stream. Instead degrade gracefully by\n // processing min(frames, blockSize) and zero-filling any remainder, mirroring\n // the sonare-rt processor's behaviour.\n const usable = Math.min(frames, this.blockSize);\n\n for (let strip = 0; strip < this.realtime.leftInputs.length; strip++) {\n const input = inputs[strip];\n const left = input?.[0];\n const right = input?.[1];\n const leftTarget = this.realtime.leftInputs[strip];\n const rightTarget = this.realtime.rightInputs[strip];\n if (left && left.length >= usable) {\n leftTarget.set(left.subarray(0, usable));\n if (right && right.length >= usable) {\n rightTarget.set(right.subarray(0, usable));\n } else {\n rightTarget.set(left.subarray(0, usable));\n }\n } else {\n leftTarget.fill(0);\n rightTarget.fill(0);\n }\n }\n\n this.realtime.process(usable);\n if (usable === frames) {\n leftOut.set(this.realtime.outLeft.subarray(0, usable));\n if (rightOut) {\n rightOut.set(this.realtime.outRight.subarray(0, usable));\n }\n } else {\n // frames > blockSize: fill the produced part and zero the remaining tail.\n leftOut.fill(0);\n leftOut.set(this.realtime.outLeft.subarray(0, usable));\n if (rightOut) {\n rightOut.fill(0);\n rightOut.set(this.realtime.outRight.subarray(0, usable));\n }\n }\n this.processedFrames += usable;\n this.publishMeter(\n this.realtime.outLeft.subarray(0, usable),\n this.realtime.outRight.subarray(0, usable),\n );\n this.publishSpectrum(\n this.realtime.outLeft.subarray(0, usable),\n this.realtime.outRight.subarray(0, usable),\n );\n return true;\n }\n\n receiveMessage(message: SonareWorkletMessage): void {\n if (this.closed) {\n return;\n }\n if (message.type === 'destroy') {\n this.destroy();\n return;\n }\n if (message.type === 'setMeterInterval') {\n this.meterIntervalFrames = Math.max(0, Math.floor(message.frames));\n return;\n }\n if (message.type === 'scheduleInsertAutomation') {\n this.mixer.scheduleInsertAutomation(\n message.stripIndex,\n message.insertIndex,\n message.paramId,\n message.samplePos ?? this.processedFrames,\n message.value,\n message.curve ?? 'linear',\n );\n }\n }\n\n destroy(): void {\n if (!this.closed) {\n this.mixer.delete();\n this.closed = true;\n }\n }\n\n private publishMeter(left: Float32Array, right: Float32Array): void {\n if (!this.transport || this.meterIntervalFrames <= 0) {\n return;\n }\n if (this.processedFrames - this.lastMeterFrame < this.meterIntervalFrames) {\n return;\n }\n this.lastMeterFrame = this.processedFrames;\n\n let peakL = 0;\n let peakR = 0;\n let sumL = 0;\n let sumR = 0;\n let sumLR = 0;\n for (let i = 0; i < left.length; i++) {\n const l = left[i] ?? 0;\n const r = right[i] ?? 0;\n const absL = Math.abs(l);\n const absR = Math.abs(r);\n if (absL > peakL) {\n peakL = absL;\n }\n if (absR > peakR) {\n peakR = absR;\n }\n sumL += l * l;\n sumR += r * r;\n sumLR += l * r;\n }\n const rmsL = Math.sqrt(sumL / Math.max(1, left.length));\n const rmsR = Math.sqrt(sumR / Math.max(1, right.length));\n const denominator = Math.sqrt(sumL * sumR);\n const meter: SonareWorkletMeterSnapshot = {\n type: 'meter',\n frame: this.processedFrames,\n peakDbL: toDb(peakL),\n peakDbR: toDb(peakR),\n rmsDbL: toDb(rmsL),\n rmsDbR: toDb(rmsR),\n correlation: denominator > 0 ? sumLR / denominator : 0,\n };\n this.transport.onMeter?.(meter);\n if (this.meterRing) {\n this.writeMeterRing(meter);\n } else {\n this.transport.postMessage?.(meter);\n }\n }\n\n private writeMeterRing(meter: SonareWorkletMeterSnapshot): void {\n const ring = this.meterRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n ring.records[offset] = encodeFrameLo(meter.frame);\n ring.records[offset + 1] = meter.peakDbL;\n ring.records[offset + 2] = meter.peakDbR;\n ring.records[offset + 3] = meter.rmsDbL;\n ring.records[offset + 4] = meter.rmsDbR;\n ring.records[offset + 5] = meter.correlation;\n ring.records[offset + 6] = encodeFrameHi(meter.frame);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // writeIndex is a free-running monotonic counter, so an overflow guard here\n // would fire on essentially every write past the first `capacity` records\n // and store an ever-growing value, not a dropped-record count. Readers\n // already detect silent overrun via firstReadable = max(readIndex,\n // writeIndex - capacity), so header slot 3 is left at its initial 0.\n }\n\n private publishSpectrum(left: Float32Array, right: Float32Array): void {\n if (this.spectrumIntervalFrames <= 0) {\n return;\n }\n if (this.processedFrames - this.lastSpectrumFrame < this.spectrumIntervalFrames) {\n return;\n }\n this.lastSpectrumFrame = this.processedFrames;\n this.computeSpectrum(left, right);\n if (this.spectrumRing) {\n this.writeSpectrumRing(this.processedFrames, this.spectrumBands);\n return;\n }\n const spectrum: SonareWorkletSpectrumSnapshot = {\n type: 'spectrum',\n frame: this.processedFrames,\n bands: new Float32Array(this.spectrumBands),\n };\n this.transport?.onSpectrum?.(spectrum);\n this.transport?.postMessage?.(spectrum);\n }\n\n private computeSpectrum(left: Float32Array, right: Float32Array): void {\n // Coarse per-render-quantum band energy, NOT a full FFT analyzer: each band\n // is a single-bin DFT (bin = band + 1) evaluated over the current block of n\n // samples. Bins at or above the block Nyquist (band + 1 > floor(n / 2))\n // alias, so the evaluated band count is clamped to floor(n / 2) and any\n // higher bands are pinned to the silence floor. Bin resolution is therefore\n // tied to the render quantum (typically 128 samples); treat the output as a\n // rough spectral tilt, not a precise spectrum.\n const n = Math.max(1, Math.min(left.length, right.length));\n const maxBand = Math.floor(n / 2);\n for (let band = 0; band < this.spectrumBands.length; band++) {\n if (band >= maxBand) {\n this.spectrumBands[band] = magnitudeToDb(0);\n continue;\n }\n const bin = band + 1;\n let real = 0;\n let imag = 0;\n for (let i = 0; i < n; i++) {\n const sample = 0.5 * ((left[i] ?? 0) + (right[i] ?? 0));\n const phase = (-2 * Math.PI * bin * i) / n;\n real += sample * Math.cos(phase);\n imag += sample * Math.sin(phase);\n }\n this.spectrumBands[band] = magnitudeToDb((2 * Math.hypot(real, imag)) / n);\n }\n }\n\n private writeSpectrumRing(frame: number, bands: Float32Array): void {\n const ring = this.spectrumRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * ring.recordFloats;\n ring.records[offset] = encodeFrameLo(frame);\n ring.records[offset + 1] = encodeFrameHi(frame);\n ring.records[offset + 2] = bands.length;\n ring.records.set(bands.subarray(0, ring.bands), offset + 3);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // See writeMeterRing: header slot 4 (the spectrum-ring overflow slot) is\n // left at its initial 0; readers detect silent overrun via the\n // firstReadable = max(readIndex, writeIndex - capacity) clamp. (Slot 3 here\n // holds the band count and is still written at ring creation.)\n }\n}\n\n/**\n * AudioWorklet-style bridge for the DAW realtime engine facade.\n *\n * The default mode uses the existing `sonare.wasm` embind facade. The\n * `sonare-rt` target is exposed as a selectable runtime target for hosts that\n * load the dedicated Emscripten AudioWorklet module.\n */\nexport class SonareRealtimeEngineWorkletProcessor {\n private static warnedChannelScratchOverflow = false;\n readonly sampleRate: number;\n readonly blockSize: number;\n readonly channelCount: number;\n readonly runtimeTarget: 'embind' | 'sonare-rt';\n private engine: RealtimeEngine;\n private closed = false;\n private commandRing?: SonareEngineCommandRingBuffer;\n private telemetryRing?: SonareEngineTelemetryRingBuffer;\n private meterRing?: SharedMeterRingWriter;\n private transport?: WorkletTransport;\n private meterIntervalFrames: number;\n private lastMeterFrame = Number.NEGATIVE_INFINITY;\n // Latest metronome gains/click length pushed via 'syncMetronome'. The\n // SetMetronome command only toggles enabled state; the config arrives here.\n private metronomeConfig: ResolvedMetronomeConfig = { ...DEFAULT_METRONOME_CONFIG };\n // Zero-copy prepared realtime path: persistent per-channel views onto the\n // engine's WASM-heap scratch (acquired once on the main thread via\n // getChannelBuffer). process() writes the AudioWorklet input straight into\n // these views, calls engine.processPrepared(frames) which runs the engine IN\n // PLACE, then reads the same views back — no std::vector or JS Float32Array is\n // allocated per render quantum (the old engine.process() round-tripped fresh\n // arrays on both heaps every block, an RT-safety hazard).\n private channelBuffers: Float32Array[];\n\n constructor(\n options: SonareRealtimeEngineWorkletProcessorOptions = {},\n transport?: WorkletTransport,\n ) {\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n this.runtimeTarget = options.runtimeTarget ?? 'embind';\n if (this.runtimeTarget === 'sonare-rt') {\n throw new Error(\n 'sonare-rt runtime is provided by the dedicated Emscripten AudioWorklet module; use SonareRealtimeEngineNode.create({ runtimeTarget: \"sonare-rt\", moduleUrl: ... }) to load it.',\n );\n }\n this.transport = transport;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.commandRing = options.commandSharedBuffer\n ? this.commandRingFromSharedBuffer(options.commandSharedBuffer, options.commandRingCapacity)\n : undefined;\n this.telemetryRing = options.telemetrySharedBuffer\n ? this.telemetryRingFromSharedBuffer(\n options.telemetrySharedBuffer,\n options.telemetryRingCapacity,\n )\n : undefined;\n this.meterRing = options.meterSharedBuffer\n ? meterRingFromSharedBuffer(options.meterSharedBuffer, options.meterRingCapacity)\n : undefined;\n this.engine = new RealtimeEngine(this.sampleRate, this.blockSize);\n // Allocate persistent WASM-heap scratch (worst case: channelCount channels x\n // blockSize frames) and acquire the per-channel heap views once.\n this.engine.prepareChannels(this.channelCount, this.blockSize);\n this.channelBuffers = new Array(this.channelCount);\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers[ch] = this.engine.getChannelBuffer(ch, this.blockSize);\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const firstOutput = output?.[0];\n if (!firstOutput) {\n return true;\n }\n const frames = firstOutput.length;\n if (frames > this.blockSize) {\n for (const channel of output ?? []) {\n channel.fill(0);\n }\n this.publishTelemetry();\n return true;\n }\n\n this.drainCommands();\n\n // Clamp `frames` to the pre-allocated scratch capacity. The earlier\n // `frames > this.blockSize` branch already returns early, so this is\n // defensive — but we warn once if it ever fires so the contract violation\n // is visible.\n let usableFrames = frames;\n if (usableFrames > this.blockSize) {\n if (!SonareRealtimeEngineWorkletProcessor.warnedChannelScratchOverflow) {\n SonareRealtimeEngineWorkletProcessor.warnedChannelScratchOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeEngineWorkletProcessor: requested ${usableFrames} frames ` +\n `exceeds pre-allocated capacity ${this.blockSize}; clamping.`,\n );\n }\n usableFrames = this.blockSize;\n }\n\n // Defend against WASM linear-memory growth detaching the cached heap views:\n // if any view's backing ArrayBuffer has been detached (byteLength === 0),\n // re-acquire all of them. This is a control-flow check (no allocation in the\n // common case where memory did not grow).\n if ((this.channelBuffers[0]?.byteLength ?? 0) === 0) {\n this.reacquireChannelBuffers();\n }\n\n const input = inputs[0];\n // Write the AudioWorklet input straight into the engine's WASM-heap views;\n // no per-block heap allocation.\n for (let ch = 0; ch < this.channelCount; ch++) {\n const dst = this.channelBuffers[ch];\n const source = input?.[ch];\n if (source && source.length === usableFrames) {\n dst.set(source.subarray(0, usableFrames));\n } else {\n dst.fill(0, 0, usableFrames);\n }\n }\n\n // Run the engine in place over the prepared scratch (allocation-free).\n this.engine.processPrepared(usableFrames);\n\n for (let ch = 0; ch < output.length; ch++) {\n const target = output[ch];\n const source = this.channelBuffers[ch] ?? this.channelBuffers[0];\n if (source) {\n target.set(source.subarray(0, Math.min(target.length, usableFrames)));\n if (target.length > usableFrames) {\n target.fill(0, usableFrames);\n }\n } else {\n target.fill(0);\n }\n }\n this.publishTelemetry();\n this.publishMeters();\n return true;\n }\n\n private reacquireChannelBuffers(): void {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers[ch] = this.engine.getChannelBuffer(ch, this.blockSize);\n }\n }\n\n receiveCommand(command: SonareEngineCommandRecord): void {\n if (!this.closed) {\n this.applyCommand(command);\n }\n }\n\n // Applies an out-of-band control-plane sync message. Runs on the AudioWorklet\n // global scope but OUTSIDE process() (the message-port callback), so the\n // bulk/allocating engine setters (setClips/setMarkers) are safe here — they\n // never run on the realtime render path. This is the audio-thread equivalent\n // of the engine's control-thread RtPublisher setters.\n receiveSync(message: SonareEngineSyncMessage): void {\n if (this.closed) {\n return;\n }\n switch (message.type) {\n case 'syncClips':\n this.engine.setClips(message.clips);\n break;\n case 'syncMarkers':\n this.engine.setMarkers(message.markers);\n break;\n case 'syncMetronome':\n this.metronomeConfig = resolveMetronomeConfig(message.config);\n this.engine.setMetronome(message.config);\n break;\n case 'syncAutomation':\n this.engine.setAutomationLane(message.paramId, message.points);\n break;\n }\n }\n\n destroy(): void {\n if (!this.closed) {\n this.engine.destroy();\n this.closed = true;\n }\n }\n\n private drainCommands(): void {\n if (!this.commandRing) {\n return;\n }\n for (let i = 0; i < 64; i++) {\n const command = popSonareEngineCommandRingBuffer(this.commandRing);\n if (!command) {\n return;\n }\n this.applyCommand(command);\n }\n }\n\n private applyCommand(command: SonareEngineCommandRecord): void {\n const sampleTime = Number(command.sampleTime ?? -1);\n switch (command.type) {\n case SonareEngineCommandType.SetParam:\n // paramId is carried in targetId, the new value in argFloat (matches the\n // SonareEngine.setParam producer). sampleTime is the render frame.\n this.engine.setParameter(\n Math.trunc(Number(command.targetId ?? 0)),\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.SetParamSmoothed:\n this.engine.setParameterSmoothed(\n Math.trunc(Number(command.targetId ?? 0)),\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.TransportPlay:\n this.engine.play(sampleTime);\n break;\n case SonareEngineCommandType.TransportStop:\n this.engine.stop(sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekSample:\n this.engine.seekSample(Number(command.argInt ?? 0), sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekPpq:\n this.engine.seekPpq(Number(command.argFloat ?? 0), sampleTime);\n break;\n case SonareEngineCommandType.SetTempoMap:\n this.engine.setTempo(Number(command.argFloat ?? 120));\n break;\n case SonareEngineCommandType.SetLoop:\n this.engine.setLoop(\n Number(command.argFloat ?? 0),\n Number(command.argInt ?? 0) / 1_000_000,\n command.targetId !== 0,\n );\n break;\n case SonareEngineCommandType.ArmRecord:\n this.engine.armCapture(Boolean(command.argInt));\n break;\n case SonareEngineCommandType.Punch:\n // Both endpoints already arrive as samples (see SonareEngine.punch);\n // do NOT re-scale by sampleRate.\n this.engine.setCapturePunch(\n Number(command.argInt ?? 0),\n Math.max(0, Math.round(Number(command.argFloat ?? 0))),\n true,\n );\n break;\n case SonareEngineCommandType.SetMetronome:\n // Metronome config (beatGain/accentGain/clickSamples/clickSeconds) is\n // delivered out-of-band via the 'syncMetronome' message so it carries\n // the caller's full config; the command only toggles enabled state as a\n // sample-aligned fallback.\n this.engine.setMetronome({\n enabled: Boolean(command.argInt),\n beatGain: this.metronomeConfig.beatGain,\n accentGain: this.metronomeConfig.accentGain,\n clickSamples: this.metronomeConfig.clickSamples,\n });\n break;\n case SonareEngineCommandType.SeekMarker:\n // The realtime engine's markers are kept in sync via 'syncMarkers'\n // (RtPublisher-style swap), so a queued kSeekMarker resolves correctly.\n this.engine.seekMarker(Math.trunc(Number(command.targetId ?? 0)), sampleTime);\n break;\n default:\n this.publishTelemetryRecord({\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n break;\n }\n }\n\n private publishTelemetry(): void {\n for (const item of this.engine.drainTelemetry(64)) {\n this.publishTelemetryRecord(telemetryFromEngine(item));\n }\n }\n\n private publishTelemetryRecord(record: SonareEngineTelemetryRecord): void {\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, record);\n return;\n }\n this.transport?.postMessage?.(record);\n }\n\n private publishMeters(): void {\n if (this.meterIntervalFrames <= 0 || (!this.transport && !this.meterRing)) {\n return;\n }\n for (const item of this.engine.drainMeterTelemetry(64)) {\n const meter = meterFromEngine(item);\n if (meter.frame - this.lastMeterFrame < this.meterIntervalFrames) {\n continue;\n }\n this.lastMeterFrame = meter.frame;\n // Prefer the lock-free SAB meter ring (matching the telemetry path and\n // SonareWorkletProcessor); only fall back to structured-clone postMessage\n // when no ring was provided, so we do not allocate/post from the audio\n // render callback in SAB mode.\n if (this.meterRing) {\n this.writeMeterRing(meter);\n } else {\n this.transport?.onMeter?.(meter);\n this.transport?.postMessage?.(meter);\n }\n }\n }\n\n private writeMeterRing(meter: SonareWorkletMeterSnapshot): void {\n const ring = this.meterRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n ring.records[offset] = encodeFrameLo(meter.frame);\n ring.records[offset + 1] = meter.peakDbL;\n ring.records[offset + 2] = meter.peakDbR;\n ring.records[offset + 3] = meter.rmsDbL;\n ring.records[offset + 4] = meter.rmsDbR;\n ring.records[offset + 5] = meter.correlation;\n ring.records[offset + 6] = encodeFrameHi(meter.frame);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // writeIndex is a free-running monotonic counter, so an overflow guard here\n // would fire on essentially every write past the first `capacity` records\n // and store an ever-growing value, not a dropped-record count. Readers\n // already detect silent overrun via firstReadable = max(readIndex,\n // writeIndex - capacity), so header slot 3 is left at its initial 0.\n }\n\n private commandRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineCommandRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n\n private telemetryRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineTelemetryRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n}\n\nexport class SonareRtRealtimeEngineRuntime {\n readonly sampleRate: number;\n readonly blockSize: number;\n readonly channelCount: number;\n private readonly module: SonareRtModule;\n private readonly memory: WebAssembly.Memory;\n private readonly engine: number;\n private readonly channelPointerTable: number;\n private readonly channelBuffers: number[];\n private readonly telemetryIntsPtr: number;\n private readonly telemetryFramesPtr: number;\n private readonly commandRing?: SonareEngineCommandRingBuffer;\n private readonly telemetryRing?: SonareEngineTelemetryRingBuffer;\n private metronomeConfig: ResolvedMetronomeConfig = { ...DEFAULT_METRONOME_CONFIG };\n private closed = false;\n\n constructor(options: SonareRtRealtimeEngineRuntimeOptions) {\n this.module = options.module;\n this.memory = options.memory;\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n this.commandRing = options.commandSharedBuffer\n ? this.commandRingFromSharedBuffer(options.commandSharedBuffer, options.commandRingCapacity)\n : undefined;\n this.telemetryRing = options.telemetrySharedBuffer\n ? this.telemetryRingFromSharedBuffer(\n options.telemetrySharedBuffer,\n options.telemetryRingCapacity,\n )\n : undefined;\n\n this.engine = this.module._sonare_rt_engine_create();\n if (this.engine <= 0) {\n throw new Error('failed to create sonare-rt engine');\n }\n if (\n this.module._sonare_rt_engine_prepare(\n this.engine,\n this.sampleRate,\n this.blockSize,\n 1024,\n 1024,\n ) !== 1\n ) {\n this.module._sonare_rt_engine_destroy(this.engine);\n throw new Error('failed to prepare sonare-rt engine');\n }\n this.channelPointerTable = this.module._malloc(\n this.channelCount * Uint32Array.BYTES_PER_ELEMENT,\n );\n this.channelBuffers = [];\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers.push(\n this.module._malloc(this.blockSize * Float32Array.BYTES_PER_ELEMENT),\n );\n }\n this.telemetryIntsPtr = this.module._malloc(64 * 4 * Int32Array.BYTES_PER_ELEMENT);\n this.telemetryFramesPtr = this.module._malloc(64 * 3 * Float64Array.BYTES_PER_ELEMENT);\n this.writeChannelPointers();\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const firstOutput = output?.[0];\n if (!firstOutput) {\n return true;\n }\n const frames = firstOutput.length;\n if (frames > this.blockSize) {\n for (const channel of output) {\n channel.fill(0);\n }\n return true;\n }\n\n this.drainCommands();\n const heap = new Float32Array(this.memory.buffer);\n const input = inputs[0];\n for (let ch = 0; ch < this.channelCount; ch++) {\n const ptr = this.channelBuffers[ch] ?? this.channelBuffers[0];\n const offset = ptr >> 2;\n const source = input?.[ch];\n if (source && source.length === frames) {\n heap.set(source, offset);\n } else {\n heap.fill(0, offset, offset + frames);\n }\n }\n\n this.module._sonare_rt_engine_process(\n this.engine,\n this.channelPointerTable,\n this.channelCount,\n frames,\n );\n\n for (let ch = 0; ch < output.length; ch++) {\n const target = output[ch];\n const ptr = this.channelBuffers[ch] ?? this.channelBuffers[0];\n target.set(heap.subarray(ptr >> 2, (ptr >> 2) + target.length));\n }\n this.publishTelemetry();\n return true;\n }\n\n receiveCommand(command: SonareEngineCommandRecord): void {\n if (!this.closed) {\n this.applyCommand(command);\n }\n }\n\n // Out-of-band control sync for the sonare-rt runtime. The sonare-rt C ABI\n // (src/wasm/rt_bindings.cpp) exposes set_metronome_enabled and seek_marker but\n // NOT set_clips / set_markers, so clip/marker mutations cannot be applied to a\n // live sonare-rt engine. We honor the metronome config and surface a clear\n // telemetry error for the unsupported clip/marker paths instead of silently\n // dropping them. The default 'embind' runtime wires all three fully.\n receiveSync(message: SonareEngineSyncMessage): void {\n if (this.closed) {\n return;\n }\n switch (message.type) {\n case 'syncMetronome':\n this.metronomeConfig = resolveMetronomeConfig(message.config);\n this.module._sonare_rt_engine_set_metronome_enabled(\n this.engine,\n message.config.enabled ? 1 : 0,\n this.metronomeConfig.beatGain,\n this.metronomeConfig.accentGain,\n this.metronomeConfig.clickSamples,\n );\n break;\n case 'syncClips':\n case 'syncMarkers':\n case 'syncAutomation':\n // The sonare-rt C ABI exposes no set_clips / set_markers /\n // set_automation_lane, so these mutations cannot reach a live sonare-rt\n // engine. Surface a clear telemetry error rather than silently dropping.\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: 0,\n });\n }\n break;\n }\n }\n\n destroy(): void {\n if (this.closed) {\n return;\n }\n this.module._free(this.telemetryFramesPtr);\n this.module._free(this.telemetryIntsPtr);\n for (const ptr of this.channelBuffers) {\n this.module._free(ptr);\n }\n this.module._free(this.channelPointerTable);\n this.module._sonare_rt_engine_destroy(this.engine);\n this.closed = true;\n }\n\n private writeChannelPointers(): void {\n const pointers = new Uint32Array(this.memory.buffer);\n const offset = this.channelPointerTable >> 2;\n for (let ch = 0; ch < this.channelBuffers.length; ch++) {\n pointers[offset + ch] = this.channelBuffers[ch];\n }\n }\n\n private drainCommands(): void {\n if (!this.commandRing) {\n return;\n }\n for (let i = 0; i < 64; i++) {\n const command = popSonareEngineCommandRingBuffer(this.commandRing);\n if (!command) {\n return;\n }\n this.applyCommand(command);\n }\n }\n\n private applyCommand(command: SonareEngineCommandRecord): void {\n const sampleTime = toBigInt64(command.sampleTime, -1n);\n switch (command.type) {\n case SonareEngineCommandType.SetParam:\n case SonareEngineCommandType.SetParamSmoothed:\n // The sonare-rt C ABI (src/wasm/rt_bindings.cpp) does not export a\n // sonare_rt_engine_set_param entry point, so parameter automation has no\n // realtime transport on this runtime target. Surface a clear error\n // telemetry record (rather than silently dropping the command) so hosts\n // can detect the unsupported path; the embind runtime fully wires this.\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n }\n break;\n case SonareEngineCommandType.TransportPlay:\n this.module._sonare_rt_engine_play(this.engine, sampleTime);\n break;\n case SonareEngineCommandType.TransportStop:\n this.module._sonare_rt_engine_stop(this.engine, sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekSample:\n this.module._sonare_rt_engine_seek_sample(\n this.engine,\n toBigInt64(command.argInt, 0n),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.TransportSeekPpq:\n this.module._sonare_rt_engine_seek_ppq(\n this.engine,\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.SetTempoMap:\n this.module._sonare_rt_engine_set_tempo(this.engine, Number(command.argFloat ?? 120));\n break;\n case SonareEngineCommandType.SetLoop:\n this.module._sonare_rt_engine_set_loop(\n this.engine,\n Number(command.argFloat ?? 0),\n Number(command.argInt ?? 0) / 1_000_000,\n command.targetId ? 1 : 0,\n );\n break;\n case SonareEngineCommandType.ArmRecord:\n this.module._sonare_rt_engine_set_capture_armed(this.engine, command.argInt ? 1 : 0);\n break;\n case SonareEngineCommandType.Punch:\n // Both endpoints already arrive as samples (see SonareEngine.punch);\n // do NOT re-scale by sampleRate.\n this.module._sonare_rt_engine_set_capture_punch(\n this.engine,\n toBigInt64(command.argInt, 0n),\n BigInt(Math.max(0, Math.round(Number(command.argFloat ?? 0)))),\n 1,\n );\n break;\n case SonareEngineCommandType.SetMetronome:\n this.module._sonare_rt_engine_set_metronome_enabled(\n this.engine,\n command.argInt ? 1 : 0,\n this.metronomeConfig.beatGain,\n this.metronomeConfig.accentGain,\n this.metronomeConfig.clickSamples,\n );\n break;\n case SonareEngineCommandType.SeekMarker:\n this.module._sonare_rt_engine_seek_marker(\n this.engine,\n Math.trunc(command.targetId ?? 0),\n sampleTime,\n );\n break;\n default:\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n }\n break;\n }\n }\n\n private publishTelemetry(): void {\n if (!this.telemetryRing) {\n this.module._sonare_rt_engine_drain_telemetry(\n this.engine,\n this.telemetryIntsPtr,\n this.telemetryFramesPtr,\n 64,\n );\n return;\n }\n const count = this.module._sonare_rt_engine_drain_telemetry(\n this.engine,\n this.telemetryIntsPtr,\n this.telemetryFramesPtr,\n 64,\n );\n const ints = new Int32Array(this.memory.buffer);\n const frames = new Float64Array(this.memory.buffer);\n const intBase = this.telemetryIntsPtr >> 2;\n const frameBase = this.telemetryFramesPtr >> 3;\n for (let i = 0; i < count; i++) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: ints[intBase + i * 4],\n error: ints[intBase + i * 4 + 1],\n renderFrame: frames[frameBase + i * 3],\n timelineSample: frames[frameBase + i * 3 + 1],\n audibleTimelineSample: frames[frameBase + i * 3 + 2],\n graphLatencySamplesQ8: ints[intBase + i * 4 + 2],\n value: ints[intBase + i * 4 + 3],\n });\n }\n }\n\n private commandRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineCommandRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n\n private telemetryRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineTelemetryRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n}\n\nexport class SonareRealtimeEngineNode {\n readonly node: AudioWorkletNode;\n readonly capabilities: SonareRealtimeEngineNodeCapabilities;\n readonly commandRing?: SonareEngineCommandRingBuffer;\n readonly telemetryRing?: SonareEngineTelemetryRingBuffer;\n readonly meterRing?: SonareMeterRingBuffer;\n readonly ready: Promise<void>;\n private telemetryReadIndex = 0;\n private meterReadIndex = 0;\n private telemetryListeners = new Set<(telemetry: SonareEngineTelemetryRecord) => void>();\n private meterListeners = new Set<(meter: SonareWorkletMeterSnapshot) => void>();\n private resolveReady!: () => void;\n private rejectReady!: (reason?: unknown) => void;\n private destroyed = false;\n\n private constructor(\n node: AudioWorkletNode,\n capabilities: SonareRealtimeEngineNodeCapabilities,\n commandRing?: SonareEngineCommandRingBuffer,\n telemetryRing?: SonareEngineTelemetryRingBuffer,\n meterRing?: SonareMeterRingBuffer,\n ) {\n this.node = node;\n this.capabilities = capabilities;\n this.commandRing = commandRing;\n this.telemetryRing = telemetryRing;\n this.meterRing = meterRing;\n this.ready = new Promise((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n });\n if (capabilities.runtimeTarget !== 'sonare-rt') {\n this.resolveReady();\n }\n this.node.port.onmessage = (event: MessageEvent<unknown>) => {\n if (isEngineTelemetryRecord(event.data)) {\n this.emitTelemetry(event.data);\n } else if (isMeterSnapshot(event.data)) {\n this.emitMeter(event.data);\n } else if (isRecord(event.data) && event.data.type === 'ready') {\n this.resolveReady();\n } else if (isRecord(event.data) && event.data.type === 'error') {\n this.rejectReady(new Error(String(event.data.message ?? 'AudioWorklet error')));\n }\n };\n }\n\n static async create(\n context: BaseAudioContext,\n options: SonareRealtimeEngineNodeOptions = {},\n ): Promise<SonareRealtimeEngineNode> {\n const runtimeTarget = options.runtimeTarget ?? 'embind';\n const processorName = options.processorName ?? 'sonare-realtime-engine-processor';\n const moduleUrl = options.moduleUrl;\n if (moduleUrl && context.audioWorklet?.addModule) {\n await context.audioWorklet.addModule(moduleUrl);\n }\n const detectedCapabilities =\n options.engineAbiVersion !== undefined\n ? {\n engineAbiVersion: options.engineAbiVersion,\n expectedEngineAbiVersion: options.expectedEngineAbiVersion ?? options.engineAbiVersion,\n abiCompatible:\n options.engineAbiVersion ===\n (options.expectedEngineAbiVersion ?? options.engineAbiVersion),\n }\n : runtimeTarget === 'embind'\n ? engineCapabilities()\n : undefined;\n if (options.requireAbiCompatible !== false && detectedCapabilities?.abiCompatible === false) {\n throw new Error(\n `Engine ABI mismatch: wasm=${detectedCapabilities.engineAbiVersion}, expected=${detectedCapabilities.expectedEngineAbiVersion}`,\n );\n }\n const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';\n const atomics = typeof globalThis.Atomics === 'object';\n const audioWorklet = typeof AudioWorkletNode !== 'undefined' || !!options.nodeFactory;\n const degradedReason =\n options.mode !== 'postMessage' && (!sharedArrayBuffer || !atomics)\n ? 'SharedArrayBuffer or Atomics unavailable; using postMessage transport.'\n : undefined;\n const mode =\n options.mode === 'postMessage' || !sharedArrayBuffer || !atomics ? 'postMessage' : 'sab';\n if (options.mode === 'sab' && mode !== 'sab') {\n throw new Error(\n 'SharedArrayBuffer mode requested but SharedArrayBuffer/Atomics are unavailable.',\n );\n }\n\n const commandRing =\n mode === 'sab'\n ? createSonareEngineCommandRingBuffer(options.commandRingCapacity ?? 128)\n : undefined;\n const telemetryRing =\n mode === 'sab'\n ? createSonareEngineTelemetryRingBuffer(options.telemetryRingCapacity ?? 128)\n : undefined;\n // Meter ring: only the embind runtime publishes engine meters into a SAB\n // ring (the sonare-rt runtime owns its own meter transport). Lock-free\n // meter delivery matches the telemetry path and keeps the audio render\n // callback allocation-free in SAB mode.\n const meterRing =\n mode === 'sab' && runtimeTarget === 'embind'\n ? createSonareMeterRingBuffer(options.meterRingCapacity ?? 128)\n : undefined;\n const channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n const processorOptions: SonareRealtimeEngineWorkletProcessorOptions = {\n runtimeTarget,\n rtModuleUrl: options.rtModuleUrl,\n rtWasmBinary: options.rtWasmBinary,\n sampleRate: options.sampleRate ?? context.sampleRate,\n blockSize: options.blockSize,\n channelCount,\n commandSharedBuffer: commandRing?.sharedBuffer,\n commandRingCapacity: commandRing?.capacity,\n telemetrySharedBuffer: telemetryRing?.sharedBuffer,\n telemetryRingCapacity: telemetryRing?.capacity,\n meterSharedBuffer: meterRing?.sharedBuffer,\n meterRingCapacity: meterRing?.capacity,\n };\n const factory =\n options.nodeFactory ??\n ((ctx: BaseAudioContext, name: string, nodeOptions: AudioWorkletNodeOptions) =>\n new AudioWorkletNode(ctx, name, nodeOptions));\n const node = factory(context, processorName, {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [channelCount],\n processorOptions,\n });\n return new SonareRealtimeEngineNode(\n node,\n {\n mode,\n runtimeTarget,\n sharedArrayBuffer,\n atomics,\n audioWorklet,\n engineAbiVersion: detectedCapabilities?.engineAbiVersion,\n expectedEngineAbiVersion: detectedCapabilities?.expectedEngineAbiVersion,\n abiCompatible: detectedCapabilities?.abiCompatible,\n degradedReason,\n },\n commandRing,\n telemetryRing,\n meterRing,\n );\n }\n\n play(sampleTime = -1): boolean {\n return this.sendCommand({ type: SonareEngineCommandType.TransportPlay, sampleTime });\n }\n\n stop(sampleTime = -1): boolean {\n return this.sendCommand({ type: SonareEngineCommandType.TransportStop, sampleTime });\n }\n\n seekSample(timelineSample: number, sampleTime = -1): boolean {\n return this.sendCommand({\n type: SonareEngineCommandType.TransportSeekSample,\n sampleTime,\n argInt: timelineSample,\n });\n }\n\n seekPpq(ppq: number, sampleTime = -1): boolean {\n return this.sendCommand({\n type: SonareEngineCommandType.TransportSeekPpq,\n sampleTime,\n argFloat: ppq,\n });\n }\n\n sendCommand(command: SonareEngineCommandRecord): boolean {\n if (this.destroyed) {\n return false;\n }\n if (this.commandRing) {\n return pushSonareEngineCommandRingBuffer(this.commandRing, command);\n }\n this.node.port.postMessage(command);\n return true;\n }\n\n pollTelemetry(): SonareEngineTelemetryRecord[] {\n if (!this.telemetryRing) {\n return [];\n }\n const read = readSonareEngineTelemetryRingBuffer(this.telemetryRing, this.telemetryReadIndex);\n this.telemetryReadIndex = read.nextReadIndex;\n for (const telemetry of read.telemetry) {\n this.emitTelemetry(telemetry);\n }\n return read.telemetry;\n }\n\n // Drains any meters published into the SAB meter ring (embind SAB mode) and\n // forwards them to onMeter listeners. In postMessage mode meters arrive via\n // node.port.onmessage instead, so this is a no-op then.\n pollMeters(): SonareWorkletMeterSnapshot[] {\n if (!this.meterRing) {\n return [];\n }\n const read = readSonareMeterRingBuffer(this.meterRing, this.meterReadIndex);\n this.meterReadIndex = read.nextReadIndex;\n for (const meter of read.meters) {\n this.emitMeter(meter);\n }\n return read.meters;\n }\n\n onTelemetry(callback: (telemetry: SonareEngineTelemetryRecord) => void): () => void {\n this.telemetryListeners.add(callback);\n return () => {\n this.telemetryListeners.delete(callback);\n };\n }\n\n onMeter(callback: (meter: SonareWorkletMeterSnapshot) => void): () => void {\n this.meterListeners.add(callback);\n return () => {\n this.meterListeners.delete(callback);\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.node.port.postMessage({ type: SonareEngineCommandType.TransportStop, sampleTime: -1 });\n this.node.disconnect();\n this.telemetryListeners.clear();\n this.meterListeners.clear();\n }\n\n private emitTelemetry(telemetry: SonareEngineTelemetryRecord): void {\n for (const listener of this.telemetryListeners) {\n listener(telemetry);\n }\n }\n\n private emitMeter(meter: SonareWorkletMeterSnapshot): void {\n for (const listener of this.meterListeners) {\n listener(meter);\n }\n }\n}\n\nexport class SonareEngine {\n readonly node: AudioWorkletNode;\n readonly capabilities: SonareRealtimeEngineNodeCapabilities;\n readonly transport: SonareEngineTransportFacade;\n private readonly realtimeNode: SonareRealtimeEngineNode;\n private readonly offlineEngine: RealtimeEngine;\n private readonly context: SuspendableAudioContext;\n private readonly sampleRate: number;\n private readonly offlineBlockSize: number;\n private readonly offlineChannelCount: number;\n private readonly automationLanes = new Map<number, EngineAutomationPoint[]>();\n private readonly clips = new Map<number, EngineClip>();\n private readonly markers = new Map<number, EngineMarker>();\n private nextClipId = 1;\n private nextMarkerId = 1;\n private destroyed = false;\n\n private constructor(\n context: BaseAudioContext,\n realtimeNode: SonareRealtimeEngineNode,\n offlineEngine: RealtimeEngine,\n sampleRate: number,\n offlineBlockSize: number,\n offlineChannelCount: number,\n ) {\n this.context = context;\n this.realtimeNode = realtimeNode;\n this.offlineEngine = offlineEngine;\n this.node = realtimeNode.node;\n this.capabilities = realtimeNode.capabilities;\n this.sampleRate = sampleRate;\n this.offlineBlockSize = offlineBlockSize;\n this.offlineChannelCount = offlineChannelCount;\n this.transport = {\n play: (sampleTime = -1) => this.realtimeNode.play(sampleTime),\n stop: (sampleTime = -1) => this.realtimeNode.stop(sampleTime),\n seekPpq: (ppq, sampleTime = -1) => {\n this.offlineEngine.seekPpq(ppq, sampleTime);\n return this.realtimeNode.seekPpq(ppq, sampleTime);\n },\n seekSeconds: (seconds, sampleTime = -1) => {\n const timelineSample = Math.max(0, Math.round(seconds * this.sampleRate));\n this.offlineEngine.seekSample(timelineSample, sampleTime);\n return this.realtimeNode.seekSample(timelineSample, sampleTime);\n },\n setTempo: (bpm) => this.setTempo(bpm),\n setLoop: (startPpq, endPpq, enabled = true) => this.setLoop(startPpq, endPpq, enabled),\n };\n }\n\n static async create(\n context: BaseAudioContext,\n options: SonareEngineOptions = {},\n ): Promise<SonareEngine> {\n const sampleRate = options.sampleRate ?? context.sampleRate;\n const blockSize = options.offlineBlockSize ?? options.blockSize ?? 128;\n const channelCount = Math.max(\n 1,\n Math.floor(options.offlineChannelCount ?? options.channelCount ?? 2),\n );\n const realtimeNode = await SonareRealtimeEngineNode.create(context, options);\n const offlineEngine = options.offlineEngine ?? new RealtimeEngine(sampleRate, blockSize);\n return new SonareEngine(\n context,\n realtimeNode,\n offlineEngine,\n sampleRate,\n blockSize,\n channelCount,\n );\n }\n\n async suspend(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n await this.context.suspend?.();\n }\n\n async resume(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n await this.context.resume?.();\n }\n\n setTempo(bpm: number): void {\n this.offlineEngine.setTempo(bpm);\n this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetTempoMap,\n sampleTime: -1,\n argFloat: bpm,\n });\n }\n\n setLoop(startPpq: number, endPpq: number, enabled = true): boolean {\n this.offlineEngine.setLoop(startPpq, endPpq, enabled);\n // Transport precision contract: the SAB command record carries exactly one\n // Float64 lane (argFloat) and one Int64 lane (argInt). startPpq travels in\n // argFloat with full double precision, matching the offline engine; endPpq\n // is carried as micro-PPQ (round(endPpq * 1e6)) in the integer lane and\n // divided back by 1e6 on the consumer. Loop ENDS are therefore snapped to\n // the nearest 1e-6 PPQ over the realtime transport (max 5e-7 PPQ drift),\n // while loop STARTS and the offline path stay exact. This is intentional:\n // the record has no second free Float64 lane, and a micro-PPQ grid on the\n // loop end is well below audible/sample-accurate resolution at any tempo.\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetLoop,\n targetId: enabled ? 1 : 0,\n sampleTime: -1,\n argFloat: startPpq,\n argInt: Math.round(endPpq * 1_000_000),\n });\n }\n\n setParam(nodeId: string, param: string | number, value: number): boolean {\n const paramId = this.resolveParamId(nodeId, param);\n // Mirror the change into the offline engine so a subsequent offline render\n // reflects the live value, then push a sample-accurate command to the\n // realtime runtime (mirrors setTempo/setLoop above).\n this.offlineEngine.setParameter(paramId, value);\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetParam,\n targetId: paramId,\n sampleTime: -1,\n argFloat: value,\n });\n }\n\n scheduleParam(\n nodeId: string,\n param: string | number,\n ppq: number,\n value: number,\n curve: number | 'linear' | 'exponential' = 'linear',\n ): void {\n const paramId = this.resolveParamId(nodeId, param);\n const lane = this.automationLanes.get(paramId) ?? [];\n lane.push({ ppq, value, curveToNext: this.curveCode(curve) });\n lane.sort((a, b) => a.ppq - b.ppq);\n this.automationLanes.set(paramId, lane);\n this.offlineEngine.setAutomationLane(paramId, lane);\n // Mirror the lane to the live worklet engine so scheduled automation plays\n // back in real time, not just in renderOffline(). Lanes can exceed the\n // fixed-size SAB command record, so they ride an out-of-band 'syncAutomation'\n // message applied outside process() (like syncClips/syncMarkers).\n this.postSync({ type: 'syncAutomation', paramId, points: lane });\n }\n\n addAutomationPoint(\n laneId: string | number,\n ppq: number,\n value: number,\n curve: number | 'linear' | 'exponential' = 'linear',\n ): void {\n this.scheduleParam('', laneId, ppq, value, curve);\n }\n\n listParameters(): EngineParameterInfo[] {\n const parameters: EngineParameterInfo[] = [];\n for (let index = 0; index < this.offlineEngine.parameterCount(); index++) {\n parameters.push(this.offlineEngine.parameterInfoByIndex(index));\n }\n return parameters;\n }\n\n setSoloMute(target: string | number, solo: boolean, mute: boolean): boolean {\n void target;\n void solo;\n void mute;\n // Per-track solo/mute is a Mixer concept (strip-indexed setSoloed/setMuted),\n // not an engine command: the WASM SonareEngine facade does not own a Mixer\n // node, so there is no realtime transport for solo/mute on this surface.\n // Throw a clear error instead of silently returning false (a silent no-op\n // would leave callers believing the change took effect). Apply solo/mute\n // directly on a Mixer instance (Mixer.setSoloed/Mixer.setMuted) instead.\n throw new Error(\n 'SonareEngine.setSoloMute is not supported: solo/mute is a Mixer feature; ' +\n 'use Mixer.setSoloed(stripIndex, ...) / Mixer.setMuted(stripIndex, ...) instead.',\n );\n }\n\n addClip(\n trackId: string | number,\n buffer: Float32Array[],\n startPpq: number,\n opts: Partial<Omit<EngineClip, 'channels' | 'startPpq'>> = {},\n ): number {\n const id = opts.id ?? this.nextClipId++;\n const clip: EngineClip = {\n ...opts,\n id,\n channels: buffer,\n startPpq,\n };\n this.clips.set(id, clip);\n this.syncClips();\n void trackId;\n return id;\n }\n\n removeClip(clipId: number): void {\n this.clips.delete(clipId);\n this.syncClips();\n }\n\n armRecord(trackId: string | number, enabled: boolean): boolean {\n this.offlineEngine.armCapture(enabled);\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.ArmRecord,\n targetId: this.resolveTargetId(trackId),\n sampleTime: -1,\n argInt: enabled ? 1 : 0,\n });\n }\n\n punch(inPpq: number, outPpq: number): boolean {\n const inSample = this.ppqToApproxSample(inPpq);\n const outSample = this.ppqToApproxSample(outPpq);\n this.offlineEngine.setCapturePunch(inSample, outSample, true);\n // Carry BOTH endpoints as already-converted SAMPLES so the realtime engine\n // agrees with the offline engine. The previous code sent the raw PPQ out\n // point and let the consumer multiply by sampleRate (treating PPQ as\n // seconds), which ignored tempo and produced a punch-out ~2x too large at\n // 120 BPM. argInt = in sample, argFloat = out sample (full-precision double).\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.Punch,\n sampleTime: -1,\n argInt: inSample,\n argFloat: outSample,\n });\n }\n\n setMetronome(opts: EngineMetronomeConfig): void {\n this.offlineEngine.setMetronome(opts);\n // The full config (beatGain/accentGain/clickSamples/clickSeconds) cannot fit\n // the fixed-size SAB command record, so it is delivered out-of-band; the\n // SetMetronome command then toggles enabled state on the audio thread.\n this.postSync({ type: 'syncMetronome', config: opts });\n this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetMetronome,\n sampleTime: -1,\n argInt: opts.enabled ? 1 : 0,\n });\n }\n\n addMarker(ppq: number, name = ''): number {\n const id = this.nextMarkerId++;\n this.markers.set(id, { id, ppq, name });\n this.syncMarkers();\n return id;\n }\n\n seekMarker(markerId: number): boolean {\n this.offlineEngine.seekMarker(markerId);\n // Forward to the live worklet engine. Its marker set is kept in sync via the\n // 'syncMarkers' message (see syncMarkers), so a queued kSeekMarker resolves\n // the marker id to its frame on the audio thread. Returns the sendCommand\n // result (previously this method always returned a misleading `false`).\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SeekMarker,\n targetId: markerId,\n sampleTime: -1,\n });\n }\n\n async renderOffline(totalFrames: number): Promise<Float32Array[]> {\n const frames = Math.max(0, Math.floor(totalFrames));\n const inputs: Float32Array[] = [];\n for (let ch = 0; ch < this.offlineChannelCount; ch++) {\n inputs.push(new Float32Array(frames));\n }\n return this.offlineEngine.renderOffline(inputs, this.offlineBlockSize);\n }\n\n onMeter(callback: (meter: SonareWorkletMeterSnapshot) => void): () => void {\n return this.realtimeNode.onMeter(callback);\n }\n\n onTelemetry(callback: (telemetry: SonareEngineTelemetryRecord) => void): () => void {\n return this.realtimeNode.onTelemetry(callback);\n }\n\n pollTelemetry(): SonareEngineTelemetryRecord[] {\n return this.realtimeNode.pollTelemetry();\n }\n\n pollMeters(): SonareWorkletMeterSnapshot[] {\n return this.realtimeNode.pollMeters();\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.transport.stop();\n this.realtimeNode.pollTelemetry();\n this.realtimeNode.destroy();\n this.offlineEngine.destroy();\n }\n\n private syncClips(): void {\n const clips = Array.from(this.clips.values());\n this.offlineEngine.setClips(clips);\n // Push the full clip set to the live worklet engine over the message port.\n // Clip audio buffers are too large for the fixed-size SAB command record, so\n // they ride a structured-clone 'syncClips' message applied OUTSIDE the audio\n // render callback (the audio-thread equivalent of an RtPublisher swap).\n this.postSync({ type: 'syncClips', clips });\n }\n\n private syncMarkers(): void {\n const markers = Array.from(this.markers.values()).sort((a, b) => a.ppq - b.ppq);\n this.offlineEngine.setMarkers(markers);\n this.postSync({ type: 'syncMarkers', markers });\n }\n\n // Posts an out-of-band control-sync message to the worklet engine processor.\n // Sync messages use a string `type` so the worklet's message handler routes\n // them to receiveSync() (numeric `type` is reserved for SonareEngineCommandRecord).\n private postSync(message: SonareEngineSyncMessage): void {\n if (this.destroyed) {\n return;\n }\n this.realtimeNode.node.port.postMessage(message);\n }\n\n private resolveParamId(nodeId: string, param: string | number): number {\n if (typeof param === 'number') {\n return param;\n }\n const byName = this.listParameters().find((info) => info.name === param);\n if (byName) {\n return byName.id;\n }\n return this.resolveTargetId(param || nodeId);\n }\n\n private resolveTargetId(target: string | number): number {\n if (typeof target === 'number') {\n return target;\n }\n const parsed = Number.parseInt(target, 10);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n\n private curveCode(curve: number | 'linear' | 'exponential'): number {\n if (typeof curve === 'number') {\n return curve;\n }\n return curve === 'exponential' ? 1 : 0;\n }\n\n private ppqToApproxSample(ppq: number): number {\n return Math.max(0, Math.round(((ppq * 60) / 120) * this.sampleRate));\n }\n}\n\nexport class SonareRealtimeVoiceChangerWorkletProcessor {\n private static warnedMonoOverflow = false;\n private static warnedInterleavedOverflow = false;\n private changer: RealtimeVoiceChanger;\n private readonly sampleRate: number;\n private readonly blockSize: number;\n private readonly channelCount: number;\n // WASM-heap typed-memory views, sized to the worst case (blockSize *\n // channelCount). Acquired on the main thread (constructor) so the\n // audio-thread process() never crosses an allocation boundary.\n private monoInput: Float32Array;\n private monoOutput: Float32Array;\n // Planar heap-backed views (one Float32Array per channel) used by the\n // multi-channel path. AudioWorklet inputs/outputs are already planar\n // Float32Arrays, so this avoids the per-sample interleave/deinterleave\n // passes that the older interleaved path needed.\n private planarChannels: Float32Array[];\n private destroyed = false;\n\n constructor(options: SonareRealtimeVoiceChangerWorkletProcessorOptions = {}) {\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 1));\n this.changer = new RealtimeVoiceChanger(options.preset ?? 'neutral-monitor');\n this.changer.prepare(this.sampleRate, this.blockSize, this.channelCount);\n // Acquire WASM-heap views once, sized to the worst case. These are alive\n // for the lifetime of the changer; if the host requests more frames per\n // process() than blockSize, we clamp (see ensure*Capacity).\n this.monoInput = this.changer.getMonoInputBuffer(this.blockSize);\n this.monoOutput = this.changer.getMonoOutputBuffer(this.blockSize);\n this.planarChannels = [];\n if (this.channelCount > 1) {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.planarChannels.push(this.changer.getPlanarChannelBuffer(ch, this.blockSize));\n }\n }\n }\n\n /**\n * Handles a control-plane message from the main thread. Runs on the\n * AudioWorklet global scope but OUTSIDE of `process()` (i.e. outside the\n * realtime audio callback), so it is safe to perform JSON parsing and\n * DSP coefficient recomputation here. `setConfig` MUST NOT be deferred\n * into `process()` because that would block the audio thread for longer\n * than one render quantum (e.g. 128 samples / 44.1 kHz = ~2.9 ms).\n */\n receiveMessage(message: SonareRealtimeVoiceChangerMessage): void {\n if (this.destroyed) {\n return;\n }\n if (message.type === 'setConfig') {\n // Apply synchronously on the message-handler thread. `setConfig` may\n // allocate and parse JSON internally; doing it here keeps `process()`\n // realtime-safe.\n this.changer.setConfig(message.preset);\n } else if (message.type === 'reset') {\n this.changer.reset();\n } else if (message.type === 'destroy') {\n this.destroy();\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n const output = outputs[0];\n if (this.destroyed || !output || output.length === 0) {\n return !this.destroyed;\n }\n\n // The cached heap views can detach if WASM linear memory grows (the embind\n // module is built ALLOW_MEMORY_GROWTH). Re-acquire them if detached\n // (byteLength === 0) before touching them; in the common no-growth case this\n // is a cheap branch with no allocation.\n if (this.monoInput.byteLength === 0) {\n this.reacquireBuffers();\n }\n\n const input = inputs[0];\n const requestedFrames = output[0]?.length ?? 0;\n const requestedChannels = Math.min(this.channelCount, output.length);\n if (requestedFrames === 0 || requestedChannels === 0) {\n return true;\n }\n\n if (requestedChannels === 1) {\n // Clamp to the pre-allocated capacity; warn (once) if the host violated\n // the contract. We never reallocate on the audio thread.\n const frames = this.ensureMonoCapacity(requestedFrames);\n const source = input?.[0];\n if (source) {\n this.monoInput.set(source.subarray(0, frames));\n } else {\n this.monoInput.fill(0, 0, frames);\n }\n this.changer.processMonoInto(\n this.monoInput.subarray(0, frames),\n this.monoOutput.subarray(0, frames),\n );\n output[0].set(this.monoOutput.subarray(0, frames));\n return true;\n }\n\n const frames = this.ensureInterleavedCapacity(requestedFrames, requestedChannels);\n const channels = requestedChannels;\n // Planar zero-copy path: AudioWorklet's input[ch] is already a\n // Float32Array per channel, so we set() straight into the heap-backed\n // planar view and processPreparedPlanar runs in place.\n for (let ch = 0; ch < channels; ch++) {\n const src = input?.[ch];\n const dst = this.planarChannels[ch];\n if (!dst) {\n continue;\n }\n if (src) {\n dst.set(src.subarray(0, frames));\n } else {\n dst.fill(0, 0, frames);\n }\n }\n this.changer.processPreparedPlanar(frames);\n for (let ch = 0; ch < channels; ch++) {\n const src = this.planarChannels[ch];\n if (src) {\n output[ch].set(src.subarray(0, frames));\n }\n // No `for frame` inner loop needed; output[ch] is a Float32Array.\n }\n return true;\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.changer.delete();\n }\n\n // Re-acquires the cached WASM-heap views after a memory-growth detachment.\n // The underlying C++ vectors are pre-warmed (ensure_*_capacity ran at prepare\n // time), so getMono*/getPlanar* return fresh views onto the SAME storage\n // without reallocating it.\n private reacquireBuffers(): void {\n this.monoInput = this.changer.getMonoInputBuffer(this.blockSize);\n this.monoOutput = this.changer.getMonoOutputBuffer(this.blockSize);\n if (this.channelCount > 1) {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.planarChannels[ch] = this.changer.getPlanarChannelBuffer(ch, this.blockSize);\n }\n }\n }\n\n /**\n * Returns the number of frames we can actually process given the\n * pre-allocated capacity. If the host requests more frames than the\n * worst-case block size declared at construction time, we clamp to the\n * available capacity and warn once — we MUST NOT reallocate on the\n * realtime audio thread.\n */\n private ensureMonoCapacity(frames: number): number {\n const capacity = this.monoInput.length;\n if (frames <= capacity) {\n return frames;\n }\n if (!SonareRealtimeVoiceChangerWorkletProcessor.warnedMonoOverflow) {\n SonareRealtimeVoiceChangerWorkletProcessor.warnedMonoOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeVoiceChangerWorkletProcessor: requested ${frames} mono frames ` +\n `exceeds pre-allocated capacity ${capacity}; clamping. ` +\n 'Increase blockSize at construction time to avoid this.',\n );\n }\n return capacity;\n }\n\n /**\n * Same contract as ensureMonoCapacity but for the planar per-channel\n * scratch. Returns the number of frames that fit in the available capacity.\n */\n private ensureInterleavedCapacity(frames: number, channels: number): number {\n const capacity = this.planarChannels[0]?.length ?? 0;\n if (frames <= capacity) {\n return frames;\n }\n if (!SonareRealtimeVoiceChangerWorkletProcessor.warnedInterleavedOverflow) {\n SonareRealtimeVoiceChangerWorkletProcessor.warnedInterleavedOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeVoiceChangerWorkletProcessor: requested ${frames}x${channels} ` +\n `planar frames exceeds pre-allocated capacity ${capacity}; clamping. ` +\n 'Increase blockSize or channelCount at construction time to avoid this.',\n );\n }\n return capacity;\n }\n}\n\nexport function registerSonareWorkletProcessor(name = 'sonare-worklet-processor'): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareWorkletProcessor extends Base {\n private bridge: SonareWorkletProcessor;\n readonly port?: WorkletPort;\n\n constructor(options?: { processorOptions?: SonareWorkletProcessorOptions }) {\n super();\n const port = this.port;\n this.bridge = new SonareWorkletProcessor(options?.processorOptions ?? { sceneJson: '' }, {\n postMessage: (message) => port?.postMessage?.(message),\n });\n const onMessage = (event: { data: unknown }) => {\n if (isWorkletMessage(event.data)) {\n this.bridge.receiveMessage(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n return this.bridge.process(inputs, outputs);\n }\n }\n scope.registerProcessor(name, RegisteredSonareWorkletProcessor);\n}\n\nexport function registerSonareRealtimeVoiceChangerWorkletProcessor(\n name = 'sonare-realtime-voice-changer-processor',\n): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareRealtimeVoiceChangerWorkletProcessor extends Base {\n private bridge: SonareRealtimeVoiceChangerWorkletProcessor;\n readonly port?: WorkletPort;\n\n constructor(options?: {\n processorOptions?: SonareRealtimeVoiceChangerWorkletProcessorOptions;\n }) {\n super();\n const port = this.port;\n this.bridge = new SonareRealtimeVoiceChangerWorkletProcessor(options?.processorOptions ?? {});\n const onMessage = (event: { data: unknown }) => {\n if (isRealtimeVoiceChangerMessage(event.data)) {\n this.bridge.receiveMessage(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n return this.bridge.process(inputs, outputs);\n }\n }\n scope.registerProcessor(name, RegisteredSonareRealtimeVoiceChangerWorkletProcessor);\n}\n\nexport function registerSonareRealtimeEngineWorkletProcessor(\n name = 'sonare-realtime-engine-processor',\n): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareRealtimeEngineWorkletProcessor extends Base {\n private bridge?: SonareRealtimeEngineWorkletProcessor;\n private rtBridge?: SonareRtRealtimeEngineRuntime;\n readonly port?: WorkletPort;\n\n constructor(options?: { processorOptions?: SonareRealtimeEngineWorkletProcessorOptions }) {\n super();\n const port = this.port;\n const processorOptions = options?.processorOptions ?? {};\n if (processorOptions.runtimeTarget === 'sonare-rt') {\n void this.initializeSonareRt(processorOptions, port);\n } else {\n this.bridge = new SonareRealtimeEngineWorkletProcessor(processorOptions, {\n postMessage: (message) => port?.postMessage?.(message),\n onMeter: (meter) => port?.postMessage?.(meter),\n });\n }\n const onMessage = (event: { data: unknown }) => {\n if (isEngineCommandRecord(event.data)) {\n this.bridge?.receiveCommand(event.data);\n this.rtBridge?.receiveCommand(event.data);\n } else if (isEngineSyncMessage(event.data)) {\n this.bridge?.receiveSync(event.data);\n this.rtBridge?.receiveSync(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.rtBridge) {\n return this.rtBridge.process(inputs, outputs);\n }\n if (this.bridge) {\n return this.bridge.process(inputs, outputs);\n }\n const output = outputs[0];\n for (const channel of output ?? []) {\n channel.fill(0);\n }\n return true;\n }\n\n private async initializeSonareRt(\n options: SonareRealtimeEngineWorkletProcessorOptions,\n port?: WorkletPort,\n ): Promise<void> {\n try {\n if (!options.rtModuleUrl) {\n throw new Error('rtModuleUrl is required for sonare-rt AudioWorklet runtime.');\n }\n const rtModuleUrl = options.rtModuleUrl;\n const memory = new WebAssembly.Memory({ initial: 1024, maximum: 1024, shared: true });\n const globalFactory = (\n globalThis as typeof globalThis & {\n SonareRtModuleFactory?: (options?: {\n wasmMemory?: WebAssembly.Memory;\n wasmBinary?: ArrayBuffer | Uint8Array;\n locateFile?: (path: string) => string;\n }) => Promise<SonareRtModule>;\n }\n ).SonareRtModuleFactory;\n const moduleFactory = globalFactory\n ? { default: globalFactory }\n : ((await import(rtModuleUrl)) as {\n default: (options?: {\n wasmMemory?: WebAssembly.Memory;\n wasmBinary?: ArrayBuffer | Uint8Array;\n locateFile?: (path: string) => string;\n }) => Promise<SonareRtModule>;\n });\n const module = await moduleFactory.default({\n wasmMemory: memory,\n wasmBinary: options.rtWasmBinary,\n locateFile: (path) => rtModuleUrl.replace(/[^/]*$/, path),\n });\n this.rtBridge = new SonareRtRealtimeEngineRuntime({\n module,\n memory,\n sampleRate: options.sampleRate,\n blockSize: options.blockSize,\n channelCount: options.channelCount,\n commandSharedBuffer: options.commandSharedBuffer,\n commandRingCapacity: options.commandRingCapacity,\n telemetrySharedBuffer: options.telemetrySharedBuffer,\n telemetryRingCapacity: options.telemetryRingCapacity,\n });\n port?.postMessage?.({ type: 'ready', runtimeTarget: 'sonare-rt' });\n } catch (error) {\n port?.postMessage?.({\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n scope.registerProcessor(name, RegisteredSonareRealtimeEngineWorkletProcessor);\n}\n"],"mappings":"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","names":["SonareEngineCommandType","SonareEngineTelemetryType","SonareEngineTelemetryError","frames","module"]}
|
|
1
|
+
{"version":3,"sources":["../src/module_state.ts","../src/codes.ts","../src/mixer.ts","../src/realtime_voice_changer.ts","../src/realtime_engine.ts","../src/index.ts","../src/worklet.ts"],"sourcesContent":["import type { SonareModule } from './sonare.js';\n\nlet wasmModule: SonareModule | null = null;\n\nexport function setSonareModule(module: SonareModule): void {\n wasmModule = module;\n}\n\nexport function getSonareModule(): SonareModule {\n if (!wasmModule) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return wasmModule;\n}\n","import type { AutomationCurve, MeterTap, PanLaw, PanMode, SendTiming } from './public_types';\n\nexport function automationCurveCode(curve: AutomationCurve): number {\n switch (curve) {\n case 'linear':\n return 0;\n case 'exponential':\n return 1;\n case 'hold':\n return 2;\n case 's-curve':\n return 3;\n default:\n throw new Error(`Invalid automation curve: ${curve}`);\n }\n}\n\nexport function panLawCode(panLaw: PanLaw | number): number {\n if (typeof panLaw === 'number') {\n return panLaw;\n }\n switch (panLaw) {\n case 'const4.5dB':\n return 1;\n case 'const6dB':\n return 2;\n case 'linear0dB':\n return 3;\n default:\n return 0;\n }\n}\n\nexport function panModeCode(panMode: PanMode | number): number {\n if (typeof panMode === 'number') {\n return panMode;\n }\n switch (panMode) {\n case 'stereoPan':\n case 'stereo-pan':\n return 1;\n case 'dualPan':\n case 'dual-pan':\n return 2;\n default:\n return 0;\n }\n}\n\nexport function meterTapCode(tap: MeterTap | number): number {\n return tap === 'preFader' || tap === 0 ? 0 : 1;\n}\n\nexport function sendTimingCode(timing: SendTiming | number): number {\n return timing === 'preFader' || timing === 0 ? 0 : 1;\n}\n","import {\n automationCurveCode,\n meterTapCode,\n panLawCode,\n panModeCode,\n sendTimingCode,\n} from './codes';\nimport { getSonareModule } from './module_state';\nimport type {\n AutomationCurve,\n GoniometerPoint,\n MeterTap,\n MixerProcessResult,\n MixMeterSnapshot,\n PanLaw,\n PanMode,\n SendTiming,\n} from './public_types';\n\nexport interface MixerRealtimeBuffer {\n leftInputs: Float32Array[];\n rightInputs: Float32Array[];\n outLeft: Float32Array;\n outRight: Float32Array;\n process: (numSamples?: number) => void;\n}\n\n// ============================================================================\n// Mixer Class (scene-based persistent mixer)\n// ============================================================================\n\n/**\n * Persistent, scene-based stereo mixer.\n *\n * Build one from a scene JSON string (e.g. {@link mixingScenePresetJson} or a\n * hand-authored scene), then feed per-strip stereo blocks through\n * {@link processStereo} to get the routed stereo master. Strips, sends, buses,\n * and inserts are described entirely by the scene; the routing graph is\n * compiled lazily on the first {@link processStereo} call (or eagerly via\n * {@link compile}).\n *\n * Call {@link delete} (or use a `try/finally`) to release the underlying WASM\n * object — the embind handle is not garbage-collected automatically.\n *\n * @example\n * ```typescript\n * const mixer = Mixer.fromSceneJson(mixingScenePresetJson('basicStereo'), 48000, 512);\n * try {\n * const out = mixer.processStereo([stripL], [stripR]);\n * } finally {\n * mixer.delete();\n * }\n * ```\n */\nexport class Mixer {\n private mixer: import('./sonare.js').WasmMixer;\n\n private constructor(mixer: import('./sonare.js').WasmMixer) {\n this.mixer = mixer;\n }\n\n /**\n * Build a mixer from a scene JSON string.\n *\n * @param json - Scene JSON (strips, buses, sends, connections, inserts)\n * @param sampleRate - Sample rate in Hz (default: 48000)\n * @param blockSize - Maximum block size per {@link processStereo} call (default: 512)\n */\n static fromSceneJson(json: string, sampleRate = 48000, blockSize = 512): Mixer {\n const module = getSonareModule();\n return new Mixer(module.createMixerFromSceneJson(json, sampleRate, blockSize));\n }\n\n /** Rebuild and compile the routing graph from the current scene topology. */\n compile(): void {\n this.mixer.compile();\n }\n\n /**\n * Mix one block of per-strip stereo audio into the stereo master.\n *\n * @param leftChannels - `leftChannels[i]` is the left channel of strip `i`\n * @param rightChannels - `rightChannels[i]` is the right channel of strip `i`\n * @returns Mixed stereo master (`left`, `right`, `sampleRate`)\n */\n processStereo(leftChannels: Float32Array[], rightChannels: Float32Array[]): MixerProcessResult {\n if (leftChannels.length !== rightChannels.length) {\n throw new Error('leftChannels and rightChannels must have the same length.');\n }\n return this.mixer.processStereo(leftChannels, rightChannels);\n }\n\n /**\n * Mix one block into caller-owned output arrays.\n *\n * This avoids allocating the result object and result `Float32Array`s. It is\n * intended for realtime bridges such as AudioWorklet; the input channel count\n * must match the scene strip count and all arrays must have the same length.\n */\n processStereoInto(\n leftChannels: Float32Array[],\n rightChannels: Float32Array[],\n outLeft: Float32Array,\n outRight: Float32Array,\n ): void {\n if (leftChannels.length !== rightChannels.length) {\n throw new Error('leftChannels and rightChannels must have the same length.');\n }\n if (outLeft.length !== outRight.length) {\n throw new Error('outLeft and outRight must have the same length.');\n }\n this.mixer.processStereoInto(leftChannels, rightChannels, outLeft, outRight);\n }\n\n /**\n * Create reusable WASM-heap input/output views for realtime-style processing.\n *\n * Fill `leftInputs[i]` / `rightInputs[i]`, call `process()`, then read\n * `outLeft` / `outRight`. The views are owned by this mixer and become invalid\n * after {@link delete}.\n */\n createRealtimeBuffer(): MixerRealtimeBuffer {\n const stripCount = this.stripCount();\n let leftInputs: Float32Array[] = [];\n let rightInputs: Float32Array[] = [];\n let outLeft = this.mixer.outputLeftView();\n let outRight = this.mixer.outputRightView();\n const acquire = (): void => {\n leftInputs = [];\n rightInputs = [];\n for (let index = 0; index < stripCount; index++) {\n leftInputs.push(this.mixer.inputLeftView(index));\n rightInputs.push(this.mixer.inputRightView(index));\n }\n outLeft = this.mixer.outputLeftView();\n outRight = this.mixer.outputRightView();\n };\n acquire();\n // The cached heap views can detach if WASM linear memory grows (the embind\n // module is built ALLOW_MEMORY_GROWTH). Re-acquire them if detached\n // (byteLength === 0) before use, mirroring the worklet RT path.\n const reacquireIfDetached = (): void => {\n if (outLeft.byteLength === 0 || (leftInputs[0]?.byteLength ?? 1) === 0) {\n acquire();\n }\n };\n return {\n get leftInputs(): Float32Array[] {\n reacquireIfDetached();\n return leftInputs;\n },\n get rightInputs(): Float32Array[] {\n reacquireIfDetached();\n return rightInputs;\n },\n get outLeft(): Float32Array {\n reacquireIfDetached();\n return outLeft;\n },\n get outRight(): Float32Array {\n reacquireIfDetached();\n return outRight;\n },\n process: (numSamples = outLeft.length) => {\n reacquireIfDetached();\n this.mixer.processPreparedStereo(numSamples);\n },\n };\n }\n\n /** Number of strips in the mixer (e.g. strips loaded from the scene). */\n stripCount(): number {\n return this.mixer.stripCount();\n }\n\n /**\n * Schedule sample-accurate insert-parameter automation on a strip's insert.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param insertIndex - Index into the strip's combined insert sequence\n * (`[pre-inserts... post-inserts...]`)\n * @param paramId - Processor-specific parameter id\n * @param samplePos - Absolute samples from the start of processing (the mixer\n * advances an internal position from 0 on the first {@link processStereo}\n * call; recompiling resets it to 0)\n * @param value - Target parameter value\n * @param curve - Interpolation curve (default: `'linear'`)\n * @throws If the strip index is out of range or the schedule call fails\n * (unknown curve, out-of-range insert index, or full event lane)\n */\n scheduleInsertAutomation(\n stripIndex: number,\n insertIndex: number,\n paramId: number,\n samplePos: number,\n value: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleInsertAutomation(\n stripIndex,\n insertIndex,\n paramId,\n samplePos,\n value,\n automationCurveCode(curve),\n );\n }\n\n /**\n * Resolve a strip's index in `[0, stripCount())` from its scene id, or `null`\n * when no strip with that id exists (matches the Node binding's `number | null`).\n */\n stripById(id: string): number | null {\n const index = this.mixer.stripById(id);\n return index < 0 ? null : index;\n }\n\n /**\n * Add a bus to the mixer topology. `role` is one of `'master'`, `'aux'`, or\n * `'submix'` (defaults to `'aux'`). Marks the routing graph dirty; call\n * {@link compile} (or {@link processStereo}) to rebuild.\n */\n addBus(id: string, role = 'aux'): void {\n this.mixer.addBus(id, role);\n }\n\n /** Remove a bus by id. Marks the routing graph dirty. */\n removeBus(id: string): void {\n this.mixer.removeBus(id);\n }\n\n /** Number of buses in the mixer topology. */\n busCount(): number {\n return this.mixer.busCount();\n }\n\n /**\n * Add a VCA group with the given gain offset (dB). `members` is a list of\n * strip ids governed by the group (may be empty).\n */\n addVcaGroup(id: string, gainDb = 0.0, members: string[] = []): void {\n this.mixer.addVcaGroup(id, gainDb, members);\n }\n\n /** Set an existing VCA group's gain in dB. */\n setVcaGroupGainDb(id: string, gainDb: number): void {\n this.mixer.setVcaGroupGainDb(id, gainDb);\n }\n\n /** Remove a VCA group by id. */\n removeVcaGroup(id: string): void {\n this.mixer.removeVcaGroup(id);\n }\n\n /** Number of VCA groups in the mixer topology. */\n vcaGroupCount(): number {\n return this.mixer.vcaGroupCount();\n }\n\n /** Set the strip's input trim in dB. */\n setInputTrimDb(stripIndex: number, db: number): void {\n this.mixer.setInputTrimDb(stripIndex, db);\n }\n\n /** Set the strip's fader level in dB. */\n setFaderDb(stripIndex: number, db: number): void {\n this.mixer.setFaderDb(stripIndex, db);\n }\n\n /**\n * Set the strip's pan position.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param pan - Pan position in `[-1, 1]`\n * @param panMode - Optional pan mode. When omitted the strip's current pan\n * mode is kept (passes `SONARE_PAN_MODE_KEEP`), so a plain pan nudge does\n * not reset a scene-defined `'stereoPan'` / `'dualPan'` mode back to\n * balance. Pass `'balance'` (or `0`) explicitly to force balance mode.\n */\n setPan(stripIndex: number, pan: number, panMode?: PanMode | number): void {\n // SONARE_PAN_MODE_KEEP (-1) = keep the strip's current pan mode.\n const mode = panMode === undefined ? -1 : panModeCode(panMode);\n this.mixer.setPan(stripIndex, pan, mode);\n }\n\n /** Set the strip's stereo width. */\n setWidth(stripIndex: number, width: number): void {\n this.mixer.setWidth(stripIndex, width);\n }\n\n /** Set the strip's mute state. */\n setMuted(stripIndex: number, muted: boolean): void {\n this.mixer.setMuted(stripIndex, muted);\n }\n\n /**\n * Set a strip's solo state. Takes effect on the next process without a\n * graph recompile.\n */\n setSoloed(stripIndex: number, soloed: boolean): void {\n this.mixer.setSoloed(stripIndex, soloed);\n }\n\n /**\n * Mark a strip solo-safe so it is never implied-muted by another strip's\n * solo. Takes effect on the next process without a graph recompile.\n */\n setSoloSafe(stripIndex: number, soloSafe: boolean): void {\n this.mixer.setSoloSafe(stripIndex, soloSafe);\n }\n\n /** Invert the polarity of the left and/or right channel of a strip. */\n setPolarityInvert(stripIndex: number, invertLeft: boolean, invertRight: boolean): void {\n this.mixer.setPolarityInvert(stripIndex, invertLeft, invertRight);\n }\n\n /** Set the strip's pan law. */\n setPanLaw(stripIndex: number, panLaw: PanLaw | number): void {\n this.mixer.setPanLaw(stripIndex, panLawCode(panLaw));\n }\n\n /**\n * Set a per-strip channel delay in samples. This changes the strip's reported\n * latency; recompile to re-run latency compensation.\n */\n setChannelDelaySamples(stripIndex: number, delaySamples: number): void {\n this.mixer.setChannelDelaySamples(stripIndex, delaySamples);\n }\n\n /** Set the strip's live VCA gain offset in dB (not persisted to the scene). */\n setVcaOffsetDb(stripIndex: number, offsetDb: number): void {\n this.mixer.setVcaOffsetDb(stripIndex, offsetDb);\n }\n\n /** Set independent left/right pan positions (dual-pan mode). */\n setDualPan(stripIndex: number, leftPan: number, rightPan: number): void {\n this.mixer.setDualPan(stripIndex, leftPan, rightPan);\n }\n\n /**\n * Add a send to a strip after construction.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param id - Send id\n * @param destinationBusId - Destination bus id\n * @param sendDb - Initial send level in dB\n * @param timing - `'preFader'` or `'postFader'` (default: `'postFader'`)\n * @returns The new send's index\n */\n addSend(\n stripIndex: number,\n id: string,\n destinationBusId: string,\n sendDb = 0.0,\n timing: SendTiming | number = 'postFader',\n ): number {\n return this.mixer.addSend(stripIndex, id, destinationBusId, sendDb, sendTimingCode(timing));\n }\n\n /** Set the send level (in dB) for an existing send by index. */\n setSendDb(stripIndex: number, sendIndex: number, sendDb: number): void {\n this.mixer.setSendDb(stripIndex, sendIndex, sendDb);\n }\n\n /**\n * Remove an existing send from a strip by index.\n *\n * Sends are addressed in add order. After removal, sends with a higher index\n * than `sendIndex` shift down by one. Recompile (or process) before reading\n * results so the routing graph rebuilds.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param sendIndex - Send index in add order\n */\n removeSend(stripIndex: number, sendIndex: number): void {\n this.mixer.removeSend(stripIndex, sendIndex);\n }\n\n /**\n * Read a strip's meter snapshot at the given tap point.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param tap - `'preFader'` or `'postFader'` (default: `'postFader'`)\n */\n meterTap(stripIndex: number, tap: MeterTap = 'postFader'): MixMeterSnapshot {\n return this.mixer.meterTap(stripIndex, meterTapCode(tap));\n }\n\n /**\n * Read a strip's meter snapshot.\n *\n * With no `tap` argument this reads the strip's own (post-fader) meter,\n * matching the Node/Python tap-less `stripMeter` contract. Pass an optional\n * `tap` (`'preFader'` / `'postFader'`) to read the tap-selectable snapshot\n * instead — the same backing call as {@link meterTap}.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param tap - Optional tap point (`'preFader'` / `'postFader'`); when omitted\n * the tap-less post-fader strip meter is read.\n */\n stripMeter(stripIndex: number, tap?: MeterTap | number): MixMeterSnapshot {\n if (tap === undefined) {\n return this.mixer.stripMeter(stripIndex);\n }\n return this.mixer.meterTap(stripIndex, meterTapCode(tap));\n }\n\n /**\n * Schedule sample-accurate fader automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param faderDb - Target fader level in dB\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleFaderAutomation(\n stripIndex: number,\n samplePos: number,\n faderDb: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleFaderAutomation(stripIndex, samplePos, faderDb, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate pan automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param pan - Target pan position\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n schedulePanAutomation(\n stripIndex: number,\n samplePos: number,\n pan: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.schedulePanAutomation(stripIndex, samplePos, pan, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate width automation on a strip.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param samplePos - Absolute samples from the start of processing\n * @param width - Target stereo width\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleWidthAutomation(\n stripIndex: number,\n samplePos: number,\n width: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleWidthAutomation(stripIndex, samplePos, width, automationCurveCode(curve));\n }\n\n /**\n * Schedule sample-accurate send-level automation on a strip's send.\n *\n * @param stripIndex - Strip index in `[0, stripCount())`\n * @param sendIndex - Send index in the strip's add order\n * @param samplePos - Absolute samples from the start of processing\n * @param db - Target send level in dB\n * @param curve - Interpolation curve (default: `'linear'`)\n */\n scheduleSendAutomation(\n stripIndex: number,\n sendIndex: number,\n samplePos: number,\n db: number,\n curve: AutomationCurve = 'linear',\n ): void {\n this.mixer.scheduleSendAutomation(\n stripIndex,\n sendIndex,\n samplePos,\n db,\n automationCurveCode(curve),\n );\n }\n\n /**\n * Read up to `maxPoints` of a strip's most recent goniometer samples\n * (oldest to newest).\n */\n readGoniometerLatest(stripIndex: number, maxPoints: number): GoniometerPoint[] {\n return this.mixer.readGoniometerLatest(stripIndex, maxPoints);\n }\n\n /** Serialize the current scene (strips, buses, sends, connections) to JSON. */\n toSceneJson(): string {\n return this.mixer.toSceneJson();\n }\n\n /**\n * Maximum processor tail length (samples) in the compiled mixer graph. Lazily\n * compiles the routing graph if the topology is dirty.\n */\n tailSamples(): number {\n return this.mixer.tailSamples();\n }\n\n /**\n * Drain delayed / tail audio by processing a zero-input block of `numSamples`\n * frames after the host stops feeding strip inputs. Returns the mixed stereo\n * master (`left`, `right`, `sampleRate`).\n */\n drainTailStereo(numSamples: number): MixerProcessResult {\n return this.mixer.drainTailStereo(numSamples);\n }\n\n /** Release the underlying WASM object. Safe to call only once. */\n delete(): void {\n this.mixer.delete();\n }\n\n /** Alias for {@link delete}, provided for cross-binding (Node) compatibility. */\n destroy(): void {\n this.delete();\n }\n}\n","import { getSonareModule } from './module_state';\nimport type { RealtimeVoiceChangerConfigInput, VoicePresetId } from './public_types';\n\n/**\n * Zero-copy realtime buffer pair for {@link RealtimeVoiceChanger} mono\n * processing. The `input` / `output` `Float32Array`s are typed-memory views\n * onto the WASM heap — write samples into `input`, call `process()`, then\n * read from `output`. The views are owned by the {@link RealtimeVoiceChanger}\n * and remain valid until `delete()` is called on it.\n */\nexport interface RealtimeVoiceChangerMonoBuffer {\n input: Float32Array;\n output: Float32Array;\n process: () => void;\n}\n\n/**\n * Zero-copy realtime buffer pair for {@link RealtimeVoiceChanger} interleaved\n * multi-channel processing. Layout is L0,R0,L1,R1,... for stereo. The views\n * are owned by the {@link RealtimeVoiceChanger}.\n */\nexport interface RealtimeVoiceChangerInterleavedBuffer {\n input: Float32Array;\n output: Float32Array;\n channels: number;\n process: () => void;\n}\n\n/**\n * Zero-copy realtime buffer for {@link RealtimeVoiceChanger} planar stereo\n * processing. Each entry in `channels` is a heap-backed `Float32Array` for one\n * channel (matching AudioWorklet's native layout). Process happens in place:\n * write samples into each channel view, call `process()`, then read back from\n * the same views.\n */\nexport interface RealtimeVoiceChangerPlanarBuffer {\n channels: Float32Array[];\n process: () => void;\n}\n\n// ============================================================================\n// RealtimeVoiceChanger Class\n// ============================================================================\n\nexport class RealtimeVoiceChanger {\n private changer: import('./sonare.js').WasmRealtimeVoiceChanger;\n\n constructor(config: RealtimeVoiceChangerConfigInput = 'neutral-monitor') {\n const module = getSonareModule();\n this.changer = module.createRealtimeVoiceChanger(config as Record<string, unknown> | string);\n }\n\n prepare(sampleRate: number, maxBlockSize = 128, channels = 1): void {\n this.changer.prepare(sampleRate, maxBlockSize, channels);\n }\n\n reset(): void {\n this.changer.reset();\n }\n\n setConfig(config: RealtimeVoiceChangerConfigInput): void {\n this.changer.setConfig(config as Record<string, unknown> | string);\n }\n\n configJson(): string {\n return this.changer.configJson();\n }\n\n latencySamples(): number {\n return this.changer.latencySamples();\n }\n\n processMono(samples: Float32Array): Float32Array {\n return this.changer.processMono(samples);\n }\n\n processMonoInto(samples: Float32Array, output: Float32Array): void {\n this.changer.processMonoInto(samples, output);\n }\n\n processInterleaved(samples: Float32Array, channels: number): Float32Array {\n return this.changer.processInterleaved(samples, channels);\n }\n\n processInterleavedInto(samples: Float32Array, channels: number, output: Float32Array): void {\n this.changer.processInterleavedInto(samples, channels, output);\n }\n\n /**\n * Acquire a typed-memory view onto the WASM heap for mono input.\n *\n * Write your input samples into the returned `Float32Array` directly (e.g.\n * via `input.set(source)`); no copy crosses the JS↔C++ bridge until\n * {@link processPreparedMono} is called. The view is owned by this\n * RealtimeVoiceChanger and becomes invalid after {@link delete}; it may\n * also be invalidated if you later call this method with a larger\n * `numSamples` value (the underlying buffer may be reallocated).\n */\n getMonoInputBuffer(numSamples: number): Float32Array {\n return this.changer.getMonoInputBuffer(numSamples);\n }\n\n /** Mono output view counterpart to {@link getMonoInputBuffer}. */\n getMonoOutputBuffer(numSamples: number): Float32Array {\n return this.changer.getMonoOutputBuffer(numSamples);\n }\n\n /**\n * Process the previously-acquired mono input buffer in place. The output\n * appears in the buffer returned by {@link getMonoOutputBuffer}. No JS↔C++\n * sample-level crossings happen on this call — it just hands control to\n * the underlying DSP on already-on-heap data.\n */\n processPreparedMono(numSamples: number): void {\n this.changer.processPreparedMono(numSamples);\n }\n\n /** Interleaved input view (layout L0,R0,L1,R1,...). */\n getInterleavedInputBuffer(numFrames: number, numChannels: number): Float32Array {\n return this.changer.getInterleavedInputBuffer(numFrames, numChannels);\n }\n\n /** Interleaved output view counterpart. */\n getInterleavedOutputBuffer(numFrames: number, numChannels: number): Float32Array {\n return this.changer.getInterleavedOutputBuffer(numFrames, numChannels);\n }\n\n /**\n * Process the previously-acquired interleaved buffer in place. Output\n * appears in the buffer returned by {@link getInterleavedOutputBuffer}.\n */\n processPreparedInterleaved(numFrames: number, numChannels: number): void {\n this.changer.processPreparedInterleaved(numFrames, numChannels);\n }\n\n /**\n * Planar-channel input/output view (one Float32Array per channel). Matches\n * AudioWorklet's native layout; processing happens in place.\n */\n getPlanarChannelBuffer(channel: number, numFrames: number): Float32Array {\n return this.changer.getPlanarChannelBuffer(channel, numFrames);\n }\n\n /**\n * Process the previously-acquired planar channel buffers in place. Each\n * channel must have been obtained from {@link getPlanarChannelBuffer}\n * with the same `numFrames`. Output replaces input in the same buffers.\n */\n processPreparedPlanar(numFrames: number): void {\n this.changer.processPreparedPlanar(numFrames);\n }\n\n /**\n * Convenience factory for the mono zero-copy path: returns the input/output\n * heap views plus a `process()` thunk wired to the same `numSamples`. The\n * views are reused across calls and become invalid after {@link delete}.\n */\n createRealtimeMonoBuffer(numSamples: number): RealtimeVoiceChangerMonoBuffer {\n let input = this.getMonoInputBuffer(numSamples);\n let output = this.getMonoOutputBuffer(numSamples);\n // The cached heap views can detach if WASM linear memory grows (the embind\n // module is built ALLOW_MEMORY_GROWTH). Re-acquire them if detached\n // (byteLength === 0) before use, mirroring the worklet RT path. In the\n // common no-growth case this is a cheap branch with no allocation.\n const reacquireIfDetached = (): void => {\n if (input.byteLength === 0 || output.byteLength === 0) {\n input = this.getMonoInputBuffer(numSamples);\n output = this.getMonoOutputBuffer(numSamples);\n }\n };\n return {\n get input(): Float32Array {\n reacquireIfDetached();\n return input;\n },\n get output(): Float32Array {\n reacquireIfDetached();\n return output;\n },\n process: () => {\n reacquireIfDetached();\n this.processPreparedMono(numSamples);\n },\n };\n }\n\n /** Same as {@link createRealtimeMonoBuffer} but for interleaved I/O. */\n createRealtimeInterleavedBuffer(\n numFrames: number,\n numChannels: number,\n ): RealtimeVoiceChangerInterleavedBuffer {\n let input = this.getInterleavedInputBuffer(numFrames, numChannels);\n let output = this.getInterleavedOutputBuffer(numFrames, numChannels);\n // Re-acquire detached views after WASM memory growth (see\n // createRealtimeMonoBuffer for rationale).\n const reacquireIfDetached = (): void => {\n if (input.byteLength === 0 || output.byteLength === 0) {\n input = this.getInterleavedInputBuffer(numFrames, numChannels);\n output = this.getInterleavedOutputBuffer(numFrames, numChannels);\n }\n };\n return {\n get input(): Float32Array {\n reacquireIfDetached();\n return input;\n },\n get output(): Float32Array {\n reacquireIfDetached();\n return output;\n },\n channels: numChannels,\n process: () => {\n reacquireIfDetached();\n this.processPreparedInterleaved(numFrames, numChannels);\n },\n };\n }\n\n /**\n * Convenience factory for the planar zero-copy path. Acquires one\n * heap-backed Float32Array per channel and returns a `process()` thunk\n * wired to the same `numFrames`. Buffers are reused across calls and\n * become invalid after {@link delete}.\n */\n createRealtimePlanarBuffer(\n numFrames: number,\n numChannels: number,\n ): RealtimeVoiceChangerPlanarBuffer {\n let channels: Float32Array[] = [];\n const acquire = (): void => {\n channels = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channels.push(this.getPlanarChannelBuffer(ch, numFrames));\n }\n };\n acquire();\n // Re-acquire detached views after WASM memory growth (see\n // createRealtimeMonoBuffer for rationale).\n const reacquireIfDetached = (): void => {\n if ((channels[0]?.byteLength ?? 0) === 0) {\n acquire();\n }\n };\n return {\n get channels(): Float32Array[] {\n reacquireIfDetached();\n return channels;\n },\n process: () => {\n reacquireIfDetached();\n this.processPreparedPlanar(numFrames);\n },\n };\n }\n\n delete(): void {\n this.changer.delete();\n }\n}\n\nexport function realtimeVoiceChangerPresetNames(): VoicePresetId[] {\n return getSonareModule().realtimeVoiceChangerPresetNames() as VoicePresetId[];\n}\n\nexport function realtimeVoiceChangerPresetJson(name: VoicePresetId): string {\n return getSonareModule().realtimeVoiceChangerPresetJson(name);\n}\n\nexport function validateRealtimeVoiceChangerPresetJson(json: string): {\n ok: boolean;\n normalizedJson?: string;\n error?: string;\n} {\n return getSonareModule().validateRealtimeVoiceChangerPresetJson(json);\n}\n","import { getSonareModule } from './module_state';\nimport type { SynthPatch } from './project';\nimport type {\n WasmClipPageRequest,\n WasmEngineAutomationPoint,\n WasmEngineBounceOptions,\n WasmEngineBounceResult,\n WasmEngineCaptureStatus,\n WasmEngineClip,\n WasmEngineFreezeOptions,\n WasmEngineFreezeResult,\n WasmEngineGraphSpec,\n WasmEngineMarker,\n WasmEngineMeterTelemetry,\n WasmEngineMetronomeConfig,\n WasmEngineParameterInfo,\n WasmEngineProcessWithMonitorResult,\n WasmEngineTelemetry,\n WasmEngineTransportState,\n WasmRealtimeEngine,\n} from './sonare.js';\n\nexport type EngineClip = WasmEngineClip;\nexport type ClipPageRequest = WasmClipPageRequest;\nexport type EngineParameterInfo = WasmEngineParameterInfo;\nexport type EngineAutomationPoint = WasmEngineAutomationPoint;\nexport type EngineMarker = WasmEngineMarker;\nexport type EngineMetronomeConfig = WasmEngineMetronomeConfig;\nexport type EngineGraphSpec = WasmEngineGraphSpec;\nexport type EngineCaptureStatus = WasmEngineCaptureStatus;\nexport type EngineBounceOptions = WasmEngineBounceOptions;\nexport type EngineBounceResult = WasmEngineBounceResult;\nexport type EngineFreezeOptions = WasmEngineFreezeOptions;\nexport type EngineFreezeResult = WasmEngineFreezeResult;\nexport type EngineTelemetry = WasmEngineTelemetry;\nexport type EngineMeterTelemetry = WasmEngineMeterTelemetry;\nexport type EngineTransportState = WasmEngineTransportState;\n\nexport const EXPECTED_ENGINE_ABI_VERSION = 3;\n\n/** Options for {@link RealtimeEngine.bindMidiCc}. All fields are optional. */\nexport interface MidiCcBindOptions {\n /** Lower end of the mapped parameter range. Default `0`. */\n minValue?: number;\n /** Upper end of the mapped parameter range. Default `1`. */\n maxValue?: number;\n}\n\nexport interface EngineCapabilities {\n engineAbiVersion: number;\n expectedEngineAbiVersion: number;\n abiCompatible: boolean;\n sharedArrayBuffer: boolean;\n atomics: boolean;\n audioWorklet: boolean;\n mode: 'sab' | 'postMessage';\n}\n\nexport function engineCapabilities(): EngineCapabilities {\n const abiVersion = getSonareModule().engineAbiVersion();\n const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';\n const atomics = typeof globalThis.Atomics === 'object';\n const audioWorklet =\n typeof AudioWorkletNode !== 'undefined' ||\n typeof (globalThis as typeof globalThis & { AudioWorkletProcessor?: unknown })\n .AudioWorkletProcessor !== 'undefined';\n return {\n engineAbiVersion: abiVersion,\n expectedEngineAbiVersion: EXPECTED_ENGINE_ABI_VERSION,\n abiCompatible: abiVersion === EXPECTED_ENGINE_ABI_VERSION,\n sharedArrayBuffer,\n atomics,\n audioWorklet,\n mode: sharedArrayBuffer && atomics ? 'sab' : 'postMessage',\n };\n}\n\n// Methods added to the embind RealtimeEngine that the generated `sonare.js`\n// declarations only gain after a WASM rebuild. The native handle is cast to this\n// shape so the wrapper can reach them without a stale type error.\ninterface WasmRealtimeEngineExt {\n setBuiltinInstrument: (destinationId: number, config: object) => void;\n setSynthInstrument: (destinationId: number, patch: object | string) => void;\n loadSoundFont: (data: Uint8Array) => void;\n setSf2Instrument: (destinationId: number, config: object) => void;\n clearMidiInstrument: (destinationId: number) => void;\n midiInstrumentCount: () => number;\n bindMidiCc: (\n channel: number,\n controller: number,\n paramId: number,\n minValue: number,\n maxValue: number,\n ) => void;\n clearMidiCcBindings: () => void;\n midiCcBindingCount: () => number;\n setMidiFx: (destinationId: number, configJson: string) => void;\n clearMidiFx: (destinationId: number) => void;\n setMidiInputSource: (destinationId: number) => void;\n clearMidiInputSource: () => void;\n midiInputPendingCount: () => number;\n createClipPageProvider: (numChannels: number, numSamples: number, pageFrames: number) => number;\n supplyClipPage: (providerId: number, pageIndex: number, channels: Float32Array[]) => void;\n clearClipPage: (providerId: number, pageIndex: number) => void;\n destroyClipPageProvider: (providerId: number) => void;\n popClipPageRequest: () => ClipPageRequest | null;\n pushMidiInputNoteOn: (\n group: number,\n channel: number,\n note: number,\n velocity: number,\n portTimeSamples: number,\n ) => void;\n pushMidiInputNoteOff: (\n group: number,\n channel: number,\n note: number,\n velocity: number,\n portTimeSamples: number,\n ) => void;\n pushMidiInputCc: (\n group: number,\n channel: number,\n controller: number,\n value: number,\n portTimeSamples: number,\n ) => void;\n pushMidiNoteOn: (\n destinationId: number,\n group: number,\n channel: number,\n note: number,\n velocity: number,\n renderFrame: number,\n ) => void;\n pushMidiNoteOff: (\n destinationId: number,\n group: number,\n channel: number,\n note: number,\n velocity: number,\n renderFrame: number,\n ) => void;\n pushMidiCc: (\n destinationId: number,\n group: number,\n channel: number,\n controller: number,\n value: number,\n renderFrame: number,\n ) => void;\n pushMidiPanic: (renderFrame: number) => void;\n clearParameters: () => void;\n}\n\nexport class RealtimeEngine {\n private native: WasmRealtimeEngine;\n\n private nativeExt(): WasmRealtimeEngineExt {\n return this.native as unknown as WasmRealtimeEngineExt;\n }\n\n constructor(\n sampleRate = 48000,\n maxBlockSize = 128,\n commandCapacity = 1024,\n telemetryCapacity = 1024,\n ) {\n const module = getSonareModule();\n const capabilities = engineCapabilities();\n if (!capabilities.abiCompatible) {\n throw new Error(\n `Engine ABI mismatch: wasm=${capabilities.engineAbiVersion}, expected=${capabilities.expectedEngineAbiVersion}`,\n );\n }\n this.native = new module.RealtimeEngine(\n sampleRate,\n maxBlockSize,\n commandCapacity,\n telemetryCapacity,\n );\n }\n\n prepare(\n sampleRate: number,\n maxBlockSize: number,\n commandCapacity = 1024,\n telemetryCapacity = 1024,\n ): void {\n this.native.prepare(sampleRate, maxBlockSize, commandCapacity, telemetryCapacity);\n }\n\n /** Queue a sample-accurate parameter change (engine kSetParam). */\n setParameter(paramId: number, value: number, renderFrame = -1): void {\n this.native.setParameter(paramId, value, renderFrame);\n }\n\n /** Queue a smoothed parameter change (engine kSetParamSmoothed). */\n setParameterSmoothed(paramId: number, value: number, renderFrame = -1): void {\n this.native.setParameterSmoothed(paramId, value, renderFrame);\n }\n\n setBuiltinInstrument(\n config: { destinationId?: number } & Record<string, unknown> = {},\n destinationId = config.destinationId ?? 0,\n ): void {\n this.nativeExt().setBuiltinInstrument(destinationId, config);\n }\n\n /**\n * Bind the patch-driven NativeSynth to a realtime MIDI destination. `patch`\n * is a {@link SynthPatch} or a preset-name string (`'saw-lead'` /\n * `'va:saw-lead'`; see {@link synthPresetNames}), resolving exactly like\n * {@link Project.bounceWithSynthInstrument}. Live note/CC commands and\n * scheduled MIDI clips routed to that destination render through the synth.\n * Unknown preset names throw. An object patch's `destinationId` is a JS\n * binding convenience, not part of the NativeSynth patch itself.\n */\n setSynthInstrument(\n patch: SynthPatch | string = {},\n destinationId = (typeof patch === 'object' ? patch.destinationId : undefined) ?? 0,\n ): void {\n this.nativeExt().setSynthInstrument(destinationId, patch);\n }\n\n /**\n * Load (parse) SoundFont 2 bytes into the engine so SF2 instruments can be\n * bound with {@link setSf2Instrument}. The host fetches the `.sf2` and\n * passes the raw bytes; they are copied into linear memory for the call and\n * not referenced afterwards. Replaces any previously loaded SoundFont.\n */\n loadSoundFont(data: Uint8Array): void {\n this.nativeExt().loadSoundFont(data);\n }\n\n /**\n * Bind a GS-compatible SoundFont player to a realtime MIDI destination, fed\n * by the engine's loaded SoundFont ({@link loadSoundFont}). Live note/CC\n * commands and scheduled MIDI clips routed to that destination render\n * through the player (16 MIDI channels, channel 10 drums, GS NRPN part\n * edits, GS/GM SysEx resets). Without a loaded SoundFont — or for programs\n * the SoundFont does not cover — notes play through the built-in\n * synthesizer GM fallback bank (the data-free floor).\n */\n setSf2Instrument(\n config: { destinationId?: number; gain?: number; polyphony?: number } = {},\n destinationId = config.destinationId ?? 0,\n ): void {\n this.nativeExt().setSf2Instrument(destinationId, config);\n }\n\n clearMidiInstrument(destinationId = 0): void {\n this.nativeExt().clearMidiInstrument(destinationId);\n }\n\n midiInstrumentCount(): number {\n return this.nativeExt().midiInstrumentCount();\n }\n\n /**\n * Bind a live MIDI CC to an engine automation parameter. The MIDI event still\n * reaches the destination instrument; when bound, its 7-bit value is also\n * mapped into [minValue, maxValue] for `paramId`.\n */\n bindMidiCc(\n channel: number,\n controller: number,\n paramId: number,\n options: MidiCcBindOptions = {},\n ): void {\n this.nativeExt().bindMidiCc(\n channel,\n controller,\n paramId,\n options.minValue ?? 0,\n options.maxValue ?? 1,\n );\n }\n\n clearMidiCcBindings(): void {\n this.nativeExt().clearMidiCcBindings();\n }\n\n midiCcBindingCount(): number {\n return this.nativeExt().midiCcBindingCount();\n }\n\n /** Install/replace a live non-destructive MIDI-FX insert for one destination. */\n setMidiFx(destinationId: number, configJson: string): void {\n this.nativeExt().setMidiFx(destinationId, configJson);\n }\n\n clearMidiFx(destinationId = 0): void {\n this.nativeExt().clearMidiFx(destinationId);\n }\n\n /** Enable the engine-owned live MIDI input source for a destination. */\n setMidiInputSource(destinationId = 0): void {\n this.nativeExt().setMidiInputSource(destinationId);\n }\n\n clearMidiInputSource(): void {\n this.nativeExt().clearMidiInputSource();\n }\n\n midiInputPendingCount(): number {\n return this.nativeExt().midiInputPendingCount();\n }\n\n pushMidiInputNoteOn(\n group: number,\n channel: number,\n note: number,\n velocity: number,\n portTimeSamples = 0,\n ): void {\n this.nativeExt().pushMidiInputNoteOn(group, channel, note, velocity, portTimeSamples);\n }\n\n pushMidiInputNoteOff(\n group: number,\n channel: number,\n note: number,\n velocity = 0,\n portTimeSamples = 0,\n ): void {\n this.nativeExt().pushMidiInputNoteOff(group, channel, note, velocity, portTimeSamples);\n }\n\n pushMidiInputCc(\n group: number,\n channel: number,\n controller: number,\n value: number,\n portTimeSamples = 0,\n ): void {\n this.nativeExt().pushMidiInputCc(group, channel, controller, value, portTimeSamples);\n }\n\n pushMidiNoteOn(\n destinationId: number,\n group: number,\n channel: number,\n note: number,\n velocity: number,\n renderFrame = -1,\n ): void {\n this.nativeExt().pushMidiNoteOn(destinationId, group, channel, note, velocity, renderFrame);\n }\n\n pushMidiNoteOff(\n destinationId: number,\n group: number,\n channel: number,\n note: number,\n velocity = 0,\n renderFrame = -1,\n ): void {\n this.nativeExt().pushMidiNoteOff(destinationId, group, channel, note, velocity, renderFrame);\n }\n\n /**\n * Queue an immediate (live) MIDI control change to a MIDI destination\n * (engine kMidiCcImmediate). `group`/`channel` are 0..15; `controller`/`value`\n * are 7-bit (0..127). `renderFrame` is the frame to fire at, or -1 for\n * immediate. Mirrors the Node/Python/C-ABI `pushMidiCc`.\n */\n pushMidiCc(\n destinationId: number,\n group: number,\n channel: number,\n controller: number,\n value: number,\n renderFrame = -1,\n ): void {\n this.nativeExt().pushMidiCc(destinationId, group, channel, controller, value, renderFrame);\n }\n\n /**\n * Queue a MIDI panic (all-notes-off) releasing every sounding note at\n * `renderFrame` (-1 = immediate). Mirrors the C-ABI `pushMidiPanic`.\n */\n pushMidiPanic(renderFrame = -1): void {\n this.nativeExt().pushMidiPanic(renderFrame);\n }\n\n /**\n * Remove all registered parameters (and their automation lanes). Control-thread\n * only; not realtime-safe. Mirrors the C-ABI `clearParameters`.\n */\n clearParameters(): void {\n this.nativeExt().clearParameters();\n }\n\n /** Read back the current transport state snapshot. */\n getTransportState(): EngineTransportState {\n return this.native.getTransportState();\n }\n\n play(renderFrame = -1): void {\n this.native.play(renderFrame);\n }\n\n stop(renderFrame = -1): void {\n this.native.stop(renderFrame);\n }\n\n seekSample(timelineSample: number, renderFrame = -1): void {\n this.native.seekSample(timelineSample, renderFrame);\n }\n\n seekPpq(ppq: number, renderFrame = -1): void {\n this.native.seekPpq(ppq, renderFrame);\n }\n\n setTempo(bpm: number): void {\n this.native.setTempo(bpm);\n }\n\n setTimeSignature(numerator: number, denominator: number): void {\n this.native.setTimeSignature(numerator, denominator);\n }\n\n setLoop(startPpq: number, endPpq: number, enabled = true): void {\n this.native.setLoop(startPpq, endPpq, enabled);\n }\n\n addParameter(info: EngineParameterInfo): void {\n this.native.addParameter(info);\n }\n\n parameterCount(): number {\n return this.native.parameterCount();\n }\n\n parameterInfoByIndex(index: number): EngineParameterInfo {\n return this.native.parameterInfoByIndex(index);\n }\n\n parameterInfo(id: number): EngineParameterInfo {\n return this.native.parameterInfo(id);\n }\n\n setAutomationLane(paramId: number, points: EngineAutomationPoint[]): void {\n this.native.setAutomationLane(paramId, points);\n }\n\n automationLaneCount(): number {\n return this.native.automationLaneCount();\n }\n\n setMarkers(markers: EngineMarker[]): void {\n this.native.setMarkers(markers);\n }\n\n markerCount(): number {\n return this.native.markerCount();\n }\n\n markerByIndex(index: number): EngineMarker {\n return this.native.markerByIndex(index);\n }\n\n marker(id: number): EngineMarker {\n return this.native.marker(id);\n }\n\n seekMarker(markerId: number, renderFrame = -1): void {\n this.native.seekMarker(markerId, renderFrame);\n }\n\n setLoopFromMarkers(startMarkerId: number, endMarkerId: number): void {\n this.native.setLoopFromMarkers(startMarkerId, endMarkerId);\n }\n\n setMetronome(config: EngineMetronomeConfig): void {\n this.native.setMetronome(config);\n }\n\n metronome(): Required<EngineMetronomeConfig> {\n return this.native.metronome();\n }\n\n countInEndSample(startSample: number, bars: number): number {\n return Number(this.native.countInEndSample(startSample, bars));\n }\n\n setGraph(spec: EngineGraphSpec): void {\n this.native.setGraph(spec);\n }\n\n graphNodeCount(): number {\n return this.native.graphNodeCount();\n }\n\n graphConnectionCount(): number {\n return this.native.graphConnectionCount();\n }\n\n setClips(clips: EngineClip[]): void {\n this.native.setClips(\n clips.map((clip) => ({\n ...clip,\n pageProvider:\n typeof clip.pageProvider === 'object' && clip.pageProvider !== null\n ? clip.pageProvider.id\n : clip.pageProvider,\n })),\n );\n }\n\n clipCount(): number {\n return this.native.clipCount();\n }\n\n createClipPageProvider(\n numChannels: number,\n numSamples: number,\n pageFrames: number,\n ): ClipPageProvider {\n const id = this.nativeExt().createClipPageProvider(numChannels, numSamples, pageFrames);\n return new ClipPageProvider(this, id);\n }\n\n supplyClipPage(providerId: number, pageIndex: number, channels: Float32Array[]): void {\n this.nativeExt().supplyClipPage(providerId, pageIndex, channels);\n }\n\n clearClipPage(providerId: number, pageIndex: number): void {\n this.nativeExt().clearClipPage(providerId, pageIndex);\n }\n\n destroyClipPageProvider(providerId: number): void {\n this.nativeExt().destroyClipPageProvider(providerId);\n }\n\n popClipPageRequest(): ClipPageRequest | null {\n return this.nativeExt().popClipPageRequest();\n }\n\n setCaptureBuffer(numChannels: number, capacityFrames: number): void {\n this.native.setCaptureBuffer(numChannels, capacityFrames);\n }\n\n armCapture(armed = true): void {\n this.native.armCapture(armed);\n }\n\n setCapturePunch(startSample: number, endSample: number, enabled = true): void {\n this.native.setCapturePunch(startSample, endSample, enabled);\n }\n\n setCaptureSource(source: EngineCaptureStatus['source']): void {\n this.native.setCaptureSource(source);\n }\n\n setRecordOffsetSamples(offsetSamples: number): void {\n this.native.setRecordOffsetSamples(offsetSamples);\n }\n\n setInputMonitor(enabled: boolean, gain = 1): void {\n this.native.setInputMonitor(enabled, gain);\n }\n\n resetCapture(): void {\n this.native.resetCapture();\n }\n\n captureStatus(): EngineCaptureStatus {\n return this.native.captureStatus();\n }\n\n capturedAudio(): Float32Array[] {\n return this.native.capturedAudio();\n }\n\n process(channels: Float32Array[]): Float32Array[] {\n return this.native.process(channels);\n }\n\n /**\n * Allocates persistent per-channel WASM-heap scratch for the zero-copy\n * `getChannelBuffer` / `processPrepared` realtime path. Call once (off the\n * audio thread) before driving `processPrepared` from an AudioWorklet so the\n * render callback never allocates on the C++/JS heap.\n */\n prepareChannels(numChannels: number, maxFrames: number): void {\n this.native.prepareChannels(numChannels, maxFrames);\n }\n\n /**\n * Returns a Float32Array view onto the persistent WASM-heap scratch for one\n * channel (valid for up to `numFrames`). Fill it, call `processPrepared`, then\n * read the same view back. Re-acquire after WASM memory growth.\n */\n getChannelBuffer(channel: number, numFrames: number): Float32Array {\n return this.native.getChannelBuffer(channel, numFrames);\n }\n\n /**\n * Runs the engine in place over the prepared per-channel scratch buffers.\n * Allocation-free: safe to call on the AudioWorklet render thread after\n * `prepareChannels`.\n */\n processPrepared(numFrames: number): void {\n this.native.processPrepared(numFrames);\n }\n\n processWithMonitor(channels: Float32Array[]): WasmEngineProcessWithMonitorResult {\n return this.native.processWithMonitor(channels);\n }\n\n renderOffline(channels: Float32Array[], blockSize = 128): Float32Array[] {\n return this.native.renderOffline(channels, blockSize);\n }\n\n bounceOffline(options: EngineBounceOptions): EngineBounceResult {\n return this.native.bounceOffline(options);\n }\n\n freezeOffline(options: EngineFreezeOptions): EngineFreezeResult {\n return this.native.freezeOffline(options);\n }\n\n drainTelemetry(maxRecords = 1024): EngineTelemetry[] {\n return this.native.drainTelemetry(maxRecords);\n }\n\n drainMeterTelemetry(maxRecords = 1024): EngineMeterTelemetry[] {\n return this.native.drainMeterTelemetry(maxRecords);\n }\n\n destroy(): void {\n this.native.delete();\n }\n}\n\nexport class ClipPageProvider {\n private disposed = false;\n\n constructor(\n private readonly engine: RealtimeEngine,\n readonly id: number,\n ) {}\n\n supply(pageIndex: number, channels: Float32Array[]): void {\n if (this.disposed) {\n throw new Error('ClipPageProvider is destroyed');\n }\n this.engine.supplyClipPage(this.id, pageIndex, channels);\n }\n\n clear(pageIndex: number): void {\n if (this.disposed) {\n return;\n }\n this.engine.clearClipPage(this.id, pageIndex);\n }\n\n destroy(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.engine.destroyClipPageProvider(this.id);\n }\n}\n","/**\n * sonare - Audio Analysis Library\n *\n * @example\n * ```typescript\n * import { init, detectBpm, detectKey, analyze } from '@libraz/libsonare';\n *\n * await init();\n *\n * // Detect BPM from audio samples\n * const bpm = detectBpm(samples, sampleRate);\n *\n * // Detect musical key\n * const key = detectKey(samples, sampleRate);\n *\n * // Full analysis\n * const result = analyze(samples, sampleRate);\n * ```\n */\n\nimport { setSonareModule } from './module_state';\nimport type { RealtimeVoiceChangerPodConfig, VoicePresetId } from './public_types';\nimport type {\n SonareModule,\n WasmDecomposeResult,\n WasmHpssWithResidualResult,\n WasmMatrix2dResult,\n} from './sonare.js';\n\nexport type { BrowserAudioDecodeOptions } from './audio';\nexport { Audio } from './audio';\nexport type {\n CompressorDetector,\n CompressorOptions,\n DeclickOptions,\n DeclipOptions,\n DecrackleMode,\n DecrackleOptions,\n DehumOptions,\n DenoiseClassicalMode,\n DenoiseClassicalNoiseEstimator,\n DenoiseClassicalOptions,\n DereverbClassicalOptions,\n DynamicsResult,\n GateOptions,\n TransientShaperOptions,\n TrimSilenceMode,\n TrimSilenceOptions,\n VoiceChangeOptions,\n VoiceChangeRealtimeOptions,\n} from './effects_mastering';\nexport {\n harmonic,\n hpss,\n masterAudio,\n masterAudioStereo,\n masterAudioStereoWithProgress,\n masterAudioWithProgress,\n mastering,\n masteringAssistantSuggest,\n masteringAudioProfile,\n masteringChain,\n masteringChainStereo,\n masteringChainStereoWithProgress,\n masteringChainWithProgress,\n masteringDynamicsCompressor,\n masteringDynamicsGate,\n masteringDynamicsTransientShaper,\n masteringInsertNames,\n masteringPairAnalysisNames,\n masteringPairAnalyze,\n masteringPairProcess,\n masteringPairProcessorNames,\n masteringPresetNames,\n masteringProcess,\n masteringProcessorNames,\n masteringProcessStereo,\n masteringRepairDeclick,\n masteringRepairDeclip,\n masteringRepairDecrackle,\n masteringRepairDehum,\n masteringRepairDenoiseClassical,\n masteringRepairDereverbClassical,\n masteringRepairTrimSilence,\n masteringStereoAnalysisNames,\n masteringStereoAnalyze,\n masteringStreamingPreview,\n mixingScenePresetJson,\n mixingScenePresetNames,\n mixStereo,\n normalize,\n noteStretch,\n percussive,\n pitchCorrectToMidi,\n pitchCorrectToMidiTimevarying,\n pitchShift,\n timeStretch,\n voiceChange,\n voiceChangeRealtime,\n} from './effects_mastering';\nexport type { MelodyOptions } from './feature_music';\nexport {\n amplitudeToDb,\n analyzeMelody,\n analyzeSections,\n bassChroma,\n chroma,\n chromaCens,\n cqt,\n cyclicTempogram,\n dbToAmplitude,\n dbToPower,\n decompose,\n decomposeWithInit,\n deemphasis,\n ebur128LoudnessRange,\n estimateTuning,\n fixFrames,\n fixLength,\n fourierTempogram,\n frameSignal,\n framesToSamples,\n framesToTime,\n hpssWithResidual,\n hybridCqt,\n hzToMel,\n hzToMidi,\n hzToNote,\n lufs,\n lufsInterleaved,\n melSpectrogram,\n melToAudio,\n melToHz,\n melToStft,\n mfcc,\n mfccToAudio,\n mfccToMel,\n midiToHz,\n momentaryLufs,\n nnFilter,\n nnlsChroma,\n noteToHz,\n onsetEnvelope,\n onsetStrengthMulti,\n padCenter,\n pcen,\n peakPick,\n phaseVocoder,\n pitchPyin,\n pitchTuning,\n pitchYin,\n plp,\n polyFeatures,\n powerToDb,\n preemphasis,\n pseudoCqt,\n remix,\n resample,\n rmsEnergy,\n samplesToFrames,\n shortTermLufs,\n spectralBandwidth,\n spectralCentroid,\n spectralContrast,\n spectralFlatness,\n spectralRolloff,\n splitSilence,\n stft,\n stftDb,\n tempogram,\n tempogramRatio,\n timeToFrames,\n tonnetz,\n trim,\n trimSilence,\n vectorNormalize,\n vqt,\n zeroCrossingRate,\n zeroCrossings,\n} from './features';\nexport type { BindMicrophoneInputOptions, MicrophoneInputBinding } from './live_audio';\nexport { bindMicrophoneInput } from './live_audio';\nexport type {\n ClippingRegion,\n ClippingReport,\n DynamicRangeReport,\n MeteringDetectClippingOptions,\n MeteringDynamicRangeOptions,\n PhaseScopeReport,\n SpectrumOptions,\n SpectrumReport,\n VectorscopeReport,\n WaveformPeakPyramidOptions,\n WaveformPeaksOptions,\n WaveformPeaksReport,\n} from './metering';\nexport {\n meteringCrestFactorDb,\n meteringDcOffset,\n meteringDetectClipping,\n meteringDynamicRange,\n meteringPeakDb,\n meteringPhaseScope,\n meteringPhaseScopeDecimated,\n meteringRmsDb,\n meteringSpectrum,\n meteringSpectrumFrame,\n meteringStereoCorrelation,\n meteringStereoWidth,\n meteringTruePeakDb,\n meteringVectorscope,\n meteringVectorscopeDecimated,\n waveformPeakPyramid,\n waveformPeaks,\n} from './metering';\nexport type {\n OpfsClipPageProviderBinding,\n OpfsClipPageProviderOptions,\n} from './opfs_clip_pages';\nexport {\n createOpfsClipPageProvider,\n createOpfsClipPageWorker,\n opfsClipPageWorkerSource,\n} from './opfs_clip_pages';\nexport type {\n BuiltinSynthBinding,\n BuiltinSynthConfig,\n BuiltinSynthWaveform,\n MidiCcLearnOptions,\n ProjectAssistSidecar,\n ProjectAutomationCurve,\n ProjectAutomationLaneDesc,\n ProjectAutomationPoint,\n ProjectBounceOptions,\n ProjectChordSymbol,\n ProjectClipCompSegment,\n ProjectClipDesc,\n ProjectClipFade,\n ProjectClipTake,\n ProjectCompileResult,\n ProjectFadeCurve,\n ProjectKeySegment,\n ProjectLoopMode,\n ProjectLoopRecordingDesc,\n ProjectLoopRecordingResult,\n ProjectMidiClipResult,\n ProjectMidiEvent,\n ProjectNotePairValidation,\n ProjectTrackDesc,\n ProjectTrackKind,\n ProjectWarpAnchor,\n ProjectWarpMapDesc,\n Sf2InstrumentConfig,\n Sf2ProgramStatus,\n SourceBackend,\n SynthBodyType,\n SynthEngineMode,\n SynthEnumTables,\n SynthFilterModel,\n SynthFilterOutput,\n SynthModDestination,\n SynthModRouting,\n SynthModSource,\n SynthOscWaveform,\n SynthPatch,\n} from './project';\nexport {\n EXPECTED_PROJECT_ABI_VERSION,\n Project,\n projectAbiVersion,\n SYNTH_BODY_TYPES,\n SYNTH_ENGINE_MODES,\n SYNTH_FILTER_MODELS,\n SYNTH_FILTER_OUTPUTS,\n SYNTH_MOD_DESTINATIONS,\n SYNTH_MOD_SOURCES,\n SYNTH_OSC_WAVEFORMS,\n synthEnumTables,\n synthPresetNames,\n synthPresetPatch,\n} from './project';\nexport type {\n AcousticOptions,\n AcousticResult,\n AnalysisResult,\n AnalyzeBpmOptions,\n AnalyzeDynamicsOptions,\n AnalyzeRhythmOptions,\n AnalyzeSectionsOptions,\n AnalyzeTimbreOptions,\n AutomationCurve,\n Beat,\n Chord,\n ChordAnalysisResult,\n ChordDetectionOptions,\n ChromaResult,\n CqtResult,\n Dynamics,\n EqBand,\n EqBandPhase,\n EqBandType,\n EqCoeffMode,\n EqMatchOptions,\n EqSpectrumSnapshot,\n EqStereoPlacement,\n GoniometerPoint,\n HpssResult,\n Key,\n KeyCandidate,\n KeyDetectionOptions,\n KeyProfileName,\n LufsResult,\n MasteringChainConfig,\n MasteringChainResult,\n MasteringOptions,\n MasteringPreset,\n MasteringProcessorParams,\n MasteringResult,\n MasteringStereoChainResult,\n MasteringStereoResult,\n MelodyPoint,\n MelodyResult,\n MelPowerResult,\n MelSpectrogramResult,\n MeterTap,\n MfccResult,\n MixerProcessResult,\n MixMeterSnapshot,\n MixOptions,\n MixResult,\n NoteStretchOptions,\n PairAnalysis,\n PairProcessor,\n PanLaw,\n PanMode,\n PitchResult,\n RealtimeVoiceChangerConfigInput,\n RealtimeVoiceChangerPodConfig,\n RhythmFeatures,\n RirResult,\n RirSynthOptions,\n RoomEstimateOptions,\n RoomEstimateResult,\n RoomGeometryOptions,\n RoomMorphOptions,\n Section,\n SendTiming,\n SoloProcessor,\n StereoAnalysis,\n StftPowerResult,\n StftResult,\n StreamingEqualizerConfig,\n StreamingMasteringChainConfig,\n StreamingPlatform,\n StreamingRetuneConfig,\n TempogramMode,\n Timbre,\n TimeSignature,\n VoicePresetId,\n} from './public_types';\nexport {\n ChordQuality,\n KeyProfile,\n Mode,\n PitchClass,\n SectionType,\n} from './public_types';\nexport type {\n BpmAnalysisResult,\n BpmCandidate,\n DynamicsAnalysisResult,\n RhythmAnalysisResult,\n TimbreAnalysisResult,\n TimbreFrame,\n} from './quick_analysis';\nexport {\n analyze,\n analyzeBpm,\n analyzeDynamics,\n analyzeImpulseResponse,\n analyzeRhythm,\n analyzeTimbre,\n analyzeWithProgress,\n chordFunctionalAnalysis,\n detectAcoustic,\n detectBeats,\n detectBpm,\n detectChords,\n detectDownbeats,\n detectKey,\n detectKeyCandidates,\n detectOnsets,\n estimateRoom,\n hasFfmpegSupport,\n roomMorph,\n synthesizeRir,\n} from './quick_analysis';\nexport type {\n EngineAutomationPoint,\n EngineBounceOptions,\n EngineBounceResult,\n EngineCapabilities,\n EngineCaptureStatus,\n EngineClip,\n EngineFreezeOptions,\n EngineFreezeResult,\n EngineGraphSpec,\n EngineMarker,\n EngineMeterTelemetry,\n EngineMetronomeConfig,\n EngineParameterInfo,\n EngineTelemetry,\n EngineTransportState,\n MidiCcBindOptions,\n} from './realtime_engine';\nexport { EXPECTED_ENGINE_ABI_VERSION, engineCapabilities, RealtimeEngine } from './realtime_engine';\nexport { scaleCorrectionSemitones, scalePitchClassEnabled, scaleQuantizeMidi } from './scale';\nexport type { ProgressCallback } from './sonare.js';\nexport { StreamAnalyzer, streamAnalyzerConfigDefaults } from './stream_analyzer';\nexport type {\n AnalyzerStats,\n BarChord,\n ChordChange,\n FrameBuffer,\n PatternScore,\n ProgressiveEstimate,\n StreamConfig,\n StreamConfigDefaults,\n StreamFramesI16,\n StreamFramesU8,\n StreamQuantizeConfig,\n} from './stream_types';\nexport type {\n MixerRealtimeBuffer,\n RealtimeVoiceChangerInterleavedBuffer,\n RealtimeVoiceChangerMonoBuffer,\n RealtimeVoiceChangerPlanarBuffer,\n} from './streaming_mixing';\nexport {\n Mixer,\n RealtimeVoiceChanger,\n realtimeVoiceChangerPresetJson,\n realtimeVoiceChangerPresetNames,\n StreamingEqualizer,\n StreamingMasteringChain,\n StreamingRetune,\n validateRealtimeVoiceChangerPresetJson,\n} from './streaming_mixing';\nexport type { ValidateOptions } from './validation';\nexport type {\n BindWebMidiOptions,\n WebMidiBinding,\n WebMidiCcBinding,\n WebMidiInputInfo,\n} from './web_midi';\nexport { bindWebMidi, isWebMidiAvailable } from './web_midi';\n\n/** Row-major 2-D matrix as a flat buffer plus its dimensions. */\nexport type Matrix2dResult = WasmMatrix2dResult;\n/** NMF factor matrices { w, h } from {@link decompose}. */\nexport type DecomposeResult = WasmDecomposeResult;\n/** Harmonic / percussive / residual signals from {@link hpssWithResidual}. */\nexport type HpssWithResidualResult = WasmHpssWithResidualResult;\n\n// ============================================================================\n// Module State\n// ============================================================================\n\nlet module: SonareModule | null = null;\nlet initPromise: Promise<void> | null = null;\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the WASM module.\n * Must be called before using any analysis functions.\n *\n * @param options - Optional module configuration\n * @returns Promise that resolves when initialization is complete\n */\nexport async function init(options?: {\n locateFile?: (path: string, prefix: string) => string;\n}): Promise<void> {\n if (module) {\n return;\n }\n\n if (initPromise) {\n return initPromise;\n }\n\n initPromise = (async () => {\n try {\n const createModule = (await import('./sonare.js')).default;\n module = await createModule(options);\n setSonareModule(module);\n } catch (error) {\n initPromise = null;\n throw error;\n }\n })();\n\n return initPromise;\n}\n\n/**\n * Check if the module is initialized.\n */\nexport function isInitialized(): boolean {\n return module !== null;\n}\n\n/**\n * Get the library version.\n */\nexport function version(): string {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.version();\n}\n\nexport function engineAbiVersion(): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.engineAbiVersion();\n}\n\nexport function voiceChangerAbiVersion(): number {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.voiceChangerAbiVersion();\n}\n\n// Canonical ordinal order of the built-in voice-character presets, matching the\n// C ABI SonareVoiceCharacterPreset enum and SONARE_REALTIME_VOICE_CHANGER_PRESET_IDS.\nconst VOICE_PRESET_ORDINALS: readonly VoicePresetId[] = [\n 'neutral-monitor',\n 'bright-idol',\n 'soft-whisper',\n 'deep-narrator',\n 'robot-mascot',\n 'dark-villain',\n];\n\nfunction resolveVoicePresetOrdinal(preset: VoicePresetId | number): number {\n if (typeof preset === 'number') {\n return preset;\n }\n const ordinal = VOICE_PRESET_ORDINALS.indexOf(preset);\n if (ordinal < 0) {\n throw new Error(`Unknown voice character preset: ${preset}`);\n }\n return ordinal;\n}\n\n/**\n * Map a voice-character preset ordinal (or canonical id) to its canonical id\n * string (e.g. `'bright-idol'`). Returns `null` for an out-of-range ordinal.\n */\nexport function voiceCharacterPresetId(preset: VoicePresetId | number): string | null {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.voiceCharacterPresetId(resolveVoicePresetOrdinal(preset));\n}\n\n/**\n * Return the canonical (normalized) flat POD config for a built-in voice\n * preset, skipping the JSON round-trip. Accepts a canonical preset id or its\n * integer ordinal. Returns `null` for an out-of-range ordinal.\n */\nexport function realtimeVoiceChangerPresetConfig(\n preset: VoicePresetId | number,\n): RealtimeVoiceChangerPodConfig | null {\n if (!module) {\n throw new Error('Module not initialized. Call init() first.');\n }\n return module.realtimeVoiceChangerPresetConfig(resolveVoicePresetOrdinal(preset));\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport { PitchClass as Pitch } from './public_types';\n","import type {\n EngineAutomationPoint,\n EngineClip,\n EngineMarker,\n EngineMeterTelemetry,\n EngineMetronomeConfig,\n EngineParameterInfo,\n EngineTelemetry,\n MixerRealtimeBuffer,\n RealtimeVoiceChangerConfigInput,\n} from './index';\nimport { engineCapabilities, Mixer, RealtimeEngine, RealtimeVoiceChanger } from './index';\nimport type { AutomationCurve } from './public_types';\nimport type { SonareRtModule } from './sonare-rt';\n\n// With code-splitting disabled, the worklet bundle carries its own copy of the\n// module singleton (a real AudioWorkletGlobalScope cannot resolve sibling\n// chunks, so the bundle must be self-contained). Re-export the lifecycle so that\n// realm can initialize its own wasm instance, independent of the main-thread\n// `index` module.\nexport { init, isInitialized } from './index';\n\nexport interface SonareWorkletProcessorOptions {\n sceneJson: string;\n sampleRate?: number;\n blockSize?: number;\n stripCount?: number;\n meterIntervalFrames?: number;\n meterSharedBuffer?: SharedArrayBuffer;\n meterRingCapacity?: number;\n spectrumIntervalFrames?: number;\n spectrumBands?: number;\n spectrumSharedBuffer?: SharedArrayBuffer;\n spectrumRingCapacity?: number;\n}\n\nexport interface SonareRealtimeEngineWorkletProcessorOptions {\n runtimeTarget?: 'embind' | 'sonare-rt';\n rtModuleUrl?: string;\n rtWasmBinary?: ArrayBuffer | Uint8Array;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n meterIntervalFrames?: number;\n commandSharedBuffer?: SharedArrayBuffer;\n commandRingCapacity?: number;\n telemetrySharedBuffer?: SharedArrayBuffer;\n telemetryRingCapacity?: number;\n meterSharedBuffer?: SharedArrayBuffer;\n meterRingCapacity?: number;\n}\n\nexport interface SonareRealtimeVoiceChangerWorkletProcessorOptions {\n preset?: RealtimeVoiceChangerConfigInput;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n}\n\nexport interface SonareRealtimeVoiceChangerSetConfigMessage {\n type: 'setConfig';\n preset: RealtimeVoiceChangerConfigInput;\n}\n\nexport interface SonareRealtimeVoiceChangerResetMessage {\n type: 'reset';\n}\n\nexport interface SonareRealtimeVoiceChangerDestroyMessage {\n type: 'destroy';\n}\n\nexport type SonareRealtimeVoiceChangerMessage =\n | SonareRealtimeVoiceChangerSetConfigMessage\n | SonareRealtimeVoiceChangerResetMessage\n | SonareRealtimeVoiceChangerDestroyMessage;\n\nexport interface SonareRealtimeEngineNodeCapabilities {\n mode: 'sab' | 'postMessage';\n runtimeTarget: 'embind' | 'sonare-rt';\n sharedArrayBuffer: boolean;\n atomics: boolean;\n audioWorklet: boolean;\n engineAbiVersion?: number;\n expectedEngineAbiVersion?: number;\n abiCompatible?: boolean;\n degradedReason?: string;\n}\n\nexport interface SonareRealtimeEngineNodeOptions\n extends SonareRealtimeEngineWorkletProcessorOptions {\n processorName?: string;\n moduleUrl?: string | URL;\n rtModuleUrl?: string;\n mode?: 'auto' | 'sab' | 'postMessage';\n engineAbiVersion?: number;\n expectedEngineAbiVersion?: number;\n requireAbiCompatible?: boolean;\n nodeFactory?: (\n context: BaseAudioContext,\n processorName: string,\n options: AudioWorkletNodeOptions,\n ) => AudioWorkletNode;\n}\n\nexport interface SonareRtRealtimeEngineRuntimeOptions {\n module: SonareRtModule;\n memory: WebAssembly.Memory;\n sampleRate?: number;\n blockSize?: number;\n channelCount?: number;\n commandSharedBuffer?: SharedArrayBuffer;\n commandRingCapacity?: number;\n telemetrySharedBuffer?: SharedArrayBuffer;\n telemetryRingCapacity?: number;\n}\n\nexport interface SonareEngineOptions extends SonareRealtimeEngineNodeOptions {\n offlineEngine?: RealtimeEngine;\n offlineBlockSize?: number;\n offlineChannelCount?: number;\n}\n\nexport interface SonareEngineTransportFacade {\n play(sampleTime?: number): boolean;\n stop(sampleTime?: number): boolean;\n seekPpq(ppq: number, sampleTime?: number): boolean;\n seekSeconds(seconds: number, sampleTime?: number): boolean;\n setTempo(bpm: number): void;\n setLoop(startPpq: number, endPpq: number, enabled?: boolean): boolean;\n}\n\ntype SuspendableAudioContext = BaseAudioContext & {\n suspend?: () => Promise<void>;\n resume?: () => Promise<void>;\n};\n\ntype WorkletInput = readonly (readonly Float32Array[])[];\ntype WorkletOutput = Float32Array[][];\n\nexport interface SonareWorkletScheduleInsertAutomationMessage {\n type: 'scheduleInsertAutomation';\n stripIndex: number;\n insertIndex: number;\n paramId: number;\n value: number;\n samplePos?: number;\n curve?: AutomationCurve;\n}\n\nexport interface SonareWorkletSetMeterIntervalMessage {\n type: 'setMeterInterval';\n frames: number;\n}\n\nexport interface SonareWorkletDestroyMessage {\n type: 'destroy';\n}\n\nexport type SonareWorkletMessage =\n | SonareWorkletScheduleInsertAutomationMessage\n | SonareWorkletSetMeterIntervalMessage\n | SonareWorkletDestroyMessage;\n\nexport interface SonareWorkletMeterSnapshot {\n type: 'meter';\n frame: number;\n peakDbL: number;\n peakDbR: number;\n rmsDbL: number;\n rmsDbR: number;\n correlation: number;\n}\n\nexport interface SonareWorkletSpectrumSnapshot {\n type: 'spectrum';\n frame: number;\n bands: Float32Array;\n}\n\nexport type SonareWorkletTransportMessage =\n | SonareWorkletMeterSnapshot\n | SonareWorkletSpectrumSnapshot\n | SonareEngineTelemetryRecord;\n\nexport const SONARE_METER_RING_HEADER_INTS = 4;\n// Record layout: [frameLo, peakDbL, peakDbR, rmsDbL, rmsDbR, correlation, frameHi].\n// The sample-frame index is monotonically increasing and quickly exceeds the\n// 2^24 exact-integer range of a single Float32 slot (~349 s at 48 kHz), so it is\n// stored split across two Float32 lanes (low 24 bits + high bits) for exact\n// reconstruction. See encodeFrameLo/encodeFrameHi/decodeFrame.\nexport const SONARE_METER_RING_RECORD_FLOATS = 7;\nexport const SONARE_SPECTRUM_RING_HEADER_INTS = 5;\n\n/** Base for splitting a frame index into two exactly-representable Float32 lanes. */\nconst SONARE_FRAME_LANE_BASE = 0x1000000; // 2^24\n\n/** Low 24 bits of a frame index (exact in Float32). */\nexport function encodeFrameLo(frame: number): number {\n const f = Math.max(0, Math.floor(frame));\n return f % SONARE_FRAME_LANE_BASE;\n}\n\n/** High bits of a frame index above 2^24 (exact in Float32 up to ~2^48). */\nexport function encodeFrameHi(frame: number): number {\n const f = Math.max(0, Math.floor(frame));\n return Math.floor(f / SONARE_FRAME_LANE_BASE);\n}\n\n/** Reconstruct a frame index from its low/high Float32 lanes. */\nexport function decodeFrame(lo: number, hi: number): number {\n return hi * SONARE_FRAME_LANE_BASE + lo;\n}\nexport const SONARE_ENGINE_RING_HEADER_INTS = 5;\nexport const SONARE_ENGINE_COMMAND_RECORD_BYTES = 32;\nexport const SONARE_ENGINE_TELEMETRY_RECORD_BYTES = 48;\n\nexport enum SonareEngineCommandType {\n SetParam = 0,\n SetParamSmoothed = 1,\n TransportPlay = 2,\n TransportStop = 3,\n TransportSeekSample = 4,\n TransportSeekPpq = 5,\n SetTempoMap = 6,\n SetLoop = 7,\n SwapGraph = 8,\n SwapAutomation = 9,\n SetSoloMute = 10,\n AddClip = 11,\n RemoveClip = 12,\n ArmRecord = 13,\n Punch = 14,\n SetMetronome = 15,\n SetMarker = 16,\n SeekMarker = 17,\n}\n\nexport enum SonareEngineTelemetryType {\n ProcessBlock = 0,\n Error = 1,\n}\n\nexport enum SonareEngineTelemetryError {\n None = 0,\n CommandQueueOverflow = 1,\n PendingCommandOverflow = 2,\n BoundaryOverflow = 3,\n TelemetryOverflow = 4,\n CaptureOverflow = 5,\n MaxBlockExceeded = 6,\n UnknownTarget = 7,\n NonRealtimeSafeParameter = 8,\n NotPrepared = 9,\n NonQueueableCommand = 10,\n AutomationBindTargetOverflow = 11,\n StaleAutomationLanes = 12,\n SmoothedParameterCapacity = 13,\n}\n\ninterface WorkletTransport {\n postMessage?: (message: SonareWorkletTransportMessage) => void;\n onMeter?: (meter: SonareWorkletMeterSnapshot) => void;\n onSpectrum?: (spectrum: SonareWorkletSpectrumSnapshot) => void;\n}\n\ninterface ResolvedMetronomeConfig {\n beatGain: number;\n accentGain: number;\n clickSamples: number;\n}\n\n// Fallback metronome gains/click length used by the worklet consumer until the\n// host posts a 'syncMetronome' config. Aligned with the embind setMetronome\n// defaults (src/wasm/bindings.cpp) so offline and realtime metronomes match.\nconst DEFAULT_METRONOME_CONFIG: ResolvedMetronomeConfig = {\n beatGain: 0.35,\n accentGain: 0.7,\n clickSamples: 96,\n};\n\nfunction resolveMetronomeConfig(config: EngineMetronomeConfig): ResolvedMetronomeConfig {\n return {\n beatGain: config.beatGain ?? DEFAULT_METRONOME_CONFIG.beatGain,\n accentGain: config.accentGain ?? DEFAULT_METRONOME_CONFIG.accentGain,\n clickSamples: config.clickSamples ?? DEFAULT_METRONOME_CONFIG.clickSamples,\n };\n}\n\nexport interface SonareMeterRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n}\n\nexport interface SonareMeterRingReadResult {\n nextReadIndex: number;\n meters: SonareWorkletMeterSnapshot[];\n}\n\nexport interface SonareSpectrumRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n bands: number;\n}\n\nexport interface SonareSpectrumRingReadResult {\n nextReadIndex: number;\n spectra: SonareWorkletSpectrumSnapshot[];\n}\n\nexport interface SonareEngineCommandRecord {\n type: SonareEngineCommandType | number;\n targetId?: number;\n sampleTime?: number | bigint;\n argFloat?: number;\n argInt?: number | bigint;\n}\n\n// Out-of-band control messages posted from the main-thread SonareEngine facade\n// to the worklet engine processor over node.port. Unlike SonareEngineCommandRecord\n// (a small POD POSTed/ringed every block) these carry bulk/structured payloads\n// (clip audio buffers, marker lists, metronome config) that cannot fit the\n// fixed-size SAB command record, so they are applied OUTSIDE process() — the\n// audio-thread equivalent of the engine's control-thread RtPublisher setters.\nexport interface SonareEngineSyncClipsMessage {\n type: 'syncClips';\n clips: EngineClip[];\n}\n\nexport interface SonareEngineSyncMarkersMessage {\n type: 'syncMarkers';\n markers: EngineMarker[];\n}\n\nexport interface SonareEngineSyncMetronomeMessage {\n type: 'syncMetronome';\n config: EngineMetronomeConfig;\n}\n\nexport interface SonareEngineSyncAutomationMessage {\n type: 'syncAutomation';\n paramId: number;\n points: EngineAutomationPoint[];\n}\n\nexport type SonareEngineSyncMessage =\n | SonareEngineSyncClipsMessage\n | SonareEngineSyncMarkersMessage\n | SonareEngineSyncMetronomeMessage\n | SonareEngineSyncAutomationMessage;\n\nexport interface SonareEngineTelemetryRecord {\n type: SonareEngineTelemetryType | number;\n error: SonareEngineTelemetryError | number;\n renderFrame: number;\n timelineSample: number;\n audibleTimelineSample: number;\n graphLatencySamplesQ8: number;\n value: number;\n}\n\nexport interface SonareEngineCommandRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n view: DataView;\n capacity: number;\n}\n\nexport interface SonareEngineTelemetryRingBuffer {\n sharedBuffer: SharedArrayBuffer;\n header: Int32Array;\n view: DataView;\n capacity: number;\n}\n\nexport interface SonareEngineTelemetryRingReadResult {\n nextReadIndex: number;\n telemetry: SonareEngineTelemetryRecord[];\n}\n\ninterface SharedMeterRingWriter {\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n}\n\ninterface SharedSpectrumRingWriter {\n header: Int32Array;\n records: Float32Array;\n capacity: number;\n bands: number;\n recordFloats: number;\n}\n\ninterface WorkletPort {\n postMessage?: (message: unknown) => void;\n onmessage?: (event: { data: unknown }) => void;\n addEventListener?: (type: 'message', listener: (event: { data: unknown }) => void) => void;\n start?: () => void;\n}\n\nfunction toDb(value: number): number {\n return value > 0 ? 20 * Math.log10(value) : Number.NEGATIVE_INFINITY;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isWorkletMessage(value: unknown): value is SonareWorkletMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return (\n value.type === 'scheduleInsertAutomation' ||\n value.type === 'setMeterInterval' ||\n value.type === 'destroy'\n );\n}\n\nfunction isEngineCommandRecord(value: unknown): value is SonareEngineCommandRecord {\n return isRecord(value) && typeof value.type === 'number';\n}\n\nfunction isEngineSyncMessage(value: unknown): value is SonareEngineSyncMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return (\n value.type === 'syncClips' ||\n value.type === 'syncMarkers' ||\n value.type === 'syncMetronome' ||\n value.type === 'syncAutomation'\n );\n}\n\nfunction isRealtimeVoiceChangerMessage(value: unknown): value is SonareRealtimeVoiceChangerMessage {\n if (!isRecord(value) || typeof value.type !== 'string') {\n return false;\n }\n return value.type === 'setConfig' || value.type === 'reset' || value.type === 'destroy';\n}\n\nfunction isEngineTelemetryRecord(value: unknown): value is SonareEngineTelemetryRecord {\n return (\n isRecord(value) &&\n typeof value.type === 'number' &&\n typeof value.error === 'number' &&\n typeof value.renderFrame === 'number' &&\n typeof value.timelineSample === 'number' &&\n typeof value.audibleTimelineSample === 'number' &&\n typeof value.graphLatencySamplesQ8 === 'number' &&\n typeof value.value === 'number'\n );\n}\n\nfunction isMeterSnapshot(value: unknown): value is SonareWorkletMeterSnapshot {\n return (\n isRecord(value) &&\n value.type === 'meter' &&\n typeof value.frame === 'number' &&\n typeof value.peakDbL === 'number' &&\n typeof value.peakDbR === 'number' &&\n typeof value.rmsDbL === 'number' &&\n typeof value.rmsDbR === 'number' &&\n typeof value.correlation === 'number'\n );\n}\n\nexport function sonareMeterRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_METER_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_METER_RING_RECORD_FLOATS * Float32Array.BYTES_PER_ELEMENT\n );\n}\n\nexport function createSonareMeterRingBuffer(capacity = 128): SonareMeterRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(sonareMeterRingBufferByteLength(clampedCapacity));\n const ring = meterRingFromSharedBuffer(sharedBuffer, clampedCapacity);\n Atomics.store(ring.header, 0, 0);\n Atomics.store(ring.header, 1, clampedCapacity);\n Atomics.store(ring.header, 2, SONARE_METER_RING_RECORD_FLOATS);\n Atomics.store(ring.header, 3, 0);\n return { sharedBuffer, header: ring.header, records: ring.records, capacity: ring.capacity };\n}\n\nexport function readSonareMeterRingBuffer(\n ring: SonareMeterRingBuffer,\n readIndex = 0,\n): SonareMeterRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const meters: SonareWorkletMeterSnapshot[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n const offset = (index % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n meters.push({\n type: 'meter',\n frame: decodeFrame(ring.records[offset], ring.records[offset + 6]),\n peakDbL: ring.records[offset + 1],\n peakDbR: ring.records[offset + 2],\n rmsDbL: ring.records[offset + 3],\n rmsDbR: ring.records[offset + 4],\n correlation: ring.records[offset + 5],\n });\n }\n return { nextReadIndex: writeIndex, meters };\n}\n\nexport function sonareSpectrumRingBufferByteLength(capacity: number, bands = 16): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const clampedBands = Math.max(1, Math.floor(bands));\n // Record layout: [frameLo, frameHi, bandCount, band0, band1, ...]. frame is\n // split across two Float32 lanes for exact reconstruction beyond 2^24.\n return (\n SONARE_SPECTRUM_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * (3 + clampedBands) * Float32Array.BYTES_PER_ELEMENT\n );\n}\n\nexport function createSonareSpectrumRingBuffer(\n capacity = 128,\n bands = 16,\n): SonareSpectrumRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const clampedBands = Math.max(1, Math.floor(bands));\n const sharedBuffer = new SharedArrayBuffer(\n sonareSpectrumRingBufferByteLength(clampedCapacity, clampedBands),\n );\n const ring = spectrumRingFromSharedBuffer(sharedBuffer, clampedCapacity, clampedBands);\n Atomics.store(ring.header, 0, 0);\n Atomics.store(ring.header, 1, clampedCapacity);\n Atomics.store(ring.header, 2, ring.recordFloats);\n Atomics.store(ring.header, 3, clampedBands);\n Atomics.store(ring.header, 4, 0);\n return {\n sharedBuffer,\n header: ring.header,\n records: ring.records,\n capacity: ring.capacity,\n bands: ring.bands,\n };\n}\n\nexport function readSonareSpectrumRingBuffer(\n ring: SonareSpectrumRingBuffer,\n readIndex = 0,\n): SonareSpectrumRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const recordFloats = Atomics.load(ring.header, 2) || 3 + ring.bands;\n const bands = Atomics.load(ring.header, 3) || ring.bands;\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const spectra: SonareWorkletSpectrumSnapshot[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n const offset = (index % ring.capacity) * recordFloats;\n const values = new Float32Array(bands);\n values.set(ring.records.subarray(offset + 3, offset + 3 + bands));\n spectra.push({\n type: 'spectrum',\n frame: decodeFrame(ring.records[offset], ring.records[offset + 1]),\n bands: values,\n });\n }\n return { nextReadIndex: writeIndex, spectra };\n}\n\nexport function sonareEngineCommandRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_ENGINE_COMMAND_RECORD_BYTES\n );\n}\n\nexport function sonareEngineTelemetryRingBufferByteLength(capacity: number): number {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n return (\n SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT +\n clampedCapacity * SONARE_ENGINE_TELEMETRY_RECORD_BYTES\n );\n}\n\nexport function createSonareEngineCommandRingBuffer(capacity = 128): SonareEngineCommandRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(\n sonareEngineCommandRingBufferByteLength(clampedCapacity),\n );\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n clampedCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n}\n\nexport function createSonareEngineTelemetryRingBuffer(\n capacity = 128,\n): SonareEngineTelemetryRingBuffer {\n const clampedCapacity = Math.max(1, Math.floor(capacity));\n const sharedBuffer = new SharedArrayBuffer(\n sonareEngineTelemetryRingBufferByteLength(clampedCapacity),\n );\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n clampedCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n}\n\nexport function pushSonareEngineCommandRingBuffer(\n ring: SonareEngineCommandRingBuffer,\n command: SonareEngineCommandRecord,\n): boolean {\n const writeIndex = Atomics.load(ring.header, 0);\n const readIndex = Atomics.load(ring.header, 1);\n if (writeIndex - readIndex >= ring.capacity) {\n Atomics.add(ring.header, 4, 1);\n return false;\n }\n writeEngineCommandRecord(\n ring.view,\n recordOffset(writeIndex, ring.capacity, SONARE_ENGINE_COMMAND_RECORD_BYTES),\n command,\n );\n Atomics.store(ring.header, 0, writeIndex + 1);\n return true;\n}\n\nexport function popSonareEngineCommandRingBuffer(\n ring: SonareEngineCommandRingBuffer,\n): SonareEngineCommandRecord | null {\n const readIndex = Atomics.load(ring.header, 1);\n const writeIndex = Atomics.load(ring.header, 0);\n if (readIndex >= writeIndex) {\n return null;\n }\n const command = readEngineCommandRecord(\n ring.view,\n recordOffset(readIndex, ring.capacity, SONARE_ENGINE_COMMAND_RECORD_BYTES),\n );\n Atomics.store(ring.header, 1, readIndex + 1);\n return command;\n}\n\nexport function writeSonareEngineTelemetryRingBuffer(\n ring: SonareEngineTelemetryRingBuffer,\n telemetry: SonareEngineTelemetryRecord,\n): void {\n const writeIndex = Atomics.load(ring.header, 0);\n writeEngineTelemetryRecord(\n ring.view,\n recordOffset(writeIndex, ring.capacity, SONARE_ENGINE_TELEMETRY_RECORD_BYTES),\n telemetry,\n );\n Atomics.store(ring.header, 0, writeIndex + 1);\n if (writeIndex + 1 > ring.capacity) {\n Atomics.store(ring.header, 4, writeIndex + 1 - ring.capacity);\n }\n}\n\nexport function readSonareEngineTelemetryRingBuffer(\n ring: SonareEngineTelemetryRingBuffer,\n readIndex = 0,\n): SonareEngineTelemetryRingReadResult {\n const writeIndex = Atomics.load(ring.header, 0);\n const nextReadIndex = Math.max(0, Math.min(readIndex, writeIndex));\n const firstReadable = Math.max(nextReadIndex, writeIndex - ring.capacity);\n const telemetry: SonareEngineTelemetryRecord[] = [];\n for (let index = firstReadable; index < writeIndex; index++) {\n telemetry.push(\n readEngineTelemetryRecord(\n ring.view,\n recordOffset(index, ring.capacity, SONARE_ENGINE_TELEMETRY_RECORD_BYTES),\n ),\n );\n }\n return { nextReadIndex: writeIndex, telemetry };\n}\n\nfunction meterRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n): SharedMeterRingWriter {\n const headerBytes = SONARE_METER_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_METER_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 1);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const minBytes = sonareMeterRingBufferByteLength(capacity);\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('meterSharedBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 1, capacity);\n Atomics.store(header, 2, SONARE_METER_RING_RECORD_FLOATS);\n return {\n header,\n records: new Float32Array(\n sharedBuffer,\n headerBytes,\n capacity * SONARE_METER_RING_RECORD_FLOATS,\n ),\n capacity,\n };\n}\n\nfunction spectrumRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n fallbackBands?: number,\n): SharedSpectrumRingWriter {\n const headerBytes = SONARE_SPECTRUM_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_SPECTRUM_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 1);\n const existingBands = Atomics.load(header, 3);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const bands = Math.max(1, Math.floor(existingBands || fallbackBands || 16));\n const recordFloats = 3 + bands;\n const minBytes = sonareSpectrumRingBufferByteLength(capacity, bands);\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('spectrumSharedBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 1, capacity);\n Atomics.store(header, 2, recordFloats);\n Atomics.store(header, 3, bands);\n return {\n header,\n records: new Float32Array(sharedBuffer, headerBytes, capacity * recordFloats),\n capacity,\n bands,\n recordFloats,\n };\n}\n\nfunction engineRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n recordBytes: number,\n fallbackCapacity?: number,\n): { header: Int32Array; view: DataView; capacity: number } {\n const headerBytes = SONARE_ENGINE_RING_HEADER_INTS * Int32Array.BYTES_PER_ELEMENT;\n const header = new Int32Array(sharedBuffer, 0, SONARE_ENGINE_RING_HEADER_INTS);\n const existingCapacity = Atomics.load(header, 2);\n const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));\n const minBytes = headerBytes + capacity * recordBytes;\n if (sharedBuffer.byteLength < minBytes) {\n throw new Error('engine SharedArrayBuffer is too small for the requested ring capacity.');\n }\n Atomics.store(header, 2, capacity);\n Atomics.store(header, 3, recordBytes);\n return {\n header,\n view: new DataView(sharedBuffer, headerBytes, capacity * recordBytes),\n capacity,\n };\n}\n\nfunction recordOffset(index: number, capacity: number, recordBytes: number): number {\n return (index % capacity) * recordBytes;\n}\n\nfunction toBigInt64(value: number | bigint | undefined, fallback: bigint): bigint {\n if (typeof value === 'bigint') {\n return value;\n }\n if (typeof value === 'number') {\n return BigInt(Math.trunc(value));\n }\n return fallback;\n}\n\nfunction writeEngineCommandRecord(\n view: DataView,\n offset: number,\n command: SonareEngineCommandRecord,\n): void {\n view.setUint32(offset, command.type, true);\n view.setUint32(offset + 4, command.targetId ?? 0, true);\n view.setBigInt64(offset + 8, toBigInt64(command.sampleTime, -1n), true);\n // argFloat occupies a full 8-byte Float64 slot (replacing the old Float32 +\n // 4-byte pad) so PPQ scalars carried here keep full double precision over the\n // SAB transport, matching the engine's double-precision seek/loop contract.\n view.setFloat64(offset + 16, command.argFloat ?? 0, true);\n view.setBigInt64(offset + 24, toBigInt64(command.argInt, 0n), true);\n}\n\nfunction readEngineCommandRecord(view: DataView, offset: number): SonareEngineCommandRecord {\n return {\n type: view.getUint32(offset, true),\n targetId: view.getUint32(offset + 4, true),\n sampleTime: Number(view.getBigInt64(offset + 8, true)),\n argFloat: view.getFloat64(offset + 16, true),\n argInt: Number(view.getBigInt64(offset + 24, true)),\n };\n}\n\nfunction writeEngineTelemetryRecord(\n view: DataView,\n offset: number,\n telemetry: SonareEngineTelemetryRecord,\n): void {\n view.setUint32(offset, telemetry.type, true);\n view.setUint32(offset + 4, telemetry.error, true);\n view.setBigInt64(offset + 8, BigInt(Math.trunc(telemetry.renderFrame)), true);\n view.setBigInt64(offset + 16, BigInt(Math.trunc(telemetry.timelineSample)), true);\n view.setBigInt64(offset + 24, BigInt(Math.trunc(telemetry.audibleTimelineSample)), true);\n view.setInt32(offset + 32, telemetry.graphLatencySamplesQ8, true);\n view.setUint32(offset + 36, telemetry.value, true);\n view.setBigInt64(offset + 40, 0n, true);\n}\n\nfunction readEngineTelemetryRecord(view: DataView, offset: number): SonareEngineTelemetryRecord {\n return {\n type: view.getUint32(offset, true),\n error: view.getUint32(offset + 4, true),\n renderFrame: Number(view.getBigInt64(offset + 8, true)),\n timelineSample: Number(view.getBigInt64(offset + 16, true)),\n audibleTimelineSample: Number(view.getBigInt64(offset + 24, true)),\n graphLatencySamplesQ8: view.getInt32(offset + 32, true),\n value: view.getUint32(offset + 36, true),\n };\n}\n\nfunction telemetryFromEngine(telemetry: EngineTelemetry): SonareEngineTelemetryRecord {\n return {\n type: telemetry.type,\n error: telemetry.error,\n renderFrame: telemetry.renderFrame,\n timelineSample: telemetry.timelineSample,\n audibleTimelineSample: telemetry.audibleTimelineSample,\n graphLatencySamplesQ8: telemetry.graphLatencySamplesQ8,\n value: telemetry.value,\n };\n}\n\nfunction meterFromEngine(meter: EngineMeterTelemetry): SonareWorkletMeterSnapshot {\n return {\n type: 'meter',\n frame: meter.renderFrame,\n peakDbL: meter.peakDbL,\n peakDbR: meter.peakDbR,\n rmsDbL: meter.rmsDbL,\n rmsDbR: meter.rmsDbR,\n correlation: meter.correlation,\n };\n}\n\nfunction magnitudeToDb(value: number): number {\n return value > 1.0e-12 ? 20 * Math.log10(value) : -120;\n}\n\n/**\n * AudioWorklet-style mixer bridge backed by the package's single `sonare.wasm`.\n *\n * The WASM module must already be initialized via `init()` before constructing\n * this bridge. Each AudioWorklet input is treated as one stereo strip:\n * `inputs[strip][0]` is left and `inputs[strip][1]` is right. Missing channels\n * are replaced with preallocated silence.\n */\nexport class SonareWorkletProcessor {\n readonly sampleRate: number;\n readonly blockSize: number;\n private mixer: Mixer;\n private realtime: MixerRealtimeBuffer;\n private closed = false;\n private processedFrames = 0;\n private lastMeterFrame = 0;\n private meterIntervalFrames: number;\n private spectrumIntervalFrames: number;\n private lastSpectrumFrame = 0;\n private transport?: WorkletTransport;\n private meterRing?: SharedMeterRingWriter;\n private spectrumRing?: SharedSpectrumRingWriter;\n private spectrumBands: Float32Array;\n\n constructor(options: SonareWorkletProcessorOptions, transport?: WorkletTransport) {\n if (!options.sceneJson) {\n throw new Error('sceneJson is required.');\n }\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.spectrumIntervalFrames = Math.max(0, Math.floor(options.spectrumIntervalFrames ?? 0));\n this.transport = transport;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.meterRing = options.meterSharedBuffer\n ? meterRingFromSharedBuffer(options.meterSharedBuffer, options.meterRingCapacity)\n : undefined;\n this.spectrumRing = options.spectrumSharedBuffer\n ? spectrumRingFromSharedBuffer(\n options.spectrumSharedBuffer,\n options.spectrumRingCapacity,\n options.spectrumBands,\n )\n : undefined;\n const spectrumBandCount = this.spectrumRing?.bands ?? Math.max(1, options.spectrumBands ?? 16);\n this.spectrumBands = new Float32Array(spectrumBandCount);\n this.mixer = Mixer.fromSceneJson(options.sceneJson, this.sampleRate, this.blockSize);\n this.mixer.compile();\n const sceneStripCount = this.mixer.stripCount();\n const stripCount = options.stripCount ?? sceneStripCount;\n if (stripCount !== sceneStripCount) {\n throw new Error('stripCount must match the scene strip count.');\n }\n this.realtime = this.mixer.createRealtimeBuffer();\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const leftOut = output?.[0];\n const rightOut = output?.[1];\n if (!leftOut) {\n return true;\n }\n const frames = leftOut.length;\n // The mixer's realtime heap buffers are sized to blockSize. A render quantum\n // that differs from blockSize (e.g. a future browser using a quantum other\n // than 128, or a misconfigured blockSize) must NOT return false here:\n // returning false permanently terminates the AudioWorkletProcessor and\n // silently kills the node mid-stream. Instead degrade gracefully by\n // processing min(frames, blockSize) and zero-filling any remainder, mirroring\n // the sonare-rt processor's behaviour.\n const usable = Math.min(frames, this.blockSize);\n\n for (let strip = 0; strip < this.realtime.leftInputs.length; strip++) {\n const input = inputs[strip];\n const left = input?.[0];\n const right = input?.[1];\n const leftTarget = this.realtime.leftInputs[strip];\n const rightTarget = this.realtime.rightInputs[strip];\n if (left && left.length >= usable) {\n leftTarget.set(left.subarray(0, usable));\n if (right && right.length >= usable) {\n rightTarget.set(right.subarray(0, usable));\n } else {\n rightTarget.set(left.subarray(0, usable));\n }\n } else {\n leftTarget.fill(0);\n rightTarget.fill(0);\n }\n }\n\n this.realtime.process(usable);\n if (usable === frames) {\n leftOut.set(this.realtime.outLeft.subarray(0, usable));\n if (rightOut) {\n rightOut.set(this.realtime.outRight.subarray(0, usable));\n }\n } else {\n // frames > blockSize: fill the produced part and zero the remaining tail.\n leftOut.fill(0);\n leftOut.set(this.realtime.outLeft.subarray(0, usable));\n if (rightOut) {\n rightOut.fill(0);\n rightOut.set(this.realtime.outRight.subarray(0, usable));\n }\n }\n this.processedFrames += usable;\n this.publishMeter(\n this.realtime.outLeft.subarray(0, usable),\n this.realtime.outRight.subarray(0, usable),\n );\n this.publishSpectrum(\n this.realtime.outLeft.subarray(0, usable),\n this.realtime.outRight.subarray(0, usable),\n );\n return true;\n }\n\n receiveMessage(message: SonareWorkletMessage): void {\n if (this.closed) {\n return;\n }\n if (message.type === 'destroy') {\n this.destroy();\n return;\n }\n if (message.type === 'setMeterInterval') {\n this.meterIntervalFrames = Math.max(0, Math.floor(message.frames));\n return;\n }\n if (message.type === 'scheduleInsertAutomation') {\n this.mixer.scheduleInsertAutomation(\n message.stripIndex,\n message.insertIndex,\n message.paramId,\n message.samplePos ?? this.processedFrames,\n message.value,\n message.curve ?? 'linear',\n );\n }\n }\n\n destroy(): void {\n if (!this.closed) {\n this.mixer.delete();\n this.closed = true;\n }\n }\n\n private publishMeter(left: Float32Array, right: Float32Array): void {\n if (!this.transport || this.meterIntervalFrames <= 0) {\n return;\n }\n if (this.processedFrames - this.lastMeterFrame < this.meterIntervalFrames) {\n return;\n }\n this.lastMeterFrame = this.processedFrames;\n\n let peakL = 0;\n let peakR = 0;\n let sumL = 0;\n let sumR = 0;\n let sumLR = 0;\n for (let i = 0; i < left.length; i++) {\n const l = left[i] ?? 0;\n const r = right[i] ?? 0;\n const absL = Math.abs(l);\n const absR = Math.abs(r);\n if (absL > peakL) {\n peakL = absL;\n }\n if (absR > peakR) {\n peakR = absR;\n }\n sumL += l * l;\n sumR += r * r;\n sumLR += l * r;\n }\n const rmsL = Math.sqrt(sumL / Math.max(1, left.length));\n const rmsR = Math.sqrt(sumR / Math.max(1, right.length));\n const denominator = Math.sqrt(sumL * sumR);\n const meter: SonareWorkletMeterSnapshot = {\n type: 'meter',\n frame: this.processedFrames,\n peakDbL: toDb(peakL),\n peakDbR: toDb(peakR),\n rmsDbL: toDb(rmsL),\n rmsDbR: toDb(rmsR),\n correlation: denominator > 0 ? sumLR / denominator : 0,\n };\n this.transport.onMeter?.(meter);\n if (this.meterRing) {\n this.writeMeterRing(meter);\n } else {\n this.transport.postMessage?.(meter);\n }\n }\n\n private writeMeterRing(meter: SonareWorkletMeterSnapshot): void {\n const ring = this.meterRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n ring.records[offset] = encodeFrameLo(meter.frame);\n ring.records[offset + 1] = meter.peakDbL;\n ring.records[offset + 2] = meter.peakDbR;\n ring.records[offset + 3] = meter.rmsDbL;\n ring.records[offset + 4] = meter.rmsDbR;\n ring.records[offset + 5] = meter.correlation;\n ring.records[offset + 6] = encodeFrameHi(meter.frame);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // writeIndex is a free-running monotonic counter, so an overflow guard here\n // would fire on essentially every write past the first `capacity` records\n // and store an ever-growing value, not a dropped-record count. Readers\n // already detect silent overrun via firstReadable = max(readIndex,\n // writeIndex - capacity), so header slot 3 is left at its initial 0.\n }\n\n private publishSpectrum(left: Float32Array, right: Float32Array): void {\n if (this.spectrumIntervalFrames <= 0) {\n return;\n }\n if (this.processedFrames - this.lastSpectrumFrame < this.spectrumIntervalFrames) {\n return;\n }\n this.lastSpectrumFrame = this.processedFrames;\n this.computeSpectrum(left, right);\n if (this.spectrumRing) {\n this.writeSpectrumRing(this.processedFrames, this.spectrumBands);\n return;\n }\n const spectrum: SonareWorkletSpectrumSnapshot = {\n type: 'spectrum',\n frame: this.processedFrames,\n bands: new Float32Array(this.spectrumBands),\n };\n this.transport?.onSpectrum?.(spectrum);\n this.transport?.postMessage?.(spectrum);\n }\n\n private computeSpectrum(left: Float32Array, right: Float32Array): void {\n // Coarse per-render-quantum band energy, NOT a full FFT analyzer: each band\n // is a single-bin DFT (bin = band + 1) evaluated over the current block of n\n // samples. Bins at or above the block Nyquist (band + 1 > floor(n / 2))\n // alias, so the evaluated band count is clamped to floor(n / 2) and any\n // higher bands are pinned to the silence floor. Bin resolution is therefore\n // tied to the render quantum (typically 128 samples); treat the output as a\n // rough spectral tilt, not a precise spectrum.\n const n = Math.max(1, Math.min(left.length, right.length));\n const maxBand = Math.floor(n / 2);\n for (let band = 0; band < this.spectrumBands.length; band++) {\n if (band >= maxBand) {\n this.spectrumBands[band] = magnitudeToDb(0);\n continue;\n }\n const bin = band + 1;\n let real = 0;\n let imag = 0;\n for (let i = 0; i < n; i++) {\n const sample = 0.5 * ((left[i] ?? 0) + (right[i] ?? 0));\n const phase = (-2 * Math.PI * bin * i) / n;\n real += sample * Math.cos(phase);\n imag += sample * Math.sin(phase);\n }\n this.spectrumBands[band] = magnitudeToDb((2 * Math.hypot(real, imag)) / n);\n }\n }\n\n private writeSpectrumRing(frame: number, bands: Float32Array): void {\n const ring = this.spectrumRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * ring.recordFloats;\n ring.records[offset] = encodeFrameLo(frame);\n ring.records[offset + 1] = encodeFrameHi(frame);\n ring.records[offset + 2] = bands.length;\n ring.records.set(bands.subarray(0, ring.bands), offset + 3);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // See writeMeterRing: header slot 4 (the spectrum-ring overflow slot) is\n // left at its initial 0; readers detect silent overrun via the\n // firstReadable = max(readIndex, writeIndex - capacity) clamp. (Slot 3 here\n // holds the band count and is still written at ring creation.)\n }\n}\n\n/**\n * AudioWorklet-style bridge for the DAW realtime engine facade.\n *\n * The default mode uses the existing `sonare.wasm` embind facade. The\n * `sonare-rt` target is exposed as a selectable runtime target for hosts that\n * load the dedicated Emscripten AudioWorklet module.\n */\nexport class SonareRealtimeEngineWorkletProcessor {\n private static warnedChannelScratchOverflow = false;\n readonly sampleRate: number;\n readonly blockSize: number;\n readonly channelCount: number;\n readonly runtimeTarget: 'embind' | 'sonare-rt';\n private engine: RealtimeEngine;\n private closed = false;\n private commandRing?: SonareEngineCommandRingBuffer;\n private telemetryRing?: SonareEngineTelemetryRingBuffer;\n private meterRing?: SharedMeterRingWriter;\n private transport?: WorkletTransport;\n private meterIntervalFrames: number;\n private lastMeterFrame = Number.NEGATIVE_INFINITY;\n // Latest metronome gains/click length pushed via 'syncMetronome'. The\n // SetMetronome command only toggles enabled state; the config arrives here.\n private metronomeConfig: ResolvedMetronomeConfig = { ...DEFAULT_METRONOME_CONFIG };\n // Zero-copy prepared realtime path: persistent per-channel views onto the\n // engine's WASM-heap scratch (acquired once on the main thread via\n // getChannelBuffer). process() writes the AudioWorklet input straight into\n // these views, calls engine.processPrepared(frames) which runs the engine IN\n // PLACE, then reads the same views back — no std::vector or JS Float32Array is\n // allocated per render quantum (the old engine.process() round-tripped fresh\n // arrays on both heaps every block, an RT-safety hazard).\n private channelBuffers: Float32Array[];\n\n constructor(\n options: SonareRealtimeEngineWorkletProcessorOptions = {},\n transport?: WorkletTransport,\n ) {\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n this.runtimeTarget = options.runtimeTarget ?? 'embind';\n if (this.runtimeTarget === 'sonare-rt') {\n throw new Error(\n 'sonare-rt runtime is provided by the dedicated Emscripten AudioWorklet module; use SonareRealtimeEngineNode.create({ runtimeTarget: \"sonare-rt\", moduleUrl: ... }) to load it.',\n );\n }\n this.transport = transport;\n this.meterIntervalFrames = Math.max(0, Math.floor(options.meterIntervalFrames ?? 2048));\n this.commandRing = options.commandSharedBuffer\n ? this.commandRingFromSharedBuffer(options.commandSharedBuffer, options.commandRingCapacity)\n : undefined;\n this.telemetryRing = options.telemetrySharedBuffer\n ? this.telemetryRingFromSharedBuffer(\n options.telemetrySharedBuffer,\n options.telemetryRingCapacity,\n )\n : undefined;\n this.meterRing = options.meterSharedBuffer\n ? meterRingFromSharedBuffer(options.meterSharedBuffer, options.meterRingCapacity)\n : undefined;\n this.engine = new RealtimeEngine(this.sampleRate, this.blockSize);\n // Allocate persistent WASM-heap scratch (worst case: channelCount channels x\n // blockSize frames) and acquire the per-channel heap views once.\n this.engine.prepareChannels(this.channelCount, this.blockSize);\n this.channelBuffers = new Array(this.channelCount);\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers[ch] = this.engine.getChannelBuffer(ch, this.blockSize);\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const firstOutput = output?.[0];\n if (!firstOutput) {\n return true;\n }\n const frames = firstOutput.length;\n if (frames > this.blockSize) {\n for (const channel of output ?? []) {\n channel.fill(0);\n }\n this.publishTelemetry();\n return true;\n }\n\n this.drainCommands();\n\n // Clamp `frames` to the pre-allocated scratch capacity. The earlier\n // `frames > this.blockSize` branch already returns early, so this is\n // defensive — but we warn once if it ever fires so the contract violation\n // is visible.\n let usableFrames = frames;\n if (usableFrames > this.blockSize) {\n if (!SonareRealtimeEngineWorkletProcessor.warnedChannelScratchOverflow) {\n SonareRealtimeEngineWorkletProcessor.warnedChannelScratchOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeEngineWorkletProcessor: requested ${usableFrames} frames ` +\n `exceeds pre-allocated capacity ${this.blockSize}; clamping.`,\n );\n }\n usableFrames = this.blockSize;\n }\n\n // Defend against WASM linear-memory growth detaching the cached heap views:\n // if any view's backing ArrayBuffer has been detached (byteLength === 0),\n // re-acquire all of them. This is a control-flow check (no allocation in the\n // common case where memory did not grow).\n if ((this.channelBuffers[0]?.byteLength ?? 0) === 0) {\n this.reacquireChannelBuffers();\n }\n\n const input = inputs[0];\n // Write the AudioWorklet input straight into the engine's WASM-heap views;\n // no per-block heap allocation.\n for (let ch = 0; ch < this.channelCount; ch++) {\n const dst = this.channelBuffers[ch];\n const source = input?.[ch];\n if (source && source.length === usableFrames) {\n dst.set(source.subarray(0, usableFrames));\n } else {\n dst.fill(0, 0, usableFrames);\n }\n }\n\n // Run the engine in place over the prepared scratch (allocation-free).\n this.engine.processPrepared(usableFrames);\n\n for (let ch = 0; ch < output.length; ch++) {\n const target = output[ch];\n const source = this.channelBuffers[ch] ?? this.channelBuffers[0];\n if (source) {\n target.set(source.subarray(0, Math.min(target.length, usableFrames)));\n if (target.length > usableFrames) {\n target.fill(0, usableFrames);\n }\n } else {\n target.fill(0);\n }\n }\n this.publishTelemetry();\n this.publishMeters();\n return true;\n }\n\n private reacquireChannelBuffers(): void {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers[ch] = this.engine.getChannelBuffer(ch, this.blockSize);\n }\n }\n\n receiveCommand(command: SonareEngineCommandRecord): void {\n if (!this.closed) {\n this.applyCommand(command);\n }\n }\n\n // Applies an out-of-band control-plane sync message. Runs on the AudioWorklet\n // global scope but OUTSIDE process() (the message-port callback), so the\n // bulk/allocating engine setters (setClips/setMarkers) are safe here — they\n // never run on the realtime render path. This is the audio-thread equivalent\n // of the engine's control-thread RtPublisher setters.\n receiveSync(message: SonareEngineSyncMessage): void {\n if (this.closed) {\n return;\n }\n switch (message.type) {\n case 'syncClips':\n this.engine.setClips(message.clips);\n break;\n case 'syncMarkers':\n this.engine.setMarkers(message.markers);\n break;\n case 'syncMetronome':\n this.metronomeConfig = resolveMetronomeConfig(message.config);\n this.engine.setMetronome(message.config);\n break;\n case 'syncAutomation':\n this.engine.setAutomationLane(message.paramId, message.points);\n break;\n }\n }\n\n destroy(): void {\n if (!this.closed) {\n this.engine.destroy();\n this.closed = true;\n }\n }\n\n private drainCommands(): void {\n if (!this.commandRing) {\n return;\n }\n for (let i = 0; i < 64; i++) {\n const command = popSonareEngineCommandRingBuffer(this.commandRing);\n if (!command) {\n return;\n }\n this.applyCommand(command);\n }\n }\n\n private applyCommand(command: SonareEngineCommandRecord): void {\n const sampleTime = Number(command.sampleTime ?? -1);\n switch (command.type) {\n case SonareEngineCommandType.SetParam:\n // paramId is carried in targetId, the new value in argFloat (matches the\n // SonareEngine.setParam producer). sampleTime is the render frame.\n this.engine.setParameter(\n Math.trunc(Number(command.targetId ?? 0)),\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.SetParamSmoothed:\n this.engine.setParameterSmoothed(\n Math.trunc(Number(command.targetId ?? 0)),\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.TransportPlay:\n this.engine.play(sampleTime);\n break;\n case SonareEngineCommandType.TransportStop:\n this.engine.stop(sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekSample:\n this.engine.seekSample(Number(command.argInt ?? 0), sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekPpq:\n this.engine.seekPpq(Number(command.argFloat ?? 0), sampleTime);\n break;\n case SonareEngineCommandType.SetTempoMap:\n this.engine.setTempo(Number(command.argFloat ?? 120));\n break;\n case SonareEngineCommandType.SetLoop:\n this.engine.setLoop(\n Number(command.argFloat ?? 0),\n Number(command.argInt ?? 0) / 1_000_000,\n command.targetId !== 0,\n );\n break;\n case SonareEngineCommandType.ArmRecord:\n this.engine.armCapture(Boolean(command.argInt));\n break;\n case SonareEngineCommandType.Punch:\n // Both endpoints already arrive as samples (see SonareEngine.punch);\n // do NOT re-scale by sampleRate.\n this.engine.setCapturePunch(\n Number(command.argInt ?? 0),\n Math.max(0, Math.round(Number(command.argFloat ?? 0))),\n true,\n );\n break;\n case SonareEngineCommandType.SetMetronome:\n // Metronome config (beatGain/accentGain/clickSamples/clickSeconds) is\n // delivered out-of-band via the 'syncMetronome' message so it carries\n // the caller's full config; the command only toggles enabled state as a\n // sample-aligned fallback.\n this.engine.setMetronome({\n enabled: Boolean(command.argInt),\n beatGain: this.metronomeConfig.beatGain,\n accentGain: this.metronomeConfig.accentGain,\n clickSamples: this.metronomeConfig.clickSamples,\n });\n break;\n case SonareEngineCommandType.SeekMarker:\n // The realtime engine's markers are kept in sync via 'syncMarkers'\n // (RtPublisher-style swap), so a queued kSeekMarker resolves correctly.\n this.engine.seekMarker(Math.trunc(Number(command.targetId ?? 0)), sampleTime);\n break;\n default:\n this.publishTelemetryRecord({\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n break;\n }\n }\n\n private publishTelemetry(): void {\n for (const item of this.engine.drainTelemetry(64)) {\n this.publishTelemetryRecord(telemetryFromEngine(item));\n }\n }\n\n private publishTelemetryRecord(record: SonareEngineTelemetryRecord): void {\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, record);\n return;\n }\n this.transport?.postMessage?.(record);\n }\n\n private publishMeters(): void {\n if (this.meterIntervalFrames <= 0 || (!this.transport && !this.meterRing)) {\n return;\n }\n for (const item of this.engine.drainMeterTelemetry(64)) {\n const meter = meterFromEngine(item);\n if (meter.frame - this.lastMeterFrame < this.meterIntervalFrames) {\n continue;\n }\n this.lastMeterFrame = meter.frame;\n // Prefer the lock-free SAB meter ring (matching the telemetry path and\n // SonareWorkletProcessor); only fall back to structured-clone postMessage\n // when no ring was provided, so we do not allocate/post from the audio\n // render callback in SAB mode.\n if (this.meterRing) {\n this.writeMeterRing(meter);\n } else {\n this.transport?.onMeter?.(meter);\n this.transport?.postMessage?.(meter);\n }\n }\n }\n\n private writeMeterRing(meter: SonareWorkletMeterSnapshot): void {\n const ring = this.meterRing;\n if (!ring) {\n return;\n }\n const writeIndex = Atomics.load(ring.header, 0);\n const offset = (writeIndex % ring.capacity) * SONARE_METER_RING_RECORD_FLOATS;\n ring.records[offset] = encodeFrameLo(meter.frame);\n ring.records[offset + 1] = meter.peakDbL;\n ring.records[offset + 2] = meter.peakDbR;\n ring.records[offset + 3] = meter.rmsDbL;\n ring.records[offset + 4] = meter.rmsDbR;\n ring.records[offset + 5] = meter.correlation;\n ring.records[offset + 6] = encodeFrameHi(meter.frame);\n Atomics.store(ring.header, 0, writeIndex + 1);\n // writeIndex is a free-running monotonic counter, so an overflow guard here\n // would fire on essentially every write past the first `capacity` records\n // and store an ever-growing value, not a dropped-record count. Readers\n // already detect silent overrun via firstReadable = max(readIndex,\n // writeIndex - capacity), so header slot 3 is left at its initial 0.\n }\n\n private commandRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineCommandRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n\n private telemetryRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineTelemetryRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n}\n\nexport class SonareRtRealtimeEngineRuntime {\n readonly sampleRate: number;\n readonly blockSize: number;\n readonly channelCount: number;\n private readonly module: SonareRtModule;\n private readonly memory: WebAssembly.Memory;\n private readonly engine: number;\n private readonly channelPointerTable: number;\n private readonly channelBuffers: number[];\n private readonly telemetryIntsPtr: number;\n private readonly telemetryFramesPtr: number;\n private readonly commandRing?: SonareEngineCommandRingBuffer;\n private readonly telemetryRing?: SonareEngineTelemetryRingBuffer;\n private metronomeConfig: ResolvedMetronomeConfig = { ...DEFAULT_METRONOME_CONFIG };\n private closed = false;\n\n constructor(options: SonareRtRealtimeEngineRuntimeOptions) {\n this.module = options.module;\n this.memory = options.memory;\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n this.commandRing = options.commandSharedBuffer\n ? this.commandRingFromSharedBuffer(options.commandSharedBuffer, options.commandRingCapacity)\n : undefined;\n this.telemetryRing = options.telemetrySharedBuffer\n ? this.telemetryRingFromSharedBuffer(\n options.telemetrySharedBuffer,\n options.telemetryRingCapacity,\n )\n : undefined;\n\n this.engine = this.module._sonare_rt_engine_create();\n if (this.engine <= 0) {\n throw new Error('failed to create sonare-rt engine');\n }\n if (\n this.module._sonare_rt_engine_prepare(\n this.engine,\n this.sampleRate,\n this.blockSize,\n 1024,\n 1024,\n ) !== 1\n ) {\n this.module._sonare_rt_engine_destroy(this.engine);\n throw new Error('failed to prepare sonare-rt engine');\n }\n this.channelPointerTable = this.module._malloc(\n this.channelCount * Uint32Array.BYTES_PER_ELEMENT,\n );\n this.channelBuffers = [];\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.channelBuffers.push(\n this.module._malloc(this.blockSize * Float32Array.BYTES_PER_ELEMENT),\n );\n }\n this.telemetryIntsPtr = this.module._malloc(64 * 4 * Int32Array.BYTES_PER_ELEMENT);\n this.telemetryFramesPtr = this.module._malloc(64 * 3 * Float64Array.BYTES_PER_ELEMENT);\n this.writeChannelPointers();\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.closed) {\n return false;\n }\n const output = outputs[0];\n const firstOutput = output?.[0];\n if (!firstOutput) {\n return true;\n }\n const frames = firstOutput.length;\n if (frames > this.blockSize) {\n for (const channel of output) {\n channel.fill(0);\n }\n return true;\n }\n\n this.drainCommands();\n const heap = new Float32Array(this.memory.buffer);\n const input = inputs[0];\n for (let ch = 0; ch < this.channelCount; ch++) {\n const ptr = this.channelBuffers[ch] ?? this.channelBuffers[0];\n const offset = ptr >> 2;\n const source = input?.[ch];\n if (source && source.length === frames) {\n heap.set(source, offset);\n } else {\n heap.fill(0, offset, offset + frames);\n }\n }\n\n this.module._sonare_rt_engine_process(\n this.engine,\n this.channelPointerTable,\n this.channelCount,\n frames,\n );\n\n for (let ch = 0; ch < output.length; ch++) {\n const target = output[ch];\n const ptr = this.channelBuffers[ch] ?? this.channelBuffers[0];\n target.set(heap.subarray(ptr >> 2, (ptr >> 2) + target.length));\n }\n this.publishTelemetry();\n return true;\n }\n\n receiveCommand(command: SonareEngineCommandRecord): void {\n if (!this.closed) {\n this.applyCommand(command);\n }\n }\n\n // Out-of-band control sync for the sonare-rt runtime. The sonare-rt C ABI\n // (src/wasm/rt_bindings.cpp) exposes set_metronome_enabled and seek_marker but\n // NOT set_clips / set_markers, so clip/marker mutations cannot be applied to a\n // live sonare-rt engine. We honor the metronome config and surface a clear\n // telemetry error for the unsupported clip/marker paths instead of silently\n // dropping them. The default 'embind' runtime wires all three fully.\n receiveSync(message: SonareEngineSyncMessage): void {\n if (this.closed) {\n return;\n }\n switch (message.type) {\n case 'syncMetronome':\n this.metronomeConfig = resolveMetronomeConfig(message.config);\n this.module._sonare_rt_engine_set_metronome_enabled(\n this.engine,\n message.config.enabled ? 1 : 0,\n this.metronomeConfig.beatGain,\n this.metronomeConfig.accentGain,\n this.metronomeConfig.clickSamples,\n );\n break;\n case 'syncClips':\n case 'syncMarkers':\n case 'syncAutomation':\n // The sonare-rt C ABI exposes no set_clips / set_markers /\n // set_automation_lane, so these mutations cannot reach a live sonare-rt\n // engine. Surface a clear telemetry error rather than silently dropping.\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: 0,\n });\n }\n break;\n }\n }\n\n destroy(): void {\n if (this.closed) {\n return;\n }\n this.module._free(this.telemetryFramesPtr);\n this.module._free(this.telemetryIntsPtr);\n for (const ptr of this.channelBuffers) {\n this.module._free(ptr);\n }\n this.module._free(this.channelPointerTable);\n this.module._sonare_rt_engine_destroy(this.engine);\n this.closed = true;\n }\n\n private writeChannelPointers(): void {\n const pointers = new Uint32Array(this.memory.buffer);\n const offset = this.channelPointerTable >> 2;\n for (let ch = 0; ch < this.channelBuffers.length; ch++) {\n pointers[offset + ch] = this.channelBuffers[ch];\n }\n }\n\n private drainCommands(): void {\n if (!this.commandRing) {\n return;\n }\n for (let i = 0; i < 64; i++) {\n const command = popSonareEngineCommandRingBuffer(this.commandRing);\n if (!command) {\n return;\n }\n this.applyCommand(command);\n }\n }\n\n private applyCommand(command: SonareEngineCommandRecord): void {\n const sampleTime = toBigInt64(command.sampleTime, -1n);\n switch (command.type) {\n case SonareEngineCommandType.SetParam:\n case SonareEngineCommandType.SetParamSmoothed:\n // The sonare-rt C ABI (src/wasm/rt_bindings.cpp) does not export a\n // sonare_rt_engine_set_param entry point, so parameter automation has no\n // realtime transport on this runtime target. Surface a clear error\n // telemetry record (rather than silently dropping the command) so hosts\n // can detect the unsupported path; the embind runtime fully wires this.\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n }\n break;\n case SonareEngineCommandType.TransportPlay:\n this.module._sonare_rt_engine_play(this.engine, sampleTime);\n break;\n case SonareEngineCommandType.TransportStop:\n this.module._sonare_rt_engine_stop(this.engine, sampleTime);\n break;\n case SonareEngineCommandType.TransportSeekSample:\n this.module._sonare_rt_engine_seek_sample(\n this.engine,\n toBigInt64(command.argInt, 0n),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.TransportSeekPpq:\n this.module._sonare_rt_engine_seek_ppq(\n this.engine,\n Number(command.argFloat ?? 0),\n sampleTime,\n );\n break;\n case SonareEngineCommandType.SetTempoMap:\n this.module._sonare_rt_engine_set_tempo(this.engine, Number(command.argFloat ?? 120));\n break;\n case SonareEngineCommandType.SetLoop:\n this.module._sonare_rt_engine_set_loop(\n this.engine,\n Number(command.argFloat ?? 0),\n Number(command.argInt ?? 0) / 1_000_000,\n command.targetId ? 1 : 0,\n );\n break;\n case SonareEngineCommandType.ArmRecord:\n this.module._sonare_rt_engine_set_capture_armed(this.engine, command.argInt ? 1 : 0);\n break;\n case SonareEngineCommandType.Punch:\n // Both endpoints already arrive as samples (see SonareEngine.punch);\n // do NOT re-scale by sampleRate.\n this.module._sonare_rt_engine_set_capture_punch(\n this.engine,\n toBigInt64(command.argInt, 0n),\n BigInt(Math.max(0, Math.round(Number(command.argFloat ?? 0)))),\n 1,\n );\n break;\n case SonareEngineCommandType.SetMetronome:\n this.module._sonare_rt_engine_set_metronome_enabled(\n this.engine,\n command.argInt ? 1 : 0,\n this.metronomeConfig.beatGain,\n this.metronomeConfig.accentGain,\n this.metronomeConfig.clickSamples,\n );\n break;\n case SonareEngineCommandType.SeekMarker:\n this.module._sonare_rt_engine_seek_marker(\n this.engine,\n Math.trunc(command.targetId ?? 0),\n sampleTime,\n );\n break;\n default:\n if (this.telemetryRing) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: SonareEngineTelemetryType.Error,\n error: SonareEngineTelemetryError.UnknownTarget,\n renderFrame: 0,\n timelineSample: 0,\n audibleTimelineSample: 0,\n graphLatencySamplesQ8: 0,\n value: Number(command.type),\n });\n }\n break;\n }\n }\n\n private publishTelemetry(): void {\n if (!this.telemetryRing) {\n this.module._sonare_rt_engine_drain_telemetry(\n this.engine,\n this.telemetryIntsPtr,\n this.telemetryFramesPtr,\n 64,\n );\n return;\n }\n const count = this.module._sonare_rt_engine_drain_telemetry(\n this.engine,\n this.telemetryIntsPtr,\n this.telemetryFramesPtr,\n 64,\n );\n const ints = new Int32Array(this.memory.buffer);\n const frames = new Float64Array(this.memory.buffer);\n const intBase = this.telemetryIntsPtr >> 2;\n const frameBase = this.telemetryFramesPtr >> 3;\n for (let i = 0; i < count; i++) {\n writeSonareEngineTelemetryRingBuffer(this.telemetryRing, {\n type: ints[intBase + i * 4],\n error: ints[intBase + i * 4 + 1],\n renderFrame: frames[frameBase + i * 3],\n timelineSample: frames[frameBase + i * 3 + 1],\n audibleTimelineSample: frames[frameBase + i * 3 + 2],\n graphLatencySamplesQ8: ints[intBase + i * 4 + 2],\n value: ints[intBase + i * 4 + 3],\n });\n }\n }\n\n private commandRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineCommandRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_COMMAND_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n\n private telemetryRingFromSharedBuffer(\n sharedBuffer: SharedArrayBuffer,\n fallbackCapacity?: number,\n ): SonareEngineTelemetryRingBuffer {\n const ring = engineRingFromSharedBuffer(\n sharedBuffer,\n SONARE_ENGINE_TELEMETRY_RECORD_BYTES,\n fallbackCapacity,\n );\n return { sharedBuffer, header: ring.header, view: ring.view, capacity: ring.capacity };\n }\n}\n\nexport class SonareRealtimeEngineNode {\n readonly node: AudioWorkletNode;\n readonly capabilities: SonareRealtimeEngineNodeCapabilities;\n readonly commandRing?: SonareEngineCommandRingBuffer;\n readonly telemetryRing?: SonareEngineTelemetryRingBuffer;\n readonly meterRing?: SonareMeterRingBuffer;\n readonly ready: Promise<void>;\n private telemetryReadIndex = 0;\n private meterReadIndex = 0;\n private telemetryListeners = new Set<(telemetry: SonareEngineTelemetryRecord) => void>();\n private meterListeners = new Set<(meter: SonareWorkletMeterSnapshot) => void>();\n private resolveReady!: () => void;\n private rejectReady!: (reason?: unknown) => void;\n private destroyed = false;\n\n private constructor(\n node: AudioWorkletNode,\n capabilities: SonareRealtimeEngineNodeCapabilities,\n commandRing?: SonareEngineCommandRingBuffer,\n telemetryRing?: SonareEngineTelemetryRingBuffer,\n meterRing?: SonareMeterRingBuffer,\n ) {\n this.node = node;\n this.capabilities = capabilities;\n this.commandRing = commandRing;\n this.telemetryRing = telemetryRing;\n this.meterRing = meterRing;\n this.ready = new Promise((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n });\n if (capabilities.runtimeTarget !== 'sonare-rt') {\n this.resolveReady();\n }\n this.node.port.onmessage = (event: MessageEvent<unknown>) => {\n if (isEngineTelemetryRecord(event.data)) {\n this.emitTelemetry(event.data);\n } else if (isMeterSnapshot(event.data)) {\n this.emitMeter(event.data);\n } else if (isRecord(event.data) && event.data.type === 'ready') {\n this.resolveReady();\n } else if (isRecord(event.data) && event.data.type === 'error') {\n this.rejectReady(new Error(String(event.data.message ?? 'AudioWorklet error')));\n }\n };\n }\n\n static async create(\n context: BaseAudioContext,\n options: SonareRealtimeEngineNodeOptions = {},\n ): Promise<SonareRealtimeEngineNode> {\n const runtimeTarget = options.runtimeTarget ?? 'embind';\n const processorName = options.processorName ?? 'sonare-realtime-engine-processor';\n const moduleUrl = options.moduleUrl;\n if (moduleUrl && context.audioWorklet?.addModule) {\n await context.audioWorklet.addModule(moduleUrl);\n }\n const detectedCapabilities =\n options.engineAbiVersion !== undefined\n ? {\n engineAbiVersion: options.engineAbiVersion,\n expectedEngineAbiVersion: options.expectedEngineAbiVersion ?? options.engineAbiVersion,\n abiCompatible:\n options.engineAbiVersion ===\n (options.expectedEngineAbiVersion ?? options.engineAbiVersion),\n }\n : runtimeTarget === 'embind'\n ? engineCapabilities()\n : undefined;\n if (options.requireAbiCompatible !== false && detectedCapabilities?.abiCompatible === false) {\n throw new Error(\n `Engine ABI mismatch: wasm=${detectedCapabilities.engineAbiVersion}, expected=${detectedCapabilities.expectedEngineAbiVersion}`,\n );\n }\n const sharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';\n const atomics = typeof globalThis.Atomics === 'object';\n const audioWorklet = typeof AudioWorkletNode !== 'undefined' || !!options.nodeFactory;\n const degradedReason =\n options.mode !== 'postMessage' && (!sharedArrayBuffer || !atomics)\n ? 'SharedArrayBuffer or Atomics unavailable; using postMessage transport.'\n : undefined;\n const mode =\n options.mode === 'postMessage' || !sharedArrayBuffer || !atomics ? 'postMessage' : 'sab';\n if (options.mode === 'sab' && mode !== 'sab') {\n throw new Error(\n 'SharedArrayBuffer mode requested but SharedArrayBuffer/Atomics are unavailable.',\n );\n }\n\n const commandRing =\n mode === 'sab'\n ? createSonareEngineCommandRingBuffer(options.commandRingCapacity ?? 128)\n : undefined;\n const telemetryRing =\n mode === 'sab'\n ? createSonareEngineTelemetryRingBuffer(options.telemetryRingCapacity ?? 128)\n : undefined;\n // Meter ring: only the embind runtime publishes engine meters into a SAB\n // ring (the sonare-rt runtime owns its own meter transport). Lock-free\n // meter delivery matches the telemetry path and keeps the audio render\n // callback allocation-free in SAB mode.\n const meterRing =\n mode === 'sab' && runtimeTarget === 'embind'\n ? createSonareMeterRingBuffer(options.meterRingCapacity ?? 128)\n : undefined;\n const channelCount = Math.max(1, Math.floor(options.channelCount ?? 2));\n const processorOptions: SonareRealtimeEngineWorkletProcessorOptions = {\n runtimeTarget,\n rtModuleUrl: options.rtModuleUrl,\n rtWasmBinary: options.rtWasmBinary,\n sampleRate: options.sampleRate ?? context.sampleRate,\n blockSize: options.blockSize,\n channelCount,\n commandSharedBuffer: commandRing?.sharedBuffer,\n commandRingCapacity: commandRing?.capacity,\n telemetrySharedBuffer: telemetryRing?.sharedBuffer,\n telemetryRingCapacity: telemetryRing?.capacity,\n meterSharedBuffer: meterRing?.sharedBuffer,\n meterRingCapacity: meterRing?.capacity,\n };\n const factory =\n options.nodeFactory ??\n ((ctx: BaseAudioContext, name: string, nodeOptions: AudioWorkletNodeOptions) =>\n new AudioWorkletNode(ctx, name, nodeOptions));\n const node = factory(context, processorName, {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [channelCount],\n processorOptions,\n });\n return new SonareRealtimeEngineNode(\n node,\n {\n mode,\n runtimeTarget,\n sharedArrayBuffer,\n atomics,\n audioWorklet,\n engineAbiVersion: detectedCapabilities?.engineAbiVersion,\n expectedEngineAbiVersion: detectedCapabilities?.expectedEngineAbiVersion,\n abiCompatible: detectedCapabilities?.abiCompatible,\n degradedReason,\n },\n commandRing,\n telemetryRing,\n meterRing,\n );\n }\n\n play(sampleTime = -1): boolean {\n return this.sendCommand({ type: SonareEngineCommandType.TransportPlay, sampleTime });\n }\n\n stop(sampleTime = -1): boolean {\n return this.sendCommand({ type: SonareEngineCommandType.TransportStop, sampleTime });\n }\n\n seekSample(timelineSample: number, sampleTime = -1): boolean {\n return this.sendCommand({\n type: SonareEngineCommandType.TransportSeekSample,\n sampleTime,\n argInt: timelineSample,\n });\n }\n\n seekPpq(ppq: number, sampleTime = -1): boolean {\n return this.sendCommand({\n type: SonareEngineCommandType.TransportSeekPpq,\n sampleTime,\n argFloat: ppq,\n });\n }\n\n sendCommand(command: SonareEngineCommandRecord): boolean {\n if (this.destroyed) {\n return false;\n }\n if (this.commandRing) {\n return pushSonareEngineCommandRingBuffer(this.commandRing, command);\n }\n this.node.port.postMessage(command);\n return true;\n }\n\n pollTelemetry(): SonareEngineTelemetryRecord[] {\n if (!this.telemetryRing) {\n return [];\n }\n const read = readSonareEngineTelemetryRingBuffer(this.telemetryRing, this.telemetryReadIndex);\n this.telemetryReadIndex = read.nextReadIndex;\n for (const telemetry of read.telemetry) {\n this.emitTelemetry(telemetry);\n }\n return read.telemetry;\n }\n\n // Drains any meters published into the SAB meter ring (embind SAB mode) and\n // forwards them to onMeter listeners. In postMessage mode meters arrive via\n // node.port.onmessage instead, so this is a no-op then.\n pollMeters(): SonareWorkletMeterSnapshot[] {\n if (!this.meterRing) {\n return [];\n }\n const read = readSonareMeterRingBuffer(this.meterRing, this.meterReadIndex);\n this.meterReadIndex = read.nextReadIndex;\n for (const meter of read.meters) {\n this.emitMeter(meter);\n }\n return read.meters;\n }\n\n onTelemetry(callback: (telemetry: SonareEngineTelemetryRecord) => void): () => void {\n this.telemetryListeners.add(callback);\n return () => {\n this.telemetryListeners.delete(callback);\n };\n }\n\n onMeter(callback: (meter: SonareWorkletMeterSnapshot) => void): () => void {\n this.meterListeners.add(callback);\n return () => {\n this.meterListeners.delete(callback);\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.node.port.postMessage({ type: SonareEngineCommandType.TransportStop, sampleTime: -1 });\n this.node.disconnect();\n this.telemetryListeners.clear();\n this.meterListeners.clear();\n }\n\n private emitTelemetry(telemetry: SonareEngineTelemetryRecord): void {\n for (const listener of this.telemetryListeners) {\n listener(telemetry);\n }\n }\n\n private emitMeter(meter: SonareWorkletMeterSnapshot): void {\n for (const listener of this.meterListeners) {\n listener(meter);\n }\n }\n}\n\nexport class SonareEngine {\n readonly node: AudioWorkletNode;\n readonly capabilities: SonareRealtimeEngineNodeCapabilities;\n readonly transport: SonareEngineTransportFacade;\n private readonly realtimeNode: SonareRealtimeEngineNode;\n private readonly offlineEngine: RealtimeEngine;\n private readonly context: SuspendableAudioContext;\n private readonly sampleRate: number;\n private readonly offlineBlockSize: number;\n private readonly offlineChannelCount: number;\n private readonly automationLanes = new Map<number, EngineAutomationPoint[]>();\n private readonly clips = new Map<number, EngineClip>();\n private readonly markers = new Map<number, EngineMarker>();\n private nextClipId = 1;\n private nextMarkerId = 1;\n private destroyed = false;\n\n private constructor(\n context: BaseAudioContext,\n realtimeNode: SonareRealtimeEngineNode,\n offlineEngine: RealtimeEngine,\n sampleRate: number,\n offlineBlockSize: number,\n offlineChannelCount: number,\n ) {\n this.context = context;\n this.realtimeNode = realtimeNode;\n this.offlineEngine = offlineEngine;\n this.node = realtimeNode.node;\n this.capabilities = realtimeNode.capabilities;\n this.sampleRate = sampleRate;\n this.offlineBlockSize = offlineBlockSize;\n this.offlineChannelCount = offlineChannelCount;\n this.transport = {\n play: (sampleTime = -1) => this.realtimeNode.play(sampleTime),\n stop: (sampleTime = -1) => this.realtimeNode.stop(sampleTime),\n seekPpq: (ppq, sampleTime = -1) => {\n this.offlineEngine.seekPpq(ppq, sampleTime);\n return this.realtimeNode.seekPpq(ppq, sampleTime);\n },\n seekSeconds: (seconds, sampleTime = -1) => {\n const timelineSample = Math.max(0, Math.round(seconds * this.sampleRate));\n this.offlineEngine.seekSample(timelineSample, sampleTime);\n return this.realtimeNode.seekSample(timelineSample, sampleTime);\n },\n setTempo: (bpm) => this.setTempo(bpm),\n setLoop: (startPpq, endPpq, enabled = true) => this.setLoop(startPpq, endPpq, enabled),\n };\n }\n\n static async create(\n context: BaseAudioContext,\n options: SonareEngineOptions = {},\n ): Promise<SonareEngine> {\n const sampleRate = options.sampleRate ?? context.sampleRate;\n const blockSize = options.offlineBlockSize ?? options.blockSize ?? 128;\n const channelCount = Math.max(\n 1,\n Math.floor(options.offlineChannelCount ?? options.channelCount ?? 2),\n );\n const realtimeNode = await SonareRealtimeEngineNode.create(context, options);\n const offlineEngine = options.offlineEngine ?? new RealtimeEngine(sampleRate, blockSize);\n return new SonareEngine(\n context,\n realtimeNode,\n offlineEngine,\n sampleRate,\n blockSize,\n channelCount,\n );\n }\n\n async suspend(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n await this.context.suspend?.();\n }\n\n async resume(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n await this.context.resume?.();\n }\n\n setTempo(bpm: number): void {\n this.offlineEngine.setTempo(bpm);\n this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetTempoMap,\n sampleTime: -1,\n argFloat: bpm,\n });\n }\n\n setLoop(startPpq: number, endPpq: number, enabled = true): boolean {\n this.offlineEngine.setLoop(startPpq, endPpq, enabled);\n // Transport precision contract: the SAB command record carries exactly one\n // Float64 lane (argFloat) and one Int64 lane (argInt). startPpq travels in\n // argFloat with full double precision, matching the offline engine; endPpq\n // is carried as micro-PPQ (round(endPpq * 1e6)) in the integer lane and\n // divided back by 1e6 on the consumer. Loop ENDS are therefore snapped to\n // the nearest 1e-6 PPQ over the realtime transport (max 5e-7 PPQ drift),\n // while loop STARTS and the offline path stay exact. This is intentional:\n // the record has no second free Float64 lane, and a micro-PPQ grid on the\n // loop end is well below audible/sample-accurate resolution at any tempo.\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetLoop,\n targetId: enabled ? 1 : 0,\n sampleTime: -1,\n argFloat: startPpq,\n argInt: Math.round(endPpq * 1_000_000),\n });\n }\n\n setParam(nodeId: string, param: string | number, value: number): boolean {\n const paramId = this.resolveParamId(nodeId, param);\n // Mirror the change into the offline engine so a subsequent offline render\n // reflects the live value, then push a sample-accurate command to the\n // realtime runtime (mirrors setTempo/setLoop above).\n this.offlineEngine.setParameter(paramId, value);\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetParam,\n targetId: paramId,\n sampleTime: -1,\n argFloat: value,\n });\n }\n\n scheduleParam(\n nodeId: string,\n param: string | number,\n ppq: number,\n value: number,\n curve: number | 'linear' | 'exponential' = 'linear',\n ): void {\n const paramId = this.resolveParamId(nodeId, param);\n const lane = this.automationLanes.get(paramId) ?? [];\n lane.push({ ppq, value, curveToNext: this.curveCode(curve) });\n lane.sort((a, b) => a.ppq - b.ppq);\n this.automationLanes.set(paramId, lane);\n this.offlineEngine.setAutomationLane(paramId, lane);\n // Mirror the lane to the live worklet engine so scheduled automation plays\n // back in real time, not just in renderOffline(). Lanes can exceed the\n // fixed-size SAB command record, so they ride an out-of-band 'syncAutomation'\n // message applied outside process() (like syncClips/syncMarkers).\n this.postSync({ type: 'syncAutomation', paramId, points: lane });\n }\n\n addAutomationPoint(\n laneId: string | number,\n ppq: number,\n value: number,\n curve: number | 'linear' | 'exponential' = 'linear',\n ): void {\n this.scheduleParam('', laneId, ppq, value, curve);\n }\n\n listParameters(): EngineParameterInfo[] {\n const parameters: EngineParameterInfo[] = [];\n for (let index = 0; index < this.offlineEngine.parameterCount(); index++) {\n parameters.push(this.offlineEngine.parameterInfoByIndex(index));\n }\n return parameters;\n }\n\n setSoloMute(target: string | number, solo: boolean, mute: boolean): boolean {\n void target;\n void solo;\n void mute;\n // Per-track solo/mute is a Mixer concept (strip-indexed setSoloed/setMuted),\n // not an engine command: the WASM SonareEngine facade does not own a Mixer\n // node, so there is no realtime transport for solo/mute on this surface.\n // Throw a clear error instead of silently returning false (a silent no-op\n // would leave callers believing the change took effect). Apply solo/mute\n // directly on a Mixer instance (Mixer.setSoloed/Mixer.setMuted) instead.\n throw new Error(\n 'SonareEngine.setSoloMute is not supported: solo/mute is a Mixer feature; ' +\n 'use Mixer.setSoloed(stripIndex, ...) / Mixer.setMuted(stripIndex, ...) instead.',\n );\n }\n\n addClip(\n trackId: string | number,\n buffer: Float32Array[],\n startPpq: number,\n opts: Partial<Omit<EngineClip, 'channels' | 'startPpq'>> = {},\n ): number {\n const id = opts.id ?? this.nextClipId++;\n const clip: EngineClip = {\n ...opts,\n id,\n channels: buffer,\n startPpq,\n };\n this.clips.set(id, clip);\n this.syncClips();\n void trackId;\n return id;\n }\n\n removeClip(clipId: number): void {\n this.clips.delete(clipId);\n this.syncClips();\n }\n\n armRecord(trackId: string | number, enabled: boolean): boolean {\n this.offlineEngine.armCapture(enabled);\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.ArmRecord,\n targetId: this.resolveTargetId(trackId),\n sampleTime: -1,\n argInt: enabled ? 1 : 0,\n });\n }\n\n punch(inPpq: number, outPpq: number): boolean {\n const inSample = this.ppqToApproxSample(inPpq);\n const outSample = this.ppqToApproxSample(outPpq);\n this.offlineEngine.setCapturePunch(inSample, outSample, true);\n // Carry BOTH endpoints as already-converted SAMPLES so the realtime engine\n // agrees with the offline engine. The previous code sent the raw PPQ out\n // point and let the consumer multiply by sampleRate (treating PPQ as\n // seconds), which ignored tempo and produced a punch-out ~2x too large at\n // 120 BPM. argInt = in sample, argFloat = out sample (full-precision double).\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.Punch,\n sampleTime: -1,\n argInt: inSample,\n argFloat: outSample,\n });\n }\n\n setMetronome(opts: EngineMetronomeConfig): void {\n this.offlineEngine.setMetronome(opts);\n // The full config (beatGain/accentGain/clickSamples/clickSeconds) cannot fit\n // the fixed-size SAB command record, so it is delivered out-of-band; the\n // SetMetronome command then toggles enabled state on the audio thread.\n this.postSync({ type: 'syncMetronome', config: opts });\n this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SetMetronome,\n sampleTime: -1,\n argInt: opts.enabled ? 1 : 0,\n });\n }\n\n addMarker(ppq: number, name = ''): number {\n const id = this.nextMarkerId++;\n this.markers.set(id, { id, ppq, name });\n this.syncMarkers();\n return id;\n }\n\n seekMarker(markerId: number): boolean {\n this.offlineEngine.seekMarker(markerId);\n // Forward to the live worklet engine. Its marker set is kept in sync via the\n // 'syncMarkers' message (see syncMarkers), so a queued kSeekMarker resolves\n // the marker id to its frame on the audio thread. Returns the sendCommand\n // result (previously this method always returned a misleading `false`).\n return this.realtimeNode.sendCommand({\n type: SonareEngineCommandType.SeekMarker,\n targetId: markerId,\n sampleTime: -1,\n });\n }\n\n async renderOffline(totalFrames: number): Promise<Float32Array[]> {\n const frames = Math.max(0, Math.floor(totalFrames));\n const inputs: Float32Array[] = [];\n for (let ch = 0; ch < this.offlineChannelCount; ch++) {\n inputs.push(new Float32Array(frames));\n }\n return this.offlineEngine.renderOffline(inputs, this.offlineBlockSize);\n }\n\n onMeter(callback: (meter: SonareWorkletMeterSnapshot) => void): () => void {\n return this.realtimeNode.onMeter(callback);\n }\n\n onTelemetry(callback: (telemetry: SonareEngineTelemetryRecord) => void): () => void {\n return this.realtimeNode.onTelemetry(callback);\n }\n\n pollTelemetry(): SonareEngineTelemetryRecord[] {\n return this.realtimeNode.pollTelemetry();\n }\n\n pollMeters(): SonareWorkletMeterSnapshot[] {\n return this.realtimeNode.pollMeters();\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.transport.stop();\n this.realtimeNode.pollTelemetry();\n this.realtimeNode.destroy();\n this.offlineEngine.destroy();\n }\n\n private syncClips(): void {\n const clips = Array.from(this.clips.values());\n this.offlineEngine.setClips(clips);\n // Push the full clip set to the live worklet engine over the message port.\n // Clip audio buffers are too large for the fixed-size SAB command record, so\n // they ride a structured-clone 'syncClips' message applied OUTSIDE the audio\n // render callback (the audio-thread equivalent of an RtPublisher swap).\n this.postSync({ type: 'syncClips', clips });\n }\n\n private syncMarkers(): void {\n const markers = Array.from(this.markers.values()).sort((a, b) => a.ppq - b.ppq);\n this.offlineEngine.setMarkers(markers);\n this.postSync({ type: 'syncMarkers', markers });\n }\n\n // Posts an out-of-band control-sync message to the worklet engine processor.\n // Sync messages use a string `type` so the worklet's message handler routes\n // them to receiveSync() (numeric `type` is reserved for SonareEngineCommandRecord).\n private postSync(message: SonareEngineSyncMessage): void {\n if (this.destroyed) {\n return;\n }\n this.realtimeNode.node.port.postMessage(message);\n }\n\n private resolveParamId(nodeId: string, param: string | number): number {\n if (typeof param === 'number') {\n return param;\n }\n const byName = this.listParameters().find((info) => info.name === param);\n if (byName) {\n return byName.id;\n }\n return this.resolveTargetId(param || nodeId);\n }\n\n private resolveTargetId(target: string | number): number {\n if (typeof target === 'number') {\n return target;\n }\n const parsed = Number.parseInt(target, 10);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n\n private curveCode(curve: number | 'linear' | 'exponential'): number {\n if (typeof curve === 'number') {\n return curve;\n }\n return curve === 'exponential' ? 1 : 0;\n }\n\n private ppqToApproxSample(ppq: number): number {\n return Math.max(0, Math.round(((ppq * 60) / 120) * this.sampleRate));\n }\n}\n\nexport class SonareRealtimeVoiceChangerWorkletProcessor {\n private static warnedMonoOverflow = false;\n private static warnedInterleavedOverflow = false;\n private changer: RealtimeVoiceChanger;\n private readonly sampleRate: number;\n private readonly blockSize: number;\n private readonly channelCount: number;\n // WASM-heap typed-memory views, sized to the worst case (blockSize *\n // channelCount). Acquired on the main thread (constructor) so the\n // audio-thread process() never crosses an allocation boundary.\n private monoInput: Float32Array;\n private monoOutput: Float32Array;\n // Planar heap-backed views (one Float32Array per channel) used by the\n // multi-channel path. AudioWorklet inputs/outputs are already planar\n // Float32Arrays, so this avoids the per-sample interleave/deinterleave\n // passes that the older interleaved path needed.\n private planarChannels: Float32Array[];\n private destroyed = false;\n\n constructor(options: SonareRealtimeVoiceChangerWorkletProcessorOptions = {}) {\n this.sampleRate = options.sampleRate ?? 48000;\n this.blockSize = options.blockSize ?? 128;\n this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 1));\n this.changer = new RealtimeVoiceChanger(options.preset ?? 'neutral-monitor');\n this.changer.prepare(this.sampleRate, this.blockSize, this.channelCount);\n // Acquire WASM-heap views once, sized to the worst case. These are alive\n // for the lifetime of the changer; if the host requests more frames per\n // process() than blockSize, we clamp (see ensure*Capacity).\n this.monoInput = this.changer.getMonoInputBuffer(this.blockSize);\n this.monoOutput = this.changer.getMonoOutputBuffer(this.blockSize);\n this.planarChannels = [];\n if (this.channelCount > 1) {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.planarChannels.push(this.changer.getPlanarChannelBuffer(ch, this.blockSize));\n }\n }\n }\n\n /**\n * Handles a control-plane message from the main thread. Runs on the\n * AudioWorklet global scope but OUTSIDE of `process()` (i.e. outside the\n * realtime audio callback), so it is safe to perform JSON parsing and\n * DSP coefficient recomputation here. `setConfig` MUST NOT be deferred\n * into `process()` because that would block the audio thread for longer\n * than one render quantum (e.g. 128 samples / 44.1 kHz = ~2.9 ms).\n */\n receiveMessage(message: SonareRealtimeVoiceChangerMessage): void {\n if (this.destroyed) {\n return;\n }\n if (message.type === 'setConfig') {\n // Apply synchronously on the message-handler thread. `setConfig` may\n // allocate and parse JSON internally; doing it here keeps `process()`\n // realtime-safe.\n this.changer.setConfig(message.preset);\n } else if (message.type === 'reset') {\n this.changer.reset();\n } else if (message.type === 'destroy') {\n this.destroy();\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n const output = outputs[0];\n if (this.destroyed || !output || output.length === 0) {\n return !this.destroyed;\n }\n\n // The cached heap views can detach if WASM linear memory grows (the embind\n // module is built ALLOW_MEMORY_GROWTH). Re-acquire them if detached\n // (byteLength === 0) before touching them; in the common no-growth case this\n // is a cheap branch with no allocation.\n if (this.monoInput.byteLength === 0) {\n this.reacquireBuffers();\n }\n\n const input = inputs[0];\n const requestedFrames = output[0]?.length ?? 0;\n const requestedChannels = Math.min(this.channelCount, output.length);\n if (requestedFrames === 0 || requestedChannels === 0) {\n return true;\n }\n\n if (requestedChannels === 1) {\n // Clamp to the pre-allocated capacity; warn (once) if the host violated\n // the contract. We never reallocate on the audio thread.\n const frames = this.ensureMonoCapacity(requestedFrames);\n const source = input?.[0];\n if (source) {\n this.monoInput.set(source.subarray(0, frames));\n } else {\n this.monoInput.fill(0, 0, frames);\n }\n this.changer.processMonoInto(\n this.monoInput.subarray(0, frames),\n this.monoOutput.subarray(0, frames),\n );\n output[0].set(this.monoOutput.subarray(0, frames));\n return true;\n }\n\n const frames = this.ensureInterleavedCapacity(requestedFrames, requestedChannels);\n const channels = requestedChannels;\n // Planar zero-copy path: AudioWorklet's input[ch] is already a\n // Float32Array per channel, so we set() straight into the heap-backed\n // planar view and processPreparedPlanar runs in place.\n for (let ch = 0; ch < channels; ch++) {\n const src = input?.[ch];\n const dst = this.planarChannels[ch];\n if (!dst) {\n continue;\n }\n if (src) {\n dst.set(src.subarray(0, frames));\n } else {\n dst.fill(0, 0, frames);\n }\n }\n this.changer.processPreparedPlanar(frames);\n for (let ch = 0; ch < channels; ch++) {\n const src = this.planarChannels[ch];\n if (src) {\n output[ch].set(src.subarray(0, frames));\n }\n // No `for frame` inner loop needed; output[ch] is a Float32Array.\n }\n return true;\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n this.changer.delete();\n }\n\n // Re-acquires the cached WASM-heap views after a memory-growth detachment.\n // The underlying C++ vectors are pre-warmed (ensure_*_capacity ran at prepare\n // time), so getMono*/getPlanar* return fresh views onto the SAME storage\n // without reallocating it.\n private reacquireBuffers(): void {\n this.monoInput = this.changer.getMonoInputBuffer(this.blockSize);\n this.monoOutput = this.changer.getMonoOutputBuffer(this.blockSize);\n if (this.channelCount > 1) {\n for (let ch = 0; ch < this.channelCount; ch++) {\n this.planarChannels[ch] = this.changer.getPlanarChannelBuffer(ch, this.blockSize);\n }\n }\n }\n\n /**\n * Returns the number of frames we can actually process given the\n * pre-allocated capacity. If the host requests more frames than the\n * worst-case block size declared at construction time, we clamp to the\n * available capacity and warn once — we MUST NOT reallocate on the\n * realtime audio thread.\n */\n private ensureMonoCapacity(frames: number): number {\n const capacity = this.monoInput.length;\n if (frames <= capacity) {\n return frames;\n }\n if (!SonareRealtimeVoiceChangerWorkletProcessor.warnedMonoOverflow) {\n SonareRealtimeVoiceChangerWorkletProcessor.warnedMonoOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeVoiceChangerWorkletProcessor: requested ${frames} mono frames ` +\n `exceeds pre-allocated capacity ${capacity}; clamping. ` +\n 'Increase blockSize at construction time to avoid this.',\n );\n }\n return capacity;\n }\n\n /**\n * Same contract as ensureMonoCapacity but for the planar per-channel\n * scratch. Returns the number of frames that fit in the available capacity.\n */\n private ensureInterleavedCapacity(frames: number, channels: number): number {\n const capacity = this.planarChannels[0]?.length ?? 0;\n if (frames <= capacity) {\n return frames;\n }\n if (!SonareRealtimeVoiceChangerWorkletProcessor.warnedInterleavedOverflow) {\n SonareRealtimeVoiceChangerWorkletProcessor.warnedInterleavedOverflow = true;\n // biome-ignore lint/suspicious/noConsole: realtime-safety diagnostic.\n console.warn(\n `SonareRealtimeVoiceChangerWorkletProcessor: requested ${frames}x${channels} ` +\n `planar frames exceeds pre-allocated capacity ${capacity}; clamping. ` +\n 'Increase blockSize or channelCount at construction time to avoid this.',\n );\n }\n return capacity;\n }\n}\n\nexport function registerSonareWorkletProcessor(name = 'sonare-worklet-processor'): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareWorkletProcessor extends Base {\n private bridge: SonareWorkletProcessor;\n readonly port?: WorkletPort;\n\n constructor(options?: { processorOptions?: SonareWorkletProcessorOptions }) {\n super();\n const port = this.port;\n this.bridge = new SonareWorkletProcessor(options?.processorOptions ?? { sceneJson: '' }, {\n postMessage: (message) => port?.postMessage?.(message),\n });\n const onMessage = (event: { data: unknown }) => {\n if (isWorkletMessage(event.data)) {\n this.bridge.receiveMessage(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n return this.bridge.process(inputs, outputs);\n }\n }\n scope.registerProcessor(name, RegisteredSonareWorkletProcessor);\n}\n\nexport function registerSonareRealtimeVoiceChangerWorkletProcessor(\n name = 'sonare-realtime-voice-changer-processor',\n): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareRealtimeVoiceChangerWorkletProcessor extends Base {\n private bridge: SonareRealtimeVoiceChangerWorkletProcessor;\n readonly port?: WorkletPort;\n\n constructor(options?: {\n processorOptions?: SonareRealtimeVoiceChangerWorkletProcessorOptions;\n }) {\n super();\n const port = this.port;\n this.bridge = new SonareRealtimeVoiceChangerWorkletProcessor(options?.processorOptions ?? {});\n const onMessage = (event: { data: unknown }) => {\n if (isRealtimeVoiceChangerMessage(event.data)) {\n this.bridge.receiveMessage(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n return this.bridge.process(inputs, outputs);\n }\n }\n scope.registerProcessor(name, RegisteredSonareRealtimeVoiceChangerWorkletProcessor);\n}\n\nexport function registerSonareRealtimeEngineWorkletProcessor(\n name = 'sonare-realtime-engine-processor',\n): void {\n const scope = globalThis as unknown as {\n AudioWorkletProcessor?: new () => object;\n registerProcessor?: (processorName: string, processorCtor: unknown) => void;\n };\n if (!scope.AudioWorkletProcessor || !scope.registerProcessor) {\n throw new Error('AudioWorkletProcessor is not available in this context.');\n }\n const Base = scope.AudioWorkletProcessor;\n class RegisteredSonareRealtimeEngineWorkletProcessor extends Base {\n private bridge?: SonareRealtimeEngineWorkletProcessor;\n private rtBridge?: SonareRtRealtimeEngineRuntime;\n readonly port?: WorkletPort;\n\n constructor(options?: { processorOptions?: SonareRealtimeEngineWorkletProcessorOptions }) {\n super();\n const port = this.port;\n const processorOptions = options?.processorOptions ?? {};\n if (processorOptions.runtimeTarget === 'sonare-rt') {\n void this.initializeSonareRt(processorOptions, port);\n } else {\n this.bridge = new SonareRealtimeEngineWorkletProcessor(processorOptions, {\n postMessage: (message) => port?.postMessage?.(message),\n onMeter: (meter) => port?.postMessage?.(meter),\n });\n }\n const onMessage = (event: { data: unknown }) => {\n if (isEngineCommandRecord(event.data)) {\n this.bridge?.receiveCommand(event.data);\n this.rtBridge?.receiveCommand(event.data);\n } else if (isEngineSyncMessage(event.data)) {\n this.bridge?.receiveSync(event.data);\n this.rtBridge?.receiveSync(event.data);\n }\n };\n if (port?.addEventListener) {\n port.addEventListener('message', onMessage);\n port.start?.();\n } else if (port) {\n port.onmessage = onMessage;\n }\n }\n\n process(inputs: WorkletInput, outputs: WorkletOutput): boolean {\n if (this.rtBridge) {\n return this.rtBridge.process(inputs, outputs);\n }\n if (this.bridge) {\n return this.bridge.process(inputs, outputs);\n }\n const output = outputs[0];\n for (const channel of output ?? []) {\n channel.fill(0);\n }\n return true;\n }\n\n private async initializeSonareRt(\n options: SonareRealtimeEngineWorkletProcessorOptions,\n port?: WorkletPort,\n ): Promise<void> {\n try {\n if (!options.rtModuleUrl) {\n throw new Error('rtModuleUrl is required for sonare-rt AudioWorklet runtime.');\n }\n const rtModuleUrl = options.rtModuleUrl;\n const memory = new WebAssembly.Memory({ initial: 1024, maximum: 1024, shared: true });\n const globalFactory = (\n globalThis as typeof globalThis & {\n SonareRtModuleFactory?: (options?: {\n wasmMemory?: WebAssembly.Memory;\n wasmBinary?: ArrayBuffer | Uint8Array;\n locateFile?: (path: string) => string;\n }) => Promise<SonareRtModule>;\n }\n ).SonareRtModuleFactory;\n const moduleFactory = globalFactory\n ? { default: globalFactory }\n : ((await import(rtModuleUrl)) as {\n default: (options?: {\n wasmMemory?: WebAssembly.Memory;\n wasmBinary?: ArrayBuffer | Uint8Array;\n locateFile?: (path: string) => string;\n }) => Promise<SonareRtModule>;\n });\n const module = await moduleFactory.default({\n wasmMemory: memory,\n wasmBinary: options.rtWasmBinary,\n locateFile: (path) => rtModuleUrl.replace(/[^/]*$/, path),\n });\n this.rtBridge = new SonareRtRealtimeEngineRuntime({\n module,\n memory,\n sampleRate: options.sampleRate,\n blockSize: options.blockSize,\n channelCount: options.channelCount,\n commandSharedBuffer: options.commandSharedBuffer,\n commandRingCapacity: options.commandRingCapacity,\n telemetrySharedBuffer: options.telemetrySharedBuffer,\n telemetryRingCapacity: options.telemetryRingCapacity,\n });\n port?.postMessage?.({ type: 'ready', runtimeTarget: 'sonare-rt' });\n } catch (error) {\n port?.postMessage?.({\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n scope.registerProcessor(name, RegisteredSonareRealtimeEngineWorkletProcessor);\n}\n"],"mappings":"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","names":["module","module","module","module","SonareEngineCommandType","SonareEngineTelemetryType","SonareEngineTelemetryError","frames","module"]}
|