Form and Void

Form and Void

MPE Synth. Formants, and then it gets weird.

We can do flutes

Sad trombones

And whatever this is

…along with a pile of other stuff.

The extra-cool thing about this script is that any parameter you map to a CC on the MPE “main channel” in the Norns interface also gets mapped in an MPE way to the other midi channels.

This script started when I was in the Isolation House to keep my family from getting covid. I felt fine, and I wanted to do some norns dev, so I started porting the exellent minim Teletype script by @modularbeat to norns (forthcoming). Anyway, there I was without a crow or a midi adapter in sight, wanting to hear the result of this sequencer I was porting, so I decided to write an engine. Eight hours later I had the beginnings of a mod matrix, and it was no longer “just” a simple engine so I could hear the chords and the arp from minim with different timbres.

Anyway, this synth works by taking grains of sine waves and 2-op FM of sine waves and playing the individual grains at audio rate. The frequency content of the grains determines the formant frequencies of the sound you hear, and the frequency at which we play them is the fundamental. The length of the grains determines the “sharpness” of the formant in frequency space. You specify two formants, and the relative presence of the two formants and the fundamental in your final sound.

Formant 1 is 2-op FM grains. You can determine the frequencies of the carrier and modulator, and the FM index.

Formant 2 is just a sine… but it can phase-modulate the fundamental directly.

The thing about doing FM with little grains of sound played synchronously at audio rate like this is that unlike standard FM, where you can easily get clangorous tones, everything will always stay harmonic in Form and Void. Sorry, look elsewhere for clangor.


Norns, midi controller or other midi sender thing. Should work with any ol’ midi controller, but it’s best if your controller does MPE or at least sends pressure messages.

If you’re using a Linnstrument or other fancy MIDI controller, you might want to find the setting to slow down the rate of messages just a tad; I found setting the message delay to 320us or so completely eliminated my stuck/missed note problem.


Use E1 to change between screens.

The formant screen helps you see what each wave of your sound is going to look like.


E2 and E3 control the two formants. Without keys, they control the frequency. Holding K2 they control the amplitude of the formant; holding K3 they control the number of waves per cycle.

The modulation screen helps you design the modulation sources for your sound


You get two envelopes and an LFO. Use K2 and K3 to cycle through params to edit; E2 and E3 each edit a parameter.

The matrix screen helps you set up how your sound is modulated.

(I promise this looks better on the norns screen; the gamma didn’t translate right to PNG) Anyway there are a bunch of little angle-y doodads that show you the modulation depth of each thing in the matrix. On the top are the destinations: fundamental, amp of fundamental, formant 1, amp of formant 1, fm index of formant 1, formant 2, amp of formant 2, fm index of formant 2.

The left side are the sources: envelope 1, envelope 2, the lfo, and the MIDI pressure/aftertouch messages.

Use E2 to select a cell in the matrix, E3 to adjust it. K2 will zero it. Hold K3 to make fine adjustments.

There is a little bit more in the params menu, but not much. I do want to call out the anti-aliasing setting — I made the default to have anti-aliasing on, but you can sure play more notes if you turn it off. I was very proud of figuring out what was making that godawful sound that was very evident above C5 or so and how to mitigate it.


in maiden.


Is that last one “arcade bassoon”?


:eyes: do tell! all incarnations of Bitters struggle a little at the top range of the keyboard to some extent

1 Like

excited to try this, from the examples it seems you’ve made the perfect dungeon synth synth :slight_smile:


Exactly where my brain went too! lol


wowowowowow thank you

Well now I gotta get a Norns and an MPE controller? :grimacing:

Sounds super cool!


It seems to me that a band-limited bitters has one problem in common with mine and one problem not in common.

The problem I don’t have is discontinuities / inherently high frequency components that alias in my desired wave shape. This is the realm of blips and bleps and blamps, and their minimum-phase variants, or maybe clever use of band-limited wavetables, and I am of limited aid because I haven’t written that code myself.

My problem is the one Bitters will encounter in sync operation: to trigger grains for every wave cycle, I need to trigger them with an impulse train of some sort. But a naïve impulse train is going to have up to one sample of phase variation in when the impulse arrives, which (I think?) amounts to making a small phase modulation with a frequency like the components of the impulse train that are reflected across Nyquist. The modulation index increases with fundamental frequency, as one sample becomes more and more of a wave, percentage-wise. My solution to this was to use a band-limited square wave to trigger my pulses, and measure where in the rise my trigger occurred to get an estimate of the phase remainder. Then I produced two sin grains, one sample apart, and used my phase estimate to linearly interpolate between them.

Edit: I should add that I did try triggering a grain with an appropriate amplitude for every sample (with an absolute value above a threshold) in a band-limited impulse train, effectively trying to do a live-convolution in the time domain. It worked and had a very clean result…. but only when the threshold was extremely low. Doing this took 20% of a core on my MacBook Pro, and was impossible for Norns.

