Event queue for thread boundary, refactor ringbuffer, move some files to unused subdir

This commit is contained in:
David Henningsson 2010-06-28 20:03:12 +00:00
parent d5ab7a8f74
commit b79a3e1cbe
19 changed files with 1173 additions and 259 deletions

View File

@ -122,7 +122,6 @@ set ( libfluidsynth_SOURCES
sfloader/fluid_sfont.h
fluid_event.c
fluid_event_priv.h
fluid_event_queue.c
fluid_event_queue.h
fluid_gen.c
fluid_gen.h
@ -130,13 +129,20 @@ set ( libfluidsynth_SOURCES
datatypes/fluid_hash.h
datatypes/fluid_list.c
datatypes/fluid_list.h
datatypes/fluid_ringbuffer.c
datatypes/fluid_ringbuffer.h
rvoice/fluid_iir_filter.c
rvoice/fluid_iir_filter.h
rvoice/fluid_lfo.h
rvoice/fluid_adsr_env.c
rvoice/fluid_adsr_env.h
rvoice/fluid_rvoice.h
rvoice/fluid_rvoice.c
rvoice/fluid_rvoice_dsp.c
rvoice/fluid_rvoice_event.h
rvoice/fluid_rvoice_event.c
rvoice/fluid_rvoice_handler.h
rvoice/fluid_rvoice_handler.c
rvoice/fluid_phase.h
fluid_mdriver.c
fluid_mdriver.h

View File

@ -0,0 +1,89 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/*
* Josh Green <josh@resonance.org>
* 2009-05-28
*/
#include "fluid_ringbuffer.h"
#include "fluidsynth_priv.h"
/**
* Create a lock free queue with a fixed maximum count and size of elements.
* @param count Count of elements in queue (fixed max number of queued elements)
* @return New lock free queue or NULL if out of memory (error message logged)
*
* Lockless FIFO queues don't use any locking mechanisms and can therefore be
* advantageous in certain situations, such as passing data between a lower
* priority thread and a higher "real time" thread, without potential lock
* contention which could stall the high priority thread. Note that there may
* only be one producer thread and one consumer thread.
*/
fluid_ringbuffer_t *
new_fluid_ringbuffer (int count, int elementsize)
{
fluid_ringbuffer_t *queue;
fluid_return_val_if_fail (count > 0, NULL);
queue = FLUID_NEW (fluid_ringbuffer_t);
if (!queue)
{
FLUID_LOG (FLUID_ERR, "Out of memory");
return NULL;
}
queue->array = FLUID_MALLOC (elementsize * count);
if (!queue->array)
{
FLUID_FREE (queue);
FLUID_LOG (FLUID_ERR, "Out of memory");
return NULL;
}
/* Clear array, in case dynamic pointer reclaiming is being done */
FLUID_MEMSET (queue->array, 0, elementsize * count);
queue->totalcount = count;
queue->elementsize = elementsize;
queue->count = 0;
queue->in = 0;
queue->out = 0;
return (queue);
}
/**
* Free an event queue.
* @param queue Lockless queue instance
*
* Care must be taken when freeing a queue, to ensure that the consumer and
* producer threads will no longer access it.
*/
void
delete_fluid_ringbuffer (fluid_ringbuffer_t *queue)
{
FLUID_FREE (queue->array);
FLUID_FREE (queue);
}

View File

@ -0,0 +1,115 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_RINGBUFFER_H
#define _FLUID_RINGBUFFER_H
#include "fluid_sys.h"
/**
* Lockless event queue instance.
*/
struct _fluid_ringbuffer_t
{
char *array; /**< Queue array of arbitrary size elements */
int totalcount; /**< Total count of elements in array */
int count; /**< Current count of elements */
int in; /**< Index in queue to store next pushed element */
int out; /**< Index in queue of next popped element */
int elementsize; /**< Size of each element */
void* userdata;
};
typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t;
fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize);
void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue);
/**
* Get pointer to next input array element in queue.
* @param queue Lockless queue instance
* @param count Normally zero, or more if you need to push several items at once
* @return Pointer to array element in queue to store data to or NULL if queue is full
*
* This function along with fluid_ringbuffer_next_inptr() form a queue "push"
* operation and is split into 2 functions to avoid an element copy. Note that
* the returned array element pointer may contain the data of a previous element
* if the queue has wrapped around. This can be used to reclaim pointers to
* allocated memory, etc.
*/
static FLUID_INLINE void*
fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset)
{
return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL
: queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount);
}
/**
* Advance the input queue index to complete a "push" operation.
* @param queue Lockless queue instance
* @param count Normally one, or more if you need to push several items at once
*
* This function along with fluid_ringbuffer_get_inptr() form a queue "push"
* operation and is split into 2 functions to avoid element copy.
*/
static FLUID_INLINE void
fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count)
{
fluid_atomic_int_add (&queue->count, count);
queue->in += count;
if (queue->in >= queue->totalcount)
queue->in -= queue->totalcount;
}
/**
* Get pointer to next output array element in queue.
* @param queue Lockless queue instance
* @return Pointer to array element data in the queue or NULL if empty, can only
* be used up until fluid_ringbuffer_next_outptr() is called.
*
* This function along with fluid_ringbuffer_next_outptr() form a queue "pop"
* operation and is split into 2 functions to avoid an element copy.
*/
static FLUID_INLINE void*
fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue)
{
return fluid_atomic_int_get (&queue->count) == 0 ? NULL
: queue->array + queue->elementsize * queue->out;
}
/**
* Advance the output queue index to complete a "pop" operation.
* @param queue Lockless queue instance
*
* This function along with fluid_ringbuffer_get_outptr() form a queue "pop"
* operation and is split into 2 functions to avoid an element copy.
*/
static FLUID_INLINE void
fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue)
{
fluid_atomic_int_add (&queue->count, -1);
if (++queue->out == queue->totalcount)
queue->out = 0;
}
#endif /* _FLUID_ringbuffer_H */

View File

