@audiofab-io/fv1-core 0.6.1 → 0.6.2

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 (59) hide show
  1. package/blocks/ATL_DEVELOPER_REFERENCE.md +156 -156
  2. package/blocks/control/constant.atl +36 -36
  3. package/blocks/control/entropy_lfo.atl +74 -74
  4. package/blocks/control/envelope.atl +120 -120
  5. package/blocks/control/invert.atl +33 -33
  6. package/blocks/control/pot.atl +149 -149
  7. package/blocks/control/power.atl +76 -76
  8. package/blocks/control/ramp_lfo.atl +122 -122
  9. package/blocks/control/scale_offset.atl +84 -84
  10. package/blocks/control/sincos_lfo.atl +126 -126
  11. package/blocks/control/smoother.atl +48 -48
  12. package/blocks/control/tremolizer.atl +54 -54
  13. package/blocks/effects/delay/micro_stutter.atl +77 -77
  14. package/blocks/effects/delay/mn3011.atl +280 -280
  15. package/blocks/effects/delay/simple_delay.atl +96 -96
  16. package/blocks/effects/delay/triple_tap_delay.atl +176 -176
  17. package/blocks/effects/lo-fi/bit_mangler.atl +74 -74
  18. package/blocks/effects/lo-fi/chiptune.atl +311 -311
  19. package/blocks/effects/lo-fi/tape_degrade.atl +181 -181
  20. package/blocks/effects/modulation/chorus.atl +141 -141
  21. package/blocks/effects/modulation/chorus_4voice.atl +188 -188
  22. package/blocks/effects/modulation/flanger.atl +184 -184
  23. package/blocks/effects/modulation/harmonic_trem.atl +129 -129
  24. package/blocks/effects/modulation/phaser.atl +299 -299
  25. package/blocks/effects/pitch/octave_up_down.atl +80 -80
  26. package/blocks/effects/pitch/pitch_offset.atl +149 -149
  27. package/blocks/effects/pitch/pitch_offset_dual.atl +197 -197
  28. package/blocks/effects/pitch/pitch_shift.atl +115 -115
  29. package/blocks/effects/pitch/sub_octave.atl +100 -100
  30. package/blocks/effects/reverb/ducking_reverb.atl +145 -145
  31. package/blocks/effects/reverb/min_reverb.atl +132 -132
  32. package/blocks/effects/reverb/plate_reverb.atl +344 -344
  33. package/blocks/effects/reverb/room_reverb.atl +293 -293
  34. package/blocks/effects/reverb/smear.atl +90 -90
  35. package/blocks/effects/reverb/spring_reverb.atl +353 -353
  36. package/blocks/filter/1p_high_pass.atl +62 -62
  37. package/blocks/filter/1p_low_pass.atl +58 -58
  38. package/blocks/filter/auto_wah.atl +207 -207
  39. package/blocks/filter/bbd_loss.atl +79 -79
  40. package/blocks/filter/shelving_high_pass.atl +76 -76
  41. package/blocks/filter/shelving_low_pass.atl +76 -76
  42. package/blocks/filter/svf_2p.atl +116 -116
  43. package/blocks/gain_mix/crossfade.atl +93 -93
  44. package/blocks/gain_mix/crossfade2.atl +86 -86
  45. package/blocks/gain_mix/crossfade3.atl +71 -71
  46. package/blocks/gain_mix/gainboost.atl +54 -54
  47. package/blocks/gain_mix/mixer2.atl +76 -76
  48. package/blocks/gain_mix/mixer3.atl +109 -109
  49. package/blocks/gain_mix/mixer4.atl +151 -151
  50. package/blocks/gain_mix/volume.atl +50 -50
  51. package/blocks/io/adc.atl +53 -53
  52. package/blocks/io/dac.atl +61 -61
  53. package/blocks/other/stickynote.atl +24 -24
  54. package/blocks/other/tone_gen_adjustable.atl +137 -137
  55. package/blocks/other/tone_gen_fixed.atl +109 -109
  56. package/dist/blockDiagram/builtinBlocks.js +107 -107
  57. package/dist/blockDiagram/builtinBlocks.js.map +1 -1
  58. package/dist/simulator/FV1Simulator.js +1 -1
  59. package/package.json +1 -1
@@ -1,311 +1,311 @@
1
- ---
2
- {
3
- "type": "effects.novelty.chiptune",
4
- "name": "Chip Tune",
5
- "category": "Effects",
6
- "subcategory": "Lo-Fi",
7
- "description": "Commodore 64 SID chip emulator: Transforms your guitar into 8-bit chiptune glory with bit crushing, square-wave shaping, ring modulation, white noise, and the legendary SID resonant filter.",
8
- "color": "#4466BB",
9
- "width": 180,
10
- "inputs": [
11
- {
12
- "id": "in",
13
- "name": "Input",
14
- "type": "audio",
15
- "required": true
16
- },
17
- {
18
- "id": "crushCV",
19
- "name": "Crush",
20
- "type": "control",
21
- "required": false,
22
- "parameter": "crush"
23
- },
24
- {
25
- "id": "squareCV",
26
- "name": "Square",
27
- "type": "control",
28
- "required": false,
29
- "parameter": "square"
30
- },
31
- {
32
- "id": "cutoffCV",
33
- "name": "Cutoff",
34
- "type": "control",
35
- "required": false,
36
- "parameter": "cutoff"
37
- },
38
- {
39
- "id": "resoCV",
40
- "name": "Resonance",
41
- "type": "control",
42
- "required": false,
43
- "parameter": "resonance"
44
- }
45
- ],
46
- "outputs": [
47
- {
48
- "id": "out",
49
- "name": "Output",
50
- "type": "audio"
51
- }
52
- ],
53
- "parameters": [
54
- {
55
- "id": "crush",
56
- "name": "Crush Depth",
57
- "type": "number",
58
- "default": 0.2,
59
- "min": 0.005,
60
- "max": 0.25,
61
- "step": 0.005,
62
- "description": "Sample-rate reduction intensity. Higher = more lo-fi 8-bit crunch."
63
- },
64
- {
65
- "id": "square",
66
- "name": "Square Drive",
67
- "type": "number",
68
- "default": 0.5,
69
- "min": 0.0,
70
- "max": 1.99,
71
- "step": 0.01,
72
- "description": "Blend from clean (0) to hard-clipped square pulse waveform (max)."
73
- },
74
- {
75
- "id": "ring_freq",
76
- "name": "Ring Mod",
77
- "type": "number",
78
- "default": 0.0,
79
- "min": 0.0,
80
- "max": 1.0,
81
- "step": 0.01,
82
- "description": "Ring modulator frequency. 0 = off. Adds metallic SID harmonics."
83
- },
84
- {
85
- "id": "noise",
86
- "name": "Noise Level",
87
- "type": "number",
88
- "default": 0.5,
89
- "min": 0.0,
90
- "max": 1.0,
91
- "step": 0.01,
92
- "description": "White noise generator level. Classic SID noise channel for hi-hats, snares, and static textures."
93
- },
94
- {
95
- "id": "cutoff",
96
- "name": "Filter Cutoff",
97
- "type": "number",
98
- "default": 3000,
99
- "min": 60,
100
- "max": 6000,
101
- "step": 10,
102
- "conversion": "SVFFREQ",
103
- "description": "SID-style resonant low-pass filter cutoff frequency."
104
- },
105
- {
106
- "id": "resonance",
107
- "name": "Filter Resonance",
108
- "type": "number",
109
- "default": 4.0,
110
- "min": 0.5,
111
- "max": 20.0,
112
- "step": 0.1,
113
- "conversion": "SVF_DAMP",
114
- "description": "Filter Q. Crank it for that classic SID filter sweep character."
115
- },
116
- {
117
- "id": "mix",
118
- "name": "Dry/Wet Mix",
119
- "type": "number",
120
- "default": 1.0,
121
- "min": 0.0,
122
- "max": 1.0,
123
- "step": 0.01,
124
- "description": "Blend between dry and SID-processed signal."
125
- }
126
- ],
127
- "registers": [
128
- "counter",
129
- "held",
130
- "squared",
131
- "ring_sin",
132
- "ring_cos",
133
- "f_ring",
134
- "ringmod",
135
- "noise_reg",
136
- "noise_dc",
137
- "lp_state",
138
- "bp_state",
139
- "damp_reg",
140
- "dry",
141
- "wet",
142
- "mix_val"
143
- ]
144
- }
145
- ---
146
- @section header
147
- equ damp_target (${param.resonance})
148
-
149
- @section init
150
- ; Seed the ring mod oscillator (cos=0.5, sin=0)
151
- ; Without this, the magic circle stays at zero forever!
152
- sof 0.0, 0.5
153
- wrax ${reg.ring_cos}, 0.0
154
-
155
- ; Seed the noise LFSR with a non-zero, non-one starting value
156
- sof 0.0, 0.3
157
- wrax ${reg.noise_reg}, 0.0
158
-
159
- @section main
160
- @if pinConnected(in)
161
-
162
- ; ======================================================
163
- ; Stage 0: Save dry signal for mix
164
- ; ======================================================
165
- rdax ${input.in}, 1.0
166
- wrax ${reg.dry}, 0.0
167
-
168
- ; ======================================================
169
- ; Stage 1: Sample-Rate Reduction (SID S&H Bit Crush)
170
- ; ======================================================
171
- skp run, ${local.SKIP_INIT}
172
- rdax ${input.in}, 1.0
173
- wrax ${reg.held}, 0.0
174
- ${local.SKIP_INIT}:
175
-
176
- ; Counter increment: inverted crush so higher = more crushed
177
- ; step = 0.255 - crush_value (smaller step = slower S&H = more lo-fi)
178
- rdax ${reg.counter}, 1.0
179
- wrax ${reg.counter}, 0.0
180
- @cv crushCV ; ACC = crush depth (0.005..0.25)
181
- sof -1.0, 0.255 ; ACC = 0.255 - crush = step size
182
- rdax ${reg.counter}, 1.0 ; ACC = counter + step
183
- wrax ${reg.counter}, 1.0
184
-
185
- sof 1.0, -0.99
186
- skp neg, ${local.SKIP_SAMPLE}
187
-
188
- clr
189
- rdax ${input.in}, 1.0
190
- wrax ${reg.held}, 0.0
191
- sof 0.0, 0.0
192
- wrax ${reg.counter}, 0.0
193
-
194
- ${local.SKIP_SAMPLE}:
195
-
196
- ; ======================================================
197
- ; Stage 2: Square Wave Shaping (SID Pulse Oscillator)
198
- ; ======================================================
199
- clr
200
- rdax ${reg.held}, 1.0
201
- sof 1.99, 0.0
202
- sof -1.99, 0.0
203
- sof -1.99, 0.0
204
- sof 0.5, 0.0
205
- wrax ${reg.squared}, 0.0
206
-
207
- ; Crossfade: out = held + (squared - held) * squareDrive
208
- rdax ${reg.squared}, 1.0
209
- rdax ${reg.held}, -1.0
210
- wrax ${reg.mix_val}, 0.0 ; Save (squared - held), clear ACC
211
- @cv squareCV ; ACC = square drive value
212
- mulx ${reg.mix_val} ; ACC = (squared - held) * drive
213
- rdax ${reg.held}, 1.0 ; ACC = held + (squared - held) * drive
214
- wrax ${reg.squared}, 0.0
215
-
216
- ; ======================================================
217
- ; Stage 3: Ring Modulation (SID Ring Mod)
218
- ; ======================================================
219
- sof 0.0, ${param.ring_freq}
220
- sof 0.15, 0.0
221
- wrax ${reg.f_ring}, 0.0
222
-
223
- ; Magic circle sine oscillator (seeded in init!)
224
- rdax ${reg.ring_sin}, -1.0
225
- mulx ${reg.f_ring}
226
- rdax ${reg.ring_cos}, 1.0
227
- wrax ${reg.ring_cos}, 1.0
228
- mulx ${reg.f_ring}
229
- rdax ${reg.ring_sin}, 1.0
230
- wrax ${reg.ring_sin}, 0.0
231
-
232
- ; Ring mod = squared * carrier
233
- rdax ${reg.squared}, 1.0
234
- mulx ${reg.ring_sin}
235
- sof 1.99, 0.0
236
- wrax ${reg.ringmod}, 0.0
237
-
238
- ; Crossfade by ring_freq: 0 = all squared, 1 = all ring
239
- sof 0.0, ${param.ring_freq}
240
- wrax ${reg.mix_val}, 0.0
241
- rdax ${reg.ringmod}, 1.0
242
- rdax ${reg.squared}, -1.0
243
- mulx ${reg.mix_val}
244
- rdax ${reg.squared}, 1.0
245
- wrax ${reg.wet}, 0.0
246
-
247
- ; ======================================================
248
- ; Stage 3.5: SID Noise Channel (Tent Map + DC Blocker)
249
- ; ======================================================
250
- ; Tent map: x_next ≈ 1 - 2|x| (chaotic noise source)
251
- ; FV-1 can't represent exact coefficients 2/1, so the raw
252
- ; output has a slight positive mean. A DC-blocking LP filter
253
- ; tracks and removes it for guaranteed zero-mean noise.
254
- rdax ${reg.noise_reg}, 1.0 ; ACC = x
255
- absa ; ACC = |x|
256
- sof -1.99, 0.999 ; ACC ≈ 1 - 2|x| — tent map
257
- wrax ${reg.noise_reg}, 0.0 ; Save x_next, ACC = 0
258
-
259
- ; DC blocker: track running average with ultra-slow LP
260
- rdax ${reg.noise_dc}, 1.0 ; ACC = old DC estimate
261
- rdfx ${reg.noise_reg}, 0.001 ; ACC = DC_old*0.999 + noise*0.001
262
- wrax ${reg.noise_dc}, -1.0 ; Save new DC estimate, ACC = -DC
263
- rdax ${reg.noise_reg}, 1.0 ; ACC = noise - DC = zero-mean noise
264
-
265
- ; Scale by noise level and add to wet signal
266
- ; Noise needs ~12dB pre-boost because the SID filter downstream
267
- ; heavily attenuates high-frequency content where most noise energy lives
268
- sof 1.99, 0.0
269
- sof 1.99, 0.0
270
- sof ${param.noise}, 0.0
271
- rdax ${reg.wet}, 1.0
272
- wrax ${reg.wet}, 0.0
273
-
274
- ; ======================================================
275
- ; Stage 4: SID Filter (Resonant SVF Low-Pass)
276
- ; ======================================================
277
- ; Load damping coefficient (resonance CV or fixed parameter)
278
- @cv resoCV
279
- wrax ${reg.damp_reg}, 0.0
280
-
281
- ; Load cutoff coefficient
282
- @cv cutoffCV
283
- wrax ${reg.mix_val}, 0.0
284
-
285
- ; Chamberlin SVF: HP → BP → LP
286
- rdax ${reg.wet}, 1.0
287
- rdax ${reg.lp_state}, -1.0
288
- wrax ${reg.wet}, 0.0 ; Reuse wet as temp = (input - lp_old)
289
- rdax ${reg.bp_state}, 1.0
290
- mulx ${reg.damp_reg} ; ACC = bp_old * damp
291
- rdax ${reg.wet}, -1.0 ; ACC = -(input - lp_old) + bp_old * damp = -(HP)
292
- sof -1.0, 0.0 ; ACC = HP
293
- mulx ${reg.mix_val} ; ACC = F * HP
294
- rdax ${reg.bp_state}, 1.0 ; ACC = bp_old + F * HP = bp_new
295
- wrax ${reg.bp_state}, 1.0 ; Save bp, keep in ACC
296
- mulx ${reg.mix_val} ; ACC = F * bp_new
297
- rdax ${reg.lp_state}, 1.0 ; ACC = lp_old + F * bp_new = lp_new
298
- wrax ${reg.lp_state}, 0.0
299
-
300
- ; ======================================================
301
- ; Stage 5: Dry/Wet Mix
302
- ; ======================================================
303
- rdax ${reg.lp_state}, 1.0
304
- rdax ${reg.dry}, -1.0
305
- sof ${param.mix}, 0.0
306
- rdax ${reg.dry}, 1.0
307
- wrax ${output.out}, 0.0
308
-
309
- @else
310
- ; Bypassed
311
- @endif
1
+ ---
2
+ {
3
+ "type": "effects.novelty.chiptune",
4
+ "name": "Chip Tune",
5
+ "category": "Effects",
6
+ "subcategory": "Lo-Fi",
7
+ "description": "Commodore 64 SID chip emulator: Transforms your guitar into 8-bit chiptune glory with bit crushing, square-wave shaping, ring modulation, white noise, and the legendary SID resonant filter.",
8
+ "color": "#4466BB",
9
+ "width": 180,
10
+ "inputs": [
11
+ {
12
+ "id": "in",
13
+ "name": "Input",
14
+ "type": "audio",
15
+ "required": true
16
+ },
17
+ {
18
+ "id": "crushCV",
19
+ "name": "Crush",
20
+ "type": "control",
21
+ "required": false,
22
+ "parameter": "crush"
23
+ },
24
+ {
25
+ "id": "squareCV",
26
+ "name": "Square",
27
+ "type": "control",
28
+ "required": false,
29
+ "parameter": "square"
30
+ },
31
+ {
32
+ "id": "cutoffCV",
33
+ "name": "Cutoff",
34
+ "type": "control",
35
+ "required": false,
36
+ "parameter": "cutoff"
37
+ },
38
+ {
39
+ "id": "resoCV",
40
+ "name": "Resonance",
41
+ "type": "control",
42
+ "required": false,
43
+ "parameter": "resonance"
44
+ }
45
+ ],
46
+ "outputs": [
47
+ {
48
+ "id": "out",
49
+ "name": "Output",
50
+ "type": "audio"
51
+ }
52
+ ],
53
+ "parameters": [
54
+ {
55
+ "id": "crush",
56
+ "name": "Crush Depth",
57
+ "type": "number",
58
+ "default": 0.2,
59
+ "min": 0.005,
60
+ "max": 0.25,
61
+ "step": 0.005,
62
+ "description": "Sample-rate reduction intensity. Higher = more lo-fi 8-bit crunch."
63
+ },
64
+ {
65
+ "id": "square",
66
+ "name": "Square Drive",
67
+ "type": "number",
68
+ "default": 0.5,
69
+ "min": 0.0,
70
+ "max": 1.99,
71
+ "step": 0.01,
72
+ "description": "Blend from clean (0) to hard-clipped square pulse waveform (max)."
73
+ },
74
+ {
75
+ "id": "ring_freq",
76
+ "name": "Ring Mod",
77
+ "type": "number",
78
+ "default": 0.0,
79
+ "min": 0.0,
80
+ "max": 1.0,
81
+ "step": 0.01,
82
+ "description": "Ring modulator frequency. 0 = off. Adds metallic SID harmonics."
83
+ },
84
+ {
85
+ "id": "noise",
86
+ "name": "Noise Level",
87
+ "type": "number",
88
+ "default": 0.5,
89
+ "min": 0.0,
90
+ "max": 1.0,
91
+ "step": 0.01,
92
+ "description": "White noise generator level. Classic SID noise channel for hi-hats, snares, and static textures."
93
+ },
94
+ {
95
+ "id": "cutoff",
96
+ "name": "Filter Cutoff",
97
+ "type": "number",
98
+ "default": 3000,
99
+ "min": 60,
100
+ "max": 6000,
101
+ "step": 10,
102
+ "conversion": "SVFFREQ",
103
+ "description": "SID-style resonant low-pass filter cutoff frequency."
104
+ },
105
+ {
106
+ "id": "resonance",
107
+ "name": "Filter Resonance",
108
+ "type": "number",
109
+ "default": 4.0,
110
+ "min": 0.5,
111
+ "max": 20.0,
112
+ "step": 0.1,
113
+ "conversion": "SVF_DAMP",
114
+ "description": "Filter Q. Crank it for that classic SID filter sweep character."
115
+ },
116
+ {
117
+ "id": "mix",
118
+ "name": "Dry/Wet Mix",
119
+ "type": "number",
120
+ "default": 1.0,
121
+ "min": 0.0,
122
+ "max": 1.0,
123
+ "step": 0.01,
124
+ "description": "Blend between dry and SID-processed signal."
125
+ }
126
+ ],
127
+ "registers": [
128
+ "counter",
129
+ "held",
130
+ "squared",
131
+ "ring_sin",
132
+ "ring_cos",
133
+ "f_ring",
134
+ "ringmod",
135
+ "noise_reg",
136
+ "noise_dc",
137
+ "lp_state",
138
+ "bp_state",
139
+ "damp_reg",
140
+ "dry",
141
+ "wet",
142
+ "mix_val"
143
+ ]
144
+ }
145
+ ---
146
+ @section header
147
+ equ damp_target (${param.resonance})
148
+
149
+ @section init
150
+ ; Seed the ring mod oscillator (cos=0.5, sin=0)
151
+ ; Without this, the magic circle stays at zero forever!
152
+ sof 0.0, 0.5
153
+ wrax ${reg.ring_cos}, 0.0
154
+
155
+ ; Seed the noise LFSR with a non-zero, non-one starting value
156
+ sof 0.0, 0.3
157
+ wrax ${reg.noise_reg}, 0.0
158
+
159
+ @section main
160
+ @if pinConnected(in)
161
+
162
+ ; ======================================================
163
+ ; Stage 0: Save dry signal for mix
164
+ ; ======================================================
165
+ rdax ${input.in}, 1.0
166
+ wrax ${reg.dry}, 0.0
167
+
168
+ ; ======================================================
169
+ ; Stage 1: Sample-Rate Reduction (SID S&H Bit Crush)
170
+ ; ======================================================
171
+ skp run, ${local.SKIP_INIT}
172
+ rdax ${input.in}, 1.0
173
+ wrax ${reg.held}, 0.0
174
+ ${local.SKIP_INIT}:
175
+
176
+ ; Counter increment: inverted crush so higher = more crushed
177
+ ; step = 0.255 - crush_value (smaller step = slower S&H = more lo-fi)
178
+ rdax ${reg.counter}, 1.0
179
+ wrax ${reg.counter}, 0.0
180
+ @cv crushCV ; ACC = crush depth (0.005..0.25)
181
+ sof -1.0, 0.255 ; ACC = 0.255 - crush = step size
182
+ rdax ${reg.counter}, 1.0 ; ACC = counter + step
183
+ wrax ${reg.counter}, 1.0
184
+
185
+ sof 1.0, -0.99
186
+ skp neg, ${local.SKIP_SAMPLE}
187
+
188
+ clr
189
+ rdax ${input.in}, 1.0
190
+ wrax ${reg.held}, 0.0
191
+ sof 0.0, 0.0
192
+ wrax ${reg.counter}, 0.0
193
+
194
+ ${local.SKIP_SAMPLE}:
195
+
196
+ ; ======================================================
197
+ ; Stage 2: Square Wave Shaping (SID Pulse Oscillator)
198
+ ; ======================================================
199
+ clr
200
+ rdax ${reg.held}, 1.0
201
+ sof 1.99, 0.0
202
+ sof -1.99, 0.0
203
+ sof -1.99, 0.0
204
+ sof 0.5, 0.0
205
+ wrax ${reg.squared}, 0.0
206
+
207
+ ; Crossfade: out = held + (squared - held) * squareDrive
208
+ rdax ${reg.squared}, 1.0
209
+ rdax ${reg.held}, -1.0
210
+ wrax ${reg.mix_val}, 0.0 ; Save (squared - held), clear ACC
211
+ @cv squareCV ; ACC = square drive value
212
+ mulx ${reg.mix_val} ; ACC = (squared - held) * drive
213
+ rdax ${reg.held}, 1.0 ; ACC = held + (squared - held) * drive
214
+ wrax ${reg.squared}, 0.0
215
+
216
+ ; ======================================================
217
+ ; Stage 3: Ring Modulation (SID Ring Mod)
218
+ ; ======================================================
219
+ sof 0.0, ${param.ring_freq}
220
+ sof 0.15, 0.0
221
+ wrax ${reg.f_ring}, 0.0
222
+
223
+ ; Magic circle sine oscillator (seeded in init!)
224
+ rdax ${reg.ring_sin}, -1.0
225
+ mulx ${reg.f_ring}
226
+ rdax ${reg.ring_cos}, 1.0
227
+ wrax ${reg.ring_cos}, 1.0
228
+ mulx ${reg.f_ring}
229
+ rdax ${reg.ring_sin}, 1.0
230
+ wrax ${reg.ring_sin}, 0.0
231
+
232
+ ; Ring mod = squared * carrier
233
+ rdax ${reg.squared}, 1.0
234
+ mulx ${reg.ring_sin}
235
+ sof 1.99, 0.0
236
+ wrax ${reg.ringmod}, 0.0
237
+
238
+ ; Crossfade by ring_freq: 0 = all squared, 1 = all ring
239
+ sof 0.0, ${param.ring_freq}
240
+ wrax ${reg.mix_val}, 0.0
241
+ rdax ${reg.ringmod}, 1.0
242
+ rdax ${reg.squared}, -1.0
243
+ mulx ${reg.mix_val}
244
+ rdax ${reg.squared}, 1.0
245
+ wrax ${reg.wet}, 0.0
246
+
247
+ ; ======================================================
248
+ ; Stage 3.5: SID Noise Channel (Tent Map + DC Blocker)
249
+ ; ======================================================
250
+ ; Tent map: x_next ≈ 1 - 2|x| (chaotic noise source)
251
+ ; FV-1 can't represent exact coefficients 2/1, so the raw
252
+ ; output has a slight positive mean. A DC-blocking LP filter
253
+ ; tracks and removes it for guaranteed zero-mean noise.
254
+ rdax ${reg.noise_reg}, 1.0 ; ACC = x
255
+ absa ; ACC = |x|
256
+ sof -1.99, 0.999 ; ACC ≈ 1 - 2|x| — tent map
257
+ wrax ${reg.noise_reg}, 0.0 ; Save x_next, ACC = 0
258
+
259
+ ; DC blocker: track running average with ultra-slow LP
260
+ rdax ${reg.noise_dc}, 1.0 ; ACC = old DC estimate
261
+ rdfx ${reg.noise_reg}, 0.001 ; ACC = DC_old*0.999 + noise*0.001
262
+ wrax ${reg.noise_dc}, -1.0 ; Save new DC estimate, ACC = -DC
263
+ rdax ${reg.noise_reg}, 1.0 ; ACC = noise - DC = zero-mean noise
264
+
265
+ ; Scale by noise level and add to wet signal
266
+ ; Noise needs ~12dB pre-boost because the SID filter downstream
267
+ ; heavily attenuates high-frequency content where most noise energy lives
268
+ sof 1.99, 0.0
269
+ sof 1.99, 0.0
270
+ sof ${param.noise}, 0.0
271
+ rdax ${reg.wet}, 1.0
272
+ wrax ${reg.wet}, 0.0
273
+
274
+ ; ======================================================
275
+ ; Stage 4: SID Filter (Resonant SVF Low-Pass)
276
+ ; ======================================================
277
+ ; Load damping coefficient (resonance CV or fixed parameter)
278
+ @cv resoCV
279
+ wrax ${reg.damp_reg}, 0.0
280
+
281
+ ; Load cutoff coefficient
282
+ @cv cutoffCV
283
+ wrax ${reg.mix_val}, 0.0
284
+
285
+ ; Chamberlin SVF: HP → BP → LP
286
+ rdax ${reg.wet}, 1.0
287
+ rdax ${reg.lp_state}, -1.0
288
+ wrax ${reg.wet}, 0.0 ; Reuse wet as temp = (input - lp_old)
289
+ rdax ${reg.bp_state}, 1.0
290
+ mulx ${reg.damp_reg} ; ACC = bp_old * damp
291
+ rdax ${reg.wet}, -1.0 ; ACC = -(input - lp_old) + bp_old * damp = -(HP)
292
+ sof -1.0, 0.0 ; ACC = HP
293
+ mulx ${reg.mix_val} ; ACC = F * HP
294
+ rdax ${reg.bp_state}, 1.0 ; ACC = bp_old + F * HP = bp_new
295
+ wrax ${reg.bp_state}, 1.0 ; Save bp, keep in ACC
296
+ mulx ${reg.mix_val} ; ACC = F * bp_new
297
+ rdax ${reg.lp_state}, 1.0 ; ACC = lp_old + F * bp_new = lp_new
298
+ wrax ${reg.lp_state}, 0.0
299
+
300
+ ; ======================================================
301
+ ; Stage 5: Dry/Wet Mix
302
+ ; ======================================================
303
+ rdax ${reg.lp_state}, 1.0
304
+ rdax ${reg.dry}, -1.0
305
+ sof ${param.mix}, 0.0
306
+ rdax ${reg.dry}, 1.0
307
+ wrax ${output.out}, 0.0
308
+
309
+ @else
310
+ ; Bypassed
311
+ @endif