@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.
- package/blocks/ATL_DEVELOPER_REFERENCE.md +156 -156
- package/blocks/control/constant.atl +36 -36
- package/blocks/control/entropy_lfo.atl +74 -74
- package/blocks/control/envelope.atl +120 -120
- package/blocks/control/invert.atl +33 -33
- package/blocks/control/pot.atl +149 -149
- package/blocks/control/power.atl +76 -76
- package/blocks/control/ramp_lfo.atl +122 -122
- package/blocks/control/scale_offset.atl +84 -84
- package/blocks/control/sincos_lfo.atl +126 -126
- package/blocks/control/smoother.atl +48 -48
- package/blocks/control/tremolizer.atl +54 -54
- package/blocks/effects/delay/micro_stutter.atl +77 -77
- package/blocks/effects/delay/mn3011.atl +280 -280
- package/blocks/effects/delay/simple_delay.atl +96 -96
- package/blocks/effects/delay/triple_tap_delay.atl +176 -176
- package/blocks/effects/lo-fi/bit_mangler.atl +74 -74
- package/blocks/effects/lo-fi/chiptune.atl +311 -311
- package/blocks/effects/lo-fi/tape_degrade.atl +181 -181
- package/blocks/effects/modulation/chorus.atl +141 -141
- package/blocks/effects/modulation/chorus_4voice.atl +188 -188
- package/blocks/effects/modulation/flanger.atl +184 -184
- package/blocks/effects/modulation/harmonic_trem.atl +129 -129
- package/blocks/effects/modulation/phaser.atl +299 -299
- package/blocks/effects/pitch/octave_up_down.atl +80 -80
- package/blocks/effects/pitch/pitch_offset.atl +149 -149
- package/blocks/effects/pitch/pitch_offset_dual.atl +197 -197
- package/blocks/effects/pitch/pitch_shift.atl +115 -115
- package/blocks/effects/pitch/sub_octave.atl +100 -100
- package/blocks/effects/reverb/ducking_reverb.atl +145 -145
- package/blocks/effects/reverb/min_reverb.atl +132 -132
- package/blocks/effects/reverb/plate_reverb.atl +344 -344
- package/blocks/effects/reverb/room_reverb.atl +293 -293
- package/blocks/effects/reverb/smear.atl +90 -90
- package/blocks/effects/reverb/spring_reverb.atl +353 -353
- package/blocks/filter/1p_high_pass.atl +62 -62
- package/blocks/filter/1p_low_pass.atl +58 -58
- package/blocks/filter/auto_wah.atl +207 -207
- package/blocks/filter/bbd_loss.atl +79 -79
- package/blocks/filter/shelving_high_pass.atl +76 -76
- package/blocks/filter/shelving_low_pass.atl +76 -76
- package/blocks/filter/svf_2p.atl +116 -116
- package/blocks/gain_mix/crossfade.atl +93 -93
- package/blocks/gain_mix/crossfade2.atl +86 -86
- package/blocks/gain_mix/crossfade3.atl +71 -71
- package/blocks/gain_mix/gainboost.atl +54 -54
- package/blocks/gain_mix/mixer2.atl +76 -76
- package/blocks/gain_mix/mixer3.atl +109 -109
- package/blocks/gain_mix/mixer4.atl +151 -151
- package/blocks/gain_mix/volume.atl +50 -50
- package/blocks/io/adc.atl +53 -53
- package/blocks/io/dac.atl +61 -61
- package/blocks/other/stickynote.atl +24 -24
- package/blocks/other/tone_gen_adjustable.atl +137 -137
- package/blocks/other/tone_gen_fixed.atl +109 -109
- package/dist/blockDiagram/builtinBlocks.js +107 -107
- package/dist/blockDiagram/builtinBlocks.js.map +1 -1
- package/dist/blockDiagram/compiler/BlockTemplate.d.ts.map +1 -1
- package/dist/blockDiagram/compiler/BlockTemplate.js +18 -0
- package/dist/blockDiagram/compiler/BlockTemplate.js.map +1 -1
- package/dist/spnbank/index.d.ts +49 -7
- package/dist/spnbank/index.d.ts.map +1 -1
- package/dist/spnbank/index.js +99 -15
- package/dist/spnbank/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
# ATL (Audiofab Template Language) Block Development Reference
|
|
2
|
-
|
|
3
|
-
This document serves as the foundational context and ruleset for creating high-quality FV-1 algorithms as ATL blocks in the FV-1 VS Code Extension.
|
|
4
|
-
|
|
5
|
-
## 1. File Structure Overview
|
|
6
|
-
Each `.atl` file must consist of two main parts separated by `---`:
|
|
7
|
-
1. **JSON Configuration Module**: Defines block metadata, inputs, outputs, parameters, memories, and hardware registers.
|
|
8
|
-
2. **ATL Code Module**: Contains the FV-1 DSP assembly mixed with ATL macros and directives.
|
|
9
|
-
|
|
10
|
-
## 2. Parameter Conversions
|
|
11
|
-
All parameters should be explicitly mapped using proper conversions. Use the `conversion` key in the parameter JSON.
|
|
12
|
-
- **Gain**: Use `"conversion": "DBLEVEL"` (UI shows dB, code gets linear).
|
|
13
|
-
- **Delay Times**: Use `"conversion": "MS_TO_SAMPLES"` (UI shows ms, code gets sample count).
|
|
14
|
-
- **Frequencies / Rates**: Use `"conversion": "HZ_TO_LFO_RATE"` for LFOs, `"conversion": "LOGFREQ"` for filters.
|
|
15
|
-
- **LFO Width**: Use `"conversion": "MS_TO_LFO_RANGE"`.
|
|
16
|
-
|
|
17
|
-
## 3. Fallbacks for Control Inputs
|
|
18
|
-
Every control input must have a corresponding "fallback" strategy when the input pin is NOT connected by the user.
|
|
19
|
-
|
|
20
|
-
### The `@cv` Macro (Recommended)
|
|
21
|
-
The standard way to handle control inputs is using the `@cv` macro. It automatically handles three scenarios based on whether the input port is connected:
|
|
22
|
-
1. **Unconnected**: Loads the value of the port's associated parameter as a constant (`SOF 0.0, <equ>`).
|
|
23
|
-
2. **Connected to a standard CV/LFO**: Loads the parameter value and scales it by the pot input (`CLR; SOF 0.0, <equ>; MULX <reg>`), giving ACC ∈ [0..paramValue].
|
|
24
|
-
3. **Connected to a zero-bypassed Pot**: Uses the 5-instruction bypass block with the pot's `Zero Bypass Value` as the fallback (instead of the parameter). The pot value is scaled by the parameter value via `RDAX reg, <equ>`.
|
|
25
|
-
|
|
26
|
-
The control input JSON **must** include a `"parameter"` field pointing to the parameter that provides the default/range:
|
|
27
|
-
|
|
28
|
-
```json
|
|
29
|
-
{ "id": "mixCV", "name": "Mix", "type": "control", "required": false, "parameter": "mix" }
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Usage in the ATL template body — just one line:
|
|
33
|
-
|
|
34
|
-
```assembly
|
|
35
|
-
; ACC now holds the correct value (constant or pot-scaled)
|
|
36
|
-
@cv mixCV
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### The `@mulcv` Macro — Scale existing ACC by CV
|
|
40
|
-
Use `@mulcv` when ACC already contains a signal you want to **multiply** by the CV value, without clearing first.
|
|
41
|
-
|
|
42
|
-
| Port state | Generated assembly | Instructions |
|
|
43
|
-
|---|---|---|
|
|
44
|
-
| Unconnected | `SOF paramValue, 0.0` | 1 |
|
|
45
|
-
| Connected | `SOF paramValue, 0.0; MULX reg` | 2 |
|
|
46
|
-
| Zero-bypassed | `WRAX scratch; RDAX reg, equ; bypass; MULX scratch` | 7 |
|
|
47
|
-
|
|
48
|
-
Example — applying a mix CV to a difference signal:
|
|
49
|
-
```assembly
|
|
50
|
-
rdax ${reg.mix_dry}, -1.0 ; ACC = wet - dry
|
|
51
|
-
@mulcv mixCV ; ACC = (wet - dry) * mix
|
|
52
|
-
rdax ${reg.mix_dry}, 1.0 ; ACC = (wet - dry) * mix + dry
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Legacy `@if pinConnected` Method
|
|
56
|
-
If you need highly custom logic between bypassed vs active states, you can still manually check if the pin is connected. Note that this method *does not* support the Zero-Bypassed Pot feature natively:
|
|
57
|
-
|
|
58
|
-
```assembly
|
|
59
|
-
@if pinConnected(mixCV)
|
|
60
|
-
rdax ${input.mixCV}, 1.0
|
|
61
|
-
@else
|
|
62
|
-
sof 0.0, ${mix}
|
|
63
|
-
@endif
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## 4. Input Connection Safeguards
|
|
67
|
-
If the block's main audio input pin is completely disconnected, the block should generally produce no instructions, or handle the bypass effectively, avoiding generating useless cycles.
|
|
68
|
-
|
|
69
|
-
```assembly
|
|
70
|
-
@section main
|
|
71
|
-
@if pinConnected(input)
|
|
72
|
-
; ... heavy processing ...
|
|
73
|
-
@else
|
|
74
|
-
; Block disabled / bypassed
|
|
75
|
-
@endif
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## 5. Memory Management
|
|
79
|
-
Delay block memory allocations MUST be explicitly placed in the `"memories"` JSON array.
|
|
80
|
-
- A fixed size: `{ "id": "pdel", "size": 3276 }`
|
|
81
|
-
- Bound to a parameter (e.g., `delayLength` converted by `MS_TO_SAMPLES`): `{ "id": "delayl", "size": "delayLength" }`
|
|
82
|
-
|
|
83
|
-
In code, reference these with `${mem.pdel}` or `${mem.delayl}`.
|
|
84
|
-
- To access base address: `${mem.id}`
|
|
85
|
-
- To access fractional taps or offset taps: `${mem.id} + offset`
|
|
86
|
-
|
|
87
|
-
## 6. Registers
|
|
88
|
-
All registers used by the block must be declared in the `"registers"` JSON array.
|
|
89
|
-
Reference them via `${reg.registryName}`. No magic EQU offsets/allocations should be done manually for registers.
|
|
90
|
-
Example: `"registers": ["wet", "dry", "temp"]`
|
|
91
|
-
|
|
92
|
-
## 7. Magic Numbers & Reusability
|
|
93
|
-
Avoid magic constants scattered throughout the assembly. Declare them using `@equals` in an `@section header`.
|
|
94
|
-
```assembly
|
|
95
|
-
@section header
|
|
96
|
-
@equals decayLimit 0.8
|
|
97
|
-
@equals bandwidth 0.31852
|
|
98
|
-
```
|
|
99
|
-
To calculate derived parameters, use `EQU` formulas if needed, e.g.:
|
|
100
|
-
```assembly
|
|
101
|
-
EQU tap1 (0.9 * ${tap1Center} * ${delayLength})
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## 8. Routing / I/O Defaults
|
|
105
|
-
If left and right outputs exist, verify which are connected to prevent writing to address zero (`ADDR_PTR` trick or just skipping) if the output isn't used:
|
|
106
|
-
```assembly
|
|
107
|
-
@if pinConnected(outL)
|
|
108
|
-
wrax ${output.outL}, 0.0
|
|
109
|
-
@else
|
|
110
|
-
wrax ADDR_PTR, 0.0 ; discard
|
|
111
|
-
@endif
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## 9. Block Macros Recap
|
|
115
|
-
- `${input.id}`: Address of input.
|
|
116
|
-
- `${output.id}`: Address of output.
|
|
117
|
-
- `${mem.id}`: Address of memory.
|
|
118
|
-
- `${reg.name}`: Address of register.
|
|
119
|
-
- `@section init`: Run once on program startup (e.g., LFO `wlds`). Use `skp run, skip_init`.
|
|
120
|
-
- `@section main`: Run every sample.
|
|
121
|
-
|
|
122
|
-
## 10. Memory Writes (`wra` vs `wrax`)
|
|
123
|
-
NEVER use `wrax` to write to delay memory!
|
|
124
|
-
- **Registers**: Use `wrax` to write into hardware registers (e.g. `wrax ${reg.mix_dry}, 1.0`).
|
|
125
|
-
- **Memory**: Use `wra` to write into delay lines (e.g. `wra ${mem.delay_line}, 0.0`).
|
|
126
|
-
|
|
127
|
-
## 11. Dropdown / Combobox Parameters
|
|
128
|
-
To create a dropdown option parameter instead of a number slider, use `"type": "select"` and provide an `"options"` array:
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"id": "mode",
|
|
132
|
-
"name": "Mode",
|
|
133
|
-
"type": "select",
|
|
134
|
-
"default": 0,
|
|
135
|
-
"options": [
|
|
136
|
-
{ "label": "Gated Reverb", "value": 0 },
|
|
137
|
-
{ "label": "Reverse Reverb", "value": 1 }
|
|
138
|
-
]
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## 12. Dynamic UI Custom Labels (`labelTemplate`)
|
|
143
|
-
If you provide a `"labelTemplate"` in the JSON, you can dynamically display parameter values. You MUST prefix parameter keys with `param.`:
|
|
144
|
-
```json
|
|
145
|
-
"labelTemplate": "${param.mode == 0 ? 'Gated Reverb' : 'Reverse Reverb'}"
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## 13. Exposing POT / CV Connectors
|
|
149
|
-
To allow the user to connect external CV inputs (such as FV-1 POTs) to parameters like `Mix` or `Feedback`, you MUST add corresponding entries to the `"inputs"` array of type `"control"`. If it's not in `"inputs"`, the user cannot patch a POT block to it!
|
|
150
|
-
```json
|
|
151
|
-
{ "id": "mixCV", "name": "Mix", "type": "control", "required": false }
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## 14. FV-1 `wrap` Instruction Constraints
|
|
155
|
-
The `wrap` macro intrinsically dictates `Acc = Acc / 2 + Delay_Read` and `Delay_Write = Acc * 0.5 + Delay_Read`.
|
|
156
|
-
Because it implicitly includes the input accumulator in its write back into the delay line, it CANNOT be used in scenarios where the phase of the accumulator needs to be negated or inverted (e.g. `Acc = -Acc / 2 + Delay_Read`). Attempting to negate the accumulator before a `wrap` instruction will cause the delay memory to recursively write back inverted values, forming an unstable positive feedback/resonant filter resulting in metallic ringing artifacts and uncontrollable gain stacking. If explicit mathematical phase tracking is required across all-pass filters without intrinsic gain side-effects, the `wrap` instruction must be manually unrolled using discrete `wra`/`rda`/`wrax`/`rdax` sequences.
|
|
1
|
+
# ATL (Audiofab Template Language) Block Development Reference
|
|
2
|
+
|
|
3
|
+
This document serves as the foundational context and ruleset for creating high-quality FV-1 algorithms as ATL blocks in the FV-1 VS Code Extension.
|
|
4
|
+
|
|
5
|
+
## 1. File Structure Overview
|
|
6
|
+
Each `.atl` file must consist of two main parts separated by `---`:
|
|
7
|
+
1. **JSON Configuration Module**: Defines block metadata, inputs, outputs, parameters, memories, and hardware registers.
|
|
8
|
+
2. **ATL Code Module**: Contains the FV-1 DSP assembly mixed with ATL macros and directives.
|
|
9
|
+
|
|
10
|
+
## 2. Parameter Conversions
|
|
11
|
+
All parameters should be explicitly mapped using proper conversions. Use the `conversion` key in the parameter JSON.
|
|
12
|
+
- **Gain**: Use `"conversion": "DBLEVEL"` (UI shows dB, code gets linear).
|
|
13
|
+
- **Delay Times**: Use `"conversion": "MS_TO_SAMPLES"` (UI shows ms, code gets sample count).
|
|
14
|
+
- **Frequencies / Rates**: Use `"conversion": "HZ_TO_LFO_RATE"` for LFOs, `"conversion": "LOGFREQ"` for filters.
|
|
15
|
+
- **LFO Width**: Use `"conversion": "MS_TO_LFO_RANGE"`.
|
|
16
|
+
|
|
17
|
+
## 3. Fallbacks for Control Inputs
|
|
18
|
+
Every control input must have a corresponding "fallback" strategy when the input pin is NOT connected by the user.
|
|
19
|
+
|
|
20
|
+
### The `@cv` Macro (Recommended)
|
|
21
|
+
The standard way to handle control inputs is using the `@cv` macro. It automatically handles three scenarios based on whether the input port is connected:
|
|
22
|
+
1. **Unconnected**: Loads the value of the port's associated parameter as a constant (`SOF 0.0, <equ>`).
|
|
23
|
+
2. **Connected to a standard CV/LFO**: Loads the parameter value and scales it by the pot input (`CLR; SOF 0.0, <equ>; MULX <reg>`), giving ACC ∈ [0..paramValue].
|
|
24
|
+
3. **Connected to a zero-bypassed Pot**: Uses the 5-instruction bypass block with the pot's `Zero Bypass Value` as the fallback (instead of the parameter). The pot value is scaled by the parameter value via `RDAX reg, <equ>`.
|
|
25
|
+
|
|
26
|
+
The control input JSON **must** include a `"parameter"` field pointing to the parameter that provides the default/range:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{ "id": "mixCV", "name": "Mix", "type": "control", "required": false, "parameter": "mix" }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Usage in the ATL template body — just one line:
|
|
33
|
+
|
|
34
|
+
```assembly
|
|
35
|
+
; ACC now holds the correct value (constant or pot-scaled)
|
|
36
|
+
@cv mixCV
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### The `@mulcv` Macro — Scale existing ACC by CV
|
|
40
|
+
Use `@mulcv` when ACC already contains a signal you want to **multiply** by the CV value, without clearing first.
|
|
41
|
+
|
|
42
|
+
| Port state | Generated assembly | Instructions |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Unconnected | `SOF paramValue, 0.0` | 1 |
|
|
45
|
+
| Connected | `SOF paramValue, 0.0; MULX reg` | 2 |
|
|
46
|
+
| Zero-bypassed | `WRAX scratch; RDAX reg, equ; bypass; MULX scratch` | 7 |
|
|
47
|
+
|
|
48
|
+
Example — applying a mix CV to a difference signal:
|
|
49
|
+
```assembly
|
|
50
|
+
rdax ${reg.mix_dry}, -1.0 ; ACC = wet - dry
|
|
51
|
+
@mulcv mixCV ; ACC = (wet - dry) * mix
|
|
52
|
+
rdax ${reg.mix_dry}, 1.0 ; ACC = (wet - dry) * mix + dry
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Legacy `@if pinConnected` Method
|
|
56
|
+
If you need highly custom logic between bypassed vs active states, you can still manually check if the pin is connected. Note that this method *does not* support the Zero-Bypassed Pot feature natively:
|
|
57
|
+
|
|
58
|
+
```assembly
|
|
59
|
+
@if pinConnected(mixCV)
|
|
60
|
+
rdax ${input.mixCV}, 1.0
|
|
61
|
+
@else
|
|
62
|
+
sof 0.0, ${mix}
|
|
63
|
+
@endif
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 4. Input Connection Safeguards
|
|
67
|
+
If the block's main audio input pin is completely disconnected, the block should generally produce no instructions, or handle the bypass effectively, avoiding generating useless cycles.
|
|
68
|
+
|
|
69
|
+
```assembly
|
|
70
|
+
@section main
|
|
71
|
+
@if pinConnected(input)
|
|
72
|
+
; ... heavy processing ...
|
|
73
|
+
@else
|
|
74
|
+
; Block disabled / bypassed
|
|
75
|
+
@endif
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 5. Memory Management
|
|
79
|
+
Delay block memory allocations MUST be explicitly placed in the `"memories"` JSON array.
|
|
80
|
+
- A fixed size: `{ "id": "pdel", "size": 3276 }`
|
|
81
|
+
- Bound to a parameter (e.g., `delayLength` converted by `MS_TO_SAMPLES`): `{ "id": "delayl", "size": "delayLength" }`
|
|
82
|
+
|
|
83
|
+
In code, reference these with `${mem.pdel}` or `${mem.delayl}`.
|
|
84
|
+
- To access base address: `${mem.id}`
|
|
85
|
+
- To access fractional taps or offset taps: `${mem.id} + offset`
|
|
86
|
+
|
|
87
|
+
## 6. Registers
|
|
88
|
+
All registers used by the block must be declared in the `"registers"` JSON array.
|
|
89
|
+
Reference them via `${reg.registryName}`. No magic EQU offsets/allocations should be done manually for registers.
|
|
90
|
+
Example: `"registers": ["wet", "dry", "temp"]`
|
|
91
|
+
|
|
92
|
+
## 7. Magic Numbers & Reusability
|
|
93
|
+
Avoid magic constants scattered throughout the assembly. Declare them using `@equals` in an `@section header`.
|
|
94
|
+
```assembly
|
|
95
|
+
@section header
|
|
96
|
+
@equals decayLimit 0.8
|
|
97
|
+
@equals bandwidth 0.31852
|
|
98
|
+
```
|
|
99
|
+
To calculate derived parameters, use `EQU` formulas if needed, e.g.:
|
|
100
|
+
```assembly
|
|
101
|
+
EQU tap1 (0.9 * ${tap1Center} * ${delayLength})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 8. Routing / I/O Defaults
|
|
105
|
+
If left and right outputs exist, verify which are connected to prevent writing to address zero (`ADDR_PTR` trick or just skipping) if the output isn't used:
|
|
106
|
+
```assembly
|
|
107
|
+
@if pinConnected(outL)
|
|
108
|
+
wrax ${output.outL}, 0.0
|
|
109
|
+
@else
|
|
110
|
+
wrax ADDR_PTR, 0.0 ; discard
|
|
111
|
+
@endif
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 9. Block Macros Recap
|
|
115
|
+
- `${input.id}`: Address of input.
|
|
116
|
+
- `${output.id}`: Address of output.
|
|
117
|
+
- `${mem.id}`: Address of memory.
|
|
118
|
+
- `${reg.name}`: Address of register.
|
|
119
|
+
- `@section init`: Run once on program startup (e.g., LFO `wlds`). Use `skp run, skip_init`.
|
|
120
|
+
- `@section main`: Run every sample.
|
|
121
|
+
|
|
122
|
+
## 10. Memory Writes (`wra` vs `wrax`)
|
|
123
|
+
NEVER use `wrax` to write to delay memory!
|
|
124
|
+
- **Registers**: Use `wrax` to write into hardware registers (e.g. `wrax ${reg.mix_dry}, 1.0`).
|
|
125
|
+
- **Memory**: Use `wra` to write into delay lines (e.g. `wra ${mem.delay_line}, 0.0`).
|
|
126
|
+
|
|
127
|
+
## 11. Dropdown / Combobox Parameters
|
|
128
|
+
To create a dropdown option parameter instead of a number slider, use `"type": "select"` and provide an `"options"` array:
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"id": "mode",
|
|
132
|
+
"name": "Mode",
|
|
133
|
+
"type": "select",
|
|
134
|
+
"default": 0,
|
|
135
|
+
"options": [
|
|
136
|
+
{ "label": "Gated Reverb", "value": 0 },
|
|
137
|
+
{ "label": "Reverse Reverb", "value": 1 }
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 12. Dynamic UI Custom Labels (`labelTemplate`)
|
|
143
|
+
If you provide a `"labelTemplate"` in the JSON, you can dynamically display parameter values. You MUST prefix parameter keys with `param.`:
|
|
144
|
+
```json
|
|
145
|
+
"labelTemplate": "${param.mode == 0 ? 'Gated Reverb' : 'Reverse Reverb'}"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 13. Exposing POT / CV Connectors
|
|
149
|
+
To allow the user to connect external CV inputs (such as FV-1 POTs) to parameters like `Mix` or `Feedback`, you MUST add corresponding entries to the `"inputs"` array of type `"control"`. If it's not in `"inputs"`, the user cannot patch a POT block to it!
|
|
150
|
+
```json
|
|
151
|
+
{ "id": "mixCV", "name": "Mix", "type": "control", "required": false }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 14. FV-1 `wrap` Instruction Constraints
|
|
155
|
+
The `wrap` macro intrinsically dictates `Acc = Acc / 2 + Delay_Read` and `Delay_Write = Acc * 0.5 + Delay_Read`.
|
|
156
|
+
Because it implicitly includes the input accumulator in its write back into the delay line, it CANNOT be used in scenarios where the phase of the accumulator needs to be negated or inverted (e.g. `Acc = -Acc / 2 + Delay_Read`). Attempting to negate the accumulator before a `wrap` instruction will cause the delay memory to recursively write back inverted values, forming an unstable positive feedback/resonant filter resulting in metallic ringing artifacts and uncontrollable gain stacking. If explicit mathematical phase tracking is required across all-pass filters without intrinsic gain side-effects, the `wrap` instruction must be manually unrolled using discrete `wra`/`rda`/`wrax`/`rdax` sequences.
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
---
|
|
2
|
-
{
|
|
3
|
-
"type": "control.constant",
|
|
4
|
-
"name": "Constant",
|
|
5
|
-
"category": "Control",
|
|
6
|
-
"description": "Generates a constant control value.",
|
|
7
|
-
"color": "#FF9800",
|
|
8
|
-
"width": 140,
|
|
9
|
-
"labelTemplate": "${param.constant}",
|
|
10
|
-
"inputs": [],
|
|
11
|
-
"outputs": [
|
|
12
|
-
{
|
|
13
|
-
"id": "value",
|
|
14
|
-
"name": "Value",
|
|
15
|
-
"type": "control"
|
|
16
|
-
}
|
|
17
|
-
],
|
|
18
|
-
"parameters": [
|
|
19
|
-
{
|
|
20
|
-
"id": "constant",
|
|
21
|
-
"name": "Value",
|
|
22
|
-
"type": "number",
|
|
23
|
-
"default": 0.5,
|
|
24
|
-
"min": 0,
|
|
25
|
-
"max": 1,
|
|
26
|
-
"step": 0.01,
|
|
27
|
-
"description": "Constant value (0.0 to 1.0)."
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
---
|
|
32
|
-
; Constant value generator
|
|
33
|
-
@section main
|
|
34
|
-
|
|
35
|
-
sof 0.0, ${param.constant}
|
|
36
|
-
wrax ${output.value}, 0.0
|
|
1
|
+
---
|
|
2
|
+
{
|
|
3
|
+
"type": "control.constant",
|
|
4
|
+
"name": "Constant",
|
|
5
|
+
"category": "Control",
|
|
6
|
+
"description": "Generates a constant control value.",
|
|
7
|
+
"color": "#FF9800",
|
|
8
|
+
"width": 140,
|
|
9
|
+
"labelTemplate": "${param.constant}",
|
|
10
|
+
"inputs": [],
|
|
11
|
+
"outputs": [
|
|
12
|
+
{
|
|
13
|
+
"id": "value",
|
|
14
|
+
"name": "Value",
|
|
15
|
+
"type": "control"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"parameters": [
|
|
19
|
+
{
|
|
20
|
+
"id": "constant",
|
|
21
|
+
"name": "Value",
|
|
22
|
+
"type": "number",
|
|
23
|
+
"default": 0.5,
|
|
24
|
+
"min": 0,
|
|
25
|
+
"max": 1,
|
|
26
|
+
"step": 0.01,
|
|
27
|
+
"description": "Constant value (0.0 to 1.0)."
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
---
|
|
32
|
+
; Constant value generator
|
|
33
|
+
@section main
|
|
34
|
+
|
|
35
|
+
sof 0.0, ${param.constant}
|
|
36
|
+
wrax ${output.value}, 0.0
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
---
|
|
2
|
-
{
|
|
3
|
-
"type": "control.entropy_lfo",
|
|
4
|
-
"name": "Entropy LFO",
|
|
5
|
-
"category": "Control",
|
|
6
|
-
"description": "Chaotic Logistic Map (fixed: subsonic sweep and uppercase label hardening).",
|
|
7
|
-
"color": "#f2b824",
|
|
8
|
-
"inputs": [
|
|
9
|
-
{ "id": "speed", "name": "Speed", "type": "control", "required": false, "parameter": "speed" },
|
|
10
|
-
{ "id": "chaos", "name": "Chaos", "type": "control", "required": false, "parameter": "r_chaos" }
|
|
11
|
-
],
|
|
12
|
-
"outputs": [
|
|
13
|
-
{ "id": "out", "name": "Out", "type": "control" }
|
|
14
|
-
],
|
|
15
|
-
"parameters": [
|
|
16
|
-
{ "id": "speed", "name": "Update Rate", "type": "number", "default": 0.5, "min": 0, "max": 1, "step": 0.01 },
|
|
17
|
-
{ "id": "r_chaos", "name": "Chaos (r)", "type": "number", "default": 0.98, "min": 0.88, "max": 1.0, "step": 0.01 }
|
|
18
|
-
],
|
|
19
|
-
"registers": [ "x", "counter", "temp", "chaos_val", "speed_val" ]
|
|
20
|
-
}
|
|
21
|
-
---
|
|
22
|
-
@section init
|
|
23
|
-
skp run, skip_init_entropy
|
|
24
|
-
sof 0.0, 0.432 ; irrational seed
|
|
25
|
-
wrax ${reg.x}, 0.0
|
|
26
|
-
sof 0.0, 0.0
|
|
27
|
-
wrax ${reg.counter}, 0.0
|
|
28
|
-
skip_init_entropy:
|
|
29
|
-
|
|
30
|
-
@section main
|
|
31
|
-
; 1. Load CV inputs into registers
|
|
32
|
-
@cv speed
|
|
33
|
-
wrax ${reg.speed_val}, 0.0
|
|
34
|
-
@cv chaos
|
|
35
|
-
wrax ${reg.chaos_val}, 0.0
|
|
36
|
-
|
|
37
|
-
; 2. Update Counter
|
|
38
|
-
rdax ${reg.counter}, 1.0
|
|
39
|
-
rdax ${reg.speed_val}, 0.002 ; Max speed ~70Hz (0.95 / 0.00203 / 32768)
|
|
40
|
-
sof 1.0, 0.00003 ; Min speed ~1Hz (0.95 / 0.00003 / 32768)
|
|
41
|
-
wrax ${reg.counter}, 1.0
|
|
42
|
-
|
|
43
|
-
; 3. Check for Update Event
|
|
44
|
-
sof 1.0, -0.95
|
|
45
|
-
skp neg, ${local.SKIP_UPDATE}
|
|
46
|
-
|
|
47
|
-
; --- Chaotic Map Block (Runs only on Trigger) ---
|
|
48
|
-
clr ; MUST clear ACC after the counter check!
|
|
49
|
-
rdax ${reg.x}, 1.0
|
|
50
|
-
wrax ${reg.temp}, 1.0 ; temp = x
|
|
51
|
-
sof -1.0, 0.999 ; ACC = 1 - x
|
|
52
|
-
mulx ${reg.temp} ; ACC = x * (1 - x)
|
|
53
|
-
sof 1.999, 0.0 ; Scale (x4)
|
|
54
|
-
sof 1.999, 0.0
|
|
55
|
-
mulx ${reg.chaos_val} ; Apply chaos factor r
|
|
56
|
-
wrax ${reg.x}, 1.0 ; update x
|
|
57
|
-
|
|
58
|
-
; Kickstart: if |x| < 0.01, reset to seed (guards against map stalling at 0)
|
|
59
|
-
absa
|
|
60
|
-
sof 1.0, -0.01
|
|
61
|
-
skp gez, ${local.SKIP_KICK}
|
|
62
|
-
sof 0.0, 0.432 ; irrational seed
|
|
63
|
-
wrax ${reg.x}, 0.0
|
|
64
|
-
${local.SKIP_KICK}:
|
|
65
|
-
|
|
66
|
-
; Reset counter
|
|
67
|
-
sof 0.0, 0.0
|
|
68
|
-
wrax ${reg.counter}, 0.0
|
|
69
|
-
|
|
70
|
-
${local.SKIP_UPDATE}:
|
|
71
|
-
; 5. Output (CLEAN)
|
|
72
|
-
clr ; Clear ACC before loading output to avoid counter pollution
|
|
73
|
-
rdax ${reg.x}, 1.0
|
|
74
|
-
wrax ${output.out}, 0.0
|
|
1
|
+
---
|
|
2
|
+
{
|
|
3
|
+
"type": "control.entropy_lfo",
|
|
4
|
+
"name": "Entropy LFO",
|
|
5
|
+
"category": "Control",
|
|
6
|
+
"description": "Chaotic Logistic Map (fixed: subsonic sweep and uppercase label hardening).",
|
|
7
|
+
"color": "#f2b824",
|
|
8
|
+
"inputs": [
|
|
9
|
+
{ "id": "speed", "name": "Speed", "type": "control", "required": false, "parameter": "speed" },
|
|
10
|
+
{ "id": "chaos", "name": "Chaos", "type": "control", "required": false, "parameter": "r_chaos" }
|
|
11
|
+
],
|
|
12
|
+
"outputs": [
|
|
13
|
+
{ "id": "out", "name": "Out", "type": "control" }
|
|
14
|
+
],
|
|
15
|
+
"parameters": [
|
|
16
|
+
{ "id": "speed", "name": "Update Rate", "type": "number", "default": 0.5, "min": 0, "max": 1, "step": 0.01 },
|
|
17
|
+
{ "id": "r_chaos", "name": "Chaos (r)", "type": "number", "default": 0.98, "min": 0.88, "max": 1.0, "step": 0.01 }
|
|
18
|
+
],
|
|
19
|
+
"registers": [ "x", "counter", "temp", "chaos_val", "speed_val" ]
|
|
20
|
+
}
|
|
21
|
+
---
|
|
22
|
+
@section init
|
|
23
|
+
skp run, skip_init_entropy
|
|
24
|
+
sof 0.0, 0.432 ; irrational seed
|
|
25
|
+
wrax ${reg.x}, 0.0
|
|
26
|
+
sof 0.0, 0.0
|
|
27
|
+
wrax ${reg.counter}, 0.0
|
|
28
|
+
skip_init_entropy:
|
|
29
|
+
|
|
30
|
+
@section main
|
|
31
|
+
; 1. Load CV inputs into registers
|
|
32
|
+
@cv speed
|
|
33
|
+
wrax ${reg.speed_val}, 0.0
|
|
34
|
+
@cv chaos
|
|
35
|
+
wrax ${reg.chaos_val}, 0.0
|
|
36
|
+
|
|
37
|
+
; 2. Update Counter
|
|
38
|
+
rdax ${reg.counter}, 1.0
|
|
39
|
+
rdax ${reg.speed_val}, 0.002 ; Max speed ~70Hz (0.95 / 0.00203 / 32768)
|
|
40
|
+
sof 1.0, 0.00003 ; Min speed ~1Hz (0.95 / 0.00003 / 32768)
|
|
41
|
+
wrax ${reg.counter}, 1.0
|
|
42
|
+
|
|
43
|
+
; 3. Check for Update Event
|
|
44
|
+
sof 1.0, -0.95
|
|
45
|
+
skp neg, ${local.SKIP_UPDATE}
|
|
46
|
+
|
|
47
|
+
; --- Chaotic Map Block (Runs only on Trigger) ---
|
|
48
|
+
clr ; MUST clear ACC after the counter check!
|
|
49
|
+
rdax ${reg.x}, 1.0
|
|
50
|
+
wrax ${reg.temp}, 1.0 ; temp = x
|
|
51
|
+
sof -1.0, 0.999 ; ACC = 1 - x
|
|
52
|
+
mulx ${reg.temp} ; ACC = x * (1 - x)
|
|
53
|
+
sof 1.999, 0.0 ; Scale (x4)
|
|
54
|
+
sof 1.999, 0.0
|
|
55
|
+
mulx ${reg.chaos_val} ; Apply chaos factor r
|
|
56
|
+
wrax ${reg.x}, 1.0 ; update x
|
|
57
|
+
|
|
58
|
+
; Kickstart: if |x| < 0.01, reset to seed (guards against map stalling at 0)
|
|
59
|
+
absa
|
|
60
|
+
sof 1.0, -0.01
|
|
61
|
+
skp gez, ${local.SKIP_KICK}
|
|
62
|
+
sof 0.0, 0.432 ; irrational seed
|
|
63
|
+
wrax ${reg.x}, 0.0
|
|
64
|
+
${local.SKIP_KICK}:
|
|
65
|
+
|
|
66
|
+
; Reset counter
|
|
67
|
+
sof 0.0, 0.0
|
|
68
|
+
wrax ${reg.counter}, 0.0
|
|
69
|
+
|
|
70
|
+
${local.SKIP_UPDATE}:
|
|
71
|
+
; 5. Output (CLEAN)
|
|
72
|
+
clr ; Clear ACC before loading output to avoid counter pollution
|
|
73
|
+
rdax ${reg.x}, 1.0
|
|
74
|
+
wrax ${output.out}, 0.0
|