@ -23,6 +23,7 @@
#include "fluid_sys.h"
#include "fluid_midi.h"
#include "fluid_ringbuffer.h"
/**
* Type of queued event.
@ -120,22 +121,18 @@ typedef struct
};
} fluid_event_queue_elem_t;
/**
* Lockless event queue instance.
*/
typedef struct
typedef struct _fluid_ringbuffer_t fluid_event_queue_t;
static FLUID_INLINE fluid_event_queue_t *
fluid_event_queue_new (int count)
{
fluid_event_queue_elem_t *array; /**< Queue array of arbitrary size elements */
int totalcount; /**< Total count of elements in array */
int count; /**< Current count of elements */
int in; /**< Index in queue to store next pushed element */
int out; /**< Index in queue of next popped element */
void *synth; /**< Owning fluid_synth_t instance */
} fluid_event_queue_t;
return (fluid_event_queue_t *) new_fluid_ringbuffer(count, sizeof(fluid_event_queue_elem_t));
}
fluid_event_queue_t *fluid_event_queue_new (int count);
void fluid_event_queue_free (fluid_event_queue_t *queue);
static FLUID_INLINE void fluid_event_queue_free (fluid_event_queue_t *queue)
{
delete_fluid_ringbuffer(queue);
}
/**
* Get pointer to next input array element in queue.
@ -151,8 +148,7 @@ void fluid_event_queue_free (fluid_event_queue_t *queue);
static FLUID_INLINE fluid_event_queue_elem_t *
fluid_event_queue_get_inptr (fluid_event_queue_t *queue)
{
return fluid_atomic_int_get (&queue->count) == queue->totalcount ? NULL
: queue->array + queue->in;
return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_inptr(queue, 0);
}
/**
@ -165,10 +161,7 @@ fluid_event_queue_get_inptr (fluid_event_queue_t *queue)
static FLUID_INLINE void
fluid_event_queue_next_inptr (fluid_event_queue_t *queue)
{
fluid_atomic_int_inc (&queue->count);
if (++queue->in == queue->totalcount)
queue->in = 0;
fluid_ringbuffer_next_inptr(queue, 1);
}
/**
@ -183,8 +176,7 @@ fluid_event_queue_next_inptr (fluid_event_queue_t *queue)
static FLUID_INLINE fluid_event_queue_elem_t *
fluid_event_queue_get_outptr (fluid_event_queue_t *queue)
{
return fluid_atomic_int_get (&queue->count) == 0 ? NULL
: queue->array + queue->out;
return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_outptr(queue);
}
/**
@ -197,10 +189,7 @@ fluid_event_queue_get_outptr (fluid_event_queue_t *queue)
static FLUID_INLINE void
fluid_event_queue_next_outptr (fluid_event_queue_t *queue)
{
fluid_atomic_int_add (&queue->count, -1);
if (++queue->out == queue->totalcount)
queue->out = 0;
fluid_ringbuffer_next_outptr(queue);
}
#endif /* _FLUID_EVENT_QUEUE_H */

View File

@ -647,6 +647,12 @@ new_fluid_synth(fluid_settings_t *settings)
}
}
/* Allocate handler */
synth->eventhandler = new_fluid_rvoice_eventhandler(1, synth->polyphony*8, synth->polyphony);
if (synth->eventhandler == NULL)
goto error_recovery;
fluid_rvoice_handler_set_polyphony(synth->eventhandler->handler, synth->polyphony);
/* Allocate the sample buffers */
synth->left_buf = NULL;
synth->right_buf = NULL;
@ -901,6 +907,9 @@ delete_fluid_synth(fluid_synth_t* synth)
}
}
if (synth->eventhandler)
delete_fluid_rvoice_eventhandler(synth->eventhandler);
/* delete all the SoundFonts */
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
@ -1085,7 +1094,7 @@ fluid_synth_get_event_queue (fluid_synth_t* synth)
queue = fluid_event_queue_new (FLUID_MAX_EVENTS_PER_BUFSIZE);
if (!queue) return NULL; /* Error has already been logged */
queue->synth = synth;
queue->userdata = synth;
/* Atomicly and in a lock free fashion, put queue pointer in queues[] array */
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
@ -1223,7 +1232,7 @@ static void
fluid_synth_thread_queue_destroy_notify (void *data)
{
fluid_event_queue_t *queue = data;
fluid_synth_t *synth = queue->synth;
fluid_synth_t *synth = queue->userdata;
/* Queues are not freed (can't be thread safe without locking in synth thread),
* added to pool for potential future use */
@ -2582,6 +2591,10 @@ fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth)
if (_PLAYING (voice)) fluid_voice_off (voice);
}
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_handler_set_polyphony, synth->eventhandler->handler,
synth->polyphony, 0.0f);
return FLUID_OK;
}
@ -2968,22 +2981,38 @@ fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
}
static void
fluid_synth_check_finished_voices(fluid_synth_t* synth)
{
int j;
fluid_rvoice_t* fv;
while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) {
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]);
}
}
}
/*
* Process a single block (FLUID_BUFSIZE) of audio.
*/
static int
fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
{
fluid_real_t local_voice_buf[FLUID_BUFSIZE];
int i, auchan;
// int start_index, voice_index;
fluid_voice_t* voice;
fluid_real_t* left_buf;
fluid_real_t* right_buf;
// fluid_real_t local_voice_buf[FLUID_BUFSIZE];
int i;
// int start_index, voice_index, auchan;
// fluid_voice_t* voice;
// fluid_real_t* left_buf;
// fluid_real_t* right_buf;
fluid_real_t* reverb_buf;
fluid_real_t* chorus_buf;
int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
int count;
fluid_real_t* bufs[synth->audio_groups*2 + synth->effects_channels*2];
// int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
// int count;
fluid_profile_ref_var (prof_ref);
/* Assign ID of synthesis thread */
@ -3003,16 +3032,6 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
else break; /* First NULL ends the array (values are never set to NULL) */
}
/* clean the audio buffers */
for (i = 0; i < synth->nbuf; i++) {
FLUID_MEMSET(synth->left_buf[i], 0, byte_size);
FLUID_MEMSET(synth->right_buf[i], 0, byte_size);
}
for (i = 0; i < synth->effects_channels; i++) {
FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size);
FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size);
}
/* Set up the reverb / chorus buffers only, when the effect is
* enabled on synth level. Nonexisting buffers are detected in the
@ -3020,104 +3039,8 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
* in that case. */
reverb_buf = synth->with_reverb ? synth->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL;
chorus_buf = synth->with_chorus ? synth->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL;
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
#if 0
if (synth->cores > 1)
{
/* Look for first active voice to process */
for (voice_index = 0; voice_index < synth->polyphony; voice_index++)
{
voice = synth->voice[voice_index];
if (_PLAYING (voice)) break;
}
/* Was there a voice to process? */
if (voice_index < synth->polyphony)
{
fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */
synth->core_voice_index = voice_index + 1; /* Set the next core_voice_index */
/* Tell the other core threads that there is work to do */
synth->core_work = TRUE;
synth->core_waiting_for_last = FALSE;
fluid_cond_broadcast (synth->core_cond);
fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */
while (TRUE)
{
got_voice: /* We got a voice to process */
count = fluid_voice_write (voice, &synth->core_bufs[voice_index * FLUID_BUFSIZE]);
if (count > 0) synth->core_voice_processed[voice_index] = voice;
/* Look for next active voice to process (in a lock free manner) */
do
{
voice_index = fluid_atomic_int_get (&synth->core_voice_index);
for (start_index = voice_index; voice_index < synth->polyphony; voice_index++)
{
voice = synth->voice[voice_index];
if (_PLAYING (voice))
{
if (fluid_atomic_int_compare_and_exchange (&synth->core_voice_index,
start_index, voice_index + 1))
goto got_voice;
break; /* compare and exchange failed (another thread grabbed the voice first) */
}
}
}
while (voice_index < synth->polyphony);
/* No more voices to process */
fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */
synth->core_work = FALSE;
/* If there are still other core threads in progress, wait for last one */
if (synth->core_inprogress > 0)
{
synth->core_waiting_for_last = TRUE;
while (synth->core_inprogress > 0)
fluid_cond_wait (synth->core_wait_last_cond, synth->core_mutex);
}
fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */
break; /* We're done */
} /* while (TRUE) - Process voices loop */
/* Mix all voices */
for (i = 0; i < synth->polyphony; i++)
{
voice = synth->core_voice_processed[i];
if (!voice) continue;
synth->core_voice_processed[i] = NULL;
auchan = fluid_channel_get_num (fluid_voice_get_channel (voice));
auchan %= synth->audio_groups;
left_buf = synth->left_buf[auchan];
right_buf = synth->right_buf[auchan];
fluid_voice_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf);
} /* while (TRUE) - Mix processed voices loop */
} /* if (i < synth->polyphony) - Are there any voices to process? */
}
else /* synth->cores < 1 - Not multi-core enabled */
{
#endif
/* call all playing synthesis processes */
for (i = 0; i < synth->polyphony; i++) {
fluid_profile_ref_var (prof_ref_voice);
voice = synth->voice[i];
if (!_PLAYING(voice)) continue;
bufs[synth->audio_groups*2 + SYNTH_REVERB_CHANNEL] = reverb_buf;
bufs[synth->audio_groups*2 + SYNTH_CHORUS_CHANNEL] = chorus_buf;
/* The output associated with a MIDI channel is wrapped around
* using the number of audio groups as modulo divider. This is
@ -3131,21 +3054,14 @@ got_voice: /* We got a voice to process */
* channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to
* output 2, 3, 6, 9, 12 etc to output 3.
*/
auchan = fluid_channel_get_num(fluid_voice_get_channel(voice));
auchan %= synth->audio_groups;
left_buf = synth->left_buf[auchan];
right_buf = synth->right_buf[auchan];
count = fluid_voice_write (voice, local_voice_buf);
if (count > 0)
fluid_voice_mix (voice, count, local_voice_buf, left_buf, right_buf,
reverb_buf, chorus_buf);
fluid_profile (FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice);
}
#if 0
for (i = 0; i < synth->audio_groups; i++) {
bufs[i*2] = synth->left_buf[i];
bufs[i*2+1] = synth->right_buf[i];
}
#endif
fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
fluid_rvoice_handler_render(synth->eventhandler->handler, 1, synth->audio_groups*2 + 2, bufs);
fluid_check_fpe("Synthesis processes");
@ -3208,6 +3124,8 @@ got_voice: /* We got a voice to process */
synth->ticks += FLUID_BUFSIZE;
fluid_synth_check_finished_voices(synth);
/* Testcase, that provokes a denormal floating point error */
#if 0
{float num=1;while (num != 0){num*=0.5;};};
@ -3448,6 +3366,7 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
return voice;
}
/**
* Allocate a synthesis voice.
* @param synth FluidSynth instance
@ -3476,6 +3395,8 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL);
fluid_return_val_if_fail (fluid_synth_is_synth_thread (synth), NULL);
fluid_synth_check_finished_voices(synth);
/* check if there's an available synthesis process */
for (i = 0; i < synth->polyphony; i++) {
if (_AVAILABLE(synth->voice[i])) {
@ -3486,6 +3407,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
/* No success yet? Then stop a running voice. */
if (voice == NULL) {
FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice");
voice = fluid_synth_free_voice_by_kill_LOCAL(synth);
}
@ -3589,6 +3511,11 @@ 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_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
/* TODO: Several voices in sync ? */
fluid_rvoice_eventhandler_flush(synth->eventhandler);
}
/**

View File

@ -40,6 +40,8 @@
#include "fluid_ladspa.h"
#include "fluid_midi_router.h"
#include "fluid_sys.h"
#include "fluid_rvoice_handler.h"
#include "fluid_rvoice_event.h"
/***************************************************************
*
@ -195,6 +197,7 @@ struct _fluid_synth_t
unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */
unsigned int storeid;
int nbuf; /**< How many audio buffers are used? (depends on nr of audio channels / groups)*/
fluid_rvoice_eventhandler_t* eventhandler;
fluid_real_t** left_buf;
fluid_real_t** right_buf;

View File

@ -26,6 +26,7 @@
#include "fluid_synth.h"
#include "fluid_sys.h"
#include "fluid_sfont.h"
#include "fluid_rvoice_event.h"
/* used for filter turn off optimization - if filter cutoff is above the
specified value and filter q is below the other value, turn filter off */
@ -41,13 +42,54 @@ static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
static fluid_real_t
fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
#define UPDATE_RVOICE0(proc) proc(voice->rvoice)
#define UPDATE_RVOICE1(proc, arg1) proc(voice->rvoice, arg1)
#define UPDATE_RVOICE2(proc, arg1, arg2) proc(voice->rvoice, arg1, arg2)
#define UPDATE_RVOICE_ENVLFO1(proc, envp, arg1) proc(&voice->rvoice->envlfo.envp, arg1)
#define UPDATE_RVOICE0(proc) \
do { \
if (voice->can_access_rvoice) proc(voice->rvoice); \
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
proc, voice->rvoice, 0, 0.0f); \
} while (0)
#define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \
do { \
if (voice->can_access_rvoice) proc(obj, rarg); \
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
proc, obj, 0, rarg); \
} while (0)
#define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \
do { \
if (voice->can_access_rvoice) proc(obj, iarg); \
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
proc, obj, iarg, 0.0f); \
} while (0)
#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
do { \
if (voice->can_access_rvoice) proc(obj, iarg, rarg); \
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
proc, obj, iarg, rarg); \
} while (0)
#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \
do { \
if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \
else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \
proc, obj, iarg, r1, r2, r3, r4, r5); \
} while (0)
#define UPDATE_RVOICE_ENVDATA(envp, section, arg1, arg2, arg3, arg4, arg5) \
fluid_adsr_env_set_data(&voice->rvoice->envlfo.envp, section, arg1, arg2, arg3, arg4, arg5)
#define UPDATE_RVOICE_FILTER1(proc, arg1) proc(&voice->rvoice->resonant_filter, arg1)
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.envp, 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)
#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg)
#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
#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)
/*
* new_fluid_voice
@ -77,7 +119,7 @@ new_fluid_voice(fluid_real_t output_rate)
voice->channel = NULL;
voice->sample = NULL;
voice->output_rate = output_rate;
UPDATE_RVOICE1(fluid_rvoice_set_output_rate, output_rate);
UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, output_rate);
/* The 'sustain' and 'finished' segments of the volume / modulation
* envelope are constant. They are never affected by any modulator
@ -142,10 +184,10 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
voice->debug = 0;
voice->has_noteoff = 0;
UPDATE_RVOICE0(fluid_rvoice_reset);
UPDATE_RVOICE1(fluid_rvoice_set_sample, sample);
fluid_rvoice_set_sample(voice->rvoice, sample);
i = fluid_channel_get_interp_method(voice->channel);
UPDATE_RVOICE1(fluid_rvoice_set_interp_method, i);
i = fluid_channel_get_interp_method(channel);
UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i);
/* Set all the generators to their default value, according to SF
* 2.01 section 8.1.3 (page 48). The value of NRPN messages are
@ -154,14 +196,22 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
* into voice parameters in
* fluid_voice_calculate_runtime_synthesis_parameters. */
fluid_gen_init(&voice->gen[0], channel);
UPDATE_RVOICE1(fluid_rvoice_set_samplemode, voice->gen[GEN_SAMPLEMODE].val);
UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, voice->gen[GEN_SAMPLEMODE].val);
voice->synth_gain = gain;
/* avoid division by zero later*/
if (voice->synth_gain < 0.0000001){
voice->synth_gain = 0.0000001;
}
UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, voice->synth_gain);
UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
/* Set up buffer mapping, should be done more flexible in the future. */
i = channel->synth->audio_groups;
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL);
i = 2 * (voice->chan % i);
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. */
@ -182,7 +232,7 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
voice->gen[i].val = val;
voice->gen[i].flags = GEN_SET;
if (i == GEN_SAMPLEMODE)
UPDATE_RVOICE1(fluid_rvoice_set_samplemode, val);
UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, val);
}
/**
@ -269,19 +319,14 @@ fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf)
{
fluid_rvoice_buffers_t buffers;
fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf};
buffers.count = 4;
buffers.bufs[0].buf = left_buf;
buffers.bufs[1].buf = right_buf;
buffers.bufs[2].buf = reverb_buf;
buffers.bufs[3].buf = chorus_buf;
buffers.bufs[0].amp = voice->amp_left;
buffers.bufs[1].amp = voice->amp_right;
buffers.bufs[2].amp = voice->amp_reverb;
buffers.bufs[3].amp = voice->amp_chorus;
fluid_rvoice_buffers_mix(&buffers, dsp_buf, count);
fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left);
fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right);
fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb);
fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus);
fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4);
fluid_check_fpe ("voice_mix");
}
@ -447,7 +492,7 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
}
/* Make an estimate on how loud this voice can get at any time (attenuation). */
UPDATE_RVOICE1(fluid_rvoice_set_min_attenuation_cB,
UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB,
fluid_voice_get_lower_boundary_for_attenuation(voice));
return FLUID_OK;
}
@ -555,8 +600,8 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
voice->pan = _GEN(voice, GEN_PAN);
voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f;
voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f;
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left);
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
break;
case GEN_ATTENUATION:
@ -567,7 +612,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
* Motivation for range checking:
* OHPiano.SF2 sets initial attenuation to a whooping -96 dB */
fluid_clip(voice->attenuation, 0.0, 1440.0);
UPDATE_RVOICE1(fluid_rvoice_set_attenuation, voice->attenuation);
UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation);
break;
/* The pitch is calculated from three different generators.
@ -580,7 +625,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
voice->pitch = (_GEN(voice, GEN_PITCH)
+ 100.0f * _GEN(voice, GEN_COARSETUNE)
+ _GEN(voice, GEN_FINETUNE));
UPDATE_RVOICE1(fluid_rvoice_set_pitch, voice->pitch);
UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch);
break;
case GEN_REVERBSEND:
@ -588,7 +633,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f;
fluid_clip(voice->reverb_send, 0.0, 1.0);
voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f;
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
break;
case GEN_CHORUSSEND:
@ -596,7 +641,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f;
fluid_clip(voice->chorus_send, 0.0, 1.0);
voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f;
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
break;
case GEN_OVERRIDEROOTKEY:
@ -618,7 +663,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
}
/* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
fluid_voice_calculate_gen_pitch(voice);
UPDATE_RVOICE1(fluid_rvoice_set_root_pitch_hz, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x);
break;
@ -663,26 +708,26 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
case GEN_MODLFOTOPITCH:
x = _GEN(voice, GEN_MODLFOTOPITCH);
fluid_clip(x, -12000.0, 12000.0);
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_pitch, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
break;
case GEN_MODLFOTOVOL:
x = _GEN(voice, GEN_MODLFOTOVOL);
fluid_clip(x, -960.0, 960.0);
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_vol, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
break;
case GEN_MODLFOTOFILTERFC:
x = _GEN(voice, GEN_MODLFOTOFILTERFC);
fluid_clip(x, -12000, 12000);
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_fc, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
break;
case GEN_MODLFODELAY:
x = _GEN(voice, GEN_MODLFODELAY);
fluid_clip(x, -12000.0f, 5000.0f);
z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, modlfo, z);
UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z);
break;
case GEN_MODLFOFREQ:
@ -692,7 +737,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_MODLFOFREQ);
fluid_clip(x, -16000.0f, 4500.0f);
x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, modlfo, x);
UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x);
break;
case GEN_VIBLFOFREQ:
@ -704,20 +749,20 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_VIBLFOFREQ);
fluid_clip(x, -16000.0f, 4500.0f);
x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate;
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, viblfo, x);
UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x);
break;
case GEN_VIBLFODELAY:
x = _GEN(voice,GEN_VIBLFODELAY);
fluid_clip(x, -12000.0f, 5000.0f);
z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, viblfo, z);
UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z);
break;
case GEN_VIBLFOTOPITCH:
x = _GEN(voice, GEN_VIBLFOTOPITCH);
fluid_clip(x, -12000.0, 12000.0);
UPDATE_RVOICE1(fluid_rvoice_set_viblfo_to_pitch, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x);
break;
case GEN_KEYNUM:
@ -752,7 +797,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
case GEN_MODENVTOPITCH:
x = _GEN(voice, GEN_MODENVTOPITCH);
fluid_clip(x, -12000.0, 12000.0);
UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_pitch, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
break;
case GEN_MODENVTOFILTERFC:
@ -763,7 +808,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
* Filter is reported to make funny noises now and then
*/
fluid_clip(x, -12000.0, 12000.0);
UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_fc, x);
UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x);
break;
@ -784,7 +829,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = (voice->sample->start
+ (int) _GEN(voice, GEN_STARTADDROFS)
+ 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
UPDATE_RVOICE1(fluid_rvoice_set_start, x);
UPDATE_RVOICE_I1(fluid_rvoice_set_start, x);
}
break;
case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */
@ -793,7 +838,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = (voice->sample->end
+ (int) _GEN(voice, GEN_ENDADDROFS)
+ 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
UPDATE_RVOICE1(fluid_rvoice_set_end, x);
UPDATE_RVOICE_I1(fluid_rvoice_set_end, x);
}
break;
case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */
@ -802,7 +847,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = (voice->sample->loopstart
+ (int) _GEN(voice, GEN_STARTLOOPADDROFS)
+ 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
UPDATE_RVOICE1(fluid_rvoice_set_loopstart, x);
UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, x);
}
break;
@ -812,7 +857,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = (voice->sample->loopend
+ (int) _GEN(voice, GEN_ENDLOOPADDROFS)
+ 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
UPDATE_RVOICE1(fluid_rvoice_set_loopend, x);
UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, x);
}
break;
@ -1036,7 +1081,7 @@ fluid_voice_noteoff(fluid_voice_t* voice)
voice->status = FLUID_VOICE_SUSTAINED;
} else {
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick);
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
voice->has_noteoff = 1;
}
@ -1079,7 +1124,7 @@ fluid_voice_kill_excl(fluid_voice_t* voice){
fluid_voice_update_param(voice, GEN_MODENVRELEASE);
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick);
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
return FLUID_OK;
@ -1295,11 +1340,11 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
voice->amp_reverb = voice->reverb_send * gain / 32768.0f;
voice->amp_chorus = voice->chorus_send * gain / 32768.0f;
UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, gain);
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left);
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right);
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb);
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus);
UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
return FLUID_OK;
}

View File

@ -29,6 +29,7 @@
#include "fluid_adsr_env.h"
#include "fluid_lfo.h"
#include "fluid_rvoice.h"
#include "fluid_sys.h"
#define NO_CHANNEL 0xff
@ -133,6 +134,26 @@ void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
int fluid_voice_kill_excl(fluid_voice_t* voice);
/**
* Locks the rvoice for rendering, so it can't be modified directly
*/
static FLUID_INLINE fluid_rvoice_t*
fluid_voice_lock_rvoice(fluid_voice_t* voice)
{
voice->can_access_rvoice = 0;
return voice->rvoice;
}
/**
* Locks the rvoice for rendering, so it can be modified directly
*/
static FLUID_INLINE void
fluid_voice_unlock_rvoice(fluid_voice_t* voice)
{
voice->can_access_rvoice = 1;
}
#define fluid_voice_get_channel(voice) ((voice)->channel)
@ -147,7 +168,8 @@ int fluid_voice_kill_excl(fluid_voice_t* voice);
* section 5 (release). */
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))
#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 _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val)

View File

@ -0,0 +1,38 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_adsr_env.h"
void
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
fluid_adsr_env_section_t section,
unsigned int count,
fluid_real_t coeff,
fluid_real_t incr,
fluid_real_t min,
fluid_real_t max)
{
env->data[section].count = count;
env->data[section].coeff = coeff;
env->data[section].incr = incr;
env->data[section].min = min;
env->data[section].max = max;
}

