Skip to content

SF2 custom modulators#2355

Merged
borisbat merged 1 commit into
masterfrom
sf2-modulators
Mar 28, 2026
Merged

SF2 custom modulators#2355
borisbat merged 1 commit into
masterfrom
sf2-modulators

Conversation

@borisbat
Copy link
Copy Markdown
Collaborator

@borisbat borisbat commented Mar 28, 2026

Summary

  • Parse SF2 modulator chunks (pmod/imod) — stored on SF2Zone.modulators alongside generators
  • Full modulator evaluation per SF2 spec 9.5.3 — source extraction, transform curves (concave/convex/switch, unipolar/bipolar), two-source multiplication, absolute value transform
  • 10 default modulators from SF2 spec 8.4.1-8.4.10 with zone override logic
  • Replaces hardcoded velocity curve — default modulator 1 now handles velocity-to-attenuation
  • midi_init_reverb() — public function for offline rendering with reverb

Audible improvement confirmed: GeneralUser GS (2445 modulators) renders with near-zero correlation to non-modulated version. Musyng Kite shows peak diff of 2.0 (full range). All three tested SF2 fonts produce measurably different (better) output.

Files changed

File Changes
strudel_sf2.das SF2Modulator struct, source unpacking helpers, parse_modulators, modulators on SF2Zone
strudel_sf2_voice.das Concave/convex curves, source value/transform, sf2_mod_get_value, sf2_apply_modulators, 10 default modulators with override logic, modulator offsets in sf2_create_c_voice
strudel_midi_player.das midi_init_reverb() public function
test_sf2_modulators.das 20 new tests

Test plan

  • 65 SF2 tests pass (22 + 23 existing + 20 new modulator tests)
  • Source unpacking (CC flag, velocity/concave/negative, pitch wheel/bipolar, NONE)
  • Evaluation (NONE->amount, velocity linear, CC7 concave/negative, abs transform, two-source)
  • Parse counts (FluidR3=746, TimGM6mb=455)
  • Voice velocity dynamics (GeneralUser loud vs soft ratio > 1.5)
  • Default modulator dynamics (FluidR3 piano v127 > v64 > v20, range > 5x)
  • Override logic (zone modulator suppresses matching default)
  • All examples compile, lint clean

@borisbat borisbat force-pushed the sf2-modulators branch 4 times, most recently from 2d76754 to a29fb86 Compare March 28, 2026 09:19
Parse SF2 modulator chunks (pmod/imod) and store on SF2Zone. Full modulator
evaluation per SF2 spec 9.5.3: source extraction (velocity, key, CC, pitch
wheel), transform curves (concave/convex/switch, unipolar/bipolar), two-source
multiplication, absolute value transform. All 10 default modulators from SF2
spec 8.4.1-8.4.10 with zone override logic. Replaces hardcoded velocity curve.

Parsing:
- SF2Modulator struct, parse_modulators for pmod/imod chunks (10 bytes/entry)
- Source unpacking: sf2_mod_src_index/cc/negative/bipolar/type
- Modulators stored on SF2Zone alongside generators

Evaluation:
- sf2_concave/sf2_convex: SF2 spec page 73 transform curves
- sf2_mod_source_value: velocity, key, CC, pitch wheel, pitch wheel sensitivity
- sf2_mod_transform_source: unipolar/bipolar, positive/negative, all 4 map types
- sf2_mod_get_value: amount * transform(src1) * transform(src2), abs transform
- sf2_apply_modulators: accumulate per-generator offsets from zone modulators

Default modulators:
- SF2_DEFAULT_MODS: 10 defaults (vel->atten, vel->filter, pressure->vib, CC1->vib,
  CC7->atten, CC10->pan, CC11->atten, CC91->reverb, CC93->chorus, pitchwheel->tune)
- sf2_mod_identity_match + sf2_apply_default_modulators with override logic
- Removed hardcoded sf2_velocity_attenuation (now handled by default mod #1)

Application in sf2_create_c_voice:
- Compute gen_offsets[64] from zone + global + preset modulators + defaults
- Apply offsets to pitch, filter FC/Q, attenuation, pan, all LFO/env routing

Also: midi_init_reverb() public function for offline rendering with reverb

20 new tests: source unpacking, evaluation (6 cases), parse counts (FluidR3=746,
TimGM6mb=455), voice velocity dynamics, default mod dynamics, override logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@borisbat borisbat merged commit 9f06f86 into master Mar 28, 2026
26 checks passed
@borisbat borisbat deleted the sf2-modulators branch April 12, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant