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

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>
This commit is contained in:
Vance Ingalls
2026-08-20 16:39:25 -07:00
co-authored by Claude Sonnet 5
parent adfdb69a78
commit fa05d3a7c6
7 changed files with 274 additions and 10 deletions
+29
View File
@@ -505,6 +505,35 @@ export const HF_AUDIO_FX: readonly HfAudioFxDef[] = [
],
web: "worklet-bitcrush",
},
{
id: "pitchshift",
label: "Pitch shift",
group: "time",
description: "Shifts pitch up or down without changing playback speed.",
params: [
{
kind: "number",
key: "semitones",
label: "Semitones",
unit: "st",
min: -12,
max: 12,
step: 1,
default: 0,
},
{
kind: "number",
key: "mix",
label: "Mix",
unit: "",
min: 0,
max: 1,
step: 0.01,
default: 1,
},
],
web: "worklet-pitchshift",
},
{
id: "delay",