Recreations of classic analog synthesizers, for the Raspberry Pi 3B.
  • C++ 95.5%
  • Starlark 1.8%
  • C 1.4%
  • JavaScript 1%
  • CSS 0.2%
Find a file
2026-10-18 01:14:12 -04:00
control Fix what the final review of hold and the pedals found 2026-10-14 19:22:17 -04:00
daemon Let voices drift in pitch and cutoff while they play, card and oscillator, with a per-note offset and a Retune button that eases them back, on eleven instruments, for issue #4 2026-10-17 22:09:14 -04:00
docs Let mini-Bit's filter saturate as its CEM3328 or, by a switch, the first Bit Ones' SSM2044, for issue #4; the CEM3320 was a misrecollection 2026-10-17 23:02:56 -04:00
engine Let the SEM's state-variable filter and mini-XP's CEM 3372 saturate, and let the SH-5, the Odyssey, the 2600 and mini-700 drift, for issue #4 2026-10-17 23:38:16 -04:00
instruments Bench mini-XP's filter saturation on the Pi: a fifth more a voice, and up to about twenty-four voices fit 2026-10-17 23:42:49 -04:00
page Add a multimode ladder, ramp, tracking and lag generators, and a quantized route curve, for mini-XP 2026-10-17 00:47:20 -04:00
platform Retune from a switch a host can automate, variance.retune, and from a controller set by --retune-cc, at Ben's asking 2026-10-17 22:45:19 -04:00
platforms Cross-compile for the Pi and add a render benchmark 2026-10-10 16:53:03 -04:00
plugin Add mini-XP, Oberheim's Xpander, Matrix-12 and Matrix-6 as one instrument, for issue #15. 2026-10-17 00:47:20 -04:00
protocol Let voices drift in pitch and cutoff while they play, card and oscillator, with a per-note offset and a Retune button that eases them back, on eleven instruments, for issue #4 2026-10-17 22:09:14 -04:00
rom Add the Bit 99's panel curves, from its owner's manual and placeholders. 2026-10-14 21:54:53 -04:00
suite Retune from a switch a host can automate, variance.retune, and from a controller set by --retune-cc, at Ben's asking 2026-10-17 22:45:19 -04:00
tools Serve the suite's page on port 80 on the Pi, by CAP_NET_BIND_SERVICE from its unit 2026-10-17 01:59:42 -04:00
ui Give every instrument with zones six, where there were four 2026-10-17 01:08:06 -04:00
web Let voices drift in pitch and cutoff while they play, card and oscillator, with a per-note offset and a Retune button that eases them back, on eleven instruments, for issue #4 2026-10-17 22:09:14 -04:00
.bazelignore Ignore .claude in Bazel, where agents' worktrees go. 2026-10-15 10:43:00 -04:00
.bazelrc Let the shared four-pole filter saturate as OTA cells do, and give the Prophets filter.saturation, for issue #4 2026-10-17 13:08:05 -04:00
.bazelversion Add the first milestone: one clean voice rendered offline 2026-10-10 16:21:00 -04:00
.gitignore Ignore the session directories .remember and .superpowers in every clone 2026-10-13 09:19:31 -04:00
BUILD.bazel Add the first milestone: one clean voice rendered offline 2026-10-10 16:21:00 -04:00
CLAUDE.md Record the CEM 3340's top-octave flattening and slow pulse fall as not modelled, by Ben's word, for issue #4 2026-10-18 01:14:12 -04:00
LICENSE Release the repository as CC0, and record that nothing will be sold 2026-10-13 09:17:46 -04:00
MODULE.bazel Add mini-XP, Oberheim's Xpander, Matrix-12 and Matrix-6 as one instrument, for issue #15. 2026-10-17 00:47:20 -04:00
MODULE.bazel.lock Add the first milestone: one clean voice rendered offline 2026-10-10 16:21:00 -04:00
README.md Let the SEM's state-variable filter and mini-XP's CEM 3372 saturate, and let the SH-5, the Odyssey, the 2600 and mini-700 drift, for issue #4 2026-10-17 23:38:16 -04:00
THIRD_PARTY_NOTICES.md Add mini-XP, Oberheim's Xpander, Matrix-12 and Matrix-6 as one instrument, for issue #15. 2026-10-17 00:47:20 -04:00

mini-800

Synthesizers for the Raspberry Pi 3B, each modeled on a hardware instrument and optionally modernized. Each runs on Linux, plays through a USB audio interface, takes notes from USB MIDI controllers, and is controlled from a page in a web browser or from a terminal over SSH. One tree builds all nineteen. The repository is named for the first of them, the Poly-800, which is otherwise one instrument among the rest:

Modeled on Programs Web port Patches
mini-800 Korg Poly-800, Poly-800 II and EX-800 mini800d, mini800ctl, mini800web, mini800render, mini800bench 8800 .m800
mini-8000 Korg DW-8000 and DW-6000 mini8000d, mini8000ctl, mini8000web, mini8000render, mini8000bench 8801 .m8000
mini-5 Roland SH-5, SH-7, SH-3 and SH-3A mini5d, mini5ctl, mini5web, mini5render, mini5bench 8802 .m5
mini-6 Roland Juno-6, Juno-60, Juno-106 and HS-60, and one block of the MKS-7 mini6d, mini6ctl, mini6web, mini6render, mini6bench 8803 .m6
mini-SEM Oberheim SEM, Two Voice, Four Voice, Eight Voice, OB-X, OB-Xa and OB-8 minisemd, minisemctl, minisemweb, minisemrender, minisembench 8804 .msem
mini-101 Roland SH-101, SH-09 and SH-2, and the MC-202's synthesizer mini101d, mini101ctl, mini101web, mini101render, mini101bench 8805 .m101
mini-P5 Sequential Circuits Prophet-5, Prophet-10 and Prophet-T8 minip5d, minip5ctl, minip5web, minip5render, minip5bench 8806 .mp5
mini-JX Roland JX-3P, JX-8P and JX-10 Super JX, and the GR-700's synthesizer minijxd, minijxctl, minijxweb, minijxrender, minijxbench 8807 .mjx
mini-JP Roland Jupiter-8, Jupiter-6, Jupiter-4, MKS-80 and Promars minijpd, minijpctl, minijpweb, minijprender, minijpbench 8808 .mjp
mini-Ody ARP Odyssey, and the Avatar's synthesizer, the Axxe and the Solus miniodyd, miniodyctl, miniodyweb, miniodyrender, miniodybench 8809 .mody
mini-2600 ARP 2600, with the 3604 and 3620 keyboards mini2600d, mini2600ctl, mini2600web, mini2600render, mini2600bench 8810 .m2600
mini-Omni ARP Omni and Omni-2, the String Ensemble (Solina), and the Quadra miniomnid, miniomnictl, miniomniweb, miniomnirender, miniomnibench 8811 .momni
mini-MS Korg MS-20 and MS-10 minimsd, minimsctl, minimsweb, minimsrender, minimsbench 8812 .mms
mini-Bit Crumar Bit One, Bit 01 and Bit 99 minibitd, minibitctl, minibitweb, minibitrender, minibitbench 8813 .mbit
mini-700 Korg miniKORG 700 and 700S, and the 800DV mini700d, mini700ctl, mini700web, mini700render, mini700bench 8814 .m700
mini-CS Yamaha CS-5, CS-10, CS-15, CS-30, CS-30L, CS-50, CS-60 and CS-80 minicsd, minicsctl, minicsweb, minicsrender, minicsbench 8815 .mcs
mini-RS Roland RS-101, RS-202, RS-09 and RS-505, and the Saturn SA-09 minirsd, minirsctl, minirsweb, minirsrender, minirsbench 8816 .mrs
mini-PRD Moog Prodigy, Rogue, Liberation, Source, Taurus, Taurus II, Sonic Six, Micromoog and Multimoog, and the Realistic Concertmate MG-1 miniprdd, miniprdctl, miniprdweb, miniprdrender, miniprdbench 8817 .mprd
mini-XP Oberheim Xpander, Matrix-12 and Matrix-6 minixpd, minixpctl, minixpweb, minixprender, minixpbench 8818 .mxp

What is here

Each instrument is five programs, shown here by the Poly-800's names: a daemon that owns the sound device and the MIDI ports and plays from a controller (mini800d), a small terminal client (mini800ctl), a bridge that serves a page to a web browser (mini800web, then http://<the Pi>:8800/), a renderer from a MIDI file to a WAV file (mini800render) and a bench that measures its speed (mini800bench). The daemon and its clients are separate processes that meet at a control socket, so a client can come and go while the synth plays.

The JX family has a sixth, minijxsyx, which reads JX-8P tones from a System Exclusive dump of your own and writes each as a patch file: minijxsyx OUTDIR FILE.syx.... No Roland tone is in the repository.

All nineteen are also one program, minisuited, with its page, minisuiteweb, on port 8888 (80 on the Pi when tools/pi/install.sh starts it at boot), minisuitectl and minisuitebench. It holds every instrument and plays one at a time, through one sound device and one MIDI port; the page has a list in its heading to choose another from, and each instrument keeps its sound while another plays and its patches where its own daemon keeps them. minisuited --instrument=mini6 starts on the Juno. On the Pi the suite starts at boot, as a system unit with its page beside it, which tools/pi/install.sh installs. The DW-8000 is in it when it is given its ROM, with --rom=PATH or --rom6000=PATH, and left out of it otherwise.

An instrument's sound can be changed while it plays, from its page or from its terminal client, which can also enter a sound by the original's numbered parameters where the original has them: the first-generation Poly-800's, the DW-8000's, the Juno-106's, the JX-8P's, the MKS-80's and the Bit 99's. Patches are saved and loaded by name as small text files, any number of them, which stand in for the originals' patch memories. The daemon keeps nothing between runs: it starts from a patch or from flags given on its command line. Each instrument has its own patch files, web port and control socket, so several can run at once, given a sound device each. Every instrument has one shared arpeggiator (--arp.on=1, or the page's Arpeggiator section), with what the Juno-60's and the DW-8000's could do between them, which can follow MIDI clock, and with a random mode, ratchets and note repeats added.

Each is also a CLAP plugin for Linux, mini800.clap and so on, for a host such as Bitwig or Reaper on a laptop, with a window that draws the instrument's page. bazel run -c opt //plugin:install puts all nineteen in ~/.clap.

All nineteen are written and tested. Twelve have been played live on a Raspberry Pi 3B from a MIDI controller: the Poly-800, the DW-8000, the SH-5, the Juno, the SEM family, the SH-101, the Prophets, the JX family, the Jupiters, the Odyssey, the Omni and the RS family. The other five, the 2600, the MS-20, the Bit One, the miniKORG 700 and the CS family, have been played by the owner on a laptop and not on the Pi; all five have had their benches run there. The same goes for what the families below added to an instrument after it was played on the Pi: heard on the laptop, and not there. The eighteenth, mini-PRD, has been played by the owner on the Pi. The nineteenth, mini-XP, has been played by nobody: it has been rendered offline and its bench run on the Pi.

The sound is clean: the oscillators and filters are ideal ones, not shaped toward the hardware, and no real instrument has been measured. Where a manual gives no figure, the figure here is a placeholder.

"Optionally modernized" means that what an instrument has beyond its original is a setting that is off, or at nothing, until asked for. The one exception is the shared four-pole filter, which self-oscillates at the top of its resonance on every instrument that has it, the Poly-800 included, whose hardware stops just short.

Shared parts

The instruments are made of shared parts, in engine/, and a part is there when more than one instrument would use it:

  • Oscillators: one that gives a sawtooth, a pulse and a triangle at once from one ramp (analog_vco.h), and one with an output for each waveform, worked out a sample at a time, for an instrument with a jack or a switch for each (wave_vco.h), the two sharing how they round their edges (vco_edges.h); Roland's sawtooth, pulse and sub oscillator from one counter (ramp_oscillator.h); a sub oscillator under a VCO (sub_oscillator.h); and a divide-down tone generator, every note sounding all the time, for a string machine (divide_down.h).
  • Filters: a four-pole low-pass that most of the instruments have as their main filter, which self-oscillates, can give back the bass that resonance takes away (filter.compensation, off as it comes), and can also be heard from its second stage, at 12 dB an octave, and whose four sections can saturate as a CEM 3320's or an IR3109's transconductance cells do, or as a transistor ladder's stages do (four_pole_filter.h, at nothing as it comes, and left out of a build with --config=no_analog); a two-pole filter with low-pass, band-pass and high-pass outputs (state_variable_filter.h); the MS-20's two-pole filter, a low-pass or a high-pass (sallen_key_filter.h); and a one-pole low-pass and high-pass (one_pole.h).
  • Modulation: the six-stage envelope, an LFO that a key can start over and that gives a triangle, a sine and a square at once (keyed_lfo.h), and a sample and hold with a lag (sample_hold.h).
  • Keys: how keys are given voices, and which key sounds where several held compete for one note (key_assign.h): a unison or a mono mode plays the latest key, the lowest held or the highest by its key priority switch, 0, 1 or 2, --voice.key_priority=1 on the DW-8000, the SH-5, the Juno, the SEM family, the SH-101, the Prophets, the Odyssey, the MS-20, the miniKORG 700, the CS family and the RS-505's bass, --zone.key_priority=1 on the JX family and the Jupiters, and --lead.key_priority=1 for the Quadra's lead on the Omni, each starting at its original's own rule where a manual says what that is; zones, up to six ranges of the keyboard, each with its own voices (zones.h), and a zone's own way with its keys (seats.h); hold and sostenuto pedals for every instrument (pedals.h, kept by the player), and chord memory, one key playing a chord taken from the keys held, by controller 67 (--chord-cc=N) or the page's Chord button (chord_memory.h, kept by the player after hold and ahead of the arpeggiator, or after it by a switch; not in the plugins); a duophonic keyboard, the lowest key and the highest, and its glide (duophonic.h, duophonic_glide.h); and portamento.
  • Patch cords: the wires of an instrument whose parts are joined by cords (patch_panel.h), with arithmetic quick enough to turn volts to hertz at every sample (fast_math.h). The page shows each input jack as a jack control that reads as what is plugged into it.
  • A modulation matrix (mod_matrix.h) on the SEM family, the Prophets, the JX family, the Jupiters and the Juno: 32 slots in each sound, --mod1.source=N --mod1.amount=0.5 --mod1.destination=N and so on, each a source, a second source it is multiplied by, a curve and an amount, beside every route the original has a slider for. A slot may deepen another slot or one of those sliders, and a VCO or noise may move a pitch, a pulse's width or the cutoff at audio rate, which --config=no_audio_mod builds without. Any MIDI controller is a source as well. The page shows it at the bottom of each sound, a card for each route in use, with "+ Route" to add one, and a patch file holds only the routes in use.
  • The arpeggiator and the effects: chorus, delay and bass and treble controls, each defeatable, after every instrument's voices.

The instruments

The Poly-800 is eight voices, or more on request, each an oscillator of four octave-spaced square waves under a six-stage envelope, in whole mode or in double mode, where a voice has two. With them are noise, the modulation generator and the joystick. Its filter has two routings: paraphonic, as on the hardware, where every voice passes through one filter under one envelope, and polyphonic, which the hardware never had, with a filter and a filter envelope for each voice. The first-generation instrument's numbered parameters are its panel. The EX-800, by Korg's manuals for it, is the same voice in a module with no keyboard, so it is this instrument as it stands, played over MIDI, with nothing added.

The DW-8000 is two wavetable oscillators a voice, noise, a filter and two envelopes for every voice, velocity and aftertouch, the four key assign modes, portamento, auto bend, the modulation generator's four shapes and the delay. It needs its waveform ROM, which is Korg's and is not in this repository. Its programs take the image as --rom=PATH, or the DW-6000's as --rom6000=PATH, or both, and do not start with neither. rom/dw8000/README.md says what the image is and how to put one together from a dump of the four chips. No other instrument needs a ROM.

The DW-6000 is settings of it: six voices, with the auto bend, the velocity, the aftertouch and the delay left off, and the chorus on, which is this project's own and not a model of the DW-6000's. Its damper pedal is controller 64, which every instrument hears as hold. Its eight waveforms are in an image of their own, --rom6000=PATH, and are waveforms 17 to 24 of osc1.wave and osc2.wave, after the DW-8000's sixteen; a waveform whose image was not given is silent, and the program says so when it starts. rom/dw6000/README.md says what that image is: no dump of the DW-6000's chips was found, and the one image that was is a third party's and probably not a copy of them.

The SH-5 is built from Roland's owner's manual and service notes: two oscillators with sync and pulse width modulation, white and pink noise, the ring modulator, the mixer's routing to either filter or past both, the low-pass, band-pass and high-pass filter, the second band-pass filter, the three envelopes and their triggers, both LFOs, the sample and hold, portamento and panning. It is monophonic, as the hardware is. The SH-7 is settings of it, by Roland's owner's manual and service notes for that: its two-note keyboard (--voice.two_note=1, the highest key on the first oscillator and the lowest on the second), the five octave squares of its VCO-1(A) (--osc1.feet16=0.6), its autobend (--osc1.autobend=-3), its high-pass filter (--hpf.cutoff=200), the second oscillator or noise moving the cutoff (--filter.fm=1.5), and a second ADSR made of the AR (--ar.decay=0.4 --ar.sustain_level=0.5), and a switch for each envelope that starts it again on every key (--adsr.retrigger=1, --ar.retrigger=1). The five squares share one level, as its manual says: raised past a total of one they are no louder for it. Its external input and envelope follower are not built. The SH-3 and SH-3A are settings of it too, by the SH-3's owner's manual and both instruments' service notes: one VCO heard through those five footages alone, each a square, an unsymmetrical rectangle or a spike (--osc1.level=0 --osc1.feet8=1 --osc1.feet8_wave=1 --osc1.feet2=0.5 --osc1.feet2_wave=2), the 8' chorus (--osc1.chorus=3), and the lowest key sounding (--voice.key_priority=1). Its GLIDE button is not built, and the SH-3A's own owner's manual was not found.

The Juno is built from Roland's manuals and service notes for the three instruments. It is the Juno-6, Juno-60 and Juno-106 as one instrument: the DCO's sawtooth, pulse and sub oscillator, noise, the four-position HPF, the filter with its one envelope either way up, the amplifier under the envelope or the gate, the LFO with its delay, pulse width from the slider, the LFO or (as on the 6 and 60) the envelope, the 106's portamento and assign modes, the chorus in its three modes, and the 106's numbered parameters as its panel. Six voices, or more on request. The HS-60, by Roland's manual for it, is a Juno-106 with speakers, so it is this instrument as it stands. The MKS-7 Super Quartet's melody and chord blocks are the same voice, two and four of them, with velocity, which is here as --filter.velocity and --amp.velocity, off until asked for, and a hold pedal, controller 64, which every instrument hears; its bass is one smaller voice that this one can be set to. One mini6d is one of those blocks: the MKS-7's three at once, each on its own MIDI channel, and its drums, are not built.

The Oberheim SEM family is built from secondary sources and not from Oberheim's manuals, which were not found. One voice is a SEM: two oscillators, each a sawtooth and a pulse, with sync, a two-pole filter that sweeps from low-pass through a notch to high-pass or switches to band-pass, two envelopes and an LFO. One, two, four or eight of them are the SEM, the Two Voice, the Four Voice and the Eight Voice, each voice with its own LFO, its own place in the stereo field and, where wanted, its own sound, chosen on the page by ticking which voices play which. Its keyboard can be split into up to six zones, each with its own voices, octave shift and arpeggiator. The OB-X's differences (one LFO for all, cross modulation, noise, envelopes with a release) are settings, as are a few of the OB-6's: a sub oscillator, a triangle and a low frequency mode for the second oscillator, the LFO to the amplifier and the filter's sweep, velocity and aftertouch; three of the TEO-5's: cross modulation through zero, a second LFO, and a delay before each envelope's attack; and, after the OB-X8, pink noise, a unison of a chosen number of voices, and a bender and lever that can move one oscillator alone. The OB-Xa and the OB-8 are settings of it too, by Oberheim's owner's manuals: the four-pole filter (--filter.four_pole=1), the filter's envelope to the second oscillator's pitch (--vco2.env_pitch=12), a triangle on either oscillator (--vco1.triangle=1), a unison on the lowest key, which is how it starts (--voice.key_priority=1), the sustain footswitch that lengthens the release (--env.pedal=1), voices that glide at slightly different rates (--voice.portamento_offset=0.2) and the OB-8's portamento bend (--voice.portamento_bend=7). The OB-X's square LFO, which by its manual goes downward only, is a switch (--lfo.square_down=1).

The SH-101 is built from Roland's owner's manual and service notes. It is monophonic, as the hardware is, until asked for more voices (--voice.count=6): a VCO giving a sawtooth and a pulse at once, a sub oscillator in its three waveforms, and noise, each with its own level in the mixer; the filter; the amplifier under the envelope or the gate; the one envelope with its three ways of being triggered, which also choose whether the lowest or the latest key sounds; the modulator's triangle, square, random and noise; pulse width from the slider, the LFO or the envelope; portamento that can be for legato notes only; and the bender with its lever. The synthesizer of the MC-202 MicroComposer is settings of it, by Roland's owner's manual and service notes for that: the same voice without the noise, with a delay before the LFO (--lfo.delay=0.5), which starts over at each trigger from the keys, and an accent (--accent.level=0.7, and --accent.filter=1 to open the filter with it), which here goes to notes struck hard, there being no sequencer to mark them. The MC-202's sequencer is not built. The SH-09 and the SH-2 are settings of it as well, by their owner's manuals and service notes: the lowest key sounding whatever the envelope's switch says (--voice.key_priority=1), a sine for the LFO (--lfo.sine=1), a 32' range (--vco.octave=-2) and an amplifier that can be held open (--amp.mode=2); and for the SH-2 a second VCO (--vco2.saw=1 --vco2.tune=7), a sine on the first (--vco.sine=1), an autobend (--autobend.amount=2) and a bender that can reach the second VCO alone (--bend.vco2_only=1).

The Prophets are built from Sequential Circuits' owner's manual and technical manual for the Prophet-5's Rev 3, and the T8's operation manual; the Prophet-10's manual was not found. Five voices, or more on request (--voice.count=10 is a Prophet-10's): two oscillators a voice with their shape switches, sync, and oscillator B's low frequency and keyboard switches; the mixer with noise; the four-pole filter and the amplifier, each with its own envelope; POLY-MOD, from the filter's envelope and oscillator B to oscillator A's frequency, its pulse width and the filter, with oscillator B's share worked out every sample; the LFO's three shapes and pink noise through WHEEL-MOD, which the modulation wheel lets through; unison on the lowest key, and glide. The Prophet-10 and the Prophet-T8 are the same instrument: a patch holds four sounds and the keyboard up to six zones, so two programs play at once, split or doubled, as on those two; and the T8's voice is settings, each off until asked for: a triangle on oscillator A, ADR and a second release, an LFO under the wheel, velocity to the envelopes' times and heights, how fast a key comes up to their release, and each key's own pressure to seven destinations. filter.saturation has the CEM 3320's four cells saturate, as its datasheet draws them; how hard is a guess. How deep its modulations go, and what velocity and pressure come to, are guessed, not measured.

The JX family is built from Roland's owner's manuals and service notes, and has been played on the Pi, where the owner found it sounds great. It is the JX-3P, JX-8P and JX-10 as one instrument, six voices or more on request (--voice.count=12 is a JX-10's): two DCOs a voice, each a sawtooth, a fixed pulse, a square or noise; sync, the JX-8P's cross modulation and the JX-3P's metal; a mixer with an envelope on the second DCO's level; a high-pass filter and a chorus for each tone; the four-pole filter; two envelopes with key follow, either of which can move the pitch, the mixer or the filter, either way up; velocity to each of those and to the level; an LFO with a delay; the six key modes; aftertouch; and the JX-8P's numbered tone parameters as its panel. A patch holds four tones and the keyboard up to six zones, so the JX-10's upper and lower, split or dual, are two zones, each with its own key mode, and its chase play, touch voice and cross fade work between them. No Roland document gives a range in hertz or seconds for any of it, so what its panel's numbers come to is guessed, and so is what the JX-8P's cross modulation is. The synthesizer of the GR-700 guitar synthesizer is, by Roland's manuals for it, the JX-3P's voice, edited from the same programmer, so a GR-700 tone is settings of this instrument with nothing added. Its guitar side, which finds each string's pitch and gives each string a voice, is not built.

The Jupiters are built from Roland's owner's manuals and service notes for all four, and have been played on the Pi, where the owner found they sound like a Jupiter. They are the Jupiter-8, the Jupiter-6, the Jupiter-4 and the MKS-80 as one instrument, eight voices unless asked otherwise (--voice.count=6 is a Jupiter-6's and 4 a Jupiter-4's): two VCOs a voice, each with a switch for every waveform, among them the Jupiter-8's sine and noise on the second; sync either way; cross modulation of the first by the second, by hand and by an envelope; one pulse width for both, moved by the LFO, an envelope or the key; the Jupiter-4's sub oscillator and ensemble; the Promars's sub oscillator under the second VCO and its second tuning (a Promars is --voice.count=1 --zone.assign=0 with the two VCOs alike, built from its service notes, its owner's manual not having been found); a high-pass in each voice; the Jupiter-8's low-pass with its -12 dB switch, and the Jupiter-6's low-pass, band-pass and high-pass made of two stages; two envelopes with key follow, the first of which can be turned over; an LFO in six shapes with a delay, and a second for the bender's vibrato; the MKS-80's velocity, aftertouch and envelope reset; the Jupiter-4's sample and hold of noise to the cutoff and its bender to the loudness; and the MKS-80's numbered tone parameters as its panel. A patch holds four sounds and the keyboard up to six zones, so upper and lower, split or dual, are two zones, each with its own assign mode: solo, the unison on one key, the unison that shares the voices among the keys held, the two poly modes, and the Jupiter-4's rotating one. One of Roland's booklets on the Jupiter-8 was not found, so how its slope switch and cross modulation work is read from its schematics; and Roland gives no range for much of the rest, which is guessed.

The three ARPs are each an instrument of its own. The Odyssey has been played on the Pi, where the owner found it sounds good, and so has the Omni; the 2600 has been played on a laptop, where he found the same.

The Odyssey is built from ARP's owner's and service manuals. It is every revision of it as one instrument: two VCOs under a duophonic keyboard, the lowest key held playing the first and the highest the second, through one filter and one amplifier. It has the whole panel, slider by slider and switch by switch: sync, the ring modulator that is really an exclusive-OR of the two pulse waves, white or pink noise, the S/H mixer and the sample and hold with its lag, the ADSR and the AR with their LFO repeat. --filter.two_pole=1 is the first model's 12 dB an octave filter; the later ones' is the default. What ARP's manuals leave unsaid, among them how deep some modulations go and whether its LFO is a sine or a triangle, is guessed.

Three more of ARP's instruments are settings of it, by ARP's own manuals for each: the synthesizer of the Avatar, which is the Odyssey's circuit boards under a guitar's pitch follower; the Axxe, which is one of its VCOs, its filter and its ADSR; and the Solus, two VCOs, the ring modulator, the filter and the ADSR. What they have that an Odyssey has not is here, each at nothing until asked for: both waveforms of a VCO in the mixer at once (--mixer.vco1_pulse=0.5), a slider of its own for the LFO's square and for the ADSR where the Odyssey has a switch (--vco1.fm_square=0.3, --vco1.fm_adsr=0.1, --vco1.pwm_adsr=0.5, --filter.sh=1), one pitch for both VCOs (--voice.one_pitch=1) and a single trigger (--adsr.single=1). The Avatar's guitar input is not built, since no sound comes in from outside yet. The Little Brother is not here: ARP's drawings of it show an oscillator with a frequency divider and fixed wave shapers and no filter, which is no setting of an Odyssey.

The 2600 is built from ARP's owner's and service manuals for it and for its 3620 keyboard, their words and not their schematics. It is the console and the keyboard: three VCOs, a ring modulator, noise, a four-pole filter, an ADSR and an AR, an amplifier, a mixer with pan and reverberation, voltage processors, a sample and hold with its clock and switch, an envelope follower and a preamplifier, under the 3620's duophonic keyboard. Every slider and switch on the panel is a parameter, and so is every jack: a parameter whose name ends in .jack says what is plugged into that input, 0 for no cable, which leaves the wire behind the panel connected as ARP's normalling has it, 1 for a dummy plug, or the number of one of the 36 output jacks (--vco1.kbd.jack=34 patches the keyboard's upper voice to the first VCO). So a stored patch holds its patch cords, and with none it plays as the manual's instrument does. Everything is worked out a sample at a time, so any output can go to any input, sound or control, which makes its one voice cost about a tenth of one of the Pi's cores. Its reverb is a plain one and not a model of the springs, and its envelopes' times are borrowed from the Odyssey's, since no manual gives them.

The Omni is built from ARP's service manuals for the Omni and the Omni-2 and the Omni-2's owner's manual; the first Omni's owner's manual was not found. It is both as one instrument, an Omni as it comes: a string machine and a paraphonic synthesizer in one, where every key sounds at once from one divide-down tone generator, so that forty-nine keys can be held. Each key gates an 8' tone and a 4' tone, with no attack and a release of its own. The string section is Violin, Viola, Cello and Bass through ARP's chorus phaser, three delay lines each swept by an oscillator of its own; the synthesizer section is the same tones through one four-pole filter and one ADSR for all the keys; and the bass is one note on the lowest key, the Omni's through the filter or the Omni-2's with a filter and envelope of its own (--bass.voice=1). The Omni-2's other differences are switches too: --bass.staccato, --adsr.single and --phaser.omni2. Its waveform is the hardware's, read from the voicing schematics: a spike for each cycle, which each key's gate clips, through the voicing filters that make its 4' tones bright and its 8' tones mellow. --wave.shaped=0 and --wave.voicing=0 give an ideal sawtooth and no filters in their place. The phaser's rates and depths, the string section's envelope, the Mix slider's law and the Omni-2's bass voice are read from the schematics too, the Omni's own from soft scans.

It can also be set as ARP's String Ensemble, which is Eminent's Solina, from Eminent's service drawings and four pages of its manual; no manual of ARP's for it was found. That is the strings alone (--mixer.balance=1), ideal sawtooths with no voicing (--wave.shaped=0 --wave.voicing=0), and what was added for it, each off until asked for: its Trumpet and Horn registers (--strings.trumpet=1, --strings.horn=1), a bass that is a square wave (--bass.square=1), a Crescendo that sets how much swell there is and not how long it takes (--strings.attack=0.67 --strings.immediate=0.3), and its ensemble, three delay lines moved by two oscillators in three phases (--phaser.ensemble=1), which --phaser.bypass=1 switches off. The Trumpet's and Horn's formant and the ensemble's depth are stand-ins, not read from the circuit.

And as three of the four sections of ARP's Quadra, from its owner's and service manuals: its strings and poly synthesizer are the Omni-2's (--phaser.omni2=1) from the second octave of its keys up (--voice.bottom=48), and its bass is a VCO of its own on the lowest two octaves (--bass.top=59), a pulse (--bass.square=1 --bass.width=0.2) that can be tuned apart and dropped an octave (--bass.tune=0.1, --bass.octave_down=1), with a decay and a resonance for its electric bass (--bass.voice=1 --bass.decay=0.5 --bass.resonance=0.5). The Quadra's phase shifter follows the mixer for whichever sections are sent through it (--shifter.strings=1 --shifter.rate=0.3 --shifter.resonance=0.5), and gives two sides. Its lead synthesizer is a fourth section, one voice of two VCOs with a filter and an envelope of their own, silent until its level is raised (--lead.level=1): a sawtooth or a pulse (--lead.pulse=1 --lead.width=0.1 --lead.pwm=1), on the highest key or on two (--lead.two_voice=1), with written intervals and a trill as settings (--lead.interval2=12 --lead.trill=7), delayed vibrato, a glide for each VCO, an envelope that attacks a second time, channel pressure for its touch sensor, and an arpeggio of its own, the Quadra's SEQUENCER switch, up or up and down at the trill's speed (--lead.arpeggio=1); --shifter.lead=1 sends it through the phase shifter. CLAUDE.md has what was chosen in building it. The phase shifter's depths and the bass filter's peak are stand-ins, the schematics not having been read.

The MS-20 and MS-10 are built from Korg's owner's and service manuals for both and the circuit diagram of the MS-20's later filter board. They are one instrument, an MS-20 as it comes: two VCOs with the ring modulator that is really an exclusive-OR, a high-pass filter and then a low-pass filter, each with a peak that self-oscillates, two envelopes, the modulation generator whose one knob leans both of its waves, and the patch panel with its sample and hold, extra amplifier, noise, wheel and button. Every jack is a parameter, as on the 2600 (--mixer.ext.jack=16 patches the phones back to the external input, which is how the hardware is made to feed back). An MS-10 is the same with the second VCO turned down, the high-pass filter switched out (--hpf.on=0) and a cord from EG 2 to FREQ (--fm.eg.jack=17). --filter.revision chooses between the instrument's earlier and later filters, which differ in where the circuit limits the sound; how either one distorts, which is most of what an MS-20 is known for, is not modelled. It can have up to twelve voices (--voice.count), each a whole MS-20, and one voice costs about a twentieth of one of the Pi's cores.

The Bit One, Bit 01 and Bit 99 are built from the Bit 99's owner's manual alone, which says the Bit 01 shares its data; the Bit One's own manuals were not had. They are one instrument, mini-Bit: six voices of two DCOs, each at a footage and up to eleven semitones above it, with a triangle, a sawtooth and a pulse switched in together and a pulse width that follows velocity; noise; a four-pole filter whose envelope closes it from the cutoff down, or with INVERT opens it; an amplifier with an envelope of its own; and two LFOs to each DCO, the filter and the amplifier. Velocity shortens the attacks and sets the depths. Two sounds, lower and upper, are split or doubled, three voices each, and go to the two sides in stereo. The manual's numbered parameters, 1 to 67, are its panel. Every envelope time is a placeholder.

The miniKORG 700 and 700S, and the 800DV are built from the 700S's owner's and service manuals and the 800DV's owner's manual. They are one instrument, mini-700, a 700 as it comes: one oscillator at 64' to 2' in a triangle, a square, a sawtooth or the two CHORUS modes, which are a pulse swept slowly or fast; the Traveler, a high-pass and then a low-pass whose levers cross freely, as a real one's do with their knobs turned round (--traveler.stops=1 keeps them apart); an envelope whose PERCUSSION/SINGING slider sets how steeply it falls; vibrato, the bender's slide into each note and portamento. A 700S is that with --effect.on=1: a second oscillator for DUET, three ring modulators and two noises, crossfaded by BALANCE. An 800DV is --voice.count=2, an upper and a lower voice on a duophonic keyboard, with its sawtooth mixer and REPEAT. The owner checked the effect section, the envelope and the filter against a 700Sm, Korg's reissue.

The CS-5, CS-10, CS-15, CS-30, CS-30L, CS-50, CS-60 and CS-80 are built from Yamaha's owner's manuals for all of them and the CS-10's and CS-30's service manuals. They are one instrument, mini-CS, a CS-60 as it comes: eight voices of a VCO with a sawtooth, a square and a sine past the filter, a high-pass and then a low-pass or one filter alone, which never sing, the CS-10's initial-level filter envelope and an ADSR, with the CS-50's and CS-60's levers, key scaling, aftertouch and a ring modulator after the voices. A CS-5 or CS-10 is one voice (--voice.count=0). A CS-80 is a second channel in every voice (--ch2.on=1), with velocity, each key's pressure, its touch bend and its tremolo; a CS-15 is one voice of two channels wired to each other; and a CS-30 is the same with three envelopes chosen for each destination and its channels' filters, ring modulators and FM feeding each other. Eight voices of both channels take about a quarter of one of the Pi's cores.

The RS-101, RS-202, RS-09, RS-505 and SA-09 are built from Roland's service notes for all five and the owner's manuals for the RS-101 and the RS-09, read in part. They are one instrument, mini-RS, an RS-09's strings as it comes: every key's squares from dividers, a gate for each key shared by every section, and five sections made of them, the RS-09's stair-wave strings or the RS-505's upper and lower halves, the RS-101's and RS-202's tablets for each clef, the RS-09's or the SA-09's organ, the RS-505's paraphonic synthesizer and its latched bass, and a bucket-brigade ensemble with each original's figures (engine/bbd_ensemble.h, --ensemble.type=1 to 5). An RS-505 is --strings.voicing=1 --strings.feet8=0 --strings.feet4=0 --strings.lower=1 --strings.upper=1 --wave.rs505=1 --synth.level=0.8 --bass.level=0.8 --ensemble.type=1; an SA-09 --strings.level=0 --organ.level=1 --organ.voicing=1 --organ.feet8=1 --organ.selector=2 --gate.percussive=1 --organ.accent=1 --ensemble.type=5 --ensemble.mode=2; CLAUDE.md has the others and what was chosen in building it. Brass, the SA-09's tone selector and most of the ensembles' depths are stand-ins. The owner has played it on the Pi and found it works; every key held, with everything at work, takes under a third of one of its cores.

The Prodigy, the Rogue, the Concertmate MG-1, the Liberation, the Source, the Taurus and the Sonic Six are built from Moog's owner's manuals for the Prodigy, the Source, the Taurus and the Sonic Six and the service manuals or notes for all of them but the Taurus II, for which nothing was found. They are one instrument, mini-PRD, a Prodigy as it comes: one voice of two VCOs, each a sawtooth, a triangle and a pulse of any width at levels of their own, the second synchronized or moved at audio rate by the first, noise, a ring modulator, an organ's divide-down polyphonic section, the ladder filter and two contours that can be one. A Rogue is --vco2.level=1 --vco2.pulse=1 --vco2.saw=0 --vco2.width=0.85 --noise.level=0.5 --env.single=1 --lfo.wave=2; an MG-1 --voice.key_priority=2 --poly.level=0.5 --ring.level=0.5 --env.single=1 --lfo.wheel=0; a Sonic Six --voice.duophonic=1 --ring.multiply=1 --lfo2.mix=0.5; CLAUDE.md has the others and what was chosen in building it. The Micromoog and the Multimoog are settings of it too, since 2026-10-17, with their doubling, their oscillator moving the filter at audio rate, their drone and their modulation's routes added: a Micromoog is --vco1.saw=0.5 --vco1.pulse=1 --vco1.width=0.3 --vco1.double=-0.5 --filter.osc=1.2, and a Multimoog the same with the second VCO up. More voices are there, off as it comes: sixteen with everything at work take under half of one of the Pi's cores.

The Xpander, the Matrix-12 and the Matrix-6 are built from Oberheim's owner's and service manuals for the Xpander and the Matrix-6's owner's manual. They are one instrument, mini-XP, an Xpander as it comes: six voices, each two VCOs whose waveforms are levels, a filter in the Xpander's fifteen modes made as its service manual says the chip makes them, two VCAs in a row, five envelopes, five LFOs, four ramps, three tracking generators and a lag, every one the voice's own, and a modulation matrix of 32 routes in each of four sounds, which can quantize. A Matrix-12 is --voice.count=12; a Matrix-6 is fewer of everything with --filter.mode=0 --fm.source=1 --fm.destination=1, its wave shape, sync and key click being --vco1.shape, --vco1.sync and --vco1.click; CLAUDE.md has the rest and what was chosen in building it, all of it unconfirmed. Twelve voices with everything at work and eight routes take about a third of one of the Pi's cores.

What is missing

  • The analog character of the hardware: every instrument is clean, but for the filters of the Prophets and the JX family, whose cells can saturate as the CEM 3320's and the IR3109's do, and of the SH-5 and mini-PRD, whose transistor ladders can, and of the MS-20 and the miniKORG 700, whose filters' cells can, and of the Juno and the SH-101, whose IR3109s can, and of the SEM family and mini-XP, whose OTA cells can, and of mini-Bit, as either of its chips (filter.saturation, at nothing as it comes), from datasheets and circuits and not from a recording; and the mixer of every instrument with one ahead of an analog filter, which overdrives when its levels are turned past full, and the 2600's filter, which three oscillators at full overdrive; voices whose tuning and cutoff drift slowly and start each note a little apart, with a Retune button that eases them back (variance.drift, variance.note); and some oscillators: the JX's and the Junos' ramps, the Prodigy's and the 2600's folded triangles and the 2600's sine, and the CEM 3340's uneven triangle on the Prophets, the Jupiters and the SH-101.
  • The originals' sequencers, on every instrument that had them, and their programmers. Patches are files and not numbered memories, and no factory patch is here.
  • Reading a dump of an original's patches.
  • Sound from outside, and with it the 2600's and the MS-20's external inputs and the MS-20's external signal processor.
  • The MS-50 as an instrument. Its modules that an MS-20 lacks are to go on mini-MS's patch panel, and are not there yet.
  • A system image for the Pi: the suite starts at boot only once installed by hand on Raspberry Pi OS.
  • A login or encryption on the web page: anyone who can reach its port can change the synth.

Each instrument's own list of what is left out, and of what is guessed, is in CLAUDE.md, with the rules, the decisions and how to run everything. instruments/README.md says how an instrument is put together, and docs/references.md lists the sources.

Where it is going

The plan is to complete these instruments, to give them the analog flavor of their hardware, and to add more, the Oberheim Xpander among them. The line is drawn so as to leave out ROMplers and operator-style FM of the kind Yamaha made.

Building and running

bazel test //...
bazel run //instruments/poly800:mini800render -- /path/to/in.mid /path/to/out.wav
bazel run //instruments/dw8000:mini8000render -- --rom=/path/to/dw8000-waves.bin \
    /path/to/in.mid /path/to/out.wav
bazel run //instruments/dw8000:mini8000render -- --rom6000=/path/to/dw6000-waves.bin \
    --voice.count=6 --osc1.wave=17 --osc2.wave=20 /path/to/in.mid /path/to/out.wav
bazel run //instruments/sh5:mini5render -- /path/to/in.mid /path/to/out.wav
bazel run //instruments/juno:mini6render -- --chorus.on=1 /path/to/in.mid /path/to/out.wav
bazel run //instruments/sem:minisemrender -- --voice.count=8 /path/to/in.mid /path/to/out.wav
bazel run //instruments/sh101:mini101render -- --sub.level=0.7 /path/to/in.mid /path/to/out.wav
bazel run //instruments/prophet:minip5render -- --osca.sync=1 /path/to/in.mid /path/to/out.wav
bazel run //instruments/jx:minijxrender -- --chorus.mode=1 /path/to/in.mid /path/to/out.wav
bazel run //instruments/jupiter:minijprender -- --vco2.saw=1 --mixer.balance=0.5 \
    /path/to/in.mid /path/to/out.wav
bazel run //instruments/odyssey:miniodyrender -- --mixer.vco2=1 --vco2.fine=8 \
    /path/to/in.mid /path/to/out.wav
bazel run //instruments/arp2600:mini2600render -- --vco1.kbd.jack=34 --filter.audio3=1 \
    /path/to/in.mid /path/to/out.wav
bazel run //instruments/omni:miniomnirender -- --strings.cello=1 --synth.feet4=1 \
    /path/to/in.mid /path/to/out.wav
bazel run //instruments/ms20:minimsrender -- --mixer.vco2=1 --filter.cutoff=600 \
    --filter.resonance=0.7 /path/to/in.mid /path/to/out.wav
bazel run //instruments/cs:minicsrender -- --ch2.on=1 /path/to/in.mid /path/to/out.wav
bazel run //instruments/rs:minirsrender -- --ensemble.type=2 /path/to/in.mid /path/to/out.wav
bazel run //instruments/prodigy:miniprdrender -- --vco2.level=1 --vco2.sync=1 \
    --vco2.env=24 /path/to/in.mid /path/to/out.wav
bazel run //instruments/xpander:minixprender -- --filter.mode=7 \
    --filter.resonance=0.6 --vco2.level=1 --vco2.tune=7 /path/to/in.mid /path/to/out.wav
bazel run -c opt //plugin:install    # every CLAP plugin, into ~/.clap
# For the Pi; tools/make_pi_sysroot.sh once per machine first.
bazel build --config=pi --config=opt //instruments/... //suite/...

Target

Aspect Choice
Board Raspberry Pi 3 Model B (BCM2837, four Cortex-A53 cores)
System 64-bit Linux
Audio and MIDI ALSA, so class-compliant USB interfaces and controllers work when plugged in
Control A web page served from the Pi, and a minimal terminal program over SSH
Build Bazel, following the sibling project ../tymbal
DSP Floating point

Licensing and provenance

mini-800 is released as CC0: see LICENSE. That covers what is the project's own. What is not is under its own terms, listed in THIRD_PARTY_NOTICES.md, and no ROM image is part of it. CC0 gives up copyright only; it licenses no trademark and no patent. Nothing here is for sale.

The repository is held to rules that keep it the project's own to give away. Three of them follow, and CLAUDE.md states them in full:

  1. Third-party code comes in only under notice-style licenses (Apache, BSD, MIT and the like). No GPL or noncommercial code. LGPL libraries such as alsa-lib and glibc are allowed only when dynamically linked.
  2. Nothing is copied from GPL or non-public sources, and no proprietary information is used in the sources: no disassembly, no data taken from a ROM, no factory patch data.
  3. The emulation is built from published documentation and from measurement of behavior.

One exception is fenced off. The DW-8000 cannot sound without its waveform ROM, so ROM dumps may be kept in rom/ and nowhere else. A copyrighted one is never committed and never compiled in: the program is given a path to it when it starts. The copyright questions this leaves, among them that the ROM reader was written by examining a dump, are unsettled and are recorded in CLAUDE.md.

THIRD_PARTY_NOTICES.md lists everything that is not the project's own, with its version and license. Two pieces of the C runtime there, libgcc and glibc's startup object, are linked into every program for the Pi and are the first rule's two named exceptions.

Each instrument is modeled on the hardware named beside it, and those names are used here only to say so.

"Korg", "Poly-800", "EX-800", "DW-6000", "DW-8000", "MS-10", "MS-20" and "MS-50" are trademarks of Korg Inc.; "Roland", "SH-5", "SH-7", "SH-3", "SH-3A", "SH-101", "SH-09", "SH-2", "MC-202", "Juno", "HS-60", "SynthPlus", "MKS-7", "Super Quartet", "JX-3P", "JX-8P", "JX-10", "Super JX", "MKS-30", "MKS-70", "GR-700", "G-707", "Jupiter-4", "Jupiter-6", "Jupiter-8", "Super Jupiter", "MKS-80", "Promars", "RS-101", "RS-202", "RS-09", "RS-505", "Saturn", "SA-09" and "VK-09" of Roland Corporation; "Yamaha", "CS-5", "CS-10", "CS-15", "CS-30", "CS-30L", "CS-50", "CS-60" and "CS-80" of Yamaha Corporation; "miniKORG" and "Maxi-Korg" of Korg Inc.; "Crumar", "Bit One", "Bit 01" and "Bit 99" of their owners; and "Oberheim", "OB-X", "OB-Xa", "OB-8", "OB-X8", "OB-6", "TEO-5", "Xpander", "Sequential" and "Prophet" of their owners. "ARP", "Odyssey", "Avatar", "Axxe", "Solus", "Little Brother", "2600", "Omni" and "Quadra" are or were trademarks of ARP Instruments, Inc., which made those instruments, or of whoever holds them now; "Solina" and "Eminent" are or were Eminent's, which made the String Ensemble that ARP sold. This project is not affiliated with or endorsed by any of them.