mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-12-01 00:21:14 +00:00
Merge pull request #193 from FluidSynth/timing
workaround timing issues
This commit is contained in:
commit
83b62cfa80
8 changed files with 71 additions and 58 deletions
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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])) {
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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...
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
local_event.method = method;
|
||||
local_event.object = object;
|
||||
local_event.intparam = intparam;
|
||||
local_event.realparams[0] = realparam;
|
||||
|
||||
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...
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
local_event.method = method;
|
||||
local_event.object = object;
|
||||
local_event.ptr = ptr;
|
||||
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue