summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_rvoice_mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_rvoice_mixer.c')
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_mixer.c1885
1 files changed, 1144 insertions, 741 deletions
diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c
index d5369aacce..8c5254f269 100644
--- a/libs/fluidsynth/src/fluid_rvoice_mixer.c
+++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c
@@ -3,16 +3,16 @@
* 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
@@ -24,10 +24,7 @@
#include "fluid_rev.h"
#include "fluid_chorus.h"
#include "fluidsynth_priv.h"
-//#include "fluid_ladspa.h"
-
-#define SYNTH_REVERB_CHANNEL 0
-#define SYNTH_CHORUS_CHANNEL 1
+#include "fluid_synth.h"
#undef ENABLE_MIXER_THREADS // Ardour does the multithreading -- synth.cpu-cores defaults to 1
@@ -37,492 +34,651 @@
typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t;
-struct _fluid_mixer_buffers_t {
- fluid_rvoice_mixer_t* mixer; /**< Owner of object */
-#ifdef ENABLE_MIXER_THREADS
- fluid_thread_t* thread; /**< Thread object */
+struct _fluid_mixer_buffers_t
+{
+ fluid_rvoice_mixer_t *mixer; /**< Owner of object */
+#if ENABLE_MIXER_THREADS
+ fluid_thread_t *thread; /**< Thread object */
#endif
- fluid_rvoice_t** finished_voices; /* List of voices who have finished */
- int finished_voice_count;
-
- int ready; /**< Atomic: buffers are ready for mixing */
-
- int buf_blocks; /**< Number of blocks allocated in the buffers */
-
- int buf_count;
- fluid_real_t** left_buf;
- fluid_real_t** right_buf;
-
- int fx_buf_count;
- fluid_real_t** fx_left_buf;
- fluid_real_t** fx_right_buf;
+ fluid_rvoice_t **finished_voices; /* List of voices who have finished */
+ int finished_voice_count;
+
+ fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */
+
+ fluid_real_t *local_buf;
+
+ int buf_count;
+ int fx_buf_count;
+
+ /** buffer to store the left part of a stereo channel to.
+ * Specifically a two dimensional array, containing \c buf_count sample buffers
+ * (i.e. for each synth.audio-channels), of which each contains
+ * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)
+ * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT
+ * boundary provided that this pointer points to an aligned buffer.
+ * So make sure to access the sample buffer by first aligning this
+ * pointer using fluid_align_ptr()
+ */
+ fluid_real_t *left_buf;
+
+ /** dito, but for right part of a stereo channel */
+ fluid_real_t *right_buf;
+
+ /** buffer to store the left part of a stereo effects channel to.
+ * Specifically a two dimensional array, containing \c fx_buf_count buffers
+ * (i.e. for each synth.effects-channels), of which each buffer contains
+ * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)
+ */
+ fluid_real_t *fx_left_buf;
+ fluid_real_t *fx_right_buf;
};
typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t;
-struct _fluid_mixer_fx_t {
- fluid_revmodel_t* reverb; /**< Reverb unit */
- fluid_chorus_t* chorus; /**< Chorus unit */
- int with_reverb; /**< Should the synth use the built-in reverb unit? */
- int with_chorus; /**< Should the synth use the built-in chorus unit? */
- int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */
+struct _fluid_mixer_fx_t
+{
+ fluid_revmodel_t *reverb; /**< Reverb unit */
+ fluid_chorus_t *chorus; /**< Chorus unit */
};
-struct _fluid_rvoice_mixer_t {
- fluid_mixer_fx_t fx;
+struct _fluid_rvoice_mixer_t
+{
+ fluid_mixer_fx_t *fx;
- fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */
- void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Used by mixer only: Receive this callback every time a voice is removed */
- void* remove_voice_callback_userdata;
+ fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */
+ fluid_rvoice_eventhandler_t *eventhandler;
- fluid_rvoice_t** rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */
- int polyphony; /**< Read-only: Length of voices array */
- int active_voices; /**< Read-only: Number of non-null voices */
- int current_blockcount; /**< Read-only: how many blocks to process this time */
+ fluid_rvoice_t **rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */
+ int polyphony; /**< Read-only: Length of voices array */
+ int active_voices; /**< Read-only: Number of non-null voices */
+ int current_blockcount; /**< Read-only: how many blocks to process this time */
+ int fx_units;
+ int with_reverb; /**< Should the synth use the built-in reverb unit? */
+ int with_chorus; /**< Should the synth use the built-in chorus unit? */
+ int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */
#ifdef LADSPA
- fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
+ fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
#endif
-#ifdef ENABLE_MIXER_THREADS
+#if ENABLE_MIXER_THREADS
// int sleeping_threads; /**< Atomic: number of threads currently asleep */
// int active_threads; /**< Atomic: number of threads in the thread loop */
- int threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */
- int current_rvoice; /**< Atomic: for the threads to know next voice to */
- fluid_cond_t* wakeup_threads; /**< Signalled when the threads should wake up */
- fluid_cond_mutex_t* wakeup_threads_m; /**< wakeup_threads mutex companion */
- fluid_cond_t* thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */
- fluid_cond_mutex_t* thread_ready_m; /**< thread_ready mutex companion */
-
- int thread_count; /**< Number of extra mixer threads for multi-core rendering */
- fluid_mixer_buffers_t* threads; /**< Array of mixer threads (thread_count in length) */
+ fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */
+ fluid_atomic_int_t current_rvoice; /**< Atomic: for the threads to know next voice to */
+ fluid_cond_t *wakeup_threads; /**< Signalled when the threads should wake up */
+ fluid_cond_mutex_t *wakeup_threads_m; /**< wakeup_threads mutex companion */
+ fluid_cond_t *thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */
+ fluid_cond_mutex_t *thread_ready_m; /**< thread_ready mutex companion */
+
+ int thread_count; /**< Number of extra mixer threads for multi-core rendering */
+ fluid_mixer_buffers_t *threads; /**< Array of mixer threads (thread_count in length) */
#endif
};
-static FLUID_INLINE void
-fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer)
-{
- int i;
- fluid_profile_ref_var(prof_ref);
- if (mixer->fx.with_reverb) {
- if (mixer->fx.mix_fx_to_out) {
- for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
- fluid_revmodel_processmix(mixer->fx.reverb,
- &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
- &mixer->buffers.left_buf[0][i],
- &mixer->buffers.right_buf[0][i]);
- }
- else {
- for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
- fluid_revmodel_processreplace(mixer->fx.reverb,
- &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
- &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
- &mixer->buffers.fx_right_buf[SYNTH_REVERB_CHANNEL][i]);
- }
- fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref);
- }
-
- if (mixer->fx.with_chorus) {
- if (mixer->fx.mix_fx_to_out) {
- for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
- fluid_chorus_processmix(mixer->fx.chorus,
- &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
- &mixer->buffers.left_buf[0][i],
- &mixer->buffers.right_buf[0][i]);
- }
- else {
- for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
- fluid_chorus_processreplace(mixer->fx.chorus,
- &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
- &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
- &mixer->buffers.fx_right_buf[SYNTH_CHORUS_CHANNEL][i]);
- }
- fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref);
- }
-
-#ifdef LADSPA
- /* Run the signal through the LADSPA Fx unit */
- if (mixer->LADSPA_FxUnit) {
- int j;
- FLUID_DECLARE_VLA(fluid_real_t*, left_buf, mixer->buffers.buf_count);
- FLUID_DECLARE_VLA(fluid_real_t*, right_buf, mixer->buffers.buf_count);
- FLUID_DECLARE_VLA(fluid_real_t*, fx_left_buf, mixer->buffers.fx_buf_count);
- FLUID_DECLARE_VLA(fluid_real_t*, fx_right_buf, mixer->buffers.fx_buf_count);
- for (j=0; j < mixer->buffers.buf_count; j++) {
- left_buf[j] = mixer->buffers.left_buf[j];
- right_buf[j] = mixer->buffers.right_buf[j];
- }
- for (j=0; j < mixer->buffers.fx_buf_count; j++) {
- fx_left_buf[j] = mixer->buffers.fx_left_buf[j];
- fx_right_buf[j] = mixer->buffers.fx_right_buf[j];
- }
- for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) {
- fluid_LADSPA_run(mixer->LADSPA_FxUnit, left_buf, right_buf, fx_left_buf,
- fx_right_buf);
- for (j=0; j < mixer->buffers.buf_count; j++) {
- left_buf[j] += FLUID_BUFSIZE;
- right_buf[j] += FLUID_BUFSIZE;
- }
- for (j=0; j < mixer->buffers.fx_buf_count; j++) {
- fx_left_buf[j] += FLUID_BUFSIZE;
- fx_right_buf[j] += FLUID_BUFSIZE;
- }
- }
- fluid_check_fpe("LADSPA");
- }
+#if ENABLE_MIXER_THREADS
+static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer);
+static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level);
#endif
-}
-/**
- * During rendering, rvoices might be finished. Set this callback
- * for getting a callback any time the rvoice is finished.
- */
-void fluid_rvoice_mixer_set_finished_voices_callback(
- fluid_rvoice_mixer_t* mixer,
- void (*func)(void*, fluid_rvoice_t*),
- void* userdata)
+static FLUID_INLINE void
+fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount)
{
- mixer->remove_voice_callback_userdata = userdata;
- mixer->remove_voice_callback = func;
-}
+ const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units;
+ int i, f;
+ void (*reverb_process_func)(fluid_revmodel_t *rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
+ void (*chorus_process_func)(fluid_chorus_t *chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
+ fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r;
-/**
- * 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 int
-fluid_mix_one(fluid_rvoice_t* rvoice, fluid_real_t** bufs, unsigned int bufcount, int blockcount)
-{
- int i, result = 0;
+ // all dry unprocessed mono input is stored in the left channel
+ fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ fluid_real_t *in_ch = in_rev;
+
+ fluid_profile_ref_var(prof_ref);
- FLUID_DECLARE_VLA(fluid_real_t, local_buf, FLUID_BUFSIZE*blockcount);
- for (i=0; i < blockcount; i++) {
- int s = fluid_rvoice_write(rvoice, &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) {
- break;
+ if(mixer->mix_fx_to_out)
+ {
+ // mix effects to first stereo channel
+ out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+ out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ reverb_process_func = fluid_revmodel_processmix;
+ chorus_process_func = fluid_chorus_processmix;
+
+ }
+ else
+ {
+ // replace effects into respective stereo effects channel
+ out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ reverb_process_func = fluid_revmodel_processreplace;
+ chorus_process_func = fluid_chorus_processreplace;
+ }
+
+
+ if(mixer->with_reverb)
+ {
+ for(f = 0; f < mixer->fx_units; f++)
+ {
+ int buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL;
+
+ for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ {
+ int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i;
+
+ reverb_process_func(mixer->fx[f].reverb,
+ &in_rev[samp_idx],
+ mixer->mix_fx_to_out ? &out_rev_l[i] : &out_rev_l[samp_idx],
+ mixer->mix_fx_to_out ? &out_rev_r[i] : &out_rev_r[samp_idx]);
+ }
+ }
+
+ fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0,
+ current_blockcount * FLUID_BUFSIZE);
+ }
+
+ if(mixer->with_chorus)
+ {
+ for(f = 0; f < mixer->fx_units; f++)
+ {
+ int buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL;
+
+ for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ {
+ int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i;
+
+ chorus_process_func(mixer->fx[f].chorus,
+ &in_ch [samp_idx],
+ mixer->mix_fx_to_out ? &out_ch_l[i] : &out_ch_l[samp_idx],
+ mixer->mix_fx_to_out ? &out_ch_r[i] : &out_ch_r[samp_idx]);
+ }
+ }
+
+ fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0,
+ current_blockcount * FLUID_BUFSIZE);
+ }
+
+#ifdef LADSPA
+
+ /* Run the signal through the LADSPA Fx unit. The buffers have already been
+ * set up in fluid_rvoice_mixer_set_ladspa. */
+ if(mixer->ladspa_fx)
+ {
+ fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE);
+ fluid_check_fpe("LADSPA");
}
- }
- fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount);
- return result;
+#endif
}
/**
* Glue to get fluid_rvoice_buffers_mix what it wants
* Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling
*/
-static FLUID_INLINE int
-fluid_mixer_buffers_prepare(fluid_mixer_buffers_t* buffers, fluid_real_t** outbufs)
-{
- fluid_real_t* reverb_buf, *chorus_buf;
- int i;
-
- /* Set up the reverb / chorus buffers only, when the effect is
- * enabled on synth level. Nonexisting buffers are detected in the
- * DSP loop. Not sending the reverb / chorus signal saves some time
- * in that case. */
- reverb_buf = buffers->mixer->fx.with_reverb ? buffers->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL;
- chorus_buf = buffers->mixer->fx.with_chorus ? buffers->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL;
- outbufs[buffers->buf_count*2 + SYNTH_REVERB_CHANNEL] = reverb_buf;
- outbufs[buffers->buf_count*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
- * typically the number of output channels on the 'sound card',
- * as long as the LADSPA Fx unit is not used. In case of LADSPA
- * unit, think of it as subgroups on a mixer.
- *
- * For example: Assume that the number of groups is set to 2.
- * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2,
- * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI
- * 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.
- */
-
- for (i = 0; i < buffers->buf_count; i++) {
- outbufs[i*2] = buffers->left_buf[i];
- outbufs[i*2+1] = buffers->right_buf[i];
- }
- return buffers->buf_count*2 + 2;
+static FLUID_INLINE int
+fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs)
+{
+ fluid_real_t *base_ptr;
+ int i;
+ const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units;
+ const int offset = buffers->buf_count * 2;
+ int with_reverb = buffers->mixer->with_reverb;
+ int with_chorus = buffers->mixer->with_chorus;
+
+ /* Set up the reverb and chorus buffers only when the effect is enabled or
+ * when LADSPA is active. Nonexisting buffers are detected in the DSP loop.
+ * Not sending the effect signals saves some time in that case. */
+#ifdef LADSPA
+ int with_ladspa = (buffers->mixer->ladspa_fx != NULL);
+ with_reverb = (with_reverb | with_ladspa);
+ with_chorus = (with_chorus | with_ladspa);
+#endif
+
+ // all the dry, non-processed mono audio for effects is to be stored in the left buffers
+ base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < buffers->mixer->fx_units; i++)
+ {
+ int fx_idx = i * fx_channels_per_unit;
+
+ outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] =
+ (with_reverb)
+ ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]
+ : NULL;
+
+ outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] =
+ (with_chorus)
+ ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]
+ : NULL;
+ }
+
+ /* The output associated with a MIDI channel is wrapped around
+ * using the number of audio groups as modulo divider. This is
+ * typically the number of output channels on the 'sound card',
+ * as long as the LADSPA Fx unit is not used. In case of LADSPA
+ * unit, think of it as subgroups on a mixer.
+ *
+ * For example: Assume that the number of groups is set to 2.
+ * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2,
+ * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI
+ * 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.
+ */
+ base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < buffers->buf_count; i++)
+ {
+ outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+ }
+
+ base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < buffers->buf_count; i++)
+ {
+ outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+ }
+
+ return offset + buffers->fx_buf_count;
}
static FLUID_INLINE void
-fluid_finish_rvoice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* rvoice)
-{
- if (buffers->finished_voice_count < buffers->mixer->polyphony)
- buffers->finished_voices[buffers->finished_voice_count++] = rvoice;
- else
- FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony");
-}
-
-static void
-fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t* buffers)
-{
- int i,j;
- for (i=0; i < buffers->finished_voice_count; i++) {
- fluid_rvoice_t* v = buffers->finished_voices[i];
- int* av = &buffers->mixer->active_voices;
- for (j=0; j < *av; j++) {
- if (v == buffers->mixer->rvoices[j]) {
- (*av)--;
- /* Pack the array */
- if (j < *av)
- buffers->mixer->rvoices[j] = buffers->mixer->rvoices[*av];
- }
- }
- if (buffers->mixer->remove_voice_callback)
- buffers->mixer->remove_voice_callback(
- buffers->mixer->remove_voice_callback_userdata, v);
- }
- buffers->finished_voice_count = 0;
-}
-
-static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t* mixer)
-{
-#ifdef ENABLE_MIXER_THREADS
- int i;
- for (i=0; i < mixer->thread_count; i++)
- fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]);
+fluid_finish_rvoice(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice)
+{
+ if(buffers->finished_voice_count < buffers->mixer->polyphony)
+ {
+ buffers->finished_voices[buffers->finished_voice_count++] = rvoice;
+ }
+ else
+ {
+ FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony");
+ }
+}
+
+static void
+fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t *buffers)
+{
+ int i, j;
+
+ for(i = 0; i < buffers->finished_voice_count; i++)
+ {
+ fluid_rvoice_t *v = buffers->finished_voices[i];
+ int av = buffers->mixer->active_voices;
+
+ for(j = 0; j < av; j++)
+ {
+ if(v == buffers->mixer->rvoices[j])
+ {
+ av--;
+
+ /* Pack the array */
+ if(j < av)
+ {
+ buffers->mixer->rvoices[j] = buffers->mixer->rvoices[av];
+ }
+ }
+ }
+
+ buffers->mixer->active_voices = av;
+
+ fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v);
+ }
+
+ buffers->finished_voice_count = 0;
+}
+
+static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer)
+{
+#if ENABLE_MIXER_THREADS
+ int i;
+
+ for(i = 0; i < mixer->thread_count; i++)
+ {
+ fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]);
+ }
+
#endif
- fluid_mixer_buffer_process_finished_voices(&mixer->buffers);
+ fluid_mixer_buffer_process_finished_voices(&mixer->buffers);
}
-static FLUID_INLINE void
-fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers,
- fluid_rvoice_t* voice, fluid_real_t** bufs,
- unsigned int bufcount)
+
+static FLUID_INLINE fluid_real_t *
+get_dest_buf(fluid_rvoice_buffers_t *buffers, int index,
+ fluid_real_t **dest_bufs, int dest_bufcount)
{
- int s = fluid_mix_one(voice, bufs, bufcount, buffers->mixer->current_blockcount);
- if (s < buffers->mixer->current_blockcount * FLUID_BUFSIZE) {
- fluid_finish_rvoice(buffers, voice);
- }
+ int j = buffers->bufs[index].mapping;
+
+ if(j >= dest_bufcount || j < 0)
+ {
+ return NULL;
+ }
+
+ return dest_bufs[j];
}
-/*
-static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers,
- fluid_rvoice_t* voice)
+
+/**
+ * Mix data down to buffers
+ *
+ * @param buffers Destination buffer(s)
+ * @param dsp_buf Mono sample source
+ * @param start_block Block to start mixing at
+ * @param sample_count number of samples to mix following \c start_block
+ * @param dest_bufs Array of buffers to mixdown to
+ * @param dest_bufcount Length of dest_bufs
+ */
+static void
+fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
+ fluid_real_t *FLUID_RESTRICT dsp_buf,
+ int start_block, int sample_count,
+ fluid_real_t **dest_bufs, int dest_bufcount)
{
- int i, retval=0;
- int fvc = buffers->finished_voice_count;
- for (i=0; i < fvc; i++)
- if (buffers->finished_voices[i] == voice) {
- fvc--;
- if (i < fvc)
- buffers->finished_voices[i] = buffers->finished_voices[fvc];
- retval++;
+ int bufcount = buffers->count;
+ int i, dsp_i;
+
+ if(sample_count <= 0 || dest_bufcount <= 0)
+ {
+ return;
+ }
+
+ FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0);
+ FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0);
+
+ for(i = 0; i < bufcount; i++)
+ {
+ fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
+ fluid_real_t amp = buffers->bufs[i].amp;
+
+ if(buf == NULL || amp == 0.0f)
+ {
+ continue;
+ }
+
+ FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0);
+
+ #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)
+
+ for(dsp_i = (start_block * FLUID_BUFSIZE); dsp_i < sample_count; dsp_i++)
+ {
+ buf[dsp_i] += amp * dsp_buf[dsp_i];
+ }
}
- fvc = buffers->finished_voice_count;
- return retval;
}
-*/
-int
-fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice)
+/**
+ * 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.
+ */
+static FLUID_INLINE void
+fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers,
+ fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs,
+ unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount)
{
- int i;
+ int i, total_samples = 0, start_block = 0;
- if (mixer->active_voices < mixer->polyphony) {
- mixer->rvoices[mixer->active_voices++] = voice;
- return FLUID_OK;
- }
-
- /* See if any voices just finished, if so, take its place.
- This can happen in voice overflow conditions. */
- for (i=0; i < mixer->active_voices; i++) {
- if (mixer->rvoices[i] == voice) {
- FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!");
- return FLUID_FAILED;
+ for(i = 0; i < blockcount; i++)
+ {
+ int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]);
+
+ if(s == -1)
+ {
+ start_block += s;
+ s = FLUID_BUFSIZE;
+ }
+
+ total_samples += s;
+
+ if(s < FLUID_BUFSIZE)
+ {
+ break;
+ }
}
- if (mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) {
- fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);
- mixer->rvoices[i] = voice;
- return FLUID_OK;
+
+ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, -start_block, total_samples - ((-start_block)*FLUID_BUFSIZE), dest_bufs, dest_bufcount);
+
+ if(total_samples < blockcount * FLUID_BUFSIZE)
+ {
+ fluid_finish_rvoice(buffers, rvoice);
}
- }
+}
- /* This should never happen */
- FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
- return FLUID_FAILED;
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice)
+{
+ int i;
+ fluid_rvoice_mixer_t *mixer = obj;
+ fluid_rvoice_t *voice = param[0].ptr;
+
+ if(mixer->active_voices < mixer->polyphony)
+ {
+ mixer->rvoices[mixer->active_voices++] = voice;
+ return; // success
+ }
+
+ /* See if any voices just finished, if so, take its place.
+ This can happen in voice overflow conditions. */
+ for(i = 0; i < mixer->active_voices; i++)
+ {
+ if(mixer->rvoices[i] == voice)
+ {
+ FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!");
+ return;
+ }
+
+ if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED)
+ {
+ fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);
+ mixer->rvoices[i] = voice;
+ return; // success
+ }
+ }
+
+ /* This should never happen */
+ FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
+ return;
}
-static int
-fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t* buffers, int value)
+static int
+fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value)
{
- void* newptr;
+ void *newptr;
- if (buffers->finished_voice_count > value)
- return FLUID_FAILED;
-
- newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t*));
- if (newptr == NULL && value > 0)
- return FLUID_FAILED;
- buffers->finished_voices = newptr;
- return FLUID_OK;
+ if(buffers->finished_voice_count > value)
+ {
+ return FLUID_FAILED;
+ }
+
+ newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t *));
+
+ if(newptr == NULL && value > 0)
+ {
+ return FLUID_FAILED;
+ }
+
+ buffers->finished_voices = newptr;
+ return FLUID_OK;
}
/**
* Update polyphony - max number of voices (NOTE: not hard real-time capable)
* @return FLUID_OK or FLUID_FAILED
*/
-int
-fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony)
{
- void* newptr;
- if (handler->active_voices > value)
- return FLUID_FAILED;
+ void *newptr;
+ fluid_rvoice_mixer_t *handler = obj;
+ int value = param[0].i;
- newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*));
- if (newptr == NULL)
- return FLUID_FAILED;
- handler->rvoices = newptr;
+ if(handler->active_voices > value)
+ {
+ return /*FLUID_FAILED*/;
+ }
- if (fluid_mixer_buffers_update_polyphony(&handler->buffers, value)
- == FLUID_FAILED)
- return FLUID_FAILED;
+ newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *));
-#ifdef ENABLE_MIXER_THREADS
- {
- int i;
- for (i=0; i < handler->thread_count; i++)
- if (fluid_mixer_buffers_update_polyphony(&handler->threads[i], value)
- == FLUID_FAILED)
- return FLUID_FAILED;
- }
+ if(newptr == NULL)
+ {
+ return /*FLUID_FAILED*/;
+ }
+
+ handler->rvoices = newptr;
+
+ if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value)
+ == FLUID_FAILED)
+ {
+ return /*FLUID_FAILED*/;
+ }
+
+#if ENABLE_MIXER_THREADS
+ {
+ int i;
+
+ for(i = 0; i < handler->thread_count; i++)
+ {
+ if(fluid_mixer_buffers_update_polyphony(&handler->threads[i], value)
+ == FLUID_FAILED)
+ {
+ return /*FLUID_FAILED*/;
+ }
+ }
+ }
#endif
- handler->polyphony = value;
- return FLUID_OK;
+ handler->polyphony = value;
+ return /*FLUID_OK*/;
}
-static void
-fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer)
+static void
+fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount)
{
- int i;
- FLUID_DECLARE_VLA(fluid_real_t*, bufs,
- mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
- int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
- fluid_profile_ref_var(prof_ref);
- for (i=0; i < mixer->active_voices; i++) {
- fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs,
- bufcount);
- fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
- }
-}
+ int i;
+ FLUID_DECLARE_VLA(fluid_real_t *, bufs,
+ mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
+ int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
+
+ fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);
+ fluid_profile_ref_var(prof_ref);
+
+ for(i = 0; i < mixer->active_voices; i++)
+ {
+ fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs,
+ bufcount, local_buf, blockcount);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,
+ blockcount * FLUID_BUFSIZE);
+ }
+}
static FLUID_INLINE void
-fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers)
+fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount)
{
- int i;
- int size = buffers->mixer->current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t);
- /* TODO: Optimize by only zero out the buffers we actually use later on. */
- for (i=0; i < buffers->buf_count; i++) {
- FLUID_MEMSET(buffers->left_buf[i], 0, size);
- FLUID_MEMSET(buffers->right_buf[i], 0, size);
- }
- for (i=0; i < buffers->fx_buf_count; i++) {
- FLUID_MEMSET(buffers->fx_left_buf[i], 0, size);
- FLUID_MEMSET(buffers->fx_right_buf[i], 0, size);
- }
-}
+ int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t);
+ /* TODO: Optimize by only zero out the buffers we actually use later on. */
+ int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count;
+ fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);
+ fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);
-static int
-fluid_mixer_buffers_init(fluid_mixer_buffers_t* buffers, fluid_rvoice_mixer_t* mixer)
-{
- int i, samplecount;
-
- buffers->mixer = mixer;
- buffers->buf_count = buffers->mixer->buffers.buf_count;
- buffers->fx_buf_count = buffers->mixer->buffers.fx_buf_count;
- buffers->buf_blocks = buffers->mixer->buffers.buf_blocks;
- samplecount = FLUID_BUFSIZE * buffers->buf_blocks;
-
-
- /* Left and right audio buffers */
+ for(i = 0; i < buf_count; i++)
+ {
+ FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+ FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+ }
+
+ buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < fx_buf_count; i++)
+ {
+ FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+ FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+ }
+}
- buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
- buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
+static int
+fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer)
+{
+ const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT;
- if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return 0;
- }
+ buffers->mixer = mixer;
+ buffers->buf_count = mixer->buffers.buf_count;
+ buffers->fx_buf_count = mixer->buffers.fx_buf_count;
- FLUID_MEMSET(buffers->left_buf, 0, buffers->buf_count * sizeof(fluid_real_t*));
- FLUID_MEMSET(buffers->right_buf, 0, buffers->buf_count * sizeof(fluid_real_t*));
+ /* Local mono voice buf */
+ buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT);
- for (i = 0; i < buffers->buf_count; i++) {
+ /* Left and right audio buffers */
- buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
- buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+ buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
+ buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
- if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == NULL)) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return 0;
+ if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
}
- }
-
- /* Effects audio buffers */
- buffers->fx_left_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count);
- buffers->fx_right_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count);
+ /* Effects audio buffers */
- if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return 0;
- }
+ buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
+ buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
- FLUID_MEMSET(buffers->fx_left_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*));
- FLUID_MEMSET(buffers->fx_right_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*));
+ if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
- for (i = 0; i < buffers->fx_buf_count; i++) {
- buffers->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
- buffers->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+ buffers->finished_voices = NULL;
- if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return 0;
+ if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony)
+ == FLUID_FAILED)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
}
- }
-
- buffers->finished_voices = NULL;
- if (fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony)
- == FLUID_FAILED) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return 0;
- }
-
- return 1;
+
+ return 1;
}
/**
* Note: Not hard real-time capable (calls malloc)
*/
-void
-fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
{
- int i;
- if (mixer->fx.chorus)
- delete_fluid_chorus(mixer->fx.chorus);
- mixer->fx.chorus = new_fluid_chorus(samplerate);
- if (mixer->fx.reverb)
- fluid_revmodel_samplerate_change(mixer->fx.reverb, samplerate);
- for (i=0; i < mixer->active_voices; i++)
- fluid_rvoice_set_output_rate(mixer->rvoices[i], samplerate);
+ fluid_rvoice_mixer_t *mixer = obj;
+ fluid_real_t samplerate = param[1].real; // becausee fluid_synth_update_mixer() puts real into arg2
+
+ int i;
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ if(mixer->fx[i].chorus)
+ {
+ delete_fluid_chorus(mixer->fx[i].chorus);
+ }
+
+ mixer->fx[i].chorus = new_fluid_chorus(samplerate);
+
+ if(mixer->fx[i].reverb)
+ {
+ fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);
+ }
+ }
+
+#if LADSPA
+
+ if(mixer->ladspa_fx != NULL)
+ {
+ fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate);
+ }
+
+#endif
}
@@ -530,188 +686,299 @@ fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samp
* @param buf_count number of primary stereo buffers
* @param fx_buf_count number of stereo effect buffers
*/
-fluid_rvoice_mixer_t*
-new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate)
+fluid_rvoice_mixer_t *
+new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio)
{
- fluid_rvoice_mixer_t* mixer = FLUID_NEW(fluid_rvoice_mixer_t);
- if (mixer == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return NULL;
- }
- FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t));
- mixer->buffers.buf_count = buf_count;
- mixer->buffers.fx_buf_count = fx_buf_count;
- mixer->buffers.buf_blocks = FLUID_MIXER_MAX_BUFFERS_DEFAULT;
-
- /* allocate the reverb module */
- mixer->fx.reverb = new_fluid_revmodel(sample_rate);
- mixer->fx.chorus = new_fluid_chorus(sample_rate);
- if (mixer->fx.reverb == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- delete_fluid_rvoice_mixer(mixer);
- return NULL;
- }
-
- if (!fluid_mixer_buffers_init(&mixer->buffers, mixer)) {
- delete_fluid_rvoice_mixer(mixer);
- return NULL;
- }
-
-#ifdef ENABLE_MIXER_THREADS
- mixer->thread_ready = new_fluid_cond();
- mixer->wakeup_threads = new_fluid_cond();
- mixer->thread_ready_m = new_fluid_cond_mutex();
- mixer->wakeup_threads_m = new_fluid_cond_mutex();
- if (!mixer->thread_ready || !mixer->wakeup_threads ||
- !mixer->thread_ready_m || !mixer->wakeup_threads_m) {
+ int i;
+ fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t);
+
+ if(mixer == NULL)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t));
+ mixer->eventhandler = evthandler;
+ mixer->fx_units = fx_units;
+ mixer->buffers.buf_count = buf_count;
+ mixer->buffers.fx_buf_count = fx_buf_count * fx_units;
+
+ /* allocate the reverb module */
+ mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units);
+ if(mixer->fx == NULL)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+
+ FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx));
+
+ for(i = 0; i < fx_units; i++)
+ {
+ mixer->fx[i].reverb = new_fluid_revmodel(sample_rate);
+ mixer->fx[i].chorus = new_fluid_chorus(sample_rate);
+
+ if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+ }
+
+ if(!fluid_mixer_buffers_init(&mixer->buffers, mixer))
+ {
+ goto error_recovery;
+ }
+
+#if ENABLE_MIXER_THREADS
+ mixer->thread_ready = new_fluid_cond();
+ mixer->wakeup_threads = new_fluid_cond();
+ mixer->thread_ready_m = new_fluid_cond_mutex();
+ mixer->wakeup_threads_m = new_fluid_cond_mutex();
+
+ if(!mixer->thread_ready || !mixer->wakeup_threads ||
+ !mixer->thread_ready_m || !mixer->wakeup_threads_m)
+ {
+ goto error_recovery;
+ }
+
+ if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK)
+ {
+ goto error_recovery;
+ }
+
+#endif
+
+ return mixer;
+
+error_recovery:
delete_fluid_rvoice_mixer(mixer);
return NULL;
- }
-#endif
-
- return mixer;
}
static void
-fluid_mixer_buffers_free(fluid_mixer_buffers_t* buffers)
-{
- int i;
-
- FLUID_FREE(buffers->finished_voices);
-
- /* free all the sample buffers */
- if (buffers->left_buf != NULL) {
- for (i = 0; i < buffers->buf_count; i++) {
- if (buffers->left_buf[i] != NULL) {
- FLUID_FREE(buffers->left_buf[i]);
- }
- }
+fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers)
+{
+ FLUID_FREE(buffers->finished_voices);
+
+ /* free all the sample buffers */
+ FLUID_FREE(buffers->local_buf);
FLUID_FREE(buffers->left_buf);
- }
+ FLUID_FREE(buffers->right_buf);
+ FLUID_FREE(buffers->fx_left_buf);
+ FLUID_FREE(buffers->fx_right_buf);
+}
+
+void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer)
+{
+ int i;
+
+ fluid_return_if_fail(mixer != NULL);
- if (buffers->right_buf != NULL) {
- for (i = 0; i < buffers->buf_count; i++) {
- if (buffers->right_buf[i] != NULL) {
- FLUID_FREE(buffers->right_buf[i]);
- }
+#if ENABLE_MIXER_THREADS
+ delete_rvoice_mixer_threads(mixer);
+
+ if(mixer->thread_ready)
+ {
+ delete_fluid_cond(mixer->thread_ready);
}
- FLUID_FREE(buffers->right_buf);
- }
- if (buffers->fx_left_buf != NULL) {
- for (i = 0; i < buffers->fx_buf_count; i++) {
- if (buffers->fx_left_buf[i] != NULL) {
- FLUID_FREE(buffers->fx_left_buf[i]);
- }
+ if(mixer->wakeup_threads)
+ {
+ delete_fluid_cond(mixer->wakeup_threads);
}
- FLUID_FREE(buffers->fx_left_buf);
- }
- if (buffers->fx_right_buf != NULL) {
- for (i = 0; i < buffers->fx_buf_count; i++) {
- if (buffers->fx_right_buf[i] != NULL) {
- FLUID_FREE(buffers->fx_right_buf[i]);
- }
+ if(mixer->thread_ready_m)
+ {
+ delete_fluid_cond_mutex(mixer->thread_ready_m);
+ }
+
+ if(mixer->wakeup_threads_m)
+ {
+ delete_fluid_cond_mutex(mixer->wakeup_threads_m);
}
- FLUID_FREE(buffers->fx_right_buf);
- }
-}
-void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer)
-{
- if (!mixer)
- return;
- fluid_rvoice_mixer_set_threads(mixer, 0, 0);
-#ifdef ENABLE_MIXER_THREADS
- if (mixer->thread_ready)
- delete_fluid_cond(mixer->thread_ready);
- if (mixer->wakeup_threads)
- delete_fluid_cond(mixer->wakeup_threads);
- if (mixer->thread_ready_m)
- delete_fluid_cond_mutex(mixer->thread_ready_m);
- if (mixer->wakeup_threads_m)
- delete_fluid_cond_mutex(mixer->wakeup_threads_m);
#endif
- fluid_mixer_buffers_free(&mixer->buffers);
- if (mixer->fx.reverb)
- delete_fluid_revmodel(mixer->fx.reverb);
- if (mixer->fx.chorus)
- delete_fluid_chorus(mixer->fx.chorus);
- FLUID_FREE(mixer->rvoices);
- FLUID_FREE(mixer);
+ fluid_mixer_buffers_free(&mixer->buffers);
+
+
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ if(mixer->fx[i].reverb)
+ {
+ delete_fluid_revmodel(mixer->fx[i].reverb);
+ }
+
+ if(mixer->fx[i].chorus)
+ {
+ delete_fluid_chorus(mixer->fx[i].chorus);
+ }
+ }
+
+ FLUID_FREE(mixer->fx);
+ FLUID_FREE(mixer->rvoices);
+ FLUID_FREE(mixer);
}
-#ifdef LADSPA
-void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer,
- fluid_LADSPA_FxUnit_t* ladspa)
+#ifdef LADSPA
+/**
+ * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers
+ * as LADSPA host buffers with sensible names */
+void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer,
+ fluid_ladspa_fx_t *ladspa_fx, int audio_groups)
{
- mixer->LADSPA_FxUnit = ladspa;
+ mixer->ladspa_fx = ladspa_fx;
+
+ if(ladspa_fx == NULL)
+ {
+ return;
+ }
+ else
+ {
+ fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+ fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ fluid_real_t *chor = rev;
+
+ rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+ chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+
+ fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups,
+ main_l,
+ FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+ fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups,
+ main_r,
+ FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+ fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1,
+ rev,
+ FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+ fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1,
+ chor,
+ FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+ }
}
#endif
-void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled)
{
- mixer->fx.with_reverb = on;
+ fluid_rvoice_mixer_t *mixer = obj;
+ int on = param[0].i;
+
+ mixer->with_reverb = on;
}
-void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled)
{
- mixer->fx.with_chorus = on;
+ fluid_rvoice_mixer_t *mixer = obj;
+ int on = param[0].i;
+ mixer->with_chorus = on;
}
-void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on)
+void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on)
{
- mixer->fx.mix_fx_to_out = on;
+ mixer->mix_fx_to_out = 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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params)
{
- fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
+ fluid_rvoice_mixer_t *mixer = obj;
+ int set = param[0].i;
+ int nr = param[1].i;
+ fluid_real_t level = param[2].real;
+ fluid_real_t speed = param[3].real;
+ fluid_real_t depth_ms = param[4].real;
+ int type = param[5].i;
+
+ int i;
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ fluid_chorus_set(mixer->fx[i].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)
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params)
{
- fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level);
+ fluid_rvoice_mixer_t *mixer = obj;
+ int set = param[0].i;
+ fluid_real_t roomsize = param[1].real;
+ fluid_real_t damping = param[2].real;
+ fluid_real_t width = param[3].real;
+ fluid_real_t level = param[4].real;
+
+ int i;
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ fluid_revmodel_set(mixer->fx[i].reverb, set, roomsize, damping, width, level);
+ }
}
-void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb)
{
- fluid_revmodel_reset(mixer->fx.reverb);
- fluid_chorus_reset(mixer->fx.chorus);
+ fluid_rvoice_mixer_t *mixer = obj;
+ int i;
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ fluid_revmodel_reset(mixer->fx[i].reverb);
+ }
+}
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus)
+{
+ fluid_rvoice_mixer_t *mixer = obj;
+ int i;
+ for(i = 0; i < mixer->fx_units; i++)
+ {
+ fluid_chorus_reset(mixer->fx[i].chorus);
+ }
}
-void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer)
+int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer,
+ fluid_real_t **left, fluid_real_t **right)
{
- fluid_revmodel_reset(mixer->fx.reverb);
+ *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+ *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+ return mixer->buffers.buf_count;
}
-void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer)
+int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer,
+ fluid_real_t **fx_left, fluid_real_t **fx_right)
{
- fluid_chorus_reset(mixer->fx.chorus);
+ *fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+ return mixer->buffers.fx_buf_count;
}
-int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
- fluid_real_t*** left, fluid_real_t*** right)
+int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer)
{
- *left = mixer->buffers.left_buf;
- *right = mixer->buffers.right_buf;
- return mixer->buffers.buf_count;
+ return FLUID_MIXER_MAX_BUFFERS_DEFAULT;
}
+#if WITH_PROFILING
+int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer)
+{
+ return mixer->active_voices;
+}
+#endif
-#ifdef ENABLE_MIXER_THREADS
+#if ENABLE_MIXER_THREADS
-static FLUID_INLINE fluid_rvoice_t*
-fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer)
+static FLUID_INLINE fluid_rvoice_t *
+fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t *mixer)
{
- int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1);
- if (i >= mixer->active_voices)
- return NULL;
- return mixer->rvoices[i];
+ int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1);
+
+ if(i >= mixer->active_voices)
+ {
+ return NULL;
+ }
+
+ return mixer->rvoices[i];
}
#define THREAD_BUF_PROCESSING 0
@@ -720,255 +987,391 @@ fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer)
#define THREAD_BUF_TERMINATE 3
/* Core thread function (processes voices in parallel to primary synthesis thread) */
-static void
-fluid_mixer_thread_func (void* data)
-{
- fluid_mixer_buffers_t* buffers = data;
- fluid_rvoice_mixer_t* mixer = buffers->mixer;
- int hasValidData = 0;
- FLUID_DECLARE_VLA(fluid_real_t*, bufs, buffers->buf_count*2 + buffers->fx_buf_count*2);
- int bufcount = 0;
-
- while (!fluid_atomic_int_get(&mixer->threads_should_terminate)) {
- fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer);
- if (rvoice == NULL) {
- // if no voices: signal rendered buffers, sleep
- fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA);
- fluid_cond_mutex_lock(mixer->thread_ready_m);
- fluid_cond_signal(mixer->thread_ready);
- fluid_cond_mutex_unlock(mixer->thread_ready_m);
-
- fluid_cond_mutex_lock(mixer->wakeup_threads_m);
- while (1) {
- int j = fluid_atomic_int_get(&buffers->ready);
- if (j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE)
- break;
- fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m);
- }
- fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
-
- hasValidData = 0;
- }
- else {
- // else: if buffer is not zeroed, zero buffers
- if (!hasValidData) {
- fluid_mixer_buffers_zero(buffers);
- bufcount = fluid_mixer_buffers_prepare(buffers, bufs);
- hasValidData = 1;
- }
- // then render voice to buffers
- fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount);
- }
- }
+static fluid_thread_return_t
+fluid_mixer_thread_func(void *data)
+{
+ fluid_mixer_buffers_t *buffers = data;
+ fluid_rvoice_mixer_t *mixer = buffers->mixer;
+ int hasValidData = 0;
+ FLUID_DECLARE_VLA(fluid_real_t *, bufs, buffers->buf_count * 2 + buffers->fx_buf_count * 2);
+ int bufcount = 0;
+ int current_blockcount = 0;
+ fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ while(!fluid_atomic_int_get(&mixer->threads_should_terminate))
+ {
+ fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer);
+
+ if(rvoice == NULL)
+ {
+ // if no voices: signal rendered buffers, sleep
+ fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA);
+ fluid_cond_mutex_lock(mixer->thread_ready_m);
+ fluid_cond_signal(mixer->thread_ready);
+ fluid_cond_mutex_unlock(mixer->thread_ready_m);
+
+ fluid_cond_mutex_lock(mixer->wakeup_threads_m);
+
+ while(1)
+ {
+ int j = fluid_atomic_int_get(&buffers->ready);
+
+ if(j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE)
+ {
+ break;
+ }
+
+ fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m);
+ }
+
+ fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
+
+ hasValidData = 0;
+ }
+ else
+ {
+ // else: if buffer is not zeroed, zero buffers
+ if(!hasValidData)
+ {
+ // blockcount may have changed, since thread was put to sleep
+ current_blockcount = mixer->current_blockcount;
+ fluid_mixer_buffers_zero(buffers, current_blockcount);
+ bufcount = fluid_mixer_buffers_prepare(buffers, bufs);
+ hasValidData = 1;
+ }
+
+ // then render voice to buffers
+ fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount);
+ }
+ }
+ return FLUID_THREAD_RETURN_VALUE;
}
static void
-fluid_mixer_buffers_mix(fluid_mixer_buffers_t* dest, fluid_mixer_buffers_t* src)
+fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount)
{
- int i,j;
- int scount = dest->mixer->current_blockcount * FLUID_BUFSIZE;
- int minbuf;
-
- minbuf = dest->buf_count;
- if (minbuf > src->buf_count)
- minbuf = src->buf_count;
- for (i=0; i < minbuf; i++) {
- for (j=0; j < scount; j++) {
- dest->left_buf[i][j] += src->left_buf[i][j];
- dest->right_buf[i][j] += src->right_buf[i][j];
+ int i, j;
+ int scount = current_blockcount * FLUID_BUFSIZE;
+ int minbuf;
+ fluid_real_t *FLUID_RESTRICT base_src;
+ fluid_real_t *FLUID_RESTRICT base_dst;
+
+ minbuf = dst->buf_count;
+
+ if(minbuf > src->buf_count)
+ {
+ minbuf = src->buf_count;
+ }
+
+ base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT);
+ base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < minbuf; i++)
+ {
+ #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+ for(j = 0; j < scount; j++)
+ {
+ int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+ base_dst[dsp_i] += base_src[dsp_i];
+ }
+ }
+
+ base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT);
+ base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < minbuf; i++)
+ {
+ #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+ for(j = 0; j < scount; j++)
+ {
+ int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+ base_dst[dsp_i] += base_src[dsp_i];
+ }
}
- }
- minbuf = dest->fx_buf_count;
- if (minbuf > src->fx_buf_count)
- minbuf = src->fx_buf_count;
- for (i=0; i < minbuf; i++) {
- for (j=0; j < scount; j++) {
- dest->fx_left_buf[i][j] += src->fx_left_buf[i][j];
- dest->fx_right_buf[i][j] += src->fx_right_buf[i][j];
+ minbuf = dst->fx_buf_count;
+
+ if(minbuf > src->fx_buf_count)
+ {
+ minbuf = src->fx_buf_count;
+ }
+
+ base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+ base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < minbuf; i++)
+ {
+ #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+ for(j = 0; j < scount; j++)
+ {
+ int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+ base_dst[dsp_i] += base_src[dsp_i];
+ }
+ }
+
+ base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+ base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+ for(i = 0; i < minbuf; i++)
+ {
+ #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+ for(j = 0; j < scount; j++)
+ {
+ int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+ base_dst[dsp_i] += base_src[dsp_i];
+ }
}
- }
}
/**
- * Go through all threads and see if someone is finished for mixing
+ * Go through all threads and see if someone is finished for mixing
*/
-static FLUID_INLINE int
-fluid_mixer_mix_in(fluid_rvoice_mixer_t* mixer, int extra_threads)
-{
- int i, result, hasmixed;
- do {
- hasmixed = 0;
- result = 0;
- for (i=0; i < extra_threads; i++) {
- int j = fluid_atomic_int_get(&mixer->threads[i].ready);
- switch (j) {
- case THREAD_BUF_PROCESSING:
- result = 1;
- break;
- case THREAD_BUF_VALID:
- fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA);
- fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i]);
- hasmixed = 1;
- break;
- }
- }
- } while (hasmixed);
- return result;
-}
-
-static void
-fluid_render_loop_multithread(fluid_rvoice_mixer_t* mixer)
-{
- int i, bufcount;
- //int scount = mixer->current_blockcount * FLUID_BUFSIZE;
- FLUID_DECLARE_VLA(fluid_real_t*, bufs,
- mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
- // How many threads should we start this time?
- int extra_threads = mixer->active_voices / VOICES_PER_THREAD;
- if (extra_threads > mixer->thread_count)
- extra_threads = mixer->thread_count;
- if (extra_threads == 0) {
- // No extra threads? No thread overhead!
- fluid_render_loop_singlethread(mixer);
- return;
- }
-
- bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
-
- // Prepare voice list
- fluid_cond_mutex_lock(mixer->wakeup_threads_m);
- fluid_atomic_int_set(&mixer->current_rvoice, 0);
- for (i=0; i < extra_threads; i++)
- fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING);
- // Signal threads to wake up
- fluid_cond_broadcast(mixer->wakeup_threads);
- fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
-
- // If thread is finished, mix it in
- while (fluid_mixer_mix_in(mixer, extra_threads)) {
- // Otherwise get a voice and render it
- fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer);
- if (rvoice != NULL) {
- fluid_profile_ref_var(prof_ref);
- fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount);
- fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
- //test++;
- }
- else {
- // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock
- int is_processing = 0;
- //waits++;
- fluid_cond_mutex_lock(mixer->thread_ready_m);
- for (i=0; i < extra_threads; i++)
- if (fluid_atomic_int_get(&mixer->threads[i].ready) ==
- THREAD_BUF_PROCESSING)
- is_processing = 1;
- if (is_processing)
- fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m);
- fluid_cond_mutex_unlock(mixer->thread_ready_m);
- }
- }
- //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d",
- // mixer->current_blockcount, test, mixer->active_voices, waits);
+static int
+fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount)
+{
+ int i, result, hasmixed;
+
+ do
+ {
+ hasmixed = 0;
+ result = 0;
+
+ for(i = 0; i < extra_threads; i++)
+ {
+ int j = fluid_atomic_int_get(&mixer->threads[i].ready);
+
+ switch(j)
+ {
+ case THREAD_BUF_PROCESSING:
+ result = 1;
+ break;
+
+ case THREAD_BUF_VALID:
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA);
+ fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i], current_blockcount);
+ hasmixed = 1;
+ break;
+ }
+ }
+ }
+ while(hasmixed);
+
+ return result;
}
-#endif
+static void
+fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount)
+{
+ int i, bufcount;
+ fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);
-/**
- * Update amount of extra mixer threads.
- * @param thread_count Number of extra mixer threads for multi-core rendering
- * @param prio_level real-time prio level for the extra mixer threads
- */
-void
-fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count,
- int prio_level)
-{
-#ifdef ENABLE_MIXER_THREADS
- char name[16];
- int i;
-
- // Kill all existing threads first
- if (mixer->thread_count) {
+ FLUID_DECLARE_VLA(fluid_real_t *, bufs,
+ mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
+ // How many threads should we start this time?
+ int extra_threads = mixer->active_voices / VOICES_PER_THREAD;
+
+ if(extra_threads > mixer->thread_count)
+ {
+ extra_threads = mixer->thread_count;
+ }
+
+ if(extra_threads == 0)
+ {
+ // No extra threads? No thread overhead!
+ fluid_render_loop_singlethread(mixer, current_blockcount);
+ return;
+ }
+
+ bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
+
+ // Prepare voice list
+ fluid_cond_mutex_lock(mixer->wakeup_threads_m);
+ fluid_atomic_int_set(&mixer->current_rvoice, 0);
+
+ for(i = 0; i < extra_threads; i++)
+ {
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING);
+ }
+
+ // Signal threads to wake up
+ fluid_cond_broadcast(mixer->wakeup_threads);
+ fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
+
+ // If thread is finished, mix it in
+ while(fluid_mixer_mix_in(mixer, extra_threads, current_blockcount))
+ {
+ // Otherwise get a voice and render it
+ fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer);
+
+ if(rvoice != NULL)
+ {
+ fluid_profile_ref_var(prof_ref);
+ fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount, local_buf, current_blockcount);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,
+ current_blockcount * FLUID_BUFSIZE);
+ //test++;
+ }
+ else
+ {
+ // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock
+ int is_processing = 0;
+ //waits++;
+ fluid_cond_mutex_lock(mixer->thread_ready_m);
+
+ for(i = 0; i < extra_threads; i++)
+ {
+ if(fluid_atomic_int_get(&mixer->threads[i].ready) ==
+ THREAD_BUF_PROCESSING)
+ {
+ is_processing = 1;
+ }
+ }
+
+ if(is_processing)
+ {
+ fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m);
+ }
+
+ fluid_cond_mutex_unlock(mixer->thread_ready_m);
+ }
+ }
+
+ //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d",
+ // current_blockcount, test, mixer->active_voices, waits);
+}
+
+static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer)
+{
+ int i;
fluid_atomic_int_set(&mixer->threads_should_terminate, 1);
// Signal threads to wake up
fluid_cond_mutex_lock(mixer->wakeup_threads_m);
- for (i=0; i < mixer->thread_count; i++)
- fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE);
+
+ for(i = 0; i < mixer->thread_count; i++)
+ {
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE);
+ }
+
fluid_cond_broadcast(mixer->wakeup_threads);
fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
-
- for (i=0; i < mixer->thread_count; i++) {
- if (mixer->threads[i].thread) {
- fluid_thread_join(mixer->threads[i].thread);
- delete_fluid_thread(mixer->threads[i].thread);
- }
- fluid_mixer_buffers_free(&mixer->threads[i]);
+
+ for(i = 0; i < mixer->thread_count; i++)
+ {
+ if(mixer->threads[i].thread)
+ {
+ fluid_thread_join(mixer->threads[i].thread);
+ delete_fluid_thread(mixer->threads[i].thread);
+ }
+
+ fluid_mixer_buffers_free(&mixer->threads[i]);
}
+
FLUID_FREE(mixer->threads);
mixer->thread_count = 0;
mixer->threads = NULL;
- }
-
- if (thread_count == 0)
- return;
-
- // Now prepare the new threads
- fluid_atomic_int_set(&mixer->threads_should_terminate, 0);
- mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count);
- if (mixer->threads == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return;
- }
- FLUID_MEMSET(mixer->threads, 0, thread_count*sizeof(fluid_mixer_buffers_t));
- mixer->thread_count = thread_count;
- for (i=0; i < thread_count; i++) {
- fluid_mixer_buffers_t* b = &mixer->threads[i];
- if (!fluid_mixer_buffers_init(b, mixer))
- return;
- fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA);
- g_snprintf (name, sizeof (name), "mixer%d", i);
- b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0);
- if (!b->thread)
- return;
- }
+}
-#endif
+/**
+ * Update amount of extra mixer threads.
+ * @param thread_count Number of extra mixer threads for multi-core rendering
+ * @param prio_level real-time prio level for the extra mixer threads
+ */
+static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level)
+{
+ char name[16];
+ int i;
+
+ // Kill all existing threads first
+ if(mixer->thread_count)
+ {
+ delete_rvoice_mixer_threads(mixer);
+ }
+
+ if(thread_count == 0)
+ {
+ return FLUID_OK;
+ }
+
+ // Now prepare the new threads
+ fluid_atomic_int_set(&mixer->threads_should_terminate, 0);
+ mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count);
+
+ if(mixer->threads == NULL)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ FLUID_MEMSET(mixer->threads, 0, thread_count * sizeof(fluid_mixer_buffers_t));
+ mixer->thread_count = thread_count;
+
+ for(i = 0; i < thread_count; i++)
+ {
+ fluid_mixer_buffers_t *b = &mixer->threads[i];
+
+ if(!fluid_mixer_buffers_init(b, mixer))
+ {
+ return FLUID_FAILED;
+ }
+
+ fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA);
+ FLUID_SNPRINTF(name, sizeof(name), "mixer%d", i);
+ b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0);
+
+ if(!b->thread)
+ {
+ return FLUID_FAILED;
+ }
+ }
+
+ return FLUID_OK;
}
+#endif
/**
* Synthesize audio into buffers
- * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples
+ * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples
* @return number of blocks rendered
*/
-int
-fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount)
-{
- fluid_profile_ref_var(prof_ref);
-
- mixer->current_blockcount = blockcount > mixer->buffers.buf_blocks ?
- mixer->buffers.buf_blocks : blockcount;
-
- // Zero buffers
- fluid_mixer_buffers_zero(&mixer->buffers);
- fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
-
-#ifdef ENABLE_MIXER_THREADS
- if (mixer->thread_count > 0)
- fluid_render_loop_multithread(mixer);
- else
+int
+fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount)
+{
+ fluid_profile_ref_var(prof_ref);
+
+ mixer->current_blockcount = blockcount;
+
+ // Zero buffers
+ fluid_mixer_buffers_zero(&mixer->buffers, blockcount);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices,
+ blockcount * FLUID_BUFSIZE);
+
+#if ENABLE_MIXER_THREADS
+
+ if(mixer->thread_count > 0)
+ {
+ fluid_render_loop_multithread(mixer, blockcount);
+ }
+ else
#endif
- fluid_render_loop_singlethread(mixer);
- fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref);
-
+ {
+ fluid_render_loop_singlethread(mixer, blockcount);
+ }
+
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices,
+ blockcount * FLUID_BUFSIZE);
+
- // Process reverb & chorus
- fluid_rvoice_mixer_process_fx(mixer);
+ // Process reverb & chorus
+ fluid_rvoice_mixer_process_fx(mixer, blockcount);
- // Call the callback and pack active voice array
- fluid_rvoice_mixer_process_finished_voices(mixer);
+ // Call the callback and pack active voice array
+ fluid_rvoice_mixer_process_finished_voices(mixer);
- return mixer->current_blockcount;
+ return blockcount;
}