FluidSynth / fluidsynth

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

MIDI files with embedded MIDI Tuning Standard data are not played correctly #102

Open derselbst opened 12 years ago

derselbst commented 12 years ago

I know fluidsynth already supports the MIDI Tuning Standard (MTS), but something is preventing it from working automatically when I specify a MIDI file (with embedded MTS data) on the command line. This works in timidity, but not in fluidsynth.

I'm including an example MIDI file with MTS data for debugging. It works fine in timidity (meaning the pitches actually come out in 15-per-octave equal temperament) but in fluidsynth I just get silence.

Reported by: keenanpepper

Original Ticket: fluidsynth/tickets/103

derselbst commented 12 years ago

Example MIDI file with embedded MTS data

Original comment by: keenanpepper

derselbst commented 10 years ago

I had the same problem, and after several hours of testing, I found out that while timidity defaults to the tuning at prog 0 bank 0, fluidsynth doesn't.

It means that you have to both set up a MTS tuning, and explicitly select that tuning for the appropriate MIDI channels, using control change messages.

So this is not really a bug. I know this post is now quite old, but maybe this information will help someone else. Personally I have had a hard time finding examples of using the MTS on the web.

Original comment by: erikronstrom

derselbst commented 9 years ago

How would you do that? Do you care to explain?

I've been searching for ways on solving this for weeks now.

Thanks!

Original comment by: depretux

derselbst commented 9 years ago

Quoted from http://www.midi.org/techspecs/midimessages.php:

To set or change the value of a Registered Parameter:

  1. Send two Control Change messages using Control Numbers 101 (65H) and 100 (64H) to select the desired Registered Parameter Number, as per the following table.

  2. To set the selected Registered Parameter to a specific value, send a Control Change messages to the Data Entry MSB controller (Control Number 6). If the selected Registered Parameter requires the LSB to be set, send another Control Change message to the Data Entry LSB controller (Control Number 38).

  3. To make a relative adjustment to the selected Registered Parameter's current value, use the Data Increment or Data Decrement controllers (Control Numbers 96 and 97).

So, to select a tuning program, you should send (in hex): B0 64 03 B0 65 00 B0 06 [tuning program number]

To select a tuning bank, you should send: B0 64 04 B0 65 00 B0 06 [tuning bank number]

This assumes sending it to MIDI channel 0. Use B1 instead of B0 for channel 1, B2 for channel 2 and so on. You can also use "running status" and leave out the B0 for all messages after the first, then the complete sequence would be

B0 64 03 65 00 [tuning program number] 64 04 65 00 06 [tuning bank number]

NOTE: These are the actual MIDI messages. If you are writing to a MIDI file, you have to prepend every message with a delta time, which you probably want to be 0! (This drove me crazy for hours before I could figure it out)

Original comment by: erikronstrom

derselbst commented 9 years ago

I tried changing the tuning bank from within the fluidsynth's command line with:

cc 0 101 0 cc 0 100 3 cc 0 6 [tunning bank number]

But I didn't hear any differences. This is after I both loaded the tuning through the command line and also sent it with pmidi.

Do you know the tuning bank and program fluid defaults to?

I appreciatte your help

Original comment by: depretux

derselbst commented 9 years ago

If I recall correctly, it doesn't default to any tuning program/bank at all - you have to set a tuning program/bank explicitly in order to make it work.

I must admit that I have not actually tested different tunings, so I can't promise that it works. I have only used single note tunings, which (finally) works for me.

Original comment by: erikronstrom

derselbst commented 9 years ago

The .mid files I'm trying actually do that for me, as I can see from fluidsynth's dump.

I'm sure I'm missing something, as I was able to get it working once, but I'm not aware of having done anything different that instance.

Anyway, thanks for your support. I'll put this aside and use command files for the time being. In case I have a solution, I'll post it here.

Bye!

Original comment by: depretux

derselbst commented 7 years ago

mts.zip

mawe42 commented 3 years ago

Even though this is a very old issue, I tried to find out what exactly is going on here. The problem can be recreated with current FluidSynth, playing the attached mts.mid file with FluidSynth results in no audible sound. And I think I now understand the reasons.

First an explanation of what that file actually does: It selects bank 0, prog 0 (piano) to channel 0 and then proceeds to play a number of chords with four notes in each of them. Each chord is played via the MIDI notes 124, 125, 126 and 127. And before each chord is played, there is a SysEx message that uses MTS to assign new frequencies (in the lower, audible range) to those four MIDI note values via tuning bank 0, tuning prog 0 (from now on abbreviated as tuning 0/0).

When playing this file with Timidity, you actually hear the chords and they sound fine (mts_timidity.ogg in attached zip)

When playing it with FluidSynth, you hear nothing. And there are multiple reasons:

  1. FluidSynth does not default to any tuning for a channel. So changing pitches in tuning 0/0 does not affect any notes on any channel.
  2. Even if you manually set the channel tuning to 0/0 while playing the file (which you can do via the shell with settuning 0 0 0), there still isn't any sound output... at least not with the default FluidR3_GM soundfont. The piano in that font only has a note range that reaches up to C7. And the notes in the MIDI file are above that range and will therefore not be played by FluidSynth.
  3. If we use a different soundfont or simply edit FluidR3_GMs piano to go right up to note G8 (127), manually select a tuning 0/0 on channel 0 with settuning 0 0 0, we do finally hear the chords! (mts_fs.ogg in attached zip) But they sound really strange compared the the Timidity output, because FluidSynth takes the samples of the highest key range and pitches them down to the frequency set in the MTS data.

The fact that FluidSynth does not have a tuning active on each channel by default isn't necessarily a bug. I don't think it is clearly defined in the MTS spec that a channel should default to tuning 0/0. But it is something that people who try to use the MTS features of FluidSynth often stumble over (me included). And other synths do seem to handle it differently... well, at least Timidity does. Not having a default tuning on each channel does have some performance benefits for the usual case where no MTS tuning is needed, though. So I think we should keep that behaviour. But I think we could add a little bit of code that automatically assigns tuning 0 / 0 to all channels that haven't got a tuning set already as soon as we receive the first MTS message.

We could also suggest that people simply put settuning commands in their config file and load that with -f on start of FluidSynth... but sadly those commands don't have an effect when executed via the command file (haven't investigated why yet). So that could be another area of improvement: make the tuning commands loaded via config file have an effect.

Then there is the difference in the sample choice between Timidity and FluidSynth. Timidity looks at the effective frequency for of the note after all pitch shifts have been applied, calculates the closest MIDI note number from that frequency and chooses the sample based on that note. FluidSynth chooses the sample based on the effective note and does not care about the frequency at all. I think both approaches work well in certain scenarios and fall down in others. So in my opinion we could simply document FluidSynths behaviour here and leave it as it is.

Finally, here are the two audio files: mts_audio.zip

mawe42 commented 3 years ago

Not having a default tuning on each channel does have some performance benefits for the usual case where no MTS tuning is needed, though.

I think that thought was wrong. In "normal" playing scenarios, the tuning is only evaluated once on a note-on event, so simply assigning tuning 0/0 to all channels by default will probably make no difference to the performance of FluidSynth.

mawe42 commented 3 years ago

I think we should change FluidSynths behaviour and have an active tuning 0 / 0 on all channels by default. As explained above, I don't think it will have any impact on the performance. If you agree, then I could prepare a PR that implements this change and updates the docs accordingly.

derselbst commented 3 years ago

Yes, that would be great!