Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/clean-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,9 @@ jobs:
# The same graphs on CPython (the wheel installed above) and on this
# MicroPython, compared sample for sample with a tolerance of 0 counts.
# Each planted fault (one input count off, one set() a block late, and
# one for each of the phaser, the glide, the LFO shapes and the
# sample-and-hold, and two for a graph pulled as a source: its queue
# one for each of the phaser, the glide, the LFO shapes, the
# sample-and-hold, the two-pole filters and the overdrive, and two for a
# graph pulled as a source: its queue
# drained after the render, and a batch published in halves) goes into
# the MicroPython render only, and the comparison must FAIL first.
# The three-way with the CircuitPython unix build is a local run, as
Expand All @@ -249,7 +250,7 @@ jobs:
MP=$GITHUB_WORKSPACE/micropython/ports/unix/build-standard/micropython
PY=$GITHUB_WORKSPACE/audiodsp/.ci-venv/bin/python
cd audiodsp
for planted in lsb late phaser lag lfo hold drain split; do
for planted in lsb late phaser lag lfo hold twopole overdrive drain split; do
if "$PY" tests/parity/verify_graph.py --micropython "$MP" --fault $planted > /dev/null; then
echo "the planted fault passed, so this comparison proves nothing: $planted" >&2
exit 1
Expand Down
4 changes: 4 additions & 0 deletions circuitpython.mk
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ AUDIODSP_CP_SOURCES += $(addprefix $(AUDIODSP_CP_SRC)/shared/, \
audiodsp_graph.c \
)

# The graph's OVERDRIVE node needs double-precision libm the RP2040 build
# cannot link (audiodsp_graph_overdrive.h), so CircuitPython leaves it out.
CFLAGS_USERMOD += -DAUDIODSP_GRAPH_HAS_OVERDRIVE=0

SRC_USERMOD_C += $(AUDIODSP_CP_SOURCES)

# CircuitPython's board ports append -Werror=float-equal and
Expand Down
63 changes: 55 additions & 8 deletions docs/how-it-works/audiograph.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
the graph once, from nodes that call the kernels audiodsp already has (the
oscillator, the ladder, the float biquad, the feedback delay, the all-pass
cascade, the sample-and-hold) and a few small ones a voice needs (an envelope,
an LFO, noise, a mixer, a VCA, a glide), then call `run()` once a block. Nothing in Python runs between the nodes, which is what
an LFO, noise, a mixer, a VCA, a glide, a clipping two-pole filter, an
overdrive), then call `run()` once a block. Nothing in Python runs between the nodes, which is what
lets a six-voice polysynth built from these kernels play in real time on an
ESP32-P4, where the same patch run node by node from Python could not.

Expand Down Expand Up @@ -74,19 +75,21 @@ rather than its last block.
| kind | inputs | parameters | out |
|---|---|---|---|
| `CONST` | | 0: the value, in counts | that value every sample |
| `OSC` | pitch, fm, pwm, sync | `OSC_FREQUENCY`, `OSC_WAVEFORM` (1 saw, 2 pulse, 3 both), `OSC_PULSE_WIDTH`, `OSC_LEVEL`, `OSC_PITCH_OCTAVES`, `OSC_FM_DEPTH`, `OSC_PWM_DEPTH` | audiooscillator's band-limited waveform |
| `OSC` | pitch, fm, pwm, sync | `OSC_FREQUENCY`, `OSC_WAVEFORM` (1 saw, 2 pulse, 4 triangle, 8 sine; a sum plays those added), `OSC_PULSE_WIDTH`, `OSC_LEVEL`, `OSC_PITCH_OCTAVES`, `OSC_FM_DEPTH`, `OSC_PWM_DEPTH` | audiooscillator's band-limited waveform |
| `LADDER` | audio, cutoff | `LADDER_CUTOFF_HZ`, `LADDER_RESONANCE`, `LADDER_DRIVE`, `LADDER_POLES`, `LADDER_PASSBAND_COMP`, `LADDER_OVERSAMPLE`, `LADDER_MIX`, `LADDER_CUTOFF_OCTAVES` | audioladder's filter, the cutoff input read every sample |
| `BIQUAD` | audio | `BIQUAD_MODE`, `BIQUAD_FREQUENCY`, `BIQUAD_Q`, `BIQUAD_GAIN_DB`, `BIQUAD_MIX` | audiobiquad's float biquad |
| `FBDELAY` | audio | `DELAY_MS`, `DELAY_FEEDBACK`, `DELAY_MIX`, `DELAY_DAMPING_HZ`, `DELAY_WOW_HZ`, `DELAY_WOW_DEPTH_MS` and audioecho's other options by number; built with `arg` = the line's length in ms; `table(node, int16_array)` sets its wow shape | audioecho's FeedbackDelay |
| `ENV` | gate | `ENV_ATTACK`, `ENV_DECAY`, `ENV_RELEASE` in seconds, `ENV_SUSTAIN` 0..1 | an ADSR, 0..`UNIT`, each stage a one-pole approach, the attack aimed past 1.0 so it arrives with a slope |
| `LFO` | | `LFO_RATE` Hz, `LFO_AMPLITUDE` counts, `LFO_SHAPE` (`LFO_TRIANGLE`, `LFO_SINE`, `LFO_SAW`, `LFO_SQUARE`, `LFO_RANDOM`) | that shape, ±amplitude, [below](#lfo-shapes) |
| `NOISE` | | 0: amplitude in counts | white noise |
| `MIX` | up to eight | 0..7: a gain per input, `MIX_OFFSET` in counts | offset + the gains times the inputs; a gain that moved ramps across the block |
| `VCA` | audio, cv_a, cv_b | `VCA_GAIN`, `VCA_SELECT` (cv_a below 0.5, else cv_b) | audio times the chosen CV over `UNIT`, times the gain |
| `VCA` | audio, cv_a, cv_b | `VCA_GAIN`, `VCA_SELECT` (cv_a below 0.5, else cv_b), `VCA_CURVE` (`VCA_LINEAR`, `VCA_EXPONENTIAL`) | audio times the chosen CV over `UNIT`, times the gain; exponential is 60 dB over the CV, `10^(3·(cv/UNIT − 1))`, unity at `UNIT` |
| `OUT` | left, right | 0: scale | writes the run's output buffer: interleaved stereo, or the mean when the graph is mono |
| `PHASER` | audio | `PHASER_FREQUENCY` Hz, `PHASER_FEEDBACK` −0.99..0.99, `PHASER_MIX` 0..1; built with `arg` = stages (0 is 4) | audiobiquad's `AllPass` on one channel: first-order all-pass stages, one feedback around the chain, a dry/wet mix |
| `LAG` | note, pitch, legato | `LAG_TIME` s, `LAG_MODE` (`LAG_OFF`, `LAG_ON`, `LAG_AUTO`), `LAG_SHAPE` (`LAG_LINEAR`, `LAG_ONE_POLE`) | the note glided, with the bend (pitch − note) on top, [below](#glide) |
| `SAMPLE_HOLD` | signal, clock | `SH_LAG` coefficient 0..1, `SH_RATE` Hz | the signal held at each rising edge of the clock, or `SH_RATE` times a second with the clock open, [below](#sample-and-hold) |
| `TWOPOLE` | audio, cutoff CV | `TWOPOLE_TYPE` (`TWOPOLE_SK_LOWPASS`, `TWOPOLE_SK_HIGHPASS`, `TWOPOLE_CMOS_LOWPASS`, `TWOPOLE_CMOS_BANDPASS`, `TWOPOLE_CMOS_HIGHPASS`), `TWOPOLE_CUTOFF_HZ`, `TWOPOLE_DEPTH`, `TWOPOLE_RESONANCE` 0..1, `TWOPOLE_DRIVE` | a 12 dB/octave filter with its nonlinearity inside the loop, [below](#two-pole-filters) |
| `OVERDRIVE` | audio | `OVERDRIVE_DRIVE` 0..1, `OVERDRIVE_TONE` 0..1, `OVERDRIVE_LEVEL` (linear, 1 is unity) | `audiofilters.Distortion`'s soft-clipped overdrive, then a high shelf, [below](#overdrive) |

The kernel nodes take their parameters under the same numbers as the
modules that wrap them (`audiooscillator`, `audioladder`, `audiobiquad`,
Expand Down Expand Up @@ -135,6 +138,44 @@ input open and `SH_RATE` set, the node refreshes that many times a second
instead, through audioshaper's `SampleHold` kernel (whole hertz, exact for
ever); with neither, it holds.

### Two-pole filters

`TWOPOLE` is three filters that keep their character in a clipper inside the
feedback loop, so a biquad cannot stand in for them: the Sallen-Key low-pass
(`TWOPOLE_SK_LOWPASS`) and high-pass (`TWOPOLE_SK_HIGHPASS`), whose peak feedback
runs through a diode clipper and whose output goes through `tanh`; and the CMOS
state-variable filter, with a clipping inverter in every stage, as a low-pass,
band-pass or high-pass (`TWOPOLE_CMOS_*`). They are vrack's `SallenKeyLPFCore`,
`SallenKeyHPFCore` and `CMOSFilterCore`, one node with a type so a patch can
change the type of a running filter and the cutoff code is one function.

`TWOPOLE_RESONANCE` is the Sallen-Key's peak or the CMOS filter's resonance,
both 0..1. `TWOPOLE_DRIVE` multiplies the input before the filter. The cutoff
is `TWOPOLE_CUTOFF_HZ` times two to the power of the cutoff CV over `UNIT`
times `TWOPOLE_DEPTH` times ten, so a depth of 1 is an octave per 1638.4 counts
of CV, 0 ignores the input, and the result is held between 20 Hz and 0.45 of
the sample rate. The CV is read at the block's first and last samples, and the
filter's coefficient walks in a straight line between them, as these filters
have always taken it: what lies between those two samples does not move the
cutoff. An open CV input leaves the cutoff where it is.

### Overdrive

`OVERDRIVE` is `audiofilters.Distortion` in overdrive mode with soft clipping,
followed by a high shelf at 1.5 kHz, the way vrack's `OverdriveCore` chains them.
`OVERDRIVE_DRIVE` 0..1 sets a pre-gain of up to 20 dB, `OVERDRIVE_LEVEL` is the
linear gain after the clipper, and `OVERDRIVE_TONE` 0..1 is the shelf's gain,
from −12 dB through flat at 0.5 to +12 dB. The shelf is `synthio.Biquad`'s: the
node runs CircuitPython's own Q15 arithmetic, as `audiofilters.Filter` does, and
squeezes a sample past ±28000 toward the rail as that filter's last step does,
so a loud signal tops out at 28521.

CircuitPython's `audiograph` has no `OVERDRIVE`. The node computes its gains
in double precision, and the RP2040 build has no double-precision `pow` or
`log10` to link against. Check with `hasattr(audiograph, "OVERDRIVE")`. On
CircuitPython, `audiofilters.Distortion` and `synthio.Biquad` are already built
in, and you can chain them yourself.

## Output

`run(out, block, enable)` writes the `OUT` node's block into `out` at block
Expand Down Expand Up @@ -233,16 +274,22 @@ output reads, makes it fail; CI runs the faults first.
interpreter you name, and compares every sample. `graph_parity_probe.py` is one
patch with the runner's first node kinds in it (two voices with their groups
parking, a fed buffer, moved gains and cutoffs, a delay reset mid-tail, a mono
graph); `graph_nodes_probe.py` is the phaser, the glide, every LFO shape and
the sample-and-hold. The tolerance is 0 counts: the three compile the same C
graph); `graph_nodes_probe.py` is the phaser, the glide, every LFO shape, the
sample-and-hold, the five two-pole types and the overdrive. The tolerance is 0 counts: the three compile the same C
and are handed the same floats, so any difference is a defect rather than
rounding. Each planted fault makes it fail: one input sample one count off, one
`set()` a block late, a phaser's frequency a block late, a legato flag flipped,
a shape switched a block late, a clock edge a sample late. CI runs it against
a shape switched a block late, a clock edge a sample late, a cutoff CV a block
late, an overdrive's tone a block late. CI runs it against
MicroPython in both float widths; the three-way with the CircuitPython unix
build is a local run.

[`tests/test_cpython_audiograph_nodes.py`](../../tests/test_cpython_audiograph_nodes.py) holds the newer nodes to what they
stand in for: `PHASER` to `audiobiquad.AllPass` at 0 counts, the glide, the
LFO's shapes and the sample-and-hold to the Python cores of the same name in
vrack, with each bar in the file's table.
LFO's shapes, the sample-and-hold and the two-pole filters to the Python cores of
the same name in vrack, and `OVERDRIVE` to `OverdriveCore`'s own chain of
`audiofilters` nodes, with each bar in the file's table. The CMOS filter and
the overdrive match at 0 counts. The Sallen-Key pair matches within one count,
on under one sample in a hundred: the node's `tanh` is a rational function in
float that is the same on every target, where vrack's is the C library's in
double.
1 change: 1 addition & 0 deletions micropython.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ target_sources(usermod_mpaudio INTERFACE
${MPAUDIO_SRC_DIR}/audiosampler/SamplerVoice.c
${MPAUDIO_SRC_DIR}/audiosampler/module.c
${MPAUDIO_SRC_DIR}/shared/audiodsp_graph.c
${MPAUDIO_SRC_DIR}/shared/audiodsp_graph_overdrive.c
${MPAUDIO_SRC_DIR}/audiograph/Graph.c
${MPAUDIO_SRC_DIR}/audiograph/module.c
)
Expand Down
1 change: 1 addition & 0 deletions micropython.mk
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ SRC_USERMOD_C += \
# from the kernels above. MicroPython only (docs/how-it-works/audiograph.md).
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_graph.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_graph_overdrive.c \
$(MPAUDIO_SRC_DIR)/audiograph/Graph.c \
$(MPAUDIO_SRC_DIR)/audiograph/module.c

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"src/shared/audiodsp_sampler.c",
"src/shared/audiodsp_meter.c",
"src/shared/audiodsp_graph.c",
"src/shared/audiodsp_graph_overdrive.c",
# Xiph's Tremor, included into this one file from
# third_party/tremor; docs/reference/vorbis.md.
"src/shared/audiodsp_vorbis.c",
Expand Down
18 changes: 18 additions & 0 deletions src/audiograph/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PHASER), MP_ROM_INT(AUDIODSP_GRAPH_PHASER) },
{ MP_ROM_QSTR(MP_QSTR_LAG), MP_ROM_INT(AUDIODSP_GRAPH_LAG) },
{ MP_ROM_QSTR(MP_QSTR_SAMPLE_HOLD), MP_ROM_INT(AUDIODSP_GRAPH_SAMPLE_HOLD) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE) },
{ MP_ROM_QSTR(MP_QSTR_ALWAYS), MP_ROM_INT(AUDIODSP_GRAPH_ALWAYS) },
{ MP_ROM_QSTR(MP_QSTR_UNIT), MP_ROM_INT(16384) },
// parameter indices of the new nodes; the kernels' nodes take their
Expand All @@ -39,6 +41,9 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_MIX_OFFSET), MP_ROM_INT(AUDIODSP_GRAPH_MIX_OFFSET) },
{ MP_ROM_QSTR(MP_QSTR_VCA_GAIN), MP_ROM_INT(AUDIODSP_GRAPH_VCA_GAIN) },
{ MP_ROM_QSTR(MP_QSTR_VCA_SELECT), MP_ROM_INT(AUDIODSP_GRAPH_VCA_SELECT) },
{ MP_ROM_QSTR(MP_QSTR_VCA_CURVE), MP_ROM_INT(AUDIODSP_GRAPH_VCA_CURVE) },
{ MP_ROM_QSTR(MP_QSTR_VCA_LINEAR), MP_ROM_INT(AUDIODSP_GRAPH_VCA_LINEAR) },
{ MP_ROM_QSTR(MP_QSTR_VCA_EXPONENTIAL), MP_ROM_INT(AUDIODSP_GRAPH_VCA_EXPONENTIAL) },
{ MP_ROM_QSTR(MP_QSTR_LFO_SHAPE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_SHAPE) },
{ MP_ROM_QSTR(MP_QSTR_LFO_TRIANGLE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_TRIANGLE) },
{ MP_ROM_QSTR(MP_QSTR_LFO_SINE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_SINE) },
Expand All @@ -55,6 +60,19 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_LAG_ONE_POLE), MP_ROM_INT(AUDIODSP_GRAPH_LAG_ONE_POLE) },
{ MP_ROM_QSTR(MP_QSTR_SH_LAG), MP_ROM_INT(AUDIODSP_GRAPH_SH_LAG) },
{ MP_ROM_QSTR(MP_QSTR_SH_RATE), MP_ROM_INT(AUDIODSP_GRAPH_SH_RATE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_TYPE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_TYPE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CUTOFF_HZ), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CUTOFF_HZ) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_DEPTH), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_DEPTH) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_RESONANCE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_RESONANCE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_DRIVE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_DRIVE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_SK_LOWPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_SK_LOWPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_SK_HIGHPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_SK_HIGHPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_LOWPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_LOWPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_BANDPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_BANDPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_HIGHPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_HIGHPASS) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_DRIVE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_DRIVE) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_TONE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_TONE) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_LEVEL), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_LEVEL) },
// the kernels' option numbers, so a caller needs no other module
{ MP_ROM_QSTR(MP_QSTR_OSC_FREQUENCY), MP_ROM_INT(AUDIODSP_OSCILLATOR_OPT_FREQUENCY) },
{ MP_ROM_QSTR(MP_QSTR_OSC_WAVEFORM), MP_ROM_INT(AUDIODSP_OSCILLATOR_OPT_WAVEFORM) },
Expand Down
2 changes: 1 addition & 1 deletion src/circuitpython_spike/shared-bindings/audiograph/Graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ static int16_t buffer_id(mp_obj_t obj) {
static mp_obj_t audiograph_graph_add(size_t n_args, const mp_obj_t *args) {
audiograph_graph_obj_t *self = graph_self(args[0]);
mp_int_t kind = mp_obj_get_int(args[1]);
if (kind < 0 || kind >= (mp_int_t)AUDIODSP_GRAPH_KINDS) {
if (kind < 0 || !audiodsp_graph_kind_built((audiodsp_graph_kind_t)kind)) {
mp_raise_ValueError(MP_ERROR_TEXT("unknown node kind"));
}
mp_int_t group = mp_obj_get_int(args[2]);
Expand Down
23 changes: 23 additions & 0 deletions src/circuitpython_spike/shared-bindings/audiograph/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "shared-bindings/audiograph/__init__.h"
#include "shared-bindings/audiograph/Graph.h"
#include "shared/audiodsp_graph_overdrive.h"

//| """The graph runner: a whole patch rendered by one C call per block
//|
Expand Down Expand Up @@ -39,6 +40,10 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PHASER), MP_ROM_INT(AUDIODSP_GRAPH_PHASER) },
{ MP_ROM_QSTR(MP_QSTR_LAG), MP_ROM_INT(AUDIODSP_GRAPH_LAG) },
{ MP_ROM_QSTR(MP_QSTR_SAMPLE_HOLD), MP_ROM_INT(AUDIODSP_GRAPH_SAMPLE_HOLD) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE) },
#if AUDIODSP_GRAPH_HAS_OVERDRIVE
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE) },
#endif
{ MP_ROM_QSTR(MP_QSTR_ALWAYS), MP_ROM_INT(AUDIODSP_GRAPH_ALWAYS) },
{ MP_ROM_QSTR(MP_QSTR_UNIT), MP_ROM_INT(16384) },
// parameter indices of the new nodes; the kernels' nodes take their
Expand All @@ -52,6 +57,9 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_MIX_OFFSET), MP_ROM_INT(AUDIODSP_GRAPH_MIX_OFFSET) },
{ MP_ROM_QSTR(MP_QSTR_VCA_GAIN), MP_ROM_INT(AUDIODSP_GRAPH_VCA_GAIN) },
{ MP_ROM_QSTR(MP_QSTR_VCA_SELECT), MP_ROM_INT(AUDIODSP_GRAPH_VCA_SELECT) },
{ MP_ROM_QSTR(MP_QSTR_VCA_CURVE), MP_ROM_INT(AUDIODSP_GRAPH_VCA_CURVE) },
{ MP_ROM_QSTR(MP_QSTR_VCA_LINEAR), MP_ROM_INT(AUDIODSP_GRAPH_VCA_LINEAR) },
{ MP_ROM_QSTR(MP_QSTR_VCA_EXPONENTIAL), MP_ROM_INT(AUDIODSP_GRAPH_VCA_EXPONENTIAL) },
{ MP_ROM_QSTR(MP_QSTR_LFO_SHAPE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_SHAPE) },
{ MP_ROM_QSTR(MP_QSTR_LFO_TRIANGLE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_TRIANGLE) },
{ MP_ROM_QSTR(MP_QSTR_LFO_SINE), MP_ROM_INT(AUDIODSP_GRAPH_LFO_SINE) },
Expand All @@ -68,6 +76,21 @@ static const mp_rom_map_elem_t audiograph_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_LAG_ONE_POLE), MP_ROM_INT(AUDIODSP_GRAPH_LAG_ONE_POLE) },
{ MP_ROM_QSTR(MP_QSTR_SH_LAG), MP_ROM_INT(AUDIODSP_GRAPH_SH_LAG) },
{ MP_ROM_QSTR(MP_QSTR_SH_RATE), MP_ROM_INT(AUDIODSP_GRAPH_SH_RATE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_TYPE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_TYPE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CUTOFF_HZ), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CUTOFF_HZ) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_DEPTH), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_DEPTH) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_RESONANCE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_RESONANCE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_DRIVE), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_DRIVE) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_SK_LOWPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_SK_LOWPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_SK_HIGHPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_SK_HIGHPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_LOWPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_LOWPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_BANDPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_BANDPASS) },
{ MP_ROM_QSTR(MP_QSTR_TWOPOLE_CMOS_HIGHPASS), MP_ROM_INT(AUDIODSP_GRAPH_TWOPOLE_CMOS_HIGHPASS) },
#if AUDIODSP_GRAPH_HAS_OVERDRIVE
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_DRIVE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_DRIVE) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_TONE), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_TONE) },
{ MP_ROM_QSTR(MP_QSTR_OVERDRIVE_LEVEL), MP_ROM_INT(AUDIODSP_GRAPH_OVERDRIVE_LEVEL) },
#endif
// the kernels' option numbers, so a caller needs no other module
{ MP_ROM_QSTR(MP_QSTR_OSC_FREQUENCY), MP_ROM_INT(AUDIODSP_OSCILLATOR_OPT_FREQUENCY) },
{ MP_ROM_QSTR(MP_QSTR_OSC_WAVEFORM), MP_ROM_INT(AUDIODSP_OSCILLATOR_OPT_WAVEFORM) },
Expand Down
Loading
Loading