-
Notifications
You must be signed in to change notification settings - Fork 47
Add sf2 (soundfont) support to wildmidi #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
b39488d
initial sf2 support
psi29a 76b4d8a
fix building on *bsd; add git
psi29a 418d477
coderabbit advice
psi29a 164b71b
critical rendering fix to prevent OOM (mix-buffer growth; realloc-int…
psi29a 1e8d0d0
add tsf in-tree
psi29a 49a0c49
CR event-pool propagation fixes
psi29a 4147d43
purge sf2 tests
psi29a 92d460a
dos2unix tsf.h
sezero 4c5d0a3
eliminate a c99'ism in tsf.h
sezero 395dd4e
bigenedian block for sf2 support; overflow fix for 32-bit; realloc g…
psi29a 9db1e54
do things the right way
psi29a 74d6b85
terminal-event slot fix
psi29a 3ab0b17
internal_midi.c: handle possible absence of SIZE_MAX macro
sezero 9998fc4
tsf.h: fix wrong preprocessor check for TSF_SQRTF.
sezero e073243
tsf.h: do not use addressof operator on arrays.
sezero 36a3a9d
sf2 support: handle possible absence of powf, expf and/or sqrtf.
sezero b1301bd
internal_midi.c: include limits.h
sezero d2c7d00
sf2: big endian support.
sezero a5a4349
tsf.h: fixed "initializer element is not constant" errors from old gcc
sezero 74fb978
bump to 0.5.0
psi29a File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # SoundFont2 (SF2) Support | ||
|
|
||
| Implementation plan and design notes for [issue #8](https://github.com/Mindwerks/wildmidi/issues/8). | ||
|
|
||
| ## Approach | ||
|
|
||
| WildMIDI gains SF2 rendering via [TinySoundFont](https://github.com/schellingb/TinySoundFont) | ||
| (`tsf.h`, MIT licensed, single-header, pure C), fetched at configure time with CMake | ||
| `FetchContent` and pinned to a known-good commit. | ||
|
|
||
| The key insight: WildMIDI's format parsers (MID, XMI, MUS, HMP, HMI) already convert | ||
| everything into a single time-stamped event list (`struct _event`), and the renderer is | ||
| just the stage that consumes that list. SF2 support swaps the *rendering* stage while | ||
| keeping every parser, the timing engine, and the public API untouched. No other MIDI | ||
| library renders XMI/MUS/HMP/HMI through SoundFonts — this is WildMIDI's niche. | ||
|
|
||
| TinySoundFont's common criticism — "you must do your own MIDI parsing" — is exactly | ||
| what makes it the right fit here: its `tsf_channel_*` API maps 1:1 onto WildMIDI's | ||
| already-decoded event types. | ||
|
|
||
| ## How it works | ||
|
|
||
| 1. **Loading** — `WildMidi_Init()` / `WildMidi_InitVIO()` sniff the config file for the | ||
| RIFF/`sfbk` magic (not the file extension). An `.sf2` file given directly as the | ||
| "config file" is loaded through the existing VIO buffer callbacks into one global | ||
| `tsf*` instance. Additionally, the timidity.cfg parser understands a | ||
| `soundfont <file>` directive (relative paths resolved against the config dir, same | ||
| as `source`), so `/etc/timidity/fluidr3_gm.cfg` works as-is. | ||
| 2. **Per-song synth** — each opened midi (`struct _mdi`) gets its own `tsf_copy()` of | ||
| the global instance (sample data shared, voices private), created in `_WM_initMDI()` | ||
| and freed in `_WM_freeMDI()`. Concurrent handles stay safe. | ||
| 3. **Rendering** — `WM_GetOutput_SF2()` mirrors the scheduling loop of | ||
| `WM_GetOutput_Linear()`. Every event still runs its normal `do_event` (tempo, lyrics, | ||
| channel state; GUS note-ons no-op safely since no GUS patches are loaded) and is | ||
| additionally translated to the corresponding `tsf_channel_*` call. Audio is rendered | ||
| by `tsf_render_short()` into the 32-bit mix buffer, so `WM_MO_REVERB` and the rest of | ||
| the output pipeline behave exactly like the GUS path. | ||
| 4. **Release tails** — when the event list ends, rendering continues while | ||
| `tsf_active_voice_count() > 0`, so SF2 release envelopes aren't clipped at | ||
| end-of-song. | ||
|
|
||
| ## Event translation | ||
|
|
||
| | WildMIDI event | TSF call | | ||
| |---|---| | ||
| | note on/off | `tsf_channel_note_on` / `_off` | | ||
| | patch change | `tsf_channel_set_presetnumber(..., isdrum)` | | ||
| | pitch wheel | `tsf_channel_set_pitchwheel` | | ||
| | all controllers (bank, volume, pan, expression, hold, RPN/NRPN, sound/notes off, …) | `tsf_channel_midi_control` (TSF implements the full CC map) | | ||
| | Roland drum-track sysex | `tsf_channel_set_bank_preset(ch, 128, 0)` | | ||
|
|
||
| Channel 9 defaults to bank 128 (drums) at synth creation, per GM convention. | ||
|
|
||
| ## Build | ||
|
|
||
| * `WANT_SF2` CMake option, default `ON`. No network dependency: TinySoundFont is | ||
| vendored at `extern/TinySoundFont/` (MIT licensed, compatible with LGPLv3 | ||
| linking). Refresh by replacing `tsf.h` with a newer upstream revision. | ||
|
|
||
| ## Non-goals (first pass) | ||
|
|
||
| * Mixing GUS patches and SF2 in one config — if a soundfont is loaded it takes over | ||
| rendering entirely. | ||
| * SF3 (ogg-compressed) soundfonts — one `#define TSF_SF3` + stb_vorbis away if wanted. | ||
| * timidity per-soundfont options (`amp=`, `order=`, `remove`) — parsed token is the | ||
| filename only. | ||
| * `WildMidi_MasterVolume()` does not affect the SF2 path yet (TSF has its own gain). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| Copyright (C) 2017-2023 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| this software and associated documentation files (the "Software"), to deal in | ||
| the Software without restriction, including without limitation the rights to | ||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| of the Software, and to permit persons to whom the Software is furnished to do | ||
| so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # TinySoundFont | ||
|
|
||
| Vendored copy of [TinySoundFont](https://github.com/schellingb/TinySoundFont) | ||
| at commit `fbc913531b85f5707f49115110bb86b1cd583885` (2025-07-19), used by | ||
| `src/sf2.c` when built with `-DWANT_SF2=ON`. | ||
|
|
||
| MIT licensed — see `LICENSE`. | ||
|
|
||
| To refresh, replace `tsf.h` and `LICENSE` with a newer upstream revision. Only | ||
| `tsf.h` is compiled; the rest of the upstream repo (examples, tools) is not | ||
| needed. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.