Merge pull request #193 from FluidSynth/timing

workaround timing issues
This commit is contained in:
Tom M 2017-09-01 18:26:20 +02:00 committed by GitHub
commit 83b62cfa80
8 changed files with 71 additions and 58 deletions

View file

@ -527,7 +527,9 @@ 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.</td>
in rendering-to-file scenarios where underruns is not an issue.
<br /><br /><strong>Deprecated:</strong><br />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.
</td>
</tr>

View file

@ -1624,7 +1624,7 @@ fluid_player_callback(void *data, unsigned int msec)
player->cur_msec = msec;
player->cur_ticks = (player->start_ticks
+ (int) ((double) (player->cur_msec - player->start_msec)
/ player->deltatime));
/ player->deltatime + 0.5)); /* 0.5 to average overall error when casting */
for (i = 0; i < player->ntracks; i++) {
if (!fluid_track_eot(player->track[i])) {

View file

@ -143,7 +143,7 @@ struct _fluid_rvoice_buffers_t
/**
* Parameters needed to synthesize a voice
* Hard real-time parameters needed to synthesize a voice
*/
struct _fluid_rvoice_t
{

View file

@ -65,6 +65,9 @@
event->realparams[1], event->realparams[2], event->realparams[3]); \
return; }
static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t* handler, const fluid_rvoice_event_t* src_event);
void
fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
{
@ -131,25 +134,14 @@ fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, int intparam,
fluid_real_t realparam)
{
fluid_rvoice_event_t* event;
fluid_rvoice_event_t local_event;
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
if (event == NULL) {
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
local_event.method = method;
local_event.object = object;
local_event.intparam = intparam;
local_event.realparams[0] = realparam;
event->method = method;
event->object = object;
event->intparam = intparam;
event->realparams[0] = realparam;
if (handler->is_threadsafe)
handler->queue_stored++;
else
fluid_rvoice_event_dispatch(event);
return FLUID_OK;
return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
}
@ -157,24 +149,13 @@ int
fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, void* ptr)
{
fluid_rvoice_event_t* event;
fluid_rvoice_event_t local_event;
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
if (event == NULL) {
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
local_event.method = method;
local_event.object = object;
local_event.ptr = ptr;
event->method = method;
event->object = object;
event->ptr = ptr;
if (handler->is_threadsafe)
handler->queue_stored++;
else
fluid_rvoice_event_dispatch(event);
return FLUID_OK;
return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
}
@ -184,28 +165,36 @@ fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
fluid_real_t r1, fluid_real_t r2,
fluid_real_t r3, fluid_real_t r4, fluid_real_t r5)
{
fluid_rvoice_event_t* event;
fluid_rvoice_event_t local_event;
event = handler->is_threadsafe ?
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
local_event.method = method;
local_event.object = object;
local_event.intparam = intparam;
local_event.realparams[0] = r1;
local_event.realparams[1] = r2;
local_event.realparams[2] = r3;
local_event.realparams[3] = r4;
local_event.realparams[4] = r5;
return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
}
static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t* handler, const fluid_rvoice_event_t* src_event)
{
fluid_rvoice_event_t* event;
int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1);
event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored);
if (event == NULL) {
fluid_atomic_int_add(&handler->queue_stored, -1);
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
return FLUID_FAILED; // Buffer full...
}
event->method = method;
event->object = object;
event->intparam = intparam;
event->realparams[0] = r1;
event->realparams[1] = r2;
event->realparams[2] = r3;
event->realparams[3] = r4;
event->realparams[4] = r5;
if (handler->is_threadsafe)
handler->queue_stored++;
else
fluid_rvoice_event_dispatch(event);
memcpy(event, src_event, sizeof(*event));
return FLUID_OK;
}
@ -233,7 +222,13 @@ new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize,
eventhandler->mixer = NULL;
eventhandler->queue = NULL;
eventhandler->finished_voices = NULL;
eventhandler->is_threadsafe = is_threadsafe;
/* 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;
eventhandler->queue_stored = 0;
eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size,

View file

@ -67,9 +67,11 @@ int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t*);
static FLUID_INLINE void
fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t* handler)
{
if (handler->queue_stored > 0) {
fluid_ringbuffer_next_inptr(handler->queue, handler->queue_stored);
handler->queue_stored = 0;
int queue_stored = fluid_atomic_int_get(&handler->queue_stored);
if (queue_stored > 0) {
fluid_atomic_int_set(&handler->queue_stored, 0);
fluid_ringbuffer_next_inptr(handler->queue, queue_stored);
}
}

View file

@ -709,6 +709,10 @@ int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer,
return mixer->buffers.fx_buf_count;
}
int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t* mixer)
{
return mixer->buffers.buf_blocks;
}
#ifdef ENABLE_MIXER_THREADS

View file

@ -42,6 +42,7 @@ int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** left, fluid_real_t*** right);
int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** fx_left, fluid_real_t*** fx_right);
int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t* mixer);
fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count,
fluid_real_t sample_rate);

View file

@ -2926,7 +2926,7 @@ void fluid_synth_process_event_queue(fluid_synth_t* synth)
static int
fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount)
{
int i;
int i, maxblocks;
fluid_profile_ref_var (prof_ref);
/* Assign ID of synthesis thread */
@ -2936,9 +2936,18 @@ fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount)
fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
/* do not render more blocks than we can store internally */
maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer);
if (blockcount > maxblocks)
blockcount = maxblocks;
for (i=0; i < blockcount; i++) {
fluid_sample_timer_process(synth);
fluid_synth_add_ticks(synth, FLUID_BUFSIZE);
/* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all()
* (should only happen with parallel render) stop processing and go for rendering
*/
if (fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) {
// Something has happened, we can't process more
blockcount = i+1;