Calibrate DX7 feedback scale
This commit is contained in:
parent
db20a90d1c
commit
4c10705320
7 changed files with 47 additions and 11 deletions
|
|
@ -215,6 +215,9 @@ public static class Dx7SysEx
|
|||
public static float OperatorOutputAmplitude(float outputLevel) =>
|
||||
MathF.Pow(2, (Math.Clamp(outputLevel, 0, 99) - 99) / 8f);
|
||||
|
||||
public static float OperatorFeedbackAmount(int feedback) =>
|
||||
FeedbackAmounts[Math.Clamp(feedback, 0, FeedbackAmounts.Length - 1)];
|
||||
|
||||
public static Dx7RateLevelEnvelopeApproximation ApproximateRateLevelEnvelope(Dx7Envelope envelope)
|
||||
{
|
||||
var l1 = Level(envelope.Level1);
|
||||
|
|
@ -566,6 +569,11 @@ public static class Dx7SysEx
|
|||
254
|
||||
];
|
||||
|
||||
private static readonly float[] FeedbackAmounts =
|
||||
[
|
||||
0f, 0.01f, 0.02f, 0.05f, 0.10f, 0.19f, 0.38f, 0.66f
|
||||
];
|
||||
|
||||
private static readonly int[] ExpScaleData =
|
||||
[
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
|
||||
|
|
|
|||
|
|
@ -31,3 +31,4 @@
|
|||
{"kind":"dx7_prc_logmel","lesson":"Listening and log-mel agreed that the aggregate PRC SYNTH1 score was misleading. The Dexed reference has strong low/body partials around 131 Hz and 393 Hz; Aquarium's earlier candidate lacked the low body despite a better aggregate score. The probe now gates on log-mel <=0.255 with a loose aggregate floor, and remaining mismatch points at DX7 output/index scaling and algorithm compensation.","source":"PRC SYNTH1 perceptual/log-mel calibration on 2026-05-14"}
|
||||
{"kind":"dx7_output_level_curve","lesson":"A project-authored Dexed single-carrier sweep shows DX7 carrier amplitude follows 2^((outputLevel - 99) / 8). The previous internal-level proxy made mid-level operators far too loud. `Dx7SysEx.OperatorOutputAmplitude` now owns the measured curve; next calibration rung is two-operator modulation index.","source":"DX7 single-carrier calibration rung on 2026-05-14"}
|
||||
{"kind":"dx7_modulation_index","lesson":"Isolated two-operator Dexed FM fits full-level modulation at about 12.55 radians. Under Aquarium's current Faust phase formula this maps to route index 6.275 for a full-level modulator. Apply this to isolated two-op branches, but do not blindly apply it to cascaded algorithm stacks before algorithm output compensation is modeled.","source":"DX7 two-operator modulation calibration rung on 2026-05-14"}
|
||||
{"kind":"dx7_feedback_scale","lesson":"Isolated Dexed feedback sweeps fit a nonlinear Aquarium feedback table: 0, .01, .02, .05, .10, .19, .38, .66 for DX7 feedback values 0-7. PRC feedback value 5 was already accidentally close via feedback*0.04; the table moves that knowledge into Dx7SysEx.OperatorFeedbackAmount.","source":"DX7 feedback calibration rung on 2026-05-14"}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"DX7 effective operator-level approximation",
|
||||
"DX7 measured carrier output amplitude curve",
|
||||
"DX7 isolated modulation-index route scale",
|
||||
"DX7 calibrated feedback amount table",
|
||||
"staged operator envelope DSL",
|
||||
"DX7 envelope-to-ADSR approximation",
|
||||
"standard ADSR envelope model",
|
||||
|
|
@ -63,9 +64,9 @@
|
|||
"rule": "Add DSL abstractions only when reference rebuilds prove the missing invariant. Do not consume references that cannot be backed by parity tests; parsing alone is inventory, not evidence of expressivity."
|
||||
},
|
||||
"dx7_reference_path": {
|
||||
"status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, measured carrier output amplitude, isolated two-op modulation route scale, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts and log-mel <= 0.255, and renderable delayed operator feedback.",
|
||||
"lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275; applying that blindly to cascaded PRC stacks is wrong until algorithm output compensation exists. Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL.",
|
||||
"next": "Follow docs/dx7-calibration-plan.md: calibrate feedback, envelope level curve, and algorithm output compensation before adding new DSL surface or tuning PRC constants."
|
||||
"status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, measured carrier output amplitude, isolated two-op modulation route scale, calibrated feedback amount table, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts and log-mel <= 0.255, and renderable delayed operator feedback.",
|
||||
"lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275; applying that blindly to cascaded PRC stacks is wrong until algorithm output compensation exists. Feedback now lowers through a fitted table instead of PRC-local constants. Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL.",
|
||||
"next": "Follow docs/dx7-calibration-plan.md: calibrate envelope level curve and algorithm output compensation before adding new DSL surface or tuning PRC constants."
|
||||
},
|
||||
"note_and_envelope_model": {
|
||||
"rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.",
|
||||
|
|
@ -80,7 +81,7 @@
|
|||
},
|
||||
"last_verified": {
|
||||
"command": "dotnet test AquariumSynthCSharp.slnx --no-restore",
|
||||
"result": "56 passed with AQUARIUM_DX7_PYTHON set to bundled Python",
|
||||
"commit": "pending DX7 modulation index calibration"
|
||||
"result": "57 passed with AQUARIUM_DX7_PYTHON set to bundled Python",
|
||||
"commit": "pending DX7 feedback calibration"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ Doctrine update:
|
|||
|
||||
Completed this slice:
|
||||
|
||||
- Completed the feedback calibration rung. Isolated Dexed feedback sweeps fit a
|
||||
nonlinear Aquarium feedback table: `0, .01, .02, .05, .10, .19, .38, .66`
|
||||
for DX7 feedback values `0..7`.
|
||||
- Added `Dx7SysEx.OperatorFeedbackAmount` and moved the PRC feedback lowering
|
||||
off the old `voice.Feedback * 0.04` constant. PRC barely changed because
|
||||
feedback value `5` maps to `.19`, close to the previous accidental `.20`.
|
||||
The win is ownership: feedback scaling is no longer folklore.
|
||||
|
||||
Previous slice:
|
||||
|
||||
- Completed the second calibration rung for isolated two-operator FM. A Dexed
|
||||
sweep fits full-scale DX7 modulation at about `12.55` radians. Given the
|
||||
current Aquarium Faust formula, that maps to an Aquarium route index of
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
project: AquariumSynthCSharp
|
||||
spine_version: 1
|
||||
updated: 2026-05-14T04:10:00+01:00
|
||||
updated: 2026-05-14T04:13:00+01:00
|
||||
|
||||
prime_directive: >
|
||||
Build coherent synth machinery. References exist to reveal missing structure,
|
||||
|
|
@ -46,6 +46,7 @@ live_invariants:
|
|||
- DX7 operator output level approximation belongs in reference import/rebuild helpers, not the generic DSL model.
|
||||
- DX7 carrier output amplitude follows the measured `2^((outputLevel - 99) / 8)` curve; do not restore the old internal-level proxy.
|
||||
- DX7 isolated two-operator modulation uses Aquarium route index `6.275` for full-level modulators; do not apply that blindly to cascaded stacks before algorithm output compensation is modeled.
|
||||
- DX7 feedback lowers through `Dx7SysEx.OperatorFeedbackAmount`; do not restore PRC-local `feedback * constant` folklore.
|
||||
- DX7 calibration must proceed through isolated rungs in `docs/dx7-calibration-plan.md`; do not keep tuning hard PRC-specific constants as a substitute for a level/index model.
|
||||
- Staged operator envelopes own multi-target operator level contours through `env=rl rates=... levels=...`; do not force DX7-style contours back into the general ADSR voice envelope model.
|
||||
- DX7 rate/level envelopes may lower to staged operator envelopes or labeled ADSR approximations, but exact DX7 EG timing/gain calibration remains missing pressure.
|
||||
|
|
@ -100,10 +101,10 @@ reference_targets:
|
|||
next_agent_start_here:
|
||||
recommended_first_task: >
|
||||
Listen to the latest generated PRC SYNTH1 WAVs under
|
||||
Continue the ladder in `docs/dx7-calibration-plan.md` with feedback scaling
|
||||
and algorithm output compensation. Single-carrier amplitude and isolated
|
||||
two-op modulation index are now fitted; cascaded PRC stacks still need a
|
||||
real compensation model.
|
||||
Continue the ladder in `docs/dx7-calibration-plan.md` with envelope level
|
||||
curve and algorithm output compensation. Single-carrier amplitude, isolated
|
||||
two-op modulation index, and feedback are now fitted; cascaded PRC stacks
|
||||
still need a real compensation model.
|
||||
must_read:
|
||||
- AGENTS.md
|
||||
- docs/audio-doctrine.md
|
||||
|
|
@ -116,6 +117,13 @@ next_agent_start_here:
|
|||
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
|
||||
|
||||
completed_slices:
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Completed the DX7 feedback calibration rung. Isolated feedback sweeps fit
|
||||
Aquarium feedback amounts `0, .01, .02, .05, .10, .19, .38, .66` for DX7
|
||||
feedback values 0-7. `Dx7SysEx.OperatorFeedbackAmount` now owns that
|
||||
table, and the PRC probe no longer uses a local `feedback * constant`.
|
||||
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"CalibratesDx7FeedbackToAquariumFeedbackAmount|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Completed the isolated two-operator modulation-index rung. Dexed sweeps
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ public sealed class Dx7ReferenceParityTests
|
|||
builder.AppendLine($" level={F(level)}");
|
||||
if (topology.SelfFeedbackOperators.Contains(op.Number))
|
||||
{
|
||||
builder.AppendLine($" feedback={F(voice.Feedback * 0.04f)}");
|
||||
builder.AppendLine($" feedback={F(Dx7SysEx.OperatorFeedbackAmount(voice.Feedback))}");
|
||||
}
|
||||
builder.AppendLine($" {envelope.ToScriptSpec()}");
|
||||
builder.AppendLine();
|
||||
|
|
|
|||
|
|
@ -216,6 +216,14 @@ public sealed class Dx7SysExTests
|
|||
Assert.InRange(fullScalePhaseDeviationRadians, 12.5f, 12.6f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalibratesDx7FeedbackToAquariumFeedbackAmount()
|
||||
{
|
||||
Assert.Equal(0, Dx7SysEx.OperatorFeedbackAmount(0));
|
||||
Assert.Equal(0.19f, Dx7SysEx.OperatorFeedbackAmount(5));
|
||||
Assert.Equal(0.66f, Dx7SysEx.OperatorFeedbackAmount(7));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApproximatesDx7RateLevelEnvelopeAsStagedEnvelope()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue