diff --git a/fluidsynth/src/rvoice/fluid_rvoice.c b/fluidsynth/src/rvoice/fluid_rvoice.c index ab193136..6c4acba6 100644 --- a/fluidsynth/src/rvoice/fluid_rvoice.c +++ b/fluidsynth/src/rvoice/fluid_rvoice.c @@ -648,13 +648,8 @@ void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value) void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value) { - if (voice->dsp.sample) { - fluid_sample_decr_ref(voice->dsp.sample); - voice->dsp.sample = NULL; - } + voice->dsp.sample = value; if (value) { - voice->dsp.sample = value; - fluid_sample_incr_ref(voice->dsp.sample); voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; } } @@ -664,10 +659,6 @@ fluid_rvoice_voiceoff(fluid_rvoice_t* voice) { fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); - if (voice->dsp.sample) { - fluid_sample_decr_ref(voice->dsp.sample); - voice->dsp.sample = NULL; - } } diff --git a/fluidsynth/src/rvoice/fluid_rvoice_mixer.c b/fluidsynth/src/rvoice/fluid_rvoice_mixer.c index e86b0461..366825e6 100644 --- a/fluidsynth/src/rvoice/fluid_rvoice_mixer.c +++ b/fluidsynth/src/rvoice/fluid_rvoice_mixer.c @@ -309,7 +309,7 @@ fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers, fluid_finish_rvoice(buffers, voice); } } - +/* static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* voice) { @@ -325,35 +325,35 @@ static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers, 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); + int i; + + if (mixer->active_voices < mixer->polyphony) { + mixer->rvoices[mixer->active_voices++] = 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; + + /* See if any voices just finished, if so, take its place. + This can happen in voice overflow conditions. */ + for (i=0; i < mixer->active_voices; i++) { + if (mixer->rvoices[i] == voice) { + FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); + return FLUID_FAILED; + } + if (mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) { + fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); + mixer->rvoices[i] = voice; + return FLUID_OK; + } } - - mixer->rvoices[mixer->active_voices++] = voice; - return FLUID_OK; + + /* This should never happen */ + FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); + return FLUID_FAILED; } static int diff --git a/fluidsynth/src/synth/fluid_synth.c b/fluidsynth/src/synth/fluid_synth.c index 3343b6ab..3fcf168a 100644 --- a/fluidsynth/src/synth/fluid_synth.c +++ b/fluidsynth/src/synth/fluid_synth.c @@ -2734,11 +2734,17 @@ fluid_synth_check_finished_voices(fluid_synth_t* synth) fluid_rvoice_t* fv; while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) { - for (j=0; j < synth->polyphony; j++) + for (j=0; j < synth->polyphony; j++) { if (synth->voice[j]->rvoice == fv) { fluid_voice_unlock_rvoice(synth->voice[j]); fluid_voice_off(synth->voice[j]); + break; } + else if (synth->voice[j]->overflow_rvoice == fv) { + fluid_voice_overflow_rvoice_finished(synth->voice[j]); + break; + } + } } } @@ -2821,7 +2827,7 @@ static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) { int i; - fluid_real_t best_prio = 999999.; + fluid_real_t best_prio = OVERFLOW_PRIO_CANNOT_KILL-1; fluid_real_t this_voice_prio; fluid_voice_t* voice; int best_voice_index=-1; diff --git a/fluidsynth/src/synth/fluid_voice.c b/fluidsynth/src/synth/fluid_voice.c index 7d7e49c4..1e3536e6 100644 --- a/fluidsynth/src/synth/fluid_voice.c +++ b/fluidsynth/src/synth/fluid_voice.c @@ -134,6 +134,14 @@ fluid_voice_update_modenv(fluid_voice_t* voice, coeff, increment, min, max); } +static inline void fluid_sample_null_ptr(fluid_sample_t** sample) +{ + if (*sample != NULL) { + fluid_sample_decr_ref(*sample); + *sample = NULL; + } +} + /* * new_fluid_voice */ @@ -147,13 +155,17 @@ new_fluid_voice(fluid_real_t output_rate) return NULL; } voice->rvoice = FLUID_NEW(fluid_rvoice_t); - if (voice->rvoice == NULL) { + voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); + if (voice->rvoice == NULL || voice->overflow_rvoice == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(voice->rvoice); FLUID_FREE(voice); return NULL; } FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); + FLUID_MEMSET(voice->overflow_rvoice, 0, sizeof(fluid_rvoice_t)); voice->can_access_rvoice = 1; + voice->can_access_overflow_rvoice = 1; voice->status = FLUID_VOICE_CLEAN; voice->chan = NO_CHANNEL; @@ -161,8 +173,7 @@ new_fluid_voice(fluid_real_t output_rate) voice->vel = 0; voice->channel = NULL; voice->sample = NULL; - voice->output_rate = output_rate; - UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, output_rate); + fluid_voice_set_output_rate(voice, output_rate); /* The 'sustain' and 'finished' segments of the volume / modulation * envelope are constant. They are never affected by any modulator @@ -190,15 +201,29 @@ delete_fluid_voice(fluid_voice_t* voice) if (voice == NULL) { return FLUID_OK; } - if (!voice->can_access_rvoice) { + if (!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) { /* stop rvoice before deleting voice! */ return FLUID_FAILED; } + FLUID_FREE(voice->overflow_rvoice); FLUID_FREE(voice->rvoice); FLUID_FREE(voice); return FLUID_OK; } +/* + * Swaps the current rvoice with the current overflow_rvoice + */ +static void fluid_voice_swap_rvoice(fluid_voice_t* voice) +{ + fluid_rvoice_t* rtemp = voice->rvoice; + int ctemp = voice->can_access_rvoice; + voice->rvoice = voice->overflow_rvoice; + voice->can_access_rvoice = voice->can_access_overflow_rvoice; + voice->overflow_rvoice = rtemp; + voice->can_access_overflow_rvoice = ctemp; +} + /* fluid_voice_init * * Initialize the synthesis process @@ -214,6 +239,16 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, * of IIR filters, position in sample etc) is initialized. */ int i; + if (!voice->can_access_rvoice) { + if (voice->can_access_overflow_rvoice) + fluid_voice_swap_rvoice(voice); + else { + FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); + return FLUID_FAILED; + } + } + /* We are now guaranteed to have access to the rvoice */ + if (voice->sample) fluid_voice_off(voice); @@ -223,12 +258,18 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, voice->vel = (unsigned char) vel; voice->channel = channel; voice->mod_count = 0; - voice->sample = sample; voice->start_time = start_time; voice->debug = 0; voice->has_noteoff = 0; UPDATE_RVOICE0(fluid_rvoice_reset); - UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample); + + /* Increment the reference count of the sample to prevent the + unloading of the soundfont while this voice is playing, + once for us and once for the rvoice. */ + fluid_sample_incr_ref(sample); + UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample); + fluid_sample_incr_ref(sample); + voice->sample = sample; i = fluid_channel_get_interp_method(channel); UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); @@ -257,10 +298,6 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i); UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1); - /* Increment the reference count of the sample to prevent the - unloading of the soundfont while this voice is playing. */ - fluid_sample_incr_ref(voice->sample); - return FLUID_OK; } @@ -277,6 +314,11 @@ fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value) voice->output_rate = value; UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); + /* Update the other rvoice as well */ + fluid_voice_swap_rvoice(voice); + UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value); + fluid_voice_swap_rvoice(voice); + return FLUID_FAILED; } @@ -1191,6 +1233,16 @@ fluid_voice_kill_excl(fluid_voice_t* voice){ return FLUID_OK; } +/* + * Called by fluid_synth when the overflow rvoice can be reclaimed. + */ +void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice) +{ + voice->can_access_overflow_rvoice = 1; + fluid_sample_null_ptr(&voice->overflow_rvoice->dsp.sample); +} + + /* * fluid_voice_off * @@ -1205,15 +1257,15 @@ fluid_voice_off(fluid_voice_t* voice) voice->chan = NO_CHANNEL; UPDATE_RVOICE0(fluid_rvoice_voiceoff); + + if (voice->can_access_rvoice) + fluid_sample_null_ptr(&voice->rvoice->dsp.sample); voice->status = FLUID_VOICE_OFF; voice->has_noteoff = 1; /* Decrement the reference count of the sample. */ - if (voice->sample) { - fluid_sample_decr_ref(voice->sample); - voice->sample = NULL; - } + fluid_sample_null_ptr(&voice->sample); /* Decrement voice count */ voice->channel->synth->active_voice_count--; @@ -1489,6 +1541,11 @@ fluid_voice_get_overflow_prio(fluid_voice_t* voice, { fluid_real_t this_voice_prio = 0; + /* Are we already overflowing? */ + if (!voice->can_access_overflow_rvoice) { + return OVERFLOW_PRIO_CANNOT_KILL; + } + /* Is this voice on the drum channel? * Then it is very important. * Also skip the released and sustained scores. diff --git a/fluidsynth/src/synth/fluid_voice.h b/fluidsynth/src/synth/fluid_voice.h index b5a35838..5bcad522 100644 --- a/fluidsynth/src/synth/fluid_voice.h +++ b/fluidsynth/src/synth/fluid_voice.h @@ -101,7 +101,9 @@ struct _fluid_voice_t /* rvoice control */ fluid_rvoice_t* rvoice; + fluid_rvoice_t* overflow_rvoice; /* Used temporarily and only in overflow situations */ int can_access_rvoice; /* False if rvoice is being rendered in separate thread */ + int can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ /* for debugging */ int debug; @@ -142,6 +144,7 @@ void fluid_voice_update_param(fluid_voice_t* voice, int gen); int fluid_voice_noteoff(fluid_voice_t* voice); int fluid_voice_off(fluid_voice_t* voice); +void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice); void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf, fluid_real_t* left_buf, fluid_real_t* right_buf, fluid_real_t* reverb_buf, fluid_real_t* chorus_buf); @@ -151,6 +154,8 @@ fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice, fluid_overflow_prio_t* score, unsigned int cur_time); +#define OVERFLOW_PRIO_CANNOT_KILL 999999. + /** * Locks the rvoice for rendering, so it can't be modified directly */