mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-10 06:51:54 +00:00
Add profiling command interface (#345)
This patch adds "profiling" commands interface for performance measurement. Fixes #152
This commit is contained in:
parent
1183fbf416
commit
182e593665
9 changed files with 1042 additions and 102 deletions
BIN
doc/FluidProfile_0004.pdf
Normal file
BIN
doc/FluidProfile_0004.pdf
Normal file
Binary file not shown.
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
|
|
Loading…
Reference in a new issue