Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/skills/audio-nodes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,16 @@ Do **not** gate `GraphObject::process()` on `isProcessable()` of inputs: CONDITI

Tail-bearing nodes (Delay/Convolver/Biquad) need no special handling: while connected they stay `CONDITIONAL_PROCESSABLE`; after disconnect they stay `isProcessable()` via the tail override until the impulse decays, so settle does not zero them mid-tail.

### WSOLA (pitchCorrection) source invariants
Hard-won invariants for `AudioBufferBaseSourceNode::processWithPitchCorrection` and back-to-back scheduled joins (`b2.start(t1 + D/pr)`, join compensation L = 0):

- **Feed real PCM only.** Never let `updatePlaybackInfo`-zeroed start-quantum frames enter the WSOLA analysis queue; mid-quantum starts must instead shift WSOLA's rendered output into `startOffset` after synthesis.
- **EOF drain.** After natural PCM EOF, keep calling `drainOutput` for full quanta while it fills them; stop when a quantum is under-filled (stretcher dry).
- **WSOLA whenever `pitchCorrection` is on.** Including `playbackRate == 1` — do not bypass to the non-WSOLA path at unity rate (buffer-source priming may still skip `|rate| == 1`).
- **Queue EOF needs `endOfStream()`.** An empty queue without that signal is only an underrun; with it, the base drain path runs even when `isEmpty()`.

`AudioFileSourceNode` still has its own prime/drain path (not yet shared).

---

## Implementing a New Node — Checklist
Expand Down
16 changes: 16 additions & 0 deletions .claude/skills/utilities/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,22 @@ Higher-level DSP blocks. Read each header before use.

---

### `WsolaTimeStretcher.h` — pitch-preserving time stretch

Needs ~`getRequiredInputFrames()` of PCM in its internal queue before the first OLA
iteration.

- **File / AudioTag:** early `SeekDecoderDaemon` fills the SPSC; construction
`primeWsolaInputFromDecoder()` waits until the analysis queue is full (no wall-clock
deadline). No first-quantum warmup — process only feeds the current quantum.
- **Buffer sources:** `AudioBufferSourceNode::start` calls `primeWsolaInput()` after
setting `vReadIndex_` (advances cursor by frames fed). Prefill happens only there;
`processWithPitchCorrection` feeds one quantum's input. Compare cold-start with DC
ones sample + abs/relative first-sound logs in `WsolaTimeStretcher`. Scratch sized
with `scratchBufferFrames(sampleRate)`.

---

### `SpectrumAnalyser.h` — shared windowed-FFT magnitude spectrum

Owns FFT scratch state (Blackman window, temp array, complex scratch, magnitude
Expand Down
Loading
Loading