Merge pull request #178 from FluidSynth/multichannel

fluid_synth_nwrite_float(): write out effect buffers
This commit is contained in:
Tom M 2017-08-01 09:30:12 +02:00 committed by GitHub
commit 25e7eb0c6b
4 changed files with 170 additions and 17 deletions

View file

@ -60,10 +60,17 @@ struct _fluid_jack_audio_driver_t
{
fluid_audio_driver_t driver;
fluid_jack_client_t *client_ref;
int audio_channels;
jack_port_t **output_ports;
int num_output_ports;
float **output_bufs;
jack_port_t **fx_ports;
int num_fx_ports;
float **fx_bufs;
fluid_audio_func_t callback;
void* data;
};
@ -268,10 +275,11 @@ fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *clie
fluid_settings_getint (settings, "audio.jack.multi", &multi);
if (multi)
if (!multi)
{
/* create the two audio output ports */
dev->num_output_ports = 1;
dev->num_fx_ports = 0;
dev->output_ports = FLUID_ARRAY (jack_port_t*, 2 * dev->num_output_ports);
@ -323,6 +331,32 @@ fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *clie
dev->output_ports[2 * i + 1]
= jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
}
fluid_settings_getint (settings, "synth.effects-channels", &dev->num_fx_ports);
dev->fx_ports = FLUID_ARRAY(jack_port_t*, 2 * dev->num_fx_ports);
if (dev->fx_ports == NULL) {
FLUID_LOG(FLUID_PANIC, "Out of memory");
return FLUID_FAILED;
}
dev->fx_bufs = FLUID_ARRAY(float*, 2 * dev->num_fx_ports);
if (dev->fx_bufs == NULL) {
FLUID_LOG(FLUID_PANIC, "Out of memory");
return FLUID_FAILED;
}
FLUID_MEMSET(dev->fx_ports, 0, 2 * dev->num_fx_ports * sizeof(jack_port_t*));
for (i = 0; i < dev->num_fx_ports; i++) {
sprintf(name, "fx_l_%02d", i);
dev->fx_ports[2 * i]
= jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
sprintf(name, "fx_r_%02d", i);
dev->fx_ports[2 * i + 1]
= jack_port_register(client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
}
}
@ -432,6 +466,15 @@ new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func
connected++;
}
}
for (i = 0; jack_ports[i] && i<2 * dev->num_fx_ports; ++i) {
err = jack_connect (client, jack_port_name(dev->fx_ports[i]), jack_ports[i]);
if (err) {
FLUID_LOG(FLUID_ERR, "Error connecting jack port");
} else {
connected++;
}
}
jack_free (jack_ports); /* free jack ports array (not the port values!) */
} else {
@ -461,6 +504,12 @@ delete_fluid_jack_audio_driver(fluid_audio_driver_t* p)
if (dev->output_ports)
FLUID_FREE (dev->output_ports);
if (dev->fx_bufs)
FLUID_FREE(dev->fx_bufs);
if (dev->fx_ports)
FLUID_FREE(dev->fx_ports);
FLUID_FREE(dev);
return 0;
}
@ -517,7 +566,7 @@ fluid_jack_driver_process (jack_nframes_t nframes, void *arg)
2 * audio_driver->num_output_ports,
audio_driver->output_bufs);
}
else if (audio_driver->num_output_ports == 1)
else if (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);
@ -530,9 +579,17 @@ fluid_jack_driver_process (jack_nframes_t nframes, void *arg)
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);
}
for (i = 0, k = audio_driver->num_fx_ports; i < audio_driver->num_fx_ports; i++, k++) {
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);
}
fluid_synth_nwrite_float (audio_driver->data, nframes, audio_driver->output_bufs,
audio_driver->output_bufs + audio_driver->num_output_ports, NULL, NULL);
fluid_synth_nwrite_float (audio_driver->data,
nframes,
audio_driver->output_bufs,
audio_driver->output_bufs + audio_driver->num_output_ports,
audio_driver->fx_bufs,
audio_driver->fx_bufs + audio_driver->num_fx_ports);
}
return 0;

View file

@ -702,6 +702,14 @@ int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
return mixer->buffers.buf_count;
}
int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** fx_left, fluid_real_t*** fx_right)
{
*fx_left = mixer->buffers.fx_left_buf;
*fx_right = mixer->buffers.fx_right_buf;
return mixer->buffers.fx_buf_count;
}
#ifdef ENABLE_MIXER_THREADS

View file

@ -39,7 +39,9 @@ void fluid_rvoice_mixer_set_finished_voices_callback(
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_real_t*** left, fluid_real_t*** right);
int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** fx_left, fluid_real_t*** fx_right);
fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count,
fluid_real_t sample_rate);

View file

@ -2423,24 +2423,60 @@ fluid_synth_program_reset(fluid_synth_t* synth)
}
/**
* Synthesize a block of floating point audio to audio buffers.
* Synthesize a block of floating point audio to separate audio buffers (multichannel rendering). First effect channel used by reverb, second for chorus.
* @param synth FluidSynth instance
* @param len Count of audio frames to synthesize
* @param left Array of floats to store left channel of audio (len in size)
* @param right Array of floats to store right channel of audio (len in size)
* @param fx_left Not currently used
* @param fx_right Not currently used
* @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size)
* @param right Array of float buffers to store right channel of planar audio (size: dito)
* @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size)
* @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito)
* @return FLUID_OK on success, FLUID_FAIL otherwise
*
* NOTE: Should only be called from synthesis thread.
* @note Should only be called from synthesis thread.
*
* Usage example:
* @code
const int FramesToRender = 64;
int channels;
// retrieve number of stereo audio channels
fluid_settings_getint(settings, "synth.audio-channels", &channels);
// we need twice as many (mono-)buffers
channels *= 2;
// fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM
float** mix_buf = new float*[channels];
for(int i = 0; i < channels; i++)
{
mix_buf[i] = new float[FramesToRender];
}
// retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan))
fluid_settings_getint(settings, "synth.effects-channels", &channels);
channels *= 2;
float** fx_buf = new float*[channels];
for(int i = 0; i < channels; i++)
{
fx_buf[i] = new float[FramesToRender];
}
float** mix_buf_l = mix_buf;
float** mix_buf_r = &mix_buf[channels/2];
float** fx_buf_l = fx_buf;
float** fx_buf_r = &fx_buf[channels/2];
fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r)
* @endcode
*/
int
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;
fluid_real_t** right_in;
fluid_real_t** left_in, **fx_left_in;
fluid_real_t** right_in, **fx_right_in;
double time = fluid_utime();
int i, num, available, count;
#ifdef WITH_FLOAT
@ -2457,6 +2493,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
if (synth->cur < FLUID_BUFSIZE) {
available = FLUID_BUFSIZE - synth->cur;
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);
num = (available > len)? len : available;
#ifdef WITH_FLOAT
@ -2475,6 +2512,29 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
}
#endif //WITH_FLOAT
}
for (i = 0; i < synth->effects_channels; i++)
{
#ifdef WITH_FLOAT
if(fx_left != NULL)
FLUID_MEMCPY(fx_left[i], fx_left_in[i] + synth->cur, bytes);
if(fx_right != NULL)
FLUID_MEMCPY(fx_right[i], fx_right_in[i] + synth->cur, bytes);
#else //WITH_FLOAT
int j;
if(fx_left != NULL) {
for (j = 0; j < num; j++)
fx_left[i][j] = (float) fx_left_in[i][j + synth->cur];
}
if(fx_right != NULL) {
for (j = 0; j < num; j++)
fx_right[i][j] = (float) fx_right_in[i][j + synth->cur];
}
#endif //WITH_FLOAT
}
count += num;
num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
}
@ -2484,6 +2544,7 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0);
fluid_synth_render_blocks(synth, 1); // TODO:
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);
num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
#ifdef WITH_FLOAT
@ -2503,6 +2564,28 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
#endif //WITH_FLOAT
}
for (i = 0; i < synth->effects_channels; i++)
{
#ifdef WITH_FLOAT
if(fx_left != NULL)
FLUID_MEMCPY(fx_left[i + count], fx_left_in[i], bytes);
if(fx_right != NULL)
FLUID_MEMCPY(fx_right[i + count], fx_right_in[i], bytes);
#else //WITH_FLOAT
int j;
if(fx_left != NULL) {
for (j = 0; j < num; j++)
fx_left[i][j + count] = (float) fx_left_in[i][j];
}
if(fx_right != NULL) {
for (j = 0; j < num; j++)
fx_right[i][j + count] = (float) fx_right_in[i][j];
}
#endif //WITH_FLOAT
}
count += num;
}
@ -2529,7 +2612,8 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
* @return FLUID_OK on success, FLUID_FAIL otherwise
*
* This function implements the default interface defined in fluidsynth/audio.h.
* NOTE: Should only be called from synthesis thread.
*
* @note Should only be called from synthesis thread.
*/
/*
* FIXME: Currently if nout != 2 memory allocation will occur!
@ -2578,7 +2662,8 @@ fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in,
* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
* lincr = 2, rincr = 2).
*
* NOTE: Should only be called from synthesis thread.
* @note Should only be called from synthesis thread.
* @note Reverb and Chorus are mixed to \c lout resp. \c rout.
*/
int
fluid_synth_write_float(fluid_synth_t* synth, int len,
@ -2676,8 +2761,9 @@ roundi (float x)
* Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
* lincr = 2, rincr = 2).
*
* NOTE: Should only be called from synthesis thread.
* NOTE: Dithering is performed when converting from internal floating point to
* @note Should only be called from synthesis thread.
* @note Reverb and Chorus are mixed to \c lout resp. \c rout.
* @note Dithering is performed when converting from internal floating point to
* 16 bit audio.
*/
int