View File

@ -22,6 +22,7 @@
#define _FLUID_ADSR_ENVELOPE_H
#include "fluidsynth_priv.h"
#include "fluid_sys.h"
/*
* envelope data
@ -59,7 +60,7 @@ struct _fluid_adsr_env_t {
/* For performance, all functions are inlined */
static inline void
static FLUID_INLINE void
fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
{
fluid_env_data_t* env_data;
@ -99,21 +100,16 @@ fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
env->count++;
}
static inline void
/* This one cannot be inlined since it is referenced in
the event queue */
void
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
fluid_adsr_env_section_t section,
unsigned int count,
fluid_real_t coeff,
fluid_real_t incr,
fluid_real_t min,
fluid_real_t max)
{
env->data[section].count = count;
env->data[section].coeff = coeff;
env->data[section].incr = incr;
env->data[section].min = min;
env->data[section].max = max;
}
fluid_real_t max);
static inline void
fluid_adsr_env_reset(fluid_adsr_env_t* env)

View File

@ -273,6 +273,8 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
/******************* sample sanity check **********/
if (!voice->dsp.sample)
return 0;
if (voice->dsp.check_sample_sanity_flag)
fluid_rvoice_check_sample_sanity(voice);
@ -369,32 +371,46 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
}
static inline fluid_real_t*
get_dest_buf(fluid_rvoice_buffers_t* buffers, int index,
fluid_real_t** dest_bufs, int dest_bufcount)
{
int j = buffers->bufs[index].mapping;
if (j >= dest_bufcount || j < 0) return NULL;
return dest_bufs[j];
}
/**
* Mix data down to buffers
*
* @param buffers Destination buffer(s)
* @param dsp_buf Mono sample source
* @param count Number of samples to process
* @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction)
* @param dest_bufs Array of buffers to mixdown to
* @param dest_bufcount Length of dest_bufs
*/
void
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
fluid_real_t* dsp_buf, int count)
fluid_real_t* dsp_buf, int samplecount,
fluid_real_t** dest_bufs, int dest_bufcount)
{
int bufcount = buffers->count;
int i, dsp_i;
if (!samplecount || !bufcount || !dest_bufcount)
return;
for (i=0; i < bufcount; i++) {
fluid_real_t amp = buffers->bufs[i].amp;
fluid_real_t* buf = buffers->bufs[i].buf;
fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
fluid_real_t* next_buf;
fluid_real_t amp = buffers->bufs[i].amp;
if (buf == NULL || amp == 0.0f)
continue;
/* Optimization for centered stereo samples - we can save one
multiplication per sample */
if (i < bufcount-1 && buffers->bufs[i+1].buf != NULL &&
buffers->bufs[i+1].amp == amp) {
fluid_real_t* next_buf = buffers->bufs[i+1].buf;
for (dsp_i = 0; dsp_i < count; dsp_i++) {
next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount));
if (next_buf && buffers->bufs[i+1].amp == amp) {
for (dsp_i = 0; dsp_i < samplecount; dsp_i++) {
fluid_real_t samp = amp * dsp_buf[dsp_i];
buf[dsp_i] += samp;
next_buf[dsp_i] += samp;
@ -402,29 +418,51 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
i++;
}
else {
for (dsp_i = 0; dsp_i < count; dsp_i++)
for (dsp_i = 0; dsp_i < samplecount; dsp_i++)
buf[dsp_i] += amp * dsp_buf[dsp_i];
}
}
}
/**
* Synthesize a voice to several buffers.
*
* @param voice rvoice to synthesize
* @return Count of samples written to the buffers. (-1 means voice is currently
* quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
* Initialize buffers up to (and including) bufnum
*/
int
fluid_rvoice_render(fluid_rvoice_t* voice)
static int
fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum)
{
fluid_real_t dsp_buf[FLUID_BUFSIZE];
int result = fluid_rvoice_write(voice, dsp_buf);
if (result > 0)
fluid_rvoice_buffers_mix(&voice->buffers, dsp_buf, result);
return result;
unsigned int i;
if (bufnum < buffers->count) return FLUID_OK;
if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
for (i = buffers->count; i <= bufnum; i++) {
buffers->bufs[bufnum].amp = 0.0f;
buffers->bufs[bufnum].mapping = i;
}
buffers->count = bufnum+1;
return FLUID_OK;
}
void
fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
unsigned int bufnum, fluid_real_t value)
{
if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
return;
buffers->bufs[bufnum].amp = value;
}
void
fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
unsigned int bufnum, int mapping)
{
if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
return;
buffers->bufs[bufnum].mapping = mapping;
}
void
fluid_rvoice_reset(fluid_rvoice_t* voice)
{
@ -524,15 +562,6 @@ fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
voice->dsp.min_attenuation_cB = value;
}
void
fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value)
{
if (bufnum >= FLUID_RVOICE_MAX_BUFS) return; /* Safety net */
if (voice->buffers.count <= bufnum)
voice->buffers.count = bufnum+1;
voice->buffers.bufs[bufnum].amp = value;
}
void
fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
{

View File

@ -134,10 +134,10 @@ struct _fluid_rvoice_dsp_t
*/
struct _fluid_rvoice_buffers_t
{
unsigned int count; /* Number of buffers */
unsigned int count; /* Number of records in "bufs" */
struct {
fluid_real_t amp;
fluid_real_t* buf;
int mapping; /* Mapping to mixdown buffer index */
} bufs[FLUID_RVOICE_MAX_BUFS];
};
@ -154,10 +154,15 @@ struct _fluid_rvoice_t
};
int fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf);
int fluid_rvoice_write(fluid_rvoice_t* voice, fluid_real_t *dsp_buf);
void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
fluid_real_t* dsp_buf, int count);
int fluid_rvoice_render(fluid_rvoice_t* voice);
fluid_real_t* dsp_buf, int samplecount,
fluid_real_t** dest_bufs, int dest_bufcount);
void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
unsigned int bufnum, fluid_real_t value);
void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
unsigned int bufnum, int mapping);
/* Dynamic update functions */
@ -171,7 +176,6 @@ void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value);
void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value);

View File

@ -0,0 +1,254 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_rvoice_event.h"
#include "fluid_rvoice.h"
#include "fluid_rvoice_handler.h"
#include "fluid_iir_filter.h"
#include "fluid_lfo.h"
#include "fluid_adsr_env.h"
#define EVENTFUNC_0(proc, type) \
if (event->method == proc) { \
proc((type) event->object); \
return; }
#define EVENTFUNC_R1(proc, type) \
if (event->method == proc) { \
proc((type) event->object, event->realparams[0]); \
return; }
#define EVENTFUNC_PTR(proc, type, type2) \
if (event->method == proc) { \
proc((type) event->object, (type2) event->ptr); \
return; }
#define EVENTFUNC_I1(proc, type) \
if (event->method == proc) { \
proc((type) event->object, event->intparam); \
return; }
#define EVENTFUNC_IR(proc, type) \
if (event->method == proc) { \
proc((type) event->object, event->intparam, event->realparams[0]); \
return; }
#define EVENTFUNC_ALL(proc, type) \
if (event->method == proc) { \
proc((type) event->object, event->intparam, event->realparams[0], \
event->realparams[1], event->realparams[2], event->realparams[3], \
event->realparams[4]); \
return; }
void
fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
{
EVENTFUNC_PTR(fluid_rvoice_handler_add_voice, fluid_rvoice_handler_t*, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*);
EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*);
EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*);
EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*);
EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*);
EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*);
EVENTFUNC_IR(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*);
EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*);
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_output_rate, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_root_pitch_hz, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_synth_gain, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_pitch, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_attenuation, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_min_attenuation_cB, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_viblfo_to_pitch, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_pitch, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_vol, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_fc, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_fc, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_interp_method, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_start, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_end, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_loopstart, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_loopend, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*);
EVENTFUNC_R1(fluid_rvoice_handler_set_polyphony, fluid_rvoice_handler_t*);
FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method);
}
/**
* In order to be able to push more than one event atomically,
* use push for all events, then use flush to commit them to the
* queue. If threadsafe is false, all events are processed immediately. */
int
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)
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;
}
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)
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;
}
int
fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, int intparam,
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;
if (event == NULL)
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);
return FLUID_OK;
}
static void
finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
{
fluid_rvoice_eventhandler_t* eventhandler = userdata;
fluid_rvoice_t** vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0);
if (vptr == NULL)
return; // Buffer full
*vptr = rvoice;
fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1);
}
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
int is_threadsafe, int queuesize, int finished_voices_size)
{
fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
if (eventhandler == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
eventhandler->handler = NULL;
eventhandler->queue = NULL;
eventhandler->finished_voices = NULL;
eventhandler->is_threadsafe = is_threadsafe;
eventhandler->queue_stored = 0;
eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size,
sizeof(fluid_rvoice_t*));
if (eventhandler->finished_voices == NULL)
goto error_recovery;
eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t));
if (eventhandler->queue == NULL)
goto error_recovery;
eventhandler->handler = new_fluid_rvoice_handler();
if (eventhandler->handler == NULL)
goto error_recovery;
fluid_rvoice_handler_set_voice_callback(eventhandler->handler,
finished_voice_callback, eventhandler);
return eventhandler;
error_recovery:
delete_fluid_rvoice_eventhandler(eventhandler);
return NULL;
}
/**
* Call fluid_rvoice_event_dispatch for all events in queue
*/
void
fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler)
{
fluid_rvoice_event_t* event;
while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) {
fluid_rvoice_event_dispatch(event);
fluid_ringbuffer_next_outptr(handler->queue);
}
}
void
delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler)
{
if (handler == NULL) return;
delete_fluid_ringbuffer(handler->queue);
delete_fluid_ringbuffer(handler->finished_voices);
FLUID_FREE(handler);
}

