@audiofab-io/fv1-core 0.5.0 → 0.6.1

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 (65) 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/blockDiagram/compiler/BlockTemplate.d.ts.map +1 -1
  59. package/dist/blockDiagram/compiler/BlockTemplate.js +18 -0
  60. package/dist/blockDiagram/compiler/BlockTemplate.js.map +1 -1
  61. package/dist/spnbank/index.d.ts +49 -7
  62. package/dist/spnbank/index.d.ts.map +1 -1
  63. package/dist/spnbank/index.js +99 -15
  64. package/dist/spnbank/index.js.map +1 -1
  65. package/package.json +2 -2
@@ -1,181 +1,181 @@
1
- ---
2
- {
3
- "type": "fx.tape_degrade",
4
- "name": "Tape Degrader",
5
- "category": "Effects",
6
- "subcategory": "Lo-Fi",
7
- "description": "Simulates tape wow, flutter, and top-end age loss.",
8
- "color": "#24F2F2",
9
- "width": 180,
10
- "inputs": [
11
- {
12
- "id": "in",
13
- "name": "Input",
14
- "type": "audio",
15
- "required": true
16
- },
17
- {
18
- "id": "wow_rate_cv",
19
- "name": "Wow Rate",
20
- "type": "control",
21
- "required": false,
22
- "parameter": "wow_rate"
23
- },
24
- {
25
- "id": "wow_depth_cv",
26
- "name": "Wow Depth",
27
- "type": "control",
28
- "required": false,
29
- "parameter": "wow_depth"
30
- },
31
- {
32
- "id": "flutter_rate_cv",
33
- "name": "Flutter Rate",
34
- "type": "control",
35
- "required": false,
36
- "parameter": "flutter_rate"
37
- },
38
- {
39
- "id": "flutter_depth_cv",
40
- "name": "Flutter Depth",
41
- "type": "control",
42
- "required": false,
43
- "parameter": "flutter_depth"
44
- }
45
- ],
46
- "outputs": [
47
- {
48
- "id": "out",
49
- "name": "Output",
50
- "type": "audio"
51
- }
52
- ],
53
- "parameters": [
54
- {
55
- "id": "wow_rate",
56
- "name": "Wow Rate (Hz)",
57
- "type": "number",
58
- "default": 0.5,
59
- "min": 0.1,
60
- "max": 2.0,
61
- "step": 0.01,
62
- "conversion": "HZ_TO_LFO_RATE"
63
- },
64
- {
65
- "id": "wow_depth",
66
- "name": "Wow Depth (ms)",
67
- "type": "number",
68
- "default": 1.5,
69
- "min": 0,
70
- "max": 5.0,
71
- "step": 0.1,
72
- "conversion": "MS_TO_LFO_RANGE"
73
- },
74
- {
75
- "id": "flutter_rate",
76
- "name": "Flutter Rate (Hz)",
77
- "type": "number",
78
- "default": 12.0,
79
- "min": 5.0,
80
- "max": 20.0,
81
- "step": 0.1,
82
- "conversion": "HZ_TO_LFO_RATE"
83
- },
84
- {
85
- "id": "flutter_depth",
86
- "name": "Flutter Depth (ms)",
87
- "type": "number",
88
- "default": 0.2,
89
- "min": 0,
90
- "max": 1.5,
91
- "step": 0.01,
92
- "conversion": "MS_TO_LFO_RANGE"
93
- },
94
- {
95
- "id": "age",
96
- "name": "Tape Age (Tone)",
97
- "type": "number",
98
- "default": 0.3,
99
- "min": 0.01,
100
- "max": 1.0,
101
- "step": 0.01,
102
- "description": "1.0 is bright, lower is darker (aged tape)."
103
- }
104
- ],
105
- "memories": [
106
- {
107
- "id": "tape_buffer",
108
- "size": 4096
109
- }
110
- ],
111
- "registers": [
112
- "wow_temp",
113
- "lpf_lp"
114
- ]
115
- }
116
- ---
117
- @section init
118
- skp run, init_tape
119
- wlds SIN0, ${wow_rate}, ${wow_depth}
120
- wlds SIN1, ${flutter_rate}, ${flutter_depth}
121
- init_tape:
122
-
123
- @section main
124
- ; CV Modulation for Wow/Flutter
125
- ; Since rates are HZ_TO_LFO_RATE, we must scale them manually
126
- @if pinConnected(wow_rate_cv)
127
- rdax ${input.wow_rate_cv}, ${wow_rate} / 511.0
128
- wrax SIN0_RATE, 0.0
129
- @endif
130
-
131
- ; Depths are MS_TO_LFO_RANGE which goes up to 32767. So scale / 32767.0
132
- @if pinConnected(wow_depth_cv)
133
- rdax ${input.wow_depth_cv}, ${wow_depth} / 32767.0
134
- wrax SIN0_RANGE, 0.0
135
- @endif
136
-
137
- @if pinConnected(flutter_rate_cv)
138
- rdax ${input.flutter_rate_cv}, ${flutter_rate} / 511.0
139
- wrax SIN1_RATE, 0.0
140
- @endif
141
-
142
- @if pinConnected(flutter_depth_cv)
143
- rdax ${input.flutter_depth_cv}, ${flutter_depth} / 32767.0
144
- wrax SIN1_RANGE, 0.0
145
- @endif
146
-
147
- ; Write to Tape
148
- rdax ${input.in}, 1.0
149
- wra ${mem.tape_buffer}, 0.0
150
-
151
- ; Read Wow Tape Head (SIN0)
152
- @equals center ${mem.tape_buffer} + 2048
153
- cho rda, SIN0, SIN | REG | COMPC, ${center}
154
- cho rda, SIN0, SIN, ${center} + 1
155
-
156
- ; Modulate with Flutter (SIN1) off the same base, summing them creates composite warble
157
- ; Actually, FV-1 CHO adds to ACC! So we just do another CHO RDA but without REG tracking?
158
- ; To maintain unity volume, we shouldn't add audio signals, we should add address offsets.
159
- ; But we can't add address offsets in SpinASM dynamically easily.
160
- ; Instead, we just read from the buffer with Flutter and average the two reads!
161
- ; Multiply Wow read by 0.5
162
- sof 0.5, 0.0
163
- wrax ${reg.wow_temp}, 0.0 ; Hold Wow audio separately from LPF state
164
-
165
- ; Read Flutter Tape Head (SIN1)
166
- cho rda, SIN1, SIN | REG | COMPC, ${center}
167
- cho rda, SIN1, SIN, ${center} + 1
168
- sof 0.5, 0.0
169
-
170
- ; Combine Wow and Flutter audio signals
171
- rdax ${reg.wow_temp}, 1.0
172
-
173
- ; Tape Age Degradation (1-Pole Lowpass: L_new = L_old + Age * (I - L_old))
174
- ; ACC = composite warble (wow*0.5 + flutter*0.5)
175
- rdax ${reg.lpf_lp}, -1.0 ; ACC = I - L_old
176
- sof ${param.age}, 0.0 ; ACC = Age * (I - L_old)
177
- rdax ${reg.lpf_lp}, 1.0 ; ACC = L_old + Age * (I - L_old) = L_new
178
- wrax ${reg.lpf_lp}, 1.0 ; Save new state and keep L_new in ACC
179
-
180
- ; Export
181
- wrax ${output.out}, 0.0
1
+ ---
2
+ {
3
+ "type": "fx.tape_degrade",
4
+ "name": "Tape Degrader",
5
+ "category": "Effects",
6
+ "subcategory": "Lo-Fi",
7
+ "description": "Simulates tape wow, flutter, and top-end age loss.",
8
+ "color": "#24F2F2",
9
+ "width": 180,
10
+ "inputs": [
11
+ {
12
+ "id": "in",
13
+ "name": "Input",
14
+ "type": "audio",
15
+ "required": true
16
+ },
17
+ {
18
+ "id": "wow_rate_cv",
19
+ "name": "Wow Rate",
20
+ "type": "control",
21
+ "required": false,
22
+ "parameter": "wow_rate"
23
+ },
24
+ {
25
+ "id": "wow_depth_cv",
26
+ "name": "Wow Depth",
27
+ "type": "control",
28
+ "required": false,
29
+ "parameter": "wow_depth"
30
+ },
31
+ {
32
+ "id": "flutter_rate_cv",
33
+ "name": "Flutter Rate",
34
+ "type": "control",
35
+ "required": false,
36
+ "parameter": "flutter_rate"
37
+ },
38
+ {
39
+ "id": "flutter_depth_cv",
40
+ "name": "Flutter Depth",
41
+ "type": "control",
42
+ "required": false,
43
+ "parameter": "flutter_depth"
44
+ }
45
+ ],
46
+ "outputs": [
47
+ {
48
+ "id": "out",
49
+ "name": "Output",
50
+ "type": "audio"
51
+ }
52
+ ],
53
+ "parameters": [
54
+ {
55
+ "id": "wow_rate",
56
+ "name": "Wow Rate (Hz)",
57
+ "type": "number",
58
+ "default": 0.5,
59
+ "min": 0.1,
60
+ "max": 2.0,
61
+ "step": 0.01,
62
+ "conversion": "HZ_TO_LFO_RATE"
63
+ },
64
+ {
65
+ "id": "wow_depth",
66
+ "name": "Wow Depth (ms)",
67
+ "type": "number",
68
+ "default": 1.5,
69
+ "min": 0,
70
+ "max": 5.0,
71
+ "step": 0.1,
72
+ "conversion": "MS_TO_LFO_RANGE"
73
+ },
74
+ {
75
+ "id": "flutter_rate",
76
+ "name": "Flutter Rate (Hz)",
77
+ "type": "number",
78
+ "default": 12.0,
79
+ "min": 5.0,
80
+ "max": 20.0,
81
+ "step": 0.1,
82
+ "conversion": "HZ_TO_LFO_RATE"
83
+ },
84
+ {
85
+ "id": "flutter_depth",
86
+ "name": "Flutter Depth (ms)",
87
+ "type": "number",
88
+ "default": 0.2,
89
+ "min": 0,
90
+ "max": 1.5,
91
+ "step": 0.01,
92
+ "conversion": "MS_TO_LFO_RANGE"
93
+ },
94
+ {
95
+ "id": "age",
96
+ "name": "Tape Age (Tone)",
97
+ "type": "number",
98
+ "default": 0.3,
99
+ "min": 0.01,
100
+ "max": 1.0,
101
+ "step": 0.01,
102
+ "description": "1.0 is bright, lower is darker (aged tape)."
103
+ }
104
+ ],
105
+ "memories": [
106
+ {
107
+ "id": "tape_buffer",
108
+ "size": 4096
109
+ }
110
+ ],
111
+ "registers": [
112
+ "wow_temp",
113
+ "lpf_lp"
114
+ ]
115
+ }
116
+ ---
117
+ @section init
118
+ skp run, init_tape
119
+ wlds SIN0, ${wow_rate}, ${wow_depth}
120
+ wlds SIN1, ${flutter_rate}, ${flutter_depth}
121
+ init_tape:
122
+
123
+ @section main
124
+ ; CV Modulation for Wow/Flutter
125
+ ; Since rates are HZ_TO_LFO_RATE, we must scale them manually
126
+ @if pinConnected(wow_rate_cv)
127
+ rdax ${input.wow_rate_cv}, ${wow_rate} / 511.0
128
+ wrax SIN0_RATE, 0.0
129
+ @endif
130
+
131
+ ; Depths are MS_TO_LFO_RANGE which goes up to 32767. So scale / 32767.0
132
+ @if pinConnected(wow_depth_cv)
133
+ rdax ${input.wow_depth_cv}, ${wow_depth} / 32767.0
134
+ wrax SIN0_RANGE, 0.0
135
+ @endif
136
+
137
+ @if pinConnected(flutter_rate_cv)
138
+ rdax ${input.flutter_rate_cv}, ${flutter_rate} / 511.0
139
+ wrax SIN1_RATE, 0.0
140
+ @endif
141
+
142
+ @if pinConnected(flutter_depth_cv)
143
+ rdax ${input.flutter_depth_cv}, ${flutter_depth} / 32767.0
144
+ wrax SIN1_RANGE, 0.0
145
+ @endif
146
+
147
+ ; Write to Tape
148
+ rdax ${input.in}, 1.0
149
+ wra ${mem.tape_buffer}, 0.0
150
+
151
+ ; Read Wow Tape Head (SIN0)
152
+ @equals center ${mem.tape_buffer} + 2048
153
+ cho rda, SIN0, SIN | REG | COMPC, ${center}
154
+ cho rda, SIN0, SIN, ${center} + 1
155
+
156
+ ; Modulate with Flutter (SIN1) off the same base, summing them creates composite warble
157
+ ; Actually, FV-1 CHO adds to ACC! So we just do another CHO RDA but without REG tracking?
158
+ ; To maintain unity volume, we shouldn't add audio signals, we should add address offsets.
159
+ ; But we can't add address offsets in SpinASM dynamically easily.
160
+ ; Instead, we just read from the buffer with Flutter and average the two reads!
161
+ ; Multiply Wow read by 0.5
162
+ sof 0.5, 0.0
163
+ wrax ${reg.wow_temp}, 0.0 ; Hold Wow audio separately from LPF state
164
+
165
+ ; Read Flutter Tape Head (SIN1)
166
+ cho rda, SIN1, SIN | REG | COMPC, ${center}
167
+ cho rda, SIN1, SIN, ${center} + 1
168
+ sof 0.5, 0.0
169
+
170
+ ; Combine Wow and Flutter audio signals
171
+ rdax ${reg.wow_temp}, 1.0
172
+
173
+ ; Tape Age Degradation (1-Pole Lowpass: L_new = L_old + Age * (I - L_old))
174
+ ; ACC = composite warble (wow*0.5 + flutter*0.5)
175
+ rdax ${reg.lpf_lp}, -1.0 ; ACC = I - L_old
176
+ sof ${param.age}, 0.0 ; ACC = Age * (I - L_old)
177
+ rdax ${reg.lpf_lp}, 1.0 ; ACC = L_old + Age * (I - L_old) = L_new
178
+ wrax ${reg.lpf_lp}, 1.0 ; Save new state and keep L_new in ACC
179
+
180
+ ; Export
181
+ wrax ${output.out}, 0.0
@@ -1,141 +1,141 @@
1
- ---
2
- {
3
- "type": "fx.chorus",
4
- "name": "Chorus",
5
- "category": "Effects",
6
- "subcategory": "Modulation",
7
- "description": "Classic chorus with LFO-modulated delay",
8
- "color": "#24f2f2",
9
- "width": 180,
10
- "inputs": [
11
- {
12
- "id": "in",
13
- "name": "Input",
14
- "type": "audio",
15
- "required": true
16
- },
17
- {
18
- "id": "lfo_rate",
19
- "name": "LFO Rate",
20
- "type": "control",
21
- "required": false
22
- },
23
- {
24
- "id": "lfo_width",
25
- "name": "LFO Width",
26
- "type": "control",
27
- "required": false
28
- }
29
- ],
30
- "outputs": [
31
- {
32
- "id": "out",
33
- "name": "Output",
34
- "type": "audio"
35
- }
36
- ],
37
- "parameters": [
38
- {
39
- "id": "baseDelay",
40
- "name": "Base Delay (ms)",
41
- "type": "number",
42
- "default": 15,
43
- "min": 1,
44
- "max": 50,
45
- "conversion": "MS_TO_SAMPLES",
46
- "description": "Base delay time (center of modulation) in ms.",
47
- "step": 0.1
48
- },
49
- {
50
- "id": "rate",
51
- "name": "LFO Rate (Hz)",
52
- "type": "number",
53
- "default": 1.0,
54
- "min": 0.05,
55
- "max": 10,
56
- "conversion": "HZ_TO_LFO_RATE",
57
- "description": "Frequency of the chorus modulation in Hz.",
58
- "step": 0.01
59
- },
60
- {
61
- "id": "width",
62
- "name": "LFO Width (ms)",
63
- "type": "number",
64
- "default": 1,
65
- "min": 0,
66
- "max": 10,
67
- "conversion": "MS_TO_LFO_RANGE",
68
- "description": "The width of modulation in ms.",
69
- "step": 0.1
70
- },
71
- {
72
- "id": "lfoSel",
73
- "name": "LFO Select",
74
- "type": "select",
75
- "default": 0,
76
- "options": [
77
- {
78
- "value": 0,
79
- "label": "LFO 0"
80
- },
81
- {
82
- "value": 1,
83
- "label": "LFO 1"
84
- }
85
- ]
86
- }
87
- ],
88
- "memories": [
89
- {
90
- "id": "delayl",
91
- "size": "${baseDelay} * 2"
92
- }
93
- ],
94
- "registers": [
95
- ]
96
- }
97
- ---
98
- @if isequalto lfoSel 0
99
- @equals lfoNum SIN0
100
- @equals lfoRateReg SIN0_RATE
101
- @equals lfoRange SIN0_RANGE
102
- @else
103
- @equals lfoNum SIN1
104
- @equals lfoRateReg SIN1_RATE
105
- @equals lfoRange SIN1_RANGE
106
- @endif
107
-
108
- @section init
109
- ; Initialize LFO
110
- skp run, done_lfo
111
- wlds ${lfoNum}, ${rate}, ${width}
112
- done_lfo:
113
-
114
- @section main
115
- @multiplyDouble lfo_depth_delay width 0.5
116
- @multiplyDouble delay_mem_size baseDelay 2.0
117
- @assert lfo_depth_delay < delay_mem_size, "Maximum LFO depth exceeds delay memory bounds. Increase base delay or decrease LFO depth."
118
-
119
- ; Chorus
120
- rdax ${input.in}, 1.0
121
- wra ${mem.delayl}, 0.0
122
-
123
- @if pinConnected(lfo_rate)
124
- rdax ${input.lfo_rate}, ${param.rate.max} / 511.0
125
- sof 1.0, ${param.rate.min} / 511.0
126
- wrax ${lfoRateReg}, 0.0
127
- @endif
128
-
129
- @if pinConnected(lfo_width)
130
- rdax ${input.lfo_width}, ${param.width.max} / 32767.0
131
- sof 1.0, ${param.width.min} / 32767.0
132
- wrax ${lfoRange}, 0.0
133
- @endif
134
-
135
- ; Read modulated tap (wet signal)
136
- ; center offset from start of delay line
137
- @equals center ${mem.delayl} + ${baseDelay}
138
- cho rda, ${lfoNum}, SIN | REG | COMPC, ${center}
139
- cho rda, ${lfoNum}, SIN, ${center} + 1
140
-
141
- wrax ${output.out}, 0.0
1
+ ---
2
+ {
3
+ "type": "fx.chorus",
4
+ "name": "Chorus",
5
+ "category": "Effects",
6
+ "subcategory": "Modulation",
7
+ "description": "Classic chorus with LFO-modulated delay",
8
+ "color": "#24f2f2",
9
+ "width": 180,
10
+ "inputs": [
11
+ {
12
+ "id": "in",
13
+ "name": "Input",
14
+ "type": "audio",
15
+ "required": true
16
+ },
17
+ {
18
+ "id": "lfo_rate",
19
+ "name": "LFO Rate",
20
+ "type": "control",
21
+ "required": false
22
+ },
23
+ {
24
+ "id": "lfo_width",
25
+ "name": "LFO Width",
26
+ "type": "control",
27
+ "required": false
28
+ }
29
+ ],
30
+ "outputs": [
31
+ {
32
+ "id": "out",
33
+ "name": "Output",
34
+ "type": "audio"
35
+ }
36
+ ],
37
+ "parameters": [
38
+ {
39
+ "id": "baseDelay",
40
+ "name": "Base Delay (ms)",
41
+ "type": "number",
42
+ "default": 15,
43
+ "min": 1,
44
+ "max": 50,
45
+ "conversion": "MS_TO_SAMPLES",
46
+ "description": "Base delay time (center of modulation) in ms.",
47
+ "step": 0.1
48
+ },
49
+ {
50
+ "id": "rate",
51
+ "name": "LFO Rate (Hz)",
52
+ "type": "number",
53
+ "default": 1.0,
54
+ "min": 0.05,
55
+ "max": 10,
56
+ "conversion": "HZ_TO_LFO_RATE",
57
+ "description": "Frequency of the chorus modulation in Hz.",
58
+ "step": 0.01
59
+ },
60
+ {
61
+ "id": "width",
62
+ "name": "LFO Width (ms)",
63
+ "type": "number",
64
+ "default": 1,
65
+ "min": 0,
66
+ "max": 10,
67
+ "conversion": "MS_TO_LFO_RANGE",
68
+ "description": "The width of modulation in ms.",
69
+ "step": 0.1
70
+ },
71
+ {
72
+ "id": "lfoSel",
73
+ "name": "LFO Select",
74
+ "type": "select",
75
+ "default": 0,
76
+ "options": [
77
+ {
78
+ "value": 0,
79
+ "label": "LFO 0"
80
+ },
81
+ {
82
+ "value": 1,
83
+ "label": "LFO 1"
84
+ }
85
+ ]
86
+ }
87
+ ],
88
+ "memories": [
89
+ {
90
+ "id": "delayl",
91
+ "size": "${baseDelay} * 2"
92
+ }
93
+ ],
94
+ "registers": [
95
+ ]
96
+ }
97
+ ---
98
+ @if isequalto lfoSel 0
99
+ @equals lfoNum SIN0
100
+ @equals lfoRateReg SIN0_RATE
101
+ @equals lfoRange SIN0_RANGE
102
+ @else
103
+ @equals lfoNum SIN1
104
+ @equals lfoRateReg SIN1_RATE
105
+ @equals lfoRange SIN1_RANGE
106
+ @endif
107
+
108
+ @section init
109
+ ; Initialize LFO
110
+ skp run, done_lfo
111
+ wlds ${lfoNum}, ${rate}, ${width}
112
+ done_lfo:
113
+
114
+ @section main
115
+ @multiplyDouble lfo_depth_delay width 0.5
116
+ @multiplyDouble delay_mem_size baseDelay 2.0
117
+ @assert lfo_depth_delay < delay_mem_size, "Maximum LFO depth exceeds delay memory bounds. Increase base delay or decrease LFO depth."
118
+
119
+ ; Chorus
120
+ rdax ${input.in}, 1.0
121
+ wra ${mem.delayl}, 0.0
122
+
123
+ @if pinConnected(lfo_rate)
124
+ rdax ${input.lfo_rate}, ${param.rate.max} / 511.0
125
+ sof 1.0, ${param.rate.min} / 511.0
126
+ wrax ${lfoRateReg}, 0.0
127
+ @endif
128
+
129
+ @if pinConnected(lfo_width)
130
+ rdax ${input.lfo_width}, ${param.width.max} / 32767.0
131
+ sof 1.0, ${param.width.min} / 32767.0
132
+ wrax ${lfoRange}, 0.0
133
+ @endif
134
+
135
+ ; Read modulated tap (wet signal)
136
+ ; center offset from start of delay line
137
+ @equals center ${mem.delayl} + ${baseDelay}
138
+ cho rda, ${lfoNum}, SIN | REG | COMPC, ${center}
139
+ cho rda, ${lfoNum}, SIN, ${center} + 1
140
+
141
+ wrax ${output.out}, 0.0