fractal-midi 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +200 -0
  2. package/NOTICE +28 -0
  3. package/README.md +147 -0
  4. package/dist/am4/applicability.d.ts +61 -0
  5. package/dist/am4/applicability.d.ts.map +1 -0
  6. package/dist/am4/applicability.js +285 -0
  7. package/dist/am4/blockTypes.d.ts +43 -0
  8. package/dist/am4/blockTypes.d.ts.map +1 -0
  9. package/dist/am4/blockTypes.js +48 -0
  10. package/dist/am4/cacheEnums.d.ts +46 -0
  11. package/dist/am4/cacheEnums.d.ts.map +1 -0
  12. package/dist/am4/cacheEnums.js +734 -0
  13. package/dist/am4/cacheParams.d.ts +3533 -0
  14. package/dist/am4/cacheParams.d.ts.map +1 -0
  15. package/dist/am4/cacheParams.js +1996 -0
  16. package/dist/am4/editorControlLabels.d.ts +45 -0
  17. package/dist/am4/editorControlLabels.d.ts.map +1 -0
  18. package/dist/am4/editorControlLabels.js +15894 -0
  19. package/dist/am4/index.d.ts +28 -0
  20. package/dist/am4/index.d.ts.map +1 -0
  21. package/dist/am4/index.js +31 -0
  22. package/dist/am4/ir/preset.d.ts +24 -0
  23. package/dist/am4/ir/preset.d.ts.map +1 -0
  24. package/dist/am4/ir/preset.js +12 -0
  25. package/dist/am4/ir/transpile.d.ts +9 -0
  26. package/dist/am4/ir/transpile.d.ts.map +1 -0
  27. package/dist/am4/ir/transpile.js +19 -0
  28. package/dist/am4/locations.d.ts +32 -0
  29. package/dist/am4/locations.d.ts.map +1 -0
  30. package/dist/am4/locations.js +58 -0
  31. package/dist/am4/paramNames.d.ts +55 -0
  32. package/dist/am4/paramNames.d.ts.map +1 -0
  33. package/dist/am4/paramNames.js +863 -0
  34. package/dist/am4/paramNamesGenerated.d.ts +41 -0
  35. package/dist/am4/paramNamesGenerated.d.ts.map +1 -0
  36. package/dist/am4/paramNamesGenerated.js +183 -0
  37. package/dist/am4/parameterBridge.d.ts +46 -0
  38. package/dist/am4/parameterBridge.d.ts.map +1 -0
  39. package/dist/am4/parameterBridge.js +300 -0
  40. package/dist/am4/params.d.ts +9577 -0
  41. package/dist/am4/params.d.ts.map +1 -0
  42. package/dist/am4/params.js +4537 -0
  43. package/dist/am4/setParam.d.ts +414 -0
  44. package/dist/am4/setParam.d.ts.map +1 -0
  45. package/dist/am4/setParam.js +819 -0
  46. package/dist/am4/shared/paramHelpers.d.ts +55 -0
  47. package/dist/am4/shared/paramHelpers.d.ts.map +1 -0
  48. package/dist/am4/shared/paramHelpers.js +146 -0
  49. package/dist/am4/symbolicIds.d.ts +11 -0
  50. package/dist/am4/symbolicIds.d.ts.map +1 -0
  51. package/dist/am4/symbolicIds.js +587 -0
  52. package/dist/am4/typeApplicability.d.ts +39 -0
  53. package/dist/am4/typeApplicability.d.ts.map +1 -0
  54. package/dist/am4/typeApplicability.js +466 -0
  55. package/dist/am4/variantResolverTables.d.ts +51 -0
  56. package/dist/am4/variantResolverTables.d.ts.map +1 -0
  57. package/dist/am4/variantResolverTables.js +3128 -0
  58. package/dist/axe-fx-ii/blockTypes.d.ts +45 -0
  59. package/dist/axe-fx-ii/blockTypes.d.ts.map +1 -0
  60. package/dist/axe-fx-ii/blockTypes.js +116 -0
  61. package/dist/axe-fx-ii/index.d.ts +5 -0
  62. package/dist/axe-fx-ii/index.d.ts.map +1 -0
  63. package/dist/axe-fx-ii/index.js +18 -0
  64. package/dist/axe-fx-ii/paramAliases.d.ts +54 -0
  65. package/dist/axe-fx-ii/paramAliases.d.ts.map +1 -0
  66. package/dist/axe-fx-ii/paramAliases.js +146 -0
  67. package/dist/axe-fx-ii/params.d.ts +11502 -0
  68. package/dist/axe-fx-ii/params.d.ts.map +1 -0
  69. package/dist/axe-fx-ii/params.js +2847 -0
  70. package/dist/axe-fx-ii/setParam.d.ts +560 -0
  71. package/dist/axe-fx-ii/setParam.d.ts.map +1 -0
  72. package/dist/axe-fx-ii/setParam.js +888 -0
  73. package/dist/axe-fx-iii/blockTypes.d.ts +87 -0
  74. package/dist/axe-fx-iii/blockTypes.d.ts.map +1 -0
  75. package/dist/axe-fx-iii/blockTypes.js +156 -0
  76. package/dist/axe-fx-iii/enumOverlay.d.ts +73 -0
  77. package/dist/axe-fx-iii/enumOverlay.d.ts.map +1 -0
  78. package/dist/axe-fx-iii/enumOverlay.js +236 -0
  79. package/dist/axe-fx-iii/index.d.ts +9 -0
  80. package/dist/axe-fx-iii/index.d.ts.map +1 -0
  81. package/dist/axe-fx-iii/index.js +20 -0
  82. package/dist/axe-fx-iii/params.d.ts +179 -0
  83. package/dist/axe-fx-iii/params.d.ts.map +1 -0
  84. package/dist/axe-fx-iii/params.js +6913 -0
  85. package/dist/axe-fx-iii/setParam.d.ts +460 -0
  86. package/dist/axe-fx-iii/setParam.d.ts.map +1 -0
  87. package/dist/axe-fx-iii/setParam.js +910 -0
  88. package/dist/index.d.ts +2 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +12 -0
  91. package/dist/shared/checksum.d.ts +10 -0
  92. package/dist/shared/checksum.d.ts.map +1 -0
  93. package/dist/shared/checksum.js +14 -0
  94. package/dist/shared/device.d.ts +195 -0
  95. package/dist/shared/device.d.ts.map +1 -0
  96. package/dist/shared/device.js +27 -0
  97. package/dist/shared/index.d.ts +8 -0
  98. package/dist/shared/index.d.ts.map +1 -0
  99. package/dist/shared/index.js +11 -0
  100. package/dist/shared/lineage/amp-lineage.json +8313 -0
  101. package/dist/shared/lineage/axefx2-amp-lineage.json +5871 -0
  102. package/dist/shared/lineage/axefx2-delay-lineage.json +226 -0
  103. package/dist/shared/lineage/axefx2-drive-lineage.json +575 -0
  104. package/dist/shared/lineage/axefx2-reverb-lineage.json +467 -0
  105. package/dist/shared/lineage/cab-lineage.json +10777 -0
  106. package/dist/shared/lineage/chorus-lineage.json +173 -0
  107. package/dist/shared/lineage/compressor-lineage.json +338 -0
  108. package/dist/shared/lineage/delay-lineage.json +313 -0
  109. package/dist/shared/lineage/drive-lineage.json +1844 -0
  110. package/dist/shared/lineage/flanger-lineage.json +313 -0
  111. package/dist/shared/lineage/phaser-lineage.json +208 -0
  112. package/dist/shared/lineage/reverb-lineage.json +793 -0
  113. package/dist/shared/lineage/wah-lineage.json +117 -0
  114. package/dist/shared/lineageLookup.d.ts +69 -0
  115. package/dist/shared/lineageLookup.d.ts.map +1 -0
  116. package/dist/shared/lineageLookup.js +196 -0
  117. package/dist/shared/packValue.d.ts +40 -0
  118. package/dist/shared/packValue.d.ts.map +1 -0
  119. package/dist/shared/packValue.js +105 -0
  120. package/dist/shared/types.d.ts +23 -0
  121. package/dist/shared/types.d.ts.map +1 -0
  122. package/dist/shared/types.js +9 -0
  123. package/package.json +75 -0
