- C++ 95.5%
- Starlark 1.8%
- C 1.4%
- JavaScript 1%
- CSS 0.2%
|
|
||
|---|---|---|
| control | ||
| daemon | ||
| docs | ||
| engine | ||
| instruments | ||
| page | ||
| platform | ||
| platforms | ||
| plugin | ||
| protocol | ||
| rom | ||
| suite | ||
| tools | ||
| ui | ||
| web | ||
| .bazelignore | ||
| .bazelrc | ||
| .bazelversion | ||
| .gitignore | ||
| BUILD.bazel | ||
| CLAUDE.md | ||
| LICENSE | ||
| MODULE.bazel | ||
| MODULE.bazel.lock | ||
| README.md | ||
| THIRD_PARTY_NOTICES.md | ||
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=1on 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=1on the JX family and the Jupiters, and--lead.key_priority=1for 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 ajackcontrol 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=Nand 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_modbuilds 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:
- 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.
- 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.
- 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.