Refactor/fix mixer implementation, fix a few bugs

This commit is contained in:
David Henningsson 2010-06-30 16:39:20 +00:00
parent a26b5a6117
commit 41853f6f69
11 changed files with 786 additions and 64 deletions

View file

@ -140,6 +140,8 @@ set ( libfluidsynth_SOURCES
rvoice/fluid_rvoice_event.c
rvoice/fluid_rvoice_handler.h
rvoice/fluid_rvoice_handler.c
rvoice/fluid_rvoice_mixer.h
rvoice/fluid_rvoice_mixer.c
rvoice/fluid_phase.h
rvoice/fluid_rev.c
rvoice/fluid_rev.h

View file

@ -729,6 +729,10 @@ int main(int argc, char** argv)
if (fast_render) {
char *filename;
if (player == NULL) {
fprintf(stderr, "No midi file specified!\n");
goto cleanup;
}
fluid_settings_dupstr (settings, "audio.file.name", &filename);
printf ("Rendering audio to file '%s'..\n", filename);

View file

@ -25,13 +25,13 @@ 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 increment,
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].increment = increment;
env->data[section].min = min;
env->data[section].max = max;
}

View file

@ -30,7 +30,7 @@
struct _fluid_env_data_t {
unsigned int count;
fluid_real_t coeff;
fluid_real_t incr;
fluid_real_t increment;
fluid_real_t min;
fluid_real_t max;
};
@ -81,7 +81,7 @@ fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
}
/* calculate the envelope value and check for valid range */
x = env_data->coeff * env->val + env_data->incr;
x = env_data->coeff * env->val + env_data->increment;
if (x < env_data->min)
{
@ -107,7 +107,7 @@ 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 increment,
fluid_real_t min,
fluid_real_t max);

View file

@ -28,7 +28,7 @@ typedef struct _fluid_lfo_t fluid_lfo_t;
struct _fluid_lfo_t {
fluid_real_t val; /* the current value of the LFO */
unsigned int delay; /* the delay of the lfo in samples */
fluid_real_t incr; /* the lfo frequency is converted to a per-buffer increment */
fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */
};
static inline void
@ -38,9 +38,9 @@ fluid_lfo_reset(fluid_lfo_t* lfo)
}
static inline void
fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t incr)
fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment)
{
lfo->incr = incr;
lfo->increment = increment;
}
static inline void
@ -61,16 +61,16 @@ fluid_lfo_calc(fluid_lfo_t* lfo, unsigned int cur_delay)
if (cur_delay < lfo->delay)
return;
lfo->val += lfo->incr;
lfo->val += lfo->increment;
if (lfo->val > (fluid_real_t) 1.0)
{
lfo->incr = -lfo->incr;
lfo->increment = -lfo->increment;
lfo->val = (fluid_real_t) 2.0 - lfo->val;
}
else if (lfo->val < (fluid_real_t) -1.0)
{
lfo->incr = -lfo->incr;
lfo->increment = -lfo->increment;
lfo->val = (fluid_real_t) -2.0 - lfo->val;
}

View file