View File

@ -0,0 +1,113 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_RVOICE_EVENT_H
#define _FLUID_RVOICE_EVENT_H
#include "fluidsynth_priv.h"
#include "fluid_rvoice_handler.h"
#include "fluid_ringbuffer.h"
#define EVENT_REAL_PARAMS (5)
typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t;
typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t;
struct _fluid_rvoice_event_t {
void* method;
void* object;
void* ptr;
int intparam;
fluid_real_t realparams[EVENT_REAL_PARAMS];
};
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
*/
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 */
int queue_stored; /**< Extras pushed but not flushed */
fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */
fluid_rvoice_handler_t* handler;
};
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
int is_threadsafe, int queuesize, int finished_voices_size);
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
void fluid_rvoice_eventhandler_dispatch_all(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;
}
}
/**
* @return next finished voice, or NULL if nothing in queue
*/
static FLUID_INLINE fluid_rvoice_t*
fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t* handler)
{
void* result = fluid_ringbuffer_get_outptr(handler->finished_voices);
if (result == NULL) return NULL;
result = * (fluid_rvoice_t**) result;
fluid_ringbuffer_next_outptr(handler->finished_voices);
return result;
}
int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, int intparam,
fluid_real_t realparam);
int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, void* ptr);
int fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
void* method, void* object, int intparam,
fluid_real_t r1, fluid_real_t r2,
fluid_real_t r3, fluid_real_t r4, fluid_real_t r5);
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_handler_add_voice,
handler->handler, rvoice);
else
fluid_rvoice_handler_add_voice(handler->handler, rvoice);
}
#endif

View File

