Mod

granola-openmpc

Rising54/100

Granola MPC - Multitrack granular synthesizer for the Akai MPC One (Gen 1)

granola-openmpc is a mod for the Akai Professional MPC One, by @nzimas. 2 stars on GitHub.

forAkai Professional MPC One·by@nzimas

Unclaimed. Is this yours? Sign in with GitHub as @nzimas to claim it.

Downloads
0
Stars
2
Forks
0
Commits
3
Last commit
Get the source

No release files yet. Build from source.

Custom license· nzimas/granola-openmpc·downloads come from the creator’s GitHub releases

3
Commits
0
Releases
3 commits and 0 releases in the last year
Activity · last 12 months

Granola MPC - Multitrack granular synthesizer for the Akai MPC One (Gen 1)

Foundational framework: OpenMPC

An independent application environment for the Akai MPC One (Gen 1, inmusic,acva). The unit boots directly into Granola MPC, with the stock Akai application disabled.

What Granola MPC is

A five-voice granular instrument that turns the MPC One into something it was never sold as. Each of the five tracks takes a sample and reads it not as a recording to be played back, but as a field of material to draw grains from — hundreds of short fragments a second, scattered around a playhead you move by hand, each one pitched, panned and shaped independently. Move the playhead slowly through a two-second sample and it can last for minutes; open the jitter and the same sample becomes a cloud with no obvious source.

Every track is a complete instrument in itself:

  • The grain cloud — size, density, jitter and drift, with a cap that keeps grain overlap inside what the board can actually sustain.
  • A multimode filter — lowpass, highpass or comb, four parameters each.
  • Nine independent LFOs, one per destination, any number running at once, from a cycle every three minutes to eight a second.
  • Four grain windows — gaussian, percussive, plateau and reverse, chosen per track, and modulatable, so the window steps between shapes as grains fire.
  • Parameter-lock pads — twelve slots per track. A slot holds the whole grain state: transposition, playhead and all four Q-Link values, so a row of pads is a sequence rather than a set of transpose buttons.
  • Gesture loops — hold RECORD and everything you touch is captured exactly as played, with no quantisation. Release and it loops, at its own length, drifting against the other tracks rather than locking to a bar.

Shared across all five: a delay and a reverb with per-track sends, a master chain of drive, three-band EQ and compression, and sixteen momentary Airwindows effects on the pads whose parameters are drawn fresh on every press.

Underneath the clouds there is a rhythm section — eight synthesised drum channels, each with its own sixteen-step pattern, its own clock division of the project tempo, and a die that writes it a fresh bar from six generative algorithms. The kick ducks the granular tracks. Holding one pad generates a break, built on the spot from a menu of transforms and different every time. And the grains themselves lock to the project tempo, so the clouds and the drums belong to the same bar rather than merely coexisting.

And when you have no sample worth granulating, Granola writes its own. The source generator composes a sequence — a real harmonic plan with phrases and cadences, not a random walk — and renders it through one of five engines: sampled orchestral instruments, a Yoshimi synthesiser, a DX7 clone with sixteen thousand voices, Mutable Instruments' Plaits, which generates its timbres rather than recalling them, or bytebeat, which is not played at all but computed — one integer expression on a sample counter. The result is committed to a track as granulation material, and the cycle begins again.

Work is kept in sixteen projects, each holding sixteen patterns — both complete snapshots of the entire machine, recalled from the pads.

The stock kernel is kept. The application layer is not.

Akai az01 Linux  (kernel 6.18.26-rt4, systemd, ALSA/DRM drivers, sshd)
        └── chroot: Debian 13 armhf runtime on the SD card
                     └── Granola MPC   (SuperCollider)
                     └── sfizz          (offline SFZ rendering)

Before you install anything, read Risk, warranty and affiliation at the foot of this page. In short: this is not an Akai product, installing it will void any warranty still in effect, and you do it entirely at your own risk.


Why it is built this way

OpenMPC runs as a chroot inside the stock OS, not as a replacement for it.

The Akai application layer — acvs, the HTTP API on port 80, network MIDI, the device daemons — is masked and stopped at boot. az01-power-button is deliberately kept: the shutdown dialog needs it.


Hardware, as measured

SoCRockchip RK3288, 4 × Cortex-A17, 2 GiB RAM
Kernel6.18.26-az01-2026-04-30-rt4, PREEMPT_RT, isolcpus=2-3
AudioALSA hw:ACVA, 44.1 kHz, 128-frame periods, S32_LE
PanelDSI-1, 800×1280 portrait, presented as 1280×800 landscape
TouchILI2117 on /dev/input/event0
Controllerinternal USB-MIDI, 09e8:0046
NetworkingEthernet only

CPU affinity is enforced: JACK and scsynth on the isolated cores 2–3, sclang on core 1, offline renders on cores 0–1.


Layout

openmpc/
  control/         supervisor and diagnostics  → /data/openmpc (internal eMMC)
    bin/           openmpcctl, openmpc-enter, openmpc-return-to-akai, doctor
    libexec/       session, runtime binds, standalone enforcement
  examples/        app bundles; granola-mpc is the appliance
  build/           reproducible ARMv7 runtime image + deploy.sh
  tools/           small C helpers built for the device (boot progress bar)
  schema/          versioned app manifest
  systemd/         unit templates
notes/             STATUS.md — the running record of everything measured
captures/redacted/ controller MIDI captures, identity replies removed

Storage is split deliberately. /data/openmpc on internal eMMC holds only the control plane and session state — about 180 KiB. Everything substantial lives on the SD card at /media/OPENMPC: the runtime, apps, the instrument library, projects, logs.

An app manifest declares what it can see. readable and writable names are validated against the schema enum before any of them reaches a mount command, so no manifest string can become a path fragment.


Granola MPC

Five stereo GrainBuf voices. Parameter ranges, curves and grain topology follow the macOS and Ableton Move versions.

Views

buttonview
MAINgrain clouds, waveform, scrubbing, transposition pads
BROWSEsample browser over the SD card
TRACK MIXsource-material generator
TRACK MUTEmaster effects
SAMPLERperformance effects
PROGRAM EDITper-track multimode filter
LFO buttonper-track modulation
MENUprojects, and with SHIFT, patterns
SHIFT + MAINrhythm section

Active view bright, inactive dim, the same convention as the track buttons. The pads belong to whichever view is open and are unlit elsewhere; see the cheat sheet for what every control does in every view.

Overlays

STEP SEQ — per-track volume, pan and stereo spread. TC — per-track send amounts into the shared delay and reverb.

Performance effects

SAMPLER opens sixteen curated Airwindows effects, one per pad, placed ahead of every master effect. Pads toggle. Each effect's parameters are drawn fresh from measured bounds every time a pad is engaged, so the same pad is a different character each press.

The five buttons down the right-hand side choose which tracks the effects act on. A targeted track routes through the effects; an untargeted one goes straight to the master insert, keeping its delay and reverb sends either way. Bright means targeted.

Rhythm section

SHIFT + MAIN. Eight synthesised drum channels — kick, snare, hat, clap, cymbal, metal, and two percussion voices — from a single drum SynthDef whose body function differs per channel. Pads 1–8 select a channel; the top row of the screen shows all eight at once.

Each channel has a sixteen-step pattern, and a die that rewrites it from six generative algorithms: Euclidean, two Euclids folded together, additive grouping (3+3+2 and its relatives), bursts, residue sieves, and a Euclid whose hits are nudged off the grid. Density is weighted by role, so a kick that fires on a third of the grid is a kick and a hat that does is an absence.

Clock division is per channel — 1/8, 1/4, 1/2, 1, x2, x4 against the project tempo. A hat on x2 against a kick on 1/2 is two grids inside one tempo, which is the point. One tick runs underneath everything at the fastest division and each channel advances every Nth tick of it, so channels on different divisions share one timebase and cannot drift apart.

Sidechain. The kick carries one control the other channels do not: how hard it ducks the granular tracks. A single ducker synth writes a control bus every voice reads, shaped like the kick and decaying over roughly a beat, so the recovery is still climbing when the next kick lands.

Macro. Each channel has one morph control on the jog wheel that sweeps a set of its voice parameters together, from dark and long to bright and short.

