Show Tremolo, Vibrato, Phaser and Flanger as devices that swing (#398)
The four effects that swing were still drawn as rows of numbers. Like
the Delay, the Saturation and the Limiter, each node now shows a device
of a fixed size. Its screen draws what it does over the first seconds
of a sound, and a light plays that in real time, over and over.
- One device for all four (ModulationPanel, ModulationFace and
modulationView), as KnobPanel is for Gain, Pitch, Mix and Pan. A short
description gives each its title, plate, buttons and knobs.
- Tremolo, 640x280 in cream: a wave its volume shapes, the volume as
its outline. It says DOWN TO 50%, EVERY 200 ms and DIPS -6 dB.
- Vibrato, 640x280 in plum: the pitch in semitones, cool below and warm
above. Under a wet below 1 the dry pitch stands beside it. It says
how late the voice is (LATE 0.93 ms).
- Phaser, 760x280 in mustard, and Flanger, 680x280 in steel blue: the
response from 20 Hz to 20 kHz, and under it a lane of the turn or the
delay over time. The response is drawn where the light is, so the
notches swirl and the comb jets. A comb finer than a column fills it.
- The light loops over 1, 2, 4 or 8 s, two LFO swings at least, for a
Flanger as long as its sweep takes to reach 20 ms. The face only
keeps the editor drawing while something swings and is heard.
- A Rate lamp beside each name goes from dark to lit with the swing.
- Nothing is listened to. The pictures use the worklets' own numbers:
lfoAt, vibratoSpeedUp (now exported from lfo.ts), the allpass chain
with its feedback, and the comb, reckoned at 48 kHz.
- Phaser and Flanger take 4 preset columns. Phased Pad was looked at
in a picture, nothing overlaps.
- Exported from nodes.ts: LFO_SHAPE, lfoShapeOf, TREMOLO_ROWS,
VIBRATO_ROWS, PHASER_STAGE_ROW and phaserStagesOf. FLANGER_MAX_MS is
exported from phaser.ts.
Decided here, as the request left the look open:
- A notch counts where the response falls below -3 dB, not some dB
under the loudest point. Feedback rings peaks far above 0 dB, and the
first rule counted 7 notches on a Phaser of 12 stages.
- Rate and Depth of Tremolo and Vibrato are big knobs, Wet is small.
Tests:
Unit modulation-view (stein2): 22 tests, new. Phaser and Flanger
give the dB of their worklets on a sine within 0.005 dB, the
Tremolo its samples, the Vibrato the pitch it reads at.
Counter-check: with the sign of the Phaser's feedback flipped in one
place, exactly the worklet test fails.
e2e 34 (atx): ALL PASSED, 142 checks, 4 new. Each device has its
size, its ports outside, a light that runs by itself, and a button
and a knob as one undo step each. Picking 8 stages on the Phaser
draws 4 notches.
Gate (atx): CHECK EXIT 0, 291 files, 4467 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
28909f0adf
commit
b6749cc43d
12 files changed
+1860
-11
No files matched your search
@@ -9,6 +9,7 @@ import { CompressorPanel } from '../soundfx/CompressorPanel';
|
||||
import { KnobPanel } from '../soundfx/KnobPanel';
|
||||
import { DelayPanel } from '../soundfx/DelayPanel';
|
||||
import { LimiterPanel } from '../soundfx/LimiterPanel';
|
||||
import { ModulationPanel } from '../soundfx/ModulationPanel';
|
||||
import { SaturationPanel } from '../soundfx/SaturationPanel';
|
||||
import { ReverbPanel } from '../soundfx/ReverbPanel';
|
||||
import { CurvePanel } from '../soundfx/CurvePanel';
|
||||
@@ -46,6 +47,56 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
: null,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* The panel a Tremolo, a Vibrato, a Phaser or a Flanger node shows (#398), in the sound FX graph
|
||||
* or the instrument graph: whether it is shown and where, its knobs and buttons, which settings
|
||||
* are idle, what the screen draws and says, where its light is, and the node's ports.
|
||||
*/
|
||||
const modulationPanel = (nodeId: string) => {
|
||||
const canvas = [editor.soundFxEditor.canvas, editor.instrumentEditor.canvas].find(
|
||||
(c) => c.faceOf(nodeId)?.widget instanceof ModulationPanel,
|
||||
);
|
||||
const panel = canvas?.faceOf(nodeId)?.widget;
|
||||
if (!canvas || !(panel instanceof ModulationPanel)) return null;
|
||||
const node = canvas.nodeScreen(nodeId);
|
||||
const ports = (keys: string[], isOutput: boolean) =>
|
||||
Object.fromEntries(keys.map((key) => [key, canvas.portGlobal(nodeId, key, isOutput)]));
|
||||
const scope = panel.scope;
|
||||
const picture = scope.picture;
|
||||
return {
|
||||
kind: panel.device.kind,
|
||||
shown: shownOnScreen(panel),
|
||||
rect: screenRect(panel),
|
||||
size: { w: panel.w, h: panel.h },
|
||||
node: node?.rect ?? null,
|
||||
inputs: ports(node?.inputs ?? [], false),
|
||||
outputs: ports(node?.outputs ?? [], true),
|
||||
knobs: Object.fromEntries(
|
||||
Object.entries(panel.knobs).map(([key, knob]) => [
|
||||
key,
|
||||
{ value: knob.value, number: knob.shown, enabled: knob.enabled, dial: knob.dialGlobal() },
|
||||
]),
|
||||
),
|
||||
idle: [...panel.idle],
|
||||
picked: panel.pickButtons.find((b) => b.picked)?.value ?? null,
|
||||
picks: Object.fromEntries(panel.pickButtons.map((b) => [b.value, screenCentre(b)])),
|
||||
side: panel.sideText.text,
|
||||
lamp: panel.lamp,
|
||||
scope: {
|
||||
says: scope.texts,
|
||||
/** The seconds the screen spans, the lowest and highest the light reads, whether it moves. */
|
||||
span: picture?.span ?? null,
|
||||
lowest: picture?.lowest ?? null,
|
||||
highest: picture?.highest ?? null,
|
||||
moving: picture?.moving ?? false,
|
||||
/** Where the light is, in seconds after the sound, and what it reads there. */
|
||||
at: scope.at,
|
||||
reading: scope.reading,
|
||||
/** A Phaser's or a Flanger's notches drawn now, by Hz. */
|
||||
notches: 'notches' in scope ? scope.notches : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
return {
|
||||
graph: {
|
||||
current: () => graphEditor.currentAssetId,
|
||||
@@ -576,6 +627,10 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
};
|
||||
},
|
||||
},
|
||||
tremolo: { panel: modulationPanel },
|
||||
vibrato: { panel: modulationPanel },
|
||||
phaser: { panel: modulationPanel },
|
||||
flanger: { panel: modulationPanel },
|
||||
instrument: {
|
||||
current: () => editor.instrumentEditor.currentAssetId,
|
||||
info: () => editor.instrumentEditor.shownInfo,
|
||||
|
||||
Reference in new issue
Block a user