ITADN
shakfu/sk-engines
README.md

sk-engines: A Spotykach (platform/engine fork)

CI

One Spotykach, twenty-one instruments. This firmware turns the hardware into a platform: the panel, pads, knobs, clock, CV and SD card stay exactly as they are, and the DSP engine underneath is swappable. Flash a different .bin and the same box is a granular looper, a dub delay, a Plaits macro-oscillator, a tape deck, a resonator, or a Csound/ChucK interpreter you write patches for.

Quickstart — from nothing to a sound

1. Get a binary. Download a sk-<engine>-<version>.bin from the releases, or build one: make -j8 libs once, then make -j8 ENGINE=delay. (Need the bootloader? It ships as bootloader-spotykach-v2.bin.)

2. Flash it. Connect the rear USB-C port on the main PCB — not the one on the Seed — with a data-capable cable. Hold Reset for ~3 s until the bottom pads breathe white, then make ENGINE=delay program-dfu. The unit reboots into the new engine.

3. Card, if the engine needs one. Effects and synths (delay, reverb, reso, mosc, edrums, …) need nothing — skip this. Players and loopers (tape, radio, bard, shuttle, softcut, pstretch) read the card, and the firmware converts nothing, so a wrong-format file plays as noise instead of being rejected. Don't hand-build it:

make sdcard SDCARD_OUT=/media/SK     # a complete, correct card
make check-sdcard CARD=/media/SK     # or explain what's wrong with the one you have

A prebuilt sk-card-<version>.zip ships with each release, and web/ does all of this in a browser if you would rather not install Python.

4. Make a sound. Turn it on and read your engine's control map in docs/engines/ — each page has a knob-by-knob table and a control-surface diagram. For delay: play something in, SOS sets wet/dry, SIZE picks the musical division, POS is feedback. Everything shared by all engines — clock and sync, CV/gate, routing, config.txt, MIDI — is in the platform manual.

make help lists every build, test, card and release command.


A fork of the official Synthux Academy Spotykach firmware, restructured as a fixed hardware/UI platform with a swappable DSP engine architecture.

The hardware and interaction model remain constant across firmware variants: multi-function encoders with pickup behavior and LED ring feedback, pad gestures, transport controls, SD-card sample storage, CV/gate I/O, and MIDI support. Individual firmware builds replace only the DSP engine and its parameter set. Clocking and transport are also provided as shared platform services, allowing any engine to synchronize to the same internal, TS4, or MIDI clock sources.

The platform is intentionally decoupled from any specific engine. Core subsystems in src/hw/, src/ui/, src/memory/, and src/transport/ contain no engine-specific dependencies, and build-time checks enforce this separation (make check-boundary).

Current engines include:

  1. granular: dual-deck granular looper (the original reference spotykach firmware as an engine)

  2. delay: tempo-synchronized stereo delay with switchable characters (Clean / Tape / Shimmer on the Reel/Slice/Drift switch) and stereo topologies (Stereo / DoubleMono / Ping-pong on the route switch), plus a feedback tone control, a modulation LFO (chorus/flange/vibrato), a Play-pad freeze, and a Rev-pad reverse

  3. edrums: four-voice Euclidean drum machine with tweakable synthesized drums

  4. reso: resonator/plucked-string instrument based on the Mutable Instruments Rings DSP code

  5. tape: dual streaming tape deck (two independent record/playback decks, SD-streamed, no in-memory length cap)

  6. shuttle: buffer-based bipolar/reverse varispeed tape (four in-RAM tracks, PITCH as a capstan-speed knob - noon stops, CW forward, CCW reverse)

  7. reverb: route-aware stereo reverb with three all-Faust algorithms (Dattorro plate / Zita-rev1 hall / Greyhole, generated from Faust sources) - the Reel/Slice/Drift switch selects the algorithm, and DoubleMono routing runs an independent mono plate on each deck (the heavier hall/Greyhole stay single-voice in a stereo route, a CPU cap validated on hardware)

  8. gigaverb: stereo reverb authored in Max/MSP gen~ and translated to C++ via gen-dsp (Tom Erbe's gigaverb)

  9. radio: dual virtual RadioMusic - two independent virtual radios (one per deck) over a shared SD library of banks, with the signature free-running "virtual playhead" (each station seems to keep broadcasting while you are tuned elsewhere): PITCH = station tuning, POS = start offset, Alt+PITCH = bank, SIZE = varispeed, ENV = inter-station static, Play/gate = re-tune. Streams headerless raw 16-bit-mono .raw files (the original RadioMusic format, at 48 kHz)

  10. chorus: stereo chorus - the demo of the generated Faust path (author a .dsp + a small JSON manifest, no hand-written C++)

  11. filter: dual resonant filter, one independent voice per channel - the generated parallel (DoubleMono) dual-deck demo

  12. voice: drone oscillator (deck A) into a resonant filter (deck B) - the generated series (chain) dual-deck demo (instrument -> FX)

  13. passthrough: minimal stereo passthrough engine demonstrating the platform API

  14. csound: a full Csound 7 instance as a synth — the patch (a .csd orchestra) defines the sound. Load orchestras from the SD card and switch between them live with Alt+PITCH, and play them over MIDI. Unlike the other engines it is a QSPI build (Csound's ~2 MB of code is too big for SRAM) and needs a one-time libcsound.a cross-build (scripts/fetch_csound.sh)

  15. chuck: the ChucK language + VM as a synth — the patch (a .ck program) defines the sound, with strongly-timed concurrent voices. Load programs from the SD card and switch between them live with Alt+PITCH (memory-stable: each patch compiles once, then switches are instant). Like csound it is a QSPI build with a one-time libchuck.a cross-build (scripts/fetch_chuck.sh); MIDI is planned

  16. mosc: a dual macro-oscillator giving each deck a full 24-engine Mutable Instruments Plaits voice (virtual analog, FM, wavetable, granular, additive, chord, speech, modal, drums, …). PITCH = note, Alt+PITCH = engine select (24 models), SIZE/POS/ENV = harmonics/timbre/morph, MOD_AMT/MODFREQ = LPG decay/colour; the Mode switch picks Gate vs Drone and the Routing switch combines the two voices (Stereo / DoubleMono / a GenerativeStereo out+aux spread). A QSPI build (the 24-engine voice is too big for SRAM) but it needs no SD card or external library — the Plaits DSP is vendored in-tree. Cloned from the reso engine (same author, shared stmlib)

  17. softcut: a dual-deck crossfaded overdub looper on monome's softcut-lib (4 voices, 2 per deck). Unlike the buffer-tape engines, softcut plays and records the same loop at once with subsample-accurate click-free crossfades and interpolated overdub — layering live input onto a running loop with an ENV-knob feedback control (sound-on-sound). It's a record-defines-loop looper: on an empty voice the first Alt+Play records a fresh take and the second closes the loop at the length you played; a voice with content overdubs on top. PITCH = bipolar varispeed/reverse, POS/SIZE = loop window, ENV = overdub feedback, the FLUX pad = a sweepable filter, MOD_AMT/MODFREQ = loop crossfade time / rate slew; Seq realigns all voices (click-free cutToPos sync), Alt+PITCH loads loop clips from SD, and Alt+Seq / Alt+Rev save the full take / trimmed loop back to the card. A normal SRAM build (the softcut DSP is tiny); 4 voices is the hardware-measured CPU budget, with 10.9 s buffers per voice. The vendored core lives in src/engine/softcut/vendor

  18. qdelay: a dub/ambient flavor of the delay (inspired by qdelay) — the same delay grammar (tempo-synced divisions, feedback, mix, PITCH, ENV tone, mod LFO, Freeze/Reverse pads, Stereo/DoubleMono/Ping-pong) with the character palette swapped to Clean / Diffuse / Duck (Reel/Slice/Drift switch). Diffuse runs the feedback through an 8-stage allpass diffuser (a JUCE-free port of qdelay's Diffusor) for a dense reverb-like wash; Duck attenuates the wet under the dry input so repeats bloom in the gaps. A normal SRAM build (the diffuser's buffers live in SDRAM)

Four further engines live in the tree and build the same way, but are not (yet) part of the numbered list above: bard (bookmark-navigated audiobook decks, SD-streamed), glitch (dual-deck lo-fi/circuit-bent noise voice, GPLv3), pstretch (real-time PaulStretch ambient time-smear), and graincloud (a granular-cloud variant of the looper).

At a glance

EngineTypeSD cardBuildAuthored inNotes
granularLooper / samplerOptional (save/load loops)SRAMC++Default build; the original firmware as an engine
graincloudLooper / samplerOptional (save/load loops)SRAM (-Os)C++The granular tree built with SPK_GRAIN_GF: a GrainflowLib cloud replaces the grain core
tapeLooper / recorderRequired (streams)SRAMC++Two SD-streamed decks, no length cap
shuttleLooperOptional (load slots)SRAMC++Four in-SDRAM tracks, bipolar varispeed
softcutLooper (overdub)Optional (load/save clips)SRAMC++Vendored monome softcut-lib, 4 voices
radioPlayer / instrumentRequired (streams)SRAMC++Dual virtual RadioMusic, .raw/.wav banks
bardPlayerRequired (streams)SRAM (-Os)C++Audiobook decks; not yet hardware-tested
delayEffectSRAMC++Clean / Tape / Shimmer characters
qdelayEffectSRAMC++Clean / Diffuse / Duck; GPLv3
reverbEffectSRAM (-Os)FaustPlate / hall / Greyhole, route-aware
gigaverbEffectSRAMgen~Tom Erbe's gigaverb via gen-dsp
chorusEffectSRAMFaustGenerated single-deck demo
filterEffectSRAMFaustGenerated parallel (DoubleMono) demo
pstretchEffectOptional (file source)SRAMC++Clean-room PaulStretch, vendored FFT
edrumsInstrumentSRAMC++Four-voice Euclidean drum machine
resoInstrument (+ resonator)SRAM (-Os)C++Mutable Instruments Rings DSP
moscInstrumentQSPIC++Dual 24-engine Plaits voice, vendored in-tree
glitchInstrumentSRAMC++12 Noisferatu algorithms; GPLv3
voiceInstrumentSRAMFaustGenerated series (osc → filter) demo
csoundInstrument (scriptable)Optional (.csd patches)QSPIC++ host + CsoundNeeds a one-time libcsound.a cross-build
chuckInstrument (scriptable)Optional (.ck patches)QSPIC++ host + ChucKNeeds a one-time libchuck.a cross-build
passthroughUtilitySRAMC++Minimal stereo passthrough

Build is where the code executes: SRAM is the normal make ENGINE=<name> build; QSPI engines are too large for the 186 KB execution SRAM and run from external flash via their own one-shot target (make engine-<name>). SD card marks whether the card is required at runtime, only used for optional load/save, or unused. -Os notes engines built size-optimized to fit.

Engines can be authored in three ways:

  1. Using C++ against IEngine

  2. Using Faust (via cyfaust)

  3. Using Max/MSP's gen~ language (via gen-dsp)

The latter two generate C++ that the platform wraps behind the same contract. The three methods are documented in docs/engine-types/.

Originally started as a feature-extension fork of the upstream firmware, the project evolved into a platform/engine architecture that enables new instruments to reuse the existing hardware and interaction language rather than reimplement them. See docs/architecture.md for an overview of the design and instructions for creating new engines.

Setup

Clone the repo recursively or run git submodule update --init --recursive to update submodules (libDaisy + DaisySP)

Note that the ws2812 driver requires a slight modification to libDaisy, as such the libDaisy submodule commit ref points at a specific branch within the bleeptools fork (which is based on Infrasonic Audio fork), containing a few other modifications as well, namely within the MIDI and mpr121 classes.

Compiling the Firmware

First you must build the libraries. For convenience there is a target in the Makefile for this, so you simply need to run the following command from a terminal (in the root directory of this repository):

make -j8 libs

Note: The -j8 flag runs make with 8 parallel jobs, feel free to change the number. It works best if you use the number of cores (including hyperthread cores) on your machine.

Then, build the actual firmware code:

make -j8

If successful the compiled binaries will end up in the build/ directory along with many other intermediate build files:

spotykach.bin
spotykach.elf

The .elf file is mainly used for debugging. The .bin file is the one that the DFU utilities will flash onto the Seed.

Build options

The firmware is a fixed hardware/UI platform that hosts a swappable DSP engine, chosen at build time with the ENGINE variable:

  • make -j8 — the granular looper (default; ENGINE=granular).

  • make -j8 ENGINE=delay — a tempo-synced stereo delay (musical divisions, feedback, pitch-shifted taps) with Clean / Tape / Shimmer characters (Reel/Slice/Drift switch), Stereo / DoubleMono / Ping-pong topologies (route switch), a feedback tone control on ENV, a delay-time modulation LFO (MODFREQ/MOD_AMT → chorus/flange/vibrato), a Freeze hold on the Play pad, and a Reverse on the Rev pad.

  • make -j8 ENGINE=qdelay — a dub/ambient flavor of the delay (inspired by qdelay): the same control grammar with the character palette swapped to Clean / Diffuse / Duck (Reel/Slice/Drift switch). Diffuse runs the feedback through an 8-stage allpass diffuser (a JUCE-free port of qdelay's Diffusor) for a dense, reverb-like wash; Duck attenuates the wet under the dry input so the repeats bloom in the gaps. Reuses the delay's tempo sync, PITCH transpose, ENV tone, mod LFO, Freeze/Reverse pads and the three topologies. A normal SRAM build (the diffuser's buffers live in SDRAM).

  • make -j8 ENGINE=edrums — a four-drum Euclidean drum machine (two drums per deck, Rev-pad swaps the editable one; polymeter, live model select) with synthesized voices you shape live: per-drum gain, decay, and grit/flux macros for drive, pitch-sweep, brightness, and body↔noise balance.

  • make -j8 ENGINE=reso — a resonator/pluck voice on the Mutable Instruments Rings DSP (modal / sympathetic-string / string / FM / string+reverb models on Alt+PITCH; three excite modes — discrete plucks, live-input resonator, scatter cloud). Vendored Rings/stmlib live under src/engine/reso/thirdparty/.

  • make -j8 ENGINE=tape — two independent mono tape decks (A/B) that play and record arbitrarily long takes to the SD card, removing the in-SDRAM loop-length cap. Per deck: Play pad = play, Alt+Play = record, PITCH = varispeed, Alt+POS = pan, MIX = volume, ENV = loop mode (none / loop / faded / Frippertronics), Alt+PITCH = tape-slot select (8 slots under /tapes/); the routing switch and mix fader place/blend the two decks. Streams mono float WAV through lock-free per-deck SDRAM rings drained by a main-loop FatFs pump.

  • make -j8 ENGINE=shuttle — a buffer-based bipolar/reverse varispeed tape: four in-RAM mono tracks (two per deck), all playing at once. PITCH is a capstan-speed knob (noon = stop, clockwise = forward to +2x, counter-clockwise = reverse to -2x); the Play pad snaps the focused track to unity. POS/SIZE set a per-track loop window, Alt+PITCH loads a /tapes/ slot into RAM, the Rev pad swaps a deck's focused track, and the Seq pad re-aligns all four tracks to a common downbeat (declicked). Random-access in-SDRAM buffers (30 s/track) trade unbounded length for trivial reverse/freeze/looping.

  • make -j8 ENGINE=radio — a dual virtual RadioMusic: two independent virtual radios (one per deck) browsing a shared /radio/<bank>/ SD library, blended by the crossfader and routing switch. Its signature is the free-running virtual playhead — every station seems to keep broadcasting while you are tuned elsewhere (a per-deck frame clock seeks each opened station to (clock + START) mod length). PITCH tunes stations (+ V/oct CV), POS sets the start offset (+ CV), Alt+PITCH picks the bank, SIZE adds 0.5–2x varispeed, ENV brings in inter-station static, Play pad / gate-in re-tune. Streams 16-bit-mono .raw (headerless, the original RadioMusic format) or .wav (self-describing, carries its own rate) — mix freely in a bank; convert with scripts/convert_radio_audio.py.

  • make -j8 ENGINE=reverb — a route-aware stereo reverb with three all-Faust algorithms (a Dattorro plate, a Zita-rev1 hall, and a Greyhole, the Reel/Slice/Drift switch selects live), generated from Faust sources by cyfaust. DoubleMono routing runs an independent mono plate per deck; the heavy hall/Greyhole are single-voice (stereo-route only), a cap that keeps two delay-line-heavy voices off the SDRAM bus at once. Built at -Os (Greyhole is the heaviest voice). Regenerate the kernels with make faust-kernels.

  • make -j8 ENGINE=gigaverb — a stereo reverb (Tom Erbe's gigaverb) authored in Max/MSP gen~ and translated to C++ by gen-dsp. The engine directory is generated from a gen~ export with make gen-engines (or scripts/gen_engine.py); see docs/engine-types/gen.md.

  • make -j8 ENGINE=chorus / filter / voice — engines generated from a Faust .dsp + a small JSON manifest with no hand-written C++ (make faust-engine MANIFEST=…): chorus (a stereo chorus, the single-deck demo), filter (a resonant filter per channel, the parallel dual-deck demo), and voice (a drone oscillator into a filter, the series dual-deck demo). See docs/engine-types/faust.md.

  • make -j8 ENGINE=passthrough — a minimal stereo-passthrough variant.

  • make engine-csound — a full Csound 7 synth, where the patch defines the sound: load .csd orchestras from the SD card, switch between them live with Alt+PITCH, and play over MIDI. This one is special — a QSPI build (Csound's ~2 MB of code is too big for SRAM, so it executes from flash), built with its own one-shot target rather than ENGINE=. It needs a one-time scripts/fetch_csound.sh to fetch + cross-build libcsound.a. See docs/engines/csound.md. Ready-to-copy example orchestras are in examples/csound/.

  • make engine-chuck — the ChucK language + VM as a synth, where the patch defines the sound: load .ck programs from the SD card and switch between them live with Alt+PITCH. Like csound it is a QSPI build with its own one-shot target and a one-time scripts/fetch_chuck.sh to cross-build libchuck.a. Patches compile once and are cached, so live switching is instant and memory-stable. See docs/engines/chuck.md; example programs in examples/chuck/.

  • make engine-mosc — a dual macro-oscillator: each deck is a full 24-engine Mutable Instruments Plaits voice (PITCH = note, Alt+PITCH = engine select, SIZE/POS/ENV = harmonics/timbre/morph; Mode = Gate/Drone, Routing = Stereo / DoubleMono / GenerativeStereo). Like csound/chuck it is a QSPI build (the 24-engine voice is too big for SRAM) with its own one-shot target — but it needs no SD card or library fetch: the Plaits DSP is vendored in-tree. See docs/engines/mosc.md.

Switching ENGINE does not require make clean. Other build flags: DEBUG=1 (enables UART logging) and LOFI_INT16=1 (16-bit loop buffer, doubling record time). See docs/architecture.md for the platform/engine design, docs/engines/ for a per-engine reference, and docs/engine-types/ for the three ways to author an engine (native C++, Faust, gen~).

There is also an opt-in CMake build (an in-progress alternative; the make build above stays canonical): make -f Makefile.cmake ENGINE=<engine> configures and builds via CMake, with output in build-cmake/<engine>/ instead of build/. It mirrors the same commands (program-dfu, engine-<name>, DEBUG=1, LOFI_INT16=1) and caches each engine in its own dir, so switching engines never forces a rebuild.

Editor tooling (clangd)

The repo's includes (the libDaisy header set, -Isrc, the build-time -DSPK_ENGINE_* define) aren't discoverable by clangd on their own, so generate a compile_commands.json from a real build:

bear -- make -j8

It is git-ignored and is a snapshot of whichever ENGINE you built (granular by default) — regenerate after adding files or changing flags.

Flashing the Firmware

The bootloader version used in this project enables USB DFU firmware updating from the external USB port - i.e. the USB-C port on the rear of the main PCB, NOT the one on the Seed. Application firmware can only be flashed using the USB-C port.

  1. Compile the firmware using the steps above

  2. Connect the USB-C connector on the main PCB to the computer (ensure the cable is not power-only)

  3. Hold the Reset button on the back of the unit for ~3 seconds. The leds under bottom pads going to "breathe" in white.

  4. Run the command make program-dfu from a terminal

make program-dfu flashes whatever is currently in build/ (it does not rebuild). To flash a non-default engine, build it first in the same step, e.g. make ENGINE=passthrough && make program-dfu.

For convenience there are one-shot targets that clean + build + flash a variant (put the device in DFU mode first, as in step 3): make engine-granular (the looper), make engine-delay, make engine-qdelay, make engine-edrums, make engine-reso, make engine-tape, make engine-shuttle, make engine-radio, make engine-reverb, make engine-chorus, make engine-filter, make engine-voice, make engine-gigaverb, and make engine-passthrough. (make engine-csound, make engine-chuck, and make engine-mosc also exist but are QSPI builds — csound/chuck need a one-time libcsound.a / libchuck.a prerequisite, mosc needs none — see docs/engines/csound.md, docs/engines/chuck.md, docs/engines/mosc.md.)

Once finished, the device will automatically boot the new firmware. This can "brick" (temporarily) the device and require reinstallation of either the bootloader, the firmware binary, or both.

The SD card

Ten engines read the card, using nine folder layouts and four incompatible audio formats — and the firmware converts nothing, so a wrong-format file plays as noise rather than being rejected. You do not have to learn that: build a correct card, and check one that misbehaves.

make sdcard SDCARD_OUT=/media/SK          # build a complete card (folders, configs, patches, demo audio)
make check-sdcard CARD=/media/SK          # explain anything that will not work, with the fix
python3 scripts/sk_card.py convert --engine tape /media/SK loop.mp3   # add your own audio

A prebuilt sk-card-<version>.zip ships with each release. See docs/sd-card.md.

All three commands also exist as a browser page in web/, for when a checkout, Python and a working decoder are more than you want to install to put a file on a card — the browser decodes mp3/flac/wav/ogg itself. make web-serve, then open http://localhost:8000. It reads the same rules the CLI does, exported as data rather than reimplemented, and the same page carries a WebSerial terminal for TERMINAL=1 builds. See web/README.md.

Testing

There are two independent suites: one that runs on your machine, and one that drives a flashed device.

Both, plus a build of every engine, run in CI: 22 firmware builds through the canonical make path (20 engines, and TERMINAL=1 on two of them because that flag changes type layout), the four off-target suites, and a CMake build of the six engines whose flags are not vanilla — the only thing keeping the opt-in CMake path from drifting away from the Makefile. csound and chuck each have to cross-build a large runtime first, so they sit in a separate workflow (qspi-libs.yml).

Both workflows are currently workflow_dispatch only — run them from the repository's Actions tab. The push/PR and weekly triggers are written out and commented in each file, to be enabled once a manual run has gone green on a real runner. Until then the automatic safety net is off, so make test locally is still the thing standing between a change and a broken engine.

Off-target (no hardware)

The engines and the platform's hardware-free layers compile for the host against a small <daisy.h> shim, so most DSP and all of the control-plane logic is testable without a device:

make test              # all four suites + the boundary guard (this is what CI runs)

make -C host test      # engine + DSP suites (delay, tape, reso, granular, csound, the terminal codec, ...)
make -C test test      # small standalone unit tests (wav, config, dividers, ...) - 116 checks
make test-scripts      # the Python host tooling (SD card rules, release packaging, converters)
make test-web          # the browser front-end in web/ (node or bun; no npm install)

make test-scripts and make test-web are two halves of one contract: the browser app reads the SD card rules as data exported from scripts/card_layout.py, so the Python side fails if the committed export has drifted, and the JS side fails if its WAV writers or its card checker disagree with the Python they mirror.

On-target (a flashed device)

TERMINAL=1 builds in a bidirectional text/command channel over the rear USB-C port — the same port used for DFU — which lets a host script drive an engine and assert on what it reports. It is opt-in and costs nothing when off: the terminal translation units compile to zero bytes and a build without the flag is byte-identical.

make -j8 ENGINE=delay TERMINAL=1     # then flash as described above
make test-hw                         # pytest harness over USB-C

make test-hw needs pyserial and pytest; it picks up a project virtualenv (.venv) if there is one, and skips cleanly when no device is attached, so it is safe to leave in a pipeline. With a device it drives every parameter the engine declares, across both decks, inside an input-isolated mode where knobs, CV, gate and switches cannot perturb the run.

The tests are generic: they are driven entirely by the device's own describe output, so one file tests every engine build. Nothing is hardcoded per engine.

For poking at a device by hand there is a REPL:

python tools/skterm.py
sk> describe                 # the device's whole control surface
sk> set param feedback A 0.75
sk> get param feedback A     # -> ok 0.7500
sk> query empty A            # engine state

The channel and its design are documented in docs/dev/terminal-control.md (what and why), docs/dev/terminal-dispatch.md (the command grammar), docs/dev/terminal-tools.md (the host side) and docs/dev/terminal-impl.md (what actually landed, including the hardware bring-up history).

Architecture & developer docs

Firmware internals are documented under docs/ — start with docs/architecture.md, which covers the hardware platform, the platform/engine seam (IEngine), and how to slot in a new engine. docs/engines/ documents each engine in detail plus the shared transport and knob-routing model, and docs/engine-types/ covers the three engine-authoring methods (native C++, Faust/cyfaust, gen~/gen-dsp). Notable changes are tracked in CHANGELOG.md.

License

This project is MIT (see LICENSE), except the qdelay and glitch engines, which are GPLv3:

Every other engine and the platform itself remain MIT and do not include the GPLv3 code. Vendored third-party DSP keeps its own upstream license alongside the code (e.g. stmlib).