Skip to content

feat(core): pitch shift — a granular shifter as the fifth FX worklet - #3276

Open
vanceingalls wants to merge 1 commit into
wa-21b-audio-mutefrom
wa-22a-pitchshift
Open

feat(core): pitch shift — a granular shifter as the fifth FX worklet#3276
vanceingalls wants to merge 1 commit into
wa-21b-audio-mutefrom
wa-22a-pitchshift

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Summary

  • Adds hf-pitchshift, a fifth AudioWorklet FX processor alongside compressor/limiter/gate/bitcrush: a dual-tap granular delay line (100 ms grain, taps 180° apart) that shifts pitch without changing playback duration.
  • Wired through the registry (audioFx.ts), the graph builder (audioFxGraph.ts, same workletBuilder/dispose-message contract as the other four worklets), plain-language copy (audioFxCopy.ts, no speech words per the audit test), and the chain-tail model (audioFxTail.ts, ~0.2s for two grains).
  • One implementation, shared by preview (Web Audio in the page) and render (same worklet run inside an OfflineAudioContext in the headless browser) — no separate ffmpeg filter needed, matching how the other four worklet effects already work.
  • Refactored nodeTail's switch into small per-effect helper functions while adding the pitchshift case, to keep it under the complexity threshold (fallow was already close before this change).

Test plan

  • bun run build (regenerates packages/core/src/generated/audio-fx-runtime-inline.ts from source — never hand-edited)
  • cd packages/core && bun run test — 2344/2344 pass, including new HfPitchshift unit tests (semitones:0 exact passthrough at the grain/2 delay, semitones:±12 octave shift via zero-crossing frequency estimate)
  • cd packages/engine && bunx vitest run src/services/audioFxRender.test.ts — 16/16 pass, including a new browser-render test that measures the actual output frequency after an octave-up shift (proves render/preview parity empirically, not just structurally)
  • cd packages/engine && bun run test — 1527 passed, 3 skipped (pre-existing), no regressions
  • bunx oxfmt / bunx oxlint clean on all touched files

Stacks on #3275 (A2) and #3274 (A1) — should merge after both.

🤖 Generated with Claude Code

Adds hf-pitchshift alongside the four existing dynamics worklets: a dual-tap
granular delay line, 100 ms grain, taps 180° apart so one is always
crossfading in as the other resets — hides the splice each tap makes on
wrap. Read-tap speed relative to the write head tracks the semitone ratio,
so pitch shifts without changing duration.

Registered through the same workletBuilder/dispose-message path the other
four use (so shapeOf never rebuilds on a param tweak, and a chain drop
retires it), wired into the registry with a plain-language copy entry and a
~0.2s chain tail (two grains). One implementation, shared by preview (Web
Audio in the page) and render (the same worklet run inside an
OfflineAudioContext in the headless browser) — confirmed by a browser-render
test that measures the actual output frequency, not just that it differs
from input.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant