FluidSynth / fluidsynth

Software synthesizer based on the SoundFont 2 specifications
https://www.fluidsynth.org
GNU Lesser General Public License v2.1
1.81k stars 252 forks source link

Support for SF2 RMIDI files with embedded soundfonts #1356

Open spessasus opened 1 month ago

spessasus commented 1 month ago

FluidSynth version

Execute fluidsynth --version and provide the output.

FluidSynth runtime version 2.3.5
Copyright (C) 2000-2024 Peter Hanappe and others.
Distributed under the LGPL license.
SoundFont(R) is a registered trademark of Creative Technology Ltd.

FluidSynth executable version 2.3.5
Sample type=double

SF2 RMIDI specification

The feature request

Fluidsynth would be able to open RIFF midi (.rmi) files. Bonus points for also reading the embedded sf2 file and using it.

Simple solution

  1. check if the file starts with RIFF instead of MThd
  2. check if the data chunk starts with RMID
  3. The first chunk is called data. The data of the chunk is the MIDI file (complete with MThd, etc).
  4. Loop through the following chunks (there may be a metadata INFO chunk. Read the DBNK chunk which is the soundfont's bank offset, others can be ignored)
  5. Find the chunk with header RIFF and the data that starts with 'RIFF' or 'DLS '. This is the embedded soundfont or dls. The provided file has an sf2 embedded, not dls.

Steps to reproduce

Please explain the steps required to duplicate the issue, esp. if you are able to provide a sample application. E.g. how to start fluidsynth, what shell commands to enter, what midi events to send, etc.

  1. get an .rmi file Example files
  2. fluidsynth soundfont.sf2 the_file.rmi
derselbst commented 1 month ago

RMID is a different file format than MIDI, which is currently not supported. Hence, this is a feature request, not a bug. And to be frank, my personal motivation to support this is nearly non-existent. Fluidsynth's builtin MIDI player is very basic. Yet, I would probably accept a PR for this.

spessasus commented 1 month ago

I wrote a complete and precise version of the SF2 RMIDI standard and I've updated the link in the issue.

Also, here's another test file: Field.rmi

derselbst commented 1 month ago

I didn't look up any official RMID spec. But pls. keep in mind that RIFF usually has a 16-bit alignment requirement. The embedded SF probably fulfills this requirement. I'm not sure if the MID has to, but if is has, it won't be a simple "read MIDI from memory" operation. One would first has to get rid of the padding bytes.

spessasus commented 1 month ago

I didn't look up any official RMID spec. But pls. keep in mind that RIFF usually has a 16-bit alignment requirement. The embedded SF probably fulfills this requirement. I'm not sure if the MID has to, but if is has, it won't be a simple "read MIDI from memory" operation. One would first has to get rid of the padding bytes.

The pad bytes aren't taken into account within the RIFF chunks. That's how the specification describes it and that's how Microsoft's RMIDIs are made as well.

image As you can see, the chunk size is odd, while the actual chunk has bytes ad the end.

MIDI file i used

spessasus commented 1 month ago

Also I've linked the specification for the SF2 RMIDI format, which is backwards compatible with the DLS version, so simply using that would work.

derselbst commented 3 weeks ago

Ok. So then my preference of implementing this would be to teach both, the MIDI player and the soundfont loader to handle and accept rmid files (rather than memory mapping the file and loading from memory).

spessasus commented 3 weeks ago

Ok. So then my preference of implementing this would be to teach both, the MIDI player and the soundfont loader to handle and accept rmid files (rather than memory mapping the file and loading from memory).

Yes, but the soundfont loader also has to load the DBNK chunk from the INFO chunk to set the bank offset correctly.

spessasus commented 2 weeks ago

Okay, I think I need to clear things up:

So, here's the official doc from MMA (via internet archive): https://web.archive.org/web/20110610135604/http://www.midi.org/about-midi/rp29spec(rmid).pdf

This (official) version only supports DLS and poses a few issues:

  1. DLS is not used anymore, like at all, where soundfonts still are, and some are really, really high quality.
  2. This stash of the DLS RMIDIS shows me a few issues:
    • Some of the files assume a GM soundbank at bank 0 and DLS at bank 1 (see AWEBLOWN.rmi)
    • Some however, assume DLS loaded at bank 0. (see PMDawn.rmi)
    • My program currently gets around this by searching for all bank selects and if it finds one that isn't 0 or 127, then the bank offset is 1, otherwise 0. But this solution isn't ideal.
  3. See #1373. This does not apply to sf2 in fluidsynth.

This is why I propose to add the new SF2 Version that I created with help of Zoltán Bacskó from Falcosoft. This version solves all of the problems mentioned above:

  1. It uses soundfonts instead of DLS. It also allows SF3 compression which further reduces the file size.
  2. It explicitly states the bank offset the soundfont shall get loaded at.
  3. It also allows for more metadata than the old RMI files.

Getting it supported in fluidsynth would help a lot in it getting more popular as fluid is a very well known sf2 synth. I also think that implementing it is rather easy as it's just:

  1. Load the RIFF LIST.
  2. load the data chunk as SMF.
  3. Load the inner RIFF chunk as a soundfont bitstream.
  4. Check for INFO chunk and DBNK chunk within.
  5. If present, load the soundfont at a given bank, otherwise at bank 1.

Bonus: this logic should also work for DLS files as they too have RIFF but with DLS instead of sfbk. The thing about DLS is having to either assume one of the offsets or detect it like my program does.