Edit 2: I think Bitters has the opportunity to export its phase remainder directly as the value used for the sync trigger, so smoothing this phase noise is reduced to considering that value when seeing the initial phase of the reset oscillator.

For Bitters, this paper may help:


The perfect excuse to get an MPE mode going on Teletouch!!


i was just thinking how awesome this would be a few hours ago!

@sixolet, with certain settings i get crackles if i play more than two notes at one time. i believe this may be related to very long attack and decay settings. i got this on two fates devices (one pi 3 and the other a pi 4).

for reference, here is an example pset that i copied from one device to the other as part of confirming the crackles could be procuded (sic) on multiple devices.


– formandvoid
“rev_eng_input”: -10.0
“rev_cut_input”: -10.0
“rev_monitor_input”: -0.74084331830979
“rev_tape_input”: -inf
“rev_return_level”: -9.6432746655329e-16
“rev_pre_delay”: 60.0
“rev_lf_fc”: 200.0
“rev_low_time”: 6.0
“rev_mid_time”: 6.0
“rev_hf_damping”: 6000.0
“comp_mix”: 1
“comp_ratio”: 4.0
“comp_threshold”: -18.0
“comp_attack”: 5.0
“comp_release”: 50.0
“comp_pre_gain”: 0.0
“comp_post_gain”: 9.0
“cut_input_adc”: -9.6432746655329e-16
“cut_input_eng”: -inf
“cut_input_tape”: -inf
“midi target”: 2
“main channel”: 1
“first channel”: 2
“mpe channels”: 7
“bend range”: 12
“pressure”: 4
“the fundamental amp”: 0.28
“the formant 1”: 631.81779621642
“the formant 1 modulator”: 700.0
“the formant 1 index”: 0
“the formant 1 amp”: 0.5
“the formant 1 waves”: 3.38
“the formant 2”: 2497.1358535079
“the formant 2 index”: 1.28
“the formant 2 amp”: 0.2
“the formant 2 gain”: 0.69
“the formant 2 waves”: 1
“the attack 1”: 3.388588179147
“the decay 1”: 0.0011081470572141
“the sustain 1”: 0.25
“the release 1”: 4
“the attack 2”: 2.0
“the decay 2”: 1.0
“the sustain 2”: 0.5
“the release 2”: 0.5
“the lfo freq”: 5.0
“the lfo width”: 0.5
“the env 1 to fundamental”: 0.028
“the env 1 to fundamental amp”: 0.36
“the env 1 to formant 1”: 1.932
“the env 1 to formant 1 amp”: 0.24
“the env 1 to formant 1 index”: 0.56
“the env 1 to formant 2”: 0.0
“the env 1 to formant 2 amp”: 0.62
“the env 1 to formant 2 index”: 0
“the env 2 to fundamental”: 0.0
“the env 2 to fundamental amp”: 0
“the env 2 to formant 1”: 0.0
“the env 2 to formant 1 amp”: 0
“the env 2 to formant 1 index”: 0
“the env 2 to formant 2”: 2
“the env 2 to formant 2 amp”: 0.3
“the env 2 to formant 2 index”: 0
“the lfo to fundamental”: 0.0
“the lfo to fundamental amp”: 0.0
“the lfo to formant 1”: 0.0
“the lfo to formant 1 amp”: 1
“the lfo to formant 1 index”: 0.0
“the lfo to formant 2”: 0.0
“the lfo to formant 2 amp”: 0.1
“the lfo to formant 2 index”: 0.0
“the pressure to fundamental”: 2
“the pressure to fundamental amp”: 0.0
“the pressure to formant 1”: 2
“the pressure to formant 1 amp”: 0.2
“the pressure to formant 1 index”: 1
“the pressure to formant 2”: 0.0
“the pressure to formant 2 amp”: 0.2
“the pressure to formant 2 index”: 0
“the antialias”: 2

cracklings aside, the sounds from the script are complexly divine. here’s an example recording (with just a couple of crackles) following my initial random noodling with the params.


Yea. If you have a long decay, notes will stick around until the decay is over, which will crackle. I’ll look at canceling very quiet lingering notes earlier…

1 Like

Okay this morning before work I implemented a kind of voice-stealing-during-release that should help. On most settings this now allows setting release to 4s and still play chords at a reasonable tempo, or fast melodic parts. We’ll just steak processing from the notes that have released a while ago — their ringing-out will be cut short. There’s still no hard limit on polyphony, and you can still get yourself into crunch territory playing 16th-note 7th chords in a high register.

The engine can handle maybe ten simultaneous notes, including those in release, at most settings with antialiasing on. With antialiasing off, more. Low frequency of formant, fewer. Many waves of formant, fewer. Very high pitch note, fewer. It’s all about the number of sine grains we have to produce simultaneously.