Breaks. Holding pad 16 generates a break and plays it for as long as the pad is held. It is generated, not a fixed rule: two or three transforms are drawn at random from a menu of sixteen — strip, solo, stutter, reverse, rotate, regrid, rewrite, recruit, holes, double, tumble, accent — each with its own random parameters, applied to a copy of the live pattern, with the figure length drawn too. The combination is rerolled if it repeats the one before it. Measured over 200 generations against a three-channel groove, 197 were distinct.

Breaks stay inside the kit you are using: only channels with hits take part, so a three-channel groove does not burst into eight channels of random drums, and recruit is the deliberate exception that brings in exactly one otherwise-silent channel. A break never empties the kit, and ratchets are expressed as grid changes rather than extra steps, so they land exactly on the clock.

The main position never stops advancing underneath a break — it is a pure function of the tick count — so releasing the pad lands on the position the pattern would have reached anyway.

Grain sync

Grains lock to the project tempo by default. The trigger rate is quantised to a power-of-two division of the beat — the density control still chooses how dense the cloud is, it just lands on 1, 2, 4, 8 or 16 grains per beat rather than between them — and the grain phase is reset on every bar, so all five voices agree with each other and with the drums instead of each running from whenever its synth happened to start.

Drift is dropped from the rate while locked, since randomising the trigger interval is what the lock exists to prevent; it still shapes grain duration, position jitter and pan, so a locked cloud is not a metronome.

Measured at 100 BPM with density 23: locked, 26.667 triggers/second, exactly sixteen per beat, landing within about a millisecond of each bar line. Unlocked, 23.1 and wandering, drifting 15–21 ms away from the bar.

It can be turned off per project in the project settings.

Project settings

In the projects view, touching an occupied slot on screen opens its settings — the pads load projects, the screen configures them, which keeps a stage gesture and a setup gesture apart. Three settings, all saved with the project: tempo, drum level (how loud the rhythm section sits against the granular tracks) and grain sync.

Touchscreen

The panel is portrait and mounted rotated, so raw touch coordinates are neither the screen's axes nor a simple flip of them. The transform was calibrated against four measured corners rather than derived from the panel's declared geometry, which does not describe how it is mounted; residuals are within about six pixels, which is well inside a fingertip.

Touch is used where a control belongs to setup rather than performance — steps, the die, clock divisions, project settings, the shutdown dialog and the waveform — while the pads and encoders keep the gestures you would use on stage.

Audio graph

5 voices ──┬─► master bus ──► Transform (drive → 3-band EQ → compressor) ──► out
           ├─► delay send ──► delay  ──┘
           └─► reverb send ─► reverb ──┘

Delay and reverb are shared master effects with per-track sends, as in the originals. Sends are post-fader, so lowering a track takes its effect tail with it. Nodes sit in three ordered groups — voices, effects, master — because SuperCollider evaluates in order and effects must run after the voices write.

A density cap bounds grain overlap. Without it, maximum size and density is 200 grains/second of 2-second grains — 400 concurrent voices, which this board cannot sustain, and which is reachable from the front panel.


User guide

Getting around

Granola opens into an unsaved blank project: no material on any track, no project loaded. Nothing is restored from the last session, so what you hear is always something you put there.

Each view is opened by its own button, lit while active. The sixteen pads belong to whichever view is open — they do different things in each, and are unlit in views that do not claim them.

Five tracks, each an independent granular voice with its own sample, parameters, filter, LFOs and gesture loop. The track buttons select; everything you edit applies to the selected track.

A first pass

  1. BROWSE — find a sample on the card, then press a track button to load it onto that track. PLAY starts the clouds.
  2. MAIN — the four Q-Links shape the grain cloud: size, density, jitter and drift. The jog wheel moves the playhead; touching the waveform scrubs.
  3. STEP SEQ and TC overlay volume/pan/spread and the two send amounts.
  4. PROGRAM EDIT gives the track a filter; the LFO view modulates up to nine destinations at once.
  5. MENU saves the whole machine into one of sixteen project slots; SHIFT + MENU saves patterns inside that project.

