Show an Envelope node as a device with its ADSR curve (#388)
game-maker / gate (push) Successful in 4m24s
game-maker / suite (push) Successful in 15m50s

The Compressor, the Gain and the Chip are devices in the graph. The
Envelope was still a list of rows, and its shape was left to the
imagination of whoever read the numbers.

- The node is a plate of 520x280 in pale sea green. Its inputs are
  on the left, out and value on the right.
- A dark screen draws the curve over a note in amber. A letter sits
  above each stage, its time below it, and the sustain in percent
  above its line.
- The time on the screen is squeezed. Each stage is as wide as the
  root of its seconds, at least 3 %, and the hold has a fixed fifth.
  So 5 ms of attack still shows beside a release of 3 s.
- The falls use SETTLE, as the player does. With sustain 0 there is
  no hold and no release, and the screen says NOTE ENDS.
- Attack, Decay, Sustain and Release are chrome knobs. The times use
  taper 4, so 5 ms sits an eighth of the way and 1 s not quite half.
- From and To have no range a knob could turn through. They are
  NumberFields on the plate, typed in or scrubbed.
- The decay at sustain 1 and From/To while nothing takes value are
  faded. While a link takes value, the bezel says what it moves
  between, as VALUE 400 TO 3500. The UI font has no arrow.
- Instrument presets give an Envelope 3 columns of 240.
- Test hook __gm.envelope.panel(nodeId).

Tests:
- Unit envelope-view: 6 new tests for the plate, the knob turn, the
  idle settings and the curve. A linear decay turns two of them red.
- e2e 34 (atx): 1 new check on the Laser preset (device between its
  ports, curve ends with the decay, decay knob and scrubbed To field
  each one undo step).
- Probe pictures on stein2: Synth Brass preset, pad, pluck, organ
  and an Envelope whose value moves a Chip.
- Gate (atx): CHECK EXIT 0, 286 files, 4383 tests. The first run was
  red on the font test (#214) for an arrow in the bezel text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-10 16:06:15 +02:00
1 parent ab37d3423a
commit c75e72d4ee
9 files changed
+887 -5

No files matched your search

+4 -2
View File
@@ -1,18 +1,20 @@
import type { GraphNode } from '../../core/graph';
import { InstrumentChip } from '../../core/instrument/nodes';
import { InstrumentChip, InstrumentEnvelope } from '../../core/instrument/nodes';
import { SoundFxCompressor, SoundFxFilter, SoundFxGain, SoundFxParametricEq } from '../../core/soundfx/nodes';
import type { NodeFace } from '../graph/nodeFace';
import type { EditorState } from '../state';
import { ChipFace } from './ChipFace';
import { CompressorFace } from './CompressorFace';
import { CurveFace } from './CurveFace';
import { EnvelopeFace } from './EnvelopeFace';
import { EqPanel } from './EqPanel';
import { FilterPanel } from './FilterPanel';
import { GainFace } from './GainFace';
/** The nodes drawn as a face of their own instead of rows (#380, #381, #384, #385, #386), by type. */
/** The nodes drawn as a face of their own instead of rows (#380, #381, #384–#386, #388), by type. */
export function nodeFaceFor(state: EditorState, assetId: string, node: GraphNode): NodeFace | null {
if (node.type === InstrumentChip.type) return new ChipFace(state, assetId, node.id);
if (node.type === InstrumentEnvelope.type) return new EnvelopeFace(state, assetId, node.id);
if (node.type === SoundFxCompressor.type) return new CompressorFace(state, assetId, node.id);
if (node.type === SoundFxParametricEq.type)
return new CurveFace(new EqPanel(state, true), assetId, node.id);