Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project setup
CMAKE_MINIMUM_REQUIRED(VERSION 3.4...3.10)
PROJECT(wildmidi LANGUAGES C VERSION 0.4.6)
PROJECT(wildmidi LANGUAGES C VERSION 0.5.0)

# WildMIDI Version
SET(WILDMIDI_VERSION ${PROJECT_VERSION})
Expand All @@ -12,10 +12,12 @@ SET(VERSION 2.1.0)
# Find Macros
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

INCLUDE(CMakePushCheckState)
INCLUDE(CMakeDependentOption)
INCLUDE(CheckCCompilerFlag)
INCLUDE(CheckCSourceCompiles)
INCLUDE(CheckIncludeFile)
INCLUDE(CheckSymbolExists)
INCLUDE(GNUInstallDirs)
INCLUDE(TestBigEndian)

Expand Down Expand Up @@ -66,6 +68,8 @@ OPTION(WANT_OPENAL "Include OpenAL (Cross Platform) support" OFF)

OPTION(WANT_DEVTEST "Build WildMIDI DevTest file to check files" OFF)

OPTION(WANT_SF2 "SoundFont2 (SF2) support via TinySoundFont" ON)

CMAKE_DEPENDENT_OPTION(WANT_MP_BUILD "Build with Multiple Processes (/MP)" OFF "WIN32;MSVC" OFF)
CMAKE_DEPENDENT_OPTION(WANT_OSX_DEPLOYMENT "OSX Deployment" OFF "APPLE" OFF)

Expand Down Expand Up @@ -189,6 +193,7 @@ CHECK_INCLUDE_FILE(inttypes.h HAVE_INTTYPES_H)
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)

# UNIX-like environments
cmake_push_check_state()
IF (UNIX AND NOT APPLE)
# find our math lib
FIND_LIBRARY(M_LIBRARY m)
Expand All @@ -197,10 +202,16 @@ IF (UNIX AND NOT APPLE)
SET(M_LIBRARY "")
SET(PKG_PRIVATELIBS "")
ELSE()
LIST(APPEND CMAKE_REQUIRED_LIBRARIES m)
SET(PKG_PRIVATELIBS "-lm")
ENDIF()
ENDIF()

check_symbol_exists(powf math.h HAVE_POWF)
check_symbol_exists(expf math.h HAVE_EXPF)
check_symbol_exists(sqrtf math.h HAVE_SQRTF)
cmake_pop_check_state()

# ######### General setup ##########
INCLUDE_DIRECTORIES(BEFORE "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include")
IF (NOT HAVE_STDINT_H) # AND NOT HAVE_INTTYPES_H
Expand Down Expand Up @@ -299,6 +310,12 @@ IF(WANT_PLAYER OR WANT_PLAYERSTATIC)
MESSAGE(STATUS "Enabled audio output backends: ${ENABLED_OUTPUT}")
ENDIF()

IF (WANT_SF2)
SET(TINYSOUNDFONT_DIR "${PROJECT_SOURCE_DIR}/extern/TinySoundFont")
INCLUDE_DIRECTORIES(SYSTEM "${TINYSOUNDFONT_DIR}")
SET(WILDMIDI_SF2 1)
ENDIF()

# Setup up our config file
CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/include/config.h.cmake" "${PROJECT_BINARY_DIR}/include/config.h")

Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ files into audio which is then passed back to the calling application.
The library API is designed so that it is easy to include WildMIDI into
applications that wish to include MIDI file playback.

Version: 0.4.6
Version: 0.5.0
Licenses: GPLv3+ and LGPLv3
Website: https://github.com/Mindwerks/wildmidi

Expand Down Expand Up @@ -38,6 +38,15 @@ Requirements:

CHANGELOG

0.5.0
* SoundFont2 (SF2) rendering support via TinySoundFont, vendored under
`extern/TinySoundFont/`. Enabled by default (`WANT_SF2=ON`); pass either a
`.sf2` file or a config with a `soundfont` directive to `WildMidi_Init`.
* Hardened event-pool and mix-buffer growth against realloc failure across the
Linear/Gauss/SF2 renderers; the event-setup helpers now propagate allocation
errors instead of writing past the buffer.
* Assorted overflow guards and cleanup on init failure paths.

0.4.6
* A lot of player cleanup and refactoring, thanks to initial work
by Azamat H. Hackimov, with addition of several safeguards and
Expand Down
2 changes: 1 addition & 1 deletion amiga/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define PACKAGE_URL "https://github.com/Mindwerks/wildmidi"
#define PACKAGE_BUGREPORT "https://github.com/Mindwerks/wildmidi/issues"

#define PACKAGE_VERSION "0.4.6"
#define PACKAGE_VERSION "0.5.0"

#define HAVE_C_INLINE

Expand Down
6 changes: 3 additions & 3 deletions android/jni/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
#define PACKAGE_NAME "WildMidi"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "WildMidi 0.4.6"
#define PACKAGE_STRING "WildMidi 0.5.0"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "wildmidi"

/* Define to the version of this package. */
#define PACKAGE_VERSION "0.4.6"
#define PACKAGE_VERSION "0.5.0"

/* Version number of package */
#define VERSION "0.4.6"
#define VERSION "0.5.0"

/* Define this to the location of the wildmidi config file */
/* #undef WILDMIDI_CFG "/etc/wildmidi/wildmidi.cfg" */
Expand Down
2 changes: 1 addition & 1 deletion djgpp/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#define PACKAGE_URL "https://github.com/Mindwerks/wildmidi"
#define PACKAGE_BUGREPORT "https://github.com/Mindwerks/wildmidi/issues"

#define PACKAGE_VERSION "0.4.6"
#define PACKAGE_VERSION "0.5.0"

#define HAVE_C_INLINE

Expand Down
67 changes: 67 additions & 0 deletions docs/SF2.md
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).
19 changes: 19 additions & 0 deletions extern/TinySoundFont/LICENSE
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.
11 changes: 11 additions & 0 deletions extern/TinySoundFont/README.md
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.
Loading
Loading