Creating samples

TRACK MIX opens the source generator, which writes its own material. Choose an engine, a generator, a musical profile and a number of layers, then press the jog wheel to render. The result can be auditioned and committed to a track as granulation material.

Five engines: sampled orchestral instruments through sfizz, synthesised patches through Yoshimi, FM voices through a DX7 clone, Plaits, which generates its timbres rather than recalling them and gives every layer a different one of its twenty-four synthesis engines according to the part that layer plays, and bytebeat, which is not played at all — one integer expression evaluated on a sample counter.

Performance

  • Parameter-lock pads (MAIN, pads 1–12) — step through the transpositions your scale allows with the A and C buttons, set the playhead and the four Q-Links as you want them, then hold SHIFT and press a pad to commit the lot. A short press plays it back. A slot you have edited since selecting it blinks until you save it — except while the gesture recorder is running, where the blink would be noise.
  • Grain windows (MAIN, pads 13–16) — gaussian, percussive, plateau, reverse, per track.
  • Performance effects (SAMPLER) — sixteen Airwindows effects on the pads, momentary, with fresh parameters on every press.
  • Gesture loops (RECORD) — hold RECORD and play; everything you touch is captured exactly as performed, with no quantisation. Release and it loops immediately. Each track's loop is its own length, so they drift against each other rather than locking to a bar.
  • Mute — SHIFT + a track button. The cloud keeps running underneath, so unmuting drops back into the texture rather than restarting it.

Rhythm

SHIFT + MAIN opens the rhythm section.

  1. Pads 1–8 select a channel; SHIFT + pad mutes it. A muted channel is skipped by the sequencer entirely, so muting also sheds load.
  2. Touch a step to toggle it, or touch the die to have a bar written for you from one of six algorithms.
  3. The row above the steps sets that channel's clock division. Each channel tile shows its own grid, so a polyrhythmic kit can be read off the top row.
  4. Q1–Q3 are volume, pan and effects send for the selected channel; on the kick, Q4 is how hard it ducks the granular tracks. The jog wheel sweeps that channel's macro.
  5. Hold pad 16 for a break. It is generated on the press and different every time; the panel names the transforms that came up.

Tempo, the rhythm section's overall level and grain sync live in the project settings — see Project settings.

Projects and patterns

A project is a snapshot of the entire machine: every track's sample, all grain parameters, filters, LFOs, sends, mutes, parameter-lock pads, grain windows, gesture loops, master effects, the source generator's settings, the whole rhythm section, and the project's own tempo and grain-sync setting. A pattern is the same snapshot one level down — sixteen of them live inside each project, and a project reopens on whichever pattern was in play when it was saved.

Both use the same gestures: press a pad to load, SHIFT + pad to save. Empty slots are unlit and do nothing. Projects are red, patterns green.

Patterns saved while no project is loaded are held in memory only, and the confirmation says so — there is no file to keep them in until a project exists.


Cheat sheet

Conventions used throughout: unlit = empty or unavailable, dim = holds something, bright = active or selected. SHIFT is held, never toggled.

Global — these work in every view

controlaction
MAINmain view
BROWSEsample browser
TRACK MIXsource generator
TRACK MUTEmaster effects
SAMPLERperformance effects
PROGRAM EDITfilter
LFO button †LFO view
MENUprojects
SHIFT + MENUpatterns
SHIFT + MAINrhythm section
track buttons 1–5select track
SHIFT + track buttonmute / unmute that track (button goes dark)
PLAY / STOPstart and stop all clouds
RECORD (hold)record a gesture loop on the selected track
RECORD (release)loop starts immediately
SHIFT + RECORDclear the selected track's gesture loop
STEP SEQmixer overlay (volume / pan / spread)
TCsend overlay (delay / reverb)

† The button captured for the LFO view. Its panel label was never recorded during capture — it is the one you assigned, MIDI note 6.

MAIN — grain clouds and waveform

controlparameterrange
Q1grain size2 ms – 2 s
Q2density0.2 – 200 grains/s
Q3jitter0 – 100 % of the buffer
Q4drift0 – 100 %
A / Ctransposition, stepped through the current scale−24 – +24 st
jog wheelplayhead position0 – 100 %
jog pressplayhead to centre
touch waveformscrub the playhead
pad 1–12 (short press)recall a stored slot
SHIFT + pad 1–12store the current grain state to that pad
pad 13–16grain window: gaussian, percussive, plateau, reverse

Pads 1–12 are parameter-lock slots per track, painted in that track's own colour. A slot holds transposition, playhead and all four Q-Link values, so a row of them is a sequence rather than a set of transpose buttons. Dim means a stored slot; bright means the track is currently sitting on it — derived from the actual values, so stepping away with A/C dims it by itself. A slot you have edited since selecting it blinks until you save, except while the gesture recorder is running.

Mixer overlay (STEP SEQ) and send overlay (TC)

controlmixersends
Q1volumedelay send
Q2panreverb send
Q3stereo spread—

Both are per track and post-fader: lowering a track takes its effect tail with it. The two overlays share the encoders, so opening one closes the other.

BROWSE — sample browser

controlaction
jog wheelmove through the listing
jog pressenter a folder, or audition a file
track button 1–5load the highlighted file onto that track

TRACK MIX — source generator

The jog wheel moves between rows; Q1 edits the highlighted row, Q2 does the same in coarse steps for the long lists. Press the jog wheel to render.

rowwhat it sets
ENGINEsso / sfizz, yoshimi, dx7, plaits, bytebeat
GENERATORwalk, euclid, progression, motif
PROFILEany, plucky, pad, lead, bass, percussive, texture
LAYERShow many independent parts are generated
ROOTkey centre
SCALEfifteen scales
TEMPOBPM
VOICES1 (mono) to 4 (poly)
BARSloop length, 1–8
DENSITYhow busy the result is

PROGRAM EDIT — filter (per track)

controlaction
pad 1lowpass
pad 2highpass
pad 3comb
pad 4filter off
Q1Q2Q3Q4
lowpass / highpasscutoffresonancedrivemix
combtunefeedbackdampingmix

Each kind keeps its own four values, so moving between them and back finds each as you left it.

LFO view — nine destinations (per track)

controlaction
pad 1–9select which destination the knobs edit
Q1shape — sine, triangle, saw, square, step random, smooth random
Q2rate — 0.005 – 8 Hz
Q3depth — 0 – 100 %

Destinations, in pad order: grain size, density, jitter, drift, spread, grain envelope shape, delay send, playhead position, reverb send.

Every destination has its own oscillator and runs whenever its depth is above zero — the pads only choose what the knobs edit, not what is modulating. The main view animates every destination that has depth dialled in.

SAMPLER — performance effects

controlaction
pad 1–16engage an effect, momentary; parameters are randomised on every press
Q1wet / dry mix
Q2fade in and out time, 1 – 10 s
five buttons down the right-hand sidechoose which tracks the effects act on

TRACK MUTE — master effects

The jog wheel moves between four tabs; Q1–Q4 edit the four parameters of the tab in view.

tabparameters
EQdrive, low, mid, high
COMPthreshold, ratio, attack, release
DELAYtime, feedback, damping, level
REVERBsize, damping, high cut, level

SHIFT + MAIN — rhythm section

controlaction
pad 1–8select a drum channel
SHIFT + pad 1–8mute / unmute that channel (pad goes dark)
pad 16 (hold)generate and play a break for as long as it is held
Q1channel volume
Q2channel pan (bipolar)
Q3channel effects send
Q4sidechain depth — kick channel only
jog wheelthat channel's macro, dark/long → bright/short
touch a steptoggle it
touch the division rowthat channel's clock division
touch the diewrite the channel a new bar

Divisions are per channel: 1/8, 1/4, 1/2, 1, x2, x4 against the project tempo. Each channel tile shows its own grid alongside its hit count.

MENU — projects, and SHIFT + MENU — patterns

controlaction
pad (short press)load that slot
SHIFT + padsave the current state into that slot

Sixteen slots each. Projects red, patterns green, and a pattern bank belongs to the project that holds it.

Touching an occupied project slot on screen opens its settings: tempo, drum level and grain sync, all saved with the project.


