An emulation of the Moog Minimoog Model D on the same hardware and software base as PicoFaceRD, PicoFaceCP, PicoFaceDX, PicoFaceYC and PicoFaceSM.
Status: running firmware for the RP2350, plus a macOS host test that compiles the identical engine sources.
The engine follows the operation manual (doc/Minimoog-Manual.pdf,
doc/Minimoog_Model_D_Manual.pdf) and the schematic in the service manual
(doc/Minimoog_Service_Manual.pdf), section by section as the panel is laid
out:
CONTROLLERS Tune, Glide, Modulation Mix (osc 3 ↔ noise), oscillator
modulation switch, osc 3 keyboard switch, pitch and mod wheel
OSCILLATOR BANK three oscillators; Range (LO/32'/16'/8'/4'/2') and Waveform
(6 positions) each, plus a ±7 semitone Frequency control on
oscillators 2 and 3
MIXER volume and on/off for osc 1–3, noise (white/pink) and the
external input — which on a Model D is what you patch the
output back into
MODIFIERS 24 dB/oct transistor ladder low-pass: Cutoff, Emphasis,
Amount of Contour, filter modulation switch, two keyboard
tracking switches; one contour generator (attack / decay /
sustain) each for filter and loudness
OUTPUT main volume, A-440 tuning tone
The essential point: the Model D is monophonic, and that is not a limitation to be quietly designed around. The keyboard produces one control voltage, and the manual says exactly what happens when you hold more than one key — "If more than one key is held down, only the lowest one has effect." Together with the single-trigger contours, which do not restart when a second key goes down while the first is held, that is most of how the instrument plays. Both are modelled here.
The engine follows the panel structurally. The ladder filter is Aaron
Krajeski's variant of the Huovilainen model, taken from
BelaMiniMoogEmulation
(stated by its author to be under no copyright); the block structure follows
moogvst. Each file header under
include/moog/ and src/moog/ names its specific source.
| File | Original circuit | Contents |
|---|---|---|
moog_osc.h |
Oscillator Bank | six waveforms, six ranges, polyBLEP on every discontinuity |
moog_ladder.h |
Modifiers: the filter | four one-pole sections in a saturating feedback loop, 24 dB/oct |
moog_env.h |
the two contour generators | RC attack curve, sustain, release governed by the DECAY switch |
moog_dsp.h |
— | one-pole, biquad, DC block, saturators, noise, drift |
moog_voice.{h,cpp} |
Controllers + Mixer + keyboard + output | note priority, glide, modulation mix, mixer overdrive, feedback, output stage |
moog_params.{h,cpp} |
the front panel itself | one table row per control: name, type, MIDI CC, panel scale |
moog_presets.{h,cpp} |
— (the original has no memory) | 25 factory panel settings |
moog.{h,cpp} |
— | parameters, presets, MIDI, sample rate |
osc 1 ──┐
osc 2 ──┤ contour ──┐
osc 3 ──┼─► mixer ──► ladder low-pass (24 dB/oct) ──► amplifier ──► output
noise ──┤ ▲ ▲ │ stage
│ │ │ contour │
feedback┘─────┘ keyboard tracking (⅓ / ⅔ / full) │
▲ ▲ │
└──────────────────┴────────────────────────────────────────┘
▲
modulation mix (osc 3 ↔ noise) × mod wheel
routed to the oscillators (switch A) and/or the filter (switch J)
Three things about this shape are worth stating, because they are what make it sound like the instrument rather than like a generic subtractive synth:
- The mixer is meant to be overdriven. Five sources, each with its own
volume, summing into a stage that saturates. Turning everything up does not
make it louder, it makes it growl. The
Drivecontrol sets how hard that stage is pushed. - The saturation is inside the filter's feedback loop, not after it. So overdriving the mixer changes the shape of the resonance rather than merely adding distortion at the end.
- Oscillator 3 is a control source whether or not it is audible. The manual: "Switch (G) does not affect the control signal produced by Oscillator 3 via the Modulation Mix." With its keyboard switch off and its range on LO it is the only LFO the instrument has.
62 parameters, each 0.0 … 1.0. See enum MoogParam in
include/moog/moog_params.h; the table that gives each one its name, type,
panel scale and MIDI controller is src/moog/moog_params.cpp, and the
firmware, the host test and the table below all read it — there is one place
that says what a control is called and which CC moves it.
Forty-odd controls laid out flat would be nearly thirty screens to page through to reach the filter, so the panel is a two-level menu whose sections are the ones printed on the instrument:
| Section | Pages |
|---|---|
PRESET |
the preset list |
CONTROLLERS |
tune, bend range, glide, modulation mix, mod wheel, the two mod switches |
OSCILLATOR |
range and waveform per oscillator, plus a detune page |
MIXER |
volume and switch per source, noise colour, feedback |
MODIFIERS |
filter, contour amount, keyboard tracking, both contour generators |
OUTPUT |
main volume, A-440 |
EFFECTS |
the two slots, then chorus, delay and reverb |
VINTAGE |
drift, drive, tone, note priority, trigger mode |
SYSTEM |
receive channel, transposition |
Three encoders, as in the master project:
| encoder 1 | the section, at the top level; the page within it below |
| button 1 | down into the section under the cursor, and back out again |
| encoder 2 | the left-hand parameter of the page |
| encoder 3 | the right-hand parameter |
The section list and the preset list are drawn as three rows with the cursor
kept in the middle; parameter pages show two labelled values. The section and
page tables are plain data at the top of src/MD_Controller.cpp — reordering
means moving a line.
Values are printed the way the panel is marked: knobs run 0.0 … 10.0, the
Cutoff control −4.0 … +4.0, and the Frequency controls of oscillators 2 and
3 −7.0 … +7.0, which on those two is also the number of semitones. Rotary
switches print the name of their position (8', Narrow, Pink).
Continuous values move in steps of 1 % and snap to the percent grid on the first click. Preset values do not sit on that grid (the cutoff of Fat Bass is 0.30, that of Reso Sweep 0.12), so without snapping the round values would be unreachable from some presets. Switches and rotary switches step one position per click — turning right is on, left is off — rather than toggling, so the direction of the encoder keeps its meaning.
The original has no memory: the sound is where the knobs are, and a patch is a
sheet of paper. The 25 factory entries in src/moog/moog_presets.cpp are the
replacement for that sheet. Selecting one writes every parameter, so a
preset is a starting point and never a layer — turn a knob afterwards and only
that knob moves.
1 Fat Bass 7 Whistle 13 Snare 19 Reso Sweep 25 Shine On
2 Lead Solo 8 String Pad 14 Wind 20 Bell
3 Taurus Bass 9 Hard Lead 15 Organ 21 Space Drone
4 Brass 10 Wobble Bass 16 Clav 22 Trumpet
5 Funk Bass 11 Vibrato Lead 17 Sub Bass 23 Detune Stack
6 Flute 12 Percussion 18 Growl Bass 24 Sixth Source
Sixth Source is straight out of the manual: "When the EMPHASIS control is set to 10, the filter breaks into oscillation, and produces a pure sine wave tone. It is thus available as a sixth sound source." All mixer switches off, both keyboard control switches on so that it plays in tune — measured, the self-oscillating filter tracks the keyboard to within 15 cents over two octaves.
Shine On is the four-note theme, entered from a patch sheet. Two sawtooths detuned by four cents, a filter kept fairly closed with just enough emphasis to go nasal, oscillator 3 down in LO doing nothing but vibrato, and the external input fed from the output — the feedback trick, which is where the warmth comes from. Measured against the sheet: the LFO runs at 5.00 Hz and the two oscillators beat at 0.50 Hz, which is what four cents comes to at A3.
The filter contour departs from the sheet on purpose. Its 200 ms attack and 500 ms decay put a slow sweep across the front of every note — measured, the spectral centroid climbed from 246 to 542 Hz over 230 ms and then sagged back to 449 Hz, which reads as a growl. This part wants a horn: one that speaks at once and then holds still. Attack 25 ms, decay 150 ms, sustain 8.5, with the cutoff dropped from −0.5 to −0.9 to pay for the higher sustain, leaves the steady-state cutoff at 788 Hz either way — the colour the sheet asks for — while the centroid now settles inside 46 ms and stays between 420 and 450 Hz.
Two values the sheet leaves open are noted in the preset's comment: the oscillator 3 frequency (the sheet only says the range switch is on LO) and the modulation wheel, which on the instrument is played by hand rather than set. It ships at about ±7 cents — a shimmer rather than a wail — and CC 1 brings in as much as the part wants.
Worth knowing before reaching for the Modulation Mix to clean up a vibrato that is too prominent: it will not help. Measured at equal depth, oscillator 3 smears the spectrum to 20 dB harmonic-to-rest while the noise source only reaches 46 dB, so mixing more noise in makes the tone cleaner, not rougher. What sets how much the vibrato is heard is the modulation wheel.
Program Change selects a preset. Pitch Bend travels as far as the Bend
parameter says (default two semitones; the original is fixed at about half an
octave). Velocity is ignored — the keyboard of a Model D produces a gate, not
a velocity, and every key sounds at the same level however it is struck.
Notes are accepted across the full MIDI range. A Model D has 44 keys, but its pitch comes from a control voltage and nothing in the circuit stops that voltage going past the ends of its own keyboard. (PicoFaceSM folds stray notes back into its manual, because a divider organ genuinely has no note outside it. Doing the same here would take the bottom octave of a 61-key controller away from a synth whose main job is bass.)
Controller numbers follow the usual meanings wherever one exists — CC 1 modulation, CC 5 and 65 portamento, CC 7 volume, CC 71 resonance, CC 73/75 attack and decay, CC 74 cutoff, CC 91 and 93 reverb and chorus depth — so a generic controller does something sensible before anyone opens a mapping editor. Everything else sits on controllers the MIDI specification leaves undefined, never on a reserved one.
| Section | Control | CC | Control | CC | |
|---|---|---|---|---|---|
| Controllers | Tune | 3 | Mod Wheel | 1 | |
| Glide | 5 | Osc Modulation | 14 | ||
| Glide switch | 65 | Osc 3 Keyboard | 15 | ||
| Mod Mix | 9 | Bend range | 20 | ||
| Oscillator 1 | Range | 21 | Waveform | 22 | |
| Oscillator 2 | Range | 23 | Waveform | 25 | |
| Frequency | 24 | ||||
| Oscillator 3 | Range | 26 | Waveform | 28 | |
| Frequency | 27 | ||||
| Mixer | Osc 1 volume | 29 | Osc 1 on | 102 | |
| Osc 2 volume | 30 | Osc 2 on | 103 | ||
| Osc 3 volume | 31 | Osc 3 on | 104 | ||
| Noise volume | 85 | Noise on | 105 | ||
| Noise colour | 106 | ||||
| Feedback volume | 86 | Feedback on | 107 | ||
| Modifiers | Cutoff | 74 | Filter modulation | 108 | |
| Emphasis | 71 | Keyboard control 1 | 109 | ||
| Amount of Contour | 87 | Keyboard control 2 | 110 | ||
| Filter attack | 88 | Filter decay | 89 | ||
| Filter sustain | 90 | Decay switch | 111 | ||
| Loudness attack | 73 | Loudness decay | 75 | ||
| Loudness sustain | 76 | ||||
| Output | Volume | 7 | A-440 | 112 | |
| Vintage | Drive | 70 | Note priority | 113 | |
| Drift | 77 | Trigger mode | 114 | ||
| Tone | 78 | Transpose | 115 | ||
| Effects | Slot A | 116 | Slot B | 117 | |
| Chorus | Rate | 12 | Depth | 13 | |
| Mix | 93 | Feedback | 16 | ||
| Delay | Time | 17 | Feedback | 18 | |
| Mix | 19 | Tone | 79 | ||
| Reverb | Size | 80 | Damping | 81 | |
| Mix | 91 | Width | 82 |
Channel messages: CC 64 sustain pedal, CC 120 all sound off, CC 123 all notes off, CC 121 reset all controllers. A parameter arriving over MIDI updates the display as well as the engine, so the screen never shows a value the sound no longer has.
Press m in the host test to print this map from the table itself.
A Model D has none. These sit behind the output stage as a section of their own, and every factory preset but one leaves both slots empty, so the dry instrument stays what the firmware sounds like. The exception is Shine On, where a delay is part of the sound rather than a decoration on it: slot A a 430 ms delay whose repeats darken as they go, slot B a hall under it. That preset measures P31 on the device against P21 for a dry one. They are also the only thing in the signal path that produces a stereo image at all — the voice is mono, as the original is.
Three effects, two slots. Each slot is empty or holds one of them, and the signal runs slot A then slot B, so the order is the player's choice: a chorus into a reverb is not the same thing as a reverb into a chorus. There is exactly one instance of each effect, which halves the memory a two-slot design would otherwise need and makes the same effect in both slots impossible rather than merely discouraged (the second slot is skipped).
| Chorus | three modulated lines around a 14 ms base delay, their LFOs a third of a cycle apart. Rate, Depth, Mix, Feedback |
| Delay | one mono line up to 750 ms, two taps, the right one reading 8 % earlier for width. The tone control sits inside the feedback loop, so each repeat returns darker than the last. Time, Feedback, Mix, Tone |
| Reverb | eight comb filters into four all-passes per channel, the Schroeder arrangement by way of Freeverb. Size, Damping, Mix, Width |
Two slots and not three is what bounds the cost. Measured against the peak load the firmware reports on the device, where the dry engine sits at 21 %:
| Slots | Host | → board |
|---|---|---|
| dry | 0.185 % | 21 % — measured on the device |
| Delay | 0.208 % | 23 % |
| Chorus | 0.238 % | 27 % |
| Reverb | 0.256 % | 29 % |
| Delay → Reverb | 0.269 % | 31 % — measured on the device |
| Chorus → Reverb | 0.301 % | 34 % |
The two marked rows are readings from the display footer, the rest are scaled from the host figures against them. Worth recording that the method holds: the delay-into-reverb row was extrapolated at 30 % before anyone played a note on the hardware, and the device then reported 31 % playing the Shine On part.
Memory is the larger cost: 255 kB of delay lines, which takes the firmware from 19 kB of RAM to 276 kB of the 512 kB available. A third simultaneous effect would have bought very little and spent the headroom that keeps the audio glitch-free while the display is being pushed out.
Nothing the panel can be set to reaches the hard clip in the I2S conversion. Two effects in series each with their own wet level can sum past full scale — a delay at maximum feedback into an undamped reverb measured 1.63 — so the section ends in a limiter that is exactly linear below 0.70 and asymptotic to 1.0 above it. Every pair of slots at maximum settings is part of the self test, watched for twenty seconds of tail after the note is released.
git submodule update --init --depth 1
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build buildResult: build/picofacemd.uf2. Target platform rp2350-arm-s, board
sparkfun_promicro_rp2350, 444 MHz. The scaffolding, hardware layer,
audio subsystem, USB MIDI and display are taken from
PicoFaceRD.
| Flash | 92.4 kB (0.55 % of 16 MB) |
| RAM | 276 kB (53 % of 512 kB), of which 255 kB is effect delay lines |
| of which sound engine | 27.5 kB code and rodata, 6.4 kB of that the preset table |
Core 0 runs the audio producer in the main loop together with USB, MIDI, the controls and the display; the DMA IRQ stays microscopic. Unlike PicoFaceRD there is no voice worker on core 1 — a Model D is one voice, and even oversampled it costs well under half a core. Core 1 is free.
| Option | Default | |
|---|---|---|
MD_DOUBLE_RESET |
OFF |
double-tap RESET enters BOOTSEL |
MD_OVERSAMPLE |
2 |
oversampling factor of the sound engine (1, 2 or 4) |
MD_SAFE_MODE |
OFF |
no veeprom, one continuously held note, UART progress marks |
The oscillators are band-limited by polyBLEP, so they alias very little on
their own. What aliases is the two saturating stages — the overdriven mixer
and the tanh in the ladder. Both are soft, so their harmonics decay quickly,
and measuring all three settings shows where the knee is:
| sawtooth | narrow pulse | compute | |
|---|---|---|---|
| 1× | −48.5 dB | −44.3 dB | 748× real time |
| 2× | −64.7 dB | −58.5 dB | 508× real time |
| 4× | −64.7 dB | −57.9 dB | 255× real time |
(worst non-harmonic component relative to the fundamental, across notes C4 to C7, filter wide open.)
2× buys 16 dB over no oversampling. 4× buys nothing at all — past that point what is left is no longer aliasing but the noise floor of the decimation filter, and it costs twice the compute to not improve. Hence the default.
Whatever is left above 22 kHz is removed by a 6th-order Butterworth at 15 kHz before decimation. That corner is not a compromise: the audio path of the original rolls off in the same region, so the anti-alias filter doubles as the vintage bandwidth the sound is expected to have.
Worth recording, because it cost real time and the obvious explanation was wrong. For a while the firmware would not start at 480 MHz while 444 MHz was rock solid, which looked like the chip simply running out of silicon margin. It was not. The cause was the flash timing during the transition.
pico_init() writes a conservative QMI M0_TIMING before raising clk_sys
and the aggressive one after. Between those two writes the flash briefly runs
at the new, high system clock with the old divider and RXDELAY:
| target | transitional flash clock | steady-state flash clock |
|---|---|---|
| 444 MHz (old code) | 111 MHz @ RXDELAY=2 |
148 MHz @ RXDELAY=3 |
| 480 MHz (old code) | 120 MHz @ RXDELAY=2 |
120 MHz @ RXDELAY=3 |
RXDELAY compensates a round-trip delay that is fixed in nanoseconds, so the
value needed grows with clk_sys. RXDELAY=2 was just enough at 111 MHz and
just short at 120 MHz — the core hung on the first instruction fetch after the
clock switch. Note the giveaway: the 480 MHz build ran the flash slower in
steady state (120 MHz) than the 444 MHz build that worked (148 MHz), so the
flash chip was never the limit. Only the transitional window was. 444 MHz was
on the working side of that edge, but with no margin worth the name.
The fix is PICOFACE_QMI_M0_TIMING_SAFE (CLKDIV=8, RXDELAY=2), which puts
the window at 55 MHz. With it, 480 MHz booted reliably — the diagnosis was
confirmed on the device rather than argued. 480 MHz was then removed again:
peak load stays under a third of one core even with 2× oversampling, the
Model D does not need the headroom, and at a core voltage of 1.60 V the slower
clock is the kinder choice when it costs nothing.
The same defect was present in PicoFaceRD, PicoFaceCP, PicoFaceDX and PicoFaceYC, which share this hardware layer. RD ran at 480 MHz unconditionally and showed exactly this symptom — it would not restart after the USB cable was unplugged. All four have been fixed and confirmed.
Two smaller things were corrected in the same place:
__dsb()/__isb()around the timing writes. The register write leaves over APB while the instruction fetches that follow reach the same peripheral over the XIP path — two routes to one endpoint, not ordered against each other. The same barrier was missing inveeprom.cpp, where only a compiler barrier guarded the post-flash-write timing restore.set_sys_clock_hz()is called withrequired=falseand its result was discarded. On an unreachable target it silently leavesclk_sysat 150 MHz; the aggressive flash timing is now only applied if the switch succeeded.
pico_bootsel_via_double_reset is not linked in. With a Waveshare Pico
Audio board driving 3 W speakers, the inrush current on plug-in dips the
supply, the chip performs a brownout reset — and the library reads that as a
double tap and enters BOOTSEL mode instead of running the program. On the
RP2350 the flag lives in the POWMAN register chip_reset.DOUBLE_TAP, which
survives the dip, so shortening the detection window does not help. With
headphones instead of speakers it does not occur.
The BOOTSEL button keeps working regardless.
./test/build_moog.sh
./test/moog_testRequires PortMidi (brew install portmidi). Opens a virtual MIDI input named
moog and plays through CoreAudio; the key bindings are documented at the top
of test/moog_test.cpp. This compiles the same src/moog/ sources as the
firmware, with -DMOOG_HOST_BUILD replacing the Pico audio subsystem.
./test/moog_test --selftestruns without an audio device, a MIDI port or a terminal: every preset is checked for level and for NaN, the panel is driven to both extremes to prove the engine cannot be made to blow up, and the controller map and the full front panel are printed. This is the form to reach for when something sounds wrong and the question is whether the engine or the wiring is at fault.
Monophonic, as the original is. Which held key sounds is the Priority
setting: Low is the Model D's own rule and the default, High and Last
are offered because a mono synth played from a MIDI keyboard is a different
proposition from one played from its own 44 keys.
Single trigger by default: holding one key and pressing another moves the
pitch without restarting the contours. That legato behaviour is a large part
of how the instrument phrases. Trigger switches it to Multi, where every
new key retriggers.
A retrigger does not start from zero. There is nothing in the circuit to discharge the contour capacitor when a new key goes down, so it simply starts charging again from wherever it is — which is why fast repeated notes on a Model D swell rather than restarting cleanly.
The DECAY switch governs both contours at once, as on the instrument: off, a
released key stops almost immediately; on, it falls at the decay time. That
one switch is why a Model D can play a tight bass line and a long pad without
anything else being touched.
Glide is two controls, not one. Glide Sw enables it, Glide sets the
time — and at a time of zero the switch has nothing to do, so it does nothing.
That is how the instrument behaves, but it makes for a switch that looks
broken, so every preset carries a usable glide time (60–270 ms) even where
its switch ships off. Flipping the switch on therefore always changes
something. The two controls share one page (CTL GLIDE) so both are visible
at once.
Glide only exists between two different pitches. The control voltage persists across silence, as it does in the original, so a glide is heard even when the previous key was released before the next one was struck — but a repeated note has nothing to glide to.
| Tuning (drift at 0) | A4 = 440.02 Hz (+0.08 ct), C4 = 261.57 Hz (−0.38 ct) |
| Aliasing, worst waveform and note, filter wide open | −58.5 dB below the fundamental (narrow pulse, C7) |
| Aliasing, sawtooth | −64.7 dB |
| Filter self-oscillation, keyboard tracking over 2 octaves | within 15 cents |
| Peak level of the 25 presets | −17.8 to −4.8 dBFS |
| Every parameter at maximum, note held | 0.46 peak — the engine cannot be driven into clipping from the panel |
| Compute | 510× real time, 0.196 % of one core |
| Relative to the PicoFaceSM engine (10 keys held) | 0.77× |
The last line is the one that matters for the target: the Solina peaks at
30–40 % on this hardware, so this engine should land at roughly 23–31 % —
to be confirmed by the P value in the display footer on the device.
- Note priority is selectable. The original has one rule, lowest note,
and that is the default.
HighandLastare additions. - The external input is a feedback path. The Model D has a microphone preamp on a rear jack, and the thing players actually do with it is patch the output back in. There is no input jack on this hardware, so the mixer's fifth channel is that feedback loop directly.
- Feedback is tapped before the main volume. On the instrument the loop runs through the volume control, so the character changes every time the volume moves. That is a trap rather than a feature; here the tap sits after the amplifier and before the volume.
- Pitch bend defaults to two semitones. The manual gives the wheel "as
much as half an octave up or down". A MIDI controller expects two, so the
travel is a parameter (
Bend, 0–12) rather than fixed. Drift,DriveandToneare panel controls. In the original these are component values, trimmers and the plain physics of a warm circuit board. Drift covers both a slow random walk per oscillator and a fixed per-oscillator tuning error, and both fade out together as the control is turned down — at zero the instrument is mathematically in tune, which no Model D has ever been but which is occasionally what you want.- Presets, and a transposition control. The original has neither.
- The modulation wheel is squared. It reaches 7.2 semitones of pitch modulation at the top, which is what it is for — sirens are a Model D sound. But linearly, a musical vibrato of 20 cents sits at wheel position 0.03, so the first perceptible movement and "unusable" are three per cent of the travel apart. Squaring puts 20 cents at 0.17 and leaves the full depth at the top: the useful range becomes the first fifth of the wheel instead of the first thirtieth. The panel still reads 0…10 linearly, as a volume control does while its pot is tapered.
- Pitch modulation runs at the oversampled rate, filter modulation at the sample rate. Oscillator 3 aimed at the other two at audio rate is a sound this instrument is known for, so that path is worth the fifth-order 2^x it costs. Filter modulation is used for sweeps and wah at LFO rates, where 44.1 kHz is already a very high control rate.
- The waveforms are not ideal shapes. The three rectangular positions use duty cycles of 0.48, 0.29 and 0.14 rather than 0.5, 0.25 and 0.125: the "square" of a Model D is not 50 %, and that asymmetry is audible. The narrow positions are level-compensated, because otherwise the waveform switch would double as a volume control.
- The triangle and sawtooth-triangular waveforms get no polyBLEP. Both are continuous — only the slope steps, not the value — so their harmonics fall off at 1/n² and the eleventh harmonic of a note at the top of the keyboard is already 40 dB down. A polyBLAMP would cost real cycles to correct something sitting under the noise floor of the original. Measured aliasing for the triangle is −64.7 dB.
tanhis a Padé approximant,x(27+x²)/(27+9x²), with the input clamped to ±3. Above that the expression grows again instead of saturating, which inside a feedback loop is not a rounding error but an explosion; at exactly ±3 it evaluates to ±1, so the clamp is continuous. It matchestanhto better than 0.3 % over the range the filter uses, for one division instead of a call into libm.- The voice is mono, duplicated to both channels. This is faithful — the Model D is a mono instrument — and is called out only because the other projects in this family are stereo.
- An effects section. The original has none. Two slots holding a chorus, a delay and a reverb between them, off in every factory preset but Shine On, and the only thing in the path that makes the instrument stereo. See the section above.
GPL v3. The ladder filter model is adapted from BelaMiniMoogEmulation, which its author has stated is under no copyright.