Merge pull request #405 from FluidSynth/fluid-synth-process

Complete implementation of fluid_synth_process() and add support for multi-channel effects rendering.
This commit is contained in:
Tom M 2018-07-11 17:26:51 +02:00 committed by GitHub
commit c4f0b19c64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 446 additions and 178 deletions

View File

@ -24,7 +24,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
<min>1</min>
<max>128</max>
<desc>
By default, the synthesizer outputs a single stereo signal. Using this option, the synthesizer can output multichannel audio. Sets the number of stereo channel pairs. So 1 is actually 2 channels (a stereo pair).</desc>
By default, the synthesizer outputs a single stereo signal. Using this option, the synthesizer can output multi-channel audio. Sets the number of stereo channel pairs. So 1 is actually 2 channels (a stereo pair).</desc>
</setting>
<setting>
<name>audio-groups</name>
@ -33,7 +33,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
<min>1</min>
<max>128</max>
<desc>
Normally the same value as synth.audio-channels. LADSPA effects subsystem can use this value though, in which case it may differ.</desc>
The output audio channel associated with a MIDI channel is wrapped around using the number of synth.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.</desc>
</setting>
<setting>
<name>chorus.active</name>
@ -85,7 +85,7 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
<min>1</min>
<max>256</max>
<desc>
(Experimental) Sets the number of synthesis CPU cores. If set to a value greater than 1, then additional synthesis threads will be created to take advantage of a multi CPU or CPU core system. This has the affect of utilizing more of the total CPU for voices or decreasing render times when synthesizing audio to a file.</desc>
Sets the number of synthesis CPU cores. If set to a value greater than 1, then additional synthesis threads will be created to take advantage of a multi CPU or CPU core system. This has the affect of utilizing more of the total CPU for voices or decreasing render times when synthesizing audio to a file.</desc>
</setting>
<setting>
<name>default-soundfont</name>
@ -119,7 +119,15 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
<def>2</def>
<min>2</min>
<max>2</max>
<desc>Currently unused.</desc>
<desc>Specifies the number of effects per group. Currently there only are two effects (i.e. reverb and chorus).</desc>
</setting>
<setting>
<name>effects-groups</name>
<type>int</type>
<def>1</def>
<min>1</min>
<max>128</max>
<desc>Specifies the number of effect units. By default, the sound of all voices is rendered by one reverb unit and one chorus unit respectively (even for multi-channel rendering). This setting gives the user control which effects of a voice to render to which independent audio channels. E.g. setting synth.effects-groups == synth.midi-channels allows to render the effects of each MIDI channel to separate audio buffers. If synth.effects-groups is smaller, it will wrap around. Note that any value >1 will significantly increase CPU usage.</desc>
</setting>
<setting>
<name>gain</name>

View File

@ -69,6 +69,7 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions
- explicit client unregistering is required for fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth()
- all public functions consistently receive signed integers for soundfont ids, bank and program numbers
- use unique device names for the "audio.portaudio.device" setting
- fluid_synth_process() received a new more flexible implementation, but now requires zeroed-out sample buffers
<strong>Other changes in FluidSynth 2.0.0 concerning developers:</strong>
@ -235,9 +236,7 @@ There are a number of general audio driver settings. The audio.driver settings d
It is possible to use the synthesizer object without creating an audio driver. This is desirable if the application using FluidSynth manages the audio output itself. The synthesizer has several API functions that can be used to obtain the audio output:
fluid_synth_write_s16() fills two buffers (left and right channel) with samples coded as signed 16 bits (the endian-ness is machine dependent). fluid_synth_write_float() fills a left and right audio buffer with 32 bits floating point samples. For multi channel audio output, the function fluid_synth_nwrite_float() has to be used.
The function fluid_synth_process() is still experimental and its use is therefore not recommended but it will probably become the generic interface in future versions.
fluid_synth_write_s16() fills two buffers (left and right channel) with samples coded as signed 16 bits (the endian-ness is machine dependent). fluid_synth_write_float() fills a left and right audio buffer with 32 bits floating point samples. The function fluid_synth_process() is the generic interface for synthesizing audio, which is also capable of multi channel audio output.
\section LoadingSoundfonts Loading and managing SoundFonts

View File

@ -27,19 +27,19 @@ struct fx_data_t
/* This function implements the callback function of the audio driver
* (see new_fluid_audio_driver2 below). The data argument is a pointer
* to your private data structure. 'len' is the number of samples in
* the buffers. 'nin' and 'nout' are the number of input and output
* audio buffers. 'in' and 'out' are an array of float buffers with
* the samples. The audio driver fills the 'in' buffers the incoming
* audio. The 'out' buffers should be filled by your function. The
* 'out' buffers will be sent to the audio output of the sound card.
* to your private data structure. 'len' is the number of audio frames
* in the buffers. 'nfx' and 'nout' are the number of input and output
* audio buffers. 'fx' and 'out' are arrays of float buffers containing
* the audio. The audio driver fills zero-initializes those buffers.
* You are responsible for filling up those buffers, as the result will
* be sent to the sound card. This is usually done by asking the synth
* to fill those buffers appropriately using fluid_synth_process()
*
* IMPORTANT NOTE: The API was designed to be generic but none of the
* audio drivers currently handles audio input. Either 'nin' will be
* zero, or the buffers will be filled with zero samples.
* NOTE: The API was designed to be generic. Audio driver may fill the
* buffers with audio input from the soundcard, rather than zeros.
*/
int fx_function(void *data, int len,
int nin, float **in,
int nfx, float **fx,
int nout, float **out)
{
struct fx_data_t *fx_data = (struct fx_data_t *) data;
@ -48,10 +48,10 @@ int fx_function(void *data, int len,
/* Call the synthesizer to fill the output buffers with its
* audio output. */
if(fluid_synth_process(fx_data->synth, len, nin, in, nout, out) != 0)
if(fluid_synth_process(fx_data->synth, len, nfx, fx, nout, out) != FLUID_OK)
{
/* Some error occured. Very unlikely to happen, though. */
return -1;
return FLUID_FAILED;
}
/* Apply your effects here. In this example, the gain is
@ -66,7 +66,7 @@ int fx_function(void *data, int len,
}
}
return 0;
return FLUID_OK;
}

View File

@ -264,7 +264,7 @@ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len,
FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t *synth, int len,
void *lout, int loff, int lincr,
void *rout, int roff, int rincr);
FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
float **left, float **right,
float **fx_left, float **fx_right);
FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len,

View File

@ -457,6 +457,9 @@ static fluid_thread_return_t fluid_alsa_audio_run_float(void *d)
{
while(dev->cont)
{
FLUID_MEMSET(left, 0, buffer_size * sizeof(float));
FLUID_MEMSET(right, 0, buffer_size * sizeof(float));
handle[0] = left;
handle[1] = right;

View File

@ -151,8 +151,8 @@ fluid_audio_driver_t *
new_fluid_core_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
return new_fluid_core_audio_driver2(settings,
(fluid_audio_func_t) fluid_synth_process,
(void *) synth);
fluid_synth_process,
synth);
}
/*
@ -398,6 +398,9 @@ fluid_core_audio_callback(void *data,
float *left = dev->buffers[0];
float *right = dev->buffers[1];
FLUID_MEMSET(left, 0, len * sizeof(float));
FLUID_MEMSET(right, 0, len * sizeof(float));
(*dev->callback)(dev->data, len, 0, NULL, 2, dev->buffers);
for(i = 0, k = 0; i < len; i++)

View File

@ -398,7 +398,9 @@ fluid_jack_client_register_ports(void *driver, int isaudio, jack_client_t *clien
}
fluid_settings_getint(settings, "synth.effects-channels", &dev->num_fx_ports);
fluid_settings_getint(settings, "synth.effects-groups", &i);
dev->num_fx_ports *= i;
dev->fx_ports = FLUID_ARRAY(jack_port_t *, 2 * dev->num_fx_ports);
if(dev->fx_ports == NULL)
@ -616,7 +618,7 @@ fluid_jack_driver_process(jack_nframes_t nframes, void *arg)
fluid_jack_audio_driver_t *audio_driver;
fluid_jack_midi_driver_t *midi_driver;
float *left, *right;
int i, k;
int i;
jack_midi_event_t midi_event;
fluid_midi_event_t *evt;
@ -657,52 +659,48 @@ fluid_jack_driver_process(jack_nframes_t nframes, void *arg)
audio_driver = fluid_atomic_pointer_get(&client->audio_driver);
if(!audio_driver)
{
return 0;
}
if(audio_driver->callback != NULL)
{
for(i = 0; i < audio_driver->num_output_ports * 2; i++)
{
audio_driver->output_bufs[i] = (float *)jack_port_get_buffer(audio_driver->output_ports[i], nframes);
}
return (*audio_driver->callback)(audio_driver->data, nframes, 0, NULL,
2 * audio_driver->num_output_ports,
audio_driver->output_bufs);
}
else if(audio_driver->num_output_ports == 1 && audio_driver->num_fx_ports == 0) /* i.e. audio.jack.multi=no */
if(audio_driver->callback == NULL && audio_driver->num_output_ports == 1 && audio_driver->num_fx_ports == 0) /* i.e. audio.jack.multi=no */
{
left = (float *) jack_port_get_buffer(audio_driver->output_ports[0], nframes);
right = (float *) jack_port_get_buffer(audio_driver->output_ports[1], nframes);
fluid_synth_write_float(audio_driver->data, nframes, left, 0, 1, right, 0, 1);
return fluid_synth_write_float(audio_driver->data, nframes, left, 0, 1, right, 0, 1);
}
else
{
for(i = 0, k = audio_driver->num_output_ports; i < audio_driver->num_output_ports; i++, k++)
fluid_audio_func_t callback = (audio_driver->callback != NULL) ? audio_driver->callback : fluid_synth_process;
for(i = 0; i < audio_driver->num_output_ports; i++)
{
audio_driver->output_bufs[i] = (float *)jack_port_get_buffer(audio_driver->output_ports[2 * i], nframes);
audio_driver->output_bufs[k] = (float *)jack_port_get_buffer(audio_driver->output_ports[2 * i + 1], nframes);
int k = i * 2;
audio_driver->output_bufs[k] = (float *)jack_port_get_buffer(audio_driver->output_ports[k], nframes);
FLUID_MEMSET(audio_driver->output_bufs[k], 0, nframes * sizeof(float));
k = 2 * i + 1;
audio_driver->output_bufs[k] = (float *)jack_port_get_buffer(audio_driver->output_ports[k], nframes);
FLUID_MEMSET(audio_driver->output_bufs[k], 0, nframes * sizeof(float));
}
for(i = 0, k = audio_driver->num_fx_ports; i < audio_driver->num_fx_ports; i++, k++)
for(i = 0; i < audio_driver->num_fx_ports; i++)
{
audio_driver->fx_bufs[i] = (float *) jack_port_get_buffer(audio_driver->fx_ports[2 * i], nframes);
audio_driver->fx_bufs[k] = (float *) jack_port_get_buffer(audio_driver->fx_ports[2 * i + 1], nframes);
int k = i * 2;
audio_driver->fx_bufs[k] = (float *) jack_port_get_buffer(audio_driver->fx_ports[k], nframes);
FLUID_MEMSET(audio_driver->fx_bufs[k], 0, nframes * sizeof(float));
k = 2 * i + 1;
audio_driver->fx_bufs[k] = (float *) jack_port_get_buffer(audio_driver->fx_ports[k], nframes);
FLUID_MEMSET(audio_driver->fx_bufs[k], 0, nframes * sizeof(float));
}
fluid_synth_nwrite_float(audio_driver->data,
return callback(audio_driver->data,
nframes,
audio_driver->output_bufs,
audio_driver->output_bufs + audio_driver->num_output_ports,
audio_driver->num_fx_ports * 2,
audio_driver->fx_bufs,
audio_driver->fx_bufs + audio_driver->num_fx_ports);
audio_driver->num_output_ports * 2,
audio_driver->output_bufs);
}
return 0;
}
int

View File

@ -567,6 +567,9 @@ fluid_oss_audio_run2(void *d)
/* it's as simple as that: */
while(dev->cont)
{
FLUID_MEMSET(left, 0, buffer_size * sizeof(float));
FLUID_MEMSET(right, 0, buffer_size * sizeof(float));
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->buffers);
fluid_synth_dither_s16(&dither_index, buffer_size, left, right,

View File

@ -300,6 +300,9 @@ fluid_pulse_audio_run2(void *d)
while(dev->cont)
{
FLUID_MEMSET(left, 0, buffer_size * sizeof(float));
FLUID_MEMSET(right, 0, buffer_size * sizeof(float));
(*dev->callback)(synth, buffer_size, 0, NULL, 2, handle);
/* Interleave the floating point data */

View File

@ -302,6 +302,9 @@ void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr double
left = dev->convbuffers[0];
right = dev->convbuffers[1];
FLUID_MEMSET(left, 0, buffer_size * sizeof(float));
FLUID_MEMSET(right, 0, buffer_size * sizeof(float));
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->convbuffers);
for(i = 0, k = 0; i < buffer_size; i++)

View File

@ -114,7 +114,7 @@ fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *e
fluid_rvoice_eventhandler_t *
new_fluid_rvoice_eventhandler(int queuesize,
int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate, int extra_threads, int prio)
int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate, int extra_threads, int prio)
{
fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
@ -145,7 +145,7 @@ new_fluid_rvoice_eventhandler(int queuesize,
goto error_recovery;
}
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, sample_rate, eventhandler, extra_threads, prio);
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, sample_rate, eventhandler, extra_threads, prio);
if(eventhandler->mixer == NULL)
{

View File

@ -50,7 +50,7 @@ struct _fluid_rvoice_eventhandler_t
fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler(
int queuesize, int finished_voices_size, int bufs,
int fx_bufs, fluid_real_t sample_rate, int, int);
int fx_bufs, int fx_units, fluid_real_t sample_rate, int, int);
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *);

View File

@ -82,14 +82,11 @@ 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_fx_t *fx;
fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */
fluid_rvoice_eventhandler_t *eventhandler;
@ -98,6 +95,10 @@ struct _fluid_rvoice_mixer_t
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_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
@ -126,28 +127,26 @@ static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int threa
static FLUID_INLINE void
fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount)
{
int i;
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;
// 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);
in_rev = &in_rev[SYNTH_REVERB_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
in_ch = &in_ch [SYNTH_CHORUS_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
if(mixer->fx.mix_fx_to_out)
if(mixer->mix_fx_to_out)
{
out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
out_ch_l = &out_rev_l[0 * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
out_ch_r = &out_rev_r[0 * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
// 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;
@ -155,36 +154,51 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun
}
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);
out_rev_l = &out_rev_l[SYNTH_REVERB_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
out_rev_r = &out_rev_r[SYNTH_REVERB_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
out_ch_l = &out_ch_l[SYNTH_CHORUS_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
out_ch_r = &out_ch_r[SYNTH_CHORUS_CHANNEL * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE];
reverb_process_func = fluid_revmodel_processreplace;
chorus_process_func = fluid_chorus_processreplace;
}
if(mixer->fx.with_reverb)
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)
{
reverb_process_func(mixer->fx.reverb, &in_rev[i], &out_rev_l[i], &out_rev_r[i]);
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->fx.with_chorus)
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)
{
chorus_process_func(mixer->fx.chorus, &in_ch[i], &out_ch_l[i], &out_ch_r[i]);
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,
@ -213,8 +227,10 @@ fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbu
{
fluid_real_t *base_ptr;
int i;
int with_reverb = buffers->mixer->fx.with_reverb;
int with_chorus = buffers->mixer->fx.with_chorus;
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.
@ -225,10 +241,23 @@ fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbu
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);
outbufs[buffers->buf_count * 2 + SYNTH_REVERB_CHANNEL] = (with_reverb) ? &base_ptr[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL;
outbufs[buffers->buf_count * 2 + SYNTH_CHORUS_CHANNEL] = (with_chorus) ? &base_ptr[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL;
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
@ -256,7 +285,7 @@ fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbu
outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
}
return buffers->buf_count * 2 + 2;
return offset + buffers->fx_buf_count;
}
@ -628,16 +657,20 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
fluid_rvoice_mixer_t *mixer = obj;
fluid_real_t samplerate = param[1].real; // becausee fluid_synth_update_mixer() puts real into arg2
if(mixer->fx.chorus)
int i;
for(i = 0; i < mixer->fx_units; i++)
{
delete_fluid_chorus(mixer->fx.chorus);
if(mixer->fx[i].chorus)
{
delete_fluid_chorus(mixer->fx[i].chorus);
}
mixer->fx.chorus = new_fluid_chorus(samplerate);
mixer->fx[i].chorus = new_fluid_chorus(samplerate);
if(mixer->fx.reverb)
if(mixer->fx[i].reverb)
{
fluid_revmodel_samplerate_change(mixer->fx.reverb, samplerate);
fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);
}
}
#if LADSPA
@ -656,8 +689,9 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
* @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_eventhandler_t *evthandler, int extra_threads, int prio)
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)
{
int i;
fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t);
if(mixer == NULL)
@ -668,24 +702,35 @@ new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate
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;
mixer->buffers.fx_buf_count = fx_buf_count * fx_units;
/* 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 || mixer->fx.chorus == NULL)
mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units);
if(mixer->fx == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
delete_fluid_rvoice_mixer(mixer);
return NULL;
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))
{
delete_fluid_rvoice_mixer(mixer);
return NULL;
goto error_recovery;
}
#if ENABLE_MIXER_THREADS
@ -697,19 +742,21 @@ new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate
if(!mixer->thread_ready || !mixer->wakeup_threads ||
!mixer->thread_ready_m || !mixer->wakeup_threads_m)
{
delete_fluid_rvoice_mixer(mixer);
return NULL;
goto error_recovery;
}
if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK)
{
delete_fluid_rvoice_mixer(mixer);
return NULL;
goto error_recovery;
}
#endif
return mixer;
error_recovery:
delete_fluid_rvoice_mixer(mixer);
return NULL;
}
static void
@ -727,6 +774,8 @@ fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers)
void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer)
{
int i;
fluid_return_if_fail(mixer != NULL);
#if ENABLE_MIXER_THREADS
@ -755,16 +804,21 @@ void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer)
#endif
fluid_mixer_buffers_free(&mixer->buffers);
if(mixer->fx.reverb)
for(i = 0; i < mixer->fx_units; i++)
{
delete_fluid_revmodel(mixer->fx.reverb);
if(mixer->fx[i].reverb)
{
delete_fluid_revmodel(mixer->fx[i].reverb);
}
if(mixer->fx.chorus)
if(mixer->fx[i].chorus)
{
delete_fluid_chorus(mixer->fx.chorus);
delete_fluid_chorus(mixer->fx[i].chorus);
}
}
FLUID_FREE(mixer->fx);
FLUID_FREE(mixer->rvoices);
FLUID_FREE(mixer);
}
@ -818,19 +872,19 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled)
fluid_rvoice_mixer_t *mixer = obj;
int on = param[0].i;
mixer->fx.with_reverb = on;
mixer->with_reverb = on;
}
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled)
{
fluid_rvoice_mixer_t *mixer = obj;
int on = param[0].i;
mixer->fx.with_chorus = on;
mixer->with_chorus = 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;
}
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params)
@ -843,7 +897,11 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params)
fluid_real_t depth_ms = param[4].real;
int type = param[5].i;
fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
int i;
for(i = 0; i < mixer->fx_units; i++)
{
fluid_chorus_set(mixer->fx[i].chorus, set, nr, level, speed, depth_ms, type);
}
}
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params)
@ -855,19 +913,31 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params)
fluid_real_t width = param[3].real;
fluid_real_t level = param[4].real;
fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level);
int i;
for(i = 0; i < mixer->fx_units; i++)
{
fluid_revmodel_set(mixer->fx[i].reverb, set, roomsize, damping, width, level);
}
}
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb)
{
fluid_rvoice_mixer_t *mixer = obj;
fluid_revmodel_reset(mixer->fx.reverb);
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;
fluid_chorus_reset(mixer->fx.chorus);
int i;
for(i = 0; i < mixer->fx_units; i++)
{
fluid_chorus_reset(mixer->fx[i].chorus);
}
}
int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer,

View File

@ -37,7 +37,7 @@ int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer);
#if WITH_PROFILING
int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer);
#endif
fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count,
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 *, int, int);
void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *);

View File

@ -223,6 +223,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0);
fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0);
fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0);
fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0);
fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0);
fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0);
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0);
@ -638,6 +639,7 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);
fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);
fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels);
fluid_settings_getint(settings, "synth.effects-groups", &synth->effects_groups);
fluid_settings_getnum_float(settings, "synth.gain", &synth->gain);
fluid_settings_getint(settings, "synth.device-id", &synth->device_id);
fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);
@ -776,7 +778,7 @@ new_fluid_synth(fluid_settings_t *settings)
/* Allocate event queue for rvoice mixer */
/* In an overflow situation, a new voice takes about 50 spaces in the queue! */
synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64,
synth->polyphony, nbuf, synth->effects_channels, synth->sample_rate, synth->cores - 1, prio_level);
synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups, synth->sample_rate, synth->cores - 1, prio_level);
if(synth->eventhandler == NULL)
{
@ -3308,6 +3310,8 @@ fluid_synth_program_reset(fluid_synth_t *synth)
fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r)
* @endcode
*
* @deprecated fluid_synth_nwrite_float() is deprecated. Consider using the more powerful and flexible fluid_synth_process().
*/
int
fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
@ -3474,54 +3478,80 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
}
/**
* @brief Synthesize floating point audio to planar audio buffers.
* @brief Synthesize floating point audio to stereo audio channels (implements the default interface #fluid_audio_func_t).
*
* Synthesize and <strong>mix</strong> audio to a given number of stereo audio channels.
* Therefore pass <code>nout = i*2</code> float buffers to \p out in order to render
* the synthesized audio to \p i stereo channels. Each float buffer must be
* Synthesize and <strong>mix</strong> audio to a given number of planar audio buffers.
* Therefore pass <code>nout = N*2</code> float buffers to \p out in order to render
* the synthesized audio to \p N stereo channels. Each float buffer must be
* able to hold \p len elements.
*
* \p out contains an array of planar buffers for normal, dry, stereo
* audio (alternating left and right). Like:
@code{.cpp}
out[0] = left_buffer_channel_1
out[1] = right_buffer_channel_1
out[2] = left_buffer_channel_2
out[3] = right_buffer_channel_2
out[0] = left_buffer_audio_channel_0
out[1] = right_buffer_audio_channel_0
out[2] = left_buffer_audio_channel_1
out[3] = right_buffer_audio_channel_1
...
out[ (i-1) * 2 + 0 ] = left_buffer_channel_i
out[ (i-1) * 2 + 1 ] = right_buffer_channel_i
out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i
out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i
@endcode
*
* for one-based channel index \p i.
* Same buffer layout is used for \p fx for storing effects
* like reverb and chorus audio.
* This function implements the default interface #fluid_audio_func_t.
* for zero-based channel index \p i.
* The buffer layout of \p fx used for storing effects
* like reverb and chorus looks similar:
@code{.cpp}
fx[0] = left_buffer_channel_of_reverb_unit_0
fx[1] = right_buffer_channel_of_reverb_unit_0
fx[2] = left_buffer_channel_of_chorus_unit_0
fx[3] = right_buffer_channel_of_chorus_unit_0
fx[4] = left_buffer_channel_of_reverb_unit_1
fx[5] = right_buffer_channel_of_reverb_unit_1
fx[6] = left_buffer_channel_of_chorus_unit_1
fx[7] = right_buffer_channel_of_chorus_unit_1
fx[8] = left_buffer_channel_of_reverb_unit_2
...
fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k
fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k
@endcode
* where <code>0 <= k < fluid_synth_count_effects_groups()</code> is a zero-based index denoting the effects unit and
* <code>0 <= j < fluid_synth_count_effects_channels()</code> is a zero-based index denoting the effect channel within
* unit \p k.
*
* Any voice playing is assigned to audio channels based on the MIDI channel its playing on. Let \p chan be the
* zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is
* going to be rendered to use:
*
* <code>i = chan % fluid_synth_count_audio_groups()</code>
*
* <code>k = chan % fluid_synth_count_effects_groups()</code>
*
* @param synth FluidSynth instance
* @param len Count of audio frames to synthesize.
* @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx.
* @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo)
* and in the range <code>0 <= nfx/2 <= fluid_synth_count_effects_channels()</code>.
* and in the range <code>0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.
* @param fx Array of buffers to store effects audio to. Buffers may
alias with buffers of \c out.
alias with buffers of \c out. NULL buffers are permitted and will cause to skip mixing any audio into that buffer.
* @param nout Count of arrays in \c out. Must be a multiple of 2
(because of stereo) and in the range <code>0 <= nout/2 <= fluid_synth_count_audio_channels()</code>.
* @param out Array of buffers to store (dry) audio to. Buffers may
alias with buffers of \c fx.
alias with buffers of \c fx. NULL buffers are permitted and will cause to skip mixing any audio into that buffer.
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
*
* @parblock
* @note Make sure to zero out the sample buffers before calling this
* function as any synthesized audio is mixed (i.e. added) to the buffers.
* @note The owner of the sample buffers must zero them out before calling this
* function, because any synthesized audio is mixed (i.e. added) to the buffers.
* E.g. if fluid_synth_process() is called from a custom audio driver process function
* (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers.
* @endparblock
*
* @parblock
* @note No matter how many buffers you pass in, fluid_synth_process()
* will always render all fluid_synth_count_audio_channels() to the
* buffers in \c out and all fluid_synth_count_effects_channels() to the
* will always render all audio channels to the
* buffers in \c out and all effects channels to the
* buffers in \c fx, provided that <code>nout > 0</code> and <code>nfx > 0</code> respectively. If
* <code>nout/2 < fluid_synth_count_audio_channels()</code> it will wrap around. Same
* is true for effects audio if <code>nfx/2 < fluid_synth_count_effects_channels()</code>.
* is true for effects audio if <code>nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.
* See usage examples below.
* @endparblock
*
@ -3533,37 +3563,183 @@ int
fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
int nout, float *out[])
{
if(nout == 2)
{
return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1);
}
else
{
float **left, **right;
int i;
left = FLUID_ARRAY(float *, nout / 2);
right = FLUID_ARRAY(float *, nout / 2);
fluid_real_t *left_in, *fx_left_in;
fluid_real_t *right_in, *fx_right_in;
int nfxchan, nfxunits, naudchan;
if((left == NULL) || (right == NULL))
double time = fluid_utime();
int i, f, num, count;
float cpu_load;
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);
fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);
nfxchan = synth->effects_channels;
nfxunits = synth->effects_groups;
naudchan = synth->audio_channels;
fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED);
fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE);
/* First, take what's still available in the buffer */
count = 0;
num = synth->cur;
if(synth->cur < FLUID_BUFSIZE)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
FLUID_FREE(left);
FLUID_FREE(right);
return FLUID_FAILED;
int available = FLUID_BUFSIZE - synth->cur;
num = (available > len) ? len : available;
if(nout != 0)
{
for(i = 0; i < naudchan; i++)
{
int j;
float *out_buf = out[(i * 2) % nout];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j] += (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
}
}
for(i = 0; i < nout / 2; i++)
out_buf = out[(i * 2 + 1) % nout];
if(out_buf != NULL)
{
left[i] = out[2 * i];
right[i] = out[2 * i + 1];
for(j = 0; j < num; j++)
{
out_buf[j] += (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
}
}
}
}
fluid_synth_nwrite_float(synth, len, left, right, NULL, NULL);
FLUID_FREE(left);
FLUID_FREE(right);
if(nfx != 0)
{
// loop over all effects units
for(f = 0; f < nfxunits; f++)
{
// write out all effects (i.e. reverb and chorus)
for(i = 0; i < nfxchan; i++)
{
int j;
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j] += (float) fx_left_in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
}
}
out_buf = fx[(buf_idx * 2 + 1) % nfx];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j] += (float) fx_right_in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
}
}
}
}
}
count += num;
num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
}
/* Then, render blocks and copy till we have 'len' samples */
while(count < len)
{
int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
int blockcount = fluid_synth_render_blocks(synth, blocksleft);
num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE;
if(nout != 0)
{
for(i = 0; i < naudchan; i++)
{
int j;
float *out_buf = out[(i * 2) % nout];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j + count] += (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
}
}
out_buf = out[(i * 2 + 1) % nout];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j + count] += (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
}
}
}
}
if(nfx != 0)
{
// loop over all effects units
for(f = 0; f < nfxunits; f++)
{
// write out all effects (i.e. reverb and chorus)
for(i = 0; i < nfxchan; i++)
{
int j;
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j + count] += (float) fx_left_in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
}
}
out_buf = fx[(buf_idx * 2 + 1) % nfx];
if(out_buf != NULL)
{
for(j = 0; j < num; j++)
{
out_buf[j + count] += (float) fx_right_in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
}
}
}
}
}
count += num;
}
synth->cur = num;
time = fluid_utime() - time;
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
fluid_atomic_float_set(&synth->cpu_load, cpu_load);
return FLUID_OK;
}
}
/**
* Synthesize a block of floating point audio samples to audio buffers.

View File

@ -118,6 +118,7 @@ struct _fluid_synth_t
int audio_groups; /**< the number of (stereo) 'sub'groups from the synth.
Typically equal to audio_channels. */
int effects_channels; /**< the number of effects channels (>= 2) */
int effects_groups; /**< the number of effects units (>= 1) */
int state; /**< the synthesizer state */
fluid_atomic_uint_t ticks_since_start; /**< the number of audio samples since the start */
unsigned int start; /**< the start in msec, as returned by system clock */

View File

@ -352,11 +352,12 @@ fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample,
UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
/* Set up buffer mapping, should be done more flexible in the future. */
i = channel->synth->audio_groups;
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i * 2 + SYNTH_REVERB_CHANNEL);
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i * 2 + SYNTH_CHORUS_CHANNEL);
i = 2 * channel->synth->audio_groups;
i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels;
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL);
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL);
i = 2 * (voice->chan % i);
i = 2 * (voice->chan % channel->synth->audio_groups);
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i);
UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1);