Show Pitch and Mix nodes as one-knob devices in green and blue (#390)
The Gain has been a small device with one big knob since #384. The Pitch and the Mix are just as much a single number, so they now look and work the same way. The Mix also gets a lever for its curve, which until now was only a row in the inspector. - GainPanel and GainFace become KnobPanel and KnobFace. Each node is described by a KnobDevice: its plate, its row, how the knob turns, and an optional lever. The Gain looks as before. - Pitch: a green 160x160 plate with a bipolar knob for Semitones. 0 stands straight up and taper 2 puts an octave at 43 % of each half, so small steps near 0 are easy and the full +-64 is still there. - Mix: a blue 180x200 plate, the lever on top and the knob below. The knob has A and B engraved at the ends of its turn. The lever reads "crossfade" down and "both at center" up with its lamp lit. A flip is one undo step. - A wired Semitones or Mix greys the knob, as for the Gain. - The instrument presets give Pitch and Mix two columns. In the graph they are 281 and 261 units wide, more than one column of 240. - The test hooks __gm.pitch.panel and __gm.mix.panel return the same shape as __gm.gain.panel, plus the lever. Tests: - Unit hardware-view: 15 tests, 3 new (Pitch turn, lever above the knob and ends on the Mix plate). Counter-check: taper 1 turns the Pitch test red. - e2e 34 (melektaus-atx): 134 checks, 1 new. - Gate (melektaus-atx, together with #391): 287 files, 4397 tests. - Pictures looked at: Gain, Pitch and Mix in a row, Pitch at -24.5, Mix with both curves, the Bell preset without overlaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e8021b1fd3
commit
e70954115c
12 files changed
+468
-184
No files matched your search
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The Compressor's hardware panel (#379): every part on the plate and none over another, the meter's
|
||||
* scale and needle, the LEDs, and the grain of the plate the same every time. The Gain's plate
|
||||
* (#384) and how its one knob turns.
|
||||
* (#384) and how its one knob turns, the Pitch's knob and the Mix's lever and ends (#390).
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
GAIN_TAPER,
|
||||
gainReduction,
|
||||
knobBox,
|
||||
knobEnds,
|
||||
LED_COUNT,
|
||||
ledColour,
|
||||
ledDb,
|
||||
@@ -22,12 +23,15 @@ import {
|
||||
METER_LABELS,
|
||||
METER_RANGE_DB,
|
||||
meterPosition,
|
||||
MIX_LAYOUT,
|
||||
MIX_PANEL,
|
||||
needleAngle,
|
||||
needleStep,
|
||||
PANEL,
|
||||
PANEL_LAYOUT,
|
||||
PITCH_TAPER,
|
||||
} from '../../src/editor/soundfx/hardwareView';
|
||||
import { GAIN_ROW } from '../../src/core/soundfx/nodes';
|
||||
import { GAIN_ROW, PITCH_ROW } from '../../src/core/soundfx/nodes';
|
||||
import { knobPosition } from '../../src/ui/knob';
|
||||
|
||||
const overlap = (a: Box, b: Box) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
|
||||
@@ -163,3 +167,51 @@ describe('the Gain (#384)', () => {
|
||||
expect(knobPosition(-6, range)).toBeGreaterThan(0.4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the Pitch and the Mix (#390)', () => {
|
||||
it('turns the Pitch from 0 straight up, an octave either way alike and well inside the turn', () => {
|
||||
const range = {
|
||||
min: PITCH_ROW.min ?? 0,
|
||||
max: PITCH_ROW.max ?? 0,
|
||||
step: 0.1,
|
||||
taper: PITCH_TAPER,
|
||||
bipolar: true,
|
||||
};
|
||||
expect(knobPosition(0, range)).toBeCloseTo(0.5, 5);
|
||||
const up = knobPosition(12, range);
|
||||
expect(up - 0.5).toBeCloseTo(0.5 - knobPosition(-12, range), 5);
|
||||
expect(up).toBeGreaterThan(0.7);
|
||||
expect(up).toBeLessThan(0.75);
|
||||
expect(knobPosition(64, range)).toBe(1);
|
||||
});
|
||||
|
||||
it('holds the Mix’s lever above the knob, centred and clear of it and of the screws', () => {
|
||||
const lever = MIX_LAYOUT.curve;
|
||||
const spot = MIX_LAYOUT.knob;
|
||||
const knob = knobBox(spot);
|
||||
expect(lever.x).toBeGreaterThanOrEqual(0);
|
||||
expect(lever.x + lever.w).toBeLessThanOrEqual(MIX_PANEL.w);
|
||||
expect(lever.x + lever.w / 2).toBe(MIX_PANEL.w / 2);
|
||||
expect(spot.x).toBe(MIX_PANEL.w / 2);
|
||||
// A lever that is down reaches 8 below its box (`Lever.redraw`).
|
||||
expect(lever.y + lever.h + 8).toBeLessThanOrEqual(knob.y);
|
||||
for (const screw of MIX_LAYOUT.screws)
|
||||
expect(overlap(lever, { x: screw.x - 7, y: screw.y - 7, w: 14, h: 14 })).toBe(false);
|
||||
// Below the dial as much air as on the Gain.
|
||||
expect(MIX_PANEL.h - (spot.y + spot.size / 2)).toBe(GAIN_PANEL.h - (GAIN_LAYOUT.knob.y + spot.size / 2));
|
||||
expect(MIX_PANEL.w).toBeLessThan(MIX_PANEL.h);
|
||||
});
|
||||
|
||||
it('names the ends of the Mix’s turn just outside the dial, left and right below its middle', () => {
|
||||
const spot = MIX_LAYOUT.knob;
|
||||
const { a, b } = knobEnds(spot);
|
||||
for (const end of [a, b]) {
|
||||
expect(Math.hypot(end.x - spot.x, end.y - spot.y)).toBeGreaterThan(spot.size / 2);
|
||||
expect(end.y).toBeGreaterThan(spot.y);
|
||||
expect(end.y).toBeLessThan(MIX_PANEL.h);
|
||||
}
|
||||
expect(a.x).toBeLessThan(spot.x);
|
||||
expect(b.x).toBeGreaterThan(spot.x);
|
||||
expect(a.y).toBe(b.y);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user