Edit a Filter in its node and resize EQ and Filter nodes (#385)
A Filter sounds like one band of the Parametric EQ, but it keeps what the EQ cannot: linked numbers, the all-pass and +-40 dB. So it stays a node of its own and gets the EQ's editor with itself as the one band. The editors also stop running out of their room: what does not fit is left out instead of being cut. - The curve of the EQ is now BandCurve with a host the EQ and the Filter both fill. CurvePanel holds what their panels share. - FilterPanel shows the Filter as one band. A drag of its handle sets frequency and gain as one undo step. A number fed by a link is greyed out and the drag leaves it alone. - A double-click on the Filter's title opens the same in a tab of its own. Delete there does nothing, the node stays. - The Filter is tapped like the EQ, so its node and its tab draw the spectrum of what passes it. A tap is two gain nodes per voice, the analysers are made only while someone listens. - EqFace and FilterFace are one CurveFace now, given the panel. - EQ and Filter nodes have a corner to resize them, drawn like the floating window's. The size is saved on the node as `size`, each drag is one undo step, the least is 240 x 120. - In the node the help line is gone. The row of numbers comes in only once the curve keeps 120 px of height. A group of the row that does not fit is not drawn, the one on the right first. - A frequency or dB label that would run into the one before it is left out. - The all-pass is drawn flat. The curve's maths takes every shape a Filter has. Found on the way: - The instrument presets set nodes 240 apart. A Filter with its editor is about 700 wide and covered the Bitcrusher, so a Filter now takes four columns. Tests: - Unit: 8 new (filter band, resize command and save, fitting rules), stage lists with a Filter now hold its taps - e2e 34 (atx): ALL PASSED, 130 checks (4 new) - Gate (atx): CHECK EXIT 0, 284 files, 4367 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
17f47f6956
commit
c087993000
26 files changed
+1514
-656
No files matched your search
@@ -5,7 +5,8 @@ import type { Widget } from '../../ui';
|
||||
import { currentNodePalette } from '../graph/NodePalette';
|
||||
import { CompressorPanel } from '../soundfx/CompressorPanel';
|
||||
import { GainPanel } from '../soundfx/GainPanel';
|
||||
import { EqPanel } from '../soundfx/EqPanel';
|
||||
import { CurvePanel } from '../soundfx/CurvePanel';
|
||||
import type { EqEditor } from '../soundfx/EqEditor';
|
||||
import { rectOf } from '../textProbe';
|
||||
import { centreOf, snapButton, type HookContext } from './hookHelpers';
|
||||
|
||||
@@ -127,17 +128,7 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
},
|
||||
/** The Parametric EQ's tab (#367, #370): for which node it is showing, its bands and where to aim. */
|
||||
eq: {
|
||||
shown: () => (editor.tab === 'eq' ? editor.eqEditor.shownFor : null),
|
||||
bands: () => editor.eqEditor.panel.bands(),
|
||||
picked: () => editor.eqEditor.panel.picked,
|
||||
/** The dB the curve is zoomed to, above and below 0 (#376). */
|
||||
range: () => editor.eqEditor.panel.range(),
|
||||
/** The dB the curve draws right now, and whether a zoom is on its way (#377). */
|
||||
zoomShown: () => editor.eqEditor.panel.curve.zoomShown,
|
||||
handleAt: (index: number) => editor.eqEditor.panel.curve.handleGlobal(index),
|
||||
pointAt: (hz: number, db: number) => editor.eqEditor.panel.curve.pointGlobal(hz, db),
|
||||
fields: () => eqFields(editor.eqEditor.panel),
|
||||
typeAt: () => centreOf(editor.eqEditor.panel.typeButton),
|
||||
...curveTabHooks(editor.eqEditor, () => editor.tab === 'eq'),
|
||||
/**
|
||||
* The spectra drawn behind the curve (#374): how many, the loudest bin in front of the node and
|
||||
* behind it in the last one, and the taps – which node is listened to, how many play, how many
|
||||
@@ -151,7 +142,7 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
panel: (nodeId: string) => {
|
||||
const canvas = editor.soundFxEditor.canvas;
|
||||
const panel = canvas.faceOf(nodeId)?.widget;
|
||||
if (!(panel instanceof EqPanel)) return null;
|
||||
if (!(panel instanceof CurvePanel)) return null;
|
||||
const node = canvas.nodeScreen(nodeId);
|
||||
const ports = (keys: string[], isOutput: boolean) =>
|
||||
Object.fromEntries(keys.map((key) => [key, canvas.portGlobal(nodeId, key, isOutput)]));
|
||||
@@ -161,6 +152,7 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
size: { w: panel.w, h: panel.h },
|
||||
curve: screenRect(panel.curve),
|
||||
node: node?.rect ?? null,
|
||||
corner: node?.corner ?? null,
|
||||
inputs: ports(node?.inputs ?? [], false),
|
||||
outputs: ports(node?.outputs ?? [], true),
|
||||
bands: panel.bands(),
|
||||
@@ -169,14 +161,21 @@ export function graphHooks({ state, editor }: HookContext) {
|
||||
handles: panel.bands().map((_, i) => panel.curve.handleGlobal(i)),
|
||||
type: screenRect(panel.typeButton),
|
||||
spectrum: panel.curve.seen,
|
||||
rows: panel.shownRows,
|
||||
labels: panel.curve.shownLabels,
|
||||
};
|
||||
},
|
||||
/** Where a frequency and a gain are on the curve of a node's panel, on screen. */
|
||||
panelPointAt: (nodeId: string, hz: number, db: number) => {
|
||||
const panel = editor.soundFxEditor.canvas.faceOf(nodeId)?.widget;
|
||||
return panel instanceof EqPanel ? panel.curve.pointGlobal(hz, db) : null;
|
||||
return panel instanceof CurvePanel ? panel.curve.pointGlobal(hz, db) : null;
|
||||
},
|
||||
},
|
||||
/** The Filter's tab (#385), as the EQ's; its node's panel is `eq.panel` too. */
|
||||
filter: {
|
||||
...curveTabHooks(editor.filterEditor, () => editor.tab === 'filter'),
|
||||
spectrum: () => ({ ...editor.filterEditor.panel.curve.seen, taps: soundLibrary.spectrumTaps() }),
|
||||
},
|
||||
/** The Compressor's tab (#378): its node, its numbers and knobs, and where to aim on the curve. */
|
||||
compressor: {
|
||||
shown: () => (editor.tab === 'compressor' ? editor.compressorEditor.shownFor : null),
|
||||
@@ -381,7 +380,7 @@ function screenRect(widget: Widget): { x: number; y: number; w: number; h: numbe
|
||||
}
|
||||
|
||||
/** The picked band's numbers as an EQ panel shows them; null for one its band type has not. */
|
||||
function eqFields(panel: EqPanel) {
|
||||
function eqFields(panel: CurvePanel) {
|
||||
return {
|
||||
type: panel.typeButton.text,
|
||||
slope: panel.slopeButton.enabled ? panel.slopeButton.text : null,
|
||||
@@ -390,3 +389,24 @@ function eqFields(panel: EqPanel) {
|
||||
frequency: panel.frequencyField.value,
|
||||
};
|
||||
}
|
||||
|
||||
/** A tab with a curve (#370, #385): for which node it is showing, its bands and where to aim. */
|
||||
function curveTabHooks(tab: EqEditor, seen: () => boolean) {
|
||||
const panel = tab.panel;
|
||||
return {
|
||||
shown: () => (seen() ? tab.shownFor : null),
|
||||
bands: () => panel.bands(),
|
||||
picked: () => panel.picked,
|
||||
/** The dB the curve is zoomed to, above and below 0 (#376). */
|
||||
range: () => panel.range(),
|
||||
/** The dB the curve draws right now, and whether a zoom is on its way (#377). */
|
||||
zoomShown: () => panel.curve.zoomShown,
|
||||
handleAt: (index: number) => panel.curve.handleGlobal(index),
|
||||
pointAt: (hz: number, db: number) => panel.curve.pointGlobal(hz, db),
|
||||
fields: () => eqFields(panel),
|
||||
typeAt: () => centreOf(panel.typeButton),
|
||||
/** What is drawn under the curve, and which axis labels (#385). */
|
||||
rows: () => panel.shownRows,
|
||||
labels: () => panel.curve.shownLabels,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user