@@ -0,0 +1,863 @@
1
+ // Universal per-block output Balance at cache id=2 — signature
2
+ // (a=-1, b=1, c=100) across every confirmed block. Blocks Guide §347
3
+ // documents Balance as a standard block-level parameter that pans
4
+ // the block's output between left and right. Requires the
5
+ // `bipolar_percent` unit (display -100..+100, internal -1..+1,
6
+ // scale 100) which generator default for c=100 would misclassify
7
+ // as plain `percent` (0..100).
8
+ const BALANCE = {
9
+ name: 'balance',
10
+ unit: 'bipolar_percent',
11
+ displayMin: -100,
12
+ displayMax: 100,
13
+ };
14
+ export const PARAM_NAMES = {
15
+ amp: {
16
+ 2: BALANCE,
17
+ // Session 29 (HW-015): Out Boost Level — dB knob on the Extras tab,
18
+ // cache (a=0, b=4, c=1, step=0.05). Wire-verified at pidHigh=0x08.
19
+ 8: { name: 'out_boost_level', unit: 'db', displayMin: 0, displayMax: 4 },
20
+ 10: 'type',
21
+ 11: 'gain',
22
+ 12: 'bass',
23
+ // ids 13/14 (mid/treble) still structural — cache signature identical
24
+ // to gain/bass (knob_0_10, 0..1 range, step 0.001). Named per the
25
+ // AM4 Owner's Manual line 1563 tone-stack order "Gain, Bass, Mid,
26
+ // Treble, Presence, Level". HW-014 spot-check still pending.
27
+ 13: 'mid',
28
+ 14: 'treble',
29
+ // Session 29 (HW-015): id 15 (pidHigh=0x0f) was mis-inferred as
30
+ // 'presence' in Session 26 from the cache signature alone. Two
31
+ // wire captures (amp-master on an unknown Marshall-family amp +
32
+ // amp-master-2 on "Brit 800 #34") prove this register is Master.
33
+ // Real Presence was subsequently captured at id 30 (pidHigh=0x1e).
34
+ 15: 'master',
35
+ // Session 29 (HW-015): Depth at pidHigh=0x1a, knob_0_10. Wire-
36
+ // verified with a full 0→10 sweep capture.
37
+ 26: 'depth',
38
+ // Session 29 (HW-015): Presence at pidHigh=0x1e, knob_0_10. Wire-
39
+ // verified on the same amp as amp-master. Corrects the Session 26
40
+ // structural guess at id 15.
41
+ 30: 'presence',
42
+ // HW-040 (Session 36, 2026-04-29): Amp Expert-Edit page from
43
+ // session-40-amp-expert.pcapng + paired AM4-Edit screenshot
44
+ // (FAS Modern III). Wiggle order + screenshot column order
45
+ // disambiguates the OFF/ON switches in the IDEAL column.
46
+ 20: { name: 'bright_cap', unit: 'pf', displayMin: 10, displayMax: 10000 },
47
+ 54: { name: 'input_trim', unit: 'count', displayMin: 0.1, displayMax: 10 },
48
+ // amp's 8-band GEQ stores ±1 wire, scale ×12 → display ±12 dB.
49
+ // Cache ids 62..69 share the (a=-1, b=1, c=12) signature. Uses the
50
+ // `amp_geq_band` unit (scale 12) — distinct from drive's GEQ which
51
+ // stores ±12 wire directly (cache c=1) and uses plain `db`.
52
+ 62: { name: 'geq_band_1', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
53
+ 63: { name: 'geq_band_2', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
54
+ 64: { name: 'geq_band_3', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
55
+ 65: { name: 'geq_band_4', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
56
+ 66: { name: 'geq_band_5', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
57
+ 67: { name: 'geq_band_6', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
58
+ 68: { name: 'geq_band_7', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
59
+ 69: { name: 'geq_band_8', unit: 'amp_geq_band', displayMin: -12, displayMax: 12 },
60
+ 77: { name: 'compressor_clarity', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
61
+ 82: { name: 'compressor_amount', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
62
+ 83: { name: 'compressor_threshold', unit: 'db', displayMin: -60, displayMax: 0 },
63
+ 84: { name: 'master_vol_trim', unit: 'count', displayMin: 0.1, displayMax: 10 },
64
+ 104: { name: 'high_treble', unit: 'db', displayMin: -12, displayMax: 12 },
65
+ // Session 89 (2026-05-16): DISTORT UI-MISSING closeout from
66
+ // samples/captured/decoded/am4-params-proposed.ts (Ghidra-mined
67
+ // catalog, Sessions 82–83) + cross-ref audit (35 UI-MISSING amp
68
+ // params at pidLow=0x003a). Same workflow as REVERB + DELAY
69
+ // commit 5de0870: route through paramNames.ts overrides to
70
+ // correct the cache pipeline's c=1 → 'db' fallback for Hz / count
71
+ // entries, plus full overrides where c is non-default (c=0.4166
72
+ // for spkr-reso knobs, c=2 for spkrdrive, c=31.62 for definition).
73
+ // Names re-state the GENERATED_PARAM_NAMES entry verbatim where
74
+ // present (firmware-truth from AM4-Edit.exe's variant resolver);
75
+ // only unit / displayMin / displayMax overrides are emitted to
76
+ // correct the cache pipeline defaults. Enum-typed ids (typecode
77
+ // 16) need custom value tables and stay hand-authored in
78
+ // params.ts later — see TODOs at the end of the amp: block.
79
+ //
80
+ // id=34 DISTORT_SPKRLFGAIN ("Low Reso") — cache type=48 a=0
81
+ // b=24 c=0.4166666… → display ×c → 0..10 knob. Generator can't
82
+ // infer c=0.4166; full override required. No GENERATED entry
83
+ // (resolver doesn't reach this id), so emit the name here too.
84
+ 34: { name: 'low_reso', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
85
+ // id=38 DISTORT_MVCAP ("Master Vol Cap") — cache type=72 (log10)
86
+ // a=1e-6 b=1e-3 c=1e6 → 1..1000 pF (Master-Volume bypass
87
+ // capacitor in pF; sibling to amp.bright_cap at id=20 which uses
88
+ // the same pf unit). GENERATED has `master_vol_cap`.
89
+ 38: { name: 'master_vol_cap', unit: 'pf', displayMin: 1, displayMax: 1000 },
90
+ // id=51 DISTORT_SPKRHFGAIN ("Hi Reso") — same shape as id=34
91
+ // (cache type=48, c=0.4166666…). GENERATED has `hi_reso`.
92
+ 51: { name: 'hi_reso', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
93
+ // id=57 DISTORT_SPKRDRIVE ("Drive") — speaker-stage drive knob.
94
+ // Cache type=48 a=0 b=5 c=2 → display 0..10 knob. Generator can't
95
+ // infer c=2; full override required. GENERATED has `drive`, which
96
+ // collides with the existing drive block's `drive` (different
97
+ // block — no collision at lookup). Renamed to `spkr_drive` to
98
+ // avoid amp.drive vs drive.drive ambiguity in tool descriptions.
99
+ 57: { name: 'spkr_drive', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
100
+ // id=79 DISTORT_INEQFREQ ("Frequency") — input-EQ peaking
101
+ // frequency. Cache type=66 a=100 b=10000 c=1 → Hz; default 'db'
102
+ // wrong. GENERATED has `frequency`. The label "Frequency" alone
103
+ // is ambiguous across amp's two EQ stages — renamed to
104
+ // `input_eq_frequency` to mirror the existing `input_eq_low_cut`
105
+ // / `input_eq_q` / `input_eq_gain` naming at adjacent paramIds.
106
+ 79: { name: 'input_eq_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
107
+ // id=81 DISTORT_DRIVE2 ("Normal Gain") — second drive register
108
+ // for amps with a Normal channel (Marshall JTM/JCM-style).
109
+ // Cache type=48 c=10 → knob_0_10 inference works; unit not
110
+ // overridden. GENERATED has `overdrive` from the variant
111
+ // resolver — keep the resolver name. (Cache pipeline will
112
+ // auto-emit this entry on next gen-params since GENERATED
113
+ // supplies the name.) Registered here for documentation and
114
+ // to give the entry an explicit displayMin/Max audit trail.
115
+ 81: { name: 'overdrive', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
116
+ // id=86 DISTORT_DEFINITION ("Definition") — Power-amp definition
117
+ // knob. Cache type=48 a=-0.31623 b=0.31623 c=31.62299 → bipolar
118
+ // ×c → -10..+10. The 31.62 scale is ≈ 10/√10 — power-amp
119
+ // definition appears stored on a log-axis but displayed as a
120
+ // bipolar knob. Generator can't infer c=31.62; full override.
121
+ // Use `count` (not bipolar_percent) since the front-panel reads
122
+ // -10.0..+10.0, not ±100%.
123
+ 86: { name: 'definition', unit: 'count', displayMin: -10, displayMax: 10 },
124
+ // id=87 DISTORT_CFTHRESH ("Compression") — Cathode-Follower
125
+ // compression amount. Cache type=53 c=100 → percent inference
126
+ // works; unit not overridden. GENERATED has `compression`.
127
+ 87: { name: 'compression', unit: 'percent', displayMin: 0, displayMax: 100 },
128
+ // id=90 DISTORT_HICUT ("High Cut") — Preamp high-cut Hz.
129
+ // Cache type=66 a=200 b=20000 c=1 → Hz; default 'db' wrong.
130
+ // GENERATED has `high_cut`.
131
+ 90: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
132
+ // id=100 DISTORT_CBRATIO ("Cathode Resistance") — Cathode-bias
133
+ // resistance amount. Cache type=53 c=100 → percent inference
134
+ // works; unit not overridden. GENERATED has `cathode_resistance`.
135
+ 100: { name: 'cathode_resistance', unit: 'percent', displayMin: 0, displayMax: 100 },
136
+ // id=125 DISTORT_VCCMON ("B+") — Power-supply B+ voltage MONITOR
137
+ // (read-only meter, not a knob). Cache type=0 a=0 b=1 c=1 → raw
138
+ // 0..1 float. Display as `count` (0..1) instead of the dB default.
139
+ // GENERATED has `vccmon` (resolver had no XML label). Renamed
140
+ // to `b_plus_monitor` to surface the function (it's the B+
141
+ // headroom indicator behind the "B+" front-panel display).
142
+ 125: { name: 'b_plus_monitor', unit: 'count', displayMin: 0, displayMax: 1 },
143
+ // id=126 DISTORT_GAINMON ("Gain") — Drive-stage gain MONITOR
144
+ // (read-only meter). Cache type=0 c=1 → count. GENERATED has
145
+ // `gain_gainmon` (dedupe artifact since `gain` is owned by id=11).
146
+ // Renamed to `gain_monitor` for clarity.
147
+ 126: { name: 'gain_monitor', unit: 'count', displayMin: 0, displayMax: 1 },
148
+ // id=137 DISTORT_VPLATEMON ("HEADROOM") — Power-amp plate-
149
+ // voltage headroom monitor. Cache type=0 c=1 → count.
150
+ // GENERATED has `headroom`.
151
+ 137: { name: 'headroom_monitor', unit: 'count', displayMin: 0, displayMax: 1 },
152
+ // id=138 DISTORT_PREPRESENCE ("Treble") — Preamp-stage presence
153
+ // shaper (the label is "Treble" on some amps, but the wire/
154
+ // catalog calls it PREPRESENCE — pre-power-amp presence).
155
+ // Cache type=48 c=10 → knob_0_10 inference works. GENERATED has
156
+ // `presence_prepresence` (dedupe vs amp.presence at id=30).
157
+ 138: { name: 'presence_prepresence', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
158
+ // id=140 DISTORT_PAHICUT ("Tone") — Power-amp high-cut shaper
159
+ // (label is "Tone" on AM4-Edit, catalog calls it PAHICUT).
160
+ // Cache type=48 c=10 → knob_0_10 inference works. GENERATED has
161
+ // `high_cut_pahicut`. Renamed to `pa_high_cut` for the same
162
+ // reason `high_cut` (id=90) is preamp-side: clearer family
163
+ // separation in describe_device output.
164
+ 140: { name: 'pa_high_cut', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
165
+ // id=145 DISTORT_GLOBALMASTER ("Overdrive Volume") — Global
166
+ // post-amp master that scales after the cab sim. Cache type=48
167
+ // c=10 → knob_0_10 inference works. GENERATED has
168
+ // `overdrive_volume`.
169
+ 145: { name: 'overdrive_volume', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
170
+ // TODOs — enum-typed ids (cache typecode=16). Each needs a custom
171
+ // enum value table hand-authored in params.ts; auto-emission via
172
+ // paramNames.ts would force the wrong enumImport (AMP_TYPES_VALUES
173
+ // is the Type enum at id=10, not these per-feature enums). The
174
+ // GENERATED_PARAM_NAMES table also skips these because the
175
+ // resolver only emits to non-enum cache slots. Symbol + range +
176
+ // value labels listed below for the future capture-driven pass:
177
+ // id=47 DISTORT_BOOST enum 0..1 "In Boost Sw" [OFF, ON]
178
+ // id=61 DISTORT_SATSWITCH enum 0..2 "Saturation Sw" [OFF, ON, ON (IDEAL)]
179
+ // id=76 DISTORT_PRETUBETYPE enum 0..8 "Preamp Tube Type" [12AX7A SYL, ECC83, 7025, 12AX7A JJ, ECC803S, EF86, 12AX7A RCA, 12AX7A, 12AX7B]
180
+ // id=93 DISTORT_SUPPLYTYPE enum 0..1 "Power Type" [AC, DC]
181
+ // id=103 DISTORT_PRESAG enum 0..1 "Preamp Sag" [OFF, ON]
182
+ // id=109 DISTORT_INEQTYPE enum 0..3 "Type" [LOWSHELF, PEAKING, HIGHSHELF, TILT EQ]
183
+ // id=111 DISTORT_PRESSHIFT enum 0..1 "Pres. Shift" [OFF, ON]
184
+ // id=117 DISTORT_EQPOSITION enum 0..2 "Location" [OUTPUT, PRE P.A., INPUT]
185
+ // id=130 DISTORT_BOOSTTYPE enum 0..14 "In Boost Type" [NEUTRAL, T808, T808 MOD, SUPER OD, FULL OD, AC BOOST, SHIMMER, FAS BOOST, GRINDER, TREBLE BOOST, MID BOOST, CC BOOST, SHRED BOOST, RCB BOOST, JP IIC+ SHRED]
186
+ // id=133 DISTORT_EQONOFF enum 0..1 "Off / On" [OFF, ON]
187
+ // id=135 DISTORT_SPKRMODEL enum 0..92 "Spkr Imp. Curve" — 93-cab impedance-curve table (factory speaker IRs). Big table; consider a dedicated SPKR_IMP_CURVE_VALUES export in cacheEnums.ts.
188
+ // id=141 DISTORT_PAONOFF enum 0..1 "Power Amp Modeling" [OFF, ON]
189
+ // id=142 DISTORT_SPKRBREAKUP enum 0..2 "Breakup" [SOFT, MEDIUM, HARD]
190
+ // id=144 DISTORT_PLATEDIODE enum 0..1 "Plate Suppr. Diodes" [OFF, ON]
191
+ // id=146 DISTORT_AUTO_SPKR_Z enum 0..1 "DynaMatch" [OFF, ON]
192
+ // id=147 DISTORT_NFBCOMP enum 0..1 "NFB Compensation" [OFF, ON]
193
+ // id=148 DISTORT_MODE_1 enum 0..3 "Mid/Gain Boost" [mode 1, mode 2, mode 3, mode 4]
194
+ // id=149 DISTORT_MODE_2 enum 0..3 "Tubes" — cache lists kind=float typecode=16 a=0 b=3; treat as 4-state enum (values pending capture).
195
+ //
196
+ // TODO — action / button-class (no cache record):
197
+ // id=65520 DISTORT_ZEROEQ button "Zero All" — XML exposes
198
+ // this as a UI control but cache has no record (button class,
199
+ // not a stored param). Wire-write should send the action to the
200
+ // GEQ block to reset all bands to 0 dB. Likely needs a synthetic
201
+ // entry in params.ts with a fixed action payload rather than a
202
+ // value range. Defer pending capture-confirmed wire shape.
203
+ },
204
+ drive: {
205
+ 2: BALANCE,
206
+ 10: 'type',
207
+ 11: 'drive',
208
+ // AM4 Owner's Manual line 1330: "Page Right and dial in Drive, Tone,
209
+ // and Level." Cache records at 0x0C and 0x0D have the identical
210
+ // knob_0_10 signature to drive.drive (0x0B); typical pedal-UI order
211
+ // matches. `mix` at 0x0E follows the universal Mix Page pattern
212
+ // (percent). All three await Session D hardware spot-check.
213
+ 12: 'tone',
214
+ 13: 'level',
215
+ 14: 'mix',
216
+ // HW-019 (Session 30, 2026-04-25): EQ-page knobs decoded from
217
+ // session-30-drive-basic-blackglass-7k. Cache ids 16/17 are the
218
+ // Hz cuts (raw passthrough — c=1 default would mis-classify as dB),
219
+ // ids 20/21/23 are the knob_0_10 Bass/Mid/Treble flanking id 22
220
+ // (mid frequency in Hz). T808 OD doesn't expose these — the
221
+ // session-30-drive-basic-t808-od capture only had drive/tone/level.
222
+ 16: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
223
+ 20: 'bass',
224
+ 21: 'mid',
225
+ 22: { name: 'mid_freq', unit: 'hz', displayMin: 200, displayMax: 2000 },
226
+ 23: 'treble',
227
+ // HW-029 + HW-039 (Session 35, 2026-04-29): Blackglass 7K Drive
228
+ // Expert-Edit page exposes a second-Hz cut + a 10-band post-Drive
229
+ // Graphic EQ + DIGITAL LO-FI + ADVANCED knobs. Decoded from
230
+ // session-31-drive-expert.pcapng + paired AM4-Edit screenshot.
231
+ // Closes HW-029 (0x002d = high_mid knob, knob_0_10 — wiggled
232
+ // adjacent to drive.mid_freq + drive.treble in the timeline).
233
+ //
234
+ // - id 17 (0x0011): high_cut sibling to id 16. Cache c=1 a=200
235
+ // b=20000 — needs the 'hz' override (default would be dB).
236
+ // - id 24 (0x0018): Bit Reduce count, cache a=0 b=24 c=1 raw.
237
+ // Uses the 'count' unit (default for c=1 would be dB).
238
+ // - id 45 (0x002d): drive.high_mid for Blackglass 7K (cache c=10
239
+ // knob_0_10). Type-specific UI label varies; the register name
240
+ // reflects the most common Blackglass usage.
241
+ 17: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
242
+ 24: { name: 'bit_reduce', unit: 'count', displayMin: 0, displayMax: 24 },
243
+ // 10-band post-Drive Graphic EQ — cache ids 29..38 all share the
244
+ // bipolar dB ±12 signature (a=-12 b=12 c=1 step=0.025). Frequencies
245
+ // per the screenshot: 100, 160, 250, 400, 640, 1000, 1600, 2500,
246
+ // 4000, 6400 Hz. Wire-display match is byte-exact on all 10 bands
247
+ // (capture vs screenshot agree exactly).
248
+ 29: { name: 'geq_band_1', unit: 'db', displayMin: -12, displayMax: 12 },
249
+ 30: { name: 'geq_band_2', unit: 'db', displayMin: -12, displayMax: 12 },
250
+ 31: { name: 'geq_band_3', unit: 'db', displayMin: -12, displayMax: 12 },
251
+ 32: { name: 'geq_band_4', unit: 'db', displayMin: -12, displayMax: 12 },
252
+ 33: { name: 'geq_band_5', unit: 'db', displayMin: -12, displayMax: 12 },
253
+ 34: { name: 'geq_band_6', unit: 'db', displayMin: -12, displayMax: 12 },
254
+ 35: { name: 'geq_band_7', unit: 'db', displayMin: -12, displayMax: 12 },
255
+ 36: { name: 'geq_band_8', unit: 'db', displayMin: -12, displayMax: 12 },
256
+ 37: { name: 'geq_band_9', unit: 'db', displayMin: -12, displayMax: 12 },
257
+ 38: { name: 'geq_band_10', unit: 'db', displayMin: -12, displayMax: 12 },
258
+ 45: 'high_mid',
259
+ },
260
+ reverb: {
261
+ 1: 'mix',
262
+ 2: BALANCE,
263
+ 10: 'type',
264
+ // Blocks Guide §Reverb Basic Page: "Time — Sets the decay time."
265
+ // Cache 0x0B is 0.1..100 seconds, c=1 (raw passthrough). Needs the
266
+ // 'seconds' unit override — generator default for c=1 is 'db'.
267
+ // displayMin rounded to 0.1 (cache stores 0.10000000149…).
268
+ 11: { name: 'time', unit: 'seconds', displayMin: 0.1 },
269
+ // Session 29 (HW-015): Size at pidHigh=0x0f, percent. Wire-verified
270
+ // on two captures — "Plate Size" (on Plate reverb type) and "Size"
271
+ // (on Room reverb type) both wrote to this register, confirming
272
+ // it's a universal reverb-size knob whose UI label depends on the
273
+ // active reverb type.
274
+ 15: 'size',
275
+ // BK-033 (HW-025 #1, Session 30): the cache record at id=16 (0x10)
276
+ // signature LOOKED like predelay (0..0.25s × 1000 = 0..250 ms) but
277
+ // wire-testing proved it's a dead address — writes ack but the
278
+ // firmware ignores them. The real predelay register is id=19 (0x13);
279
+ // AM4-Edit captures wrote there for "Pre-Delay → 85 ms / 111.4 ms".
280
+ // Skipping id=16 here so the generator doesn't emit the wrong cache
281
+ // mapping; the corrected entry lives hand-authored in params.ts.
282
+ // The cache record at 0x13 has no name slot here either — it's
283
+ // not exposed via the auto-gen path; instead reverb.predelay is
284
+ // a pure KNOWN_PARAMS hand-authored entry going forward.
285
+ // Session 29 (HW-015): Spring-reverb-specific. Number of Springs
286
+ // (integer count 2..6) at pidHigh=0x1b; cache c=1 structurally
287
+ // ambiguous — needs 'count' override. Spring Tone (knob_0_10) at
288
+ // pidHigh=0x1c; cache signature matches knob_0_10 default. Both
289
+ // only visible in AM4-Edit when a Spring reverb type is active,
290
+ // but the registers remain writable on any type — writes simply
291
+ // no-op on non-spring reverbs.
292
+ 27: { name: 'springs', unit: 'count', displayMin: 2, displayMax: 6 },
293
+ 28: 'spring_tone',
294
+ // Session 29 follow-up (2026-04-21): Shimmer Verb / Plex Verb
295
+ // "Shift 1" and "Shift 2" pitch-shifter voices. Blocks Guide
296
+ // §Shimmer Verb Parameters: "Shift 1–8 — Sets the amount of
297
+ // detune within a range of ±24 semitones. This is where
298
+ // 'Shimmer' is born." AM4's reverb has two such voices (ids
299
+ // 56/57); the AxeFx/FM8-voice variant ships more. Cache signature
300
+ // (a=-24, b=24, c=1, step=1) matches the BG documentation
301
+ // exactly — needs the 'semitones' unit override since c=1 is
302
+ // structurally ambiguous. Structural registration; HW-014-style
303
+ // spot-check still required.
304
+ 56: { name: 'shift_1', unit: 'semitones', displayMin: -24, displayMax: 24 },
305
+ 57: { name: 'shift_2', unit: 'semitones', displayMin: -24, displayMax: 24 },
306
+ // Session 88 (2026-05-16): REVERB params from
307
+ // samples/captured/decoded/am4-params-proposed.ts (Ghidra-mined
308
+ // catalog, Session 82–83). Routed through paramNames.ts overrides
309
+ // (not direct cacheParams hand-edit) per the Session 84 unit-fallback
310
+ // trap — the cache pipeline's c=1 default emits `unit: 'db'`, wrong
311
+ // for Hz / Q / count / semitones / seconds. Names defer to the
312
+ // resolver-derived GENERATED_PARAM_NAMES entries (firmware-truth from
313
+ // AM4-Edit.exe's variant resolver) where they exist; only unit /
314
+ // displayMin / displayMax overrides are emitted to correct the
315
+ // cache pipeline defaults. Eight ids (51, 53, 54, 59, 61, 64, 68,
316
+ // 70) have no GENERATED entry — those that are enums need a custom
317
+ // value list and stay hand-authored in params.ts (see TODOs below).
318
+ //
319
+ // id=13 REVERB_HFRATIO ("High Decay") — cache type=64 log10
320
+ // a=0.01 b=1 → decay-ratio knob, NOT dB. Display 0.01..1 as a
321
+ // count (effectively a percent ×100, but cache emits as raw float).
322
+ 13: { name: 'high_decay', unit: 'count', displayMin: 0.01, displayMax: 1 },
323
+ // id=22 REVERB_RATE — cache type=66 log10 a=0.01 b=1 → modulation
324
+ // rate. Range 0..1 is a normalized rate knob (UI shows 0.0..1.00 Hz
325
+ // approximately). Use 'hz' with the raw range.
326
+ 22: { name: 'rate', unit: 'hz', displayMin: 0.01, displayMax: 1 },
327
+ // ids 30/31 REVERB_FREQ1/FREQ2 — cache type=66 log10 c=1, a/b are
328
+ // the actual Hz range. Default 'db' is wrong.
329
+ 30: { name: 'frequency_1', unit: 'hz', displayMin: 20, displayMax: 2000 },
330
+ 31: { name: 'frequency_2', unit: 'hz', displayMin: 100, displayMax: 10000 },
331
+ // ids 32/33 REVERB_Q1/Q2 — cache type=64 log10 a=0.1 b=10 → Q
332
+ // factor 0.1..10. Default 'db' is wrong; use 'count'.
333
+ 32: { name: 'q_1', unit: 'count', displayMin: 0.1, displayMax: 10 },
334
+ 33: { name: 'q_2', unit: 'count', displayMin: 0.1, displayMax: 10 },
335
+ // id=37 REVERB_LFTIME ("Low Decay") — cache type=64 log10 a=0.02
336
+ // b=2 sec → seconds, not dB.
337
+ 37: { name: 'low_decay', unit: 'seconds', displayMin: 0.02, displayMax: 2 },
338
+ // id=38 REVERB_LFXOVER ("Xover Frequency") — cache type=66 a=100
339
+ // b=10000 → Hz.
340
+ 38: { name: 'xover_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
341
+ // id=45 REVERB_EARLYDECAY ("Early Decay") — cache c=50 a=0 b=2 →
342
+ // display 0..100% (a*c..b*c). Generator can't infer c=50; full
343
+ // override required.
344
+ 45: { name: 'early_decay', unit: 'percent', displayMin: 0, displayMax: 100 },
345
+ // id=49 REVERB_BASETYPE — cache kind=float type=16 range 0..8 →
346
+ // integer count selector (which base reverb algorithm). Should
347
+ // ideally be an enum with named base types, but cache lacks the
348
+ // value table — registered as count for now.
349
+ 49: { name: 'basetype', unit: 'count', displayMin: 0, displayMax: 8 },
350
+ // id=50 REVERB_LFOPHASE — cache type=54 c=57.29578 (rad→deg) →
351
+ // 0..180 degrees per radians-encoded LFO phase convention.
352
+ 50: { name: 'lfo_phase', unit: 'degrees', displayMin: 0, displayMax: 180 },
353
+ // id=55 REVERB_PITCHMIX — c=100 already correct percent; no
354
+ // override needed. Skipping.
355
+ // id=60 REVERB_PITCHTIME ("Splice Time") — cache type=52 c=1000
356
+ // a=0.01 b=2 sec → 10..2000 ms. Generator emits 'ms' but
357
+ // displayMin defaults to 0; override the lower bound.
358
+ 60: { name: 'splice_time', unit: 'ms', displayMin: 10, displayMax: 2000 },
359
+ // id=63 REVERB_PITCHBAL ("Voice Balance") — c=100 a=-1 b=1 →
360
+ // bipolar_percent; generator default for c=100 is plain percent.
361
+ 63: { name: 'voice_balance', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
362
+ // id=67 REVERB_PITCHLPF ("Pitch High Cut") — cache type=66 a=200
363
+ // b=20000 → Hz; default 'db' wrong.
364
+ 67: { name: 'pitch_high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
365
+ // ids 71/72 REVERB_LOWQ/HIGHQ ("Low Cut Q" / "High Cut Q") —
366
+ // cache type=64 log10 a=0.1 b=10 → Q factor count; default 'db'
367
+ // wrong.
368
+ 71: { name: 'low_cut_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
369
+ 72: { name: 'high_cut_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
370
+ // TODO (no GENERATED entry, enum without value table — hand-author
371
+ // in params.ts with proper enum map):
372
+ // id=51 REVERB_INPUTSELECT enum 0..2 (likely L+R / L / R)
373
+ // id=53 REVERB_LOWSLOPE enum 0..1 (slope toggle)
374
+ // id=54 REVERB_HIGHSLOPE enum 0..1 (slope toggle)
375
+ // id=59 REVERB_PITCHDIR enum 0..3 (pitch shift direction)
376
+ // id=61 REVERB_PITCHPOS enum 0..2 (pitch position)
377
+ // id=64 REVERB_PREDLYTEMPO enum 0..78 (TEMPO_DIVISIONS_VALUES)
378
+ // id=68 REVERB_SPRINGTYPE enum 0..1 (spring type toggle)
379
+ // id=70 REVERB_PREDLYTAP enum 0..1 (predelay tap mode)
380
+ },
381
+ delay: {
382
+ // Mix follows the universal percent-at-0x01 pattern (Blocks Guide
383
+ // §Common Mix/Level Parameters, p. 7). "delay block uses a
384
+ // different Mix Law compared to other blocks" — same param, just
385
+ // different internal curve; still the wet/dry knob.
386
+ 1: 'mix',
387
+ 2: BALANCE,
388
+ 10: 'type',
389
+ 12: 'time',
390
+ // Session 29 (HW-015): Feedback at pidHigh=0x0e. Cache (a=-1, b=1,
391
+ // c=100) is bipolar — negative feedback inverts the phase of the
392
+ // repeats, a standard Fractal delay feature.
393
+ 14: { name: 'feedback', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
394
+ // HW-020 (Session 30, 2026-04-25): Ducking attenuation amount,
395
+ // session-30-delay-basic-digital-mono capture. Cache id=46 a=0
396
+ // b=80 c=1 → raw dB 0..80. Same signature as reverb.ducking
397
+ // (HW-018). delay.level (out-of-band, pidHigh=0x0000) and
398
+ // delay.stack_hold (per-block non-Type enum, pidHigh=0x001f) are
399
+ // hand-authored in params.ts directly.
400
+ 46: 'ducking',
401
+ // HW-040 (Session 36, 2026-04-29): Delay Expert-Edit page on
402
+ // Ambient Stereo from session-40-delay-expert.pcapng. ~32 new
403
+ // params across BASIC + DIFFUSOR + EQ + MIX + DUCKER + COMPANDER
404
+ // + STACK/HOLD + LO FI sections. Cache shapes pin units; screenshot
405
+ // labels confirm names. Bypass_mode / kill_dry / phase_reverse /
406
+ // slopes / compander enums are hand-authored in params.ts.
407
+ 13: { name: 'lr_time_ratio', unit: 'percent', displayMin: 1, displayMax: 100 },
408
+ 16: { name: 'feedback_r', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
409
+ 18: { name: 'stereo_spread', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
410
+ 20: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
411
+ 21: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
412
+ 27: { name: 'input_gain', unit: 'percent', displayMin: 0, displayMax: 100 },
413
+ 32: { name: 'master_feedback', unit: 'percent', displayMin: 0, displayMax: 200 },
414
+ 47: { name: 'ducker_threshold', unit: 'db', displayMin: -80, displayMax: 20 },
415
+ 48: { name: 'ducker_release', unit: 'ms', displayMin: 1, displayMax: 1000 },
416
+ 49: { name: 'diffusor', unit: 'percent', displayMin: 0, displayMax: 100 },
417
+ 50: { name: 'diffusion_time', unit: 'percent', displayMin: 1, displayMax: 100 },
418
+ 63: { name: 'eq_q_high_low', unit: 'count', displayMin: 0.1, displayMax: 10 },
419
+ 64: { name: 'bit_reduction', unit: 'count', displayMin: 0, displayMax: 24 },
420
+ 65: { name: 'eq_freq_1', unit: 'hz', displayMin: 20, displayMax: 2000 },
421
+ 66: { name: 'eq_freq_2', unit: 'hz', displayMin: 100, displayMax: 10000 },
422
+ 67: { name: 'eq_q_1', unit: 'count', displayMin: 0.1, displayMax: 10 },
423
+ 68: { name: 'eq_q_2', unit: 'count', displayMin: 0.1, displayMax: 10 },
424
+ 69: { name: 'eq_gain_1', unit: 'db', displayMin: -12, displayMax: 12 },
425
+ 70: { name: 'eq_gain_2', unit: 'db', displayMin: -12, displayMax: 12 },
426
+ // HW-053 (2026-05-04): cache id=72 (DELAY_SPEED, "Motor Speed") has
427
+ // a=0.5, b=2, c=1 — a tape-motor speed multiplier, not dB. Without
428
+ // this hand override the cache pipeline emits unit='db' from the
429
+ // c=1 default. Range 0.5..2.0 = half-speed to double-speed; only
430
+ // applies when delay.type is Ping-Pong (per type-applicability).
431
+ 72: { name: 'motor_speed', unit: 'count', displayMin: 0.5, displayMax: 2 },
432
+ 76: { name: 'compander_time', unit: 'ms', displayMin: 1, displayMax: 100 },
433
+ 77: { name: 'compander_threshold', unit: 'db', displayMin: -100, displayMax: -20 },
434
+ 78: { name: 'master_time', unit: 'percent', displayMin: 25, displayMax: 400 },
435
+ 79: { name: 'lfo_rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
436
+ 80: { name: 'lfo_depth', unit: 'percent', displayMin: 0, displayMax: 100 },
437
+ 87: { name: 'stack_feedback', unit: 'percent', displayMin: 0, displayMax: 100 },
438
+ 88: { name: 'hold_feedback', unit: 'percent', displayMin: 0, displayMax: 100 },
439
+ // Session 88 (2026-05-16): DELAY params from
440
+ // samples/captured/decoded/am4-params-proposed.ts (Ghidra-mined
441
+ // catalog, Session 82–83). Same workflow as the REVERB block above:
442
+ // route through paramNames.ts overrides to correct the cache
443
+ // pipeline's c=1 → 'db' fallback for Hz / Q / count / degrees
444
+ // entries. Names use the resolver-derived GENERATED_PARAM_NAMES
445
+ // entries (firmware-truth) where they exist; ids with no GENERATED
446
+ // entry are enums that need custom value tables and stay
447
+ // hand-authored in params.ts (see TODOs below).
448
+ //
449
+ // id=17 DELAY_DELAYPAN ("Echo Pan") — c=100 a=-1 b=1 → bipolar
450
+ // pan, not a 0..100 percent. Generator default for c=100 is plain
451
+ // 'percent'; override to bipolar_percent.
452
+ 17: { name: 'echo_pan', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
453
+ // ids 22/23 DELAY_RATE1/RATE2 ("Mod Rate" / "Rate") — cache type=66
454
+ // log10 a=0.1..10 / 0.2..20 → Hz. Default 'db' wrong.
455
+ 22: { name: 'mod_rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
456
+ 23: { name: 'rate', unit: 'hz', displayMin: 0.2, displayMax: 20 },
457
+ // ids 34/35 DELAY_FEEDLR/FEEDRL ("Rotation" / second routing) —
458
+ // c=100 a=-1 b=1 → bipolar_percent. Generator default is plain
459
+ // 'percent' which loses the sign.
460
+ 34: { name: 'rotation', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
461
+ 35: { name: 'lfo_phase', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
462
+ // ids 38/39 DELAY_PANL/PANR ("Pan L" / "Pan R") — type=48 c=100
463
+ // a=-1 b=1 → bipolar pan.
464
+ 38: { name: 'pan_l', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
465
+ 39: { name: 'pan_r', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
466
+ // ids 40/41 DELAY_LFO1PHASE/LFO2PHASE — type=54 c=57.29578 (rad→
467
+ // deg) → 0..180 degrees per radians-encoded LFO phase convention.
468
+ // Generator's c=57.29... is unrecognized; falls through to skip.
469
+ // Full override required.
470
+ 40: { name: 'modulation_phase', unit: 'degrees', displayMin: 0, displayMax: 180 },
471
+ 41: { name: 'lfo_phase_2', unit: 'degrees', displayMin: 0, displayMax: 180 },
472
+ // id=42 DELAY_SPLICETIME ("Crossfade Time") — type=52 c=1000
473
+ // a=0.001 b=0.255 → ms 1..255. Generator emits ms but displayMin
474
+ // floors to 0; override the lower bound to match cache.
475
+ 42: { name: 'crossfade_time', unit: 'ms', displayMin: 1, displayMax: 255 },
476
+ // id=56 DELAY_RATE3 ("Sweep Rate") — type=66 → Hz; default 'db' wrong.
477
+ 56: { name: 'sweep_rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
478
+ // id=58 DELAY_LFO3PHASE ("Sweep Phase") — type=54 → degrees.
479
+ 58: { name: 'sweep_phase', unit: 'degrees', displayMin: 0, displayMax: 180 },
480
+ // ids 60/61 DELAY_FSTART/FSTOP ("Sweep Start/Stop Freq") — type=66
481
+ // → Hz; default 'db' wrong.
482
+ 60: { name: 'sweep_start_freq', unit: 'hz', displayMin: 100, displayMax: 1000 },
483
+ 61: { name: 'sweep_stop_freq', unit: 'hz', displayMin: 500, displayMax: 5000 },
484
+ // id=62 DELAY_Q ("Sweep Resonance") — type=80 log10 c=10 a=0.2
485
+ // b=20 → Q-factor 0.2..20. Cache c=10 makes generator emit
486
+ // 'knob_0_10' with bounds 0..10 — wrong upper bound and misleading
487
+ // unit name. Use 'count' with the real range.
488
+ 62: { name: 'sweep_resonance', unit: 'count', displayMin: 0.2, displayMax: 20 },
489
+ // id=82 DELAY_RATE4 ("Pan Rate") — type=66 → Hz.
490
+ 82: { name: 'pan_rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
491
+ // id=85 DELAY_LFO4PHASE — type=54 → degrees. GENERATED named it
492
+ // `lfo_phase_lfo4phase` (deduplication artifact); use the cleaner
493
+ // `lfo_phase_4` since LFO1 phase already owns plain `lfo_phase`.
494
+ 85: { name: 'lfo_phase_4', unit: 'degrees', displayMin: 0, displayMax: 180 },
495
+ // TODO (no GENERATED entry, enum without value table — hand-author
496
+ // in params.ts with proper enum map):
497
+ // id=11 DELAY_TYPE enum 0..7 (delay mode variant)
498
+ // id=28 DELAY_LFO1TYPE enum 0..9 (LFO waveform: sine/tri/...)
499
+ // id=29 DELAY_LFO2TYPE enum 0..9 (LFO waveform)
500
+ // id=33 DELAY_TEMPOR enum 0..78 (TEMPO_DIVISIONS_VALUES)
501
+ // id=43 DELAY_RUN enum 0..1 (run/stop toggle)
502
+ // id=44 DELAY_MODE enum 0..1 (mode toggle)
503
+ // id=52 DELAY_LFO1TARGET enum 0..2 (LFO 1 target)
504
+ // id=53 DELAY_LFO2TARGET enum 0..2 (LFO 2 target)
505
+ // id=54 DELAY_LFO1TEMPO enum 0..78 (TEMPO_DIVISIONS_VALUES)
506
+ // id=55 DELAY_LFO2TEMPO enum 0..78 (TEMPO_DIVISIONS_VALUES)
507
+ // id=57 DELAY_LFO3TYPE enum 0..9 (LFO waveform)
508
+ // id=59 DELAY_LFO3TEMPO enum 0..78 (TEMPO_DIVISIONS_VALUES)
509
+ // id=71 DELAY_MAXDEPTH enum 0..1 (max depth toggle)
510
+ // id=81 DELAY_LFO4TYPE enum 0..9 (LFO waveform)
511
+ // id=83 DELAY_LFO4TEMPO enum 0..78 (TEMPO_DIVISIONS_VALUES)
512
+ // id=86 DELAY_LFO4TARGET enum 0..3 (LFO 4 target)
513
+ // id=89 DELAY_SVFTYPE enum 1..3 (SVF filter type)
514
+ },
515
+ // Universal `mix` at pidHigh 0x01 across every effect block that
516
+ // exposes a Mix Page per the Blocks Guide (p. 7). Skipped for
517
+ // Amp/Drive (different semantics), Wah/GEQ/Gate/VolPan (no wet/dry —
518
+ // AM4 manual p.34 line 1423: "Effects with no mix, such as Wah,
519
+ // GEQ, etc., will show 'NA'"). Cache signature matches percent
520
+ // (0..1 × 100) structurally identical to the confirmed reverb.mix.
521
+ // Modulation-block LFO controls. Blocks Guide §Chorus/Flanger/Phaser
522
+ // document "Rate (Hz/BPM): Controls the speed of the modulation" —
523
+ // all three blocks expose a rate knob with the same cache-c=1 raw-Hz
524
+ // signature. Depth is a percent knob at a distinct pidHigh per block.
525
+ chorus: {
526
+ 1: 'mix',
527
+ 2: BALANCE,
528
+ 10: 'type',
529
+ 12: { name: 'rate', unit: 'hz', displayMin: 0.1 },
530
+ 14: 'depth',
531
+ // HW-040 (Session 36, 2026-04-29): Chorus Expert-Edit page on
532
+ // Analog Stereo from session-40-chorus-expert.pcapng. Cache shapes
533
+ // pin units; screenshot labels confirm names.
534
+ 11: { name: 'number_of_voices', unit: 'count', displayMin: 1, displayMax: 4 },
535
+ 15: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
536
+ 21: { name: 'lfo_phase_pct', unit: 'percent', displayMin: 0, displayMax: 100 },
537
+ 22: { name: 'lfo_rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
538
+ 23: { name: 'width', unit: 'percent', displayMin: 0, displayMax: 100 },
539
+ 24: { name: 'drive', unit: 'knob_0_10', displayMin: 0.5, displayMax: 500 },
540
+ 25: { name: 'lfo_freq', unit: 'hz', displayMin: 20, displayMax: 2000 },
541
+ 26: { name: 'lfo_depth_2', unit: 'bipolar_percent', displayMin: -200, displayMax: 200 },
542
+ },
543
+ // geq Expert-Edit additions are merged into the existing geq:
544
+ // entry above (lines ~299) — duplicate removed in Session 36
545
+ // cleanup. The 10 GEQ bands + master_q now live there.
546
+ flanger: {
547
+ 1: 'mix',
548
+ 2: BALANCE,
549
+ 10: 'type',
550
+ 11: { name: 'rate', unit: 'hz', displayMin: 0.05 },
551
+ 13: 'depth',
552
+ // Session 29 (HW-015): Feedback at pidHigh=0x0e. Cache (a=-0.995,
553
+ // b=0.995, c=100) — bipolar_percent with the internal range
554
+ // clamped slightly short of ±1.0 per Fractal's flanger
555
+ // implementation.
556
+ 14: { name: 'feedback', unit: 'bipolar_percent', displayMin: -99, displayMax: 99 },
557
+ // Session 90 cont (2026-05-17): FLANGER unit overrides to correct
558
+ // the cache pipeline's c=1 → 'db' default for entries the catalog
559
+ // says are Hz / count / ms / knob. Names defer to GENERATED_PARAM_NAMES
560
+ // for ids the resolver covered (19/23/25/30/35) — only unit fixes
561
+ // emitted here. Source: paramNamesGenerated.ts + cache-section3.json
562
+ // ranges.
563
+ 19: { name: 'smooth_steps', unit: 'count', displayMin: 0.5, displayMax: 50 },
564
+ 23: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
565
+ 25: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
566
+ 35: { name: 'vpo_exponent', unit: 'count', displayMin: 0.01, displayMax: 100 },
567
+ },
568
+ phaser: {
569
+ 1: 'mix',
570
+ 2: BALANCE,
571
+ 10: 'type',
572
+ 12: { name: 'rate', unit: 'hz', displayMin: 0.1 },
573
+ // Session 29 (HW-015): Feedback at pidHigh=0x10. Cache (a=-0.9,
574
+ // b=0.9, c=111.1) — bipolar, internal ±0.9 with an unusual
575
+ // display-scale of 111.1 meaning internal -0.9 displays as
576
+ // -99.99%. We use the standard bipolar_percent unit (scale 100)
577
+ // with displayMin/Max clamped to ±90 so input stays within the
578
+ // internal range; the displayed percentage in AM4-Edit may read
579
+ // slightly higher than the value Claude used (e.g. "50" sets
580
+ // internal 0.5, AM4-Edit displays ~55.5%) but the wire behavior
581
+ // is correct. Natural-language UX impact is negligible.
582
+ 16: { name: 'feedback', unit: 'bipolar_percent', displayMin: -90, displayMax: 90 },
583
+ // Session 90 cont (2026-05-17): PHASER unit overrides. Same
584
+ // cache-c=1 → 'db' default correction as flanger above. Names
585
+ // defer to GENERATED_PARAM_NAMES.
586
+ 17: { name: 'min_frequency', unit: 'hz', displayMin: 5, displayMax: 500 },
587
+ 18: { name: 'max_frequency', unit: 'hz', displayMin: 200, displayMax: 20000 },
588
+ 20: { name: 'bias', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
589
+ 22: { name: 'feedback_point', unit: 'count', displayMin: 0, displayMax: 11 },
590
+ 25: { name: 'q', unit: 'count', displayMin: 0.1, displayMax: 10 },
591
+ 29: { name: 'shape_vcrk', unit: 'count', displayMin: 0.1, displayMax: 10 },
592
+ 30: { name: 'shape', unit: 'count', displayMin: 0.01, displayMax: 0.99 },
593
+ // id=31 high_cut: cache stores in kHz (0.5..50). 'count' until
594
+ // confirmed via capture — Hz unit would mislabel the display value.
595
+ 31: { name: 'high_cut', unit: 'count', displayMin: 0.5, displayMax: 50 },
596
+ 35: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 200 },
597
+ 36: { name: 'high_cut_lpf', unit: 'hz', displayMin: 2000, displayMax: 20000 },
598
+ },
599
+ wah: {
600
+ 1: 'mix',
601
+ 2: BALANCE,
602
+ 10: 'type',
603
+ // HW-040 (Session 36, 2026-04-29): Wah Expert-Edit page on FAS Wah
604
+ // from session-40-wah-expert.pcapng. Cache shapes pin units;
605
+ // screenshot labels confirm names. **BK-035 audit (Session 36 cont,
606
+ // 2026-04-29):** the original auto-generated names for ids 13..20
607
+ // were misaligned vs the AM4-Edit screenshot. Each label below was
608
+ // re-derived from the value-matched audit table run via
609
+ // `scripts/audit-block-vs-screenshot.ts` against
610
+ // `docs/audit-input/wah.json`. Old → new mapping:
611
+ // 13 (was `q`, range 2..20) → `q_resonance`, range 0..10
612
+ // 14 (was `q_resonance`) → `q_tracking`
613
+ // 15 (was `q_tracking`) → `wah_control`
614
+ // 16 (was `drive`) → `fat`
615
+ // 17 (was `fat`) → `drive`
616
+ // 18 (was unregistered) → `control_taper` (enum, hand-authored in params.ts)
617
+ // 19 (was `low_cut_frequency`) → `inductor_bias`
618
+ // 20 (was `inductor_bias`) → `low_cut_frequency`
619
+ 11: { name: 'min_frequency', unit: 'hz', displayMin: 100, displayMax: 1000 },
620
+ 12: { name: 'max_frequency', unit: 'hz', displayMin: 500, displayMax: 5000 },
621
+ 13: { name: 'q_resonance', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
622
+ 14: { name: 'q_tracking', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
623
+ 15: { name: 'wah_control', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
624
+ 16: { name: 'fat', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
625
+ 17: { name: 'drive', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
626
+ 19: { name: 'inductor_bias', unit: 'knob_0_10', displayMin: 0, displayMax: 10 },
627
+ 20: { name: 'low_cut_frequency', unit: 'hz', displayMin: 20, displayMax: 2000 },
628
+ 22: { name: 'graphic_eq_band_1', unit: 'db', displayMin: -12, displayMax: 12 },
629
+ 23: { name: 'graphic_eq_band_2', unit: 'db', displayMin: -12, displayMax: 12 },
630
+ 24: { name: 'graphic_eq_band_3', unit: 'db', displayMin: -12, displayMax: 12 },
631
+ 25: { name: 'graphic_eq_band_4', unit: 'db', displayMin: -12, displayMax: 12 },
632
+ 26: { name: 'graphic_eq_band_5', unit: 'db', displayMin: -12, displayMax: 12 },
633
+ 27: { name: 'graphic_eq_band_6', unit: 'db', displayMin: -12, displayMax: 12 },
634
+ 28: { name: 'graphic_eq_band_7', unit: 'db', displayMin: -12, displayMax: 12 },
635
+ 29: { name: 'graphic_eq_band_8', unit: 'db', displayMin: -12, displayMax: 12 },
636
+ },
637
+ // HW-040 (Session 36, 2026-04-29): NEW BLOCKS — PEQ (parametric EQ,
638
+ // pidLow=0x0036, S2 cacheBlock=4) and Rotary (pidLow=0x0056, S3
639
+ // cacheBlock=4). Neither has a Type enum at id=10. Captures from
640
+ // session-40-{peq,rot}-expert.pcapng.
641
+ peq: {
642
+ 1: 'mix',
643
+ 2: BALANCE,
644
+ // 5 channels of parametric EQ, each with Type / Frequency / Q /
645
+ // Gain / Solo. Cache lays them out in groups: ids 10-14 are the
646
+ // 5 frequencies (Hz, varying ranges), 15-19 are the 5 Q values
647
+ // (count 0.1..10), 20-24 are the 5 gains (dB ±20).
648
+ 10: { name: 'channel_1_frequency', unit: 'hz', displayMin: 20, displayMax: 2000 },
649
+ 11: { name: 'channel_2_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
650
+ 12: { name: 'channel_3_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
651
+ 13: { name: 'channel_4_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
652
+ 14: { name: 'channel_5_frequency', unit: 'hz', displayMin: 200, displayMax: 20000 },
653
+ 15: { name: 'channel_1_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
654
+ 16: { name: 'channel_2_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
655
+ 17: { name: 'channel_3_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
656
+ 18: { name: 'channel_4_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
657
+ 19: { name: 'channel_5_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
658
+ 20: { name: 'channel_1_gain', unit: 'db', displayMin: -20, displayMax: 20 },
659
+ 21: { name: 'channel_2_gain', unit: 'db', displayMin: -20, displayMax: 20 },
660
+ 22: { name: 'channel_3_gain', unit: 'db', displayMin: -20, displayMax: 20 },
661
+ 23: { name: 'channel_4_gain', unit: 'db', displayMin: -20, displayMax: 20 },
662
+ 24: { name: 'channel_5_gain', unit: 'db', displayMin: -20, displayMax: 20 },
663
+ },
664
+ rotary: {
665
+ 1: 'mix',
666
+ 2: BALANCE,
667
+ // FAS Rotary cabinet sim. Cache layout (cacheBlock=4 in S3).
668
+ // **BK-035 audit (Session 36 cont, 2026-04-29):** initial cache-driven
669
+ // names had two pidHigh swaps vs the AM4-Edit screenshot. Re-derived
670
+ // via `scripts/audit-block-vs-screenshot.ts` against
671
+ // `docs/audit-input/rotary.json`:
672
+ // id 10 (was `drive`) → `rate` (Leslie speed knob; cache
673
+ // range 0..10 ×1 → display 0..10 Hz)
674
+ // id 21 (was `mic_spacing`) → `drive` (cache range 0.5..500 ×10)
675
+ // id 16 (was unregistered) → `mic_spacing` (cache π-encoded:
676
+ // range 0..π × 31.831 → display 0..100)
677
+ // Plus 5 new entries founder confirmed from screenshot:
678
+ // id 0 → `level` (db); id 4 → `bypass_mode` (enum, hand-authored
679
+ // in params.ts); id 14 → `tempo` (TEMPO_DIVISIONS, hand-authored);
680
+ // id 20 → `stereo_spread` (bipolar -200..200%); id 23 →
681
+ // `input_select` (enum [L+R, LEFT, RIGHT], hand-authored).
682
+ 10: { name: 'rate', unit: 'hz', displayMin: 0, displayMax: 10 },
683
+ 11: { name: 'low_depth', unit: 'percent', displayMin: 0, displayMax: 100 },
684
+ 12: { name: 'high_depth', unit: 'percent', displayMin: 0, displayMax: 100 },
685
+ 13: { name: 'high_level', unit: 'db', displayMin: -6, displayMax: 6 },
686
+ 15: { name: 'rotor_length', unit: 'percent', displayMin: 0.1, displayMax: 100 },
687
+ 16: { name: 'mic_spacing', unit: 'rotary_mic_spacing', displayMin: 0, displayMax: 100 },
688
+ 17: { name: 'low_rate_multiplier', unit: 'count', displayMin: 0.1, displayMax: 10 },
689
+ 18: { name: 'low_time_constant', unit: 'count', displayMin: 0.1, displayMax: 10 },
690
+ 19: { name: 'high_time_constant', unit: 'count', displayMin: 0.1, displayMax: 10 },
691
+ 20: { name: 'stereo_spread', unit: 'bipolar_percent', displayMin: -200, displayMax: 200 },
692
+ 21: { name: 'drive', unit: 'knob_0_10', displayMin: 0.5, displayMax: 500 },
693
+ 22: { name: 'mic_distance', unit: 'count', displayMin: 0.01, displayMax: 1 },
694
+ },
695
+ compressor: {
696
+ 1: 'mix',
697
+ 2: BALANCE,
698
+ // HW-021 (Session 30, 2026-04-25): Compressor first-page knobs from
699
+ // session-30-comp-basic-jfet-studio. Cache ids 10..15 are the
700
+ // canonical comp-config registers per Blocks Guide §Compressor:
701
+ // Threshold (dB), Ratio (1..20:1, new `ratio` unit), Attack (ms),
702
+ // Release (ms), Knee Type enum (id 14, not yet wiggled), Auto
703
+ // Makeup OFF/ON (id 15, hand-authored in params.ts because per-
704
+ // block non-Type enums skip the generator). compressor.level
705
+ // (pidHigh=0x0000) is out-of-band hand-authored.
706
+ 10: { name: 'threshold', unit: 'db', displayMin: -60, displayMax: 20 },
707
+ 12: { name: 'attack', unit: 'ms', displayMin: 0.1, displayMax: 100 },
708
+ 13: { name: 'release', unit: 'ms', displayMin: 2, displayMax: 2000 },
709
+ 19: 'type',
710
+ // Ratio uses the new `ratio` unit (display = internal, scale 1) so
711
+ // Claude reads "ratio 4" as 4:1 not 4 dB. Cache c=1 default would
712
+ // mis-classify as dB; full override required.
713
+ 11: { name: 'ratio', unit: 'ratio', displayMin: 1, displayMax: 20 },
714
+ // HW-028 + HW-039 (Session 35, 2026-04-29): JFET Studio Compressor
715
+ // Expert-Edit page exposes a Sidechain section + a Drive-engine
716
+ // emphasis knob. Decoded from session-31-comp-jfet-expert.pcapng
717
+ // + paired AM4-Edit screenshot. Cache shapes pin units; screenshot
718
+ // labels confirm the names. Wire-vs-screenshot value mismatches on
719
+ // Ratio (wire 2.22 / shot 3.000) and Look-Ahead (wire 4.33 ms / shot
720
+ // 2.000 ms) — founder noted screenshot was for label confirmation,
721
+ // not exact final-value sync; cache shapes + label position keep
722
+ // registration unambiguous. Closes HW-028 (0x0017 = emphasis at
723
+ // cache c=20 fine knob 0..20; 0x0029 = drive at cache c=10
724
+ // knob_0_10).
725
+ 17: { name: 'sidechain_low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
726
+ 21: { name: 'look_ahead_time', unit: 'ms', displayMin: 0, displayMax: 2 },
727
+ 23: { name: 'emphasis', unit: 'knob_0_20', displayMin: 0, displayMax: 20 },
728
+ 26: { name: 'sidechain_high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
729
+ 27: { name: 'sidechain_gain', unit: 'db', displayMin: -12, displayMax: 12 },
730
+ 28: { name: 'sidechain_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
731
+ // Q is a fractional 0.1..10 quality factor — `count` here is
732
+ // structural (display = wire passthrough), not integer-only.
733
+ 29: { name: 'sidechain_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
734
+ 39: { name: 'sidechain_emphasis_freq', unit: 'hz', displayMin: 100, displayMax: 10000 },
735
+ 41: 'drive',
736
+ // Session 90 cont (2026-05-17): COMP ratio_compansion at id=36 is a
737
+ // ratio (1..10:1), not dB. Cache-c=1 default to 'db' was wrong.
738
+ 36: { name: 'ratio_compansion', unit: 'ratio', displayMin: 1, displayMax: 10 },
739
+ },
740
+ geq: {
741
+ 1: 'mix',
742
+ 2: BALANCE,
743
+ 20: 'type',
744
+ // HW-040 (Session 36, 2026-04-29): GEQ Expert-Edit page on
745
+ // 10 Band Variable Q from session-40-geq-expert.pcapng. 10 bands
746
+ // (cache ids 10-19), all bipolar dB ±12, plus Master Q (id 21).
747
+ // **BK-035 audit (Session 36 cont, 2026-04-29):** added Level
748
+ // (hand-authored in params.ts because pidHigh=0x0000 has no cache
749
+ // record at id=0 across blocks) and Bypass Mode (hand-authored enum).
750
+ 10: { name: 'band_1', unit: 'db', displayMin: -12, displayMax: 12 },
751
+ 11: { name: 'band_2', unit: 'db', displayMin: -12, displayMax: 12 },
752
+ 12: { name: 'band_3', unit: 'db', displayMin: -12, displayMax: 12 },
753
+ 13: { name: 'band_4', unit: 'db', displayMin: -12, displayMax: 12 },
754
+ 14: { name: 'band_5', unit: 'db', displayMin: -12, displayMax: 12 },
755
+ 15: { name: 'band_6', unit: 'db', displayMin: -12, displayMax: 12 },
756
+ 16: { name: 'band_7', unit: 'db', displayMin: -12, displayMax: 12 },
757
+ 17: { name: 'band_8', unit: 'db', displayMin: -12, displayMax: 12 },
758
+ 18: { name: 'band_9', unit: 'db', displayMin: -12, displayMax: 12 },
759
+ 19: { name: 'band_10', unit: 'db', displayMin: -12, displayMax: 12 },
760
+ 21: { name: 'master_q', unit: 'count', displayMin: 0.1, displayMax: 10 },
761
+ },
762
+ filter: {
763
+ 1: 'mix',
764
+ 2: BALANCE,
765
+ 10: 'type',
766
+ // Blocks Guide §Filter: Frequency is the filter cutoff (20..20000 Hz
767
+ // at cache-c=1 raw). Universal control for every filter type.
768
+ 11: { name: 'freq', unit: 'hz' },
769
+ // HW-032 (Session 30 cont 8, 2026-04-25): Low/High cut on the
770
+ // filter Config page — `session-32-filter-extended.pcapng`. Cache
771
+ // c=1 raw Hz; needs the 'hz' override since the generator default
772
+ // for c=1 is 'db'. Wire-verified at 100 Hz / 1800 Hz.
773
+ 18: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
774
+ 19: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
775
+ // HW-034 (Session 33, 2026-04-26): All-Pass filter Config-page
776
+ // residuals — `session-33-filter-extended.pcapng`. Wire-verified
777
+ // at +13% / 4 poles on an All-Pass filter. Feedback's cache
778
+ // signature (a=-1, b=1, c=100) requires the bipolar_percent
779
+ // override since the generator default for c=100 is plain
780
+ // percent. Order is a raw integer (cache c=1 a=1 b=12) — needs
781
+ // 'count' override since c=1 default is 'db'.
782
+ 21: { name: 'feedback', unit: 'bipolar_percent', displayMin: -100, displayMax: 100 },
783
+ 28: { name: 'order', unit: 'count', displayMin: 1, displayMax: 12 },
784
+ // HW-053 (2026-05-04): cache id=33 (FILTER_SENS, "Sensitivity") has
785
+ // a=0.1, b=40, c=10, typecode=80 (log10). The generator's c=10
786
+ // default forces displayMin=0, displayMax=10 (knob_0_10). With
787
+ // displayMin=0, the runtime decode falls back to LINEAR even when
788
+ // typecode 80 wants log10 — yielding the inverted-taper bug HW-053
789
+ // observed (write 7 → display 3.25). Override with a positive
790
+ // displayMin so log10 fires correctly. Only applies when
791
+ // filter.type is Envelope Filter / Auto-Wah / Touch-Wah (per
792
+ // type-applicability).
793
+ 33: { name: 'sensitivity', unit: 'count', displayMin: 0.1, displayMax: 40 },
794
+ // Session 90 cont (2026-05-17): FILTER unit overrides. Same
795
+ // cache-c=1 → 'db' correction. id=33 sensitivity already overridden
796
+ // above per HW-053.
797
+ 12: { name: 'q', unit: 'count', displayMin: 0.1, displayMax: 10 },
798
+ 24: { name: 'rate', unit: 'hz', displayMin: 0.1, displayMax: 10 },
799
+ 26: { name: 'mod_frequency', unit: 'hz', displayMin: 20, displayMax: 20000 },
800
+ 31: { name: 'start_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
801
+ 32: { name: 'stop_frequency', unit: 'hz', displayMin: 100, displayMax: 10000 },
802
+ },
803
+ tremolo: {
804
+ 1: 'mix',
805
+ 2: BALANCE,
806
+ 10: 'type',
807
+ // Blocks Guide §Tremolo: Rate sets the modulation speed (0.2..20 Hz
808
+ // at cache-c=1 raw). Depth is a percent knob.
809
+ 12: { name: 'rate', unit: 'hz', displayMin: 0.2 },
810
+ 13: 'depth',
811
+ // Session 90 cont (2026-05-17): TREMOLO unit override — crossover
812
+ // freq is Hz not dB (cache-c=1 default).
813
+ 21: { name: 'crossover_freq', unit: 'hz', displayMin: 200, displayMax: 2000 },
814
+ },
815
+ enhancer: {
816
+ 1: 'mix',
817
+ 2: BALANCE,
818
+ // HW-037 (Session 35, 2026-04-29): Config-page knobs from
819
+ // session-33-enhancer-extended.pcapng + paired screenshot. Wire-
820
+ // verified at width=33% / depth=11% / low_cut=22.2 Hz /
821
+ // high_cut=6500 Hz on a Modern enhancer. Width + Depth follow the
822
+ // generator's c=100 → percent default; Low/High Cut need the 'hz'
823
+ // override since cache c=1 default is dB. enhancer.level is out-of-
824
+ // band hand-authored in params.ts (pidHigh=0x0000, no cache record).
825
+ 10: 'width',
826
+ 11: 'depth',
827
+ 12: { name: 'low_cut', unit: 'hz', displayMin: 20, displayMax: 2000 },
828
+ 13: { name: 'high_cut', unit: 'hz', displayMin: 200, displayMax: 20000 },
829
+ // AM4-Edit labels this "Mode" but we keep `type` for cross-block consistency.
830
+ 14: 'type',
831
+ },
832
+ gate: {
833
+ 2: BALANCE,
834
+ // HW-035 (Session 34, 2026-04-26): slot-Gate Config-page knobs on
835
+ // Modern Gate type — `session-34-slotgate-extended.pcapng`.
836
+ // Threshold/Attack/Hold/Release/Attenuation are dB and ms knobs
837
+ // with cache c=1 (raw dB, signed) and c=1000 (ms) signatures
838
+ // respectively. Sidechain enum (cache id=15) is hand-authored
839
+ // in params.ts since the generator only handles one enum import
840
+ // per block (used for `type` at id=19).
841
+ 10: { name: 'threshold', unit: 'db', displayMin: -100, displayMax: 0 },
842
+ 11: { name: 'attack', unit: 'ms', displayMin: 0, displayMax: 1000 },
843
+ 12: { name: 'hold', unit: 'ms', displayMin: 0, displayMax: 1000 },
844
+ 13: { name: 'release', unit: 'ms', displayMin: 0, displayMax: 1000 },
845
+ 19: 'type',
846
+ 20: { name: 'attenuation', unit: 'db', displayMin: -80, displayMax: 0 },
847
+ },
848
+ volpan: {
849
+ 2: BALANCE,
850
+ // The Volume-vs-Auto-Swell selector. Registered as `volpan.mode` in
851
+ // KNOWN_PARAMS for historical reasons — keep the name stable.
852
+ 15: 'mode',
853
+ // HW-032 (Session 30 cont 8, 2026-04-25): Auto-Swell envelope params
854
+ // on the Volume/Pan Config page — `session-32-volpan-extended.pcapng`.
855
+ // Threshold (id=16, dB) wire-verified at -20 dB; Attack (id=17, ms)
856
+ // wire-verified at 300 ms. Cache c=1 for threshold (raw dB, needs
857
+ // 'db' override since generator default is also 'db' but we set the
858
+ // range explicitly). Cache c=1000 for attack means generator picks
859
+ // 'ms' automatically — no override needed except the display range.
860
+ 16: { name: 'threshold', unit: 'db', displayMin: -100, displayMax: 0 },
861
+ 17: { name: 'attack', unit: 'ms', displayMin: 1, displayMax: 5000 },
862
+ },
863
+ };