Add profiling command interface (#345)

This patch adds "profiling" commands interface for performance measurement.
Fixes #152
This commit is contained in:
jjceresa 2018-03-12 18:20:46 +01:00 committed by derselbst
parent 1183fbf416
commit 182e593665
9 changed files with 1042 additions and 102 deletions

BIN
doc/FluidProfile_0004.pdf Normal file

Binary file not shown.

View file

@ -228,7 +228,18 @@ static const fluid_cmd_t fluid_commands[] = {
{ "router_par2", "router", fluid_handle_router_par2,
"router_par2 min max mul add filters and maps parameter 2 (vel/cc val)"},
{ "router_end", "router", fluid_handle_router_end,
"router_end closes and commits the current routing rule"}
"router_end closes and commits the current routing rule"},
#if WITH_PROFILING
/* Profiling commands */
{ "profile", "profile", fluid_handle_profile,
"profile Prints default parameters used by prof_start"},
{ "prof_set_notes", "profile", fluid_handle_prof_set_notes,
"prof_set_notes nbr [bank prog] Sets notes number generated by prof_start"},
{ "prof_set_print", "profile", fluid_handle_prof_set_print,
"prof_set_print mode Sets print mode (0:simple, 1:full infos)"},
{ "prof_start", "profile", fluid_handle_prof_start,
"prof_start [n_prof [dur]] Starts n_prof measures of duration(ms) each"}
#endif
};
/**
@ -2996,6 +3007,410 @@ int fluid_handle_ladspa_link(void* data, int ac, char **av, fluid_ostream_t out)
#endif /* LADSPA */
#if WITH_PROFILING
/*
* Locks profile command to prevent simultaneous changes by an other shell
* (may be a server shell (tcp)).
*
* @return FLUID_OK if success , otherwise FLUID_FAILED.
*/
static int fluid_profile_lock_command(fluid_ostream_t out)
{
if (! fluid_atomic_int_compare_and_exchange(&fluid_profile_lock,0,1))
{
fluid_ostream_printf(out,
"profile command in use in another shell. Try later!\n");
return FLUID_FAILED;
}
return FLUID_OK;
}
/*
* Unlocks profile command.
*/
static void fluid_profile_unlock_command(void)
{
fluid_atomic_int_set(&fluid_profile_lock,0);
}
/*
* command: profile
*
* Prints default parameters used by prof_start command.
*
* Notes:0, bank:0, prog:16, print:0, n_prof:1, dur:500 ms.
*
* @return FLUID_OK if success , otherwise FLUID_FAILED.
*/
int
fluid_handle_profile(void* data, int ac, char** av, fluid_ostream_t out)
{
/* locks to prevent simultaneous changes by an other shell */
/* (may be a server shell (tcp)) */
if ( fluid_profile_lock_command(out) == FLUID_FAILED )
{
return FLUID_FAILED;
}
/* prints default parameters */
fluid_ostream_printf(out,
" Notes:%d, bank:%d, prog:%d, print:%d, n_prof:%d, dur:%d ms\n",
fluid_profile_notes, fluid_profile_bank, fluid_profile_prog,
fluid_profile_print,
fluid_profile_n_prof, fluid_profile_dur);
/* unlocks */
fluid_profile_unlock_command();
return FLUID_OK;
}
/*
* command: prof_set_notes nbr [bank prog]
*
* Sets notes number generated by prof_start command.
*
* nbr: notes numbers (generated during command "prof_start").
* bank, prog: preset bank and program number (default value if not specified).
*
* @return FLUID_OK if success , otherwise FLUID_FAILED.
*/
int
fluid_handle_prof_set_notes(void* data, int ac, char** av, fluid_ostream_t out)
{
unsigned short nbr; /* previous parameters */
unsigned char bank, prog; /* previous parameters */
int r; /* return */
/* locks to prevent simultaneous changes by an other shell */
if( fluid_profile_lock_command(out) == FLUID_FAILED )
{
return FLUID_FAILED;
}
/* checks parameters */
if ( ac < 1 )
{
fluid_ostream_printf(out, "profile_notes: too few arguments\n");
fluid_profile_unlock_command();
return FLUID_FAILED;
}
/* gets default parameters */
nbr = fluid_profile_notes, bank = fluid_profile_bank;
prog = fluid_profile_prog;
r = fluid_is_number(av[0]);
if ( r )
{ /* checks nbr */
nbr= atoi(av[0]); /* get nbr parameter */
if ( ac >= 2 )
{ /* [bank prog] are optional */
if ( ac >= 3 )
{
r = fluid_is_number(av[1]) && fluid_is_number(av[2]);
if( r )
{
bank= atoi(av[1]); /* gets bank parameter */
prog= atoi(av[2]); /* gets prog parameter */
}
}
else
{ /* prog is needed */
fluid_ostream_printf(out, "profile_set_notes: too few arguments\n");
fluid_profile_unlock_command();
return FLUID_FAILED;
}
}
}
if ( !r )
{
fluid_ostream_printf(out, "profile_set_notes: invalid argument\n");
fluid_profile_unlock_command();
return FLUID_FAILED;
}
/* Saves new parameters */
fluid_profile_notes = nbr; fluid_profile_bank = bank; fluid_profile_prog = prog;
/* unlocks */
fluid_profile_unlock_command();
return FLUID_OK;
}
/*
* command: prof_set_print mode
*
* The command sets the print mode.
*
* mode: result print mode(used by prof_start").
* 0: simple printing, >0: full printing
*
* @return FLUID_OK if success , otherwise FLUID_FAILED.
*/
int
fluid_handle_prof_set_print(void* data, int ac, char** av, fluid_ostream_t out)
{
int r;
/* locks to prevent simultaneous changes by an other shell */
if ( fluid_profile_lock_command(out) == FLUID_FAILED )
{
return FLUID_FAILED;
}
/* checks parameters */
if ( ac < 1 )
{
fluid_ostream_printf(out, "profile_set_print: too few arguments\n");
fluid_profile_unlock_command();
return FLUID_FAILED;
}
/* gets parameters */
if ( fluid_is_number(av[0]) )
{ /* checks and gets mode */
fluid_profile_print = atoi(av[0]); /* gets and saves mode parameter */
r = FLUID_OK;
}
else
{
fluid_ostream_printf(out, "profile_set_print: invalid argument\n");
r = FLUID_FAILED;
}
/* unlocks */
fluid_profile_unlock_command();
return r;
}
/*
* Generates simultaneous notes for precise profiling.
*
* @param synth, synthesizer instance.
* @param notes, the number of notes to generate.
* @param bank, prog, soundfont bank preset number used.
* @param out, stream output device.
* @return the number of voices generated. It can be lower than notes number
* when the preset have instrument only on few key range.
*/
static unsigned short fluid_profile_send_notes(fluid_synth_t* synth, int notes,
int bank, int prog,
fluid_ostream_t out)
{
int n; /* number of notes generated */
int n_voices,n_actives=0; /* Maximum voices, voices generated */
int n_chan, chan, key ;
/* MIDI channels count and maximum polyphony */
n_chan = fluid_synth_count_midi_channels(synth); /* channels count */
n_voices = fluid_synth_get_polyphony(synth); /* maximum voices */
/* */
fluid_ostream_printf(out,"Generating %d notes, ",notes);
for (n = 0 , key = FLUID_PROFILE_LAST_KEY+1, chan = -1; n < notes; n++, key++)
{
if ( key > FLUID_PROFILE_LAST_KEY )
{ /* next channel */
chan++;
if (chan >= n_chan) break; /* stops generation */
/* select preset */
fluid_synth_bank_select(synth,chan,bank);
fluid_synth_program_change(synth,chan,prog);
key = FLUID_PROFILE_FIRST_KEY;
}
fluid_synth_noteon(synth, chan, key, FLUID_PROFILE_DEFAULT_VEL);
n_actives = fluid_synth_get_active_voice_count(synth); /* running voices */
if ( n_actives >= n_voices )
{
fluid_ostream_printf(out,"max polyphony reached:%d, ", n_voices);
break; /* stops notes generation */
}
}
fluid_ostream_printf(out,"generated voices:%d\n", n_actives);
return n_actives;
}
/*
* command: prof_start [n_prof [dur] ]
*
* Starts n_prof measures of dur duration(ms) each.
*
* n_prof number of measures (default value if not specified).
* dur: measure duration (ms) (defaut value if not specified).
*
* The result of each measure is displayed.
*
* Note: The command ends when the last measure ends or when the user
* cancels the command using <ENTER> key (cancellation using <ENTER>
* is implemented using FLUID_PROFILE_CANCEL macro in fluid_sys.h).
*
* @return FLUID_OK if success , otherwise FLUID_FAILED.
*/
int
fluid_handle_prof_start(void *data, int ac, char** av, fluid_ostream_t out)
{
FLUID_ENTRY_COMMAND(data);
fluid_synth_t* synth = handler->synth;
unsigned short n_prof,n, dur; /* previous parameters */
unsigned int total_dur, rem_dur; /* total and remainder duration (ms) */
unsigned short notes; /* notes number to generate */
int n_actives = 0; /* actives voices */
float gain; /* current gain */
int r = 1; /* checking parameter result */
/* Locks to prevent simultaneous command by an other shell */
if ( fluid_profile_lock_command(out) == FLUID_FAILED)
{
return FLUID_FAILED;
}
/* Gets previous parameters values */
n_prof = fluid_profile_n_prof; /* number of measuses */
dur = fluid_profile_dur; /* duration of one measure (ms) */
/* check parameters */
if (ac >= 1 )
{
r = fluid_is_number(av[0]);
if (r)
{
n_prof= atoi(av[0]); /* gets n_prof parameter */
if (ac >= 2 )
{
r = fluid_is_number(av[1]);
if (r)
{
dur = atoi(av[1]);/* gets dur parameter */
}
}
}
}
if ( !r || n_prof < 1 || dur < 1 )
{
fluid_ostream_printf(out, "profile_start: invalid argument\n");
fluid_profile_unlock_command();
return FLUID_FAILED;
}
/* Saves new parameters */
fluid_profile_n_prof = n_prof;
fluid_profile_dur = dur;
/* Saves current gain */
gain = fluid_synth_get_gain(synth);
/* Generates notes if any */
notes = fluid_profile_notes;
if (notes )
{
/* checks if the synth is playing */
/* Warn the user */
if (fluid_synth_get_active_voice_count(synth))
{
fluid_ostream_printf(out,
"Warning: can't generate notes, please stop any playing\n");
}
else
{
float send_gain;
/* sets low gain before sending notes */
fluid_synth_set_gain(synth, 0.01);
/* sends notes */
n_actives = fluid_profile_send_notes(synth, notes, fluid_profile_bank,
fluid_profile_prog, out);
/* compensates gain to avoid a loud sound */
send_gain = 1.0 * pow(10, (n_actives * FLUID_PROFILE_VOICE_ATTEN)/20 );
fluid_synth_set_gain(synth, send_gain);
/* Before starting profiling immediately we wait to ensures that voices are
currently synthesized by audio rendering API. This ensure that macro
probes will register the expected number of actives voices.
*/
fluid_msleep(200); /* wait 200 ms */
}
}
/* Starts - waits - prints n_prof measures */
fluid_ostream_printf(out,"Number of measures(n_prof):%d, duration of one mesure(dur):%dms\n",
n_prof,dur);
/* Clears any previous <ENTER> pending key */
fluid_profile_is_cancel_req();
total_dur = rem_dur = n_prof * dur;
for ( n = 0 ; n < n_prof; rem_dur -= dur, n++ )
{
unsigned int end_ticks;/* ending position (in ticks) */
unsigned int tm, ts, rm, rs;
int status;
ts = total_dur / 1000;
tm = ts / 60; ts = ts % 60; /* total minutes and seconds */
rs = rem_dur / 1000;
rm = rs / 60; rs = rs % 60; /* remainder minutes and seconds */
/* Prints total and remainder duration */
#ifdef FLUID_PROFILE_CANCEL
fluid_ostream_printf(out,
"\nProfiling time(mm:ss): Total=%d:%d Remainder=%d:%d, press <ENTER> to cancel\n",
tm,ts,rm,rs);
#else
fluid_ostream_printf(out,
"\nProfiling time(mm:ss): Total=%d:%d Remainder=%d:%d\n",
tm,ts,rm,rs);
#endif
/* converts duration(ms) in end position in ticks. */
end_ticks = fluid_atomic_int_get(&synth->ticks_since_start)+
dur * synth->sample_rate / 1000;
/* requests to start the measurement in audio rendering API */
fluid_profile_start_stop(end_ticks, n);
/* waits while running */
do
{
/* passive waiting */
fluid_msleep(500); /* wait 500 ms */
status = fluid_profile_get_status();
}
while ( status == PROFILE_RUNNING );
/* checks if data are ready */
if( status == PROFILE_READY)
{
/* profiling data are ready, prints profile data */
fluid_profiling_print_data(synth->sample_rate,out);
}
/* checks if the measurement has been cancelled */
else if( status == PROFILE_CANCELED || status == PROFILE_STOP )
{
fluid_ostream_printf(out, "Profiling cancelled.\n");
break; /* cancel the command */
}
}
/* Stops voices if any had been generated */
if ( n_actives )
{
fluid_ostream_printf(out, "Stopping %d voices...", n_actives);
fluid_synth_system_reset(synth);
/* waits until all voices become inactives */
do
{
fluid_msleep( 10 ); /* wait 10 ms */
n_actives = fluid_synth_get_active_voice_count(synth);
}
while ( n_actives );
fluid_ostream_printf(out, "voices stopped.\n");
}
/* Restores initial gain */
fluid_synth_set_gain(synth, gain);
/* Unlocks */
fluid_profile_unlock_command();
return FLUID_OK;
}
#endif /* WITH_PROFILING */
int
fluid_is_number(char* a)
{

View file

@ -84,6 +84,13 @@ int fluid_handle_router_chan(void* data, int ac, char** av, fluid_ostream_t out)
int fluid_handle_router_par1(void* data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_router_par2(void* data, int ac, char** av, fluid_ostream_t out);
#if WITH_PROFILING
int fluid_handle_profile(void *data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_prof_set_notes(void *data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_prof_set_print(void *data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_prof_start(void *data, int ac, char** av, fluid_ostream_t out);
#endif
int fluid_handle_basicchannels (void* data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_resetbasicchannels (void* data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_setbasicchannels (void* data, int ac, char** av, fluid_ostream_t out);
@ -96,7 +103,6 @@ int fluid_handle_breathmode(void* data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_setbreathmode(void* data, int ac, char** av, fluid_ostream_t out);
int fluid_handle_sleep(void *data, int ac, char** av, fluid_ostream_t out);
#ifdef LADSPA
int fluid_handle_ladspa_effect(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_ladspa_link(void *data, int ac, char **av, fluid_ostream_t out);

View file

@ -119,7 +119,8 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer)
&mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
&mixer->buffers.fx_right_buf[SYNTH_REVERB_CHANNEL][i]);
}
fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref,0,
mixer->current_blockcount * FLUID_BUFSIZE);
}
if (mixer->fx.with_chorus) {
@ -137,7 +138,8 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer)
&mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
&mixer->buffers.fx_right_buf[SYNTH_CHORUS_CHANNEL][i]);
}
fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref,0,
mixer->current_blockcount * FLUID_BUFSIZE);
}
#ifdef LADSPA
@ -399,7 +401,8 @@ fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer)
for (i=0; i < mixer->active_voices; i++) {
fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs,
bufcount);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref,1,
mixer->current_blockcount * FLUID_BUFSIZE);
}
}
@ -722,6 +725,13 @@ int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t* mixer)
return mixer->buffers.buf_blocks;
}
#if WITH_PROFILING
int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t* mixer)
{
return mixer->active_voices;
}
#endif
#ifdef ENABLE_MIXER_THREADS
static FLUID_INLINE fluid_rvoice_t*
@ -874,7 +884,8 @@ fluid_render_loop_multithread(fluid_rvoice_mixer_t* mixer)
if (rvoice != NULL) {
fluid_profile_ref_var(prof_ref);
fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref,1,
mixer->current_blockcount * FLUID_BUFSIZE);
//test++;
}
else {
@ -973,7 +984,8 @@ fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount)
// Zero buffers
fluid_mixer_buffers_zero(&mixer->buffers);
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices,
mixer->current_blockcount * FLUID_BUFSIZE);
#ifdef ENABLE_MIXER_THREADS
if (mixer->thread_count > 0)
@ -981,7 +993,8 @@ fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount)
else
#endif
fluid_render_loop_singlethread(mixer);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices,
mixer->current_blockcount * FLUID_BUFSIZE);
// Process reverb & chorus

View file

@ -43,7 +43,9 @@ int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t* mixer,
fluid_real_t*** fx_left, fluid_real_t*** fx_right);
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_real_t sample_rate);

View file

@ -3111,6 +3111,7 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
float cpu_load;
fluid_profile_ref_var (prof_ref);
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_enter(synth);
@ -3140,8 +3141,10 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_exit(synth);
fluid_profile(FLUID_PROF_WRITE, prof_ref);
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
len);
return FLUID_OK;
}
@ -3212,8 +3215,8 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
fluid_real_t right_sample;
double time = fluid_utime();
int di;
//double prof_ref_on_block;
float cpu_load;
fluid_profile_ref_var (prof_ref);
if (!synth->eventhandler->is_threadsafe)
@ -3230,12 +3233,9 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
/* fill up the buffers as needed */
if (cur >= synth->curmax) {
int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE;
//prof_ref_on_block = fluid_profile_ref();
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
cur = 0;
//fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
}
left_sample = roundi (left_in[0][cur] * 32766.0f + rand_table[0][di]);
@ -3257,8 +3257,6 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
synth->cur = cur;
synth->dither_index = di; /* keep dither buffer continous */
fluid_profile(FLUID_PROF_WRITE, prof_ref);
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);
@ -3266,6 +3264,9 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
if (!synth->eventhandler->is_threadsafe)
fluid_synth_api_exit(synth);
fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
len);
return 0;
}
@ -3319,7 +3320,7 @@ fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
*dither_index = di; /* keep dither buffer continous */
fluid_profile(FLUID_PROF_WRITE, prof_ref);
fluid_profile(FLUID_PROF_WRITE, prof_ref,0,len);
}
static void
@ -3400,7 +3401,9 @@ fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount)
{float num=1;while (num != 0){num*=0.5;};};
#endif
fluid_check_fpe("??? Remainder of synth_one_block ???");
fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref);
fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref,
fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
blockcount * FLUID_BUFSIZE);
return blockcount;
}

