Skip to content

feat(audio): play automation envelopes in preview and bake them at render - #3016

Open
vanceingalls wants to merge 2 commits into
wa-3-automation-modelfrom
wa-4-automation-playback
Open

feat(audio): play automation envelopes in preview and bake them at render#3016
vanceingalls wants to merge 2 commits into
wa-3-automation-modelfrom
wa-4-automation-playback

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Stacked on #3015.

Makes the envelopes actually run, on both sides.

Preview — each lane is scheduled onto the AudioParams behind its knob using
native ramps and value curves. Nothing evaluates the envelope per frame: it is
handed to the audio thread once, so it stays sample-accurate however busy the
main thread is. Timing comes from the transport, so an envelope survives seeking
into the middle of a clip, a clip that has not started yet, and a playback rate
that compresses clip seconds into context seconds.

A segment is only scheduled as a straight ramp when nothing bends it — no
curvature, a linear parameter scale, and no unit mapping. Log-scaled and mapped
segments are sampled instead, because a delay knob in milliseconds and a wet/dry
pair moving in opposition are not linear in the parameter they drive.

Render — the offline path schedules with the same scheduler, in the
OfflineAudioContext that already runs the same graph builders. The input WAV is
the clip's own audio from its first sample, so clip-local time is offline time.
Volume lanes take the existing PCM bake rather than a second mechanism: the lane
becomes keyframes, so a straight fade stays two of them and only a bent segment
is sampled — the baker interpolates linearly and would otherwise quietly
straighten the curve.

Lanes with nowhere to write are skipped rather than reported: a one-pole filter
exposes no frequency param, and the worklet effects expose none at all.

A browser test sweeps a lowpass from below a 2 kHz tone to well above it and
measures both ends. Parsing an envelope is not the same as scheduling it, and
only running the real thing tells the two apart — this test is what caught that
the injected runtime bundle needed rebuilding.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Fallow audit report

Found 20 findings.

Duplication (19)
Severity Rule Location Description
minor fallow/code-duplication packages/core/scripts/build-audio-fx-runtime.ts:3 Code clone group 1 (9 lines, 2 instances)
minor fallow/code-duplication packages/core/scripts/build-audio-fx-runtime.ts:13 Code clone group 2 (14 lines, 2 instances)
minor fallow/code-duplication packages/core/scripts/build-position-edits-render.ts:3 Code clone group 1 (9 lines, 2 instances)
minor fallow/code-duplication packages/core/scripts/build-position-edits-render.ts:13 Code clone group 2 (14 lines, 2 instances)
minor fallow/code-duplication packages/core/src/audio/audioFxAutomation.test.ts:130 Code clone group 3 (14 lines, 2 instances)
minor fallow/code-duplication packages/core/src/audio/audioFxAutomation.test.ts:182 Code clone group 3 (14 lines, 2 instances)
minor fallow/code-duplication packages/core/src/audio/audioFxGraph.ts:225 Code clone group 4 (15 lines, 2 instances)
minor fallow/code-duplication packages/core/src/audio/audioFxGraph.ts:258 Code clone group 4 (15 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/PropertyPanelEmptyState.test.tsx:11 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx:17 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx:16 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx:16 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx:13 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx:13 Code clone group 6 (36 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx:10 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFxSection.test.tsx:14 Code clone group 6 (36 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFxSection.test.tsx:14 Code clone group 5 (12 lines, 7 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFxSection.test.tsx:89 Code clone group 7 (6 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/components/editor/propertyPanelFxSection.test.tsx:201 Code clone group 7 (6 lines, 2 instances)
Health (1)
Severity Rule Location Description
minor fallow/high-crap-score packages/core/stubs/audio-fx-runtime-entry.ts:74 'render' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)

Generated by fallow.

vanceingalls and others added 2 commits August 4, 2026 14:41
Schedules each lane onto the AudioParams behind its knob using native ramps
and value curves. Nothing evaluates the envelope per frame: it is handed to
the audio thread once, so it stays sample-accurate however busy the main
thread is, and the offline render will schedule it the same way.

Timing comes from the transport, so an envelope survives seeking into the
middle of a clip, a clip that has not started yet, and a playback rate that
compresses clip seconds into context seconds.

A straight line is only scheduled as a ramp when nothing bends it — no
curvature, a linear parameter scale, and no unit mapping. Log-scaled
parameters and mapped ones are sampled instead, since a delay knob in
milliseconds and a wet/dry pair moving in opposition are not linear in the
parameter they drive.

Lanes with nowhere to write are skipped rather than reported: a one-pole
filter exposes no frequency param, and the worklet effects expose none at
all. Editing an envelope mid-playback re-aims it at the live playhead rather
than restarting the track.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The offline render schedules FX lanes with the same scheduler preview uses,
inside the OfflineAudioContext that already runs the same graph builders. The
input WAV is the clip's own audio from its first sample, so clip-local time
is offline time and the envelope needs no offset.

Volume lanes take the existing PCM bake rather than a second mechanism: the
lane is converted to keyframes, so a straight fade stays two of them and only
a bent segment is sampled — the baker interpolates linearly and would
otherwise quietly straighten the curve. A volume lane supersedes keyframes
probed from the timeline, which `lint` already warns about.

A browser test sweeps a lowpass from below a 2 kHz tone to well above it and
measures both ends. Parsing the envelope is not the same as scheduling it,
and only running the real thing tells the two apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vanceingalls
vanceingalls force-pushed the wa-4-automation-playback branch from 9364e6b to bfa3878 Compare August 4, 2026 21:43
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