@ -20,7 +20,7 @@
#include "fluid_rvoice_event.h"
#include "fluid_rvoice.h"
#include "fluid_rvoice_handler.h"
#include "fluid_rvoice_mixer.h"
#include "fluid_iir_filter.h"
#include "fluid_lfo.h"
#include "fluid_adsr_env.h"
@ -57,10 +57,16 @@
event->realparams[4]); \
return; }
#define EVENTFUNC_R4(proc, type) \
if (event->method == proc) { \
proc((type) event->object, event->intparam, event->realparams[0], \
event->realparams[1], event->realparams[2], event->realparams[3]); \
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_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*);
EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*);
@ -96,7 +102,15 @@ fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*);
EVENTFUNC_PTR(fluid_rvoice_set_sample, fluid_rvoice_t*, fluid_sample_t*);
EVENTFUNC_R1(fluid_rvoice_handler_set_polyphony, fluid_rvoice_handler_t*);
EVENTFUNC_R1(fluid_rvoice_mixer_set_samplerate, fluid_rvoice_mixer_t*);
EVENTFUNC_I1(fluid_rvoice_mixer_set_polyphony, fluid_rvoice_mixer_t*);
EVENTFUNC_I1(fluid_rvoice_mixer_set_reverb_enabled, fluid_rvoice_mixer_t*);
EVENTFUNC_I1(fluid_rvoice_mixer_set_chorus_enabled, fluid_rvoice_mixer_t*);
EVENTFUNC_I1(fluid_rvoice_mixer_set_mix_fx, fluid_rvoice_mixer_t*);
EVENTFUNC_0(fluid_rvoice_mixer_reset_fx, fluid_rvoice_mixer_t*);
EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*);
EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*);
FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method);
}
@ -196,14 +210,14 @@ finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
}
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
int is_threadsafe, int queuesize, int finished_voices_size)
int is_threadsafe, int queuesize, int finished_voices_size, int bufs, int fx_bufs)
{
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->mixer = NULL;
eventhandler->queue = NULL;
eventhandler->finished_voices = NULL;
eventhandler->is_threadsafe = is_threadsafe;
@ -218,10 +232,10 @@ fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
if (eventhandler->queue == NULL)
goto error_recovery;
eventhandler->handler = new_fluid_rvoice_handler();
if (eventhandler->handler == NULL)
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs);
if (eventhandler->mixer == NULL)
goto error_recovery;
fluid_rvoice_handler_set_voice_callback(eventhandler->handler,
fluid_rvoice_mixer_set_finished_voices_callback(eventhandler->mixer,
finished_voice_callback, eventhandler);
return eventhandler;
@ -249,6 +263,7 @@ void
delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler)
{
if (handler == NULL) return;
delete_fluid_rvoice_mixer(handler->mixer);
delete_fluid_ringbuffer(handler->queue);
delete_fluid_ringbuffer(handler->finished_voices);
FLUID_FREE(handler);

View file

@ -23,7 +23,7 @@
#define _FLUID_RVOICE_EVENT_H
#include "fluidsynth_priv.h"
#include "fluid_rvoice_handler.h"
#include "fluid_rvoice_mixer.h"
#include "fluid_ringbuffer.h"
#define EVENT_REAL_PARAMS (5)
@ -52,11 +52,11 @@ struct _fluid_rvoice_eventhandler_t {
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_mixer_t* mixer;
};
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
int is_threadsafe, int queuesize, int finished_voices_size);
int is_threadsafe, int queuesize, int finished_voices_size, int bufs, int fx_bufs);
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
@ -102,10 +102,10 @@ 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);
fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice,
handler->mixer, rvoice);
else
fluid_rvoice_handler_add_voice(handler->handler, rvoice);
fluid_rvoice_mixer_add_voice(handler->mixer, rvoice);
}

View file

@ -0,0 +1,587 @@
/* 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_mixer.h"
#include "fluid_rvoice.h"
#include "fluid_sys.h"
#include "fluid_rev.h"
#include "fluid_chorus.h"
#define SYNTH_REVERB_CHANNEL 0
#define SYNTH_CHORUS_CHANNEL 1
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 */
#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;
};
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_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_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 */
#ifdef 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) */
#endif
};
static FLUID_INLINE void
fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer)
{
int i;
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]);
}
}
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]);
}
}
}
/**
* 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)
{
mixer->remove_voice_callback_userdata = userdata;
mixer->remove_voice_callback = func;
}
/**
* 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;
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;
}
}
fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount);
return result;
}
/**
* Glue to get fluid_rvoice_buffers_mix what it wants
* Note: Make sure outbufs has 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 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 < thread_count; i++)
fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]);
#endif
fluid_mixer_buffer_process_finished_voices(&mixer->buffers);
}
int
fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice)
{
if (mixer->active_voices >= mixer->polyphony) {
FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
return FLUID_FAILED;
}
mixer->rvoices[mixer->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_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value)
{
void* newptr;
if (handler->active_voices > value)
return FLUID_FAILED;
if (handler->buffers.finished_voice_count > value)
return FLUID_FAILED;
newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*));
if (newptr == NULL)
return FLUID_FAILED;
handler->rvoices = newptr;
newptr = FLUID_REALLOC(handler->buffers.finished_voices, value * sizeof(fluid_rvoice_t*));
if (newptr == NULL)
return FLUID_FAILED;
handler->buffers.finished_voices = newptr;
handler->polyphony = value;
return FLUID_OK;
}
static FLUID_INLINE void fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer)
{
int i;
int scount = mixer->current_blockcount * FLUID_BUFSIZE;
fluid_real_t* bufs[mixer->buffers.buf_count + mixer->buffers.fx_buf_count];
int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
for (i=0; i < mixer->active_voices; i++) {
int s = fluid_mix_one(mixer->rvoices[i], bufs, bufcount, mixer->current_blockcount);
if (s < scount) {
fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);
}
}
}
static FLUID_INLINE void
fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers)
{
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);
}
}
/**
* Synthesize audio into buffers
* @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)
{
mixer->current_blockcount = blockcount > mixer->buffers.buf_blocks ?
mixer->buffers.buf_blocks : blockcount;
// Zero buffers
fluid_mixer_buffers_zero(&mixer->buffers);
// If no additional threads, just render all voices in a simple loop
#ifdef ENABLE_MIXER_THREADS
if (thread_count > 0)
fluid_render_loop_multithread(mixer);
else
#endif
fluid_render_loop_singlethread(mixer);
// If additional threads: Prepare voice list
// Signal threads to wake up
// while threads are running:
// If thread is finished, mix it in
// Otherwise get a voice and render it
// If no voices, wait for mixes
// Process reverb & chorus
fluid_rvoice_mixer_process_fx(mixer);
// Call the callback and pack active voice array
fluid_rvoice_mixer_process_finished_voices(mixer);
return mixer->current_blockcount;
}
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_count;
/* Left and right audio buffers */
buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return 0;
}
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*));
for (i = 0; i < buffers->buf_count; i++) {
buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == 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);
if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return 0;
}
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*));
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);
if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return 0;
}
}
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)
{
int i;
if (mixer->fx.chorus)
delete_fluid_chorus(mixer->fx.chorus);
mixer->fx.chorus = new_fluid_chorus(samplerate);
for (i=0; i < mixer->active_voices; i++)
fluid_rvoice_set_output_rate(mixer->rvoices[i], samplerate);
}
/**
* @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_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();
mixer->fx.chorus = new_fluid_chorus(44100); /* FIXME: Hardcoded 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;
}
return mixer;
}
static void
fluid_mixer_buffers_free(fluid_mixer_buffers_t* buffers)
{
int i;
/* 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_FREE(buffers->left_buf);
}
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]);
}
}
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]);
}
}
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]);
}
}
FLUID_FREE(buffers->fx_right_buf);
}
}
void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer)
{
if (!mixer)
return;
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);
}
void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on)
{
mixer->fx.with_reverb = on;
}
void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on)
{
mixer->fx.with_chorus = on;
}
void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on)
{
mixer->fx.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)
{
fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
}
void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
double roomsize, double damping,
double width, double level)
{
fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level);
}
void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer)
{
fluid_revmodel_reset(mixer->fx.reverb);
fluid_chorus_reset(mixer->fx.chorus);
}
int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** left, fluid_real_t*** right)
{
*left = mixer->buffers.left_buf;
*right = mixer->buffers.right_buf;
return mixer->buffers.buf_count;
}
#ifdef ENABLE_MIXER_THREADS
/* 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;
// get a voice
// if no voices: signal rendered buffers, sleep
// else: if buffer is not zeroed, zero buffers
// then render voice to buffers
}
#endif

View file

@ -0,0 +1,69 @@
/* 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_MIXER_H
#define _FLUID_RVOICE_MIXER_H
#include "fluidsynth_priv.h"
#include "fluid_rvoice.h"
#include <stdlib.h>
typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t;
#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE)
void fluid_rvoice_mixer_set_finished_voices_callback(
fluid_rvoice_mixer_t* mixer,
void (*func)(void*, fluid_rvoice_t*),
void* userdata);
int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount);
int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** left, fluid_real_t*** right);
fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count);
void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t*);
void fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate);
void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on);
void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on);
void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on);
int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value);
int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice);
void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set,
int nr, double level, double speed,
double depth_ms, int type);
void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
double roomsize, double damping,
double width, double level);
void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer);
/**
* Update amount of 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(int thread_count, int prio_level);
#endif

View file

@ -492,7 +492,7 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_synth_t* synth;
fluid_sfloader_t* loader;
double gain;
int i;
int i, nbuf;
/* initialize all the conversion tables and other stuff */
if (fluid_synth_initialized == 0) {
@ -593,9 +593,9 @@ new_fluid_synth(fluid_settings_t *settings)
/* The number of buffers is determined by the higher number of nr
* groups / nr audio channels. If LADSPA is unused, they should be
* the same. */
synth->nbuf = synth->audio_channels;
if (synth->audio_groups > synth->nbuf) {
synth->nbuf = synth->audio_groups;
nbuf = synth->audio_channels;
if (synth->audio_groups > nbuf) {
nbuf = synth->audio_groups;
}
#ifdef LADSPA
@ -648,11 +648,18 @@ new_fluid_synth(fluid_settings_t *settings)
}
/* Allocate handler */
synth->eventhandler = new_fluid_rvoice_eventhandler(1, synth->polyphony*8, synth->polyphony);
synth->eventhandler = new_fluid_rvoice_eventhandler(0, synth->polyphony*8,
synth->polyphony,
nbuf, synth->effects_channels);
if (synth->eventhandler == NULL)
goto error_recovery;
fluid_rvoice_handler_set_polyphony(synth->eventhandler->handler, synth->polyphony);
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_mixer_set_polyphony,
synth->eventhandler->mixer, synth->polyphony, 0.0f);
fluid_synth_set_reverb_on(synth, synth->with_reverb);
fluid_synth_set_chorus_on(synth, synth->with_chorus);
#if 0
/* Allocate the sample buffers */
synth->left_buf = NULL;
synth->right_buf = NULL;
@ -705,34 +712,39 @@ new_fluid_synth(fluid_settings_t *settings)
goto error_recovery;
}
}
#endif
synth->cur = FLUID_BUFSIZE;
synth->dither_index = 0;
#if 0
/* allocate the reverb module */
synth->reverb = new_fluid_revmodel();
if (synth->reverb == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
#endif
synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE;
synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP;
synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH;
synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL;
fluid_revmodel_set (synth->reverb, FLUID_REVMODEL_SET_ALL,
synth->reverb_roomsize, synth->reverb_damping,
synth->reverb_width, synth->reverb_level);
fluid_rvoice_eventhandler_push5(synth->eventhandler,
fluid_rvoice_mixer_set_reverb_params,
synth->eventhandler->mixer,
FLUID_REVMODEL_SET_ALL, synth->reverb_roomsize,
synth->reverb_damping, synth->reverb_width,
synth->reverb_level, 0.0f);
#if 0
/* allocate the chorus module */
synth->chorus = new_fluid_chorus(synth->sample_rate);
if (synth->chorus == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
#endif
/* Initialize multi-core variables if multiple cores enabled */
if (synth->cores > 1)
{
@ -952,6 +964,7 @@ delete_fluid_synth(fluid_synth_t* synth)
FLUID_FREE(synth->voice);
}
#if 0
/* free all the sample buffers */
if (synth->left_buf != NULL) {
for (i = 0; i < synth->nbuf; i++) {
@ -998,6 +1011,7 @@ delete_fluid_synth(fluid_synth_t* synth)
if (synth->chorus != NULL) {
delete_fluid_chorus(synth->chorus);
}
#endif
/* free the tunings, if any */
if (synth->tuning != NULL) {
@ -1273,6 +1287,8 @@ fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel)
channel = synth->channel[chan];
// if (chan != 11) return FLUID_OK; /* DEBUG */
/* make sure this channel has a preset */
if (channel->preset == NULL) {
if (synth->verbose) {
@ -1903,8 +1919,11 @@ fluid_synth_system_reset_LOCAL(fluid_synth_t* synth)
for (i = 0; i < synth->midi_channels; i++)
fluid_channel_reset(synth->channel[i]);
fluid_chorus_reset(synth->chorus);
fluid_revmodel_reset(synth->reverb);
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_mixer_reset_fx,
synth->eventhandler->mixer, 0, 0.0f);
// fluid_chorus_reset(synth->chorus);
// fluid_revmodel_reset(synth->reverb);
return FLUID_OK;
}
@ -2592,7 +2611,7 @@ fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth)
}
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_handler_set_polyphony, synth->eventhandler->handler,
fluid_rvoice_mixer_set_polyphony, synth->eventhandler->mixer,
synth->polyphony, 0.0f);
return FLUID_OK;
@ -2678,8 +2697,8 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
float** left, float** right,
float** fx_left, float** fx_right)
{
fluid_real_t** left_in = synth->left_buf;
fluid_real_t** right_in = synth->right_buf;
fluid_real_t** left_in;
fluid_real_t** right_in;
double time = fluid_utime();
int i, num, available, count, bytes;
float cpu_load;
@ -2689,6 +2708,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
num = synth->cur;
if (synth->cur < FLUID_BUFSIZE) {
available = FLUID_BUFSIZE - synth->cur;
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
num = (available > len)? len : available;
bytes = num * sizeof(float);
@ -2704,6 +2724,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
/* Then, run one_block() and copy till we have 'len' samples */
while (count < len) {
fluid_synth_one_block(synth, 1);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
bytes = num * sizeof(float);
@ -2789,8 +2810,8 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
int i, j, k, l;
float* left_out = (float*) lout;
float* right_out = (float*) rout;
fluid_real_t* left_in = synth->left_buf[0];
fluid_real_t* right_in = synth->right_buf[0];
fluid_real_t** left_in;
fluid_real_t** right_in;
double time = fluid_utime();
float cpu_load;
@ -2800,11 +2821,13 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
/* fill up the buffers as needed */
if (l == FLUID_BUFSIZE) {
fluid_synth_one_block(synth, 0);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
l = 0;
}
left_out[j] = (float) left_in[l];
right_out[k] = (float) right_in[l];
left_out[j] = (float) left_in[0][l];
right_out[k] = (float) right_in[0][l];
}
synth->cur = l;
@ -2876,8 +2899,8 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
int i, j, k, cur;
signed short* left_out = (signed short*) lout;
signed short* right_out = (signed short*) rout;
fluid_real_t* left_in = synth->left_buf[0];
fluid_real_t* right_in = synth->right_buf[0];
fluid_real_t** left_in;
fluid_real_t** right_in;
fluid_real_t left_sample;
fluid_real_t right_sample;
double time = fluid_utime();
@ -2895,13 +2918,14 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
prof_ref_on_block = fluid_profile_ref();
fluid_synth_one_block(synth, 0);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
cur = 0;
fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
}
left_sample = roundi (left_in[cur] * 32766.0f + rand_table[0][di]);
right_sample = roundi (right_in[cur] * 32766.0f + rand_table[1][di]);
left_sample = roundi (left_in[0][cur] * 32766.0f + rand_table[0][di]);
right_sample = roundi (right_in[0][cur] * 32766.0f + rand_table[1][di]);
di++;
if (di >= DITHER_SIZE) di = 0;
@ -3008,9 +3032,9 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
// fluid_voice_t* voice;
// fluid_real_t* left_buf;
// fluid_real_t* right_buf;
fluid_real_t* reverb_buf;
fluid_real_t* chorus_buf;
fluid_real_t* bufs[synth->audio_groups*2 + synth->effects_channels*2];
// fluid_real_t* reverb_buf;
// fluid_real_t* chorus_buf;
// 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);
@ -3032,7 +3056,12 @@ 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) */
}
fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer,
!do_not_mix_fx_to_out);
fluid_rvoice_mixer_render(synth->eventhandler->mixer, 1);
#if 0
/* 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
@ -3111,6 +3140,7 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
}
fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref);
#endif
#ifdef LADSPA
/* Run the signal through the LADSPA Fx unit */
@ -4097,6 +4127,10 @@ fluid_synth_set_reverb_on(fluid_synth_t* synth, int on)
fluid_return_if_fail (synth != NULL);
fluid_atomic_int_set (&synth->with_reverb, on != 0);
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_mixer_set_reverb_enabled,
synth->eventhandler->mixer,
on != 0, 0.0f);
}
/**
@ -4180,7 +4214,10 @@ fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize,
if (set & FLUID_REVMODEL_SET_LEVEL)
fluid_atomic_float_set (&synth->reverb_level, level);
fluid_revmodel_set (synth->reverb, set, roomsize, damping, width, level);
fluid_rvoice_eventhandler_push5(synth->eventhandler,
fluid_rvoice_mixer_set_reverb_params,
synth->eventhandler->mixer, set,
roomsize, damping, width, level, 0.0f);
fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock reverb */
@ -4250,6 +4287,11 @@ fluid_synth_set_chorus_on(fluid_synth_t* synth, int on)
fluid_return_if_fail (synth != NULL);
fluid_atomic_int_set (&synth->with_chorus, on != 0);
fluid_rvoice_eventhandler_push(synth->eventhandler,
fluid_rvoice_mixer_set_chorus_enabled,
synth->eventhandler->mixer,
on != 0, 0.0f);
}
/**
@ -4317,7 +4359,10 @@ fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level,
if (set & FLUID_CHORUS_SET_TYPE)
fluid_atomic_int_set (&synth->chorus_type, type);
fluid_chorus_set (synth->chorus, set, nr, level, speed, depth_ms, type);
fluid_rvoice_eventhandler_push5(synth->eventhandler,
fluid_rvoice_mixer_set_chorus_params,
synth->eventhandler->mixer, set,
nr, level, speed, depth_ms, type);
fluid_rec_mutex_unlock (synth->mutex); /* -- Unlock chorus */

View file

@ -196,9 +196,9 @@ struct _fluid_synth_t
int active_voice_count; /**< count of active voices */
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)*/
// 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;
fluid_real_t** fx_left_buf;
@ -206,7 +206,7 @@ struct _fluid_synth_t
fluid_revmodel_t* reverb;
fluid_chorus_t* chorus;
*/
float reverb_roomsize; /**< Shadow of reverb roomsize */
float reverb_damping; /**< Shadow of reverb damping */
float reverb_width; /**< Shadow of reverb width */