View file

@ -1274,7 +1274,7 @@ fluid_voice_noteoff(fluid_voice_t* voice)
{
fluid_channel_t* channel;
fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref);
fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0);
channel = voice->channel;
@ -1365,7 +1365,7 @@ void fluid_voice_off(fluid_voice_t* voice)
void
fluid_voice_stop(fluid_voice_t* voice)
{
fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref);
fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0);
voice->chan = NO_CHANNEL;

View file

@ -381,17 +381,45 @@ unsigned int fluid_curtime(void)
/**
* Get time in microseconds to be used in relative timing operations.
* @return Unix time in microseconds.
* @return time in microseconds.
* Note: When used for profiling we need high precision clock given
* by g_get_monotonic_time()if available (glib version >= 2.53.3).
* If glib version is too old and in the case of Windows the function
* uses high precision performance counter instead of g_getmonotic_time().
*/
double
fluid_utime (void)
{
GTimeVal timeval;
g_get_current_time (&timeval);
return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28
/* use high precision monotonic clock if available (g_monotonic_time().
* For Winfdows, if this clock is actually implemented as low prec. clock
* (i.e. in case glib is too old), high precision performance counter are
* used instead.
* see: https://bugzilla.gnome.org/show_bug.cgi?id=783340
*/
#if defined(WITH_PROFILING) && defined(WIN32) &&\
/* glib < 2.53.3 */\
(GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3))
/* use high precision performance counter. */
static LARGE_INTEGER freq_cache = {0,0}; /* Performance Frequency */
LARGE_INTEGER perf_cpt;
if (! freq_cache.QuadPart)
{
QueryPerformanceFrequency(&freq_cache); /* Frequency value */
}
QueryPerformanceCounter(&perf_cpt); /* Counter value */
return perf_cpt.QuadPart * 1000000.0/freq_cache.QuadPart; /* time in micros */
#else
return (double) g_get_monotonic_time();
#endif
#else
/* fallback to less precise clock */
GTimeVal timeval;
g_get_current_time (&timeval);
return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
#endif
}
#if defined(WIN32) /* Windoze specific stuff */
@ -520,22 +548,59 @@ void fluid_clear_fpe_i386 (void)
*/
#if WITH_PROFILING
/* Profiling interface beetween profiling command shell and audio rendering API
(FluidProfile_0004.pdf- 3.2.2).
Macros are in defined in fluid_sys.h.
*/
fluid_profile_data_t fluid_profile_data[] =
/*
-----------------------------------------------------------------------------
Shell task side | Profiling interface | Audio task side
-----------------------------------------------------------------------------
profiling | Internal | | | Audio
command <---> |<-- profling -->| Data |<--macros -->| <--> rendering
shell | API | | | API
*/
/* default parameters for shell command "prof_start" in fluid_sys.c */
unsigned short fluid_profile_notes = 0; /* number of generated notes */
/* preset bank:0 prog:16 (organ) */
unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK;
unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG;
/* print mode */
unsigned char fluid_profile_print= FLUID_PROFILE_DEFAULT_PRINT;
/* number of measures */
unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF;
/* measure duration in ms */
unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION;
/* lock between multiple-shell */
fluid_atomic_int_t fluid_profile_lock = 0;
/**/
/*----------------------------------------------
Profiling Data
-----------------------------------------------*/
unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */
unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */
fluid_profile_data_t fluid_profile_data[] = /* Data duration */
{
{ FLUID_PROF_WRITE, "fluid_synth_write_* ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0},
{ FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0}
{"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0},
{"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0},
{"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0},
{"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0}
};
/*----------------------------------------------
Internal profiling API
-----------------------------------------------*/
/* logging profiling data (used on synthesizer instance deletion) */
void fluid_profiling_print(void)
{
int i;
@ -544,24 +609,338 @@ void fluid_profiling_print(void)
FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
for (i = 0; i < FLUID_PROF_LAST; i++) {
if (fluid_profile_data[i].count > 0) {
for (i = 0; i < FLUID_PROFILE_NBR; i++)
{
if (fluid_profile_data[i].count > 0)
{
FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
fluid_profile_data[i].description,
fluid_profile_data[i].min,
fluid_profile_data[i].total / fluid_profile_data[i].count,
fluid_profile_data[i].max);
} else {
FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description);
}
else
{
FLUID_LOG(FLUID_DBG, "%s: no profiling available",
fluid_profile_data[i].description);
}
}
}
/* Macro that returns cpu load in percent (%)
* @dur: duration (micro second).
* @sample_rate: sample_rate used in audio driver (Hz).
* @n_amples: number of samples collected during 'dur' duration.
*/
#define fluid_profile_load(dur,sample_rate,n_samples) \
(dur * sample_rate / n_samples / 10000.0)
/* prints cpu loads only
*
* @param sample_rate the sample rate of audio output.
* @param out output stream device.
*
* ------------------------------------------------------------------------------
* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
* ------------------------------------------------------------------------------
* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
* -------|---------|---------|----------|---------|---------|-------------------
* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612
*/
static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out)
{
unsigned int n_voices; /* voices number */
static const char max_voices_not_available[]=" not available";
const char * pmax_voices;
char max_voices_available[20];
/* First computes data to be printed */
double total, voices, reverb, chorus, all_voices, voice;
/* voices number */
n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices/
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count: 0;
/* total load (%) */
total = fluid_profile_data[FLUID_PROF_WRITE].count ?
fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total,sample_rate,
fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0;
/* reverb load (%) */
reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ?
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total,
sample_rate,
fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0;
/* chorus load (%) */
chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ?
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total,
sample_rate,
fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0;
/* total voices load: total - reverb - chorus (%) */
voices = total - reverb - chorus;
/* One voice load (%): all_voices / n_voices. */
all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total,
sample_rate,
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0;
voice = n_voices ? all_voices / n_voices : 0;
/* estimated maximum voices number */
if(voice > 0)
{
FLUID_SNPRINTF(max_voices_available,sizeof(max_voices_available),
"%17d",(unsigned int) ((100.0 - reverb - chorus)/voice));
pmax_voices = max_voices_available;
}
else
{
pmax_voices = max_voices_not_available;
}
/* Now prints data */
fluid_ostream_printf(out,
" ------------------------------------------------------------------------------\n");
fluid_ostream_printf(out,
" Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n",
sample_rate, 1000000.0/sample_rate);
fluid_ostream_printf(out,
" ------------------------------------------------------------------------------\n");
fluid_ostream_printf(out,
" nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n");
fluid_ostream_printf(out,
" -------|---------|---------|----------|---------|---------|-------------------\n");
fluid_ostream_printf(out,
"%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices,
reverb, chorus, voice, pmax_voices);
}
/*
* prints profiling data (used by profile shell command: prof_start).
* The function is an internal profiling API between the "profile" command
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
*
* @param sample_rate the sample rate of audio output.
* @param out output stream device.
*
* When print mode is 1, the function prints all the informations (see below).
* When print mode is 0, the fonction prints only the cpu loads.
*
* ------------------------------------------------------------------------------
* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond)
* ------------------------------------------------------------------------------
* Code under profiling |Voices| Duration (microsecond) | Load(%)
* | nbr| min| avg| max|
* ---------------------------|------|--------------------------------|----------
* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544
* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100
* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084
* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163
* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839
* synth_one_block:reverb --->| no profiling available
* synth_one_block:chorus --->| no profiling available
* voice:note --------------->| no profiling available
* voice:release ------------>| no profiling available
* ------------------------------------------------------------------------------
* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
* ------------------------------------------------------------------------------
* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
* -------|---------|---------|----------|---------|---------|-------------------
* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612
*/
void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out)
{
int i;
if (fluid_profile_print)
{
/* print all details: Duration(microsecond) and cpu loads(%) */
fluid_ostream_printf(out,
" ------------------------------------------------------------------------------\n");
fluid_ostream_printf(out,
" Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n",
sample_rate, 1000000.0/sample_rate);
fluid_ostream_printf(out,
" ------------------------------------------------------------------------------\n");
fluid_ostream_printf(out,
" Code under profiling |Voices| Duration (microsecond) | Load(%%)\n");
fluid_ostream_printf(out,
" | nbr| min| avg| max|\n");
fluid_ostream_printf(out,
" ---------------------------|------|--------------------------------|----------\n");
for (i = 0; i < FLUID_PROFILE_NBR; i++)
{
unsigned int count = fluid_profile_data[i].count;
if (count > 0)
{ /* data are available */
if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS )
{
double load = fluid_profile_load(fluid_profile_data[i].total,sample_rate,
fluid_profile_data[i].n_samples);
fluid_ostream_printf(out," %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n",
fluid_profile_data[i].description, /* code under profiling */
fluid_profile_data[i].n_voices / count, /* voices number */
fluid_profile_data[i].min, /* minimum duration */
fluid_profile_data[i].total / count, /* average duration */
fluid_profile_data[i].max, /* maximum duration */
load ); /* cpu load */
}
else
{ /* note and release duration */
fluid_ostream_printf(out," %s|%6d|%10.0f|%10.0f|%10.0f|\n",
fluid_profile_data[i].description, /* code under profiling */
fluid_profile_data[i].n_voices / count,
fluid_profile_data[i].min, /* minimum duration */
fluid_profile_data[i].total / count, /* average duration */
fluid_profile_data[i].max); /* maximum duration */
}
}
else
{ /* data aren't available */
fluid_ostream_printf(out,
" %s| no profiling available\n", fluid_profile_data[i].description);
}
}
}
/* prints cpu loads only */
fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */
}
/*
Returns true if the user cancels the current profiling measurement.
Actually this is implemented using the <ENTER> key. To add this functionality:
1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h.
2) Adds the necessary code inside fluid_profile_is_cancel().
When FLUID_PROFILE_CANCEL is not defined, the function return FALSE.
*/
int fluid_profile_is_cancel_req(void)
{
#ifdef FLUID_PROFILE_CANCEL
#if defined(WIN32) /* Windows specific stuff */
/* Profile cancellation is supported for Windows */
/* returns TRUE if key <ENTER> is depressed */
return(GetAsyncKeyState(VK_RETURN) & 0x1);
#elif defined(__OS2__) /* OS/2 specific stuff */
/* Profile cancellation isn't yet supported for OS2 */
/* For OS2, replaces the following line with the function that returns
true when the keyboard key <ENTER> is depressed */
return FALSE; /* default value */
#else /* POSIX stuff */
/* Profile cancellation is supported for Linux */
/* returns true is <ENTER> is depressed */
{
/* Here select() is used to poll the standard input to see if an input
is ready. As the standard input is usually buffered, the user
needs to depress <ENTER> to set the input to a "ready" state.
*/
struct timeval tv;
fd_set fds; /* just one fds need to be polled */
tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */
tv.tv_usec = 0;
FD_ZERO(&fds); /* reset fds */
FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */
return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */
}
#endif /* OS stuff */
#else /* FLUID_PROFILE_CANCEL not defined */
return FALSE; /* default value */
#endif /* FLUID_PROFILE_CANCEL */
}
/**
* Returns status used in shell command "prof_start".
* The function is an internal profiling API between the "profile" command
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
*
* @return status
* - PROFILE_READY profiling data are ready.
* - PROFILE_RUNNING, profiling data are still under acquisition.
* - PROFILE_CANCELED, acquisition has been cancelled by the user.
* - PROFILE_STOP, no acquisition in progress.
*
* When status is PROFILE_RUNNING, the caller can do passive waiting, or other
* work before recalling the function later.
*/
int fluid_profile_get_status(void)
{
/* Checks if user has requested to cancel the current measurement */
/* Cancellation must have precedence over other status */
if(fluid_profile_is_cancel_req())
{
fluid_profile_start_stop(0,0); /* stops the measurement */
return PROFILE_CANCELED;
}
switch(fluid_profile_status)
{
case PROFILE_READY:
return PROFILE_READY; /* profiling data are ready */
case PROFILE_START:
return PROFILE_RUNNING;/* profiling data are under acquisition */
default:
return PROFILE_STOP;
}
}
/**
* Starts or stops profiling measurement.
* The function is an internal profiling API between the "profile" command
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
*
* @param end_tick end position of the measure (in ticks).
* - If end_tick is greater then 0, the function starts a measure if a measure
* isn't running. If a measure is already running, the function does nothing
* and returns.
* - If end_tick is 0, the function stops a measure.
* @param clear_data,
* - If clear_data is 0, the function clears fluid_profile_data before starting
* a measure, otherwise, the data from the started measure will be accumulated
* within fluid_profile_data.
*/
void fluid_profile_start_stop(unsigned int end_ticks, short clear_data)
{
if ( end_ticks ) /* This is a "start" request */
{
/* Checks if a measure is already running */
if (fluid_profile_status != PROFILE_START )
{
short i;
fluid_profile_end_ticks = end_ticks;
/* Clears profile data */
if (clear_data == 0) for (i = 0; i < FLUID_PROFILE_NBR; i++)
{
fluid_profile_data[i].min = 1e10;/* min sets to max value */
fluid_profile_data[i].max = 0; /* maximum sets to min value */
fluid_profile_data[i].total = 0; /* total duration microsecond */
fluid_profile_data[i].count = 0; /* data count */
fluid_profile_data[i].n_voices = 0; /* voices number */
fluid_profile_data[i].n_samples = 0;/* audio samples number */
}
fluid_profile_status = PROFILE_START; /* starts profiling */
}
/* else do nothing when profiling is already started */
}
else /* This is a "stop" request */
{
/* forces the current running profile (if any) to stop */
fluid_profile_status = PROFILE_STOP; /* stops profiling */
}
}
#endif /* WITH_PROFILING */
/***************************************************************
*
* Threads

View file

@ -357,14 +357,128 @@ fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock);
/* Profiling */
#if WITH_PROFILING
/** profiling interface beetween Profiling command shell and Audio
rendering API (FluidProfile_0004.pdf- 3.2.2)
*/
/*
-----------------------------------------------------------------------------
Shell task side | Profiling interface | Audio task side
-----------------------------------------------------------------------------
profiling | Internal | | | Audio
command <---> |<-- profling -->| Data |<--macros -->| <--> rendering
shell | API | | | API
*/
/* default parameters for shell command "prof_start" in fluid_sys.c */
#define FLUID_PROFILE_DEFAULT_BANK 0 /* default bank */
#define FLUID_PROFILE_DEFAULT_PROG 16 /* default prog (organ) */
#define FLUID_PROFILE_FIRST_KEY 12 /* first key generated */
#define FLUID_PROFILE_LAST_KEY 108 /* last key generated */
#define FLUID_PROFILE_DEFAULT_VEL 64 /* default note velocity */
#define FLUID_PROFILE_VOICE_ATTEN -0.04f /* gain attenuation per voice (dB) */
#define FLUID_PROFILE_DEFAULT_PRINT 0 /* default print mode */
#define FLUID_PROFILE_DEFAULT_N_PROF 1 /* default number of measures */
#define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms) */
extern unsigned short fluid_profile_notes; /* number of generated notes */
extern unsigned char fluid_profile_bank; /* bank,prog preset used by */
extern unsigned char fluid_profile_prog; /* generated notes */
extern unsigned char fluid_profile_print; /* print mode */
extern unsigned short fluid_profile_n_prof;/* number of measures */
extern unsigned short fluid_profile_dur; /* measure duration in ms */
extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */
/**/
/*----------------------------------------------
Internal profiling API (in fluid_sys.c)
-----------------------------------------------*/
/* Starts a profiling measure used in shell command "prof_start" */
void fluid_profile_start_stop(unsigned int end_ticks, short clear_data);
/* Returns status used in shell command "prof_start" */
int fluid_profile_get_status(void);
/* Prints profiling data used in shell command "prof_start" */
void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out);
/* Returns True if profiling cancellation has been requested */
int fluid_profile_is_cancel_req(void);
/* For OS that implement <ENTER> key for profile cancellation:
1) Adds #define FLUID_PROFILE_CANCEL
2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c
*/
#if defined(WIN32) /* Profile cancellation is supported for Windows */
#define FLUID_PROFILE_CANCEL
#elif defined(__OS2__) /* OS/2 specific stuff */
/* Profile cancellation isn't yet supported for OS2 */
#else /* POSIX stuff */
#define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */
#include <unistd.h> /* STDIN_FILENO */
#include <sys/select.h> /* select() */
#endif /* posix */
/* logging profiling data (used on synthesizer instance deletion) */
void fluid_profiling_print(void);
/*----------------------------------------------
Profiling Data (in fluid_sys.c)
-----------------------------------------------*/
/** Profiling data. Keep track of min/avg/max values to profile a
piece of code. */
typedef struct _fluid_profile_data_t
{
const char* description; /* name of the piece of code under profiling */
double min, max, total; /* duration (microsecond) */
unsigned int count; /* total count */
unsigned int n_voices; /* voices number */
unsigned int n_samples; /* audio samples number */
} fluid_profile_data_t;
enum
{
/* commands/status (profiling interface) */
PROFILE_STOP, /* command to stop a profiling measure */
PROFILE_START, /* command to start a profile measure */
PROFILE_READY, /* status to signal that a profiling measure has finished
and ready to be printed */
/*- State returned by fluid_profile_get_status() -*/
/* between profiling commands and internal profiling API */
PROFILE_RUNNING, /* a profiling measure is running */
PROFILE_CANCELED,/* a profiling measure has been canceled */
};
/* Data interface */
extern unsigned char fluid_profile_status ; /* command and status */
extern unsigned int fluid_profile_end_ticks; /* ending position (in ticks) */
extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */
/*----------------------------------------------
Probes macros
-----------------------------------------------*/
/** Macro to obtain a time reference used for the profiling */
#define fluid_profile_ref() fluid_utime()
/** Macro to create a variable and assign the current reference time for profiling.
* So we don't get unused variable warnings when profiling is disabled. */
#define fluid_profile_ref_var(name) double name = fluid_utime()
/**
* Profile numbers. List all the pieces of code you want to profile
* Profile identifier numbers. List all the pieces of code you want to profile
* here. Be sure to add an entry in the fluid_profile_data table in
* fluid_sys.c
*/
enum {
enum
{
FLUID_PROF_WRITE,
FLUID_PROF_ONE_BLOCK,
FLUID_PROF_ONE_BLOCK_CLEAR,
@ -374,45 +488,55 @@ enum {
FLUID_PROF_ONE_BLOCK_CHORUS,
FLUID_PROF_VOICE_NOTE,
FLUID_PROF_VOICE_RELEASE,
FLUID_PROF_LAST
FLUID_PROFILE_NBR /* number of profile probes */
};
/** Those macros are used to calculate the min/avg/max. Needs a profile number, a
time reference, the voices and samples number. */
#if WITH_PROFILING
void fluid_profiling_print(void);
/** Profiling data. Keep track of min/avg/max values to execute a
piece of code. */
typedef struct _fluid_profile_data_t {
int num;
char* description;
double min, max, total;
unsigned int count;
} fluid_profile_data_t;
extern fluid_profile_data_t fluid_profile_data[];
/** Macro to obtain a time refence used for the profiling */
#define fluid_profile_ref() fluid_utime()
/** Macro to create a variable and assign the current reference time for profiling.
* So we don't get unused variable warnings when profiling is disabled. */
#define fluid_profile_ref_var(name) double name = fluid_utime()
/** Macro to calculate the min/avg/max. Needs a time refence and a
profile number. */
#define fluid_profile(_num,_ref) { \
/* local macro : acquiere data */
#define fluid_profile_data(_num, _ref, voices, samples)\
{\
double _now = fluid_utime();\
double _delta = _now - _ref;\
fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ? _delta : fluid_profile_data[_num].min; \
fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ? _delta : fluid_profile_data[_num].max; \
fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ?\
_delta : fluid_profile_data[_num].min; \
fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ?\
_delta : fluid_profile_data[_num].max;\
fluid_profile_data[_num].total += _delta;\
fluid_profile_data[_num].count++;\
fluid_profile_data[_num].n_voices += voices;\
fluid_profile_data[_num].n_samples += samples;\
_ref = _now;\
}
/** Macro to collect data, called from inner functions inside audio
rendering API */
#define fluid_profile(_num, _ref, voices, samples)\
{\
if ( fluid_profile_status == PROFILE_START)\
{ /* acquires data */\
fluid_profile_data(_num, _ref, voices, samples)\
}\
}
/** Macro to collect data, called from audio rendering API (fluid_write_xxxx()).
This macro control profiling ending position (in ticks).
*/
#define fluid_profile_write(_num, _ref, voices, samples)\
{\
if (fluid_profile_status == PROFILE_START)\
{\
/* acquires data first: must be done before checking that profile is
finished to ensure at least one valid data sample.
*/\
fluid_profile_data(_num, _ref, voices, samples)\
if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\
{\
/* profiling is finished */\
fluid_profile_status = PROFILE_READY;\
}\
}\
}
#else
@ -420,11 +544,9 @@ extern fluid_profile_data_t fluid_profile_data[];
#define fluid_profiling_print()
#define fluid_profile_ref() 0
#define fluid_profile_ref_var(name)
#define fluid_profile(_num,_ref)
#endif
#define fluid_profile(_num,_ref,voices, samples)
#define fluid_profile_write(_num,_ref, voices, samples)
#endif /* WITH_PROFILING */
/**