@ -0,0 +1,204 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_rvoice_handler.h"
fluid_rvoice_handler_t* new_fluid_rvoice_handler(void)
{
fluid_rvoice_handler_t* handler;
handler = FLUID_NEW(fluid_rvoice_handler_t);
if (handler == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(handler, 0, sizeof(fluid_rvoice_handler_t));
return handler;
}
void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler)
{
if (handler == NULL)
return;
#if 0
FLUID_FREE(handler->finished_voices);
#endif
FLUID_FREE(handler->voices);
FLUID_FREE(handler);
}
int
fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice)
{
if (handler->active_voices >= handler->polyphony) {
FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_handler_add_voice");
return FLUID_FAILED;
}
handler->voices[handler->active_voices++] = voice;
return FLUID_OK;
}
/**
* Update polyphony - max number of voices (NOTE: not hard real-time capable)
* @return FLUID_OK or FLUID_FAILED
*/
int
fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value)
{
void* newptr;
if (handler->active_voices > value)
return FLUID_FAILED;
#if 0
if (handler->finished_voice_count > value)
return FLUID_FAILED;
#endif
newptr = FLUID_REALLOC(handler->voices, value * sizeof(fluid_rvoice_t*));
if (newptr == NULL)
return FLUID_FAILED;
handler->voices = newptr;
#if 0
newptr = FLUID_REALLOC(handler->finished_voices, value * sizeof(fluid_rvoice_t*));
if (newptr == NULL)
return FLUID_FAILED;
handler->finished_voices = newptr;
#endif
handler->polyphony = value;
return FLUID_OK;
}
static void
fluid_rvoice_handler_remove_voice(fluid_rvoice_handler_t* handler, int index)
{
#if 0
if (handler->finished_voice_count < handler->polyphony)
handler->finished_voices[handler->finished_voice_count++] = handler->voices[index];
#endif
if (handler->remove_voice_callback != NULL)
handler->remove_voice_callback(handler->remove_voice_callback_userdata,
handler->voices[index]);
handler->active_voices--;
if (index < handler->active_voices) /* Move the last voice into the "hole" */
handler->voices[index] = handler->voices[handler->active_voices];
}
/**
* Synthesize one voice
* @return Number of samples written
*/
#if 0
static inline int
fluid_rvoice_handler_write_one(fluid_rvoice_handler_t* handler, int index,
fluid_real_t* buf, int blockcount)
{
int i, result = 0;
fluid_rvoice_t* voice = handler->voices[index];
for (i=0; i < blockcount; i++) {
int s = fluid_rvoice_write(voice, buf);
if (s == -1) {
FLUID_MEMSET(buf, 0, FLUID_BUFSIZE*sizeof(fluid_real_t));
s = FLUID_BUFSIZE;
}
buf += s;
result += s;
}
return result;
}
#endif
/**
* Synthesize one voice and add to buffer.
* NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means
* voice has been finished, removed and possibly replaced with another voice.
* @return Number of samples written
*/
static inline int
fluid_rvoice_handler_mix_one(fluid_rvoice_handler_t* handler, int index,
fluid_real_t** bufs, unsigned int blockcount, unsigned int bufcount)
{
unsigned int i, j=0, result = 0;
fluid_rvoice_t* voice = handler->voices[index];
fluid_real_t local_buf[FLUID_BUFSIZE*blockcount];
for (i=0; i < blockcount; i++) {
int s = fluid_rvoice_write(voice, &local_buf[FLUID_BUFSIZE*i]);
if (s == -1) {
s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */
FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t*));
}
result += s;
if (s < FLUID_BUFSIZE) {
j = 1;
break;
}
}
fluid_rvoice_buffers_mix(&voice->buffers, local_buf, result, bufs, bufcount);
if (j)
fluid_rvoice_handler_remove_voice(handler, index);
return result;
}
static inline void
fluid_resetbufs(int blockcount, int bufcount, fluid_real_t** bufs)
{
int i;
for (i=0; i < bufcount; i++)
FLUID_MEMSET(bufs[i], 0, blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t));
}
/**
* Single-threaded scenario, no worker threads
*/
static inline void
fluid_rvoice_handler_render_loop_simple(fluid_rvoice_handler_t* handler,
int blockcount, int bufcount, fluid_real_t** bufs)
{
int i;
int scount = blockcount * FLUID_BUFSIZE;
for (i=0; i < handler->active_voices; i++) {
int s = fluid_rvoice_handler_mix_one(handler, i, bufs, blockcount, bufcount);
if (s < scount) i--; /* Need to render the moved voice as well */
}
}
/**
* @param blockcount number of samples to render is blockcount*FLUID_BUFSIZE
* @param bufcount number of buffers to render into
* @param bufs array of bufcount buffers, each containing blockcount*FLUID_BUFSIZE samples
*/
void
fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler,
int blockcount, int bufcount, fluid_real_t** bufs)
{
fluid_resetbufs(blockcount, bufcount, bufs);
fluid_rvoice_handler_render_loop_simple(handler, blockcount, bufcount, bufs);
}

View File

@ -0,0 +1,80 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_RVOICE_HANDLER_H
#define _FLUID_RVOICE_HANDLER_H
#include "fluid_rvoice.h"
#include "fluid_sys.h"
typedef struct _fluid_rvoice_handler_t fluid_rvoice_handler_t;
struct _fluid_rvoice_handler_t {
fluid_rvoice_t** voices; /* Sorted so that all nulls are last */
int polyphony; /* Length of voices array */
int active_voices; /* Number of non-null voices */
#if 0
fluid_rvoice_t** finished_voices; /* List of voices who have finished */
int finished_voice_count;
#endif
void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Recieve this callback every time a voice is removed */
void* remove_voice_callback_userdata;
};
int fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice);
int fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value);
void fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler,
int blockcount, int bufcount,
fluid_real_t** bufs);
static FLUID_INLINE void
fluid_rvoice_handler_set_voice_callback(
fluid_rvoice_handler_t* handler,
void (*func)(void*, fluid_rvoice_t*),
void* userdata)
{
handler->remove_voice_callback_userdata = userdata;
handler->remove_voice_callback = func;
}
#if 0
static FLUID_INLINE fluid_rvoice_t**
fluid_rvoice_handler_get_finished_voices(fluid_rvoice_handler_t* handler,
int* count)
{
*count = handler->finished_voice_count;
return handler->finished_voices;
}
static inline void
fluid_rvoice_handler_clear_finished_voices(fluid_rvoice_handler_t* handler)
{
handler->finished_voice_count = 0;
}
#endif
fluid_rvoice_handler_t* new_fluid_rvoice_handler(void);
void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler);
#endif