Add ability to mark channels as important in overflow priority calculation

FluidSynths overflow priority calculation, that determines which voice to
kill if the current polyphony limit has been reached, treats all channels
as equal. Only percussion channels can get a user defined score added to
their priority.

In certain use-cases there can be a number of MIDI channels that are much
more important than other ones, and not just percussion channels. For
example, a channel playing a constant pad sound which would be very
noticeable if killed.

This change adds two new synth.overflow settings:
- synth.overflow.important
- synth.overflow.important-channels

They add the ability to mark MIDI channels as "important" and have
the overflow calculation add a user defined score to voices on those
channels.
This commit is contained in:
Marcus Weseloh 2017-12-02 15:57:21 +01:00
parent 0a57c4cf0a
commit a3aef4b2ee
6 changed files with 175 additions and 2 deletions

View file

@ -127,6 +127,30 @@ https://stackoverflow.com/a/6251757
making them less likely to be killed in an overflow situation.
</desc>
</setting>
<setting>
<name>overflow.important</name>
<type>num</type>
<def>5000</def>
<min>-50000</min>
<max>50000</max>
<desc>
This score is added to voices on channels marked with the
synth.overflow.important-channels setting.
</desc>
</setting>
<setting>
<name>overflow.important-channels</name>
<type>str</type>
<def>""</def>
<desc>
This setting is a comma-separated list of MIDI channel numbers that should
be treated as "important" by the overflow calculation, adding the score
set by synth.overflow.important to each voice on those channels. It can
be used to make voices on particular MIDI channels
less likely (synth.overflow.important &gt; 0) or more likely
(synth.overflow.important &lt; 0) to be killed in an overflow situation.
</desc>
</setting>
<setting>
<name>overflow.percussion</name>
<type>num</type>

View file

@ -98,12 +98,18 @@ static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan,
int param, float value, int absolute);
static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id);
static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels);
/* Callback handlers for real-time settings */
static void fluid_synth_handle_sample_rate(void *data, const char *name, double value);
static void fluid_synth_handle_gain(void *data, const char *name, double value);
static void fluid_synth_handle_polyphony(void *data, const char *name, int value);
static void fluid_synth_handle_device_id(void *data, const char *name, int value);
static void fluid_synth_handle_overflow(void *data, const char *name, double value);
static void fluid_synth_handle_important_channels(void *data, const char *name,
const char *value);
/***************************************************************
@ -184,6 +190,8 @@ void fluid_synth_settings(fluid_settings_t* settings)
fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0);
fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0);
fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0);
fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0);
fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0);
fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0);
fluid_settings_add_option(settings, "synth.midi-bank-select", "gm");
@ -515,6 +523,7 @@ new_fluid_synth(fluid_settings_t *settings)
{
fluid_synth_t* synth;
fluid_sfloader_t* loader;
char *important_channels;
int i, nbuf;
int with_ladspa = 0;
@ -576,6 +585,7 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained);
fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume);
fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age);
fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important);
/* register the callbacks */
fluid_settings_callback_num(settings, "synth.sample-rate",
@ -596,6 +606,10 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_synth_handle_overflow, synth);
fluid_settings_callback_num(settings, "synth.overflow.volume",
fluid_synth_handle_overflow, synth);
fluid_settings_callback_num(settings, "synth.overflow.important",
fluid_synth_handle_overflow, synth);
fluid_settings_callback_str(settings, "synth.overflow.important-channels",
fluid_synth_handle_important_channels, synth);
/* do some basic sanity checking on the settings */
@ -633,7 +647,6 @@ new_fluid_synth(fluid_settings_t *settings)
synth->effects_channels = 2;
}
/* The number of buffers is determined by the higher number of nr
* groups / nr audio channels. If LADSPA is unused, they should be
* the same. */
@ -642,6 +655,16 @@ new_fluid_synth(fluid_settings_t *settings)
nbuf = synth->audio_groups;
}
if (fluid_settings_dupstr(settings, "synth.overflow.important-channels",
&important_channels) == FLUID_OK)
{
if (fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK)
{
FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels");
}
FLUID_FREE(important_channels);
}
/* as soon as the synth is created it starts playing. */
synth->state = FLUID_SYNTH_PLAYING;
synth->sfont_info = NULL;
@ -900,6 +923,8 @@ delete_fluid_synth(fluid_synth_t* synth)
fluid_mod_delete(mod);
}
FLUID_FREE(synth->overflow.important_channels);
fluid_rec_mutex_destroy(synth->mutex);
FLUID_FREE(synth);
@ -3150,11 +3175,13 @@ static void fluid_synth_handle_overflow (void *data, const char *name, double va
else if (FLUID_STRCMP(name, "synth.overflow.age") == 0) {
synth->overflow.age = value;
}
else if (FLUID_STRCMP(name, "synth.overflow.important") == 0) {
synth->overflow.important = value;
}
fluid_synth_api_exit(synth);
}
/* Selects a voice for killing. */
static fluid_voice_t*
fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
@ -5281,3 +5308,79 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
return synth->ladspa_fx;
}
/**
* Set the important channels for voice overflow priority calculation.
*
* @param synth FluidSynth instance
* @param channels comma-separated list of channel numbers
* @return FLUID_OK on success, otherwise FLUID_FAILED
*/
static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels)
{
int i;
int retval = FLUID_FAILED;
int *values = NULL;
int num_values;
fluid_overflow_prio_t *scores;
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
scores = &synth->overflow;
if (scores->num_important_channels < synth->midi_channels)
{
scores->important_channels = FLUID_REALLOC(scores->important_channels,
sizeof(*scores->important_channels) * synth->midi_channels);
if (scores->important_channels == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto exit;
}
scores->num_important_channels = synth->midi_channels;
}
for (i = 0; i < scores->num_important_channels; i++)
{
scores->important_channels[i] = FALSE;
}
if (channels != NULL)
{
values = FLUID_ARRAY(int, synth->midi_channels);
if (values == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto exit;
}
/* Every channel given in the comma-separated list of channel numbers
* is set to TRUE, i.e. flagging it as "important". Channel numbers are
* 1-based. */
num_values = fluid_settings_split_csv(channels, values, synth->midi_channels);
for (i = 0; i < num_values; i++)
{
if (values[i] > 0 && values[i] <= synth->midi_channels)
{
scores->important_channels[values[i] - 1] = TRUE;
}
}
}
retval = FLUID_OK;
exit:
FLUID_FREE(values);
return retval;
}
/*
* Handler for synth.overflow.important-channels setting.
*/
static void fluid_synth_handle_important_channels(void *data, const char *name,
const char *value)
{
fluid_synth_t *synth = (fluid_synth_t *)data;
fluid_synth_api_enter(synth);
fluid_synth_set_important_channels(synth, value);
fluid_synth_api_exit(synth);
}

View file

@ -1687,6 +1687,7 @@ fluid_voice_get_overflow_prio(fluid_voice_t* voice,
unsigned int cur_time)
{
float this_voice_prio = 0;
int channel;
/* Are we already overflowing? */
if (!voice->can_access_overflow_rvoice) {
@ -1734,6 +1735,13 @@ fluid_voice_get_overflow_prio(fluid_voice_t* voice,
}
this_voice_prio += score->volume / a;
}
/* Check if this voice is on an important channel. If so, then add the
* score for important channels */
channel = fluid_voice_get_channel(voice);
if (channel < score->num_important_channels && score->important_channels[channel]) {
this_voice_prio += score->important;
}
return this_voice_prio;
}

View file

@ -42,6 +42,9 @@ struct _fluid_overflow_prio_t
float sustained; /**< Is this voice sustained? Then add this score (usually negative) */
float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */
float age; /**< This score will be divided by the number of seconds the voice has lasted */
float important; /**< This score will be added to all important channels */
char *important_channels; /**< "important" flags indexed by MIDI channel number */
int num_important_channels; /**< Number of elements in the important_channels array */
};
enum fluid_voice_status

View file

@ -1738,3 +1738,36 @@ fluid_settings_foreach (fluid_settings_t* settings, void* data,
delete_fluid_list (bag.names); /* -- Free names list */
}
/**
* Split a comma-separated list of integers and fill the passed
* in buffer with the parsed values.
*
* @param str the comma-separated string to split
* @param buf user-supplied buffer to hold the parsed numbers
* @param buf_len length of user-supplied buffer
* @return number of parsed values or -1 on failure
*/
int fluid_settings_split_csv(const char *str, int *buf, int buf_len)
{
char *s;
char *tok;
char *tokstr;
int n = 0;
s = tokstr = FLUID_STRDUP(str);
if (s == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return -1;
}
while ((tok = fluid_strtok(&tokstr, ",")) && n < buf_len)
{
buf[n++] = atoi(tok);
}
FLUID_FREE(s);
return n;
}

View file

@ -50,4 +50,6 @@ int fluid_settings_register_int(fluid_settings_t* settings, const char* name, in
int fluid_settings_callback_int(fluid_settings_t* settings, const char* name,
fluid_int_update_t fun, void* data);
int fluid_settings_split_csv(const char *str, int *buf, int buf_len);
#endif /* _FLUID_SETTINGS_H */