A Yamaha reface YC combo/home organ clone for the RP2350. PicoFaceYC is the sibling project of PicoFaceDX, sharing the same hardware base (SparkFun Pro Micro RP2350 board, I2S DAC output, SH1106 128x64 OLED, 3 rotary encoders). The organ engine features a wavetable-based additive footage synthesis architecture ported from setBfree concepts.
| Category | Details |
|---|---|
| Engine | Organ Flutes tone generator (Wavetable-based additive footage synthesis). 5 Wave types: H (Tonewheel warm), V (Vox square), F (Farfisa sawtooth), A (Japanese transistor bright), Y (Yamaha transistor). Derived/ported from setBfree (BeatrixCPP). |
| Polyphony | 16 voices (YC_MAX_VOICES). Yamaha spec states 128; 32 still overloaded the RP2350 at 9-footage-per-voice + full FX (§36), so the cap was lowered to 16 for guaranteed stability. Tunable constexpr in yc_core.h; a CPU-load watchdog (§38) forces all-notes-off on sustained overload. Raise only after on-hardware CPU-load measurement. |
| Sample rate | 44100 Hz |
| DMA buffer | 64 Samples (kChunkLen in YC_Synth_Bridge) |
| DSP style | Header-only (include/yc_engine/), no dynamic allocation, fixed buffers, single-precision float throughout. |
| MIDI | USB-MIDI (TinyUSB, no DIN-Port). Note On/Off with Octave Transpose, Sustain (CC64) always active, RX channel filter (Omni default), gated Panel CCs, outgoing Panel Mirroring, SysEx Parameter Change/Request, Identity Reply. NO Program Change. |
| Effects | Fixed chain: Percussion -> Vibrato/Chorus -> Overdrive -> Rotary Speaker -> Reverb -> Soft-Clip Limiter. |
| Persistence | Virtual EEPROM (wear-leveled Flash append log, reused from PicoFaceDX). Autosave 2s after last change. |
| Presets | None. Panel state is fully SysEx-addressable. |
| Display | SH1106 128x64 OLED |
| Controls | 3 rotary encoders (Selector/ParamA/ParamB) across 11 pages. |
USB-MIDI (Core 1)
|
v
RefaceMidi (Core 1) --IPC--> YC_Synth_Bridge (Core 0) --> yc_engine (Tonegen/Percussion/Vibrato/Rotary/FX/Reverb) --> I2S DAC (DMA-IRQ)
^
|
YC_Controller (Core 1) --IPC--> [same Bridge]
|
v
YC_GUI --> SH1106 OLED
Pinout is unchanged from PicoFaceDX (same board).
| Function | Pin |
|---|---|
| I2S DATA | 26 |
| I2S BCLK | 27 |
| I2S LRCLK | 28 |
| OLED SDA | 2 |
| OLED SCL | 3 |
| Selector encoder CLK | 6 |
| Selector encoder DT | 7 |
| Selector encoder SW | 8 |
| Param A encoder CLK | 10 |
| Param A encoder DT | 11 |
| Param A encoder SW | 14 (optional) |
| Param B encoder CLK | 12 |
| Param B encoder DT | 13 |
| Param B encoder SW | 15 (optional) |
| Status LED | 25 |
| DIN-MIDI RX | 5 (Header present, unused) |
effects/
ram_hot.h
include/
yc_engine/
yc_core.h
yc_tonegen.h
yc_wavetable.h
yc_percussion.h
yc_vibrato.h
yc_rotary.h
yc_fx.h
yc_reverb.h
yc_engine.h
YC_Controller.h
YC_GUI.h
YC_Synth_Bridge.h
ipc.h
midi_reface.h
midi_input_usb.h
pico_frontpanel.h
pico_userinterface.h
pico_hw.h
settings.h
veeprom.h
project_config.h
tusb_config.h
get_serial.h
src/
Corresponding .cpp files
main.cpp
usb_descriptors.c
get_serial.c
test/
yc_engine_host_test.cpp
doc/
MIDI_IMPLEMENTATION.md
PERSISTENCE.md
UI_PAGE_MAP.md
IPC_PROTOCOL.md
CHANGELOG_YC_ENGINE.md
reface manual PDFs
tools/OpenB3/BeatrixCPP/
setBfree reference (read-only, not compiled)
Toolchain: arm-none-eabi-gcc, CMake >= 3.22, Ninja.
Clone the repository using --recurse-submodules to fetch the required pico-sdk and pico-extras.
Build:
git clone --recurse-submodules https://github.com/<owner>/PicoFaceYC.git
cd PicoFaceYC
mkdir build && cd build
cmake -G Ninja ..
ninjaFirmware Footprint (after the §21-§30 optimization pass):
- Flash: ~117 KB of 16 MB (~0.71%) — LUT set moved from RAM to XIP flash (.rodata)
- RAM: ~55 KB of 512 KB (~10.8%) — LUTs in flash, but the active wave table is mirrored to an 8 KB RAM buffer (§33) so the voice-render hot path never touches XIP flash
The interface uses 3 rotary encoders (Selector, ParamA, ParamB) across 11 pages. Selector long-press (>=500ms) opens the system menu (About/CPU Load).
| Page | Param A | Param B |
|---|---|---|
| 1. VOLUME | Volume (0-127) | - (reserved) |
| 2. WAVE/OCTAVE | Wave Type | Octave |
| 3. FOOTAGE 16'/5 1/3' | 16' Drawbar | 5 1/3' Drawbar |
| 4. FOOTAGE 8'/4' | 8' Drawbar | 4' Drawbar |
| 5. FOOTAGE 2 2/3'/2' | 2 2/3' Drawbar | 2' Drawbar |
| 6. FOOTAGE 1 3/5'/1 1/3' | 1 3/5' Drawbar | 1 1/3' Drawbar |
| 7. FOOTAGE 1' | 1' Drawbar | - (reserved) |
| 8. PERCUSSION | Type | Length |
| 9. VIBRATO/CHORUS | Select | Depth |
| 10. ROTARY | Speed | - |
| 11. EFFECT | Distortion | Reverb |
Note: On the PERCUSSION page, ParamA short-press toggles Percussion On/Off.
PicoFaceYC uses USB-MIDI via TinyUSB (no DIN-Port).
- Note Handling: Note On/Off with Octave Transpose. Sustain (CC64) is always active. RX channel filter uses Omni as default.
- Panel CCs: Gated panel CCs (Wave/Footage x9/Percussion x3/Vibrato-Chorus x2/Rotary/Distortion/Reverb) are only processed when MIDI Control is active.
- Panel Mirroring: Encoder changes on the panel are transmitted as outgoing MIDI-CC.
- SysEx: Supports Parameter Change (Set) and Parameter Request (Reply) for all Tone Generator addresses, as well as Identity Reply.
- Program Change: NOT supported (YC does not support it according to the official MIDI Implementation Chart).
- Limitations: The Model-ID byte for SysEx is an unverified placeholder (see
doc/MIDI_IMPLEMENTATION.md). The full Bulk-Dump block (with checksum) is not yet implemented (TODO).
PicoFaceYC uses a Virtual EEPROM mechanism (wear-leveled Flash append log, reused from PicoFaceDX) to store yc_panel_state_t. This includes Wave, Octave, 9 Footages, Percussion, Vibrato/Chorus, Rotary Speed, Distortion, Reverb, Volume, and MIDI-Control-Mode. Autosave triggers 2s after the last change, with a 250ms polling interval.
None. The reface YC has no Program Change or a preset bank according to the official MIDI Implementation Chart. The complete panel state is directly SysEx-addressable.
PicoFaceYC uses a fixed effect chain (no slot system like PicoFaceDX):
- Percussion: Monophonic, single-trigger, 2nd/3rd harmonic. Attack/Decay controlled via Length.
- Vibrato/Chorus: Modulated delay-line.
- Overdrive: 1024-point LUT with tanh curve.
- Rotary Speaker: Algorithmic Horn/Drum model with crossover filter and physical speed ramps between OFF/STOP/SLOW/FAST.
- Reverb: Schroeder type (4 Comb + 2 Allpass).
- Soft-Clip Limiter: Final safety stage.
- DSP Style: Header-only DSP implementation in
include/yc_engine/with no dynamic allocation. - RAM_HOT(): The
RAM_HOT()macro (effects/ram_hot.h, unchanged from DX) places audio hot-path data in RAM. - Soft-Clip Limiter: A single
yc_soft_clip()stage applied as the final DSP step insideyc_engine_render_block(the redundant firmware-sidesoftClipSampleinmain.cppwas removed in §29); the I2S path does a direct*32767cast plus hard clamp. - Lookup Tables in Flash (active wave mirrored to RAM): The wavetable/sine/overdrive/footage-gain LUTs are
inline const float[]ininclude/yc_engine/yc_lut_data.h(.rodata/XIP flash). The active wave table is copied into an 8 KB RAM bufferyc_wavetable_ram(yc_wavetable_select, §33) so the voice-render inner loop reads RAM, not flash — the RP2350 XIP cache is only 16 KB. Regenerate the flash tables withtools/gen_luts.cpp(host g++). - Testing:
test/yc_engine_host_test.cppprovides a host test driver that can be compiled with a standardg++without the Pico-SDK. - Hardware Watchdog: A 4 s hardware watchdog (kicked from the audio IRQ, §40) reboots the chip if Core 0 stalls (HardFault/deadlock). A watchdog-reboot counter
WDR: Nis shown on the System → CPU Load screen (§41) to diagnose freezes: rising WDR = Core 0 stall (self-recovered); frozen with WDR = 0 = Core 1/USB stall.
The tone generation concepts (Tonewheel/Drawbar model, Percussion, Vibrato/Chorus scanner, Rotary Speaker principle) were derived from the DSP concepts of setBfree/BeatrixCPP (tools/OpenB3/) and reimplemented for the RP2350 header-only architecture. Code for the YC port was developed with an LLM-assisted workflow: architecture and review by the maintainer, code generation via glm-5.2, matching existing project conventions (exactly like the DX port).
GPLv3 - see LICENSE.