Dithering patch applied from Mihail Zenkov, fixed 100% CPU consumption

when using "default" ALSA device with some cards, some bug fixes with
ALSA sequencer driver.
This commit is contained in:
Element Green 2006-11-21 20:04:31 +00:00
parent fd62e609a3
commit 4c68e10ab3
5 changed files with 249 additions and 133 deletions

View file

@ -6,10 +6,10 @@
[:Development:]
Many people contributed to FluidSynth, send suggestions or bug
Many people contributed to FluidSynth, sent suggestions or bug
fixes. The project was started by Peter Hanappe who is the main
author. Josh Green is the current maintainer. Below you'll find a
summery of contributions.
summary of contributions.
* Peter Hanappe. Initiated the project. files: sticked his nose in all
@ -24,10 +24,6 @@ summery of contributions.
(the blue waves with FluidSynth letters partially submerged).
files: iiwu_defsfont.{c,h}, iiwu_alsa{c,h} and others.
* Stephane Letz from Grame wrote most of the MidiShare driver, all of
the PortAudio driver, ported iiwusynth to MacOS X, and sent in many
fixes. files: iiwu_midishare.c, iiwu_portaudio.c
* Markus Nentwig (re-)designed the resonant filter, the chorus, the
LADSPA subsystem, the MIDI router, optimized for SSE, made many
changes and bug fixes and got the synthesizer to actually work. Most
@ -36,6 +32,10 @@ summery of contributions.
iiwu_sse.h, iiwu_dsp_core.h, iiwu_rev.{c,h}, and basically all the
other files.
* Stephane Letz from Grame wrote most of the MidiShare driver, all of
the PortAudio driver, ported iiwusynth to MacOS X, and sent in many
fixes. files: iiwu_midishare.c, iiwu_portaudio.c
* Antoine Schmitt added the sequencer support, support for sample
loading (RAM Sfont), developed the
MacroMedia Director Xtra, and send in many many bug reports. Thanks
@ -67,8 +67,7 @@ summery of contributions.
portability.
* The midi device uses code from jMax's alsarawmidi.c file and from
Smurf's midi_alsaraw.c by Josh Green. Josh also added support for
the alsa sequencer interface. file: iiwu_alsa.c
Smurf's midi_alsaraw.c by Josh Green. file: iiwu_alsa.c
* The reverb algorithm was written by Jezar
(http://www.dreampoint.co.uk). His code is public domain. The code
@ -115,3 +114,4 @@ Gerald Pye
Rui Nuno Capela
Frieder Bürzele
Henri Manson
Mihail Zenkov (Audio bit depth conversion dithering patch)

View file

@ -1,3 +1,43 @@
2006-11-21 Josh Green <jgreen@users.sourceforge.net>
* src/fluid_alsa.c (new_fluid_alsa_audio_driver2): Modified all ALSA
calls to check return code error as "< 0" as per ALSA examples, sample
rate is now compared with what was expected and warning message displays
both values, if target sample rate wasn't set update the local
period_size variable (was causing 100% CPU consumption by ALSA, from
the resultant erroneous sw_params calls).
(fluid_alsa_audio_run_float): Using case statement for
error codes from snd_pcm_writen() for the sake of tidiness.
(fluid_alsa_audio_run_s16): Using case statement for error codes
from snd_pcm_writei() for the sake of tidiness, re-instated call
of device callback function that was broken with the dither patch
(don't want to break the API), now using new fluid_synth_dither_s16()
to convert floating point sample data to 16 bit with dithering.
(fluid_alsa_seq_run): Timeout in poll() call set to 100ms (from 1ms!),
snd_seq_event_input_pending is used to check if events are available
before calling snd_seq_event_input to prevent blocking, check of
snd_seq_event_input error code moved to the right location (bug fix).
* src/fluid_synth.h: Added dither_index parameter to fluid_synth_t
structure to allow for per synth dithering continuity.
* src/fluid_synth.c: Modified dithering to use new dither_index field
for per synth dithering continuity, fixed off by 1 error with
dithering index comparison, removed usage of roundf in dithering (is
it sufficient to just integer truncate?).
(fluid_synth_dither_s16): New function to perform dithering on
buffers of floating point sample data.
2006-11-20 Josh Green <jgreen@users.sourceforge.net>
* src/fluid_alsa.c: Applied dithering patch from Mihail Zenkov.
* src/fluid_synth.c: Applied dithering patch from Mihail Zenkov.
2006-03-04 Josh Green <jgreen@users.sourceforge.net>
* src/fluid_alsa.c (delete_fluid_alsa_audio_driver): Now calling
snd_pcm_close() to close the ALSA audio driver handle.
(fluid_alsa_seq_run): Check for -ENOSPC error was logicly inverted.
(new_fluid_alsa_seq_driver): Sequencer is now opened in blocking mode.
2006-02-20 Josh Green <jgreen@users.sourceforge.net>
* Fixed build error that occured when neither LASH or LADCCA are
@ -18,7 +58,7 @@
"-o help" switch for listing settings, welcome message now printed
whenever FluidSynth is run and simplified,
(print_usage): hard coded application name as "fluidsynth".
* src/configure.ac: Changed --enable-SSE option to --enable-broken-SSE
* configure.ac: Changed --enable-SSE option to --enable-broken-SSE
and --enable-SSE now just displays a fat warning about not using it.
* src/fluid_jack.c: Warning is now displayed if synth sample rate
doesn't match jackd.

View file

@ -200,7 +200,6 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
dev->callback = func;
dev->cont = 1;
dev->buffer_size = period_size;
uframes = period_size;
/* Open the PCM device */
if ((err = snd_pcm_open(&dev->pcm, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) != 0) {
@ -225,34 +224,36 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
snd_pcm_hw_params_any(dev->pcm, hwparams);
if (snd_pcm_hw_params_set_access(dev->pcm, hwparams, fluid_alsa_formats[i].access) != 0) {
if (snd_pcm_hw_params_set_access(dev->pcm, hwparams, fluid_alsa_formats[i].access) < 0) {
continue;
}
if (snd_pcm_hw_params_set_format(dev->pcm, hwparams, fluid_alsa_formats[i].format) != 0) {
if (snd_pcm_hw_params_set_format(dev->pcm, hwparams, fluid_alsa_formats[i].format) < 0) {
continue;
}
if ((err = snd_pcm_hw_params_set_channels(dev->pcm, hwparams, 2)) != 0) {
if ((err = snd_pcm_hw_params_set_channels(dev->pcm, hwparams, 2)) < 0) {
FLUID_LOG(FLUID_ERR, "Failed to set the channels: %s",
snd_strerror (err));
goto error_recovery;
}
tmp = (unsigned int) sample_rate;
if ((err = snd_pcm_hw_params_set_rate_near(dev->pcm, hwparams, &tmp, &dir)) != 0) {
if ((err = snd_pcm_hw_params_set_rate_near(dev->pcm, hwparams, &tmp, NULL)) < 0) {
FLUID_LOG(FLUID_ERR, "Failed to set the sample rate: %s",
snd_strerror (err));
goto error_recovery;
}
if (dir != 0) {
/* There's currently no way to change the sampling rate of the
synthesizer after it's been created. */
FLUID_LOG(FLUID_WARN, "The sample rate is set to %d, "
"the synthesizer may be out of tune", tmp);
goto error_recovery;
}
if (snd_pcm_hw_params_set_period_size_near(dev->pcm, hwparams, &uframes, &dir) != 0) {
if (tmp != sample_rate) {
/* There's currently no way to change the sampling rate of the
synthesizer after it's been created. */
FLUID_LOG(FLUID_WARN, "Requested sample rate of %d, got %d instead, "
"synthesizer likely out of tune!", sample_rate, tmp);
}
uframes = period_size;
if (snd_pcm_hw_params_set_period_size_near(dev->pcm, hwparams, &uframes, &dir) < 0) {
FLUID_LOG(FLUID_ERR, "Failed to set the period size");
goto error_recovery;
}
@ -260,10 +261,11 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
FLUID_LOG(FLUID_WARN, "Requested a period size of %d, got %d instead",
period_size, (int) uframes);
dev->buffer_size = (int) uframes;
period_size = uframes; /* period size is used below, so set it to the real value */
}
tmp = periods;
if (snd_pcm_hw_params_set_periods_near(dev->pcm, hwparams, &tmp, &dir) != 0) {
if (snd_pcm_hw_params_set_periods_near(dev->pcm, hwparams, &tmp, &dir) < 0) {
FLUID_LOG(FLUID_ERR, "Failed to set the number of periods");
goto error_recovery;
}
@ -272,7 +274,7 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
periods, (int) tmp);
}
if (snd_pcm_hw_params(dev->pcm, hwparams) != 0) {
if (snd_pcm_hw_params(dev->pcm, hwparams) < 0) {
FLUID_LOG(FLUID_WARN, "Audio device hardware configuration failed");
continue;
}
@ -289,27 +291,27 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings,
/* Set the software params */
snd_pcm_sw_params_current(dev->pcm, swparams);
if (snd_pcm_sw_params_set_start_threshold(dev->pcm, swparams, period_size) != 0) {
FLUID_LOG(FLUID_ERR, "Cannot turn off start threshold.");
FLUID_LOG(FLUID_ERR, "Failed to set start threshold.");
}
if (snd_pcm_sw_params_set_stop_threshold(dev->pcm, swparams, ~0u) != 0) {
FLUID_LOG(FLUID_ERR, "Cannot turn off stop threshold.");
}
if (snd_pcm_sw_params_set_silence_threshold(dev->pcm, swparams, 0) != 0) {
FLUID_LOG(FLUID_ERR, "Cannot set 0 silence threshold.");
}
if (snd_pcm_sw_params_set_silence_size(dev->pcm, swparams, 0) != 0) {
FLUID_LOG(FLUID_ERR, "Cannot set 0 silence size.");
}
if (snd_pcm_sw_params_set_avail_min(dev->pcm, swparams, period_size / 2) != 0) {
FLUID_LOG(FLUID_ERR, "Software setup for minimum available frames failed.");
}
if (snd_pcm_sw_params(dev->pcm, swparams) != 0) {
FLUID_LOG(FLUID_ERR, "Software setup failed.");
}
@ -390,6 +392,7 @@ int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p)
|| (state == SND_PCM_STATE_PAUSED)) {
snd_pcm_drop(dev->pcm);
}
snd_pcm_close (dev->pcm);
}
FLUID_FREE(dev);
@ -400,6 +403,7 @@ int delete_fluid_alsa_audio_driver(fluid_audio_driver_t* p)
static void* fluid_alsa_audio_run_float(void* d)
{
fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d;
fluid_synth_t *synth = (fluid_synth_t *)(dev->data);
float* left;
float* right;
float* handle[2];
@ -415,9 +419,6 @@ static void* fluid_alsa_audio_run_float(void* d)
return NULL;
}
handle[0] = left;
handle[1] = right;
if (snd_pcm_nonblock(dev->pcm, 0) != 0) { /* double negation */
FLUID_LOG(FLUID_ERR, "Failed to set the audio device to blocking mode");
goto error_recovery;
@ -433,8 +434,8 @@ static void* fluid_alsa_audio_run_float(void* d)
handle[0] = left;
handle[1] = right;
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, handle);
(*dev->callback)(synth, buffer_size, 0, NULL, 2, handle);
offset = 0;
while (offset < buffer_size) {
@ -442,32 +443,38 @@ static void* fluid_alsa_audio_run_float(void* d)
handle[0] = left + offset;
handle[1] = right + offset;
n = snd_pcm_writen(dev->pcm, (void**) handle, buffer_size - offset);
if (n == -EAGAIN) {
snd_pcm_wait(dev->pcm, 1);
n = snd_pcm_writen(dev->pcm, (void *)handle, buffer_size - offset);
} else if ((n == -EPIPE) || (n == -EBADFD)) {
if (snd_pcm_prepare(dev->pcm) != 0) {
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
goto error_recovery;
}
} else if (n == -ESTRPIPE) {
if ((snd_pcm_resume(dev->pcm) != 0)
&& (snd_pcm_prepare(dev->pcm) != 0)) {
FLUID_LOG(FLUID_ERR, "Failed to resume the audio device");
goto error_recovery;
}
} else if (n < 0) {
if (n < 0) /* error occurred? */
{
switch (n)
{
case -EAGAIN:
snd_pcm_wait(dev->pcm, 1);
break;
case -EPIPE:
case -EBADFD:
if (snd_pcm_prepare(dev->pcm) != 0) {
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
goto error_recovery;
}
break;
case -ESTRPIPE:
if ((snd_pcm_resume(dev->pcm) != 0)
&& (snd_pcm_prepare(dev->pcm) != 0)) {
FLUID_LOG(FLUID_ERR, "Failed to resume the audio device");
goto error_recovery;
}
break;
default:
FLUID_LOG(FLUID_ERR, "The audio device error: %s", snd_strerror(n));
goto error_recovery;
} else {
goto error_recovery;
break;
}
} else { /* no error occurred */
offset += n;
}
}
}
}
error_recovery:
@ -481,6 +488,7 @@ static void* fluid_alsa_audio_run_float(void* d)
static void* fluid_alsa_audio_run_s16(void* d)
{
fluid_alsa_audio_driver_t* dev = (fluid_alsa_audio_driver_t*) d;
fluid_synth_t* synth = (fluid_synth_t *)(dev->data);
float* left;
float* right;
short* buf;
@ -517,53 +525,49 @@ static void* fluid_alsa_audio_run_s16(void* d)
handle[0] = left;
handle[1] = right;
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, handle);
(*dev->callback)(synth, buffer_size, 0, NULL, 2, handle);
for (i = 0, k = 0; i < buffer_size; i++, k += 2) {
s = 32768.0f * left[i];
fluid_clip(s, -32768.0f, 32767.0f);
buf[k] = (short) s;
}
/* convert floating point data to 16 bit (with dithering) */
fluid_synth_dither_s16 (synth, buffer_size, left, right, buf, 0, 2, buf, 1, 2);
for (i = 0, k = 1; i < buffer_size; i++, k += 2) {
s = 32768.0f * right[i];
fluid_clip(s, -32768.0f, 32767.0f);
buf[k] = (short) s;
}
offset = 0;
while (offset < buffer_size) {
n = snd_pcm_writei(dev->pcm, (void*) (buf + 2 * offset), buffer_size - offset);
if (n == -EAGAIN) {
snd_pcm_wait(dev->pcm, 1);
} else if ((n == -EPIPE) || (n == -EBADFD)) {
if (snd_pcm_prepare(dev->pcm) != 0) {
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
goto error_recovery;
}
} else if (n == -ESTRPIPE) {
if ((snd_pcm_resume(dev->pcm) != 0)
&& (snd_pcm_prepare(dev->pcm) != 0)) {
FLUID_LOG(FLUID_ERR, "Failed to resume the audio device");
goto error_recovery;
}
} else if (n < 0) {
if (n < 0) /* error occurred? */
{
switch (n)
{
case -EAGAIN:
snd_pcm_wait(dev->pcm, 1);
break;
case -EPIPE:
case -EBADFD:
if (snd_pcm_prepare(dev->pcm) != 0) {
FLUID_LOG(FLUID_ERR, "Failed to prepare the audio device");
goto error_recovery;
}
break;
case -ESTRPIPE:
if ((snd_pcm_resume(dev->pcm) != 0)
&& (snd_pcm_prepare(dev->pcm) != 0)) {
FLUID_LOG(FLUID_ERR, "Failed to resume the audio device");
goto error_recovery;
}
break;
default:
FLUID_LOG(FLUID_ERR, "The audio device error: %s", snd_strerror(n));
goto error_recovery;
} else {
goto error_recovery;
break;
}
} else { /* no error occurred */
offset += n;
}
}
}
}
error_recovery:
FLUID_FREE(left);
@ -801,7 +805,7 @@ fluid_alsa_midi_run(void* d)
(*dev->driver.handler)(dev->driver.data, evt);
}
}
};
}
}
pthread_exit(NULL);
}
@ -901,8 +905,8 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings,
id = id_pid;
}
/* open the sequencer INPUT only, non-blocking */
err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
/* open the sequencer INPUT only */
err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, 0);
if (err < 0) {
FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer");
goto error_recovery;
@ -1074,14 +1078,23 @@ fluid_alsa_seq_run(void* d)
while (dev->status == FLUID_MIDI_LISTENING) {
/* is there something to read? */
n = poll(dev->pfd, dev->npfd, 1); /* use a 1 milliseconds timeout */
n = poll(dev->pfd, dev->npfd, 100); /* use a 100 milliseconds timeout */
if (n < 0) {
perror("poll");
} else if (n > 0) {
/* read new events */
while ((n = snd_seq_event_input(dev->seq_handle, &seq_ev)) >= 0)
} else if (n > 0) { /* check for pending events */
while (snd_seq_event_input_pending (dev->seq_handle, 0))
{
n = snd_seq_event_input(dev->seq_handle, &seq_ev); /* read the events */
/* Negative value indicates an error, ignore interrupted system call
* (-EPERM) and input event buffer overrun (-ENOSPC) */
if (n < 0 && n != -EPERM && n != -ENOSPC) /* FIXME - report buffer overrun? */
{
FLUID_LOG(FLUID_ERR, "Error while reading ALSA sequencer (code=%d)", n);
dev->status = FLUID_MIDI_DONE;
break;
}
switch (seq_ev->type)
{
case SND_SEQ_EVENT_NOTEON:
@ -1132,21 +1145,8 @@ fluid_alsa_seq_run(void* d)
/* send the events to the next link in the chain */
(*dev->driver.handler)(dev->driver.data, &evt);
}
}
if (n < 0) /* Negative value indicates an error */
{
if (n == -EPERM) /* interrupted system call? */
;
else if (n != -ENOSPC) /* input event buffer overrun? */
FLUID_LOG(FLUID_WARN, "ALSA sequencer buffer overrun, lost events");
else
{
FLUID_LOG(FLUID_ERR, "Error occured while reading ALSA sequencer events (code=%d)", n);
dev->status = FLUID_MIDI_DONE;
}
}
}
} /* if poll() > 0 */
} /* while (dev->status == FLUID_MIDI_LISTENING) */
pthread_exit(NULL);
}

View file

@ -18,6 +18,7 @@
* 02111-1307, USA
*/
#include <math.h>
#include "fluid_synth.h"
#include "fluid_sys.h"
@ -58,6 +59,7 @@ int fluid_synth_set_gen2(fluid_synth_t* synth, int chan,
/* has the synth module been initialized? */
static int fluid_synth_initialized = 0;
static void fluid_synth_init(void);
static void init_dither(void);
/* default modulators
* SF2.01 page 52 ff:
@ -156,6 +158,8 @@ fluid_synth_init()
fluid_sys_config();
init_dither();
/* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */
fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */
@ -545,6 +549,7 @@ new_fluid_synth(fluid_settings_t *settings)
synth->cur = FLUID_BUFSIZE;
synth->dither_index = 0;
/* allocate the reverb module */
synth->reverb = new_fluid_revmodel();
@ -1675,6 +1680,27 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
return 0;
}
#define DITHER_SIZE 48000
#define DITHER_CHANNELS 2
static float rand_table[DITHER_CHANNELS][DITHER_SIZE];
static void init_dither(void)
{
float d, dp;
int c, i;
for (c = 0; c < DITHER_CHANNELS; c++) {
dp = 0;
for (i = 0; i < DITHER_SIZE-1; i++) {
d = rand() / (float)RAND_MAX - 0.5f;
rand_table[c][i] = d - dp;
dp = d;
}
rand_table[c][DITHER_SIZE-1] = 0 - dp;
}
}
/*
* fluid_synth_write_s16
*/
@ -1692,6 +1718,7 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
fluid_real_t left_sample;
fluid_real_t right_sample;
double time = fluid_utime();
int di = synth->dither_index;
/* make sure we're playing */
if (synth->state != FLUID_SYNTH_PLAYING) {
@ -1713,27 +1740,24 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
}
left_sample=left_in[cur]* 32767.0f;
right_sample=right_in[cur] * 32767.0f;
left_sample = left_in[cur] * 32767.0f + rand_table[0][di];
right_sample = right_in[cur] * 32767.0f + rand_table[1][di];
di++;
if (di >= DITHER_SIZE) di = 0;
/* digital clipping */
if (left_sample > 32767.0f) {
left_sample = 32767;
}
if (left_sample < -32768.0f) {
left_sample = -32768;
}
if (right_sample > 32767.0f) {
right_sample = 32767;
}
if (right_sample < -32768.0f) {
right_sample = -32768;
}
if (left_sample > 32767.0f) left_sample = 32767.0f;
if (left_sample < -32768.0f) left_sample = -32768.0f;
if (right_sample > 32767.0f) right_sample = 32767.0f;
if (right_sample < -32768.0f) right_sample = -32768.0f;
left_out[j] = (signed short) left_sample;
right_out[k] = (signed short) right_sample;
}
synth->cur = cur;
synth->dither_index = di; /* keep dither buffer continous */
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
@ -1747,6 +1771,55 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
return 0;
}
/*
* fluid_synth_dither_s16
* Converts stereo floating point sample data to signed 16 bit data with
* dithering. This function and fluid_synth_write_s16 should not be used
* on the same synth instance (dithering is per synth).
* Only used internally currently.
*/
void
fluid_synth_dither_s16(fluid_synth_t* synth, int len, float* lin, float* rin,
void* lout, int loff, int lincr,
void* rout, int roff, int rincr)
{
int i, j, k;
signed short* left_out = (signed short*) lout;
signed short* right_out = (signed short*) rout;
double prof_ref = fluid_profile_ref();
fluid_real_t left_sample;
fluid_real_t right_sample;
/* double time = fluid_utime(); */
int di = synth->dither_index;
for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) {
left_sample = lin[i] * 32767.0f + rand_table[0][di];
right_sample = rin[i] * 32767.0f + rand_table[1][di];
di++;
if (di >= DITHER_SIZE) di = 0;
/* digital clipping */
if (left_sample > 32767.0f) left_sample = 32767.0f;
if (left_sample < -32768.0f) left_sample = -32768.0f;
if (right_sample > 32767.0f) right_sample = 32767.0f;
if (right_sample < -32768.0f) right_sample = -32768.0f;
left_out[j] = (signed short) left_sample;
right_out[k] = (signed short) right_sample;
}
synth->dither_index = di; /* keep dither buffer continous */
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
/* FIXME - Should cpu_load be processed here?
time = fluid_utime() - time;
synth->cpu_load = 0.5 * (synth->cpu_load +
time * synth->sample_rate / len / 10000.0);
*/
}
/*
* fluid_synth_one_block

View file

@ -136,6 +136,7 @@ struct _fluid_synth_t
fluid_revmodel_t* reverb;
fluid_chorus_t* chorus;
int cur; /** the current sample in the audio buffers to be output */
int dither_index; /* current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */
char outbuf[256]; /** buffer for message output */
double cpu_load;
@ -210,8 +211,10 @@ int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value);
fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id);
void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id);
/* FIXME: Might be useful in public API */
void fluid_synth_dither_s16(fluid_synth_t* synth, int len, float* lin, float* rin,
void* lout, int loff, int lincr,
void* rout, int roff, int rincr);
/*
* misc
*/