Files
feuerwerk/src/editor/soundfx/nodeTabs.ts
T
adminandClaude Opus 5.5 c087993000 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>
2026-10-10 14:30:00 +02:00

48 lines
1.7 KiB
TypeScript

/**
* The nodes of a sound FX graph with a tab of their own, opened by a double-click on them: a
* Parametric EQ (#370), a Compressor (#378) and a Filter (#385). A tab is a document named by its
* kind, the asset and the node (`eq:<asset>:<node>`).
*/
import type { GraphNode } from '../../core/graph';
import type { Project } from '../../core/model/Project';
import { SoundFxCompressor, SoundFxFilter, SoundFxParametricEq } from '../../core/soundfx/nodes';
import { compressorNodeOf, compressorTabLabel } from './CompressorEditor';
import { eqTabLabel } from './EqEditor';
import { eqNodeOf } from './EqPanel';
import { filterNodeOf } from './FilterPanel';
export type NodeTabKind = 'eq' | 'compressor' | 'filter';
export const NODE_TAB_KINDS: readonly NodeTabKind[] = ['eq', 'compressor', 'filter'];
const KIND_OF_TYPE: Readonly<Record<string, NodeTabKind>> = {
[SoundFxParametricEq.type]: 'eq',
[SoundFxCompressor.type]: 'compressor',
[SoundFxFilter.type]: 'filter',
};
/** The kind of tab a node opens, or null for one without. */
export const nodeTabOf = (type: string): NodeTabKind | null => KIND_OF_TYPE[type] ?? null;
const NODE_OF: Readonly<Record<NodeTabKind, typeof eqNodeOf>> = {
eq: eqNodeOf,
compressor: compressorNodeOf,
filter: filterNodeOf,
};
/** The node a tab is about, or null once it, or its asset, is gone. */
export function nodeOfTab(
project: Project,
kind: NodeTabKind,
assetId: string,
nodeId: string,
): GraphNode | null {
return NODE_OF[kind](project, assetId, nodeId);
}
export function nodeTabLabel(project: Project, kind: NodeTabKind, assetId: string, nodeId: string): string {
return kind === 'compressor'
? compressorTabLabel(project, assetId, nodeId)
: eqTabLabel(project, assetId, nodeId);
}