Merge pull request #294 from FluidSynth/overflow-important-channels

New feature for overflow calculation: specify important channels
This commit is contained in:
Tom M 2017-12-05 09:22:54 +01:00 committed by GitHub
commit e0e319cf30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 279 additions and 26 deletions

View file

@ -114,6 +114,96 @@ https://stackoverflow.com/a/6251757
<desc>
Sets the minimum note duration in milliseconds. This ensures that really short duration note events, such as percussion notes, have a better chance of sounding as intended. Set to 0 to disable this feature.</desc>
</setting>
<setting>
<name>overflow.age</name>
<type>num</type>
<def>1000</def>
<min>-10000</min>
<max>10000</max>
<desc>
This score is divided by the number of seconds this voice has been
active and is added to the overflow priority. It is usually a positive
value and gives voices which have just been started a higher priority,
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. Channel
numbers are 1-based, so the first MIDI channel is number 1.
</desc>
</setting>
<setting>
<name>overflow.percussion</name>
<type>num</type>
<def>4000</def>
<min>-10000</min>
<max>10000</max>
<desc>
Sets the overflow priority score added to voices on a percussion
channel. This is usually a positive score, to give percussion voices
a higher priority and less chance of being killed in an overflow
situation.
</desc>
</setting>
<setting>
<name>overflow.released</name>
<type>num</type>
<def>-2000</def>
<min>-10000</min>
<max>10000</max>
<desc>
Sets the overflow priority score added to voices that have already
received a note-off event. This is usually a negative score, to give released
voices a lower priority so that they are killed first in an overflow
situation.
</desc>
</setting>
<setting>
<name>overflow.sustained</name>
<type>num</type>
<def>-1000</def>
<min>-10000</min>
<max>10000</max>
<desc>
Sets the overflow priority score added to voices that are currently
sustained. With the default value, sustained voices are considered less
important and are more likely to be killed in an overflow situation.
</desc>
</setting>
<setting>
<name>overflow.volume</name>
<type>num</type>
<def>500</def>
<min>-10000</min>
<max>10000</max>
<desc>
Sets the overflow priority score added to voices based on their current
volume. The voice volume is normalized to a value between 0 and 1 and
multiplied with this setting. So voices with maximum volume get added
the full score, voices with only half that volume get added half of this
score.
</desc>
</setting>
<setting>
<name>parallel-render</name>
<type>bool</type>

View file

@ -96,12 +96,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);
/***************************************************************
@ -182,6 +188,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");
@ -513,8 +521,7 @@ new_fluid_synth(fluid_settings_t *settings)
{
fluid_synth_t* synth;
fluid_sfloader_t* loader;
double gain;
double num_val;
char *important_channels;
int i, nbuf;
int with_ladspa = 0;
@ -567,21 +574,16 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);
fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);
fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels);
fluid_settings_getnum(settings, "synth.gain", &gain);
synth->gain = gain;
fluid_settings_getnum_float(settings, "synth.gain", &synth->gain);
fluid_settings_getint(settings, "synth.device-id", &synth->device_id);
fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);
fluid_settings_getnum(settings, "synth.overflow.percussion", &num_val);
synth->overflow.percussion = num_val;
fluid_settings_getnum(settings, "synth.overflow.released", &num_val);
synth->overflow.released = num_val;
fluid_settings_getnum(settings, "synth.overflow.sustained", &num_val);
synth->overflow.sustained = num_val;
fluid_settings_getnum(settings, "synth.overflow.volume", &num_val);
synth->overflow.volume = num_val;
fluid_settings_getnum(settings, "synth.overflow.age", &num_val);
synth->overflow.age = num_val;
fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion);
fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released);
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",
@ -602,6 +604,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 */
@ -639,7 +645,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. */
@ -648,6 +653,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;
@ -905,6 +920,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);
@ -3155,18 +3172,20 @@ 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)
{
int i;
fluid_real_t best_prio = OVERFLOW_PRIO_CANNOT_KILL-1;
fluid_real_t this_voice_prio;
float best_prio = OVERFLOW_PRIO_CANNOT_KILL-1;
float this_voice_prio;
fluid_voice_t* voice;
int best_voice_index=-1;
unsigned int ticks = fluid_synth_get_ticks(synth);
@ -5286,3 +5305,77 @@ 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;
}
FLUID_MEMSET(scores->important_channels, FALSE,
sizeof(*scores->important_channels) * scores->num_important_channels);
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

@ -1681,12 +1681,13 @@ fluid_voice_optimize_sample(fluid_sample_t* s)
return FLUID_OK;
}
fluid_real_t
float
fluid_voice_get_overflow_prio(fluid_voice_t* voice,
fluid_overflow_prio_t* score,
unsigned int cur_time)
{
fluid_real_t this_voice_prio = 0;
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

@ -37,11 +37,14 @@ typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t;
struct _fluid_overflow_prio_t
{
fluid_real_t percussion; /**< Is this voice on the drum channel? Then add this score */
fluid_real_t released; /**< Is this voice in release stage? Then add this score (usually negative) */
fluid_real_t sustained; /**< Is this voice sustained? Then add this score (usually negative) */
fluid_real_t volume; /**< Multiply current (or future) volume (a value between 0 and 1) */
fluid_real_t age; /**< This score will be divided by the number of seconds the voice has lasted */
float percussion; /**< Is this voice on the drum channel? Then add this score */
float released; /**< Is this voice in release stage? Then add this score (usually negative) */
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
@ -148,7 +151,7 @@ void fluid_voice_stop(fluid_voice_t* voice);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice);
int fluid_voice_kill_excl(fluid_voice_t* voice);
fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice,
float fluid_voice_get_overflow_prio(fluid_voice_t* voice,
fluid_overflow_prio_t* score,
unsigned int cur_time);

View file

@ -1248,6 +1248,27 @@ fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
return retval;
}
/**
* float-typed wrapper for fluid_settings_getnum
*
* @param settings a settings object
* @param name a setting's name
* @param val variable pointer to receive the setting's float value
* @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
*/
int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val)
{
double tmp;
if (fluid_settings_getnum(settings, name, &tmp) == FLUID_OK)
{
*val = tmp;
return FLUID_OK;
}
return FLUID_FAILED;
}
/**
* Get the range of values of a numeric setting
*
@ -1716,3 +1737,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

@ -40,6 +40,9 @@ int fluid_settings_register_num(fluid_settings_t* settings, const char* name, do
int fluid_settings_callback_num(fluid_settings_t* settings, const char* name,
fluid_num_update_t fun, void* data);
/* Type specific wrapper for fluid_settings_getnum */
int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val);
typedef void (*fluid_int_update_t)(void* data, const char* name, int value);
int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def,
@ -47,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 */