What it does
Marbles makes decisions for a patch. Its T section is a clock and two coin-flippers: T2 ticks, and on each tick T1 or T3 (or both, or neither) fire, depending on the mode and the bias. Its X section draws three voltages, X1–X3, each time its clock ticks, from a distribution SPREAD and BIAS shape, optionally quantized to a scale. Y is a fourth, slower voltage. DEJA VU decides how often a value is new and how often it is one of the last LENGTH values again.
Unmodified TGenerator and XYGenerator
Emilie Gillet's generator code, byte-identical to pichenettes/eurorack at 08460a6, at the
original's 32 kHz. This firmware maps the Alchemy Lab's pots, buttons and jacks onto it,
using the scaling from Marbles' own cv_reader.cc.
A CV and gate module
No audio. Seven of the ten jacks are outputs: X1–X3 and Y on J3–J6, the T gates on J7, J9 and J10. J1 and J2 take clocks, and J8 is the one CV input.
Six pots on two pages
PLAY holds the eleven-knob module's six most-used knobs. Hold B3 for SETUP: steps, length and the modes Marbles set with buttons. Three Settings pages hold the hidden options, the Y section among them.
The panel
Every hole is where it sits on an Alchemy Lab. The coordinates come from the SDK's own front-panel template, and the drawing is generated from them. Pick a page, then click a pot, a button or a jack.
The panel
The jacks
The Alchemy Lab has three ways to put a voltage on a jack. Marbles uses all three and gives the fastest to the T gates: the codec's DC-coupled outputs run per sample. The X voltages go through the slower I²C converter, where a millisecond does no harm to a stepped voltage.
| Jack | Role | Path | Timing |
|---|---|---|---|
| J1 | t CLOCK in: T follows this clock | codec input, AC-coupled | edge per sample, on above +1.0 V, off below +0.5 V |
| J2 | X CLOCK in: X steps on this clock | codec input, AC-coupled | same |
| J3 · J4 · J5 | X1 · X2 · X3 | MCP4728 DAC over I²C | staged when a value moves ≥ 2.4 mV, one flush per 1 ms poll (~0.45 ms of bus) |
| J6 | Y | MCP4728 DAC over I²C | same |
| J7 | T3 | STM32 DAC | once per 16-sample block (0.5 ms) |
| J8 | CV in, default RATE at 1 V/oct | ADC | once per block |
| J9 | T1 | codec output, DC-coupled | per sample |
| J10 | T2, the master clock | codec output, DC-coupled | per sample |
The clock gets the clean path
T2 is Marbles' steady master clock, the output most likely to clock other modules, so it rides the only sample-accurate path. T1 sits next to it on the codec pair; T3 takes the STM32 DAC with 0.5 ms of jitter, which gates don't notice.
Gates leave 2 ms late
X reaches its jack up to ~2 ms after it is computed. The T gates are held back 64 samples (2 ms) so a sample-and-hold or envelope fired by T1 sees the new X1, as on the original. The original held them back 2 samples. Relative to J1's clock, T is 2 ms late.
Edges instead of switches
Marbles knew a cable was in J1/J2 from the jack's switch. The Lab can't tell, so a clock input counts as patched while edges arrive (the last within 3 s). Settings can force the internal clock or pick X's clock explicitly.
Check X against a tuner before playing melodies
The SDK rates J3–J8 at about ±50 mV in output mode, because their calibration was measured with the output switch open. At 1 V per octave that is about ±60 cents: fine for modulation, possibly out of tune into an oscillator. J1 and J2 are AC-coupled, so a long held gate sags and releases early; clocks and triggers are fine. Gates on J9/J10 are written as 0 V and +4.995 V; their real levels haven't been measured.
The loop, live
Turn DEJA VU to noon and lock a button: the gates and the voltages repeat exactly every LENGTH steps. Turn it left for new values, right to shuffle the loop. The random sequence, the coin-toss, independent, three-state and drum models, the jitter and the quantizer are ported line for line from the vendored C++; the notes below list the one simplification.
Buttons · tap on / off, hold 2 s to lock
Play page
Setup page
What is ported, and the one simplification
Ported: RandomSequence::NextValue and NextVector (the
16-value loop, p = (2·deja vu − 1)², new values at or below noon, random jumps above it,
locked = 0.5 so p = 0); the four T models' bitmask code and drum patterns; the jitter
(JITTER⁴ × 36 semitones, with the phase-difference pull back to the straight clock); the
firmware's RATE curve and deja vu deadband; the loop-length table; the X modes' spread
and bias per channel; the degenerate and Bernoulli ends of SPREAD; and the quantizer's
weight thresholds with the six factory scales. X1, X2 and X3 each step on T1, T2 and T3,
the default X CLOCK.
Simplified: the X distribution. Marbles reads a precomputed inverse-CDF table; this page draws from a beta curve set by SPREAD and BIAS, so shapes are close, not identical. Left of noon, X STEPS eases to each value instead of running Marbles' lag processor. Clusters and Divider aren't simulated. The page uses the browser's random numbers; the module uses the STM32's hardware generator. The firmware and its native tests run the real code; this page is for getting a feel for it.
Settings
The options Marbles hid behind button combinations, including the whole Y section. Changes are saved with the rest of the state.
| Page | P1 | P2 | P3 | P4 | P5 | P6 |
|---|---|---|---|---|---|---|
| Main | Brightness | t RANGE: ×¼, ×1, ×4 | Preset slot | Preset action | J1 CLOCK: Auto, Ignore | X CLOCK: Auto, T1+T2+T3, T1, T2, T3, J2 |
| Firmware | FILE | FLASH | DFU | J8 CV TO: Rate, t Bias, t Jitter, Deja vu, X Spread, X Bias, X Steps, Length | EXTERNAL X: Off, On | GATE LENGTH |
| Y | Y SPREAD | Y BIAS | Y STEPS | Y DIVIDER: 1/64 … 1/1 (default 1/8) | Y RANGE: +2 V, +5 V, ±5 V | GATE JITTER |
- J8 CV TO. RATE takes exact 1 V/oct (12 semitones per volt, as Marbles' rate input). Any other destination moves its knob's whole travel over 10 V.
- EXTERNAL X. Marbles' external processing: X samples the voltage on J8 on each X clock and quantizes it to the scale. J8 then has no other destination.
- X CLOCK Auto uses J2 while edges arrive, otherwise T1 clocks X1, T2 clocks X2 and T3 clocks X3. J1 CLOCK Ignore keeps T on the internal clock whatever J1 receives.
- Y DIVIDER sets how many X steps pass before Y takes a new value.
- The firmware page's FILE, FLASH and DFU are the SD card picker.
On the card
Marbles carries the same SD card firmware picker as Mark, Belt, Smack and the other Mutable ports, so any of them can hand the module to any other without a computer.
FAT32, a folder named alchemy
Put marbles_alchemy.bin in /alchemy with the other firmwares.
On a Mac, run dot_clean on the card before ejecting so resource forks
don't show up as files.
Settings, firmware page
P1 FILE picks the file. On P2 FLASH, turn below 85 % and then all the way up: it compares, erases, writes, verifies and reboots. If that firmware is already loaded it writes nothing and just shows solid green.
A file on the card
Marbles keeps its working state in /alchemy/state/marbles.st and saves it 5 s
after the last change, once your hands are off the buttons. Switch away and back, and it
returns as you left it. Up to v0.1.0 it used slot 10; the first boot of v0.2.0 copies that
onto the card. The other Lab firmwares keep their own slots, 1–5 and 12–15, so 6–11 are free for presets you save by hand.
Build and install
Download the firmware, or build it from the repository, which holds all seven ports, then put it on the card or flash it over the front USB-C.
Download v0.2.0 · built and tested on a computer, not yet run on a module
marbles_alchemy.binSD card zip · all seven
The zip holds all seven firmwares and the Elements samples, laid out for the card: unzip it onto the card's top level and the .bin files land in /alchemy, elements.smp in /mi. Release notes and SHA-256 checksums.
git clone https://github.com/timncox/mi-alchemy cd mi-alchemy git submodule update --init git -C lib/libDaisy submodule update --init --recursive make libdaisy # once make FW=marbles # build-marbles/marbles_alchemy.bin, ~399 KB make test # the native suites for all four engines
The picker
Copy the .bin to /alchemy and flash it from any firmware in the family, as above.
Hold B3 at power-on
The bootloader serves DFU on the front USB-C. Flash with Hermetic's web programmer or dfu-util, pinned to your module with -S.
HostLink only in 0.1
The front USB-C talks to the web programmer. This version has no Launchpad or USB-audio mode, and it never changes the card's shared USB setting.
Custom firmware
Hermetic Modular and Mutable Instruments didn't write or test this port and can't support it. Please don't contact them about it; open an issue on the repository instead.
How it is built
| Piece | What it is |
|---|---|
| The engine | marbles::TGenerator, XYGenerator, the ramp extractor and the lookup tables, byte-identical to pichenettes/eurorack @ 08460a6, at 32 kHz in 16-sample blocks. |
marbles_engine.h | What marbles.cc's Process() did, without the hardware: clock edge detection, the parameter scaling from cv_reader.cc, the deja vu deadband, the loop-length quantizer, the X clock source and the 2 ms T delay. The native test runs this exact code. |
| Parameter scaling | RATE = pot × 120 − 60 semitones around 2 Hz; t BIAS × 1.05 − 0.025; X SPREAD and BIAS × 1.02 − 0.01; STEPS × 1.04 − 0.02; LENGTH through Marbles' 1–16 table. A one-pole filter smooths the knob values. |
| Scales | The six factory scales copied from settings.cc, which can't be compiled here because it includes the STM32F4 flash driver. |
| Random numbers | The STM32's hardware generator feeds Marbles' random stream from the 1 ms poll, as its own RNG driver did. |
| Working state | /alchemy/state/marbles.st (slot 10 up to v0.1.0): both pages, the Settings, the deja vu buttons and last session's CPU peak, schema-tagged so another firmware's state is never read as this one's. |
| Budget | 398,844 B image: SRAM 82.75 % of 480 KB, DTCM 83,880 B, D2 only the LED window. CPU on the module is unmeasured; Marbles ran on a 168 MHz STM32F405 with its whole UI. |
Left out
- Scale recording and per-note scale edits: the six factory scales only.
- Explicit reset mode, the self-patching detector (pick T1, T2 or T3 as X CLOCK instead), factory calibration and output-test modes, and the colour-blind LED scheme.
- The Markov T model, which the original firmware also keeps out of reach.
- CV on every knob: there is one CV input, J8, so that Y could keep its jack.
Status
The native test suite runs marbles_engine.h over the vendored
generators at 32 kHz with address and undefined-behaviour sanitizers. All of it passes.
Nothing below has been checked on a module.
Clocks and loops
RATE at noon gives 2.000 Hz, +12 semitones 4.000 Hz, J8 at +1 V 4.000 Hz. A 3 Hz clock on J1 gives 3.000 Hz at 1/1 and 12.000 Hz at ×4. Deja vu locked at LENGTH 2, 5 and 16 repeats X and T with no mismatches.
Voltages
X stays inside and fills the +2 V, +5 V and ±5 V ranges. Quantized X lands on the scale for all six scales. EXTERNAL X: +2 V on J8 gives 2.000 V on X1.
To check on a module, in order
- It boots from the picker, the P1 ring shows the CPU readout, B3 flashes with T2.
- T1 on J9, T2 on J10, T3 on J7; RATE at noon is 2 Hz.
- A clock into J1 takes over T; X CLOCK Auto follows a clock into J2.
- X1–X3 and Y on J3–J6, measured against a tuner or meter (the ±50 mV question).
- J8 into RATE at 1 V/oct.
- Whether the panel feels slow with smooth X (STEPS left of noon), which flushes the I²C DAC every millisecond and uses about half of the control loop.
- Settings survive switching to another firmware and back.