Skip to content

Repository files navigation

PicoFaceRD

PicoFaceRD prototype hardware

A Roland MKS-20 / MK-80 ("S/A synthesis") digital piano clone for the Raspberry Pi RP2350.

PicoFaceRD is a sibling project of PicoFaceCP and PicoFaceDX, sharing the same hardware base (Waveshare RP2350 plus, I2S DAC, SH1106 128×64 OLED, 3 rotary encoders). Instead of emulating the original hardware cycle-exactly at runtime, it plays a descriptor-driven re-implementation of the S/A engine: the original firmware's voice programming was captured note-by-note on a host-side reference emulator, distilled into compact per-note descriptors, and is replayed on-device with chip-exact envelope arithmetic. The result is validated against the reference emulator with a cross-correlation matrix of 1920 cells (median r = 0.9997).

Features

Area Details
Sounds All 16 patches: MKS-20 (Piano 1–3, Harpsichord, Clavi, Vibraphone, E-Piano 1–2) and MK-80 (Classic, Special, Blend, Contemporary, A. Piano 1–2, Clavi, Vibraphone)
Engine Timeline-replay of captured S/A voice programming; 10 parts per voice, chip-exact envelope math, native 20 kHz / 32 kHz per patch (no resampling)
Polyphony 8 / 16 / 24 / 32 voices or Auto — a load-adaptive voice governor with active culling (default; the original is 16-voice)
Effects Vintage DAC stage (12-bit requantization + 2-pole reconstruction filter), bass/treble shelves, tremolo, mono 4-stage phaser, stereo BBD-style chorus
MIDI USB-MIDI, implementation modeled on the MK-80 MIDI implementation chart (sounding range 21–108 with octave folding, damper, FX switches CC 92/93/95, reset CC 121, all-notes-off CC 123, program change, pitch bend ±2 semitones) — see doc/MIDI.md
Tuning Master tune ±50 cents in 1-cent steps with live A4 frequency display
Persistence All panel settings in a wear-leveled flash append log (versioned, CRC-protected), auto-saved 2 s after the last edit while idle
Display Boot splash, header-bar page UI with percent values (1 % encoder steps) and a live diagnostics footer
Clocking Dual Cortex-M33 @ 480 MHz, flash at 120 MHz QSPI (in spec), dual-core voice rendering

Hardware

  • Waveshare RP2350 plus (16 MB flash)
  • I2S DAC (e.g. PCM5102) — DOUT GP26, BCLK GP27, LRCLK GP28
  • SH1106 128×64 OLED, I2C — SDA GP2, SCL GP3
  • 3 rotary encoders — Select: GP6/GP7 (switch GP8), A: GP10/GP11 (switch GP14), B: GP12/GP13 (switch GP15)
  • USB-MIDI over the native USB port

The full pin map lives in project_config.h, and doc/hardware/PROTOTYPE.md describes a complete 3U/10HP module build (panel, stripboards, wiring).

Note: the build uses the SDK board definition sparkfun_promicro_rp2350 as a compatible 16 MB stand-in — the Waveshare RP2350 Plus has no board file in the pinned SDK, and all pins used here are addressed explicitly, so the only thing taken from the board file is the 16 MB flash size (which matches).

User interface

Encoder Select switches the page (with wrap-around), encoders A and B edit the two page parameters. Continuous values are shown in percent and step 1 % per detent.

Page Encoder A Encoder B
PATCH Instrument 1–16 (header shows the bank: MKS-20 / MK-80) Volume
CHORUS Depth (0 % = off) Rate
TREMOLO Depth (0 % = off) Rate
PHASER Depth (0 % = off) Rate (0.1–5 Hz)
EQ Bass (50 % = neutral) Treble (50 % = neutral)
VOICES Polyphony 8/16/24/32/Auto — (line B shows live Act <active>/<limit>)
TUNE Master tune ±50 cents — (line B shows the resulting A4 frequency)
SYS Vintage DAC filter ON/OFF MIDI receive channel 1–16 / Omni

The footer shows a live diagnostics line: <instrument> P<peak-load %> U<buffer underruns> D<dropped events> A<active voices> N<note-on count>.

Voice governor (Auto mode)

In Auto, the polyphony limit follows the CPU load: the base limit is the proven per-rate cap (16 voices for 20 kHz patches, 12 for 32 kHz). When the instantaneous render load reaches 90 % — or a buffer underrun is detected — the governor cuts the limit (down to a floor of 6), and excess voices are faded out within a single 64-sample block (~3 ms, click-free) instead of waiting for their natural decay. Recovery is deliberately slow (+1 voice per 700 ms, only below 70 % load) to avoid pumping. Manual settings bypass the governor entirely.

Building

git clone https://github.com/Michi71/PicoFaceRD.git
cd PicoFaceRD
git submodule update --init          # pico-sdk, pico-extras, u8g2
cd lib/pico-sdk && git submodule update --init && cd ../..

mkdir build && cd build
cmake ..
cmake --build . --target picofacerd2 -j8

If you already have a shared SDK checkout, export PICO_SDK_PATH (and optionally PICO_EXTRAS_PATH) instead of initialising the lib/pico-sdk / lib/pico-extras submodules — the in-tree submodules are only the fallback. The lib/u8g2/u8g2 submodule is always required.

Flash build/picofacerd2.uf2 via BOOTSEL (double-tap reset). Firmware footprint: ~5.3 MB flash (33 % of 16 MB), ~34 KB static RAM + ~44 KB heap (pack descriptors).

Architecture

                         Core 0                                Core 1
  ┌───────────────────────────────────────────────┐   ┌─────────────────────────┐
  │ main loop: USB-MIDI · encoders · OLED (staged │   │ RAM-resident render     │
  │ half-tile flush) · settings autosave          │   │ worker: odd-index       │
  │        │  same-core SPSC event ring           │   │ voices, one doorbell    │
  │        ▼                                      │   │ rendezvous per 64-      │
  │ audio producer: drains ring → RdNewEngine     │◄──┤ sample block            │
  │ block render (even voices) → vintage FX →     │   └─────────────────────────┘
  │ softclip → I2S buffer pool → PIO I2S DMA      │
  └───────────────────────────────────────────────┘

The sound data pipeline is host-side: a MAME-derived reference emulator (based on giulioz/rdpiano) plays each (patch, note, velocity) while a capture hook records the firmware's register writes; an analyzer distills them into per-note part descriptors (pitch, wave region, envelope segment chains); a packer emits compact .rdp packs that are embedded in the firmware together with losslessly repacked 4-byte sample banks. On-device, RdNewEngine replays those descriptors with the same envelope arithmetic as the chip.

Details in doc/ARCHITECTURE.md. The full (German) engineering log with the complete debugging history lives in doc/RD_PORT.md.

Development & testing

Everything is verified host-first on the Mac/Linux side before it touches the device:

tools/rd_extract/run_regression.sh

builds the host tools, extracts the 16 embedded packs from the firmware sources (so the test covers exactly what ships), runs a six-cell A/B matrix against the reference emulator with frozen expected correlations, and runs stuck-voice stress tests (chord hammering with sustain pedal, single- and dual-thread) — REGRESSION PASS/FAIL with exit code. The extraction/analysis toolchain is documented in tools/rd_extract/README.md.

tools/midi/ holds MIDI utilities for testing: midi_keyboard_only.py strips a Standard MIDI File down to pure keyboard performance (notes, damper, pitch bend) so sequencer dumps can be replayed against the device.

ROM data & credits

  • giulioz/rdpiano — the reverse engineering of the Roland S/A sound generation (MCU emulation, ROM decryption) that this project builds on. The host-side reference emulator is derived from that work and from MAME.
  • The embedded sample banks and note descriptors are derived data from MKS-20 / MK-80 ROM images. Roland is not affiliated with this project; all trademarks belong to their owners. This is a non-commercial educational/preservation project.
  • u8g2 (display), Raspberry Pi Pico SDK / pico-extras (platform, PIO I2S audio).
  • Sibling projects: PicoFaceCP (Reface CP clone, source of the UI style, phaser and veeprom modules).

License

GPL-3.0 — see LICENSE.

About

MKS-20/MK-80 emulation for RP2350

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages