overdub

fizzik-move

filliformes/fizzik-move · ★ 0

Physical modeling synthesizer for Schwung

Built on Schwung

README from GitHub, MIT

Fizzik

Polyphonic simplex physical-modeling synth for Ableton Move, built on the Schwung framework.

Fizzik makes sound the way physical objects do: a short excitation (a strike, a pluck, a breath of noise) is injected into two simulated resonators — strings, beams, plates, membranes — and what you hear is the object ringing. A single Couple knob cross-feeds the two resonators into each other, so complex, emergent timbres arise from the interaction of two simple structures. That's the simplex idea: richness from simplicity.

Distilled from MechanOdd by odoare (FX-Mechanics); all DSP reimplemented independently in C for the Move.


Quick start

  1. Install via the Schwung Module Store (or MOVE_HOST=move.local ./scripts/install.sh), power-cycle the Move, and add Fizzik as a sound generator.
  2. You land on the Patch page: browse Preset (knob 1), or roll the dice — Rnd Patch (k2) for a new instrument.
  3. Play the pads. Press into a held pad — polyphonic aftertouch makes notes bloom, swell, and sing (pick a feel with AT Preset on the Aftertouch page).
  4. Sculpt live with the global filter (Patch knobs 5–8) — 12 analog voicings.
  5. Got an MPE controller? Set the slot to Receive All, turn MPE On on the last page, and every finger gets its own pitch bend, pressure and timbre.

Architecture

             ┌──────────── Couple (cross-feedback) ────────────┐
             ▼                                                  ▼
MIDI ─► EXCITER ──┬──► RESONATOR A ──┐
        noise      │                  ├─► Balance ─► Amp Env ─► Pan ─┐
        mallet     └──► RESONATOR B ──┘                              │  × 6 voices
        crackle                                                      ▼
                                             ┌───────────────────────┘
                                             ▼
        FILTER (12 voicings) ─► Drive ─► EQ ─► Chorus ─► Delay ─► Reverb
              ─► Width ─► Glue Comp ─► Soft-Clip ─► Lookahead Limiter ─► OUT
  • 6-voice polyphony. Each voice: one exciter, two independent resonators, coupling, amp envelope, stereo spread. Voice stealing picks the quietest voice.
  • Every coupled feedback node is guarded (DC-blocker + soft-limit) so coupling is self-limiting — it saturates like a real object instead of blowing up.
  • All continuous knobs are smoothed (~20 ms, analog-style): no zipper, no clicks.

The resonator models

ModelPhysicsCharacterStructure knob
StringDispersive digital waveguide with tension modulationPlucks, harps, basses, sitarsDispersion (stiffness → inharmonic shimmer)
Beam1D modal morph f₀·n·√((1−b)+b·n²)Kalimbas, xylophones, music boxesString→beam morph (harmonic → n² partials)
PlateKirchhoff plate modes ∝ (m/a)²+(n/b)²Bells, gongs, metallic shimmerAspect ratio (square → long)
Membrane2D wave modes ∝ √((m/a)²+(n/b)²)Drums, toms, skinsAspect ratio

Tension (String & Beam): tension-modulation nonlinearity (Tolonen/Välimäki) — hard hits transiently raise the pitch, which settles as the note decays: the pitch-glide of a hard-plucked string.


Pages & parameters

Jog wheel navigates pages; knobs 1–8 edit the current page.

1 · Patch (home page)

#ParamWhat it does
1Preset31 factory presets (click-free switching, even mid-note; last = Init blank patch)
2Rnd PatchFire-button: new random instrument — exciter + both resonators + couple/balance
3Rnd ExciterFire-button: randomize only the strike — audible on the next note
4Rnd ResonFire-button: randomize only the two resonators (keeps your exciter)
5CutoffGlobal filter cutoff (30 Hz – 18 kHz)
6ResonanceGlobal filter resonance (ladder voicings self-oscillate at max)
7Filter TypeLP / HP / BP / Notch
8Voicing12 filter voicings

The three randomizers are momentary buttons — click to fire. They're tuned to stay musical: darker-leaning, never above the played pitch, level-consistent, and applied through a fast fade so they never click.

2 · Exciter — the strike

#ParamWhat it does
1Exc MixNoise burst ↔ mallet (a single pitched sine cycle)
2CrackleRandom impulse clicks mixed into the strike
3ColorResonant low-pass on the excitation — dark thud ↔ bright snap
4AttackBurst attack, 0.2–40 ms (slow = bowed/breathy onsets)
5DecayBurst length, 2–400 ms (long = scraped/bowed textures)
6Exc ResoResonance of the Color filter (adds a formant-like peak)
7Vel LevelHow much velocity drives loudness
8Vel ColorHow much velocity drives brightness

The exciter shapes the attack of the next note — it doesn't change ringing notes.

3 & 4 · Reson A / Reson B — the two objects

#ParamWhat it does
1ModelString / Beam / Plate / Membrane
2StructureModel-specific (see table above)
3DecayRing time, from dead thunk to near-endless
4DampHigh-frequency damping — for modal models this is the brightness control
5PositionStrike/pickup position — comb-like timbre changes (high = hollow)
6ToneString only: loop brightness filter
7Tune−24…+24 semitones relative to the played note
8TensionString/Beam: nonlinear pitch-glide on hard hits

Detune A vs B a fifth or octave apart, then use Couple — that's where Fizzik sings.

5 · Voice

#ParamWhat it does
1CoupleCross-feedback A↔B. 0 = independent, up = interacting, emergent, alive
2BalanceA ↔ B output mix
3GlidePortamento, up to 500 ms
4Amp AtkAmplitude attack, 0.5–200 ms
5Amp RelRelease, 20 ms–3 s (the resonators keep ringing inside it)
6SpreadPer-note stereo panning width
7DriveWarm saturation (dry/wet blended — silent at 0)
8LevelPatch level

6 · FX

#ParamWhat it does
1ReverbWet mix of the stereo Schroeder reverb
2Rev SizeTail length
3Rev DampTail darkness
4DelayWet mix of the stereo ping-pong delay
5Dly Time30–700 ms (tape-style pitch warp when turned)
6Dly FbkFeedback (echoes cross L↔R)
7Dly ToneEcho brightness
8WidthStereo width (M/S), 50% = as-is

7 · FX 2 (mastering)

#ParamWhat it does
1ToneTilt EQ — dark ↔ bright around 500 Hz
2BodyLow-end weight (~150 Hz shelf)
3ChorusStereo chorus mix
4Cho Rate0.05–6 Hz
5Cho DepthModulation depth
6GlueBus compressor — cohesion and sustain for chords
7Lim DrivePushes into the limiter (maximizer loudness)
8Lim CeilBrickwall ceiling. Defaults low (≈ −3.5 dB) for headphone safety

The output chain ends in a warm soft-clip followed by a 2 ms lookahead brickwall limiter (design borrowed from MechanOdd's Limiter). Whatever you do with resonance, coupling, or the randomizers, the output cannot exceed the ceiling. Raise Lim Ceil if you want more level; it is intentionally never randomized.

8 · Mod — two LFOs

Each LFO: Rate (0.05–20 Hz) · Depth · Shape (Sine/Tri/Saw/Square/S&H) · Target (Off, Cutoff, Pitch, Couple, Balance, Tension, Tone, Reso).

Try: LFO1→Couple (slow sine) for breathing interaction; LFO2→Pitch (S&H, low depth) for broken-machine detune; LFO→Cutoff (square) for rhythmic filter chops.

9 · Aftertouch — press into the sound

Move's pads send polyphonic aftertouch: each held note responds to its own finger.

#ParamWhat it does
1AT Preset10 curated feels (sets all seven depths at once)
2AT BrightPressure opens the pressed note's resonator brightness
3AT BowPressure re-excites the note — plucks bloom into bowed sustains
4AT CutoffPeak pressure opens the global filter
5AT VibPressure vibrato (per note)
6AT BendPressure bends pitch up (string-tension feel)
7AT Vib RateVibrato speed, 3–9 Hz
8AT CurvePressure response — soft (sensitive) ↔ hard (needs a push)

AT Presets: Off · Gentle · Brighten · Bow · Swell · Vibrato · Expressive (default) · Cello · Wild · Sforzato.

Channel aftertouch (from external MIDI) is also supported.

10 · MPE — per-note expression from external controllers

Plug in an MPE controller (LinnStrument, Seaboard, Osmose, Erae, Push 3…) and every finger gets its own pitch bend, pressure and timbre. Fizzik's bowed/struck physical models are exactly what MPE was made for: slide between notes, bow one string of a chord, brighten a single voice.

Setup: set the Schwung slot's Receive channel to All and leave Forward on Auto or Thru (so the per-note channels reach the synth untouched — an explicit forward channel would flatten them), then turn MPE On here. Leave your controller on its default MPE layout (lower zone, 48-semitone bend) and it just works.

#ParamWhat it does
1MPEOff / On. Off = classic behaviour (channels ignored)
2ZoneLower (master ch 1, notes on 2–16) or Upper (master ch 16, notes on 1–15)
3Bend RangePer-note pitch-bend range in semitones (1–96, default 48 — match your controller)
4Mstr BendMaster-channel / global bend range (0–24, default 2)
5PressureHow much per-note pressure drives the Aftertouch engine
6TimbreDepth of the controller's Y axis (CC74)
7Timbre TgtWhat Y does: Bright (resonator brightness, centred), Bow (continuous bowing), Vib (vibrato depth) or Cutoff (global filter)
8Bend GlidePitch-bend smoothing, ~1–60 ms (low = precise slides, high = liquid)
  • Pressure goes through the Aftertouch page — so every AT Preset (Bow, Cello, Swell…) is instantly an MPE pressure preset, per finger.
  • Pitch bend works without MPE too: with MPE Off, a normal bend wheel bends all notes by the Mstr Bend range.
  • All MPE settings are part of the global performance layer: they survive preset changes and are saved with your track.
  • The Move's own pads are not MPE (they send polyphonic aftertouch, handled by the Aftertouch page) — this page is for external controllers.
  • Zone and bend range are set with the knobs; the controller's own MPE configuration messages are not read. If slides land flat or sharp, match Bend Range to your controller.

New in 0.2.0. MPE has been verified with generated MIDI streams (per-note bend, channel isolation, pressure, timbre), but not yet with every hardware controller. If yours behaves oddly, please open an issue with the controller model and settings.


Presets (31)

AlienChurch · BowedGlass · CaveStrings · CouncilsPiano · DistortedBass · FeedbackHarp · JudgementAwaits · OldResonances · PreparedPiano · RythmicBow · SensitiveSkin · Sharp · ShockingPluck · Slappy · SurroundedByBells · XyloStyle · GlassKalimba · IronLullaby · TidalGong · HollowReed · StarlightPad · BrokenMusicBox · DeepDiveBass · CopperTongue · GhostSitar · MarbleDrum · WhisperHarp · TitaniumBell · FrozenLake · PulseEngine · Init

Init (last in the list) is a blank neutral patch — twin plain strings, no coupling, no FX — made to be a clean starting point for your own sounds: dial it in, then save it as a user preset.

All presets are level-calibrated (peak and perceived loudness) so browsing never jumps out at you. Presets carry only the instrument — the global performance layer (filter, aftertouch, LFOs, FX 2 mastering) persists across preset changes and randomizes, so your live setup stays put.

The 12 filter voicings

Clean SVF · SEM · MS-20 · Steiner · Ladder 4P · Ladder 2P · Ladder 1P · Prophet · Oberheim · Diode · Sallen-Key · Vintage

Two engines under the hood (trapezoidal SVF + zero-delay-feedback transistor ladder, from the published VA-filter math), level-matched, all working in all four filter types. The ladder voicings self-oscillate at full resonance — bounded, never runaway. The filter is the main tool for taming a patch that's too bright.

Tips

  • Bell into drum: Reson A = Plate (Tune +12), Reson B = Membrane, Balance center, Couple ~30% — struck metal over a resonant skin.
  • Bowing without a bow: AT Preset = Bow or Cello, hold a pluck and press.
  • MPE cello: MPE On, Timbre Tgt = Bow, AT Preset = Vibrato — slide Y to bow each string, press for vibrato, glide between notes with per-finger pitch bend.
  • Dub station: Delay mix + feedback up, then ride Dly Time — tape-style warble.
  • One-button mayhem, safely: park on Patch, hit Rnd Patch between phrases. The limiter ceiling guarantees it never gets dangerous.

Building from source

./scripts/build.sh                      # Docker ARM64 cross-compile
MOVE_HOST=move.local ./scripts/install.sh

Requires Docker (aarch64-linux-gnu-gcc). Single C file, no dependencies. Power-cycle the Move after installing so it reloads the module metadata.

Offline tests

The DSP links natively, so behaviour is checked without a Move (any gcc; the repo uses a fizzik-native Debian image):

gcc -O2 -ffast-math -o /tmp/t scripts/test_state.c src/dsp/fizzik.c -lm && /tmp/t
TestChecks
test_state.cSaved tracks/presets restore exactly (preset, level, every page incl. MPE)
test_mpe.cPer-note bend, channel isolation, bend seeding, channel-aware note-off, CC74 bow
test_filter_stress.c12 voicings × 4 types at full resonance stay bounded; chords don't clip
test_voicing_levels.cAll filter voicings level-matched to Clean SVF
test_levels.cPer-preset loudness calibration meter

scripts/mpe_demo.py (Windows, experimental) plays an MPE test stream into the Move over USB for trying MPE without a controller — run probe first to find the port that reaches Fizzik. It depends on the PC's MIDI service opening the Move's ports, which is not reliable on every machine.

Changelog

0.2.0

  • MPE for external controllers: per-note pitch bend, pressure and timbre (CC74), on a new MPE page (zone, bend ranges, pressure/timbre depth, timbre target, bend glide).
  • Pitch bend now works without MPE too (bend wheel → all notes, Mstr Bend range).
  • On-device help rewritten to match the current pages.

0.1.1

  • Fixed saved tracks reopening on a factory preset; presets/tracks now restore exactly.
  • New Init preset (blank patch).
  • Chords no longer distort: real polyphonic headroom; all 12 filter voicings level-matched.
  • Main page reworked (full filter on knobs, randomizers are buttons, Rnd All removed); both LFOs draw the animated graphic.

0.1.0 — first release.

Credits

Inspired by MechanOdd by odoare (FX-Mechanics). All algorithms reimplemented from published references: J.O. Smith, Physical Audio Signal Processing (CCRMA); Tolonen/Välimäki/Karjalainen, tension-modulation nonlinearity; RBJ Audio-EQ Cookbook; Cytomic/Zavalishin VA-filter design. Built with Schwung by Charles Vestal.

By Filliformes.

License

MIT — see LICENSE.