Merge pull request #363 from FluidSynth/197

rvoice_* related cleanup
This commit is contained in:
Tom M 2018-04-08 13:49:06 +02:00 committed by GitHub
commit 189433a757
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 32 additions and 96 deletions

View file

@ -227,7 +227,7 @@ https://stackoverflow.com/a/6251757
This is the low-latency setting. If on, you're allowed to call fluid_synth_write_s16, fluid_synth_write_float, fluid_synth_nwrite_float or fluid_synth_process in parallel with the rest of the calls, and it won't be blocked by time intensive calls to the synth. Turn it off if throughput is more important than latency, e g in rendering-to-file scenarios where underruns is not an issue.
</desc>
<deprecated>
As of 1.1.7 this option is deprecated. This option enforces thread safety for rvoice_mixer, which causes rvoice_events to be queued internally. The current implementation relies on the fact that this option is set to TRUE to correctly render any amount of requested audio. Also calling fluid_synth_write_* in parallel is not considered to be a use-case. It would cause undefined audio output, as it would be unpredictable for the user which rvoice_events specifically would be dispatched to which fluid_synth_write_* call.
As of 1.1.7 this option is deprecated and has been removed in 2.0. This option enforces thread safety for rvoice_mixer, which causes rvoice_events to be queued internally. The current implementation relies on the fact that this option is set to TRUE to correctly render any amount of requested audio. Also calling fluid_synth_write_* in parallel is not considered to be a use-case. It would cause undefined audio output, as it would be unpredictable for the user which rvoice_events specifically would be dispatched to which fluid_synth_write_* call.
</deprecated>
</setting>
<setting>
@ -262,7 +262,7 @@ https://stackoverflow.com/a/6251757
<type>bool</type>
<def>1 (TRUE)</def>
<desc>
Controls whether the synth's public API is protected by a mutex or not. Default is on, turn it off for slightly better performance if you know you're only accessing the synth from one thread only, this could be the case in many embedded use cases for example. Note that libfluidsynth can use many threads by itself (shell is one, midi driver is one, midi player is one etc) so you should usually leave it on. Also see synth.parallel-render.
Controls whether the synth's public API is protected by a mutex or not. Default is on, turn it off for slightly better performance if you know you're only accessing the synth from one thread only, this could be the case in many embedded use cases for example. Note that libfluidsynth can use many threads by itself (shell is one, midi driver is one, midi player is one etc) so you should usually leave it on.
</desc>
</setting>
<setting>

View file

@ -579,7 +579,6 @@ int main(int argc, char** argv)
#endif
fluid_settings_setstr(settings, "player.timing-source", "sample");
fluid_settings_setint(settings, "synth.lock-memory", 0);
fluid_settings_setint(settings, "synth.parallel-render", 1); /* TODO: Fast_render should not need this, but currently do */
}
/* create the synthesizer */

View file

@ -247,8 +247,8 @@ fluid_chorus_reset(fluid_chorus_t* chorus)
* @param type Chorus waveform type (#fluid_chorus_mod)
*/
void
fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level,
float speed, float depth_ms, int type)
fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, fluid_real_t level,
fluid_real_t speed, fluid_real_t depth_ms, int type)
{
int modulation_depth_samples;
int i;

View file

@ -52,8 +52,8 @@ void delete_fluid_chorus(fluid_chorus_t* chorus);
int fluid_chorus_init(fluid_chorus_t* chorus);
void fluid_chorus_reset(fluid_chorus_t* chorus);
void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level,
float speed, float depth_ms, int type);
void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, fluid_real_t level,
fluid_real_t speed, fluid_real_t depth_ms, int type);
void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);

View file

@ -475,8 +475,8 @@ fluid_revmodel_update(fluid_revmodel_t* rev)
* @param level Reverb level
*/
void
fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize,
float damping, float width, float level)
fluid_revmodel_set(fluid_revmodel_t* rev, int set, fluid_real_t roomsize,
fluid_real_t damping, fluid_real_t width, fluid_real_t level)
{
if (set & FLUID_REVMODEL_SET_ROOMSIZE)
{

View file

@ -68,8 +68,8 @@ void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
void fluid_revmodel_reset(fluid_revmodel_t* rev);
void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize,
float damping, float width, float level);
void fluid_revmodel_set(fluid_revmodel_t* rev, int set, fluid_real_t roomsize,
fluid_real_t damping, fluid_real_t width, fluid_real_t level);
void fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate);

View file

@ -28,7 +28,7 @@
* Interpolates audio data (obtains values between the samples of the original
* waveform data).
*
* Variables loaded from the voice structure (assigned in fluid_voice_write()):
* Variables loaded from the voice structure (assigned in fluid_rvoice_write()):
* - dsp_data: Pointer to the original waveform data
* - dsp_phase: The position in the original waveform data.
* This has an integer and a fractional part (between samples).

View file

@ -243,7 +243,7 @@ finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
}
fluid_rvoice_eventhandler_t*
new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize,
new_fluid_rvoice_eventhandler(int queuesize,
int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate)
{
fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
@ -255,12 +255,6 @@ new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize,
eventhandler->queue = NULL;
eventhandler->finished_voices = NULL;
/* HACK 2017-08-27: always enforce threadsafety, i.e. enforce enqueuing events
* otherwise we mess up rendering if more than one block is requested by the user
* because fluid_rvoice_eventhandler_dispatch_count() always stays zero causing
* that too many events are dispatched too early, causing incorrectly timed audio
*/
eventhandler->is_threadsafe = TRUE;
fluid_atomic_int_set(&eventhandler->queue_stored, 0);
eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size,

View file

@ -44,11 +44,10 @@ void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event);
/*
* Bridge between the renderer thread and the midi state thread.
* If is_threadsafe is true, that means fluid_rvoice_eventhandler_fetch_all
* can be called in parallell with fluid_rvoice_eventhandler_push/flush
* fluid_rvoice_eventhandler_fetch_all() can be called in parallell
* with fluid_rvoice_eventhandler_push/flush()
*/
struct _fluid_rvoice_eventhandler_t {
int is_threadsafe; /* False for optimal performance, true for atomic operations */
fluid_ringbuffer_t* queue; /**< List of fluid_rvoice_event_t */
fluid_atomic_int_t queue_stored; /**< Extras pushed but not flushed */
fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */
@ -56,7 +55,7 @@ struct _fluid_rvoice_eventhandler_t {
};
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
int is_threadsafe, int queuesize, int finished_voices_size, int bufs,
int queuesize, int finished_voices_size, int bufs,
int fx_bufs, fluid_real_t sample_rate);
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
@ -105,11 +104,8 @@ static FLUID_INLINE void
fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler,
fluid_rvoice_t* rvoice)
{
if (handler->is_threadsafe)
fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice,
handler->mixer, rvoice);
else
fluid_rvoice_mixer_add_voice(handler->mixer, rvoice);
}

View file

@ -676,14 +676,14 @@ void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on)
}
void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set,
int nr, double level, double speed,
double depth_ms, int type)
int nr, fluid_real_t level, fluid_real_t speed,
fluid_real_t depth_ms, int type)
{
fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
}
void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
double roomsize, double damping,
double width, double level)
fluid_real_t roomsize, fluid_real_t damping,
fluid_real_t width, fluid_real_t level)
{
fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level);
}

View file

@ -58,11 +58,11 @@ void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on);
int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value);
int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice);
void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set,
int nr, double level, double speed,
double depth_ms, int type);
int nr, fluid_real_t level, fluid_real_t speed,
fluid_real_t depth_ms, int type);
void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
double roomsize, double damping,
double width, double level);
fluid_real_t roomsize, fluid_real_t damping,
fluid_real_t width, fluid_real_t level);
void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer);
void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer);
void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer);

View file

@ -211,7 +211,6 @@ void fluid_synth_settings(fluid_settings_t* settings)
fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);
fluid_settings_register_int(settings, "synth.parallel-render", 1, 0, 1, FLUID_HINT_TOGGLED);
fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0);
fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0);
@ -693,9 +692,8 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_private_init(synth->tuning_iter);
/* Allocate event queue for rvoice mixer */
fluid_settings_getint(settings, "synth.parallel-render", &i);
/* 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->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony*64,
synth->polyphony, nbuf, synth->effects_channels, synth->sample_rate);
if (synth->eventhandler == NULL)
@ -2900,9 +2898,6 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
int bytes;
#endif
float cpu_load;
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_enter(synth);
/* First, take what's still available in the buffer */
count = 0;
@ -3011,9 +3006,6 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
time = fluid_utime() - time;
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
fluid_atomic_float_set (&synth->cpu_load, cpu_load);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_exit(synth);
return FLUID_OK;
}
@ -3096,10 +3088,7 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
float cpu_load;
fluid_profile_ref_var (prof_ref);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_enter(synth);
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
l = synth->cur;
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
@ -3123,9 +3112,6 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
time = fluid_utime() - time;
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
fluid_atomic_float_set (&synth->cpu_load, cpu_load);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_exit(synth);
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
@ -3203,10 +3189,7 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
float cpu_load;
fluid_profile_ref_var (prof_ref);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_enter(synth);
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
@ -3246,9 +3229,6 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
fluid_atomic_float_set (&synth->cpu_load, cpu_load);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_exit(synth);
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
len);
@ -3634,8 +3614,7 @@ fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);
fluid_voice_start(voice); /* Start the new voice */
if (synth->eventhandler->is_threadsafe)
fluid_voice_lock_rvoice(voice);
fluid_voice_lock_rvoice(voice);
fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
fluid_synth_api_exit(synth);
}

View file

@ -129,7 +129,7 @@ fluid_voice_update_modenv(fluid_voice_t* voice,
coeff, increment, min, max);
}
static FLUID_INLINE void fluid_sample_null_ptr(fluid_sample_t** sample)
static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t** sample)
{
if (*sample != NULL) {
fluid_sample_decr_ref(*sample);
@ -384,35 +384,6 @@ fluid_real_t fluid_voice_gen_value(const fluid_voice_t* voice, int num)
}
}
/**
* Synthesize a voice to a buffer.
*
* @param voice Voice to synthesize
* @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
* @return Count of samples written to dsp_buf (can be 0)
*
* Panning, reverb and chorus are processed separately. The dsp interpolation
* routine is in (fluid_rvoice_dsp.c).
*/
int
fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
{
int result;
if (!voice->can_access_rvoice)
return 0;
result = fluid_rvoice_write(voice->rvoice, dsp_buf);
if (result == -1)
return 0;
if ((result < FLUID_BUFSIZE) && fluid_voice_is_playing(voice)) /* Voice finished by itself */
fluid_voice_off(voice);
return result;
}
/*
* fluid_voice_start
*/
@ -1341,7 +1312,7 @@ fluid_voice_kill_excl(fluid_voice_t* voice){
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_sample_unref(&voice->overflow_rvoice->dsp.sample);
}
/*
@ -1370,13 +1341,13 @@ fluid_voice_stop(fluid_voice_t* voice)
voice->chan = NO_CHANNEL;
if (voice->can_access_rvoice)
fluid_sample_null_ptr(&voice->rvoice->dsp.sample);
fluid_voice_sample_unref(&voice->rvoice->dsp.sample);
voice->status = FLUID_VOICE_OFF;
voice->has_noteoff = 1;
/* Decrement the reference count of the sample. */
fluid_sample_null_ptr(&voice->sample);
fluid_voice_sample_unref(&voice->sample);
/* Decrement voice count */
voice->channel->synth->active_voice_count--;

View file

@ -120,8 +120,6 @@ void delete_fluid_voice(fluid_voice_t* voice);
void fluid_voice_start(fluid_voice_t* voice);
void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice);
int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf);
int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
fluid_zone_range_t *inst_zone_range,
fluid_channel_t* channel, int key, int vel,
@ -169,11 +167,10 @@ float fluid_voice_get_overflow_prio(fluid_voice_t* voice,
/**
* Locks the rvoice for rendering, so it can't be modified directly
*/
static FLUID_INLINE fluid_rvoice_t*
static FLUID_INLINE void
fluid_voice_lock_rvoice(fluid_voice_t* voice)
{
voice->can_access_rvoice = 0;
return voice->rvoice;
}
/**