Skip to content

Instantly share code, notes, and snippets.

@h4k1m0u
Last active April 11, 2024 20:31
Show Gist options
  • Save h4k1m0u/afab0af633a114a026c5672210a1e1c9 to your computer and use it in GitHub Desktop.
Save h4k1m0u/afab0af633a114a026c5672210a1e1c9 to your computer and use it in GitHub Desktop.
Notes on the soundfront2 and midi formats
#include <fluidsynth.h>
#include <unistd.h>
// global vars have external linkage by default (https://stackoverflow.com/a/4239955/2228912)
static fluid_settings_t *settings;
static fluid_synth_t* synth;
static fluid_audio_driver_t* driver;
// global constants also have external linkages (https://stackoverflow.com/a/13185812/2228912)
// Key for C4 (middle C) = 60 (key code incremented by one when going a half-tone up)
// int c4 = 60;
static const int G4 = 67;
static const int A4 = 69;
static const int B4 = 71;
static const int C5 = 72;
static const int D5 = 74;
// midi channel to play notes on (max for midi = 16)
static const int CHANNEL = 0;
// sleep determines duration (leaves time for note to play)
static const unsigned int DURATION = 500 * 1000; // 500ms
// how hard key was struck (max = 127)
static const int VELOCITY = 127;
/* free() do nothing when called on NULL pointers */
static void clean_up() {
// driver must be the first to delete (depends on others) => reverse order of init.
delete_fluid_audio_driver(driver);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
}
static void play_note(int note) {
fluid_synth_noteon(synth, CHANNEL, note, VELOCITY);
usleep(DURATION);
fluid_synth_noteoff(synth, CHANNEL, note);
}
/**
* Plays first few notes from "Frere Jaques" using Fluidsynth C API
* Frere Jaques (Key of G): https://www.true-piano-lessons.com/frere-jacques.html
* Reference: https://www.fluidsynth.org/api/UsageGuide.html
*
* Build & run:
* $ gcc -lfluidsynth freres_jaques.c -o app
* $ ./app /usr/share/soundfonts/FluidR3_GM.sf2
*/
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("USAGE: %s SF2-FILE\n", argv[0]);
return 1;
}
// settings
settings = new_fluid_settings();
if (settings == NULL) {
printf("Failed to create settings\n");
clean_up();
return 1;
}
// synthesizer
synth = new_fluid_synth(settings);
if (synth == NULL) {
printf("Failed to create synthesizer\n");
clean_up();
return 1;
}
// soundfont
const char* path_sf2 = argv[1];
int id_soundfont = fluid_synth_sfload(synth, path_sf2, 1);
if (id_soundfont == FLUID_FAILED) {
printf("Failed to load soundfont\n");
clean_up();
return 1;
}
// audio driver: alsa, pulseaudio, jack... (must be last one to create)
// starts playing audio (in separate thread) once driver created
driver = new_fluid_audio_driver(settings, synth);
if (driver == NULL) {
printf("Failed to create audio driver\n");
clean_up();
return 1;
}
// play first notes from "Frere Jaques"
for (size_t i = 0; i < 2; i++) {
play_note(G4);
play_note(A4);
play_note(B4);
play_note(G4);
}
for (size_t i = 0; i < 2; i++) {
play_note(B4);
play_note(C5);
play_note(D5);
usleep(DURATION);
}
clean_up();
return 0;
}

SF2 format

  • File format (binary) storing multiple instruments.
  • Stores instrument samples to play MIDI.
  • Samples are stored:
    • For each note of an instrument (e.g. all piano keys).
    • Possibly multiple velocities by key (how hard the note was struck).
  • Polyphone can be used to read sf2 files.
  • soundfont-fluid and freepats-general-midi provide free samples in sf2 format (installed to /usr/share/soundfonts/).
  • Jack2: is an audio server like pulseaudio and alsa but professional audio production thanks to its low latency:
$ sudo pacman -Sy jack2 jack2-dbus
$ jack_control start

Synthesizers

$ sudo vim /etc/timidity/timidity.cfg
...
soundfont /usr/share/soundfonts/FluidR3_GM.sf2 # <= line to add
...
$ timidity <file.mid>
$ fluidsynth <file.sf2> <file.mid>

Midi file

  • Binary file.
  • It doesn't contain samples or instrument sounds.
  • It contains instead notes played, with their pitches, velocities...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment