Skip to content

SoundFont instruments: soundfont and soundfont_drums - #176

Merged
bdbarnett merged 9 commits into
mainfrom
sampler/soundfont
Oct 17, 2026
Merged

bdbarnett merged 9 commits into
mainfrom
sampler/soundfont

Conversation

@bdbarnett

Copy link
Copy Markdown
Contributor

Play a SoundFont 2 (.sf2) bank as instruments.

import audioinstruments
piano = audioinstruments.create("soundfont", 48000)        # Program Change n = bank 0's program n
kit = audioinstruments.create("soundfont_drums", 48000)    # bank 128's kits, GM percussion keys

The bank is the bank= option, or $SOUNDFONT, or the first .sf2 in ~/soundfonts. No bank ships with the library; any General MIDI bank works. Samples stay on disk and are read the first time a note needs them.

  • audioinstruments._sf2 reads .sf2 files, written from the SoundFont 2.04 specification. It lists presets, and turns a key and velocity into regions with every generator applied: loop points, tuning, envelope, attenuation, pan, exclusive class and stereo links. Modulators are recorded but not applied; 24-bit samples are read as 16-bit.
  • The instruments play on audiodsp's new audiosampler.Sampler, with 32 voices. Bank Select reaches the other banks, exclusive class chokes (a closed hi-hat stops an open one), and voices are stolen oldest-releasing first. Initial attenuation is scaled by 0.4, the convention banks are voiced for.
  • Macros: Volume, Attack, Release and Tune. Pitch bend is ±2 semitones.

AUDIODSP_PIN moves to 6db15e3 (audiodsp#272), which adds audiosampler. The pin file's entry lists the other changes since v0.6.6, all of them v0.6.7's.

Tests: test_cpython_sf2 (15) and test_cpython_soundfont (11) run against a bank the tests write from scratch. test_cpython_soundfont_gm runs against a real General MIDI bank when one is present and skips otherwise. Against FluidR3_GM, all 128 programs sound and stop on release, and six pitched programs land within 1% at C4. All 47 Standard-kit keys sound, and the choke works.

Not yet: the sustain pedal (CC 64), and a short fade on a choke instead of a cut.

audioinstruments._sf2 opens an .sf2, reads the hydra, and turns a preset,
key and velocity into flat regions: the sample's absolute frames with the
fine and coarse address offsets applied, root key and tuning in cents,
loop mode, the volume envelope in seconds and linear levels, attenuation,
pan, exclusive class and the stereo link. Instrument global zones supply
defaults, local zones override, preset generators add, ranges intersect,
and the generators a preset may not carry are ignored there. Samples
stay on disk and read_sample() fetches frames on demand; load_samples=True
keeps the chunk in memory on the desktop. Modulators are only noticed;
sm24 is ignored. Runs on CPython and MicroPython.
tests/support/sf2_builder.py writes a tiny .sf2 from the specification:
three presets including a bank-128 kit, a global zone at both levels,
split key ranges, a velocity split, every loop mode, an exclusive-class
pair and a stereo pair. tests/test_cpython_sf2.py checks presets() and
regions() exactly; tests/support/sf2_smoke.py builds and checks the same
bank on any interpreter, MicroPython unix included.
audioinstruments.soundfont plays a bank's melodic presets under one
program change, with Bank Select for the other banks, and
audioinstruments.soundfont_drums plays bank 128 with the GM percussion
map as its pads. The bank comes from the bank= option, $SOUNDFONT or
~/soundfonts; the hydra is read at create and samples the first time a
note needs them. initialAttenuation is scaled by 0.4, the convention the
banks were voiced for. Exclusive classes choke, and a note with every
voice busy steals the oldest releasing one.
A stereo pair in one exclusive class stopped its own first half when
the second region's choke ran: the hi-hats and guiros of a GM kit came
out right-channel only.
…DI gate

Eleven tests on the bank sf2_builder writes: finding the bank, patches
and pads, one voice per region, the velocity split, release, the choke,
Bank Select, voice stealing and the macros. Six more against
FluidR3_GM.sf2 when it is at ~/soundfonts (skipped otherwise): all 128
programs sound and fall silent within their release, six pitched presets
within 1% of middle C, every key of the Standard kit, the closed hi-hat
choking the open one, and 16 held notes measured against the block.
@bdbarnett
bdbarnett merged commit a3f4a94 into main Oct 17, 2026
6 checks passed
@bdbarnett
bdbarnett deleted the sampler/soundfont branch October 17, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant