mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-12-01 00:21:14 +00:00
Merge pull request #178 from FluidSynth/multichannel
fluid_synth_nwrite_float(): write out effect buffers
This commit is contained in:
commit
25e7eb0c6b
4 changed files with 170 additions and 17 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue