robson / Exceed · a unisynth project

gmprocedural

The General MIDI bank every Windows PC ships, 235 instruments and 495 waves, rebuilt from synthesis parameters instead of samples. Each wave is fitted by measuring it, then listening to it, until the copy sounds like the original.

model family · drawn behindadditive
orange: the original · cyan: the model
230 KB
parameters and skeleton, xz-packed, for the 3.44 MB Windows gm.dls
≤ 512
numbers per wave, in at most four layers. No sample data, no wavetables, no residuals
10
model families: additive, noise, drum, metal, modal, shaker, FM, formant, Karplus-Strong, subtractive
1.4 s
to build the whole bank in your browser (WebAssembly), byte-identical to the native build
239 / 495
waves with a fit score of 70 or better. The rest is where the work continues
the problem

A 64k intro cannot carry 3.4 MB of samples.

General MIDI is the cheapest way to write music: 128 instruments and a drum kit, known to every sequencer. Windows has the bank built in, but macOS, Linux and the browser do not, and the file is © Microsoft / Roland, so it cannot be shipped. The answer here: describe every wave with a small, fixed set of synthesis models, and let a no_std generator rebuild the bank at load time.

A GM wave is an attack and a short loop. The long decay is not in the wave: the note's volume envelope lives in the DLS articulation. Piano 1, waves 289–298, are 4.8 k–13.7 k samples: 0.2–0.6 s of attack, then a loop of 155–2272 samples. So a model only has to reproduce the first half second plus one steady loop, not a ten-second piano decay.

A loop of L samples is periodic in L, so its spectrum sits on the grid rate/L. The 1339-sample loop of wave 293 has a 16.5 Hz grid. Stretched piano partials and beating unisons are already quantised to that grid in the original. Every periodic family therefore renders its loop with integer phasors on exactly that grid. The loop is periodic by construction, with no click and nothing lost.

The structure is copied; only the waves are synthesised. Instruments, regions, key ranges, articulation, rates, lengths and loop points come from the original as a skeleton without audio. The output is a normal RIFF 'DLS ' buffer: any DLS player, the unisynth sampler, WaveSabre's Adultery and the browser DAW all play it unchanged.

attack · free-running · fades into the loop loop · L samples repeated by the player wave 293 · Piano 1 · 22 050 Hz loop spectrum: partials only on k · rate / L (16.5 Hz grid)
the model

Ten families. Four layers. 512 numbers.

A wave is up to four layers, each one of ten fixed model families, placed in a region: the exactly periodic sustain, a free-running attack that fades into the loop, or the whole one-shot. Parameters have ranges and defaults, and the build rejects anything over budget, so a parameter file can never smuggle in a waveform.

layers Σ→gain→harmonic correction→time × band tf→fine envelope env→soft knee 0.9→int16

Harmonic correction: dB gains at harmonics of f0, exact on the loop (DFT, gain per bin, inverse DFT) and a linear-phase FIR of at least four periods on the attack. tf: 20 log-spaced bands × up to 9 rows from 0 to 640 ms, applied by STFT to the attack only. env: up to 24 points from 0 to 400 ms, finer than the STFT can see.

Deterministic by construction. The generator uses only basic IEEE operations: its own sin, exp2, log2, an xorshift RNG seeded with the wave index, and a radix-2 + Bluestein FFT whose twiddles come from the same math. clippy bans f32 transcendentals and mul_add in the crate. The bank is byte-identical natively and in WebAssembly, built whole or in part.

the fit

Measure first. Then search. Keep only improvements.

gmfit never searches blind. It reads the instrument's published physics as priors, analyses the original into a model it can already defend, lets each family propose candidates, and only then hands a narrowed box to CMA-ES. Every step must beat the committed result or nothing is written.

