mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-02-20 19:02:04 +00:00
Fix a new voice overflow code, and some related bugfixes.
This commit is contained in:
parent
f61be79541
commit
c414d7373e
10 changed files with 302 additions and 104 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
13
fluidsynth/src/rvoice/fluid_lfo.c
Normal file
13
fluidsynth/src/rvoice/fluid_lfo.c
Normal 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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue