Show a Saturation node as a device that glows with its drive (#396)
A Saturation in the graph was a stack of rows. The user wanted a device of its own style and size, like the Delay and the Reverb, that shows warmth and heat the harder the sound is driven. - The node is a copper plate of 720x280 with a button per type, the auto-gain lever, a big drive knob and small knobs for tone, output and wet. Each change is one undo step, wired numbers lock their knob. - The screen draws a sine at -12 dBFS, the level auto-gain is reckoned at, as a grey ghost. Over it is the wave that comes out: bent by the type, its DC taken out, auto-gain and output after it, blended by wet. Under it stand the auto-gain and the peak. - The heat is the drive over 48 dB, as far as wet lets it be heard. The wave goes from cold steel through ember red to white, a glow rises from below and flickers, and embers fly up from about 8 dB on. A lamp beside the name glows with it. - The node keeps the editor drawing only while there is heat. Without drive it draws nothing, unlike the Delay, which always animates. - The Reverb's private type button is now PickButton with its own colours, and the Saturation's type buttons use it as well. - No instrument preset uses a Saturation, so no preset needs more columns. Tests: - Unit saturation-view: 14 new tests. - Counter-check: without taking out the DC, exactly the DC test fails. - e2e 34 (atx): ALL PASSED, 137 checks, one new for type, lever and drive with undo. - Gate (atx): CHECK EXIT 0, 289 files, 4431 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c53ce0cbe
commit
80b25488ce
11 files changed
+1092
-21
No files matched your search
@@ -1095,15 +1095,15 @@ export const SoundFxLimiter: SoundFxNodeDef = effect(
|
||||
const DRIVE = { ...numberRow('drive', 'Drive (dB)', 0, 48, 0.5), default: 12 };
|
||||
const TONE = { ...numberRow('tone', 'Tone', 0, 1, 0.01), default: 1 };
|
||||
const OUTPUT = numberRow('output', 'Output (dB)', -24, 24, 0.5);
|
||||
const SATURATION_AUTO_GAIN = { ...COMPRESS_AUTO_GAIN, default: true };
|
||||
export const SATURATION_AUTO_GAIN = { ...COMPRESS_AUTO_GAIN, default: true };
|
||||
const SATURATION_MIX = { ...numberRow('mix', 'Wet', 0, 1, 0.01), default: 1 };
|
||||
const SATURATION_ROWS = {
|
||||
export const SATURATION_ROWS = {
|
||||
drive: DRIVE,
|
||||
tone: TONE,
|
||||
output: OUTPUT,
|
||||
mix: SATURATION_MIX,
|
||||
};
|
||||
const SATURATION_TYPE = {
|
||||
export const SATURATION_TYPE = {
|
||||
key: 'curve',
|
||||
label: 'Type',
|
||||
kind: 'enum' as const,
|
||||
@@ -1111,7 +1111,7 @@ const SATURATION_TYPE = {
|
||||
options: SATURATION_TYPES,
|
||||
};
|
||||
|
||||
function saturationTypeOf(settings: PropertyBag): SaturationType {
|
||||
export function saturationTypeOf(settings: PropertyBag): SaturationType {
|
||||
return SATURATION_TYPES.find((option) => option.value === settings[SATURATION_TYPE.key])?.value ?? 'soft';
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export function saturationCurve(type: SaturationType): Float32Array {
|
||||
}
|
||||
|
||||
/** The level a reference sine is at: -12 dBFS, about where a sound is loud but not at its peak. */
|
||||
const REFERENCE = 0.25;
|
||||
export const SATURATION_REFERENCE = 0.25;
|
||||
const STEPS = 256;
|
||||
|
||||
/**
|
||||
@@ -82,11 +82,11 @@ const STEPS = 256;
|
||||
export function saturationAutoGain(type: SaturationType, driveDb: number): number {
|
||||
const drive = 10 ** (driveDb / 20);
|
||||
const out = Array.from({ length: STEPS }, (_, i) =>
|
||||
saturate(type, REFERENCE * Math.sin((2 * Math.PI * i) / STEPS) * drive),
|
||||
saturate(type, SATURATION_REFERENCE * Math.sin((2 * Math.PI * i) / STEPS) * drive),
|
||||
);
|
||||
const mean = out.reduce((sum, v) => sum + v, 0) / STEPS;
|
||||
const rms = Math.sqrt(out.reduce((sum, v) => sum + (v - mean) ** 2, 0) / STEPS);
|
||||
const gain = rms > 0 ? 20 * Math.log10(REFERENCE / Math.SQRT2 / rms) : 48;
|
||||
const gain = rms > 0 ? 20 * Math.log10(SATURATION_REFERENCE / Math.SQRT2 / rms) : 48;
|
||||
return Math.max(-48, Math.min(48, gain));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user