@jadujoel/web-audio-clip-node 0.1.1 → 0.1.3
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 +9 -3
- package/dist/audio/processor-code.d.ts +1 -1
- package/dist/audio/processor-code.js +1 -1
- package/dist/audio/processor-kernel.js +20 -6
- package/dist/audio/version.d.ts +1 -1
- package/dist/audio/version.js +1 -1
- package/dist/lib.bundle.js +6 -0
- package/dist/lib.bundle.js.map +7 -0
- package/dist/processor.js +2 -2
- package/dist/processor.js.map +3 -3
- package/examples/cdn-vanilla/README.md +6 -6
- package/examples/cdn-vanilla/index.html +4 -8
- package/examples/esm-bundler/README.md +3 -4
- package/examples/esm-bundler/bun.lock +15 -0
- package/examples/esm-bundler/index.html +1 -1
- package/examples/esm-bundler/main.ts +41 -0
- package/examples/esm-bundler/package.json +3 -6
- package/examples/react/README.md +2 -2
- package/examples/react/package.json +3 -5
- package/examples/self-hosted/src/main.ts +24 -24
- package/package.json +2 -1
- package/examples/esm-bundler/src/main.ts +0 -43
- package/examples/react/vite.config.ts +0 -6
package/dist/processor.js.map
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"sources": ["../src/audio/types.ts", "../src/audio/processor-kernel.ts", "../src/audio/processor.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"export const State = {\n\tInitial: 0,\n\tStarted: 1,\n\tStopped: 2,\n\tPaused: 3,\n\tScheduled: 4,\n\tEnded: 5,\n\tDisposed: 6,\n} as const;\n\nexport type ClipProcessorState = (typeof State)[keyof typeof State];\n\nexport interface ClipProcessorOptions {\n\tbuffer?: Float32Array[];\n\tstreamBuffer?: StreamBufferState;\n\tloop?: boolean;\n\tloopStart?: number;\n\tloopEnd?: number;\n\tloopCrossfade?: number;\n\toffset?: number;\n\tduration?: number;\n\tplayhead?: number;\n\tstate?: ClipProcessorState;\n\tstartWhen?: number;\n\tstopWhen?: number;\n\tpauseWhen?: number;\n\tresumeWhen?: number;\n\tplayedSamples?: number;\n\ttimesLooped?: number;\n\tfadeInDuration?: number;\n\tfadeOutDuration?: number;\n\tenableFadeIn?: boolean;\n\tenableFadeOut?: boolean;\n\tenableLoopStart?: boolean;\n\tenableLoopEnd?: boolean;\n\tenableLoopCrossfade?: boolean;\n\tenableGain?: boolean;\n\tenablePan?: boolean;\n\tenableHighpass?: boolean;\n\tenableLowpass?: boolean;\n\tenableDetune?: boolean;\n\tenablePlaybackRate?: boolean;\n}\n\nexport interface ClipWorkletOptions extends AudioWorkletNodeOptions {\n\tprocessorOptions?: ClipProcessorOptions;\n}\n\nexport type ClipNodeState =\n\t| \"initial\"\n\t| \"scheduled\"\n\t| \"started\"\n\t| \"stopped\"\n\t| \"paused\"\n\t| \"resumed\"\n\t| \"ended\"\n\t| \"disposed\";\n\nexport type FrameData = readonly [\n\tcurrentTime: number,\n\tcurrentFrame: number,\n\tplayhead: number,\n\ttimeTaken: number,\n];\n\nexport type ClipProcessorToggleMessageType =\n\t| \"toggleFadeIn\"\n\t| \"toggleFadeOut\"\n\t| \"toggleLoopStart\"\n\t| \"toggleLoopEnd\"\n\t| \"toggleLoopCrossfade\"\n\t| \"toggleGain\"\n\t| \"togglePan\"\n\t| \"toggleHighpass\"\n\t| \"toggleLowpass\"\n\t| \"toggleDetune\"\n\t| \"togglePlaybackRate\";\n\n// ---------------------------------------------------------------------------\n// Processor message types (moved from processor.ts)\n// ---------------------------------------------------------------------------\n\nexport interface ClipProcessorOnmessageEvent {\n\treadonly data: ClipProcessorMessageRx;\n}\n\nexport type ClipProcessorOnmessage = (ev: ClipProcessorOnmessageEvent) => void;\n\nexport interface ProcessorWorkletOptions extends AudioWorkletNodeOptions {\n\treadonly processorOptions?: ClipProcessorOptions;\n}\n\nexport interface ClipProcessorStateMap {\n\treadonly Initial: 0;\n\treadonly Started: 1;\n\treadonly Stopped: 2;\n\treadonly Paused: 3;\n\treadonly Scheduled: 4;\n\treadonly Ended: 5;\n\treadonly Disposed: 6;\n}\n\nexport type ClipProcessorMessageRx =\n\t| ClipProcessorBufferMessageRx\n\t| ClipProcessorBufferInitMessageRx\n\t| ClipProcessorBufferRangeMessageRx\n\t| ClipProcessorBufferEndMessageRx\n\t| ClipProcessorBufferResetMessageRx\n\t| ClipProcessorStartMessageRx\n\t| ClipProcessorStopMessageRx\n\t| ClipProcessorPauseMessageRx\n\t| ClipProcessorResumeMessageRx\n\t| ClipProcessorDisposeMessageRx\n\t| ClipProcessorLoopMessageRx\n\t| ClipProcessorLoopStartMessageRx\n\t| ClipProcessorLoopEndMessageRx\n\t| ClipProcessorPlayheadMessageRx\n\t| ClipProcessorFadeInMessageRx\n\t| ClipProcessorFadeOutMessageRx\n\t| ClipProcessorLoopCrossfadeMessageRx\n\t| ClipProcessorToggleMessageRx\n\t| ClipProcessorLogStateMessageRx;\n\nexport type ClipProcessorMessageType =\n\t| \"buffer\"\n\t| \"bufferInit\"\n\t| \"bufferRange\"\n\t| \"bufferEnd\"\n\t| \"bufferReset\"\n\t| \"start\"\n\t| \"stop\"\n\t| \"pause\"\n\t| \"resume\"\n\t| \"dispose\"\n\t| \"loop\"\n\t| \"loopStart\"\n\t| \"loopEnd\"\n\t| \"playhead\"\n\t| \"playbackRate\"\n\t| \"offset\"\n\t| \"fadeIn\"\n\t| \"fadeOut\"\n\t| \"loopCrossfade\"\n\t| ClipProcessorToggleMessageType\n\t| \"logState\";\n\nexport interface ClipProcessorLogStateMessageRx {\n\treadonly type: \"logState\";\n\treadonly data?: never;\n}\n\nexport interface ClipProcessorToggleMessageRx {\n\treadonly type: ClipProcessorToggleMessageType;\n\treadonly data?: boolean;\n}\n\nexport interface ClipProcessorBufferMessageRx {\n\treadonly type: \"buffer\";\n\treadonly data: Float32Array[];\n}\n\nexport interface StreamBufferSpan {\n\tstartSample: number;\n\tendSample: number;\n}\n\nexport interface BufferRangeWrite {\n\treadonly startSample: number;\n\treadonly channelData: Float32Array[];\n\treadonly totalLength?: number | null;\n\treadonly streamEnded?: boolean;\n}\n\nexport interface StreamBufferState {\n\ttotalLength: number | null;\n\tcommittedLength: number;\n\tstreamEnded: boolean;\n\tstreaming: boolean;\n\twrittenSpans: StreamBufferSpan[];\n\tpendingWrites: BufferRangeWrite[];\n\tlowWaterThreshold: number;\n\tlowWaterNotified: boolean;\n\tlastUnderrunSample: number | null;\n}\n\nexport interface ClipProcessorBufferInitMessageRx {\n\treadonly type: \"bufferInit\";\n\treadonly data: {\n\t\treadonly channels: number;\n\t\treadonly totalLength: number;\n\t\treadonly streaming?: boolean;\n\t};\n}\n\nexport interface ClipProcessorBufferRangeMessageRx {\n\treadonly type: \"bufferRange\";\n\treadonly data: BufferRangeWrite;\n}\n\nexport interface ClipProcessorBufferEndMessageRx {\n\treadonly type: \"bufferEnd\";\n\treadonly data?: {\n\t\treadonly totalLength?: number;\n\t};\n}\n\nexport interface ClipProcessorBufferResetMessageRx {\n\treadonly type: \"bufferReset\";\n\treadonly data?: never;\n}\n\nexport interface ClipProcessorStartMessageRx {\n\treadonly type: \"start\";\n\treadonly data?: {\n\t\treadonly duration?: number;\n\t\treadonly offset?: number;\n\t\treadonly when?: number;\n\t};\n}\n\nexport interface ClipProcessorStopMessageRx {\n\treadonly type: \"stop\";\n\treadonly data?: number;\n}\n\nexport interface ClipProcessorPauseMessageRx {\n\treadonly type: \"pause\";\n\treadonly data?: number;\n}\n\nexport interface ClipProcessorResumeMessageRx {\n\treadonly type: \"resume\";\n\treadonly data?: number;\n}\n\nexport interface ClipProcessorDisposeMessageRx {\n\treadonly type: \"dispose\";\n\treadonly data?: never;\n}\n\nexport interface ClipProcessorLoopMessageRx {\n\treadonly type: \"loop\";\n\treadonly data: boolean;\n}\n\nexport interface ClipProcessorLoopStartMessageRx {\n\treadonly type: \"loopStart\";\n\treadonly data: number;\n}\n\nexport interface ClipProcessorLoopEndMessageRx {\n\treadonly type: \"loopEnd\";\n\treadonly data: number;\n}\n\nexport interface ClipProcessorPlayheadMessageRx {\n\treadonly type: \"playhead\";\n\treadonly data: number;\n}\n\nexport interface ClipProcessorFadeInMessageRx {\n\treadonly type: \"fadeIn\";\n\treadonly data: number;\n}\n\nexport interface ClipProcessorFadeOutMessageRx {\n\treadonly type: \"fadeOut\";\n\treadonly data: number;\n}\n\nexport interface ClipProcessorLoopCrossfadeMessageRx {\n\treadonly type: \"loopCrossfade\";\n\treadonly data: number;\n}\n\n// ---------------------------------------------------------------------------\n// Block parameters (used by kernel)\n// ---------------------------------------------------------------------------\n\nexport interface BlockParameters {\n\treadonly playhead: number;\n\treadonly durationSamples: number;\n\treadonly loop: boolean;\n\treadonly loopStartSamples: number;\n\treadonly loopEndSamples: number;\n\treadonly bufferLength: number;\n\treadonly playbackRates: Float32Array;\n}\n\nexport interface BlockReturnState {\n\treadonly playhead: number;\n\treadonly ended: boolean;\n\treadonly looped: boolean;\n\treadonly indexes: number[];\n}\n",
|
|
6
|
-
"// processor-kernel.ts — Pure DSP logic, state machine, all filters\n// NO AudioWorklet or platform dependencies. Fully testable.\n\nimport {\n\ttype BlockParameters,\n\ttype BlockReturnState,\n\ttype BufferRangeWrite,\n\ttype ClipProcessorOptions,\n\tState,\n\ttype StreamBufferSpan,\n\ttype StreamBufferState,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const SAMPLE_BLOCK_SIZE = 128;\n\nfunction createStreamBufferState(\n\tbuffer: Float32Array[] = [],\n): StreamBufferState {\n\tconst totalLength = buffer[0]?.length ?? 0;\n\tconst hasBuffer = totalLength > 0;\n\treturn {\n\t\ttotalLength: hasBuffer ? totalLength : null,\n\t\tcommittedLength: hasBuffer ? totalLength : 0,\n\t\tstreamEnded: hasBuffer,\n\t\tstreaming: false,\n\t\twrittenSpans: hasBuffer ? [{ startSample: 0, endSample: totalLength }] : [],\n\t\tpendingWrites: [],\n\t\tlowWaterThreshold: SAMPLE_BLOCK_SIZE * 4,\n\t\tlowWaterNotified: false,\n\t\tlastUnderrunSample: null,\n\t};\n}\n\nfunction getBufferLength(buffer: Float32Array[]): number {\n\treturn buffer[0]?.length ?? 0;\n}\n\nfunction getLogicalBufferLength(\n\tproperties: Required<ClipProcessorOptions>,\n): number {\n\treturn (\n\t\tproperties.streamBuffer.totalLength ?? getBufferLength(properties.buffer)\n\t);\n}\n\nfunction createSilentBuffer(channels: number, length: number): Float32Array[] {\n\treturn Array.from({ length: channels }, () => new Float32Array(length));\n}\n\nfunction mergeWrittenSpan(\n\tspans: StreamBufferSpan[],\n\tnextSpan: StreamBufferSpan,\n): StreamBufferSpan[] {\n\tconst merged = [...spans, nextSpan].sort(\n\t\t(a, b) => a.startSample - b.startSample,\n\t);\n\tconst result: StreamBufferSpan[] = [];\n\tfor (const span of merged) {\n\t\tconst previous = result[result.length - 1];\n\t\tif (!previous || span.startSample > previous.endSample) {\n\t\t\tresult.push({ ...span });\n\t\t\tcontinue;\n\t\t}\n\t\tprevious.endSample = Math.max(previous.endSample, span.endSample);\n\t}\n\treturn result;\n}\n\nfunction getCommittedLength(spans: StreamBufferSpan[]): number {\n\tlet committedLength = 0;\n\tfor (const span of spans) {\n\t\tif (span.startSample > committedLength) break;\n\t\tcommittedLength = Math.max(committedLength, span.endSample);\n\t}\n\treturn committedLength;\n}\n\nfunction resetLowWaterState(\n\tstreamBuffer: StreamBufferState,\n\tplayhead: number,\n): void {\n\tif (\n\t\tstreamBuffer.committedLength - Math.floor(playhead) >=\n\t\tstreamBuffer.lowWaterThreshold\n\t) {\n\t\tstreamBuffer.lowWaterNotified = false;\n\t}\n}\n\nfunction ensureBufferCapacity(\n\tproperties: Required<ClipProcessorOptions>,\n\trequiredChannels: number,\n\trequiredLength: number,\n): void {\n\tconst currentLength = getBufferLength(properties.buffer);\n\tconst currentChannels = properties.buffer.length;\n\tif (currentLength >= requiredLength && currentChannels >= requiredChannels) {\n\t\treturn;\n\t}\n\n\tconst nextLength = Math.max(currentLength, requiredLength);\n\tconst nextChannels = Math.max(currentChannels, requiredChannels);\n\tconst nextBuffer = createSilentBuffer(nextChannels, nextLength);\n\tfor (let ch = 0; ch < currentChannels; ch++) {\n\t\tnextBuffer[ch].set(properties.buffer[ch].subarray(0, currentLength));\n\t}\n\tproperties.buffer = nextBuffer;\n\tif (\n\t\tproperties.streamBuffer.totalLength == null ||\n\t\tproperties.streamBuffer.totalLength < nextLength\n\t) {\n\t\tproperties.streamBuffer.totalLength = nextLength;\n\t}\n}\n\nfunction applyBufferRangeWrite(\n\tproperties: Required<ClipProcessorOptions>,\n\twrite: BufferRangeWrite,\n): void {\n\tconst startSample = Math.max(Math.floor(write.startSample), 0);\n\tconst writeLength = write.channelData[0]?.length ?? 0;\n\tconst requestedTotalLength = write.totalLength ?? null;\n\tconst requiredLength = Math.max(\n\t\tstartSample + writeLength,\n\t\trequestedTotalLength ?? 0,\n\t);\n\tensureBufferCapacity(\n\t\tproperties,\n\t\tMath.max(write.channelData.length, properties.buffer.length, 1),\n\t\trequiredLength,\n\t);\n\n\tfor (let ch = 0; ch < write.channelData.length; ch++) {\n\t\tproperties.buffer[ch].set(write.channelData[ch], startSample);\n\t}\n\n\tif (requestedTotalLength != null) {\n\t\tproperties.streamBuffer.totalLength = requestedTotalLength;\n\t}\n\tif (writeLength > 0) {\n\t\tproperties.streamBuffer.writtenSpans = mergeWrittenSpan(\n\t\t\tproperties.streamBuffer.writtenSpans,\n\t\t\t{ startSample, endSample: startSample + writeLength },\n\t\t);\n\t\tproperties.streamBuffer.committedLength = getCommittedLength(\n\t\t\tproperties.streamBuffer.writtenSpans,\n\t\t);\n\t}\n\tif (write.streamEnded === true) {\n\t\tproperties.streamBuffer.streamEnded = true;\n\t}\n\tresetLowWaterState(properties.streamBuffer, properties.playhead);\n}\n\nfunction applyPendingBufferWrites(\n\tproperties: Required<ClipProcessorOptions>,\n): void {\n\tif (properties.streamBuffer.pendingWrites.length === 0) {\n\t\treturn;\n\t}\n\tfor (const write of properties.streamBuffer.pendingWrites) {\n\t\tapplyBufferRangeWrite(properties, write);\n\t}\n\tproperties.streamBuffer.pendingWrites = [];\n}\n\nfunction setWholeBuffer(\n\tproperties: Required<ClipProcessorOptions>,\n\tbuffer: Float32Array[],\n): void {\n\tproperties.buffer = buffer;\n\tproperties.streamBuffer = createStreamBufferState(buffer);\n}\n\n// ---------------------------------------------------------------------------\n// Properties & offset\n// ---------------------------------------------------------------------------\n\nexport function getProperties(\n\topts: ClipProcessorOptions = {},\n\tsampleRate: number,\n): Required<ClipProcessorOptions> {\n\tconst {\n\t\tbuffer = [],\n\t\tstreamBuffer = createStreamBufferState(buffer),\n\t\tduration = -1,\n\t\tloop = false,\n\t\tloopStart = 0,\n\t\tloopEnd = (buffer[0]?.length ?? 0) / sampleRate,\n\t\tloopCrossfade = 0,\n\t\tplayhead = 0,\n\t\toffset = 0,\n\t\tstartWhen = 0,\n\t\tstopWhen = 0,\n\t\tpauseWhen = 0,\n\t\tresumeWhen = 0,\n\t\tplayedSamples = 0,\n\t\tstate = State.Initial,\n\t\ttimesLooped = 0,\n\t\tfadeInDuration = 0,\n\t\tfadeOutDuration = 0,\n\t\tenableFadeIn = fadeInDuration > 0,\n\t\tenableFadeOut = fadeOutDuration > 0,\n\t\tenableLoopStart = true,\n\t\tenableLoopEnd = true,\n\t\tenableLoopCrossfade = loopCrossfade > 0,\n\t\tenableHighpass = true,\n\t\tenableLowpass = true,\n\t\tenableGain = true,\n\t\tenablePan = true,\n\t\tenableDetune = true,\n\t\tenablePlaybackRate = true,\n\t} = opts;\n\n\treturn {\n\t\tbuffer,\n\t\tstreamBuffer,\n\t\tloop,\n\t\tloopStart,\n\t\tloopEnd,\n\t\tloopCrossfade,\n\t\tduration,\n\t\tplayhead,\n\t\toffset,\n\t\tstartWhen,\n\t\tstopWhen,\n\t\tpauseWhen,\n\t\tresumeWhen,\n\t\tplayedSamples,\n\t\tstate,\n\t\ttimesLooped,\n\t\tfadeInDuration,\n\t\tfadeOutDuration,\n\t\tenableFadeIn,\n\t\tenableFadeOut,\n\t\tenableLoopStart,\n\t\tenableLoopEnd,\n\t\tenableHighpass,\n\t\tenableLowpass,\n\t\tenableGain,\n\t\tenablePan,\n\t\tenableDetune,\n\t\tenablePlaybackRate,\n\t\tenableLoopCrossfade,\n\t};\n}\n\nfunction getBufferDurationSeconds(\n\tproperties: Required<ClipProcessorOptions>,\n\tsampleRate: number,\n): number {\n\treturn getLogicalBufferLength(properties) / sampleRate;\n}\n\nfunction normalizeLoopBounds(\n\tproperties: Required<ClipProcessorOptions>,\n\tsampleRate: number,\n): void {\n\tconst bufferDuration = getBufferDurationSeconds(properties, sampleRate);\n\tif (bufferDuration <= 0) {\n\t\tproperties.loopStart = 0;\n\t\tproperties.loopEnd = 0;\n\t\treturn;\n\t}\n\n\tif (!Number.isFinite(properties.loopStart) || properties.loopStart < 0) {\n\t\tproperties.loopStart = 0;\n\t}\n\tif (properties.loopStart >= bufferDuration) {\n\t\tproperties.loopStart = 0;\n\t}\n\tif (\n\t\t!Number.isFinite(properties.loopEnd) ||\n\t\tproperties.loopEnd <= properties.loopStart ||\n\t\tproperties.loopEnd > bufferDuration\n\t) {\n\t\tproperties.loopEnd = bufferDuration;\n\t}\n}\n\nexport function setOffset(\n\tproperties: Required<ClipProcessorOptions>,\n\toffset: number | undefined,\n\tsampleRate: number,\n): number {\n\tif (offset === undefined) {\n\t\tproperties.offset = 0;\n\t\treturn 0;\n\t}\n\tif (offset < 0) {\n\t\treturn setOffset(\n\t\t\tproperties,\n\t\t\tgetLogicalBufferLength(properties) + offset,\n\t\t\tsampleRate,\n\t\t);\n\t}\n\tif (offset > (getLogicalBufferLength(properties) || 1) - 1) {\n\t\treturn setOffset(\n\t\t\tproperties,\n\t\t\tgetLogicalBufferLength(properties) % offset,\n\t\t\tsampleRate,\n\t\t);\n\t}\n\tconst offs = Math.floor(offset * sampleRate);\n\tproperties.offset = offs;\n\treturn offs;\n}\n\n// ---------------------------------------------------------------------------\n// Index calculation\n// ---------------------------------------------------------------------------\n\nexport function findIndexesNormal(p: BlockParameters): BlockReturnState {\n\tconst { playhead, bufferLength, loop, loopStartSamples, loopEndSamples } = p;\n\tlet length = 128;\n\tif (!loop && playhead + 128 > bufferLength) {\n\t\tlength = Math.max(bufferLength - playhead, 0);\n\t}\n\tconst indexes: number[] = new Array(length);\n\n\tif (!loop) {\n\t\tfor (let i = 0, head = playhead; i < length; i++, head++) {\n\t\t\tindexes[i] = head;\n\t\t}\n\t\tconst nextPlayhead = playhead + length;\n\t\treturn {\n\t\t\tplayhead: nextPlayhead,\n\t\t\tindexes,\n\t\t\tlooped: false,\n\t\t\tended: nextPlayhead >= bufferLength,\n\t\t};\n\t}\n\n\tlet head = playhead;\n\tlet looped = false;\n\tfor (let i = 0; i < length; i++, head++) {\n\t\tif (head >= loopEndSamples) {\n\t\t\thead = loopStartSamples + (head - loopEndSamples);\n\t\t\tlooped = true;\n\t\t}\n\t\tindexes[i] = head;\n\t}\n\treturn { indexes, looped, ended: false, playhead: head };\n}\n\nexport function findIndexesWithPlaybackRates(\n\tp: BlockParameters,\n): BlockReturnState {\n\tconst {\n\t\tplayhead,\n\t\tbufferLength,\n\t\tloop,\n\t\tloopStartSamples,\n\t\tloopEndSamples,\n\t\tplaybackRates,\n\t} = p;\n\tlet length = 128;\n\tif (!loop && playhead + 128 > bufferLength) {\n\t\tlength = Math.max(bufferLength - playhead, 0);\n\t}\n\tconst indexes: number[] = new Array(length);\n\tlet head = playhead;\n\tlet looped = false;\n\n\tif (loop) {\n\t\tfor (let i = 0; i < length; i++) {\n\t\t\tindexes[i] = Math.min(Math.max(Math.floor(head), 0), bufferLength - 1);\n\t\t\tconst rate = playbackRates[i] ?? playbackRates[0] ?? 1;\n\t\t\thead += rate;\n\t\t\tif (rate >= 0 && (head > loopEndSamples || head > bufferLength)) {\n\t\t\t\thead = loopStartSamples;\n\t\t\t\tlooped = true;\n\t\t\t} else if (rate < 0 && (head < loopStartSamples || head < 0)) {\n\t\t\t\thead = loopEndSamples;\n\t\t\t\tlooped = true;\n\t\t\t}\n\t\t}\n\t\treturn { playhead: head, indexes, looped, ended: false };\n\t}\n\n\tfor (let i = 0; i < length; i++) {\n\t\tindexes[i] = Math.min(Math.max(Math.floor(head), 0), bufferLength - 1);\n\t\thead += playbackRates[i] ?? playbackRates[0] ?? 1;\n\t}\n\treturn {\n\t\tplayhead: head,\n\t\tindexes,\n\t\tlooped: false,\n\t\tended: head >= bufferLength || head < 0,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Buffer operations\n// ---------------------------------------------------------------------------\n\nexport function fill(\n\ttarget: Float32Array[],\n\tsource: Float32Array[],\n\tindexes: number[],\n): void {\n\tfor (let i = 0; i < indexes.length; i++) {\n\t\tfor (let ch = 0; ch < target.length; ch++) {\n\t\t\ttarget[ch][i] = source[ch][indexes[i]];\n\t\t}\n\t}\n\tfor (let i = indexes.length; i < target[0].length; i++) {\n\t\tfor (let ch = 0; ch < target.length; ch++) {\n\t\t\ttarget[ch][i] = 0;\n\t\t}\n\t}\n}\n\nexport function fillWithSilence(buffer: Float32Array[]): void {\n\tfor (let ch = 0; ch < buffer.length; ch++) {\n\t\tfor (let j = 0; j < buffer[ch].length; j++) {\n\t\t\tbuffer[ch][j] = 0;\n\t\t}\n\t}\n}\n\nexport function monoToStereo(signal: Float32Array[]): void {\n\tconst r = new Float32Array(signal[0].length);\n\tfor (let i = 0; i < signal[0].length; i++) {\n\t\tr[i] = signal[0][i];\n\t}\n\tsignal.push(r);\n}\n\nexport function copy(source: Float32Array[], target: Float32Array[]): void {\n\tfor (let i = target.length; i < source.length; i++) {\n\t\ttarget[i] = new Float32Array(source[i].length);\n\t}\n\tfor (let ch = 0; ch < source.length; ch++) {\n\t\tfor (let i = 0; i < source[ch].length; i++) {\n\t\t\ttarget[ch][i] = source[ch][i];\n\t\t}\n\t}\n}\n\nexport function checkNans(output: Float32Array[]): number {\n\tlet numNans = 0;\n\tfor (let ch = 0; ch < output.length; ch++) {\n\t\tfor (let j = 0; j < output[ch].length; j++) {\n\t\t\tif (Number.isNaN(output[ch][j])) {\n\t\t\t\tnumNans++;\n\t\t\t\toutput[ch][j] = 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn numNans;\n}\n\n// ---------------------------------------------------------------------------\n// Filters\n// ---------------------------------------------------------------------------\n\nexport interface BiquadState {\n\tx_1: number;\n\tx_2: number;\n\ty_1: number;\n\ty_2: number;\n}\n\nexport function createFilterState(): BiquadState[] {\n\treturn [\n\t\t{ x_1: 0, x_2: 0, y_1: 0, y_2: 0 },\n\t\t{ x_1: 0, x_2: 0, y_1: 0, y_2: 0 },\n\t];\n}\n\nexport function gainFilter(arr: Float32Array[], gains: Float32Array): void {\n\tif (gains.length === 1) {\n\t\tconst g = gains[0];\n\t\tif (g === 1) return;\n\t\tfor (const ch of arr) {\n\t\t\tfor (let i = 0; i < ch.length; i++) ch[i] *= g;\n\t\t}\n\t\treturn;\n\t}\n\tlet g = gains[0];\n\tfor (const ch of arr) {\n\t\tfor (let i = 0; i < ch.length; i++) {\n\t\t\tg = gains[i] ?? g;\n\t\t\tch[i] *= g;\n\t\t}\n\t}\n}\n\nexport function panFilter(signal: Float32Array[], pans: Float32Array): void {\n\tlet pan = pans[0];\n\tfor (let i = 0; i < signal[0].length; i++) {\n\t\tpan = pans[i] ?? pan;\n\t\tconst leftGain = pan <= 0 ? 1 : 1 - pan;\n\t\tconst rightGain = pan >= 0 ? 1 : 1 + pan;\n\t\tsignal[0][i] *= leftGain;\n\t\tsignal[1][i] *= rightGain;\n\t}\n}\n\nexport function lowpassFilter(\n\tbuffer: Float32Array[],\n\tcutoffs: Float32Array,\n\tsampleRate: number,\n\tstates: BiquadState[],\n): void {\n\tfor (let channel = 0; channel < buffer.length; channel++) {\n\t\tconst arr = buffer[channel];\n\t\tlet { x_1, x_2, y_1, y_2 } = states[channel] ?? {\n\t\t\tx_1: 0,\n\t\t\tx_2: 0,\n\t\t\ty_1: 0,\n\t\t\ty_2: 0,\n\t\t};\n\t\tif (cutoffs.length === 1) {\n\t\t\tconst cutoff = cutoffs[0];\n\t\t\tif (cutoff >= 20000) return;\n\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\tconst b0 = (1 - Math.cos(w0)) / 2;\n\t\t\tconst b1 = 1 - Math.cos(w0);\n\t\t\tconst b2 = (1 - Math.cos(w0)) / 2;\n\t\t\tconst a0 = 1 + alpha;\n\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\tconst a2 = 1 - alpha;\n\t\t\tconst h0 = b0 / a0,\n\t\t\t\th1 = b1 / a0,\n\t\t\t\th2 = b2 / a0,\n\t\t\t\th3 = a1 / a0,\n\t\t\t\th4 = a2 / a0;\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y = h0 * x + h1 * x_1 + h2 * x_2 - h3 * y_1 - h4 * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t} else {\n\t\t\tconst prevCutoff = cutoffs[0];\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst cutoff = cutoffs[i] ?? prevCutoff;\n\t\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\t\tconst b0 = (1 - Math.cos(w0)) / 2;\n\t\t\t\tconst b1 = 1 - Math.cos(w0);\n\t\t\t\tconst b2 = (1 - Math.cos(w0)) / 2;\n\t\t\t\tconst a0 = 1 + alpha;\n\t\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\t\tconst a2 = 1 - alpha;\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t}\n\t\tstates[channel] = { x_1, x_2, y_1, y_2 };\n\t}\n}\n\nexport function highpassFilter(\n\tbuffer: Float32Array[],\n\tcutoffs: Float32Array,\n\tsampleRate: number,\n\tstates: BiquadState[],\n): void {\n\tfor (let channel = 0; channel < buffer.length; channel++) {\n\t\tconst arr = buffer[channel];\n\t\tlet { x_1, x_2, y_1, y_2 } = states[channel] ?? {\n\t\t\tx_1: 0,\n\t\t\tx_2: 0,\n\t\t\ty_1: 0,\n\t\t\ty_2: 0,\n\t\t};\n\t\tif (cutoffs.length === 1) {\n\t\t\tconst cutoff = cutoffs[0];\n\t\t\tif (cutoff <= 20) return;\n\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\tconst b0 = (1 + Math.cos(w0)) / 2;\n\t\t\tconst b1 = -(1 + Math.cos(w0));\n\t\t\tconst b2 = (1 + Math.cos(w0)) / 2;\n\t\t\tconst a0 = 1 + alpha;\n\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\tconst a2 = 1 - alpha;\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t} else {\n\t\t\tconst prevCutoff = cutoffs[0];\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst cutoff = cutoffs[i] ?? prevCutoff;\n\t\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\t\tconst b0 = (1 + Math.cos(w0)) / 2;\n\t\t\t\tconst b1 = -(1 + Math.cos(w0));\n\t\t\t\tconst b2 = (1 + Math.cos(w0)) / 2;\n\t\t\t\tconst a0 = 1 + alpha;\n\t\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\t\tconst a2 = 1 - alpha;\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t}\n\t\tstates[channel] = { x_1, x_2, y_1, y_2 };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Message handler\n// ---------------------------------------------------------------------------\n\nexport interface OutboundMessage {\n\ttype: string;\n\tdata?: unknown;\n}\n\nexport function handleProcessorMessage(\n\tproperties: Required<ClipProcessorOptions>,\n\tmessage: { type: string; data?: unknown },\n\tcurrentTime: number,\n\tsampleRate: number,\n): OutboundMessage[] {\n\tconst { type, data } = message;\n\tswitch (type) {\n\t\tcase \"buffer\":\n\t\t\tsetWholeBuffer(properties, data as Float32Array[]);\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\tcase \"bufferInit\": {\n\t\t\tconst init = data as {\n\t\t\t\tchannels: number;\n\t\t\t\ttotalLength: number;\n\t\t\t\tstreaming?: boolean;\n\t\t\t};\n\t\t\tproperties.buffer = createSilentBuffer(init.channels, init.totalLength);\n\t\t\tproperties.streamBuffer = {\n\t\t\t\t...createStreamBufferState(),\n\t\t\t\ttotalLength: init.totalLength,\n\t\t\t\tstreamEnded: false,\n\t\t\t\tstreaming: init.streaming ?? true,\n\t\t\t};\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\t}\n\t\tcase \"bufferRange\":\n\t\t\tproperties.streamBuffer.pendingWrites.push(data as BufferRangeWrite);\n\t\t\treturn [];\n\t\tcase \"bufferEnd\": {\n\t\t\tconst endData = data as { totalLength?: number } | undefined;\n\t\t\tif (endData?.totalLength != null) {\n\t\t\t\tproperties.streamBuffer.totalLength = endData.totalLength;\n\t\t\t}\n\t\t\tproperties.streamBuffer.streamEnded = true;\n\t\t\treturn [];\n\t\t}\n\t\tcase \"bufferReset\":\n\t\t\tproperties.buffer = [];\n\t\t\tproperties.streamBuffer = createStreamBufferState();\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\tcase \"start\":\n\t\t\tproperties.timesLooped = 0;\n\t\t\t{\n\t\t\t\tconst d = data as\n\t\t\t\t\t| { duration?: number; offset?: number; when?: number }\n\t\t\t\t\t| undefined;\n\t\t\t\tproperties.duration = d?.duration ?? -1;\n\t\t\t\tif (properties.duration === -1) {\n\t\t\t\t\tproperties.duration = properties.loop\n\t\t\t\t\t\t? Number.MAX_SAFE_INTEGER\n\t\t\t\t\t\t: (properties.buffer[0]?.length ?? 0) / sampleRate;\n\t\t\t\t}\n\t\t\t\tsetOffset(properties, d?.offset, sampleRate);\n\t\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\t\tproperties.playhead = properties.offset;\n\t\t\t\tproperties.startWhen = d?.when ?? currentTime;\n\t\t\t\tproperties.stopWhen = properties.startWhen + properties.duration;\n\t\t\t\tproperties.playedSamples = 0;\n\t\t\t\tproperties.state = State.Scheduled;\n\t\t\t}\n\t\t\treturn [{ type: \"scheduled\" }];\n\t\tcase \"stop\":\n\t\t\tif (\n\t\t\t\tproperties.state === State.Ended ||\n\t\t\t\tproperties.state === State.Initial\n\t\t\t)\n\t\t\t\treturn [];\n\t\t\tproperties.stopWhen = (data as number | undefined) ?? properties.stopWhen;\n\t\t\tproperties.state = State.Stopped;\n\t\t\treturn [{ type: \"stopped\" }];\n\t\tcase \"pause\":\n\t\t\tproperties.state = State.Paused;\n\t\t\tproperties.pauseWhen = (data as number | undefined) ?? currentTime;\n\t\t\treturn [{ type: \"paused\" }];\n\t\tcase \"resume\":\n\t\t\tproperties.state = State.Started;\n\t\t\tproperties.startWhen = (data as number | undefined) ?? currentTime;\n\t\t\treturn [{ type: \"resume\" }];\n\t\tcase \"dispose\":\n\t\t\tproperties.state = State.Disposed;\n\t\t\tproperties.buffer = [];\n\t\t\tproperties.streamBuffer = createStreamBufferState();\n\t\t\treturn [{ type: \"disposed\" }];\n\t\tcase \"loop\": {\n\t\t\tconst loop = data as boolean;\n\t\t\tconst st = properties.state;\n\t\t\tif (loop && (st === State.Scheduled || st === State.Started)) {\n\t\t\t\tproperties.stopWhen = Number.MAX_SAFE_INTEGER;\n\t\t\t\tproperties.duration = Number.MAX_SAFE_INTEGER;\n\t\t\t}\n\t\t\tproperties.loop = loop;\n\t\t\tif (loop) {\n\t\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\t\tcase \"loopStart\":\n\t\t\tproperties.loopStart = data as number;\n\t\t\treturn [];\n\t\tcase \"loopEnd\":\n\t\t\tproperties.loopEnd = data as number;\n\t\t\treturn [];\n\t\tcase \"loopCrossfade\":\n\t\t\tproperties.loopCrossfade = data as number;\n\t\t\treturn [];\n\t\tcase \"playhead\":\n\t\t\tproperties.playhead = Math.floor(data as number);\n\t\t\treturn [];\n\t\tcase \"fadeIn\":\n\t\t\tproperties.fadeInDuration = data as number;\n\t\t\treturn [];\n\t\tcase \"fadeOut\":\n\t\t\tproperties.fadeOutDuration = data as number;\n\t\t\treturn [];\n\t\tcase \"toggleGain\":\n\t\t\tproperties.enableGain =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableGain;\n\t\t\treturn [];\n\t\tcase \"togglePan\":\n\t\t\tproperties.enablePan =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enablePan;\n\t\t\treturn [];\n\t\tcase \"toggleLowpass\":\n\t\t\tproperties.enableLowpass =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLowpass;\n\t\t\treturn [];\n\t\tcase \"toggleHighpass\":\n\t\t\tproperties.enableHighpass =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableHighpass;\n\t\t\treturn [];\n\t\tcase \"toggleDetune\":\n\t\t\tproperties.enableDetune =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableDetune;\n\t\t\treturn [];\n\t\tcase \"togglePlaybackRate\":\n\t\t\tproperties.enablePlaybackRate =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enablePlaybackRate;\n\t\t\treturn [];\n\t\tcase \"toggleFadeIn\":\n\t\t\tproperties.enableFadeIn =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableFadeIn;\n\t\t\treturn [];\n\t\tcase \"toggleFadeOut\":\n\t\t\tproperties.enableFadeOut =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableFadeOut;\n\t\t\treturn [];\n\t\tcase \"toggleLoopStart\":\n\t\t\tproperties.enableLoopStart =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopStart;\n\t\t\treturn [];\n\t\tcase \"toggleLoopEnd\":\n\t\t\tproperties.enableLoopEnd =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopEnd;\n\t\t\treturn [];\n\t\tcase \"toggleLoopCrossfade\":\n\t\t\tproperties.enableLoopCrossfade =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopCrossfade;\n\t\t\treturn [];\n\t\tcase \"logState\":\n\t\t\treturn [];\n\t}\n\treturn [];\n}\n\n// ---------------------------------------------------------------------------\n// Process block\n// ---------------------------------------------------------------------------\n\nexport interface ProcessContext {\n\tcurrentTime: number;\n\tcurrentFrame: number;\n\tsampleRate: number;\n}\n\nexport interface ProcessResult {\n\tkeepAlive: boolean;\n\tmessages: OutboundMessage[];\n}\n\nexport function processBlock(\n\tprops: Required<ClipProcessorOptions>,\n\toutputs: Float32Array[][],\n\tparameters: Record<string, Float32Array>,\n\tctx: ProcessContext,\n\tfilterState: { lowpass: BiquadState[]; highpass: BiquadState[] },\n): ProcessResult {\n\tconst messages: OutboundMessage[] = [];\n\tlet state = props.state;\n\tif (state === State.Disposed) return { keepAlive: false, messages };\n\n\tapplyPendingBufferWrites(props);\n\n\tif (state === State.Initial) return { keepAlive: true, messages };\n\n\tif (state === State.Ended) {\n\t\tfillWithSilence(outputs[0]);\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tif (state === State.Scheduled) {\n\t\tif (ctx.currentTime >= props.startWhen) {\n\t\t\tstate = props.state = State.Started;\n\t\t\tmessages.push({ type: \"started\" });\n\t\t} else {\n\t\t\tfillWithSilence(outputs[0]);\n\t\t\treturn { keepAlive: true, messages };\n\t\t}\n\t} else if (state === State.Paused) {\n\t\tif (ctx.currentTime > props.pauseWhen) {\n\t\t\tfillWithSilence(outputs[0]);\n\t\t\treturn { keepAlive: true, messages };\n\t\t}\n\t}\n\n\tif (ctx.currentTime > props.stopWhen) {\n\t\tfillWithSilence(outputs[0]);\n\t\tprops.state = State.Ended;\n\t\tmessages.push({ type: \"ended\" });\n\t\tprops.playedSamples = 0;\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst output0 = outputs[0];\n\tconst sourceLength = getLogicalBufferLength(props);\n\tif (sourceLength === 0) {\n\t\tfillWithSilence(output0);\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst {\n\t\tplaybackRate: playbackRates,\n\t\tdetune: detunes,\n\t\tlowpass,\n\t\thighpass,\n\t\tgain: gains,\n\t\tpan: pans,\n\t} = parameters;\n\n\tconst {\n\t\tbuffer,\n\t\tloopStart,\n\t\tloopEnd,\n\t\tloopCrossfade,\n\t\tstopWhen,\n\t\tplayedSamples,\n\t\tenableLowpass,\n\t\tenableHighpass,\n\t\tenableGain,\n\t\tenablePan,\n\t\tenableDetune,\n\t\tenableFadeOut,\n\t\tenableFadeIn,\n\t\tenableLoopStart,\n\t\tenableLoopEnd,\n\t\tenableLoopCrossfade,\n\t\tplayhead,\n\t\tfadeInDuration,\n\t\tfadeOutDuration,\n\t} = props;\n\tconst hasIncompleteStream =\n\t\tprops.streamBuffer.streaming &&\n\t\tprops.streamBuffer.committedLength < sourceLength;\n\tconst loop = props.loop && !hasIncompleteStream;\n\n\tconst nc = Math.min(buffer.length, output0.length);\n\tconst durationSamples = props.duration * ctx.sampleRate;\n\n\tconst loopCrossfadeSamples = Math.floor(ctx.sampleRate * loopCrossfade);\n\tconst maxLoopStartSample = Math.max(sourceLength - SAMPLE_BLOCK_SIZE, 0);\n\tconst loopStartSamples = enableLoopStart\n\t\t? Math.min(Math.floor(loopStart * ctx.sampleRate), maxLoopStartSample)\n\t\t: 0;\n\tconst loopEndSamples = enableLoopEnd\n\t\t? Math.min(Math.floor(loopEnd * ctx.sampleRate), sourceLength)\n\t\t: sourceLength;\n\tconst loopLengthSamples = loopEndSamples - loopStartSamples;\n\n\t// Apply detune to playback rates: effectiveRate = rate * 2^(detune/1200)\n\tconst needsDetune = enableDetune && detunes.length > 0 && detunes[0] !== 0;\n\tlet effectiveRates = playbackRates;\n\tif (needsDetune) {\n\t\tconst len = Math.max(\n\t\t\tplaybackRates.length,\n\t\t\tdetunes.length,\n\t\t\tSAMPLE_BLOCK_SIZE,\n\t\t);\n\t\teffectiveRates = new Float32Array(len);\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tconst rate = playbackRates[i] ?? playbackRates[playbackRates.length - 1];\n\t\t\tconst cents = detunes[i] ?? detunes[detunes.length - 1];\n\t\t\teffectiveRates[i] = rate * 2 ** (cents / 1200);\n\t\t}\n\t}\n\n\tconst useRateIndexing = props.enablePlaybackRate || needsDetune;\n\tconst isZeroRateBlock =\n\t\tuseRateIndexing &&\n\t\teffectiveRates.length > 0 &&\n\t\teffectiveRates.every((rate) => rate === 0);\n\n\tif (\n\t\tprops.streamBuffer.streaming &&\n\t\t!props.streamBuffer.streamEnded &&\n\t\t!props.streamBuffer.lowWaterNotified &&\n\t\tprops.streamBuffer.committedLength - Math.floor(playhead) <\n\t\t\tprops.streamBuffer.lowWaterThreshold\n\t) {\n\t\tmessages.push({\n\t\t\ttype: \"bufferLowWater\",\n\t\t\tdata: {\n\t\t\t\tplayhead: Math.floor(playhead),\n\t\t\t\tcommittedLength: props.streamBuffer.committedLength,\n\t\t\t},\n\t\t});\n\t\tprops.streamBuffer.lowWaterNotified = true;\n\t}\n\n\tif (isZeroRateBlock) {\n\t\tfillWithSilence(output0);\n\t\tfor (let i = 1; i < outputs.length; i++) {\n\t\t\tcopy(output0, outputs[i]);\n\t\t}\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst blockParams: BlockParameters = {\n\t\tbufferLength: sourceLength,\n\t\tloop,\n\t\tplayhead,\n\t\tloopStartSamples,\n\t\tloopEndSamples,\n\t\tdurationSamples,\n\t\tplaybackRates: effectiveRates,\n\t};\n\n\tconst {\n\t\tindexes,\n\t\tended,\n\t\tlooped,\n\t\tplayhead: updatedPlayhead,\n\t} = useRateIndexing\n\t\t? findIndexesWithPlaybackRates(blockParams)\n\t\t: findIndexesNormal(blockParams);\n\n\tconst underrunSample = indexes.find(\n\t\t(index) =>\n\t\t\tindex >= props.streamBuffer.committedLength && index < sourceLength,\n\t);\n\tif (\n\t\tunderrunSample !== undefined &&\n\t\t!props.streamBuffer.streamEnded &&\n\t\tprops.streamBuffer.lastUnderrunSample !== underrunSample\n\t) {\n\t\tmessages.push({\n\t\t\ttype: \"bufferUnderrun\",\n\t\t\tdata: {\n\t\t\t\tplayhead: Math.floor(playhead),\n\t\t\t\tcommittedLength: props.streamBuffer.committedLength,\n\t\t\t\trequestedSample: underrunSample,\n\t\t\t},\n\t\t});\n\t\tprops.streamBuffer.lastUnderrunSample = underrunSample;\n\t} else if (underrunSample === undefined) {\n\t\tprops.streamBuffer.lastUnderrunSample = null;\n\t}\n\n\tfill(output0, buffer, indexes);\n\n\t// --- Loop crossfade ---\n\tconst xfadeNumSamples = Math.min(\n\t\tMath.floor(loopCrossfade * ctx.sampleRate),\n\t\tloopLengthSamples,\n\t);\n\tconst isWithinLoopRange =\n\t\tloop && playhead > loopStartSamples && playhead < loopEndSamples;\n\tconst needsCrossfade =\n\t\tenableLoopCrossfade &&\n\t\tloopCrossfadeSamples > 0 &&\n\t\tsourceLength > SAMPLE_BLOCK_SIZE;\n\n\tif (isWithinLoopRange && needsCrossfade) {\n\t\t// Crossfade out at loop start: fade out tail of previous loop iteration.\n\t\t// Source: reads from END of loop (loopEnd - xfade to loopEnd).\n\t\t{\n\t\t\tconst endIndex = loopStartSamples + xfadeNumSamples;\n\t\t\tif (\n\t\t\t\txfadeNumSamples > 0 &&\n\t\t\t\tplayhead > loopStartSamples &&\n\t\t\t\tplayhead < endIndex\n\t\t\t) {\n\t\t\t\tconst elapsed = playhead - loopStartSamples;\n\t\t\t\tconst n = Math.min(Math.floor(endIndex - playhead), SAMPLE_BLOCK_SIZE);\n\t\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\t\tconst position = (elapsed + i) / xfadeNumSamples;\n\t\t\t\t\tconst g = Math.cos((Math.PI * position) / 2);\n\t\t\t\t\tconst srcIdx = Math.floor(\n\t\t\t\t\t\tloopEndSamples - xfadeNumSamples + elapsed + i,\n\t\t\t\t\t);\n\t\t\t\t\tif (srcIdx >= 0 && srcIdx < sourceLength) {\n\t\t\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\t\t\toutput0[ch][i] += buffer[ch][srcIdx] * g;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Crossfade in approaching loop end: fade in head of next loop iteration.\n\t\t// Source: reads from START of loop (loopStart to loopStart + xfade).\n\t\t{\n\t\t\tconst startIndex = loopEndSamples - xfadeNumSamples;\n\t\t\tif (\n\t\t\t\txfadeNumSamples > 0 &&\n\t\t\t\tplayhead > startIndex &&\n\t\t\t\tplayhead < loopEndSamples\n\t\t\t) {\n\t\t\t\tconst elapsed = playhead - startIndex;\n\t\t\t\tconst n = Math.min(\n\t\t\t\t\tMath.floor(loopEndSamples - playhead),\n\t\t\t\t\tSAMPLE_BLOCK_SIZE,\n\t\t\t\t);\n\t\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\t\tconst position = (elapsed + i) / xfadeNumSamples;\n\t\t\t\t\tconst g = Math.sin((Math.PI * position) / 2);\n\t\t\t\t\tconst srcIdx = Math.floor(loopStartSamples + elapsed + i);\n\t\t\t\t\tif (srcIdx >= 0 && srcIdx < sourceLength) {\n\t\t\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\t\t\toutput0[ch][i] += buffer[ch][srcIdx] * g;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Fade in ---\n\tif (enableFadeIn && fadeInDuration > 0) {\n\t\tconst fadeInSamples = Math.floor(fadeInDuration * ctx.sampleRate);\n\t\tconst remaining = fadeInSamples - playedSamples;\n\t\tif (remaining > 0) {\n\t\t\tconst n = Math.min(remaining, SAMPLE_BLOCK_SIZE);\n\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\tconst t = (playedSamples + i) / fadeInSamples;\n\t\t\t\tconst g = t * t * t; // cubic: slow start, fast finish\n\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\toutput0[ch][i] *= g;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Fade out ---\n\tif (enableFadeOut && fadeOutDuration > 0) {\n\t\tconst fadeOutSamples = Math.floor(fadeOutDuration * ctx.sampleRate);\n\t\tconst remainingSamples = Math.floor(\n\t\t\tctx.sampleRate * (stopWhen - ctx.currentTime),\n\t\t);\n\t\tif (remainingSamples < fadeOutSamples + SAMPLE_BLOCK_SIZE) {\n\t\t\tfor (let i = 0; i < SAMPLE_BLOCK_SIZE; i++) {\n\t\t\t\tconst sampleRemaining = remainingSamples - i;\n\t\t\t\tif (sampleRemaining >= fadeOutSamples) continue; // not yet in fade zone\n\t\t\t\tconst t = sampleRemaining <= 0 ? 0 : sampleRemaining / fadeOutSamples;\n\t\t\t\tconst g = t * t * t; // cubic fade-out: fast drop, slow tail\n\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\toutput0[ch][i] *= g;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Filters ---\n\tif (enableLowpass)\n\t\tlowpassFilter(output0, lowpass, ctx.sampleRate, filterState.lowpass);\n\tif (enableHighpass)\n\t\thighpassFilter(output0, highpass, ctx.sampleRate, filterState.highpass);\n\tif (enableGain) gainFilter(output0, gains);\n\tif (nc === 1) monoToStereo(output0);\n\tif (enablePan) panFilter(output0, pans);\n\n\tif (looped) {\n\t\tprops.timesLooped++;\n\t\tmessages.push({ type: \"looped\", data: props.timesLooped });\n\t}\n\tif (ended) {\n\t\tprops.state = State.Ended;\n\t\tmessages.push({ type: \"ended\" });\n\t}\n\n\tprops.playedSamples += indexes.length;\n\tprops.playhead = updatedPlayhead;\n\n\tconst numNans = checkNans(output0);\n\tif (numNans > 0) {\n\t\tconsole.log({\n\t\t\tnumNans,\n\t\t\tindexes,\n\t\t\tplayhead: updatedPlayhead,\n\t\t\tended,\n\t\t\tlooped,\n\t\t\tsourceLength,\n\t\t});\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tfor (let i = 1; i < outputs.length; i++) {\n\t\tcopy(output0, outputs[i]);\n\t}\n\treturn { keepAlive: true, messages };\n}\n",
|
|
6
|
+
"// processor-kernel.ts — Pure DSP logic, state machine, all filters\n// NO AudioWorklet or platform dependencies. Fully testable.\n\nimport {\n\ttype BlockParameters,\n\ttype BlockReturnState,\n\ttype BufferRangeWrite,\n\ttype ClipProcessorOptions,\n\tState,\n\ttype StreamBufferSpan,\n\ttype StreamBufferState,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const SAMPLE_BLOCK_SIZE = 128;\n\nfunction createStreamBufferState(\n\tbuffer: Float32Array[] = [],\n): StreamBufferState {\n\tconst totalLength = buffer[0]?.length ?? 0;\n\tconst hasBuffer = totalLength > 0;\n\treturn {\n\t\ttotalLength: hasBuffer ? totalLength : null,\n\t\tcommittedLength: hasBuffer ? totalLength : 0,\n\t\tstreamEnded: hasBuffer,\n\t\tstreaming: false,\n\t\twrittenSpans: hasBuffer ? [{ startSample: 0, endSample: totalLength }] : [],\n\t\tpendingWrites: [],\n\t\tlowWaterThreshold: SAMPLE_BLOCK_SIZE * 4,\n\t\tlowWaterNotified: false,\n\t\tlastUnderrunSample: null,\n\t};\n}\n\nfunction getBufferLength(buffer: Float32Array[]): number {\n\treturn buffer[0]?.length ?? 0;\n}\n\nfunction getLogicalBufferLength(\n\tproperties: Required<ClipProcessorOptions>,\n): number {\n\treturn (\n\t\tproperties.streamBuffer.totalLength ?? getBufferLength(properties.buffer)\n\t);\n}\n\nfunction createSilentBuffer(channels: number, length: number): Float32Array[] {\n\treturn Array.from({ length: channels }, () => new Float32Array(length));\n}\n\nfunction mergeWrittenSpan(\n\tspans: StreamBufferSpan[],\n\tnextSpan: StreamBufferSpan,\n): StreamBufferSpan[] {\n\tconst merged = [...spans, nextSpan].sort(\n\t\t(a, b) => a.startSample - b.startSample,\n\t);\n\tconst result: StreamBufferSpan[] = [];\n\tfor (const span of merged) {\n\t\tconst previous = result[result.length - 1];\n\t\tif (!previous || span.startSample > previous.endSample) {\n\t\t\tresult.push({ ...span });\n\t\t\tcontinue;\n\t\t}\n\t\tprevious.endSample = Math.max(previous.endSample, span.endSample);\n\t}\n\treturn result;\n}\n\nfunction getCommittedLength(spans: StreamBufferSpan[]): number {\n\tlet committedLength = 0;\n\tfor (const span of spans) {\n\t\tif (span.startSample > committedLength) break;\n\t\tcommittedLength = Math.max(committedLength, span.endSample);\n\t}\n\treturn committedLength;\n}\n\nfunction resetLowWaterState(\n\tstreamBuffer: StreamBufferState,\n\tplayhead: number,\n): void {\n\tif (\n\t\tstreamBuffer.committedLength - Math.floor(playhead) >=\n\t\tstreamBuffer.lowWaterThreshold\n\t) {\n\t\tstreamBuffer.lowWaterNotified = false;\n\t}\n}\n\nfunction ensureBufferCapacity(\n\tproperties: Required<ClipProcessorOptions>,\n\trequiredChannels: number,\n\trequiredLength: number,\n): void {\n\tconst currentLength = getBufferLength(properties.buffer);\n\tconst currentChannels = properties.buffer.length;\n\tif (currentLength >= requiredLength && currentChannels >= requiredChannels) {\n\t\treturn;\n\t}\n\n\tconst nextLength = Math.max(currentLength, requiredLength);\n\tconst nextChannels = Math.max(currentChannels, requiredChannels);\n\tconst nextBuffer = createSilentBuffer(nextChannels, nextLength);\n\tfor (let ch = 0; ch < currentChannels; ch++) {\n\t\tnextBuffer[ch].set(properties.buffer[ch].subarray(0, currentLength));\n\t}\n\tproperties.buffer = nextBuffer;\n\tif (\n\t\tproperties.streamBuffer.totalLength == null ||\n\t\tproperties.streamBuffer.totalLength < nextLength\n\t) {\n\t\tproperties.streamBuffer.totalLength = nextLength;\n\t}\n}\n\nfunction applyBufferRangeWrite(\n\tproperties: Required<ClipProcessorOptions>,\n\twrite: BufferRangeWrite,\n): void {\n\tconst startSample = Math.max(Math.floor(write.startSample), 0);\n\tconst writeLength = write.channelData[0]?.length ?? 0;\n\tconst requestedTotalLength = write.totalLength ?? null;\n\tconst requiredLength = Math.max(\n\t\tstartSample + writeLength,\n\t\trequestedTotalLength ?? 0,\n\t);\n\tensureBufferCapacity(\n\t\tproperties,\n\t\tMath.max(write.channelData.length, properties.buffer.length, 1),\n\t\trequiredLength,\n\t);\n\n\tfor (let ch = 0; ch < write.channelData.length; ch++) {\n\t\tproperties.buffer[ch].set(write.channelData[ch], startSample);\n\t}\n\n\tif (requestedTotalLength != null) {\n\t\tproperties.streamBuffer.totalLength = requestedTotalLength;\n\t}\n\tif (writeLength > 0) {\n\t\tproperties.streamBuffer.writtenSpans = mergeWrittenSpan(\n\t\t\tproperties.streamBuffer.writtenSpans,\n\t\t\t{ startSample, endSample: startSample + writeLength },\n\t\t);\n\t\tproperties.streamBuffer.committedLength = getCommittedLength(\n\t\t\tproperties.streamBuffer.writtenSpans,\n\t\t);\n\t}\n\tif (write.streamEnded === true) {\n\t\tproperties.streamBuffer.streamEnded = true;\n\t}\n\tresetLowWaterState(properties.streamBuffer, properties.playhead);\n}\n\nfunction applyPendingBufferWrites(\n\tproperties: Required<ClipProcessorOptions>,\n): void {\n\tif (properties.streamBuffer.pendingWrites.length === 0) {\n\t\treturn;\n\t}\n\tfor (const write of properties.streamBuffer.pendingWrites) {\n\t\tapplyBufferRangeWrite(properties, write);\n\t}\n\tproperties.streamBuffer.pendingWrites = [];\n}\n\nfunction setWholeBuffer(\n\tproperties: Required<ClipProcessorOptions>,\n\tbuffer: Float32Array[],\n): void {\n\tproperties.buffer = buffer;\n\tproperties.streamBuffer = createStreamBufferState(buffer);\n}\n\n// ---------------------------------------------------------------------------\n// Properties & offset\n// ---------------------------------------------------------------------------\n\nexport function getProperties(\n\topts: ClipProcessorOptions = {},\n\tsampleRate: number,\n): Required<ClipProcessorOptions> {\n\tconst {\n\t\tbuffer = [],\n\t\tstreamBuffer = createStreamBufferState(buffer),\n\t\tduration = -1,\n\t\tloop = false,\n\t\tloopStart = 0,\n\t\tloopEnd = (buffer[0]?.length ?? 0) / sampleRate,\n\t\tloopCrossfade = 0,\n\t\tplayhead = 0,\n\t\toffset = 0,\n\t\tstartWhen = 0,\n\t\tstopWhen = 0,\n\t\tpauseWhen = 0,\n\t\tresumeWhen = 0,\n\t\tplayedSamples = 0,\n\t\tstate = State.Initial,\n\t\ttimesLooped = 0,\n\t\tfadeInDuration = 0,\n\t\tfadeOutDuration = 0,\n\t\tenableFadeIn = fadeInDuration > 0,\n\t\tenableFadeOut = fadeOutDuration > 0,\n\t\tenableLoopStart = true,\n\t\tenableLoopEnd = true,\n\t\tenableLoopCrossfade = loopCrossfade > 0,\n\t\tenableHighpass = true,\n\t\tenableLowpass = true,\n\t\tenableGain = true,\n\t\tenablePan = true,\n\t\tenableDetune = true,\n\t\tenablePlaybackRate = true,\n\t} = opts;\n\n\treturn {\n\t\tbuffer,\n\t\tstreamBuffer,\n\t\tloop,\n\t\tloopStart,\n\t\tloopEnd,\n\t\tloopCrossfade,\n\t\tduration,\n\t\tplayhead,\n\t\toffset,\n\t\tstartWhen,\n\t\tstopWhen,\n\t\tpauseWhen,\n\t\tresumeWhen,\n\t\tplayedSamples,\n\t\tstate,\n\t\ttimesLooped,\n\t\tfadeInDuration,\n\t\tfadeOutDuration,\n\t\tenableFadeIn,\n\t\tenableFadeOut,\n\t\tenableLoopStart,\n\t\tenableLoopEnd,\n\t\tenableHighpass,\n\t\tenableLowpass,\n\t\tenableGain,\n\t\tenablePan,\n\t\tenableDetune,\n\t\tenablePlaybackRate,\n\t\tenableLoopCrossfade,\n\t};\n}\n\nfunction getBufferDurationSeconds(\n\tproperties: Required<ClipProcessorOptions>,\n\tsampleRate: number,\n): number {\n\treturn getLogicalBufferLength(properties) / sampleRate;\n}\n\nfunction normalizeLoopBounds(\n\tproperties: Required<ClipProcessorOptions>,\n\tsampleRate: number,\n): void {\n\tconst bufferDuration = getBufferDurationSeconds(properties, sampleRate);\n\tif (bufferDuration <= 0) {\n\t\tproperties.loopStart = 0;\n\t\tproperties.loopEnd = 0;\n\t\treturn;\n\t}\n\n\tif (!Number.isFinite(properties.loopStart) || properties.loopStart < 0) {\n\t\tproperties.loopStart = 0;\n\t}\n\tif (properties.loopStart >= bufferDuration) {\n\t\tproperties.loopStart = 0;\n\t}\n\tif (\n\t\t!Number.isFinite(properties.loopEnd) ||\n\t\tproperties.loopEnd <= properties.loopStart ||\n\t\tproperties.loopEnd > bufferDuration\n\t) {\n\t\tproperties.loopEnd = bufferDuration;\n\t}\n}\n\nexport function setOffset(\n\tproperties: Required<ClipProcessorOptions>,\n\toffset: number | undefined,\n\tsampleRate: number,\n): number {\n\tif (offset === undefined) {\n\t\tproperties.offset = 0;\n\t\treturn 0;\n\t}\n\tif (offset < 0) {\n\t\treturn setOffset(\n\t\t\tproperties,\n\t\t\tgetLogicalBufferLength(properties) + offset,\n\t\t\tsampleRate,\n\t\t);\n\t}\n\tif (offset > (getLogicalBufferLength(properties) || 1) - 1) {\n\t\treturn setOffset(\n\t\t\tproperties,\n\t\t\tgetLogicalBufferLength(properties) % offset,\n\t\t\tsampleRate,\n\t\t);\n\t}\n\tconst offs = Math.floor(offset * sampleRate);\n\tproperties.offset = offs;\n\treturn offs;\n}\n\n// ---------------------------------------------------------------------------\n// Index calculation\n// ---------------------------------------------------------------------------\n\nexport function findIndexesNormal(p: BlockParameters): BlockReturnState {\n\tconst { playhead, bufferLength, loop, loopStartSamples, loopEndSamples } = p;\n\tlet length = 128;\n\tif (!loop && playhead + 128 > bufferLength) {\n\t\tlength = Math.max(bufferLength - playhead, 0);\n\t}\n\tconst indexes: number[] = new Array(length);\n\n\tif (!loop) {\n\t\tfor (let i = 0, head = playhead; i < length; i++, head++) {\n\t\t\tindexes[i] = head;\n\t\t}\n\t\tconst nextPlayhead = playhead + length;\n\t\treturn {\n\t\t\tplayhead: nextPlayhead,\n\t\t\tindexes,\n\t\t\tlooped: false,\n\t\t\tended: nextPlayhead >= bufferLength,\n\t\t};\n\t}\n\n\tlet head = playhead;\n\tlet looped = false;\n\tfor (let i = 0; i < length; i++, head++) {\n\t\tif (head >= loopEndSamples) {\n\t\t\thead = loopStartSamples + (head - loopEndSamples);\n\t\t\tlooped = true;\n\t\t}\n\t\tindexes[i] = head;\n\t}\n\treturn { indexes, looped, ended: false, playhead: head };\n}\n\nexport function findIndexesWithPlaybackRates(\n\tp: BlockParameters,\n): BlockReturnState {\n\tconst {\n\t\tplayhead,\n\t\tbufferLength,\n\t\tloop,\n\t\tloopStartSamples,\n\t\tloopEndSamples,\n\t\tplaybackRates,\n\t} = p;\n\tlet length = 128;\n\tif (!loop && playhead + 128 > bufferLength) {\n\t\tlength = Math.max(bufferLength - playhead, 0);\n\t}\n\tconst indexes: number[] = new Array(length);\n\tlet head = playhead;\n\tlet looped = false;\n\n\tif (loop) {\n\t\tfor (let i = 0; i < length; i++) {\n\t\t\tindexes[i] = Math.min(Math.max(Math.floor(head), 0), bufferLength - 1);\n\t\t\tconst rate = playbackRates[i] ?? playbackRates[0] ?? 1;\n\t\t\thead += rate;\n\t\t\tif (rate >= 0 && (head > loopEndSamples || head > bufferLength)) {\n\t\t\t\thead = loopStartSamples;\n\t\t\t\tlooped = true;\n\t\t\t} else if (rate < 0 && (head < loopStartSamples || head < 0)) {\n\t\t\t\thead = loopEndSamples;\n\t\t\t\tlooped = true;\n\t\t\t}\n\t\t}\n\t\treturn { playhead: head, indexes, looped, ended: false };\n\t}\n\n\tfor (let i = 0; i < length; i++) {\n\t\tindexes[i] = Math.min(Math.max(Math.floor(head), 0), bufferLength - 1);\n\t\thead += playbackRates[i] ?? playbackRates[0] ?? 1;\n\t}\n\treturn {\n\t\tplayhead: head,\n\t\tindexes,\n\t\tlooped: false,\n\t\tended: head >= bufferLength || head < 0,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Buffer operations\n// ---------------------------------------------------------------------------\n\nexport function fill(\n\ttarget: Float32Array[],\n\tsource: Float32Array[],\n\tindexes: number[],\n): void {\n\tconst nc = Math.min(target.length, source.length);\n\tfor (let i = 0; i < indexes.length; i++) {\n\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\ttarget[ch][i] = source[ch][indexes[i]];\n\t\t}\n\t}\n\tfor (let ch = nc; ch < target.length; ch++) {\n\t\tfor (let i = 0; i < target[ch].length; i++) {\n\t\t\ttarget[ch][i] = 0;\n\t\t}\n\t}\n\tfor (let i = indexes.length; i < target[0].length; i++) {\n\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\ttarget[ch][i] = 0;\n\t\t}\n\t}\n}\n\nexport function fillWithSilence(buffer: Float32Array[]): void {\n\tfor (let ch = 0; ch < buffer.length; ch++) {\n\t\tfor (let j = 0; j < buffer[ch].length; j++) {\n\t\t\tbuffer[ch][j] = 0;\n\t\t}\n\t}\n}\n\nexport function monoToStereo(signal: Float32Array[]): void {\n\tif (signal.length >= 2) {\n\t\t// Output already has a second channel — copy mono data into it\n\t\tfor (let i = 0; i < signal[0].length; i++) {\n\t\t\tsignal[1][i] = signal[0][i];\n\t\t}\n\t} else {\n\t\tconst r = new Float32Array(signal[0].length);\n\t\tfor (let i = 0; i < signal[0].length; i++) {\n\t\t\tr[i] = signal[0][i];\n\t\t}\n\t\tsignal.push(r);\n\t}\n}\n\nexport function copy(source: Float32Array[], target: Float32Array[]): void {\n\tfor (let i = target.length; i < source.length; i++) {\n\t\ttarget[i] = new Float32Array(source[i].length);\n\t}\n\tfor (let ch = 0; ch < source.length; ch++) {\n\t\tfor (let i = 0; i < source[ch].length; i++) {\n\t\t\ttarget[ch][i] = source[ch][i];\n\t\t}\n\t}\n}\n\nexport function checkNans(output: Float32Array[]): number {\n\tlet numNans = 0;\n\tfor (let ch = 0; ch < output.length; ch++) {\n\t\tfor (let j = 0; j < output[ch].length; j++) {\n\t\t\tif (Number.isNaN(output[ch][j])) {\n\t\t\t\tnumNans++;\n\t\t\t\toutput[ch][j] = 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn numNans;\n}\n\n// ---------------------------------------------------------------------------\n// Filters\n// ---------------------------------------------------------------------------\n\nexport interface BiquadState {\n\tx_1: number;\n\tx_2: number;\n\ty_1: number;\n\ty_2: number;\n}\n\nexport function createFilterState(): BiquadState[] {\n\treturn [\n\t\t{ x_1: 0, x_2: 0, y_1: 0, y_2: 0 },\n\t\t{ x_1: 0, x_2: 0, y_1: 0, y_2: 0 },\n\t];\n}\n\nexport function gainFilter(arr: Float32Array[], gains: Float32Array): void {\n\tif (gains.length === 1) {\n\t\tconst g = gains[0];\n\t\tif (g === 1) return;\n\t\tfor (const ch of arr) {\n\t\t\tfor (let i = 0; i < ch.length; i++) ch[i] *= g;\n\t\t}\n\t\treturn;\n\t}\n\tlet g = gains[0];\n\tfor (const ch of arr) {\n\t\tfor (let i = 0; i < ch.length; i++) {\n\t\t\tg = gains[i] ?? g;\n\t\t\tch[i] *= g;\n\t\t}\n\t}\n}\n\nexport function panFilter(signal: Float32Array[], pans: Float32Array): void {\n\tlet pan = pans[0];\n\tfor (let i = 0; i < signal[0].length; i++) {\n\t\tpan = pans[i] ?? pan;\n\t\tconst leftGain = pan <= 0 ? 1 : 1 - pan;\n\t\tconst rightGain = pan >= 0 ? 1 : 1 + pan;\n\t\tsignal[0][i] *= leftGain;\n\t\tsignal[1][i] *= rightGain;\n\t}\n}\n\nexport function lowpassFilter(\n\tbuffer: Float32Array[],\n\tcutoffs: Float32Array,\n\tsampleRate: number,\n\tstates: BiquadState[],\n): void {\n\tfor (let channel = 0; channel < buffer.length; channel++) {\n\t\tconst arr = buffer[channel];\n\t\tlet { x_1, x_2, y_1, y_2 } = states[channel] ?? {\n\t\t\tx_1: 0,\n\t\t\tx_2: 0,\n\t\t\ty_1: 0,\n\t\t\ty_2: 0,\n\t\t};\n\t\tif (cutoffs.length === 1) {\n\t\t\tconst cutoff = cutoffs[0];\n\t\t\tif (cutoff >= 20000) return;\n\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\tconst b0 = (1 - Math.cos(w0)) / 2;\n\t\t\tconst b1 = 1 - Math.cos(w0);\n\t\t\tconst b2 = (1 - Math.cos(w0)) / 2;\n\t\t\tconst a0 = 1 + alpha;\n\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\tconst a2 = 1 - alpha;\n\t\t\tconst h0 = b0 / a0,\n\t\t\t\th1 = b1 / a0,\n\t\t\t\th2 = b2 / a0,\n\t\t\t\th3 = a1 / a0,\n\t\t\t\th4 = a2 / a0;\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y = h0 * x + h1 * x_1 + h2 * x_2 - h3 * y_1 - h4 * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t} else {\n\t\t\tconst prevCutoff = cutoffs[0];\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst cutoff = cutoffs[i] ?? prevCutoff;\n\t\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\t\tconst b0 = (1 - Math.cos(w0)) / 2;\n\t\t\t\tconst b1 = 1 - Math.cos(w0);\n\t\t\t\tconst b2 = (1 - Math.cos(w0)) / 2;\n\t\t\t\tconst a0 = 1 + alpha;\n\t\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\t\tconst a2 = 1 - alpha;\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t}\n\t\tstates[channel] = { x_1, x_2, y_1, y_2 };\n\t}\n}\n\nexport function highpassFilter(\n\tbuffer: Float32Array[],\n\tcutoffs: Float32Array,\n\tsampleRate: number,\n\tstates: BiquadState[],\n): void {\n\tfor (let channel = 0; channel < buffer.length; channel++) {\n\t\tconst arr = buffer[channel];\n\t\tlet { x_1, x_2, y_1, y_2 } = states[channel] ?? {\n\t\t\tx_1: 0,\n\t\t\tx_2: 0,\n\t\t\ty_1: 0,\n\t\t\ty_2: 0,\n\t\t};\n\t\tif (cutoffs.length === 1) {\n\t\t\tconst cutoff = cutoffs[0];\n\t\t\tif (cutoff <= 20) return;\n\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\tconst b0 = (1 + Math.cos(w0)) / 2;\n\t\t\tconst b1 = -(1 + Math.cos(w0));\n\t\t\tconst b2 = (1 + Math.cos(w0)) / 2;\n\t\t\tconst a0 = 1 + alpha;\n\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\tconst a2 = 1 - alpha;\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t} else {\n\t\t\tconst prevCutoff = cutoffs[0];\n\t\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\t\tconst cutoff = cutoffs[i] ?? prevCutoff;\n\t\t\t\tconst w0 = (2 * Math.PI * cutoff) / sampleRate;\n\t\t\t\tconst alpha = Math.sin(w0) / 2;\n\t\t\t\tconst b0 = (1 + Math.cos(w0)) / 2;\n\t\t\t\tconst b1 = -(1 + Math.cos(w0));\n\t\t\t\tconst b2 = (1 + Math.cos(w0)) / 2;\n\t\t\t\tconst a0 = 1 + alpha;\n\t\t\t\tconst a1 = -2 * Math.cos(w0);\n\t\t\t\tconst a2 = 1 - alpha;\n\t\t\t\tconst x = arr[i];\n\t\t\t\tconst y =\n\t\t\t\t\t(b0 / a0) * x +\n\t\t\t\t\t(b1 / a0) * x_1 +\n\t\t\t\t\t(b2 / a0) * x_2 -\n\t\t\t\t\t(a1 / a0) * y_1 -\n\t\t\t\t\t(a2 / a0) * y_2;\n\t\t\t\tx_2 = x_1;\n\t\t\t\tx_1 = x;\n\t\t\t\ty_2 = y_1;\n\t\t\t\ty_1 = y;\n\t\t\t\tarr[i] = y;\n\t\t\t}\n\t\t}\n\t\tstates[channel] = { x_1, x_2, y_1, y_2 };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Message handler\n// ---------------------------------------------------------------------------\n\nexport interface OutboundMessage {\n\ttype: string;\n\tdata?: unknown;\n}\n\nexport function handleProcessorMessage(\n\tproperties: Required<ClipProcessorOptions>,\n\tmessage: { type: string; data?: unknown },\n\tcurrentTime: number,\n\tsampleRate: number,\n): OutboundMessage[] {\n\tconst { type, data } = message;\n\tswitch (type) {\n\t\tcase \"buffer\":\n\t\t\tsetWholeBuffer(properties, data as Float32Array[]);\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\tcase \"bufferInit\": {\n\t\t\tconst init = data as {\n\t\t\t\tchannels: number;\n\t\t\t\ttotalLength: number;\n\t\t\t\tstreaming?: boolean;\n\t\t\t};\n\t\t\tproperties.buffer = createSilentBuffer(init.channels, init.totalLength);\n\t\t\tproperties.streamBuffer = {\n\t\t\t\t...createStreamBufferState(),\n\t\t\t\ttotalLength: init.totalLength,\n\t\t\t\tstreamEnded: false,\n\t\t\t\tstreaming: init.streaming ?? true,\n\t\t\t};\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\t}\n\t\tcase \"bufferRange\":\n\t\t\tproperties.streamBuffer.pendingWrites.push(data as BufferRangeWrite);\n\t\t\treturn [];\n\t\tcase \"bufferEnd\": {\n\t\t\tconst endData = data as { totalLength?: number } | undefined;\n\t\t\tif (endData?.totalLength != null) {\n\t\t\t\tproperties.streamBuffer.totalLength = endData.totalLength;\n\t\t\t}\n\t\t\tproperties.streamBuffer.streamEnded = true;\n\t\t\treturn [];\n\t\t}\n\t\tcase \"bufferReset\":\n\t\t\tproperties.buffer = [];\n\t\t\tproperties.streamBuffer = createStreamBufferState();\n\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\treturn [];\n\t\tcase \"start\":\n\t\t\tproperties.timesLooped = 0;\n\t\t\t{\n\t\t\t\tconst d = data as\n\t\t\t\t\t| { duration?: number; offset?: number; when?: number }\n\t\t\t\t\t| undefined;\n\t\t\t\tproperties.duration = d?.duration ?? -1;\n\t\t\t\tif (properties.duration === -1) {\n\t\t\t\t\tproperties.duration = properties.loop\n\t\t\t\t\t\t? Number.MAX_SAFE_INTEGER\n\t\t\t\t\t\t: (properties.buffer[0]?.length ?? 0) / sampleRate;\n\t\t\t\t}\n\t\t\t\tsetOffset(properties, d?.offset, sampleRate);\n\t\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\t\tproperties.playhead = properties.offset;\n\t\t\t\tproperties.startWhen = d?.when ?? currentTime;\n\t\t\t\tproperties.stopWhen = properties.startWhen + properties.duration;\n\t\t\t\tproperties.playedSamples = 0;\n\t\t\t\tproperties.state = State.Scheduled;\n\t\t\t}\n\t\t\treturn [{ type: \"scheduled\" }];\n\t\tcase \"stop\":\n\t\t\tif (\n\t\t\t\tproperties.state === State.Ended ||\n\t\t\t\tproperties.state === State.Initial\n\t\t\t)\n\t\t\t\treturn [];\n\t\t\tproperties.stopWhen = (data as number | undefined) ?? properties.stopWhen;\n\t\t\tproperties.state = State.Stopped;\n\t\t\treturn [{ type: \"stopped\" }];\n\t\tcase \"pause\":\n\t\t\tproperties.state = State.Paused;\n\t\t\tproperties.pauseWhen = (data as number | undefined) ?? currentTime;\n\t\t\treturn [{ type: \"paused\" }];\n\t\tcase \"resume\":\n\t\t\tproperties.state = State.Started;\n\t\t\tproperties.startWhen = (data as number | undefined) ?? currentTime;\n\t\t\treturn [{ type: \"resume\" }];\n\t\tcase \"dispose\":\n\t\t\tproperties.state = State.Disposed;\n\t\t\tproperties.buffer = [];\n\t\t\tproperties.streamBuffer = createStreamBufferState();\n\t\t\treturn [{ type: \"disposed\" }];\n\t\tcase \"loop\": {\n\t\t\tconst loop = data as boolean;\n\t\t\tconst st = properties.state;\n\t\t\tif (loop && (st === State.Scheduled || st === State.Started)) {\n\t\t\t\tproperties.stopWhen = Number.MAX_SAFE_INTEGER;\n\t\t\t\tproperties.duration = Number.MAX_SAFE_INTEGER;\n\t\t\t}\n\t\t\tproperties.loop = loop;\n\t\t\tif (loop) {\n\t\t\t\tnormalizeLoopBounds(properties, sampleRate);\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\t\tcase \"loopStart\":\n\t\t\tproperties.loopStart = data as number;\n\t\t\treturn [];\n\t\tcase \"loopEnd\":\n\t\t\tproperties.loopEnd = data as number;\n\t\t\treturn [];\n\t\tcase \"loopCrossfade\":\n\t\t\tproperties.loopCrossfade = data as number;\n\t\t\treturn [];\n\t\tcase \"playhead\":\n\t\t\tproperties.playhead = Math.floor(data as number);\n\t\t\treturn [];\n\t\tcase \"fadeIn\":\n\t\t\tproperties.fadeInDuration = data as number;\n\t\t\treturn [];\n\t\tcase \"fadeOut\":\n\t\t\tproperties.fadeOutDuration = data as number;\n\t\t\treturn [];\n\t\tcase \"toggleGain\":\n\t\t\tproperties.enableGain =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableGain;\n\t\t\treturn [];\n\t\tcase \"togglePan\":\n\t\t\tproperties.enablePan =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enablePan;\n\t\t\treturn [];\n\t\tcase \"toggleLowpass\":\n\t\t\tproperties.enableLowpass =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLowpass;\n\t\t\treturn [];\n\t\tcase \"toggleHighpass\":\n\t\t\tproperties.enableHighpass =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableHighpass;\n\t\t\treturn [];\n\t\tcase \"toggleDetune\":\n\t\t\tproperties.enableDetune =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableDetune;\n\t\t\treturn [];\n\t\tcase \"togglePlaybackRate\":\n\t\t\tproperties.enablePlaybackRate =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enablePlaybackRate;\n\t\t\treturn [];\n\t\tcase \"toggleFadeIn\":\n\t\t\tproperties.enableFadeIn =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableFadeIn;\n\t\t\treturn [];\n\t\tcase \"toggleFadeOut\":\n\t\t\tproperties.enableFadeOut =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableFadeOut;\n\t\t\treturn [];\n\t\tcase \"toggleLoopStart\":\n\t\t\tproperties.enableLoopStart =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopStart;\n\t\t\treturn [];\n\t\tcase \"toggleLoopEnd\":\n\t\t\tproperties.enableLoopEnd =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopEnd;\n\t\t\treturn [];\n\t\tcase \"toggleLoopCrossfade\":\n\t\t\tproperties.enableLoopCrossfade =\n\t\t\t\t(data as boolean | undefined) ?? !properties.enableLoopCrossfade;\n\t\t\treturn [];\n\t\tcase \"logState\":\n\t\t\treturn [];\n\t}\n\treturn [];\n}\n\n// ---------------------------------------------------------------------------\n// Process block\n// ---------------------------------------------------------------------------\n\nexport interface ProcessContext {\n\tcurrentTime: number;\n\tcurrentFrame: number;\n\tsampleRate: number;\n}\n\nexport interface ProcessResult {\n\tkeepAlive: boolean;\n\tmessages: OutboundMessage[];\n}\n\nexport function processBlock(\n\tprops: Required<ClipProcessorOptions>,\n\toutputs: Float32Array[][],\n\tparameters: Record<string, Float32Array>,\n\tctx: ProcessContext,\n\tfilterState: { lowpass: BiquadState[]; highpass: BiquadState[] },\n): ProcessResult {\n\tconst messages: OutboundMessage[] = [];\n\tlet state = props.state;\n\tif (state === State.Disposed) return { keepAlive: false, messages };\n\n\tapplyPendingBufferWrites(props);\n\n\tif (state === State.Initial) return { keepAlive: true, messages };\n\n\tif (state === State.Ended) {\n\t\tfillWithSilence(outputs[0]);\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tif (state === State.Scheduled) {\n\t\tif (ctx.currentTime >= props.startWhen) {\n\t\t\tstate = props.state = State.Started;\n\t\t\tmessages.push({ type: \"started\" });\n\t\t} else {\n\t\t\tfillWithSilence(outputs[0]);\n\t\t\treturn { keepAlive: true, messages };\n\t\t}\n\t} else if (state === State.Paused) {\n\t\tif (ctx.currentTime > props.pauseWhen) {\n\t\t\tfillWithSilence(outputs[0]);\n\t\t\treturn { keepAlive: true, messages };\n\t\t}\n\t}\n\n\tif (ctx.currentTime > props.stopWhen) {\n\t\tfillWithSilence(outputs[0]);\n\t\tprops.state = State.Ended;\n\t\tmessages.push({ type: \"ended\" });\n\t\tprops.playedSamples = 0;\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst output0 = outputs[0];\n\tconst sourceLength = getLogicalBufferLength(props);\n\tif (sourceLength === 0) {\n\t\tfillWithSilence(output0);\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst {\n\t\tplaybackRate: playbackRates,\n\t\tdetune: detunes,\n\t\tlowpass,\n\t\thighpass,\n\t\tgain: gains,\n\t\tpan: pans,\n\t} = parameters;\n\n\tconst {\n\t\tbuffer,\n\t\tloopStart,\n\t\tloopEnd,\n\t\tloopCrossfade,\n\t\tstopWhen,\n\t\tplayedSamples,\n\t\tenableLowpass,\n\t\tenableHighpass,\n\t\tenableGain,\n\t\tenablePan,\n\t\tenableDetune,\n\t\tenableFadeOut,\n\t\tenableFadeIn,\n\t\tenableLoopStart,\n\t\tenableLoopEnd,\n\t\tenableLoopCrossfade,\n\t\tplayhead,\n\t\tfadeInDuration,\n\t\tfadeOutDuration,\n\t} = props;\n\tconst hasIncompleteStream =\n\t\tprops.streamBuffer.streaming &&\n\t\tprops.streamBuffer.committedLength < sourceLength;\n\tconst loop = props.loop && !hasIncompleteStream;\n\n\tconst nc = Math.min(buffer.length, output0.length);\n\tconst durationSamples = props.duration * ctx.sampleRate;\n\n\tconst loopCrossfadeSamples = Math.floor(ctx.sampleRate * loopCrossfade);\n\tconst maxLoopStartSample = Math.max(sourceLength - SAMPLE_BLOCK_SIZE, 0);\n\tconst loopStartSamples = enableLoopStart\n\t\t? Math.min(Math.floor(loopStart * ctx.sampleRate), maxLoopStartSample)\n\t\t: 0;\n\tconst loopEndSamples = enableLoopEnd\n\t\t? Math.min(Math.floor(loopEnd * ctx.sampleRate), sourceLength)\n\t\t: sourceLength;\n\tconst loopLengthSamples = loopEndSamples - loopStartSamples;\n\n\t// Apply detune to playback rates: effectiveRate = rate * 2^(detune/1200)\n\tconst needsDetune = enableDetune && detunes.length > 0 && detunes[0] !== 0;\n\tlet effectiveRates = playbackRates;\n\tif (needsDetune) {\n\t\tconst len = Math.max(\n\t\t\tplaybackRates.length,\n\t\t\tdetunes.length,\n\t\t\tSAMPLE_BLOCK_SIZE,\n\t\t);\n\t\teffectiveRates = new Float32Array(len);\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tconst rate = playbackRates[i] ?? playbackRates[playbackRates.length - 1];\n\t\t\tconst cents = detunes[i] ?? detunes[detunes.length - 1];\n\t\t\teffectiveRates[i] = rate * 2 ** (cents / 1200);\n\t\t}\n\t}\n\n\tconst useRateIndexing = props.enablePlaybackRate || needsDetune;\n\tconst isZeroRateBlock =\n\t\tuseRateIndexing &&\n\t\teffectiveRates.length > 0 &&\n\t\teffectiveRates.every((rate) => rate === 0);\n\n\tif (\n\t\tprops.streamBuffer.streaming &&\n\t\t!props.streamBuffer.streamEnded &&\n\t\t!props.streamBuffer.lowWaterNotified &&\n\t\tprops.streamBuffer.committedLength - Math.floor(playhead) <\n\t\t\tprops.streamBuffer.lowWaterThreshold\n\t) {\n\t\tmessages.push({\n\t\t\ttype: \"bufferLowWater\",\n\t\t\tdata: {\n\t\t\t\tplayhead: Math.floor(playhead),\n\t\t\t\tcommittedLength: props.streamBuffer.committedLength,\n\t\t\t},\n\t\t});\n\t\tprops.streamBuffer.lowWaterNotified = true;\n\t}\n\n\tif (isZeroRateBlock) {\n\t\tfillWithSilence(output0);\n\t\tfor (let i = 1; i < outputs.length; i++) {\n\t\t\tcopy(output0, outputs[i]);\n\t\t}\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tconst blockParams: BlockParameters = {\n\t\tbufferLength: sourceLength,\n\t\tloop,\n\t\tplayhead,\n\t\tloopStartSamples,\n\t\tloopEndSamples,\n\t\tdurationSamples,\n\t\tplaybackRates: effectiveRates,\n\t};\n\n\tconst {\n\t\tindexes,\n\t\tended,\n\t\tlooped,\n\t\tplayhead: updatedPlayhead,\n\t} = useRateIndexing\n\t\t? findIndexesWithPlaybackRates(blockParams)\n\t\t: findIndexesNormal(blockParams);\n\n\tconst underrunSample = indexes.find(\n\t\t(index) =>\n\t\t\tindex >= props.streamBuffer.committedLength && index < sourceLength,\n\t);\n\tif (\n\t\tunderrunSample !== undefined &&\n\t\t!props.streamBuffer.streamEnded &&\n\t\tprops.streamBuffer.lastUnderrunSample !== underrunSample\n\t) {\n\t\tmessages.push({\n\t\t\ttype: \"bufferUnderrun\",\n\t\t\tdata: {\n\t\t\t\tplayhead: Math.floor(playhead),\n\t\t\t\tcommittedLength: props.streamBuffer.committedLength,\n\t\t\t\trequestedSample: underrunSample,\n\t\t\t},\n\t\t});\n\t\tprops.streamBuffer.lastUnderrunSample = underrunSample;\n\t} else if (underrunSample === undefined) {\n\t\tprops.streamBuffer.lastUnderrunSample = null;\n\t}\n\n\tfill(output0, buffer, indexes);\n\n\t// --- Loop crossfade ---\n\tconst xfadeNumSamples = Math.min(\n\t\tMath.floor(loopCrossfade * ctx.sampleRate),\n\t\tloopLengthSamples,\n\t);\n\tconst isWithinLoopRange =\n\t\tloop && playhead > loopStartSamples && playhead < loopEndSamples;\n\tconst needsCrossfade =\n\t\tenableLoopCrossfade &&\n\t\tloopCrossfadeSamples > 0 &&\n\t\tsourceLength > SAMPLE_BLOCK_SIZE;\n\n\tif (isWithinLoopRange && needsCrossfade) {\n\t\t// Crossfade out at loop start: fade out tail of previous loop iteration.\n\t\t// Source: reads from END of loop (loopEnd - xfade to loopEnd).\n\t\t{\n\t\t\tconst endIndex = loopStartSamples + xfadeNumSamples;\n\t\t\tif (\n\t\t\t\txfadeNumSamples > 0 &&\n\t\t\t\tplayhead > loopStartSamples &&\n\t\t\t\tplayhead < endIndex\n\t\t\t) {\n\t\t\t\tconst elapsed = playhead - loopStartSamples;\n\t\t\t\tconst n = Math.min(Math.floor(endIndex - playhead), SAMPLE_BLOCK_SIZE);\n\t\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\t\tconst position = (elapsed + i) / xfadeNumSamples;\n\t\t\t\t\tconst g = Math.cos((Math.PI * position) / 2);\n\t\t\t\t\tconst srcIdx = Math.floor(\n\t\t\t\t\t\tloopEndSamples - xfadeNumSamples + elapsed + i,\n\t\t\t\t\t);\n\t\t\t\t\tif (srcIdx >= 0 && srcIdx < sourceLength) {\n\t\t\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\t\t\toutput0[ch][i] += buffer[ch][srcIdx] * g;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Crossfade in approaching loop end: fade in head of next loop iteration.\n\t\t// Source: reads from START of loop (loopStart to loopStart + xfade).\n\t\t{\n\t\t\tconst startIndex = loopEndSamples - xfadeNumSamples;\n\t\t\tif (\n\t\t\t\txfadeNumSamples > 0 &&\n\t\t\t\tplayhead > startIndex &&\n\t\t\t\tplayhead < loopEndSamples\n\t\t\t) {\n\t\t\t\tconst elapsed = playhead - startIndex;\n\t\t\t\tconst n = Math.min(\n\t\t\t\t\tMath.floor(loopEndSamples - playhead),\n\t\t\t\t\tSAMPLE_BLOCK_SIZE,\n\t\t\t\t);\n\t\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\t\tconst position = (elapsed + i) / xfadeNumSamples;\n\t\t\t\t\tconst g = Math.sin((Math.PI * position) / 2);\n\t\t\t\t\tconst srcIdx = Math.floor(loopStartSamples + elapsed + i);\n\t\t\t\t\tif (srcIdx >= 0 && srcIdx < sourceLength) {\n\t\t\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\t\t\toutput0[ch][i] += buffer[ch][srcIdx] * g;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Fade in ---\n\tif (enableFadeIn && fadeInDuration > 0) {\n\t\tconst fadeInSamples = Math.floor(fadeInDuration * ctx.sampleRate);\n\t\tconst remaining = fadeInSamples - playedSamples;\n\t\tif (remaining > 0) {\n\t\t\tconst n = Math.min(remaining, SAMPLE_BLOCK_SIZE);\n\t\t\tfor (let i = 0; i < n; i++) {\n\t\t\t\tconst t = (playedSamples + i) / fadeInSamples;\n\t\t\t\tconst g = t * t * t; // cubic: slow start, fast finish\n\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\toutput0[ch][i] *= g;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Fade out ---\n\tif (enableFadeOut && fadeOutDuration > 0) {\n\t\tconst fadeOutSamples = Math.floor(fadeOutDuration * ctx.sampleRate);\n\t\tconst remainingSamples = Math.floor(\n\t\t\tctx.sampleRate * (stopWhen - ctx.currentTime),\n\t\t);\n\t\tif (remainingSamples < fadeOutSamples + SAMPLE_BLOCK_SIZE) {\n\t\t\tfor (let i = 0; i < SAMPLE_BLOCK_SIZE; i++) {\n\t\t\t\tconst sampleRemaining = remainingSamples - i;\n\t\t\t\tif (sampleRemaining >= fadeOutSamples) continue; // not yet in fade zone\n\t\t\t\tconst t = sampleRemaining <= 0 ? 0 : sampleRemaining / fadeOutSamples;\n\t\t\t\tconst g = t * t * t; // cubic fade-out: fast drop, slow tail\n\t\t\t\tfor (let ch = 0; ch < nc; ch++) {\n\t\t\t\t\toutput0[ch][i] *= g;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Filters ---\n\tif (enableLowpass)\n\t\tlowpassFilter(output0, lowpass, ctx.sampleRate, filterState.lowpass);\n\tif (enableHighpass)\n\t\thighpassFilter(output0, highpass, ctx.sampleRate, filterState.highpass);\n\tif (enableGain) gainFilter(output0, gains);\n\tif (nc === 1) monoToStereo(output0);\n\tif (enablePan) panFilter(output0, pans);\n\n\tif (looped) {\n\t\tprops.timesLooped++;\n\t\tmessages.push({ type: \"looped\", data: props.timesLooped });\n\t}\n\tif (ended) {\n\t\tprops.state = State.Ended;\n\t\tmessages.push({ type: \"ended\" });\n\t}\n\n\tprops.playedSamples += indexes.length;\n\tprops.playhead = updatedPlayhead;\n\n\tconst numNans = checkNans(output0);\n\tif (numNans > 0) {\n\t\tconsole.log({\n\t\t\tnumNans,\n\t\t\tindexes,\n\t\t\tplayhead: updatedPlayhead,\n\t\t\tended,\n\t\t\tlooped,\n\t\t\tsourceLength,\n\t\t});\n\t\treturn { keepAlive: true, messages };\n\t}\n\n\tfor (let i = 1; i < outputs.length; i++) {\n\t\tcopy(output0, outputs[i]);\n\t}\n\treturn { keepAlive: true, messages };\n}\n",
|
|
7
7
|
"// AudioWorklet processor — runs in AudioWorkletGlobalScope\n// Bundled separately and served at /processor.js\n// This is a thin shell — all DSP logic lives in processor-kernel.ts\n\ndeclare const currentTime: number;\ndeclare const currentFrame: number;\ndeclare const sampleRate: number;\ndeclare class AudioWorkletProcessor {\n\treadonly port: MessagePort;\n\tconstructor(options?: AudioWorkletNodeOptions);\n\tprocess(\n\t\tinputs: Float32Array[][],\n\t\toutputs: Float32Array[][],\n\t\tparameters: Record<string, Float32Array>,\n\t): boolean;\n}\n\ndeclare function registerProcessor(\n\tname: string,\n\tctor: new (options?: AudioWorkletNodeOptions) => AudioWorkletProcessor,\n): void;\n\nimport {\n\tcreateFilterState,\n\tgetProperties,\n\thandleProcessorMessage,\n\tprocessBlock,\n} from \"./processor-kernel\";\nimport type { ClipProcessorOptions, ProcessorWorkletOptions } from \"./types\";\nimport { State } from \"./types\";\n\nclass ClipProcessor extends AudioWorkletProcessor {\n\tstatic get parameterDescriptors() {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"playbackRate\",\n\t\t\t\tautomationRate: \"a-rate\" as const,\n\t\t\t\tdefaultValue: 1.0,\n\t\t\t},\n\t\t\t{ name: \"detune\", automationRate: \"a-rate\" as const, defaultValue: 0 },\n\t\t\t{\n\t\t\t\tname: \"gain\",\n\t\t\t\tautomationRate: \"a-rate\" as const,\n\t\t\t\tdefaultValue: 1,\n\t\t\t\tminValue: 0,\n\t\t\t},\n\t\t\t{ name: \"pan\", automationRate: \"a-rate\" as const, defaultValue: 0 },\n\t\t\t{\n\t\t\t\tname: \"highpass\",\n\t\t\t\tautomationRate: \"a-rate\" as const,\n\t\t\t\tdefaultValue: 20,\n\t\t\t\tminValue: 20,\n\t\t\t\tmaxValue: 20000,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"lowpass\",\n\t\t\t\tautomationRate: \"a-rate\" as const,\n\t\t\t\tdefaultValue: 20000,\n\t\t\t\tminValue: 20,\n\t\t\t\tmaxValue: 20000,\n\t\t\t},\n\t\t];\n\t}\n\n\tproperties: Required<ClipProcessorOptions>;\n\tprivate filterState = {\n\t\tlowpass: createFilterState(),\n\t\thighpass: createFilterState(),\n\t};\n\tprivate lastFrameTime = 0;\n\n\tconstructor(options?: ProcessorWorkletOptions) {\n\t\tsuper(options);\n\t\tthis.properties = getProperties(options?.processorOptions, sampleRate);\n\t\tthis.port.onmessage = (ev: MessageEvent) => {\n\t\t\tconst messages = handleProcessorMessage(\n\t\t\t\tthis.properties,\n\t\t\t\tev.data,\n\t\t\t\tcurrentTime,\n\t\t\t\tsampleRate,\n\t\t\t);\n\t\t\tfor (const msg of messages) this.port.postMessage(msg);\n\t\t\tif (this.properties.state === State.Disposed) this.port.close();\n\t\t};\n\t}\n\n\tprocess(\n\t\t_inputs: Float32Array[][],\n\t\toutputs: Float32Array[][],\n\t\tparameters: Record<string, Float32Array>,\n\t): boolean {\n\t\ttry {\n\t\t\tconst result = processBlock(\n\t\t\t\tthis.properties,\n\t\t\t\toutputs,\n\t\t\t\tparameters,\n\t\t\t\t{ currentTime, currentFrame, sampleRate },\n\t\t\t\tthis.filterState,\n\t\t\t);\n\t\t\tfor (const msg of result.messages) this.port.postMessage(msg);\n\n\t\t\t// Frame reporting\n\t\t\tconst timeTaken = currentTime - this.lastFrameTime;\n\t\t\tthis.lastFrameTime = currentTime;\n\t\t\tthis.port.postMessage({\n\t\t\t\ttype: \"frame\",\n\t\t\t\tdata: [\n\t\t\t\t\tcurrentTime,\n\t\t\t\t\tcurrentFrame,\n\t\t\t\t\tMath.floor(this.properties.playhead),\n\t\t\t\t\ttimeTaken * 1000,\n\t\t\t\t],\n\t\t\t});\n\n\t\t\treturn result.keepAlive;\n\t\t} catch (e) {\n\t\t\tconsole.log(e);\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\nregisterProcessor(\"ClipProcessor\", ClipProcessor);\n"
|
|
8
8
|
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "E1A630180247A19C64756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# CDN Vanilla Example
|
|
2
2
|
|
|
3
|
-
Zero-install example —
|
|
3
|
+
Zero-install example — serve `index.html` with any static file server.
|
|
4
4
|
|
|
5
5
|
Everything is loaded from [jsDelivr](https://www.jsdelivr.com/).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
> **Note:** Opening the file directly (`file://`) won't work because browsers
|
|
8
|
+
> treat `file:` URLs as unique security origins, blocking cross-origin module
|
|
9
|
+
> imports.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
```sh
|
|
12
|
+
bun index.html
|
|
13
13
|
```
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
<button id="play" type="button">▶ Play</button>
|
|
16
16
|
<button id="stop" type="button">■ Stop</button>
|
|
17
17
|
<p id="status">Click Play to start (loads a sample tone).</p>
|
|
18
|
-
|
|
19
18
|
<script type="module">
|
|
20
|
-
// Import the
|
|
21
|
-
import { ClipNode, getProcessorCdnUrl } from "https://cdn.jsdelivr.net/npm/@jadujoel/web-audio-clip-node
|
|
19
|
+
// Import the bundled library from the CDN (single file, no module resolution needed)
|
|
20
|
+
import { ClipNode, getProcessorCdnUrl } from "https://cdn.jsdelivr.net/npm/@jadujoel/web-audio-clip-node/dist/lib.bundle.js";
|
|
22
21
|
|
|
23
22
|
const status = document.getElementById("status");
|
|
24
23
|
let ctx;
|
|
@@ -38,11 +37,8 @@
|
|
|
38
37
|
document.getElementById("play").addEventListener("click", async () => {
|
|
39
38
|
if (!ctx) {
|
|
40
39
|
ctx = new AudioContext();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
clip = new ClipNode(ctx, {
|
|
44
|
-
processorOptions: { sampleRate: ctx.sampleRate },
|
|
45
|
-
});
|
|
40
|
+
await ctx.audioWorklet.addModule("https://cdn.jsdelivr.net/npm/@jadujoel/web-audio-clip-node/dist/processor.js");
|
|
41
|
+
clip = new ClipNode(ctx);
|
|
46
42
|
clip.connect(ctx.destination);
|
|
47
43
|
clip.buffer = createToneBuffer(ctx);
|
|
48
44
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
# ESM Bundler Example (
|
|
1
|
+
# ESM Bundler Example (Bun + TypeScript)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The processor is loaded via `getProcessorBlobUrl()` (embedded, zero-config).
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
|
|
7
|
-
npm run dev
|
|
6
|
+
bun run dev
|
|
8
7
|
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 0,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "esm-bundler-example",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@jadujoel/web-audio-clip-node": "^0.1.2",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
"packages": {
|
|
13
|
+
"@jadujoel/web-audio-clip-node": ["@jadujoel/web-audio-clip-node@0.1.2", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18", "zustand": ">=4" }, "optionalPeers": ["react", "react-dom", "zustand"] }, "sha512-u9r9AYcGul3QYtBwBvc//ZoR7xvigU85Tt4MZ13TTNJE4/s9NAIuOjTOOt/mfkzqa3+Sv+K2QF+3b/dSqYZM5A=="],
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ClipNode, getProcessorBlobUrl } from "@jadujoel/web-audio-clip-node";
|
|
2
|
+
|
|
3
|
+
const app = document.getElementById("app")!;
|
|
4
|
+
app.innerHTML = `
|
|
5
|
+
<h1>ClipNode</h1>
|
|
6
|
+
<button id="play">▶ Play</button>
|
|
7
|
+
<button id="stop">■ Stop</button>
|
|
8
|
+
<p id="status">Click Play to start.</p>
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
function createToneBuffer(ctx: AudioContext, freq = 440, duration = 2) {
|
|
12
|
+
const length = ctx.sampleRate * duration;
|
|
13
|
+
const buf = ctx.createBuffer(1, length, ctx.sampleRate);
|
|
14
|
+
const data = buf.getChannelData(0);
|
|
15
|
+
for (let i = 0; i < length; i++) {
|
|
16
|
+
data[i] = Math.sin((2 * Math.PI * freq * i) / ctx.sampleRate);
|
|
17
|
+
}
|
|
18
|
+
return buf;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let ctx: AudioContext;
|
|
22
|
+
let clip: ClipNode;
|
|
23
|
+
|
|
24
|
+
document.getElementById("play")!.addEventListener("click", async () => {
|
|
25
|
+
if (!ctx) {
|
|
26
|
+
ctx = new AudioContext();
|
|
27
|
+
await ctx.audioWorklet.addModule(getProcessorBlobUrl());
|
|
28
|
+
clip = new ClipNode(ctx);
|
|
29
|
+
clip.connect(ctx.destination);
|
|
30
|
+
clip.buffer = createToneBuffer(ctx);
|
|
31
|
+
}
|
|
32
|
+
clip.start();
|
|
33
|
+
document.getElementById("status")!.textContent = "Playing…";
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
document.getElementById("stop")!.addEventListener("click", () => {
|
|
37
|
+
if (clip) {
|
|
38
|
+
clip.stop();
|
|
39
|
+
document.getElementById("status")!.textContent = "Stopped.";
|
|
40
|
+
}
|
|
41
|
+
});
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "
|
|
7
|
-
"build": "
|
|
6
|
+
"dev": "bun index.html",
|
|
7
|
+
"build": "bun build ./src/main.ts --outdir=dist --minify"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@jadujoel/web-audio-clip-node": "^0.1.
|
|
11
|
-
},
|
|
12
|
-
"devDependencies": {
|
|
13
|
-
"vite": "^6"
|
|
10
|
+
"@jadujoel/web-audio-clip-node": "^0.1.2"
|
|
14
11
|
}
|
|
15
12
|
}
|
package/examples/react/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Minimal React app using the built-in hooks and UI components.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
bun install
|
|
7
|
+
bun index.html
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Uses `useClipNode` + `useClipControls` to wire everything up, and renders `<TransportButtons />` and `<AudioControl />` out of the box.
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "
|
|
7
|
-
"build": "vite build"
|
|
6
|
+
"dev": "bun index.html"
|
|
8
7
|
},
|
|
9
8
|
"dependencies": {
|
|
10
9
|
"@jadujoel/web-audio-clip-node": "^0.1.1",
|
|
@@ -13,9 +12,8 @@
|
|
|
13
12
|
"zustand": "^5"
|
|
14
13
|
},
|
|
15
14
|
"devDependencies": {
|
|
15
|
+
"@types/bun": "latest",
|
|
16
16
|
"@types/react": "^19",
|
|
17
|
-
"@types/react-dom": "^19"
|
|
18
|
-
"@vitejs/plugin-react": "^4",
|
|
19
|
-
"vite": "^6"
|
|
17
|
+
"@types/react-dom": "^19"
|
|
20
18
|
}
|
|
21
19
|
}
|
|
@@ -10,37 +10,37 @@ app.innerHTML = `
|
|
|
10
10
|
`;
|
|
11
11
|
|
|
12
12
|
function createToneBuffer(ctx: AudioContext, freq = 440, duration = 2) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const length = ctx.sampleRate * duration;
|
|
14
|
+
const buf = ctx.createBuffer(1, length, ctx.sampleRate);
|
|
15
|
+
const data = buf.getChannelData(0);
|
|
16
|
+
for (let i = 0; i < length; i++) {
|
|
17
|
+
data[i] = Math.sin((2 * Math.PI * freq * i) / ctx.sampleRate);
|
|
18
|
+
}
|
|
19
|
+
return buf;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
let ctx: AudioContext;
|
|
23
23
|
let clip: ClipNode;
|
|
24
24
|
|
|
25
25
|
document.getElementById("play")!.addEventListener("click", async () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
if (!ctx) {
|
|
27
|
+
ctx = new AudioContext();
|
|
28
|
+
// Load the processor from your own server (public/processor.js)
|
|
29
|
+
const processorUrl = getProcessorModuleUrl(window.location.href);
|
|
30
|
+
await ctx.audioWorklet.addModule(processorUrl);
|
|
31
|
+
clip = new ClipNode(ctx, {
|
|
32
|
+
processorOptions: { sampleRate: ctx.sampleRate },
|
|
33
|
+
});
|
|
34
|
+
clip.connect(ctx.destination);
|
|
35
|
+
clip.buffer = createToneBuffer(ctx);
|
|
36
|
+
}
|
|
37
|
+
clip.start();
|
|
38
|
+
document.getElementById("status")!.textContent = "Playing…";
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
document.getElementById("stop")!.addEventListener("click", () => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (clip) {
|
|
43
|
+
clip.stop();
|
|
44
|
+
document.getElementById("status")!.textContent = "Stopped.";
|
|
45
|
+
}
|
|
46
46
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jadujoel/web-audio-clip-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Full-featured AudioWorklet clip player with playback rate, detune, gain, pan, filters, looping, fades, crossfade, and streaming buffer support. React components included.",
|
|
6
6
|
"keywords": [
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"types": "./dist/lib.d.ts",
|
|
25
25
|
"import": "./dist/lib.js"
|
|
26
26
|
},
|
|
27
|
+
"./bundle": "./dist/lib.bundle.js",
|
|
27
28
|
"./react": {
|
|
28
29
|
"types": "./dist/lib-react.d.ts",
|
|
29
30
|
"import": "./dist/lib-react.js"
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { ClipNode, getProcessorBlobUrl } from "@jadujoel/web-audio-clip-node";
|
|
2
|
-
|
|
3
|
-
const app = document.getElementById("app")!;
|
|
4
|
-
app.innerHTML = `
|
|
5
|
-
<h1>ClipNode – Vite + TypeScript</h1>
|
|
6
|
-
<button id="play">▶ Play</button>
|
|
7
|
-
<button id="stop">■ Stop</button>
|
|
8
|
-
<p id="status">Click Play to start.</p>
|
|
9
|
-
`;
|
|
10
|
-
|
|
11
|
-
function createToneBuffer(ctx: AudioContext, freq = 440, duration = 2) {
|
|
12
|
-
const length = ctx.sampleRate * duration;
|
|
13
|
-
const buf = ctx.createBuffer(1, length, ctx.sampleRate);
|
|
14
|
-
const data = buf.getChannelData(0);
|
|
15
|
-
for (let i = 0; i < length; i++) {
|
|
16
|
-
data[i] = Math.sin((2 * Math.PI * freq * i) / ctx.sampleRate);
|
|
17
|
-
}
|
|
18
|
-
return buf;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let ctx: AudioContext;
|
|
22
|
-
let clip: ClipNode;
|
|
23
|
-
|
|
24
|
-
document.getElementById("play")!.addEventListener("click", async () => {
|
|
25
|
-
if (!ctx) {
|
|
26
|
-
ctx = new AudioContext();
|
|
27
|
-
await ctx.audioWorklet.addModule(getProcessorBlobUrl());
|
|
28
|
-
clip = new ClipNode(ctx, {
|
|
29
|
-
processorOptions: { sampleRate: ctx.sampleRate },
|
|
30
|
-
});
|
|
31
|
-
clip.connect(ctx.destination);
|
|
32
|
-
clip.buffer = createToneBuffer(ctx);
|
|
33
|
-
}
|
|
34
|
-
clip.start();
|
|
35
|
-
document.getElementById("status")!.textContent = "Playing…";
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
document.getElementById("stop")!.addEventListener("click", () => {
|
|
39
|
-
if (clip) {
|
|
40
|
-
clip.stop();
|
|
41
|
-
document.getElementById("status")!.textContent = "Stopped.";
|
|
42
|
-
}
|
|
43
|
-
});
|