Third-party components and their licences

OpenMPC's own source is MIT (LICENSE). It builds on, installs or renders through the following, each under its own terms. Several are copyleft; check before redistributing anything built from this repository.

componentused forlicence
SuperCollideraudio engine and languageGPL-3.0-or-later
JACKaudio serverLGPL-2.1 / GPL-2.0
Qtthe on-screen interface, via sclangLGPL-3.0
sfizzoffline SFZ renderingBSD-2-Clause
Yoshimisynthesised source materialGPL-2.0
mi-UGens by Volker BöhmPlaits as a SuperCollider UGenGPL-3.0
Mutable Instruments Plaitsthe DSP mi-UGens portsMIT (firmware), CC-BY-SA-4.0 (hardware)
DX7-Supercollider by Aziz Ege GonulFM voices and its 16384-preset banksee repository
bytebeat by midouestbytebeat interpreter and UGensee repository
PoundHardthe drum voice the rhythm section is built onsee repository
Airwindows by Chris Johnsonperformance effectsMIT
Sonatina Symphonic Orchestrasampled orchestral instrumentsCC-BY-3.0
Debian armhfthe runtime the chroot is built fromvarious, per package
MPC-LiveXplore by TheKikGencontroller protocol groundworksee repository

Sonatina Symphonic Orchestra, the Yoshimi preset banks and the DX7 bank are not in this repository and are fetched or installed separately — see "Third-party payloads" below.

The Akai stock firmware, its kernel and its proprietary components are not redistributed here and remain the property of their owners.


Source material generator

Generates sequences, renders them through a selected engine and commits the result to a track as granulation material.

5 tone engines, chosen with the ENGINE field:

  • SSO / sfizz — sampled orchestral instruments, rendered offline.

  • Yoshimi — synthesised. Each layer derives a new patch from a stock Yoshimi preset, mutated within bounds set by the preset's own category, so a pad stays a pad and a plucked instrument keeps its transient. 902 presets across 24 banks ship with yoshimi-data; the profile decides which banks are eligible and the preset itself is random.

  • DX7 — Aziz Ege Gonul's DX7-Supercollider, an accurate Yamaha DX7 clone built on the FM7 UGen from SC3-Plugins, with its own bank of 16384 presets. It is used unmodified: DX7.scd is loaded as published and its own mainCaller plays the notes. Because it is SuperCollider, it renders offline and is the quickest of the three — about 0.8x the loop length. Its own bank carries no patch names, so dx7_import.py converts named .syx cartridges into the same format and puts them at the head of the bank; the profile then selects by name (456 labelled patches), falling back to the full 16384 when nothing matches.

  • Plaits — Mutable Instruments' Plaits, through Volker Böhm's mi-UGens port, compiled for this board. Alone among the four it has no preset library: its sound is four continuous controls, so the timbre is generated rather than recalled. Plaits is twenty-four synthesis engines in one — virtual analog, waveshaping, two-op and three banks of six-operator FM, granular, additive, wavetable, chords, speech, swarm, two noise engines, plucked string, modal body, three drum models, phase distortion, wave terrain, string machine and chiptune — and every layer is given a different one, chosen for the part that layer plays. Engines from the same family are kept apart, so three layers do not arrive as the same idea three times. The generated controls are ranged per engine, because the same knob is a modulator ratio on the FM engines and a filter frequency on the noise ones; sampling all four uniformly for every engine is what makes generated Plaits patches sound alike. It renders offline, and is the fastest of the five.

  • Bytebeat — midouest's bytebeat interpreter and UGen, compiled for this board with its own Catch2 suite run on the target architecture rather than the host, because char signedness and shifts of signed types differ on ARM in exactly the places this code cares about.

    This engine is unlike the other four, structurally rather than as a matter of taste. The others are played: they take a sequence of notes and render it. Bytebeat cannot be played — it has no note-on, no pitch input and no way to be told what to do at a particular moment. The expression is the piece, and what it does at second thirty is decided by the same arithmetic that decides second one. So the generated sequence is ignored here, and root, scale and voices have nothing to attach to.

    What replaces them is expression generation. The variety has to come from the formulas, so they are built from a grammar of families known to behave differently from one another, and every layer is given a different family. The counter's clock rate is generated too: the same expression at 2 kHz and at 32 kHz is two unrelated sounds, which makes rate the single most effective diversity control here. Expressions are screened before anything is rendered — the standalone interpreter evaluates seconds of audio in milliseconds, so a formula that turns out silent, constant or a pure square wave is discarded and replaced rather than discovered in the audio afterwards.

Yoshimi has no offline render mode, so it is run headless on a pty with the ALSA default PCM redefined as a file sink through ALSA_CONFIG_PATH — scoped to that process, with a null slave, so no hardware is opened and a render can never contend with the live audio device. Rendering is therefore realtime.

Four note-pattern generators — constrained random walk, Euclidean, chord progression, motif development — over 15 scales, 1–8 bars, 1–6 layers. Instruments are chosen at random per generation, biased by a profile (plucky, pad, lead, bass, percussive, texture) that also reshapes note length, density and register. Everything is seed-deterministic: a seed fully describes a result.

Two findings shaped this and are worth keeping in mind:

  • --use-eot is mandatory. Without it sfizz renders until every voice falls silent, and SSO's looped and long-release patches never do — one produced 197 MB in 40 seconds and was still going. Because that flag stops at the last MIDI event, the generator writes a silent terminator on the loop boundary: a note-off for a note that was never on, which cannot make a sound but keeps the render running to a full loop.
  • The instrument index deliberately includes non-top-level SFZ files. Indexing only files that nothing else #includes yielded 90 instruments, 71 of them keyswitch patches that pick an arbitrary articulation unless a keyswitch note is sent. Indexing every file that defines playable regions yields 741, of which 668 are plain single articulations.

Build and deploy

One-shot install onto a device that already has SSH working:

openmpc/build/export-runtime.sh 0.2.0        # build the ARMv7 runtime image
openmpc/build/install.sh --dry-run           # print every step, change nothing
openmpc/build/install.sh --i-have-a-backup   # do it

install.sh pushes the control plane, the systemd units, the runtime, the SDK and the app, then verifies. It leaves boot activation off unless --enable-at-boot is given, so a device that fails to install still boots the stock application. It is untested end to end — every step in it has been performed by hand, but their assembly has not. Read it, and dry-run it first.

The individual steps, which is what the installer calls:

openmpc/build/export-runtime.sh 0.2.0        # build the ARMv7 runtime image
openmpc/build/deploy.sh runtime 0.2.0 --switch --restart
openmpc/build/deploy.sh app granola-mpc --restart
openmpc/build/deploy.sh control              # safe under a live session
openmpc/build/deploy.sh sdk --restart

Both scripts read MPC_HOST and MPC_KEY from the environment — the address of your device on the network and the private key that reaches it. Set them to whatever is true for your setup.

The device has no rsync, so every transfer is tar-through-ssh, staged beside the destination and swapped with rename(2). A push that would replace a bind-mounted tree under a live session is refused unless --restart is given, because deleting the old directory unlinks files the running app can still reach through the mount.

Boot splash

The splash lives in its own partition as a headerless raw framebuffer image — 800 × 1280 portrait, 32-bit BGRA, followed by unrelated data that must be left alone, so a write stops at the image and no further. make-splash.py composes a replacement in display space and rotates it on the way out; it has no dependencies, decoding PNG itself, so it runs wherever the splash is built.

openmpc/tools/splash-progress.c fills a progress track drawn into that splash, and boot stages call it with rising numbers. It must be given --prime on the first call: U-Boot paints the panel directly and never fills /dev/fb0, so the buffer is black, and writing into it makes the driver flush a region that would otherwise erase a widening band of the real splash.

A blank or corrupted splash does not affect the boot itself, and a missing openmpc-splash-progress binary is simply skipped.

openmpc/build/build-splash-progress.sh          # static armv7, into control/bin
openmpc/build/make-splash.py --logo granola-logo-horizontal.png \
    --out splash.raw --preview splash-preview.png

The binary is not tracked, for the same reason no other compiled artefact here is; build it before deploy.sh control if you want the bar.

Third-party payloads (not in the repository)

# Sonatina Symphonic Orchestra → /media/OPENMPC/instruments
curl -L -o sso.tar.gz https://codeload.github.com/peastman/sso/tar.gz/refs/tags/v4.0
cat sso.tar.gz | ssh "$MPC_HOST" 'mkdir -p /media/OPENMPC/instruments && tar -C /media/OPENMPC/instruments -xzf -'
python3 openmpc/examples/granola-mpc/index_instruments.py <local-sso-copy> \
    openmpc/examples/granola-mpc/instruments.json

sfizz is not packaged for Debian in any suite and is compiled from source in Dockerfile.armv7 as a separate build stage.


Safety and recovery

  • U-Boot and the partition table are untouched.
  • The stock rootfs is read-only and remains the fastboot recovery image. Nothing was deleted from it: re-enabling acvs restores the original fallback with no other change.
  • openmpc-enter arms an independent safety timer before stopping the stock application, and rolls back on any failure.
  • Take a verified full eMMC and boot-area backup before you start. backup-mpc-emmc.sh does this and records checksums.

Accepted trade: with the stock application disabled there is no fallback UI. A failed start leaves the appliance retrying, with SSH reachable.


Gotchas discovered the hard way

Each of these cost real debugging time and is documented in notes/STATUS.md:

  • /etc is an overlay mounted after the boot transaction is computed. Units and mask symlinks placed in /etc/systemd/system are invisible to early boot; services start from /usr/lib regardless. Standalone enforcement therefore runs from the appliance script, not from a unit of its own.
  • /var is an overlay on /data. The stock app writes a waveform thumbnail for every audio file it can see. With an instrument library on the card that reached 2.5 GiB and filled the internal partition, after which nothing could start. The cache is now bind-mounted onto the SD card.
  • Qt eglfs applies a high-DPI scale factor. Without QT_ENABLE_HIGHDPI_SCALING=0 the app is handed a 355×222 surface on a 1280×800 panel and every layout collapses.
  • JACK leaves shared memory behind when killed. The next server then hangs after printing its banner, before ever opening the audio device — which looks exactly like a broken sound card. openmpc-session clears stale objects when no jackd is running.
  • Q-Links report a bounded 0–127 position for endless encoders, and the stock init configures them in two groups (01 01 and 00 00). The 00 00 pair jams against the ends of its range; all four are now set to 01 01.
  • Array:includes compares Strings by identity in SuperCollider. The sample browser's extension filter silently matched nothing.

Known limitations

  • Reverb has no independent decay control. FreeVerb2 exposes room and damping only. A genuine size/decay pair needs a different algorithm, since JPverb lives in sc3-plugins which is not installed.
  • Csound's armhf liblinear_algebra.so has an unresolved dorgqr_ symbol; those opcodes are unavailable.

Licence

OpenMPC is released under the MIT Licence — see LICENSE. That covers this repository's own source only; the third-party components it builds on carry their own terms, several of them copyleft, and are listed under Third-party components and their licences.


Credit

Granola is by nzimas; this is the MPC One port. Sonatina Symphonic Orchestra is maintained at peastman/sso. Controller protocol groundwork draws on TheKikGen/MPC-LiveXplore.


Risk, warranty and affiliation

OpenMPC is not an Akai product and has no affiliation, association, authorisation, endorsement or connection with Akai Professional, inMusic Brands, or any of their subsidiaries. "Akai", "MPC" and "MPC One" are trademarks of their respective owners and are used here only to identify the hardware this software runs on.

Installing this will almost certainly void any warranty still in effect on your device. It requires root access to a machine the manufacturer never intended you to have root on.

You install and run this entirely at your own risk and on your own responsibility. It modifies system state on a device that costs real money and is not designed to be modified. It can render the unit unusable. Nobody involved in this project is liable for damage to your hardware, loss of your work, or any other consequence of using it, and there is no support, no warranty and no promise that it works at all. The software is provided "as is" — see LICENSE.

If any of that is not acceptable to you, do not install it. Back up your device first; backup-mpc-emmc.sh in this repository exists for that reason.