Closed mchehab closed 3 years ago
You may only place set
commands in a config file for those settings that are real-time settings. All the settings you've used are non-realtime. Hence the synth gets created with the default values for those settings. Then, your config file modifies them. Then, it creates the audio-driver with those modified values. fluid_synth_process()
detects a mismatch in the number of audio-channels and returns -1, which we in turn return to jack, and jack kicks us out of the process loop.
Expected behavior Fluidsynth should be able to accept changing the number of audio and effect channels via configuration settings.
No. It's not supported and fluidsynth is telling you:
Warning: 'audio.jack.multi' is not a realtime setting, changes won't take effect.
Warning: 'audio.jack.autoconnect' is not a realtime setting, changes won't take effect.
Warning: 'synth.audio-channels' is not a realtime setting, changes won't take effect.
fluidsynth: error: requested set value for setting 'synth.effects-channels' out of range
set: Value out of range. Try 'info synth.effects-channels' for valid ranges
Warning: 'synth.effects-channels' is not a realtime setting, changes won't take effect.
This bug also affects qsynth, making impossible to use it with more than 2 stereo channel outputs.
It's not impossible. Just use the dedicated commandline flags, either -L 6
or -o synth.audio-channels=6
.
causing fluidsynth to silently crash
Where is that "crash" here? Jack only kicks us out of its processing loop and fluidsynth remains usable - at least for me. I agree, we should provide better user feedback in this case. But I don't see anything else we can do here.
You may only place
set
commands in a config file for those settings that are real-time settings. All the settings you've used are non-realtime. Hence the synth gets created with the default values for those settings. Then, your config file modifies them. Then, it creates the audio-driver with those modified values.fluid_synth_process()
detects a mismatch in the number of audio-channels and returns -1, which we in turn return to jack, and jack kicks us out of the process loop.
Yes, that's the behaviour I detected.
No. It's not supported and fluidsynth is telling you:
Warning: 'audio.jack.multi' is not a realtime setting, changes won't take effect. Warning: 'audio.jack.autoconnect' is not a realtime setting, changes won't take effect. Warning: 'synth.audio-channels' is not a realtime setting, changes won't take effect. fluidsynth: error: requested set value for setting 'synth.effects-channels' out of range set: Value out of range. Try 'info synth.effects-channels' for valid ranges Warning: 'synth.effects-channels' is not a realtime setting, changes won't take effect.
This bug also affects qsynth, making impossible to use it with more than 2 stereo channel outputs.
It's not impossible. Just use the dedicated commandline flags, either
-L 6
or-o synth.audio-channels=6
.
Yeah, using fluidsynth command line works, but I didn't find any way to do something like that using qsynth
frontend. On a quick look on its source, it sounded to me that it is using fluidsynth API calls in order to setup the number of channels, but it is probably passing the number of audio/effect channels too late.
Probably qsynth
is doing something similar to what fluidsynth.c
does. At fluidsynth.c
it first initializes the synthetizer, then it parses the config file, which is too late for "synth." settings.
causing fluidsynth to silently crash
Where is that "crash" here? Jack only kicks us out of its processing loop and fluidsynth remains usable - at least for me. I agree, we should provide better user feedback in this case. But I don't see anything else we can do here.
It is a "crash" in the sense that synth GUIs like qsynth
think that fluidsynth
is properly running, while the Jack output thread has long gone. So, if one sets the number of audio channels to, let's say, 6, it pretends that everything is working.
Anyway, I just added a PR that should be detecting this problem in advance and printing some warnings: https://github.com/FluidSynth/fluidsynth/pull/725
Yet, IMHO, the best would be to let the config file to be able of setting those parameters, either parsing the set commands from it before calling:
synth = new_fluid_synth(settings);
Or by adding an update method that would allow to dynamically change the number of audio channels.
Yeah, using fluidsynth command line works, but I didn't find any way to do something like that using qsynth frontend.
In qsynth you just click on Setup > Audio. In this dialog, you can set up the number of channels accordingly. After closing the dialog, qsynth destroys the synth and audio-driver and recreates them.
Or by adding an update method that would allow to dynamically change the number of audio channels.
Setting up audio channels requires memory allocation. There is no way we can do this on-the-fly when the synth is already running. It would interfere with concurrent rendering calls. That's impossible.
either parsing the set commands from it before calling
Possible, theoretically. One would need to parse the file twice (which is ugly), break the existing API by uncluttering the command handler from the objects it requires for its creation while taking huge care to not break any existing functionality. Incredibly time-consuming, and not very beneficial in the end.
Yeah, using fluidsynth command line works, but I didn't find any way to do something like that using qsynth frontend.
In qsynth you just click on Setup > Audio. In this dialog, you can set up the number of channels accordingly. After closing the dialog, qsynth destroys the synth and audio-driver and recreates them.
I'm currently debugging the issue there. I was assuming that this would be due to the same issue as reported on this issue, , but it is a different one. It seems that there are two problems at qsynth
:
So, The net effect is that, if the number of audio channels is bigger than 2, it gets a Jack terminate message (only displayed on its message log window):
Jack: JackPosixThread::Terminate
Such problems initially seemed to be related to fluid_synth_process()
, but calling fluidsynth
command line interface works. So, it could be due to some unrelated issue - or perhaps due to the lack of some validation either at qsynth or at the API function calls.
Or by adding an update method that would allow to dynamically change the number of audio channels.
Setting up audio channels requires memory allocation. There is no way we can do this on-the-fly when the synth is already running. It would interfere with concurrent rendering calls. That's impossible.
True, but, it would be possible to re-size the buffer when the synth is idle.
Btw, in the specific case of set commands from config files, the problem is that the parsing order is racy. What it does is:
If, at step (2), any command change the non-realtime settings from fluid_synth, things break.
No idea how easy would be to implement it, but a possible approach would be to split new_fluid_synth()
into 2 separate functions: the first one that would be just doing a minimal set of configurations that would make fluid_cmd.c
happy, and a new function would be doing the settings-dependent setup.
either parsing the set commands from it before calling
Possible, theoretically.
One would need to parse the file twice (which is ugly),
IMHO, passing such settings via command line args is a lot uglier :-) :
fluidsynth -jsv -L6 -o "audio.jack.multi=true" -o "audio.driver=jack" \
-o "audio.file.format=s24" -o "audio.periods=2" -o "audio.period-size=256" \
-o "audio.realtime-prio=99" -o "audio.sample-format=24bits" -o "audio.jack.multi=true" \
-o "midi.alsa_seq.id=Synth" -o "audio.jack.id=Synth" -o "midi.jack.id=Synth-midi" \
-o "midi.driver=alsa_seq" -o "midi.realtime-prio=99" -o "shell.port=9800" \
-o "synth.effects-groups=3" -o "synth.cpu-cores=4" -o "synth.midi-bank-select='gs'" \
-o "synth.sample-rate=48000" \
'/usr/share/soundfonts/Live HQ Natural SoundFont GM.sf2' \
'/usr/share/soundfonts/Chateau Grand-v1.8.sf2' \
/usr/share/soundfonts/SGM-v2.01-YamahaGrand-Guit-Bass-v2.7.sf2 \
/usr/share/sounds/sf2/FluidR3_GM.sf2 /usr/share/sounds/sf2/FluidR3_GS.sf2
break the existing API by uncluttering the command handler from the objects it requires for its creation while taking huge care to not break any existing functionality. Incredibly time-consuming, and not very beneficial in the end.
I see the point.
Or by adding an update method that would allow to dynamically change the number of audio channels.
Setting up audio channels requires memory allocation. There is no way we can do this on-the-fly when the synth is already running. It would interfere with concurrent rendering calls. That's impossible.
True, but, it would be possible to re-size the buffer when the synth is idle.
The synth is stateless. It is always "ready-to-render". If you run the fluidsynth executable via cmdline, the synth always renders silence, so it's always busy (as soon as the audio-driver is created). Even if there were an "idle" state and you decide to tear down the channels and re-create them, the synth may start rendering again at any time, even if you are still busy doing this kind of "construction works". One would need synchronization here, and this means the synth would not be real-time safe anymore. So no, it's not possible.
Btw, in the specific case of set commands from config files, the problem is that the parsing order is racy.
Yes, that's clear to me. But it's the best order we can get without breaking other things. We've been through this pain several times already.
the first one that would be just doing a minimal set of configurations that would make fluid_cmd.c happy
That "minimal set" is a fully configured synth. You can put note and CC events in a configuration file as well, configure MIDI router rules, etc. So a half-complete-throw-away-synth is not sufficient.
IMHO, passing such settings via command line args is a lot uglier :-)
Well... that's the idea of UIs like qsynth. Alternatively, write that commandline into a shell file or use an alias. The soundfonts can be safely placed into the config file btw.
but a possible approach would be to split new_fluid_synth() into 2 separate functions: the first one that would be just doing a minimal set of configurations that would make fluid_cmd.c happy
Your idea of splitting new_fluid_synth()
(our core-function) has distracted me from your actual idea. So, coming back to your thought, here is an idea:
In fluidsynth.c
right after creating the fluid_settings_t
object and before creating any other object, we create a fluid_cmd_handler
, which only has settings-related commands registered:
extern fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_synth_t *synth, fluid_midi_router_t *router, fluid_settings_t* settings);
preliminary_handler = new_fluid_cmd_handler2(NULL, NULL, settings);
// parse all set commands:
fluid_source(preliminary_handler, cfg_file);
// continue initializing...
the_final_real_handler = new_fluid_cmd_handler(synth, router);
// parse the entire file again, even the set commands (they won't have changed since the parsing from above)
fluid_source(the_final_real_handler, cfg_file);
// do rest of fluidsynth initialization...
One would only need to add this new function new_fluid_cmd_handler2
, make it a bit smarter than the existing new_fluid_cmd_handler
in terms of "which commands to register if objects were NULL" and replace the handler->synth->settings
things with direct access to the settings
object passed in e.g. handler->settings
.
This should be super simple to do and quick as well. You're welcome to draft a PR for that. Currently, I have a few other points on my agenda.
One would only need to add this new function
new_fluid_cmd_handler2
, make it a bit smarter than the existingnew_fluid_cmd_handler
in terms of "which commands to register if objects were NULL" and replace thehandler->synth->settings
things with direct access to thesettings
object passed in e.g.handler->settings
.This should be super simple to do and quick as well. You're welcome to draft a PR for that. Currently, I have a few other points on my agenda.
Yeah, I can work on something like that. In a matter of fact, I did an experiment like that earlier today:
https://github.com/mchehab/fluidsynth/commit/a43e2e4d08942580d7a573bd08608c9dc840d624
There is one issue on this hack, that you mentioned: it needs to be a little bit smarter, in order to avoid creating some symbols:
fluidsynth: error: Failed to register numeric setting 'synth.reverb.room-size' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.reverb.damp' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.reverb.width' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.reverb.level' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.chorus.level' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.chorus.speed' as it already exists with a different type
fluidsynth: error: Failed to register numeric setting 'synth.chorus.depth' as it already exists with a different type
fluidsynth: error: Failed to register string setting 'midi.portname' as it already exists with a different type
fluidsynth: error: Failed to register string setting 'synth.default-soundfont' as it already exists with a different type
I suspect it shouldn't be hard to change this hack to the approach you suggested.
On an OOT comment, this was what I cooked in order to fix multi-channel support with Jack on Qsynth:
https://github.com/mchehab/qsynth/commit/8f7dabc4931c41174990dd4bec67d20716b8b3e0
One would only need to add this new function
new_fluid_cmd_handler2
, make it a bit smarter than the existingnew_fluid_cmd_handler
in terms of "which commands to register if objects were NULL" and replace thehandler->synth->settings
things with direct access to thesettings
object passed in e.g.handler->settings
. This should be super simple to do and quick as well. You're welcome to draft a PR for that. Currently, I have a few other points on my agenda.Yeah, I can work on something like that. In a matter of fact, I did an experiment like that earlier today:
Added a patch series solving it at PR #737.
FluidSynth version
Describe the bug
When either one of the options bellow:
Are used within a config file, the Jack Kthread terminates, causing
fluidsynth
to silently crash. When this bug happens, it is not possible anymore to manually adjust the connection graph withqjackctl
, or to usefluidsynth
.This bug also affects
qsynth
, making impossible to use it with more than 2 stereo channel outputs.Expected behavior
Fluidsynth
should be able to accept changing the number of audio and effect channels via configuration settings.Steps to reproduce
Create a
config
file with:Call
fluidsynth -v -f config
. The result is:Additional context
The problem is related to the
callback
call at:fluid_jack_driver_process()
function, as this quick hack prevents Jack thread to stop:With this quick hack,
fluidsynth
now returns: