Fix a new voice overflow code, and some related bugfixes.

This commit is contained in:
David Henningsson 2010-07-31 20:50:47 +00:00
parent f61be79541
commit c414d7373e
10 changed files with 302 additions and 104 deletions

View file

@ -132,6 +132,7 @@ set ( libfluidsynth_SOURCES
rvoice/fluid_chorus.h
rvoice/fluid_iir_filter.c
rvoice/fluid_iir_filter.h
rvoice/fluid_lfo.c
rvoice/fluid_lfo.h
rvoice/fluid_rvoice.h
rvoice/fluid_rvoice.c

View file

@ -585,6 +585,14 @@ int main(int argc, char** argv)
}
fluid_settings_setint(settings, "synth.audio-groups", audio_groups);
if (fast_render) {
midi_in = 0;
interactive = 0;
with_server = 0;
fluid_settings_setstr(settings, "player.timing-source", "sample");
fluid_settings_setint(settings, "synth.parallel-render", 1); /* TODO: Fast_render should not need this, but currently do */
}
/* create the synthesizer */
synth = new_fluid_synth(settings);
if (synth == NULL) {
@ -614,13 +622,6 @@ int main(int argc, char** argv)
/* signal(SIGINT, handle_signal); */
#endif
if (fast_render) {
midi_in = 0;
interactive = 0;
with_server = 0;
fluid_settings_setstr(settings, "player.timing-source", "sample");
}
/* start the synthesis thread */
if (!fast_render) {
adriver = new_fluid_audio_driver(settings, synth);

View file

@ -0,0 +1,13 @@
#include "fluid_lfo.h"
void
fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment)
{
lfo->increment = increment;
}
void
fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay)
{
lfo->delay = delay;
}

View file

@ -37,17 +37,9 @@ fluid_lfo_reset(fluid_lfo_t* lfo)
lfo->val = 0.0f;
}
static inline void
fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment)
{
lfo->increment = increment;
}
static inline void
fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay)
{
lfo->delay = delay;
}
// These two cannot be inlined since they're used by event_dispatch
void fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment);
void fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay);
static inline fluid_real_t
fluid_lfo_get_val(fluid_lfo_t* lfo)

View file

@ -32,6 +32,7 @@
#define EVENTFUNC_R1(proc, type) \
if (event->method == proc) { \
if(event->intparam != 0) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
proc((type) event->object, event->realparams[0]); \
return; }
@ -42,6 +43,7 @@
#define EVENTFUNC_I1(proc, type) \
if (event->method == proc) { \
if(event->realparams[0] != 0.0f) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
proc((type) event->object, event->intparam); \
return; }
@ -69,6 +71,7 @@ fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
EVENTFUNC_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*);
EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*);
EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*);
@ -131,8 +134,10 @@ fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
if (event == NULL)
if (event == NULL) {
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
event->method = method;
event->object = object;
@ -155,8 +160,10 @@ fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
if (event == NULL)
if (event == NULL) {
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
event->method = method;
event->object = object;
@ -180,8 +187,10 @@ fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
if (event == NULL)
if (event == NULL) {
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
event->method = method;
event->object = object;

View file

@ -269,13 +269,48 @@ fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers,
}
}
static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers,
fluid_rvoice_t* voice)
{
int i, retval=0;
int fvc = buffers->finished_voice_count;
for (i=0; i < fvc; i++)
if (buffers->finished_voices[i] == voice) {
fvc--;
if (i < fvc)
buffers->finished_voices[i] = buffers->finished_voices[fvc];
retval++;
}
fvc = buffers->finished_voice_count;
return retval;
}
int
fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice)
{
// Check if this voice is already in array, this can happen in some overflow conditions
int i, j=0;
for (i=0; i < mixer->active_voices; i++) {
if (mixer->rvoices[i] == voice)
j++;
}
if (j > 0) {
// It's already present, make sure it won't get deleted right away
#ifdef ENABLE_MIXER_THREADS
for (i=0; i < mixer->thread_count; i++)
fluid_mixer_buffers_replace_voice(&mixer->threads[i], voice);
#endif
fluid_mixer_buffers_replace_voice(&mixer->buffers, voice);
return FLUID_OK;
}
if (mixer->active_voices >= mixer->polyphony) {
FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
return FLUID_FAILED;
}
mixer->rvoices[mixer->active_voices++] = voice;
return FLUID_OK;
}

