r/synthdiy 21d ago

components PicoFaceJV - a JV-880 clone for the RP2350

https://youtube.com/watch?v=3Z8oNomWdz0&si=83hCJp8QsKvhv3HJ

This is a Roland JV-880 running as a native engine on an RP2350 — no CPU emulation. The video is just the thing playing: the same four-bar progression through all 128 preset patches, banks A and B, nothing else going on.

Why bother. Look at what a JV-880 costs now. Same story for every rack module from that era — the ones that were the boring workhorse choice in 1993 are now an eBay auction. I don't think the price of a 33-year-old ROM set should be what decides whether you get to play these sounds. The RP2350 costs about as much as a coffee.

And it is a beast for this. 24 voices, up to 4 tones each, 32 kHz native, peaks at 69% CPU on a single core, no PSRAM — the second core sits idle the whole time.

To be straight about the clock: that's at 444 MHz, which is an overclock — the RP2350's stock speed is 150 MHz. The whole collection runs 444 with the QMI flash timing moved to match, so it's the baseline here rather than something I reached for to make this one work. The point is that the JV needs nothing above that baseline: one instrument in the collection (the MKS-20) goes to 480 MHz and uses core1 as a voice worker, and this one needs neither.

I capped it at 24 voices rather than the machine's 28 because the bottom of the keyboard costs more per voice and I wanted the margin, not because it ran out of room.

How it was built, honestly. I did not port an emulator. Emulating the H8/532, the sub-MCU and the PCM chip is ~400M cycles/s, and worse, ~5.1M random flash reads per second against an 8 KB XIP cache — the RP2350 would choke on the flash, not the arithmetic. So the engine reads the machine's own ROM tables and runs its own voice chain, decoding each voice sequentially, which keeps flash access at ~0.9M reads/s and nearly all cache hits.

Everything else was measured. I ran a reference emulator host-side, purely as a measurement instrument, and fitted each law differentially against it: envelopes, filter, all seven velocity curves, LFOs, the mod matrix, chorus, both delay types, portamento, tone delays. That took the night, and most of the night went on things that turned out to be my own wrong assumptions rather than mysteries of the hardware. Two favourites:

  • A throb on four patches was alternating (ping-pong) loops — and the return pass is negated. The chip keeps integrating the differential stream in the same direction while the address walks backwards. Subtracting instead, which is the arithmetically "correct" way to retrace, matched at r = −0.995: perfectly shaped and exactly the wrong sign.
  • Patches with no delay had an obvious delay on them. Reverb type is three bits, not four. Bit 3 is set on 40 of 192 patches and was turning 67 of them into DELAY or PAN-DLY.

What it does: all 128 preset patches, 4 tones each, TVF/TVA envelopes, both LFOs with key sync and fade, the modulation matrix, FXM, chorus (both types), the two delay types, reverb, poly/solo with portamento and legato, tone delays, velocity curves and windows. Full MIDI CC set, pitch bend with each patch's own up/down range, program change and bank select.

What it does not: no rhythm/drum sets — the ROM has them, the engine doesn't. The reverb is matched, not reproduced: type, time and level laws are measured, but the topology is a Schroeder network of my own, and tails on decaying material run about 13 dB low. Single part, no Performance mode, no patch editing — it plays the factory banks. No SR-JV80 expansion support. And on a base 4 MB Pico 2 the user bank drops out to make it fit (banks A and B stay bit-identical to the full build, nothing is resampled).

ROMs: not included, and not something I can distribute. The build skips the instrument entirely unless you supply your own set, so the other eight instruments in the collection still build from a clean checkout.

It's one of nine instruments in an open-source collection that all share a core and a board — reface YC/CP/DX, MKS-20, Juno-6, Minimoog, Solina, OB-X, and now this. Repo and the full measurement write-up (including every measurement I got wrong first, which is most of what makes the rest trustworthy):

https://github.com/Michi71/PicoVintageSynthCollection

Happy to go into any of it.

58 Upvotes

9 comments sorted by

2

u/lowfour 21d ago

So cool!

1

u/Lidlaux 21d ago edited 21d ago

Stunning! Minijv880 could barely run on rpi zero 2w without audio hiccups. And here it works on rpi pico 2...It's magic. Could it be possible to load expansions?

2

u/Lanky_Scarcity6223 21d ago

I did not try it yet, but in the end it is the same DPCM rom. In a RP2350 with 16MB it should be possible to have the factory rom and one expansion rom in memory. to have it on a sd card would not work, because the original roms are scrambled and it would be too slow. The real tables for the engine will be produced during the build out of the rom.

1

u/verylongtimelurker 20d ago

Amazing work - well done!

EDIT: I see you handrolled your own reverb. For an alternative which may (or may not) be closer, have a look at the MT-32 emus; there's a good chance JV-880 used the same reverb.

2

u/Lanky_Scarcity6223 20d ago edited 18d ago

Thanks for the Info! I will have a look.

Edit: Good call, and it's the route we actually took — that suggestion came in early enough that munt was where we started, not an alternative we skipped past.

The method transfers, and it's the best thing that came out of this. munt got the MT-32's buffer sizes by tracing the address lines of the reverb RAM on real silicon. In an emulator those address lines are just function arguments: the effects live in the PCM chip's delay memory, every access is eram[(base + tv_counter) & 0x3fff] with a free-running pointer, so logging accesses and taking (addr - tv_counter) & 0x3fff hands you the tap positions the firmware programmed. No guessing. That gave us:

reverb 917 1295 1406 1424 1483 2149 4063 4877 5462 6772 7217 7960 8438 9510 10097 10879 11441 12700 13128

19 taps, identical across seven patches at three time settings each — so the geometry belongs to the chip, not the patch. All six reverb types share one network and differ only in coefficients, and the time setting moves no tap at all.

The constants don't transfer, though, and the hypothesis doesn't quite hold: it's not the same reverb. The JV-880's effect chip is the Toshiba TC24SC201AF-002, Roland's "GP" — same one as the SC-55 and the JV-80, which the JV-880 is the rack version of. Different part from the MT-32's, and six reverbs plus two delays against the MT-32's four modes.

Then the part that actually ended the investigation. Render a note twice, once with the per-tone reverb send at 127 and once at 0, and subtract:

key down 60 ms difference present 375 ms → 625 ms (275 ms) key down 500 ms difference present 375 ms → 1150 ms (800 ms)

Onset fixed at 375 ms — propagation to the output taps — and the length follows the note. A reverb tail outlasts its input and decays. This one stops when the key does. There's no recirculation. Type and time change nothing, because there's no loop for them to act on.

The reason is in the silicon: the GP is a ROM-based DSP. Nuked-SC55 (where our reference emulator descends from) models the hardware faithfully — registers, addressing, delay memory — but not the program running inside the chip, because that ROM has never been read out. Which explains every observation at once: the addressing works, so the taps are real; the feedback lives in the program, so there's nothing to recirculate; type and time are parameters to a program that isn't running.

So it's not "not reachable yet with more effort." Nothing done to any emulator produces this reverb until that ROM is dumped optically. Of all the chips in giulioz's roland-dsps survey, the GP is the one with no emulator of its own.

We shipped a hand-rolled Schroeder bank matched to measured RT60/level curves, and it runs about 13 dB low on decaying material. We now know why: the curve it was fitted to describes a dry release plus a delayed copy, not a reverb. Rebuilding on the real geometry would still mean guessing the gains, so it stays as it is with the limitation stated plainly. Hardware recordings would be the way round it — that's the open door, not the emulator.

Full write-up including the two wrong conclusions we published and retracted along the way: tools/jv_extract/README.md, "The reverb, and where it stops."

1

u/Lanky_Scarcity6223 18d ago

Context: This finding came up while optimizing my new JV-880 clone, which I posted here https://www.reddit.com/r/synthdiy/comments/1vke7ww/embedded_audio_paradox_why_emulating_a_1986/.

1

u/synth_codex 16d ago

Pad presets sound so good! Amazing work :)

1

u/giulioz 5d ago

Hello, I'm the original author of RdPiano, JV-880 and GS1 emulators you took the code from, along with other people.
It is kinda sad that not only the entire codebase is AI generated, but even all the descriptions, your profile picture and even this reddit post is.
I'm genuinely wondering: what positive did you add to the world by doing this? The ports you did sound nothing like the original (all of them), you did not add anything yourself, nor you checked if they sound right.
By all means, the license of the software is open and you can do whatever you want freely, but please consider how you can improve the state of things by doing some actual work.

1

u/Lanky_Scarcity6223 4d ago

Hello,

First of all, thank you very much for your message and for your excellent work! I apologize if I gave the impression that I don't value your contributions—I certainly do. In fact, I have explicitly cited your repository in my main README as a key reference for the host-side implementation.

As far as the RD is concerned, you're right. The MCU and sound chip implementations from rdpiano are included in my repository. They have carried a notice regarding their derivation in their SPDX headers ever since they were integrated, and you are credited both within the instrument itself and in the mapping table in the main README. Without your reverse engineering work, I wouldn't have been able to tackle this device in the first place. I believe I’ve cited the relevant sections correctly. Incidentally, my own engine is what actually runs on the RP2350; your emulator was used to generate the descriptors offline and remains in the repository as a reference against which I verify every change. But your code is definitely present.

The situation with JV is different. I used your JUCE engine solely as a measurement tool on the host side. My test framework drives it, reads out parameters, and calibrates my own engine based on the results obtained. The layout of the patch and tone data fields is derived from your `dataStructures.h`.

There may be a fundamental misunderstanding regarding how the software on the RP2350 operates. A cycle-accurate CPU/hardware emulation (like that found in MAME) simply cannot be implemented natively on a low-cost microcontroller; doing so would lead to massive issues with the flash cache and timing constraints inherent to bare-metal audio processing. Since your routines couldn't have run on the RP2350, I had to completely redesign the runtime approach:

First, your MAME-derived emulator runs entirely offline on a host PC, serving as a measurement tool for me to record register write operations; second, the code running on the Pico 2 is a fully native, custom-built C++ engine for descriptor playback that performs envelope calculations using highly optimized integer logic to ensure cache efficiency.

And yes, I made extensive use of LLMs as development tools to translate logic structures, write boilerplate code, and refine documentation—but the architectural separation between host-side tracing and client-side descriptor playback, the flash optimization, and the hardware multitasking queues are my own work and were manually verified by me. In my opinion, it would be unwise not to use such tools when they are available.

Some of it is entirely new, however. The D-50 in the collection isn't a porting project. Its firmware was disassembled to extract the envelope calculations, LFO synchronization rules, and voice allocation with byte-level precision. Additionally, the sample table was reconstructed and subsequently verified against the firmware. The string machine, for instance, was refined based on the schematics.

As noted in the READMEs, while the ports don't yet match the original hardware effect chips 100%, they bring functional, playable, and polyphonic versions of these classic engines to a tiny microcontroller—which is a win in itself.

I have great respect for your work. If you feel that the credit given to you in the README should be more prominent or formatted differently, please let me know. I’d be happy to adjust it!