gm.dlslocal only extractskeleton, no audio priorsper GM program analyzepitch · loop · env candidatesevery family CMA-ESseeded, full cov. correctharm · tf · env refinefit ↔ correct keep if betterscore + ear waves/NNN.jsoncommitted gmgen::build()DLS bytes at load The original is read from GM_DLS on the developer's machine. Only structure and parameters are committed.
  1. Priors from the literature

    Every GM program and drum key maps to a prior: model kind, families to try, and physical starting points. Piano inharmonicity B, mallet mode ratios, Hammond footages, TR-808 hi-hat square frequencies, cowbell 540/800 Hz, the vowel formants of a choir. Search ranges are narrowed around them.

  2. Analysis before search

    Pitch on the loop grid, harmonic tables by cluster energy, unison and detune from split partials, inharmonicity, envelopes on windows of at least two periods. Loop vibrato and AM become per-harmonic tables, and the noise floor between partials becomes a periodic noise layer.

  3. Character analysis

    Not every sound is a tone. Onset detection and Morlet-wavelet band tracking find strikes, trills and glides (modal). Collision statistics and per-band ring times find shakers. Analytic-signal chirp tracking finds FM. LPC and syllables find voices, and the T60 band decay finds plucked strings.

  4. Candidates, then CMA-ES

    Each family proposes models; all are scored and the best by rank survives. Then a seeded, full-covariance CMA-ES (Hansen's formulation, rank-one + rank-μ, cumulative step size) tunes the explicit scalars inside the unit box. It is deterministic for a seed and runs in parallel.

  5. Analytic corrections

    What the models cannot express is corrected in closed form within budget: a harmonic gain curve (exact on the loop), a time × band gain grid on the attack, and a fine envelope that catches onset detail finer than the STFT. refine alternates fit and correction.

  6. Keep only improvements

    A result is written only if its rank, the score minus penalties for audible defects, beats the committed one, and report --check fails if any wave drops. Bulk fits run per GM category, and each round ends with listening.

the judge

A score for the search. An ear for the score.

The optimiser needs one number. But one number averages away exactly what a listener hears: a buzz once per period, a missing noise floor, a 2 dB hole before the loop. So the score carries defect flags, and above it sits a hearing model built after PEAQ. Each listening round turned what the ear caught into a new measurement.

# similarity of the played signals
# (loop unrolled to 0.5 s, A-weighted)
score = exp(−( d_spec/W₁ + d_onset/W₂
              + d_env/W₃  + d_loop/W₄
              + d_per + d_mod ))
rank  = score − 0.1 · character flags
              − hearing excess

$ gmfit render 376      # standard kick
score 0.721  tier B   flags: lines

Tiers: A ≥ 0.85, B ≥ 0.70; any defect flag drops a wave to C.

  • d_specMean |dB| of log-mel spectrograms at three STFT sizes, A-weighted per band and floored 50 dB under the peak so inaudible differences do not count.
  • d_onsetThe same comparison on the first 50 ms only, so a kick's click weighs what the ear gives it instead of being diluted over half a second.
  • d_envRMS envelopes every 2 ms over the attack. On the loop, a held note's shimmer is a texture: smoothed shape plus wobble depth.
  • d_loopExact DFT magnitudes of one loop period, compared bin by bin up to Nyquist.
  • d_per · d_modPeriodicity (tone versus noise), and the strongest partial's flutter and pitch movement.
  • flagspitch_off (5 cents), env_off, loop_click, buzz (per-period peakiness), clipped, tone_off, mod_off, lines, transient_off.
  • hearOuter/middle-ear weighting, half-Bark bands, spreading, forward masking, Zwicker specific loudness. It reports what is added or missing beyond a JND, plus flutter, swell, roughness and ring time. It is calibrated so that two independent noises of the same texture count as identical.
where it stands

Honest numbers.

Every wave of both sets is fitted, with no placeholders left. Reeds, pipes, organs and brass score highest: steady harmonic tones are what the families express best. Drums, chromatic percussion, ensembles and sound effects score lowest, because their attacks and noisy textures carry detail the models can only approximate. The scores are strict: a wave can sound right in a song and still score 0.6. In whole songs the per-wave score undersells it: the median song is closer to gm.dls through the procedural bank (≈ 1.9 dB) than through gmplus, another real Roland-derived bank (≈ 4.1 dB).

Windows gm.dls (16-bit samples)3 440 660 B
the same samples, xz -9e≈ 2.9 MB
procedural parameters + skeleton, xz -9e≈ 230 KB
–
gen-gm tiers A / B / C of 495 waves (fitted to Windows gm.dls)
–
gen-plus tiers A / B / C of 555 waves (fitted to gmplus, 32 kHz, 60 stereo pairs)
–
median song timbre distance, gen-gm ↔ gm.dls (dB per band and frame)
–
for scale: gmplus ↔ gm.dls, two real sample banks, same songs

Not done: generation takes 1.14 s natively (1.4 s in the browser) against a 300 ms budget, and the gmplus set is about 8× more audio (12 s in the browser). Fitting continues in the repository; these pages are re-rendered from each committed state.

gen-gm mean scoregen-plus mean score
listen

Four banks, one position, one key.

Windows gm.dls is copyrighted, so it plays as a recording. The other three play live in your browser from the MIDI file: the gmplus Sound Canvas soundfont, and the procedural banks gen-gm and gen-plus, which this page builds on your machine from the committed parameters with gmgen compiled to WebAssembly (about 1.4 s and 12 s). All four use the same engine, so only the banks differ. Keys 1–4 switch banks without losing your place.

papers and sources

Standing on published physics.

Each family, prior and measurement traces back to a paper, a standard or an open-source reference. The per-instrument notes, with every prior, are in research/gm-procedural-instruments.md; the full design in docs/synth/gm-procedural.md.

Synthesis models

  1. X. Serra, J. O. Smith 1990 — Spectral Modeling Synthesis: a sound analysis/synthesis system based on a deterministic plus stochastic decomposition. Computer Music Journal 14(4).additive + noise layers; the analysis split
  2. J. Engel, L. Hantrakul, C. Gu, A. Roberts 2020 — DDSP: Differentiable Digital Signal Processing. ICLR.harmonic-plus-noise with per-frame amplitudes
  3. K. Karplus, A. Strong 1983 — Digital synthesis of plucked-string and drum timbres. CMJ 7(2).ks family
  4. D. A. Jaffe, J. O. Smith 1983 — Extensions of the Karplus-Strong plucked-string algorithm. CMJ 7(2).pick comb, dynamics filter, tuning all-pass, stiffness
  5. V. Välimäki, J. Pakarinen, C. Erkut, M. Karjalainen 2006 — Discrete-time modelling of musical instruments. Reports on Progress in Physics 69.string and body models
  6. J. O. Smith, S. A. Van Duyne 1995 — Commuted piano synthesis. ICMC.body folded into the excitation
  7. B. Bank, S. Zambon, F. Fontana 2010 — A modal-based real-time piano synthesizer. IEEE TASLP 18(4).piano priors, modal family
  8. P. R. Cook 1997 — Physically Informed Sonic Modeling (PhISM): synthesis of percussive sounds. CMJ 21(3).shaker family (PhISEM)
  9. J. M. Chowning 1973 — The synthesis of complex audio spectra by means of frequency modulation. JAES 21(7).fm family; electric piano and tubular-bell presets
  10. J.-C. Risset 1965 — Computer study of trumpet tones. Bell Labs.brightness follows amplitude (additive tilt)
  11. M. R. Schroeder 1970 — Synthesis of low-peak-factor signals and binary sequences with low autocorrelation. IEEE Trans. Inf. Theory 16(1).default partial phases, and why ensembles need random ones
  12. V. Välimäki, A. Huovilainen 2007 — Antialiasing oscillators in subtractive synthesis. IEEE Signal Processing Magazine 24(2).PolyBLEP pulses (metal, formant)
  13. R. Bristow-Johnson — Cookbook formulae for audio EQ biquad filter coefficients.every filter; subtractive applies its complex response per harmonic

Instrument acoustics

  1. N. H. Fletcher, T. D. Rossing — The Physics of Musical Instruments. Springer.membrane and bar mode ratios, steelpans, bells
  2. G. Reid 1999–2004 — Synth Secrets, parts 23–59. Sound On Sound.per-category recipes: drums 33–41, strings 48–51, pipes 52–54
  3. C. Perng, J. O. Smith, T. D. Rossing 2010 — Harpsichord sound synthesis using a physical plectrum model. DAFx.harpsichord and clavinet

Measurement and hearing

  1. ITU-R BS.1387 — Method for objective measurements of perceived audio quality (PEAQ); T. Thiede et al. 2000, JAES 48(1/2).ear model, ModDiff flutter
  2. E. Zwicker, H. Fastl — Psychoacoustics: Facts and Models. Springer.specific loudness, Bark bands, masking
  3. E. Terhardt 1979 — Calculating virtual pitch. Hearing Research 1.outer and middle ear weighting
  4. J. H. McDermott, E. P. Simoncelli 2011 — Sound texture perception via statistics of the auditory periphery. Neuron 71(5).noise is a texture, not a waveform: compare statistics
  5. ITU-R BS.1770 / EBU R 128 — loudness in LUFS.song level matching
  6. D. J. Hermes 1988 — Measurement of pitch by subharmonic summation. JASA 83(1).syllable pitch in voices
  7. J. Makhoul 1975 — Linear prediction: a tutorial review. Proc. IEEE 63(4).formant analysis
  8. C. Torrence, G. P. Compo 1998 — A practical guide to wavelet analysis. BAMS 79(1).Morlet band tracking: trills, strikes, decays

Search and numerics

  1. N. Hansen 2016 — The CMA Evolution Strategy: a tutorial.the optimiser, as formulated there
  2. L. Bluestein 1970 — A linear filtering approach to the computation of the discrete Fourier transform. IEEE Trans. Audio Electroacoust. 18(4).any-length deterministic FFT for loop DFTs

Formats and open code

  1. MIDI Manufacturers Association — Downloadable Sounds Level 1 / 2; General MIDI 1.bank layout, articulation connections
  2. E-mu / Creative — SoundFont 2.04 specification.reading the gmplus targets
  3. P. Cook, G. Scavone — STK, the Synthesis ToolKit.Modal, Shakers, Rhodey, TubeBell, Flute, BlowBotl
  4. É. Gillet — Mutable Instruments Plaits (MIT).analog and synthetic drum engines, hi-hat
  5. Csound — phisem, fof, the vowel formant tables.shakers, choir formants
provenance and thanks

What it was measured against.

Neither sample bank is in the repository or on this site. They are read from the developer's own copies. The repository holds structure and fitted parameters only; the audio here is our own render, published for comparison.

the original

Microsoft / Roland

gm.dls · 1996 · © Microsoft / Roland

Every Windows PC since 98 has made General MIDI sound like this: a GS sound set Roland licensed to Microsoft, in 3.4 MB. It is the target of gen-gm, read from a local copy and never shipped.

the better reference

Distortion Dog

gmplus · Release 1 · fan soundfont

A soundfont built from Roland Sound Canvas samples, longer and cleaner than the Windows bank. gen-plus is fitted to it at 32 kHz, including its layered and stereo zones.

the coverage set

The Very Best of GUS MIDI

archive.org BESTMID1–7 · 1994

About 720 files hand-picked in 1994 as ones that sound good on any GM card. 24 were taken greedily, each adding the most instruments not yet played.

Song credits go to their composers and arrangers, named in the song selector. The renders are for comparison only and are removed on request. Tools: Rust, Bun, SpessaSynth (Apache-2.0; the engine for both the recordings and live playback), LAME, Google Cloud Storage. Part of unisynth, the 64k-intro synth this bank was made for.