View file

@ -65,6 +65,8 @@ static int fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan);
static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num);
static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name,
int value);
static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name,
fluid_real_t value);
static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data,
int len, char *response,
int *response_len, int avail_response,
@ -176,6 +178,21 @@ static fluid_revmodel_presets_t revmodel_preset[] = {
* INITIALIZATION & UTILITIES
*/
static void fluid_synth_register_overflow(fluid_settings_t* settings,
fluid_num_update_t update_func,
void* update_data)
{
fluid_settings_register_num(settings, "synth.overflow.drum-channel",
4000, -10000, 10000, 0, update_func, update_data);
fluid_settings_register_num(settings, "synth.overflow.sustained",
-1000, -10000, 10000, 0, update_func, update_data);
fluid_settings_register_num(settings, "synth.overflow.released",
-2000, -10000, 10000, 0, update_func, update_data);
fluid_settings_register_num(settings, "synth.overflow.age",
1000, -10000, 10000, 0, update_func, update_data);
fluid_settings_register_num(settings, "synth.overflow.volume",
500, -10000, 10000, 0, update_func, update_data);
}
void fluid_synth_settings(fluid_settings_t* settings)
{
@ -192,7 +209,7 @@ void fluid_synth_settings(fluid_settings_t* settings)
fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL);
fluid_settings_register_int(settings, "synth.polyphony",
256, 16, 4096, 0, NULL, NULL);
256, 1, 65535, 0, NULL, NULL);
fluid_settings_register_int(settings, "synth.midi-channels",
16, 16, 256, 0, NULL, NULL);
fluid_settings_register_num(settings, "synth.gain",
@ -218,6 +235,9 @@ void fluid_synth_settings(fluid_settings_t* settings)
fluid_settings_register_int(settings, "synth.parallel-render", 1, 0, 1,
FLUID_HINT_TOGGLED, NULL, NULL);
fluid_synth_register_overflow(settings, NULL, NULL);
}
/**
@ -430,6 +450,22 @@ fluid_synth_init(void)
fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */
}
static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t* synth)
{
if (synth->eventhandler->is_threadsafe)
return fluid_atomic_int_get(&synth->ticks_since_start);
else
return synth->ticks_since_start;
}
static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t* synth, int val)
{
if (synth->eventhandler->is_threadsafe)
fluid_atomic_int_add((int*) &synth->ticks_since_start, val);
else
synth->ticks_since_start += val;
}
/***************************************************************
* FLUID SAMPLE TIMERS
@ -452,13 +488,14 @@ void fluid_sample_timer_process(fluid_synth_t* synth)
fluid_sample_timer_t* st;
long msec;
int cont;
unsigned int ticks = fluid_synth_get_ticks(synth);
for (st=synth->sample_timers; st; st=st->next) {
if (st->isfinished) {
continue;
}
msec = (long) (1000.0*((double) (synth->ticks - st->starttick))/synth->sample_rate);
msec = (long) (1000.0*((double) (ticks - st->starttick))/synth->sample_rate);
cont = (*st->callback)(st->data, msec);
if (cont == 0) {
st->isfinished = 1;
@ -473,7 +510,7 @@ fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_c
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
result->starttick = synth->ticks;
result->starttick = fluid_synth_get_ticks(synth);
result->isfinished = 0;
result->data = data;
result->callback = callback;
@ -587,13 +624,16 @@ new_fluid_synth(fluid_settings_t *settings)
0.2f, 0.0f, 10.0f, 0,
(fluid_num_update_t) fluid_synth_update_gain, synth);
fluid_settings_register_int(settings, "synth.polyphony",
synth->polyphony, 16, 4096, 0,
synth->polyphony, 1, 65535, 0,
(fluid_int_update_t) fluid_synth_update_polyphony,
synth);
fluid_settings_register_int(settings, "synth.device-id",
synth->device_id, 126, 0, 0,
(fluid_int_update_t) fluid_synth_update_device_id, synth);
fluid_synth_register_overflow(settings,
(fluid_num_update_t) fluid_synth_update_overflow, synth);
/* do some basic sanity checking on the settings */
if (synth->midi_channels % 16 != 0) {
@ -649,13 +689,14 @@ new_fluid_synth(fluid_settings_t *settings)
synth->sfont_info = NULL;
synth->sfont_hash = new_fluid_hashtable (NULL, NULL);
synth->noteid = 0;
synth->ticks = 0;
synth->ticks_since_start = 0;
synth->tuning = NULL;
fluid_private_init(synth->tuning_iter);
/* Allocate event queue for rvoice mixer */
fluid_settings_getint(settings, "synth.parallel-render", &i);
synth->eventhandler = new_fluid_rvoice_eventhandler(i, synth->polyphony*8,
/* In an overflow situation, a new voice takes about 50 spaces in the queue! */
synth->eventhandler = new_fluid_rvoice_eventhandler(i, synth->polyphony*64,
synth->polyphony,
nbuf, synth->effects_channels);
if (synth->eventhandler == NULL)
@ -697,7 +738,8 @@ new_fluid_synth(fluid_settings_t *settings)
}
fluid_synth_set_sample_rate(synth, synth->sample_rate);
fluid_synth_update_overflow(synth, "", 0.0f);
fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,
synth->polyphony, 0.0f);
fluid_synth_set_reverb_on(synth, synth->with_reverb);
@ -1352,14 +1394,12 @@ fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel)
channel = synth->channel[chan];
// if (chan != 11) return FLUID_OK; /* DEBUG */
/* make sure this channel has a preset */
if (channel->preset == NULL) {
if (synth->verbose) {
FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s",
chan, key, vel, 0,
(float) synth->ticks / 44100.0f,
fluid_synth_get_ticks(synth) / 44100.0f,
(fluid_curtime() - synth->start) / 1000.0f,
0.0f, 0, "channel has no preset");
}
@ -2708,10 +2748,9 @@ fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony)
{
int result;
fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail (polyphony >= 16 && polyphony <= synth->nvoice, FLUID_FAILED);
fluid_return_val_if_fail (polyphony >= 1 && polyphony <= 65535, FLUID_FAILED);
fluid_synth_api_enter(synth);
fluid_atomic_int_set (&synth->shadow_polyphony, polyphony);
if (fluid_synth_should_queue (synth))
@ -2725,10 +2764,25 @@ static int
fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth)
{
fluid_voice_t *voice;
int i;
synth->polyphony = fluid_atomic_int_get (&synth->shadow_polyphony);
int i, new_polyphony;
new_polyphony = fluid_atomic_int_get (&synth->shadow_polyphony);
if (synth->polyphony > synth->nvoice) {
/* Create more voices */
fluid_voice_t** new_voices = FLUID_REALLOC(synth->voice,
sizeof(fluid_voice_t*) * new_polyphony);
if (new_voices == NULL)
return FLUID_FAILED;
synth->voice = new_voices;
for (i = synth->nvoice; i < new_polyphony; i++) {
synth->voice[i] = new_fluid_voice(synth->sample_rate);
if (synth->voice[i] == NULL)
return FLUID_FAILED;
}
synth->nvoice = new_polyphony;
}
synth->polyphony = new_polyphony;
/* turn off any voices above the new limit */
for (i = synth->polyphony; i < synth->nvoice; i++)
{
@ -3203,7 +3257,7 @@ fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount)
for (i=0; i < blockcount; i++) {
fluid_sample_timer_process(synth);
synth->ticks += FLUID_BUFSIZE;
fluid_synth_add_ticks(synth, FLUID_BUFSIZE);
if (fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) {
// Something has happened, we can't process more
blockcount = i+1;
@ -3407,6 +3461,26 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth,
}
}
static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name,
fluid_real_t value)
{
fluid_synth_api_enter(synth);
fluid_settings_getnum(synth->settings, "synth.overflow.drum-channel",
&synth->overflow.drum_channel);
fluid_settings_getnum(synth->settings, "synth.overflow.released",
&synth->overflow.released);
fluid_settings_getnum(synth->settings, "synth.overflow.sustained",
&synth->overflow.sustained);
fluid_settings_getnum(synth->settings, "synth.overflow.volume",
&synth->overflow.volume);
fluid_settings_getnum(synth->settings, "synth.overflow.age",
&synth->overflow.age);
FLUID_API_RETURN(0);
}
/* Selects a voice for killing. */
static fluid_voice_t*
fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
@ -3416,7 +3490,14 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
fluid_real_t this_voice_prio;
fluid_voice_t* voice;
int best_voice_index=-1;
unsigned int ticks = fluid_synth_get_ticks(synth);
/*overflow_prio.drum_channel = 4000;
overflow_prio.released = -2000;
overflow_prio.sustained = -1000;
overflow_prio.volume = 1000;
overflow_prio.age = 500;*/
for (i = 0; i < synth->polyphony; i++) {
voice = synth->voice[i];
@ -3425,53 +3506,14 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
if (_AVAILABLE(voice)) {
return voice;
}
/* Determine, how 'important' a voice is.
* Start with an arbitrary number */
this_voice_prio = 10000.;
/* Is this voice on the drum channel?
* Then it is very important.
* Also, forget about the released-note condition:
* Typically, drum notes are triggered only very briefly, they run most
* of the time in release phase.
*/
if (voice->chan == 9){
this_voice_prio += 4000;
} else if (_RELEASED(voice)){
/* The key for this voice has been released. Consider it much less important
* than a voice, which is still held.
*/
this_voice_prio -= 2000.;
}
if (_SUSTAINED(voice)){
/* The sustain pedal is held down on this channel.
* Consider it less important than non-sustained channels.
* This decision is somehow subjective. But usually the sustain pedal
* is used to play 'more-voices-than-fingers', so it shouldn't hurt
* if we kill one voice.
*/
this_voice_prio -= 1000;
}
/* We are not enthusiastic about releasing voices, which have just been started.
* Otherwise hitting a chord may result in killing notes belonging to that very same
* chord.
* So subtract the age of the voice from the priority - an older voice is just a little
* bit less important than a younger voice.
* This is a number between roughly 0 and 100.*/
this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
/* take a rough estimate of loudness into account. Louder voices are more important. */
// FIXME
// this_voice_prio += fluid_voice_get_loudness(voice) * 1000.;
this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow,
ticks);
/* check if this voice has less priority than the previous candidate. */
if (this_voice_prio < best_prio)
best_voice_index = i,
if (this_voice_prio < best_prio) {
best_voice_index = i;
best_prio = this_voice_prio;
}
}
if (best_voice_index < 0) {
@ -3479,6 +3521,8 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
}
voice = synth->voice[best_voice_index];
FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ",
voice->id, best_voice_index, voice->chan, voice->key);
fluid_voice_off(voice);
return voice;
@ -3507,6 +3551,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
int i, k;
fluid_voice_t* voice = NULL;
fluid_channel_t* channel = NULL;
unsigned int ticks;
fluid_return_val_if_fail (sample != NULL, NULL);
FLUID_API_ENTRY_CHAN(NULL);
@ -3529,6 +3574,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);
FLUID_API_RETURN(NULL);
}
ticks = fluid_synth_get_ticks(synth);
if (synth->verbose) {
k = 0;
@ -3540,7 +3586,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",
chan, key, vel, synth->storeid,
(float) synth->ticks / 44100.0f,
(float) ticks / 44100.0f,
(fluid_curtime() - synth->start) / 1000.0f,
0.0f,
k);
@ -3551,7 +3597,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
}
if (fluid_voice_init (voice, sample, channel, key, vel,
synth->storeid, synth->ticks,
synth->storeid, ticks,
fluid_atomic_float_get (&synth->gain)) != FLUID_OK) {
FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
FLUID_API_RETURN(NULL);

View file

@ -185,8 +185,9 @@ struct _fluid_synth_t
Typically equal to audio_channels. */
int effects_channels; /**< the number of effects channels (>= 2) */
int state; /**< the synthesizer state */
unsigned int ticks; /**< the number of audio samples since the start */
unsigned int ticks_since_start; /**< the number of audio samples since the start */
unsigned int start; /**< the start in msec, as returned by system clock */
fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */
fluid_list_t *loaders; /**< the SoundFont loaders */
fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */

View file

@ -86,9 +86,15 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
} while (0)
#define UPDATE_RVOICE_ENVDATA(envp, section, arg1, arg2, arg3, arg4, arg5) \
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.envp, section, arg1, arg2, arg3, arg4, arg5)
#define UPDATE_RVOICE_VOLENV(section, arg1, arg2, arg3, arg4, arg5) \
do { \
fluid_adsr_env_set_data(&voice->volenv, section, arg1, arg2, arg3, arg4, arg5) \
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, arg1, arg2, arg3, arg4, arg5) \
} while(0)
#define UPDATE_RVOICE_MODENV(section, arg1, arg2, arg3, arg4, arg5) \
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, arg1, arg2, arg3, arg4, arg5)
#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1)
#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1)
#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1)
@ -98,6 +104,35 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg)
#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg)
static inline void
fluid_voice_update_volenv(fluid_voice_t* voice,
fluid_adsr_env_section_t section,
unsigned int count,
fluid_real_t coeff,
fluid_real_t increment,
fluid_real_t min,
fluid_real_t max)
{
fluid_adsr_env_set_data(&voice->volenv, section, count, coeff, increment,
min, max);
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data,
&voice->rvoice->envlfo.volenv, section, count,
coeff, increment, min, max);
}
static inline void
fluid_voice_update_modenv(fluid_voice_t* voice,
fluid_adsr_env_section_t section,
unsigned int count,
fluid_real_t coeff,
fluid_real_t increment,
fluid_real_t min,
fluid_real_t max)
{
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data,
&voice->rvoice->envlfo.modenv, section, count,
coeff, increment, min, max);
}
/*
* new_fluid_voice
@ -134,13 +169,13 @@ new_fluid_voice(fluid_real_t output_rate)
* or generator. Therefore it is enough to initialize them once
* during the lifetime of the synth.
*/
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVSUSTAIN,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVSUSTAIN,
0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVFINISHED,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVFINISHED,
0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVSUSTAIN,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVSUSTAIN,
0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVFINISHED,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED,
0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
return voice;
@ -899,7 +934,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_VOLENVDELAY);
fluid_clip(x, -12000.0f, 5000.0f);
count = NUM_BUFFERS_DELAY(x);
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVDELAY,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDELAY,
count, 0.0f, 0.0f, -1.0f, 1.0f);
break;
@ -907,14 +942,14 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_VOLENVATTACK);
fluid_clip(x, -12000.0f, 8000.0f);
count = 1 + NUM_BUFFERS_ATTACK(x);
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVATTACK,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVATTACK,
count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
break;
case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */
case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */
count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVHOLD,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVHOLD,
count, 1.0f, 0.0f, -1.0f, 2.0f);
break;
@ -924,7 +959,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN);
fluid_clip(y, 0.0f, 1.0f);
count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVDECAY,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDECAY,
count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
break;
@ -932,7 +967,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_VOLENVRELEASE);
fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f);
count = 1 + NUM_BUFFERS_RELEASE(x);
UPDATE_RVOICE_ENVDATA(volenv, FLUID_VOICE_ENVRELEASE,
fluid_voice_update_volenv(voice, FLUID_VOICE_ENVRELEASE,
count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f);
break;
@ -940,7 +975,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */
x = _GEN(voice, GEN_MODENVDELAY);
fluid_clip(x, -12000.0f, 5000.0f);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVDELAY,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDELAY,
NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f);
break;
@ -948,14 +983,14 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_MODENVATTACK);
fluid_clip(x, -12000.0f, 8000.0f);
count = 1 + NUM_BUFFERS_ATTACK(x);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVATTACK,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVATTACK,
count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
break;
case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */
case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */
count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVHOLD,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVHOLD,
count, 1.0f, 0.0f, -1.0f, 2.0f);
break;
@ -965,7 +1000,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */
y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN);
fluid_clip(y, 0.0f, 1.0f);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVDECAY,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDECAY,
count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
break;
@ -973,7 +1008,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_MODENVRELEASE);
fluid_clip(x, -12000.0f, 8000.0f);
count = 1 + NUM_BUFFERS_RELEASE(x);
UPDATE_RVOICE_ENVDATA(modenv, FLUID_VOICE_ENVRELEASE,
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVRELEASE,
count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f);
break;
@ -1444,3 +1479,53 @@ fluid_voice_optimize_sample(fluid_sample_t* s)
return FLUID_OK;
}
fluid_real_t
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;
/* Is this voice on the drum channel?
* Then it is very important.
* Also skip the released and sustained scores.
*/
if (voice->chan == 9){
this_voice_prio += score->drum_channel;
}
else if (voice->has_noteoff) {
/* Noteoff has */
this_voice_prio += score->released;
} else if (_SUSTAINED(voice)){
/* This voice is still active, since the sustain pedal is held down.
* Consider it less important than non-sustained channels.
* This decision is somehow subjective. But usually the sustain pedal
* is used to play 'more-voices-than-fingers', so it shouldn't hurt
* if we kill one voice.
*/
this_voice_prio += score->sustained;
}
/* We are not enthusiastic about releasing voices, which have just been started.
* Otherwise hitting a chord may result in killing notes belonging to that very same
* chord. So give newer voices a higher score. */
if (score->age) {
cur_time -= voice->start_time;
if (cur_time < 1)
cur_time = 1; // Avoid div by zero
this_voice_prio += (score->age * voice->output_rate) / cur_time;
}
/* take a rough estimate of loudness into account. Louder voices are more important. */
if (score->volume) {
fluid_real_t a = voice->attenuation;
if (voice->has_noteoff) {
// FIXME: Should take into account where on the envelope we are...?
}
if (a < 0.1)
a = 0.1; // Avoid div by zero
this_voice_prio += score->volume / a;
}
return this_voice_prio;
}

View file

@ -33,6 +33,17 @@
#define NO_CHANNEL 0xff
typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t;
struct _fluid_overflow_prio_t
{
fluid_real_t drum_channel; /**< 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 */
};
enum fluid_voice_status
{
FLUID_VOICE_CLEAN,
@ -65,6 +76,7 @@ struct _fluid_voice_t
fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */
unsigned int start_time;
fluid_adsr_env_t volenv; /* Volume envelope (dupe in rvoice) */
/* basic parameters */
fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */
@ -135,6 +147,9 @@ void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
int fluid_voice_kill_excl(fluid_voice_t* voice);
fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice,
fluid_overflow_prio_t* score,
unsigned int cur_time);
/**
* Locks the rvoice for rendering, so it can't be modified directly
@ -147,7 +162,7 @@ fluid_voice_lock_rvoice(fluid_voice_t* voice)
}
/**
* Locks the rvoice for rendering, so it can be modified directly
* Unlocks the rvoice for rendering, so it can be modified directly
*/
static FLUID_INLINE void
fluid_voice_unlock_rvoice(fluid_voice_t* voice)
@ -172,7 +187,7 @@ fluid_voice_unlock_rvoice(fluid_voice_t* voice)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \
(((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)))
#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
//#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val)