update API doc of fluid_synth_process() and add usage example

This commit is contained in:
derselbst 2018-05-17 14:43:59 +02:00
parent 04930d62d1
commit 75e168cb58
8 changed files with 192 additions and 30 deletions

View file

@ -122,7 +122,7 @@ HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET = ..doc/doxy_formula.css
HTML_ALIGN_MEMBERS = YES
HTML_EXTRA_FILES = ../doc/fluidsettings.xml ../doc/fluidsettings.xsl
GENERATE_HTMLHELP = NO

View file

@ -122,7 +122,7 @@ HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/doxy_formula.css
HTML_ALIGN_MEMBERS = YES
HTML_EXTRA_FILES = @CMAKE_SOURCE_DIR@/doc/fluidsettings.xml @CMAKE_SOURCE_DIR@/doc/fluidsettings.xsl
GENERATE_HTMLHELP = NO

12
doc/doxy_formula.css Normal file
View file

@ -0,0 +1,12 @@
code {
background-color: #eeeeee;
text-shadow: none;
color: black;
margin-left: 4px;
margin-right: 4px;
padding-left: 4px;
padding-right: 4px;
border-radius: 3px;
white-space: nowrap;
}

View file

@ -892,3 +892,8 @@ Example of how to register audio drivers using fluid_audio_driver_register() (ad
\example fluidsynth_sfload_mem.c
Example of how read a soundfont from memory (advanced users only)
*/
/*!
\example fluidsynth_process.c
Usage examples of how to render audio using fluid_synth_process() (advanced users only)
*/

105
doc/fluidsynth_process.c Normal file
View file

@ -0,0 +1,105 @@
/*
* This is a C99 program that outlines different usage examples for fluid_synth_process()
*/
#include <stdio.h>
#include <string.h>
#include <fluidsynth.h>
int main()
{
// any arbitrary number of audio samples to render during on call of fluid_synth_process()
enum { SAMPLES = 512 };
// ...creation of synth omitted...
// USECASE1: render all dry audio channels + reverb and chorus to one stereo channel
{
// planar sample buffers that received synthesized (monophonic) audio
float left[SAMPLES], right[SAMPLES];
// array of buffers used to setup channel mapping
float *dry[1*2], *fx[1*2];
// first make sure to zero out the sample buffers
memset(left, 0, sizeof(left1));
memset(right, 0, sizeof(right1));
// setup channel mapping for a single stereo channel to which to render all dry audio to
dry[0] = left;
dry[1] = right;
// setup channel mapping for a single effects channel to which to render reverb and chorus to
// just using the same sample buffers as for dry audio is fine here, will cause the effects to be mixed with dry output
fx[0] = left;
fx[1] = right;
int err = fluid_synth_process(synth, SAMPLES, 2, fx, 2, dry);
if(err == FLUID_FAILED)
puts(oops);
// USECASE2: only render dry audio and discard effects
// same as above, but call fluid_synth_process() like:
int err = fluid_synth_process(synth, SAMPLES, 0, NULL, 2, dry);
if(err == FLUID_FAILED)
puts(oops);
}
// USECASE3: render audio and discard all samples
{
int err = fluid_synth_process(synth, SAMPLES, 0, NULL, 0, NULL);
if(err == FLUID_FAILED)
puts(oops);
}
// USECASE4: multi-channel rendering, i.e. render all audio and effects channels to dedicated audio buffers
// ofc its not a good idea to allocate all the arrays on the stack
{
// lookup number of audio and effect (stereo-)channels of the synth
// see „synth.audio-channels“ and „synth.effects-channels“ settings respectively
int n_aud_chan = fluid_synth_count_audio_channels(synth);
int n_fx_chan = fluid_synth_count_effects_channels(synth);
// allocate one single sample buffer
float samp_buf[SAMPLES * (n_aud_chan + n_fx_chan) * 2];
// array of buffers used to setup channel mapping
float *dry[n_aud_chan * 2], *fx[n_fx_chan * 2];
// setup buffers to mix dry stereo audio to
// buffers are alternating left and right for each audio channel, i.e.:
// dry[0] = first audio channel left
// dry[1] = first audio channel right
// dry[2] = second audio channel left
// ...
// dry[i*2 + 0] = ith audio channel left
// dry[i*2 + 1] = ith audio channel right
for(int i=0; i<n_aud_chan*2; i++)
{
dry[i] = &samp_buf[i * SAMPLES];
}
// setup buffers to mix effects stereo audio to
// similar channel layout as above, but currently special as there are only 2 hardcoded effects channels:
// fx[0] = global reverb channel left
// fx[1] = global reverb channel right
// fx[2] = global chorus channel left
// fx[3] = global chorus channel right
for(int i=0; i<n_fx_chan*2; i++)
{
fx[i] = &samp_buf[n_aud_chan*2*SAMPLES + i * SAMPLES];
}
// dont forget to zero sample buffer(s) before each rendering
memset(samp_buf, 0, sizeof(samp_buf));
int err = fluid_synth_process(synth, SAMPLES, n_fx_chan*2, fx, n_aud_chan*2, dry);
if(err == FLUID_FAILED)
puts(oops);
}
return 0;
}

View file

@ -41,19 +41,20 @@ extern "C" {
/**
* Callback function type used with new_fluid_audio_driver2() to allow for
* custom user audio processing before the audio is sent to the driver. This
* function is responsible for rendering the audio to the buffers.
* custom user audio processing before the audio is sent to the driver. This
* function is responsible for rendering the audio to the buffers. For details
* please refer to fluid_synth_process().
* @param data The user data parameter as passed to new_fluid_audio_driver2().
* @param len Length of the audio in frames.
* @param nin Count of buffers in 'in'
* @param in Not used currently
* @param nout Count of arrays in 'out' (i.e., channel count)
* @param out Output buffers, one for each channel
* @return Should return 0 on success, non-zero if an error occured.
* @param len Count of audio frames to synthesize.
* @param nfx Count of arrays in \c fx.
* @param fx Array of buffers to store effects audio to. Buffers may alias with buffers of \c out.
* @param nout Count of arrays in \c out.
* @param out Array of buffers to store (dry) audio to. Buffers may alias with buffers of \c fx.
* @return Should return #FLUID_OK on success, #FLUID_FAILED if an error occured.
*/
typedef int (*fluid_audio_func_t)(void* data, int len,
int nin, float** in,
int nout, float** out);
int nfx, float* fx[],
int nout, float* out[]);
FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver(fluid_settings_t* settings,
fluid_synth_t* synth);

View file

@ -276,8 +276,8 @@ 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,
int nin, float** in,
int nout, float** out);
int nfx, float* fx[],
int nout, float* out[]);
/* Synthesizer's interface to handle SoundFont loaders */

View file

@ -2845,7 +2845,7 @@ fluid_synth_program_reset(fluid_synth_t* synth)
* @note Should only be called from synthesis thread.
*
* Usage example:
* @code
* @code{.cpp}
const int FramesToRender = 64;
int channels;
// retrieve number of stereo audio channels
@ -3006,25 +3006,64 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
}
/**
* Synthesize floating point audio to audio buffers.
* @param synth FluidSynth instance
* @param len Count of audio frames to synthesize
* @param nin Ignored
* @param in Ignored
* @param nout Count of arrays in 'out'
* @param out Array of arrays to store audio to
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
* @brief Synthesize floating point audio to planar audio buffers.
*
* This function implements the default interface defined in fluidsynth/audio.h.
*
* 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
* 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[ (i-1) * 2 + 0 ] = left_buffer_channel_i
out[ (i-1) * 2 + 1 ] = right_buffer_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.
*
* @param synth FluidSynth instance
* @param len Count of audio frames to synthesize.
* @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>.
* @param fx Array of buffers to store effects audio to. Buffers may
alias with buffers of \c out.
* @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.
* @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.
* @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
* 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>.
* See usage examples below.
* @endparblock
*
* @parblock
* @note Should only be called from synthesis thread.
*/
/*
* FIXME: Currently if nout != 2 memory allocation will occur!
* @endparblock
*/
int
fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in,
int nout, float** out)
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);