mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-28 06:52:57 +00:00
Merge pull request #294 from FluidSynth/overflow-important-channels
New feature for overflow calculation: specify important channels
This commit is contained in:
commit
e0e319cf30
6 changed files with 279 additions and 26 deletions
|
@ -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 > 0) or more likely
|
||||
(synth.overflow.important < 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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue