10 #include <sys/ioctl.h>
13 #include <alsa/asoundlib.h>
15 #include <sys/types.h>
31 // handle 32/64 bits int size issues
35 #define uint32 unsigned int
36 #define uint64 unsigned long int
39 #define int64 long int
43 #define uint32 unsigned int
44 #define uint64 unsigned long long int
47 #define int64 long long int
50 // check 32/64 bits issues are correctly handled
52 #define CHECKINTSIZE \
53 assert(sizeof(int32)==4);\
54 assert(sizeof(int64)==8);
59 // On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
60 // flags to avoid costly denormals
62 #include <xmmintrin.h>
64 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
66 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
69 #define AVOIDDENORMALS
72 //#define BENCHMARKMODE
74 // g++ -Wall -O3 -lm -lpthread -lasound `gtk-config --cflags --libs` test.cpp -o test
76 #define check_error(err) if (err) { printf("%s:%d, alsa error %d : %s\n", __FILE__, __LINE__, err, snd_strerror(err)); exit(1); }
77 #define check_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); exit(1); }
78 #define display_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); }
80 #define max(x,y) (((x)>(y)) ? (x) : (y))
81 #define min(x,y) (((x)<(y)) ? (x) : (y))
83 // abs is now predefined
84 //template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }
87 inline int lsr (int x
, int n
) { return int(((unsigned int)x
) >> n
); }
90 inline int int2pow2 (int x
) { int r
=0; while ((1<<r
)<x
) r
++; return r
; }
94 * Used to set the priority and scheduling of the audio thread
96 bool setRealtimePriority ()
101 struct sched_param param
;
104 pw
= getpwnam ("root");
106 param
.sched_priority
= 99; /* 0 to 99 */
107 err
= sched_setscheduler(0, SCHED_FIFO
, ¶m
);
113 /******************************************************************************
114 *******************************************************************************
118 *******************************************************************************
119 *******************************************************************************/
121 //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
122 //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); }
127 #define BENCHMARKMODE
132 * Returns the number of clock cycles elapsed since the last reset
135 static __inline__ uint64
rdtsc(void)
142 __asm__
__volatile__("rdtsc" : "=a" (count
.i32
[0]), "=d" (count
.i32
[1]));
152 // these values are used to determine the number of clocks in a second
156 // these tables contains the last KMESURE in clocks
157 uint64 starts
[KMESURE
];
158 uint64 stops
[KMESURE
];
160 #define STARTMESURE starts[mesure%KMESURE] = rdtsc();
161 #define STOPMESURE stops[mesure%KMESURE] = rdtsc(); mesure = mesure+1;
169 gettimeofday(&tv1
, &tz
);
170 firstRDTSC
= rdtsc();
176 gettimeofday(&tv2
, &tz
);
181 * return the number of RDTSC clocks per seconds
185 // If the environment variable CLOCKSPERSEC is defined
186 // we use it instead of our own measurement
187 char* str
= getenv("CLOCKSPERSEC");
189 int64 cps
= (int64
) atoll(str
);
190 if (cps
> 1000000000) {
193 return (lastRDTSC
-firstRDTSC
) / (tv2
.tv_sec
- tv1
.tv_sec
) ;
196 return (lastRDTSC
-firstRDTSC
) / (tv2
.tv_sec
- tv1
.tv_sec
) ;
202 * Converts a duration, expressed in RDTSC clocks, into seconds
204 double rdtsc2sec( uint64 clk
)
206 return double(clk
) / double(rdtscpersec());
209 double rdtsc2sec( double clk
)
211 return clk
/ double(rdtscpersec());
216 * Converts RDTSC clocks into Megabytes/seconds according to the
217 * number of frames processed during the period, the number of channels
218 * and 4 bytes samples.
220 double megapersec(int frames
, int chans
, uint64 clk
)
222 return double(frames
*chans
*4)/double(1024*1024*rdtsc2sec(clk
));
227 * Compute the mean value of a vector of measures
229 static uint64
meanValue( vector
<uint64
>::const_iterator a
, vector
<uint64
>::const_iterator b
)
233 while (a
!=b
) { r
+= *a
++; n
++; }
234 return (n
>0) ? r
/n
: 0;
238 * Print the median value (in Megabytes/second) of KMESURE
239 * throughputs measurements
241 void printstats(const char* applname
, int bsize
, int ichans
, int ochans
)
243 assert(mesure
> KMESURE
);
244 vector
<uint64
> V(KMESURE
);
246 for (int i
= 0; i
<KMESURE
; i
++) {
247 V
[i
] = stops
[i
] - starts
[i
];
250 sort(V
.begin(), V
.end());
252 // Mean of 10 best values (gives relatively stable results)
253 uint64 meaval00
= meanValue(V
.begin(), V
.begin()+ 5);
254 uint64 meaval25
= meanValue(V
.begin()+KMESURE
/4 - 2, V
.begin()+KMESURE
/4 + 3);
255 uint64 meaval50
= meanValue(V
.begin()+KMESURE
/2 - 2, V
.begin()+KMESURE
/2 + 3);
256 uint64 meaval75
= meanValue(V
.begin()+3*KMESURE
/4 - 2, V
.begin()+3*KMESURE
/4 + 3);
257 uint64 meaval100
= meanValue(V
.end() - 5, V
.end());
261 << '\t' << megapersec(bsize
, ichans
+ochans
, meaval00
)
262 << '\t' << megapersec(bsize
, ichans
+ochans
, meaval25
)
263 << '\t' << megapersec(bsize
, ichans
+ochans
, meaval50
)
264 << '\t' << megapersec(bsize
, ichans
+ochans
, meaval75
)
265 << '\t' << megapersec(bsize
, ichans
+ochans
, meaval100
)
277 /******************************************************************************
278 *******************************************************************************
282 *******************************************************************************
283 *******************************************************************************/
285 enum { kRead
= 1, kWrite
= 2, kReadWrite
= 3 };
289 * A convenient class to pass parameters to AudioInterface
295 const char* fCardName
;
296 unsigned int fFrequency
;
297 unsigned int fBuffering
;
298 unsigned int fPeriods
;
300 unsigned int fSoftInputs
;
301 unsigned int fSoftOutputs
;
313 AudioParam
& cardName(const char* n
) { fCardName
= n
; return *this; }
314 AudioParam
& frequency(int f
) { fFrequency
= f
; return *this; }
315 AudioParam
& buffering(int fpb
) { fBuffering
= fpb
; return *this; }
316 AudioParam
& periods(int p
) { fPeriods
= p
; return *this; }
317 AudioParam
& inputs(int n
) { fSoftInputs
= n
; return *this; }
318 AudioParam
& outputs(int n
) { fSoftOutputs
= n
; return *this; }
324 * An ALSA audio interface
326 class AudioInterface
: public AudioParam
329 snd_pcm_t
* fOutputDevice
;
330 snd_pcm_t
* fInputDevice
;
331 snd_pcm_hw_params_t
* fInputParams
;
332 snd_pcm_hw_params_t
* fOutputParams
;
334 snd_pcm_format_t fSampleFormat
;
335 snd_pcm_access_t fSampleAccess
;
337 unsigned int fCardInputs
;
338 unsigned int fCardOutputs
;
340 unsigned int fChanInputs
;
341 unsigned int fChanOutputs
;
343 // interleaved mode audiocard buffers
344 void* fInputCardBuffer
;
345 void* fOutputCardBuffer
;
347 // non interleaved mode audiocard buffers
348 void* fInputCardChannels
[256];
349 void* fOutputCardChannels
[256];
351 // non interleaved mod, floating point software buffers
352 float* fInputSoftChannels
[256];
353 float* fOutputSoftChannels
[256];
357 const char* cardName() { return fCardName
; }
358 int frequency() { return fFrequency
; }
359 int buffering() { return fBuffering
; }
360 int periods() { return fPeriods
; }
362 float** inputSoftChannels() { return fInputSoftChannels
; }
363 float** outputSoftChannels() { return fOutputSoftChannels
; }
366 AudioInterface(const AudioParam
& ap
= AudioParam()) : AudioParam(ap
)
377 * Open the audio interface
383 // allocation d'un stream d'entree et d'un stream de sortie
384 err
= snd_pcm_open( &fInputDevice
, fCardName
, SND_PCM_STREAM_CAPTURE
, 0 ); check_error(err
)
385 err
= snd_pcm_open( &fOutputDevice
, fCardName
, SND_PCM_STREAM_PLAYBACK
, 0 ); check_error(err
)
387 // recherche des parametres d'entree
388 err
= snd_pcm_hw_params_malloc ( &fInputParams
); check_error(err
);
389 setAudioParams(fInputDevice
, fInputParams
);
391 // recherche des parametres de sortie
392 err
= snd_pcm_hw_params_malloc ( &fOutputParams
); check_error(err
)
393 setAudioParams(fOutputDevice
, fOutputParams
);
395 // set the number of physical input and output channels close to what we need
396 fCardInputs
= fSoftInputs
;
397 fCardOutputs
= fSoftOutputs
;
399 snd_pcm_hw_params_set_channels_near(fInputDevice
, fInputParams
, &fCardInputs
);
400 snd_pcm_hw_params_set_channels_near(fOutputDevice
, fOutputParams
, &fCardOutputs
);
402 printf("inputs : %u, outputs : %u\n", fCardInputs
, fCardOutputs
);
404 // enregistrement des parametres d'entree-sortie
406 err
= snd_pcm_hw_params (fInputDevice
, fInputParams
); check_error (err
);
407 err
= snd_pcm_hw_params (fOutputDevice
, fOutputParams
); check_error (err
);
409 //assert(snd_pcm_hw_params_get_period_size(fInputParams,NULL) == snd_pcm_hw_params_get_period_size(fOutputParams,NULL));
411 // allocation of alsa buffers
412 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
413 fInputCardBuffer
= calloc(interleavedBufferSize(fInputParams
), 1);
414 fOutputCardBuffer
= calloc(interleavedBufferSize(fOutputParams
), 1);
417 for (unsigned int i
= 0; i
< fCardInputs
; i
++) {
418 fInputCardChannels
[i
] = calloc(noninterleavedBufferSize(fInputParams
), 1);
420 for (unsigned int i
= 0; i
< fCardOutputs
; i
++) {
421 fOutputCardChannels
[i
] = calloc(noninterleavedBufferSize(fOutputParams
), 1);
426 // allocation of floating point buffers needed by the dsp code
428 fChanInputs
= max(fSoftInputs
, fCardInputs
); assert (fChanInputs
< 256);
429 fChanOutputs
= max(fSoftOutputs
, fCardOutputs
); assert (fChanOutputs
< 256);
431 for (unsigned int i
= 0; i
< fChanInputs
; i
++) {
432 fInputSoftChannels
[i
] = (float*) calloc (fBuffering
, sizeof(float));
433 for (int j
= 0; j
< fBuffering
; j
++) {
434 fInputSoftChannels
[i
][j
] = 0.0;
438 for (unsigned int i
= 0; i
< fChanOutputs
; i
++) {
439 fOutputSoftChannels
[i
] = (float*) calloc (fBuffering
, sizeof(float));
440 for (int j
= 0; j
< fBuffering
; j
++) {
441 fOutputSoftChannels
[i
][j
] = 0.0;
449 void setAudioParams(snd_pcm_t
* stream
, snd_pcm_hw_params_t
* params
)
453 // set params record with initial values
454 err
= snd_pcm_hw_params_any ( stream
, params
);
455 check_error_msg(err
, "unable to init parameters")
457 // set alsa access mode (and fSampleAccess field) either to non interleaved or interleaved
459 err
= snd_pcm_hw_params_set_access (stream
, params
, SND_PCM_ACCESS_RW_NONINTERLEAVED
);
461 err
= snd_pcm_hw_params_set_access (stream
, params
, SND_PCM_ACCESS_RW_INTERLEAVED
);
462 check_error_msg(err
, "unable to set access mode neither to non-interleaved or to interleaved");
464 snd_pcm_hw_params_get_access(params
, &fSampleAccess
);
467 // search for 32-bits or 16-bits format
468 err
= snd_pcm_hw_params_set_format (stream
, params
, SND_PCM_FORMAT_S32
);
470 err
= snd_pcm_hw_params_set_format (stream
, params
, SND_PCM_FORMAT_S16
);
471 check_error_msg(err
, "unable to set format to either 32-bits or 16-bits");
473 snd_pcm_hw_params_get_format(params
, &fSampleFormat
);
474 // set sample frequency
475 snd_pcm_hw_params_set_rate_near (stream
, params
, &fFrequency
, 0);
477 // set period and period size (buffering)
478 err
= snd_pcm_hw_params_set_period_size (stream
, params
, fBuffering
, 0);
479 check_error_msg(err
, "period size not available");
481 err
= snd_pcm_hw_params_set_periods (stream
, params
, fPeriods
, 0);
482 check_error_msg(err
, "number of periods not available");
487 ssize_t
interleavedBufferSize (snd_pcm_hw_params_t
* params
)
489 _snd_pcm_format format
; snd_pcm_hw_params_get_format(params
, &format
);
490 snd_pcm_uframes_t psize
; snd_pcm_hw_params_get_period_size(params
, &psize
, NULL
);
491 unsigned int channels
; snd_pcm_hw_params_get_channels(params
, &channels
);
492 ssize_t bsize
= snd_pcm_format_size (format
, psize
* channels
);
497 ssize_t
noninterleavedBufferSize (snd_pcm_hw_params_t
* params
)
499 _snd_pcm_format format
; snd_pcm_hw_params_get_format(params
, &format
);
500 snd_pcm_uframes_t psize
; snd_pcm_hw_params_get_period_size(params
, &psize
, NULL
);
501 ssize_t bsize
= snd_pcm_format_size (format
, psize
);
513 * Read audio samples from the audio card. Convert samples to floats and take
514 * care of interleaved buffers
519 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
521 int count
= snd_pcm_readi(fInputDevice
, fInputCardBuffer
, fBuffering
);
523 display_error_msg(count
, "reading samples");
524 int err
= snd_pcm_prepare(fInputDevice
);
525 check_error_msg(err
, "preparing input stream");
528 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
530 short* buffer16b
= (short*) fInputCardBuffer
;
531 for (int s
= 0; s
< fBuffering
; s
++) {
532 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
533 fInputSoftChannels
[c
][s
] = float(buffer16b
[c
+ s
*fCardInputs
])*(1.0/float(SHRT_MAX
));
537 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
539 int* buffer32b
= (int*) fInputCardBuffer
;
540 for (int s
= 0; s
< fBuffering
; s
++) {
541 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
542 fInputSoftChannels
[c
][s
] = float(buffer32b
[c
+ s
*fCardInputs
])*(1.0/float(INT_MAX
));
547 printf("unrecognized input sample format : %u\n", fSampleFormat
);
551 } else if (fSampleAccess
== SND_PCM_ACCESS_RW_NONINTERLEAVED
) {
553 int count
= snd_pcm_readn(fInputDevice
, fInputCardChannels
, fBuffering
);
555 display_error_msg(count
, "reading samples");
556 int err
= snd_pcm_prepare(fInputDevice
);
557 check_error_msg(err
, "preparing input stream");
560 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
562 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
563 short* chan16b
= (short*) fInputCardChannels
[c
];
564 for (int s
= 0; s
< fBuffering
; s
++) {
565 fInputSoftChannels
[c
][s
] = float(chan16b
[s
])*(1.0/float(SHRT_MAX
));
569 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
571 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
572 int* chan32b
= (int*) fInputCardChannels
[c
];
573 for (int s
= 0; s
< fBuffering
; s
++) {
574 fInputSoftChannels
[c
][s
] = float(chan32b
[s
])*(1.0/float(INT_MAX
));
579 printf("unrecognized input sample format : %u\n", fSampleFormat
);
584 check_error_msg(-10000, "unknow access mode");
592 * write the output soft channels to the audio card. Convert sample
593 * format and interleaves buffers when needed
599 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
601 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
603 short* buffer16b
= (short*) fOutputCardBuffer
;
604 for (int f
= 0; f
< fBuffering
; f
++) {
605 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
606 float x
= fOutputSoftChannels
[c
][f
];
607 buffer16b
[c
+ f
*fCardOutputs
] = short( max(min(x
,1.0),-1.0) * float(SHRT_MAX
) ) ;
611 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
613 int* buffer32b
= (int*) fOutputCardBuffer
;
614 for (int f
= 0; f
< fBuffering
; f
++) {
615 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
616 float x
= fOutputSoftChannels
[c
][f
];
617 buffer32b
[c
+ f
*fCardOutputs
] = int( max(min(x
,1.0),-1.0) * float(INT_MAX
) ) ;
622 printf("unrecognized output sample format : %u\n", fSampleFormat
);
626 int count
= snd_pcm_writei(fOutputDevice
, fOutputCardBuffer
, fBuffering
);
628 display_error_msg(count
, "w3");
629 int err
= snd_pcm_prepare(fOutputDevice
);
630 check_error_msg(err
, "preparing output stream");
635 } else if (fSampleAccess
== SND_PCM_ACCESS_RW_NONINTERLEAVED
) {
637 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
639 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
640 short* chan16b
= (short*) fOutputCardChannels
[c
];
641 for (int f
= 0; f
< fBuffering
; f
++) {
642 float x
= fOutputSoftChannels
[c
][f
];
643 chan16b
[f
] = short( max(min(x
,1.0),-1.0) * float(SHRT_MAX
) ) ;
647 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
649 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
650 int* chan32b
= (int*) fOutputCardChannels
[c
];
651 for (int f
= 0; f
< fBuffering
; f
++) {
652 float x
= fOutputSoftChannels
[c
][f
];
653 chan32b
[f
] = int( max(min(x
,1.0),-1.0) * float(INT_MAX
) ) ;
659 printf("unrecognized output sample format : %u\n", fSampleFormat
);
663 int count
= snd_pcm_writen(fOutputDevice
, fOutputCardChannels
, fBuffering
);
665 display_error_msg(count
, "w3");
666 int err
= snd_pcm_prepare(fOutputDevice
);
667 check_error_msg(err
, "preparing output stream");
672 check_error_msg(-10000, "unknow access mode");
679 * print short information on the audio device
684 snd_ctl_card_info_t
* card_info
;
685 snd_ctl_t
* ctl_handle
;
686 err
= snd_ctl_open (&ctl_handle
, fCardName
, 0); check_error(err
);
687 snd_ctl_card_info_alloca (&card_info
);
688 err
= snd_ctl_card_info(ctl_handle
, card_info
); check_error(err
);
689 printf("%s|%d|%d|%d|%d|%s\n",
690 snd_ctl_card_info_get_driver(card_info
),
691 fCardInputs
, fCardOutputs
,
692 fFrequency
, fBuffering
,
693 snd_pcm_format_name((_snd_pcm_format
)fSampleFormat
));
697 * print more detailled information on the audio device
702 snd_ctl_card_info_t
* card_info
;
703 snd_ctl_t
* ctl_handle
;
705 printf("Audio Interface Description :\n");
706 printf("Sampling Frequency : %d, Sample Format : %s, buffering : %d\n",
707 fFrequency
, snd_pcm_format_name((_snd_pcm_format
)fSampleFormat
), fBuffering
);
708 printf("Software inputs : %2d, Software outputs : %2d\n", fSoftInputs
, fSoftOutputs
);
709 printf("Hardware inputs : %2d, Hardware outputs : %2d\n", fCardInputs
, fCardOutputs
);
710 printf("Channel inputs : %2d, Channel outputs : %2d\n", fChanInputs
, fChanOutputs
);
712 // affichage des infos de la carte
713 err
= snd_ctl_open (&ctl_handle
, fCardName
, 0); check_error(err
);
714 snd_ctl_card_info_alloca (&card_info
);
715 err
= snd_ctl_card_info(ctl_handle
, card_info
); check_error(err
);
716 printCardInfo(card_info
);
718 // affichage des infos liees aux streams d'entree-sortie
719 if (fSoftInputs
> 0) printHWParams(fInputParams
);
720 if (fSoftOutputs
> 0) printHWParams(fOutputParams
);
723 void printCardInfo(snd_ctl_card_info_t
* ci
)
725 printf("Card info (address : %p)\n", ci
);
726 printf("\tID = %s\n", snd_ctl_card_info_get_id(ci
));
727 printf("\tDriver = %s\n", snd_ctl_card_info_get_driver(ci
));
728 printf("\tName = %s\n", snd_ctl_card_info_get_name(ci
));
729 printf("\tLongName = %s\n", snd_ctl_card_info_get_longname(ci
));
730 printf("\tMixerName = %s\n", snd_ctl_card_info_get_mixername(ci
));
731 printf("\tComponents = %s\n", snd_ctl_card_info_get_components(ci
));
732 printf("--------------\n");
735 void printHWParams( snd_pcm_hw_params_t
* params
)
737 printf("HW Params info (address : %p)\n", params
);
739 printf("\tChannels = %d\n", snd_pcm_hw_params_get_channels(params
));
740 printf("\tFormat = %s\n", snd_pcm_format_name((_snd_pcm_format
)snd_pcm_hw_params_get_format(params
)));
741 printf("\tAccess = %s\n", snd_pcm_access_name((_snd_pcm_access
)snd_pcm_hw_params_get_access(params
)));
742 printf("\tRate = %d\n", snd_pcm_hw_params_get_rate(params
, NULL
));
743 printf("\tPeriods = %d\n", snd_pcm_hw_params_get_periods(params
, NULL
));
744 printf("\tPeriod size = %d\n", (int)snd_pcm_hw_params_get_period_size(params
, NULL
));
745 printf("\tPeriod time = %d\n", snd_pcm_hw_params_get_period_time(params
, NULL
));
746 printf("\tBuffer size = %d\n", (int)snd_pcm_hw_params_get_buffer_size(params
));
747 printf("\tBuffer time = %d\n", snd_pcm_hw_params_get_buffer_time(params
, NULL
));
749 printf("--------------\n");
759 /******************************************************************************
760 *******************************************************************************
762 GRAPHIC USER INTERFACE (v2)
765 *******************************************************************************
766 *******************************************************************************/
773 struct Meta
: map
<const char*, const char*>
775 void declare (const char* key
, const char* value
) { (*this)[key
]=value
; }
780 typedef void (*uiCallback
)(float val
, void* data
);
783 * Graphic User Interface : abstract definition
788 typedef list
<uiItem
*> clist
;
789 typedef map
<float*, clist
*> zmap
;
792 static list
<UI
*> fGuiList
;
798 UI() : fStopped(false) {
799 fGuiList
.push_back(this);
803 // suppression de this dans fGuiList
806 // -- zone management
808 void registerZone(float* z
, uiItem
* c
)
810 if (fZoneMap
.find(z
) == fZoneMap
.end()) fZoneMap
[z
] = new clist();
811 fZoneMap
[z
]->push_back(c
);
814 // -- saveState(filename) : save the value of every zone to a file
816 void saveState(const char* filename
)
818 ofstream
f(filename
);
820 for (zmap::iterator i
=fZoneMap
.begin(); i
!=fZoneMap
.end(); i
++) {
821 f
<< *(i
->first
) << ' ';
828 // -- recallState(filename) : load the value of every zone from a file
830 void recallState(const char* filename
)
832 ifstream
f(filename
);
834 for (zmap::iterator i
=fZoneMap
.begin(); i
!=fZoneMap
.end(); i
++) {
841 void updateAllZones();
843 void updateZone(float* z
);
845 static void updateAllGuis()
847 list
<UI
*>::iterator g
;
848 for (g
= fGuiList
.begin(); g
!= fGuiList
.end(); g
++) {
849 (*g
)->updateAllZones();
855 virtual void addButton(const char* label
, float* zone
) = 0;
856 virtual void addToggleButton(const char* label
, float* zone
) = 0;
857 virtual void addCheckButton(const char* label
, float* zone
) = 0;
858 virtual void addVerticalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
859 virtual void addHorizontalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
860 virtual void addNumEntry(const char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
862 // -- passive widgets
864 virtual void addNumDisplay(const char* label
, float* zone
, int precision
) = 0;
865 virtual void addTextDisplay(const char* label
, float* zone
, char* names
[], float min
, float max
) = 0;
866 virtual void addHorizontalBargraph(const char* label
, float* zone
, float min
, float max
) = 0;
867 virtual void addVerticalBargraph(const char* label
, float* zone
, float min
, float max
) = 0;
869 void addCallback(float* zone
, uiCallback foo
, void* data
);
871 // -- widget's layouts
873 virtual void openFrameBox(const char* label
) = 0;
874 virtual void openTabBox(const char* label
) = 0;
875 virtual void openHorizontalBox(const char* label
) = 0;
876 virtual void openVerticalBox(const char* label
) = 0;
877 virtual void closeBox() = 0;
879 virtual void show() = 0;
880 virtual void run() = 0;
882 void stop() { fStopped
= true; }
883 bool stopped() { return fStopped
; }
885 virtual void declare(float* zone
, const char* key
, const char* value
) {}
890 * User Interface Item: abstract definition
901 uiItem (UI
* ui
, float* zone
) : fGUI(ui
), fZone(zone
), fCache(-123456.654321)
903 ui
->registerZone(zone
, this);
911 void modifyZone(float v
)
916 fGUI
->updateZone(fZone
);
920 float cache() { return fCache
; }
921 virtual void reflectZone() = 0;
929 struct uiCallbackItem
: public uiItem
931 uiCallback fCallback
;
934 uiCallbackItem(UI
* ui
, float* zone
, uiCallback foo
, void* data
)
935 : uiItem(ui
, zone
), fCallback(foo
), fData(data
) {}
937 virtual void reflectZone() {
945 * Update all user items reflecting zone z
948 inline void UI::updateZone(float* z
)
951 clist
* l
= fZoneMap
[z
];
952 for (clist::iterator c
= l
->begin(); c
!= l
->end(); c
++) {
953 if ((*c
)->cache() != v
) (*c
)->reflectZone();
959 * Update all user items not up to date
962 inline void UI::updateAllZones()
964 for (zmap::iterator m
= fZoneMap
.begin(); m
!= fZoneMap
.end(); m
++) {
966 clist
* l
= m
->second
;
968 for (clist::iterator c
= l
->begin(); c
!= l
->end(); c
++) {
969 if ((*c
)->cache() != v
) (*c
)->reflectZone();
974 inline void UI::addCallback(float* zone
, uiCallback foo
, void* data
)
976 new uiCallbackItem(this, zone
, foo
, data
);
980 /******************************************************************************
981 *******************************************************************************
983 GRAPHIC USER INTERFACE
986 *******************************************************************************
987 *******************************************************************************/
991 #define stackSize 256
995 #define kSingleMode 0
1000 class GTKUI
: public UI
1003 static bool fInitialized
;
1004 static list
<UI
*> fGuiList
;
1009 GtkWidget
* fBox
[stackSize
];
1010 int fMode
[stackSize
];
1013 GtkWidget
* addWidget(const char* label
, GtkWidget
* w
);
1014 virtual void pushBox(int mode
, GtkWidget
* w
);
1019 static const gboolean expand
= TRUE
;
1020 static const gboolean fill
= TRUE
;
1021 static const gboolean homogene
= FALSE
;
1023 GTKUI(char * name
, int* pargc
, char*** pargv
);
1027 virtual void openFrameBox(const char* label
);
1028 virtual void openTabBox(const char* label
= "");
1029 virtual void openHorizontalBox(const char* label
= "");
1030 virtual void openVerticalBox(const char* label
= "");
1032 virtual void closeBox();
1034 // -- active widgets
1036 virtual void addButton(const char* label
, float* zone
);
1037 virtual void addToggleButton(const char* label
, float* zone
);
1038 virtual void addCheckButton(const char* label
, float* zone
);
1039 virtual void addVerticalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
);
1040 virtual void addHorizontalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
);
1041 virtual void addNumEntry(const char* label
, float* zone
, float init
, float min
, float max
, float step
);
1043 // -- passive display widgets
1045 virtual void addNumDisplay(const char* label
, float* zone
, int precision
);
1046 virtual void addTextDisplay(const char* label
, float* zone
, char* names
[], float min
, float max
);
1047 virtual void addHorizontalBargraph(const char* label
, float* zone
, float min
, float max
);
1048 virtual void addVerticalBargraph(const char* label
, float* zone
, float min
, float max
);
1050 virtual void show();
1057 /******************************************************************************
1058 *******************************************************************************
1060 GRAPHIC USER INTERFACE (v2)
1063 *******************************************************************************
1064 *******************************************************************************/
1066 // global static fields
1068 bool GTKUI::fInitialized
= false;
1069 list
<UI
*> UI::fGuiList
;
1073 static gint
delete_event( GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
1078 static void destroy_event( GtkWidget
*widget
, gpointer data
)
1084 GTKUI::GTKUI(char * name
, int* pargc
, char*** pargv
)
1086 if (!fInitialized
) {
1087 gtk_init(pargc
, pargv
);
1088 fInitialized
= true;
1091 fWindow
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
1092 //gtk_container_set_border_width (GTK_CONTAINER (fWindow), 10);
1093 gtk_window_set_title (GTK_WINDOW (fWindow
), name
);
1094 gtk_signal_connect (GTK_OBJECT (fWindow
), "delete_event", GTK_SIGNAL_FUNC (delete_event
), NULL
);
1095 gtk_signal_connect (GTK_OBJECT (fWindow
), "destroy", GTK_SIGNAL_FUNC (destroy_event
), NULL
);
1098 fBox
[fTop
] = gtk_vbox_new (homogene
, 4);
1099 fMode
[fTop
] = kBoxMode
;
1100 gtk_container_add (GTK_CONTAINER (fWindow
), fBox
[fTop
]);
1104 // empilement des boites
1106 void GTKUI::pushBox(int mode
, GtkWidget
* w
)
1108 assert(++fTop
< stackSize
);
1113 void GTKUI::closeBox()
1115 assert(--fTop
>= 0);
1119 // les differentes boites
1121 void GTKUI::openFrameBox(const char* label
)
1123 GtkWidget
* box
= gtk_frame_new (label
);
1124 //gtk_container_set_border_width (GTK_CONTAINER (box), 10);
1126 pushBox(kSingleMode
, addWidget(label
, box
));
1129 void GTKUI::openTabBox(const char* label
)
1131 pushBox(kTabMode
, addWidget(label
, gtk_notebook_new ()));
1134 void GTKUI::openHorizontalBox(const char* label
)
1136 GtkWidget
* box
= gtk_hbox_new (homogene
, 4);
1137 gtk_container_set_border_width (GTK_CONTAINER (box
), 10);
1139 if (fMode
[fTop
] != kTabMode
&& label
[0] != 0) {
1140 GtkWidget
* frame
= addWidget(label
, gtk_frame_new (label
));
1141 gtk_container_add (GTK_CONTAINER(frame
), box
);
1142 gtk_widget_show(box
);
1143 pushBox(kBoxMode
, box
);
1145 pushBox(kBoxMode
, addWidget(label
, box
));
1149 void GTKUI::openVerticalBox(const char* label
)
1151 GtkWidget
* box
= gtk_vbox_new (homogene
, 4);
1152 gtk_container_set_border_width (GTK_CONTAINER (box
), 10);
1154 if (fMode
[fTop
] != kTabMode
&& label
[0] != 0) {
1155 GtkWidget
* frame
= addWidget(label
, gtk_frame_new (label
));
1156 gtk_container_add (GTK_CONTAINER(frame
), box
);
1157 gtk_widget_show(box
);
1158 pushBox(kBoxMode
, box
);
1160 pushBox(kBoxMode
, addWidget(label
, box
));
1164 GtkWidget
* GTKUI::addWidget(const char* label
, GtkWidget
* w
)
1166 switch (fMode
[fTop
]) {
1167 case kSingleMode
: gtk_container_add (GTK_CONTAINER(fBox
[fTop
]), w
); break;
1168 case kBoxMode
: gtk_box_pack_start (GTK_BOX(fBox
[fTop
]), w
, expand
, fill
, 0); break;
1169 case kTabMode
: gtk_notebook_append_page (GTK_NOTEBOOK(fBox
[fTop
]), w
, gtk_label_new(label
)); break;
1171 gtk_widget_show (w
);
1175 // --------------------------- Press button ---------------------------
1177 struct uiButton
: public uiItem
1181 uiButton (UI
* ui
, float* zone
, GtkButton
* b
) : uiItem(ui
, zone
), fButton(b
) {}
1183 static void pressed( GtkWidget
*widget
, gpointer data
)
1185 uiItem
* c
= (uiItem
*) data
;
1189 static void released( GtkWidget
*widget
, gpointer data
)
1191 uiItem
* c
= (uiItem
*) data
;
1195 virtual void reflectZone()
1199 if (v
> 0.0) gtk_button_pressed(fButton
); else gtk_button_released(fButton
);
1203 void GTKUI::addButton(const char* label
, float* zone
)
1206 GtkWidget
* button
= gtk_button_new_with_label (label
);
1207 addWidget(label
, button
);
1209 uiButton
* c
= new uiButton(this, zone
, GTK_BUTTON(button
));
1211 gtk_signal_connect (GTK_OBJECT (button
), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed
), (gpointer
) c
);
1212 gtk_signal_connect (GTK_OBJECT (button
), "released", GTK_SIGNAL_FUNC (uiButton::released
), (gpointer
) c
);
1216 // --------------------------- Toggle Buttons ---------------------------
1218 struct uiToggleButton
: public uiItem
1220 GtkToggleButton
* fButton
;
1222 uiToggleButton(UI
* ui
, float* zone
, GtkToggleButton
* b
) : uiItem(ui
, zone
), fButton(b
) {}
1224 static void toggled (GtkWidget
*widget
, gpointer data
)
1226 float v
= (GTK_TOGGLE_BUTTON (widget
)->active
) ? 1.0 : 0.0;
1227 ((uiItem
*)data
)->modifyZone(v
);
1230 virtual void reflectZone()
1234 gtk_toggle_button_set_active(fButton
, v
> 0.0);
1238 void GTKUI::addToggleButton(const char* label
, float* zone
)
1241 GtkWidget
* button
= gtk_toggle_button_new_with_label (label
);
1242 addWidget(label
, button
);
1244 uiToggleButton
* c
= new uiToggleButton(this, zone
, GTK_TOGGLE_BUTTON(button
));
1245 gtk_signal_connect (GTK_OBJECT (button
), "toggled", GTK_SIGNAL_FUNC (uiToggleButton::toggled
), (gpointer
) c
);
1249 // --------------------------- Check Button ---------------------------
1251 struct uiCheckButton
: public uiItem
1253 GtkToggleButton
* fButton
;
1255 uiCheckButton(UI
* ui
, float* zone
, GtkToggleButton
* b
) : uiItem(ui
, zone
), fButton(b
) {}
1257 static void toggled (GtkWidget
*widget
, gpointer data
)
1259 float v
= (GTK_TOGGLE_BUTTON (widget
)->active
) ? 1.0 : 0.0;
1260 ((uiItem
*)data
)->modifyZone(v
);
1263 virtual void reflectZone()
1267 gtk_toggle_button_set_active(fButton
, v
> 0.0);
1271 void GTKUI::addCheckButton(const char* label
, float* zone
)
1274 GtkWidget
* button
= gtk_check_button_new_with_label (label
);
1275 addWidget(label
, button
);
1277 uiCheckButton
* c
= new uiCheckButton(this, zone
, GTK_TOGGLE_BUTTON(button
));
1278 gtk_signal_connect (GTK_OBJECT (button
), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled
), (gpointer
) c
);
1282 // --------------------------- Adjustmenty based widgets ---------------------------
1284 struct uiAdjustment
: public uiItem
1286 GtkAdjustment
* fAdj
;
1288 uiAdjustment(UI
* ui
, float* zone
, GtkAdjustment
* adj
) : uiItem(ui
, zone
), fAdj(adj
) {}
1290 static void changed (GtkWidget
*widget
, gpointer data
)
1292 float v
= GTK_ADJUSTMENT (widget
)->value
;
1293 ((uiItem
*)data
)->modifyZone(v
);
1296 virtual void reflectZone()
1300 gtk_adjustment_set_value(fAdj
, v
);
1304 static int precision(double n
)
1306 if (n
< 0.009999) return 3;
1307 else if (n
< 0.099999) return 2;
1308 else if (n
< 0.999999) return 1;
1312 // -------------------------- Vertical Slider -----------------------------------
1314 void GTKUI::addVerticalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
)
1317 GtkObject
* adj
= gtk_adjustment_new(init
, min
, max
, step
, 10*step
, 0);
1319 uiAdjustment
* c
= new uiAdjustment(this, zone
, GTK_ADJUSTMENT(adj
));
1321 gtk_signal_connect (GTK_OBJECT (adj
), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed
), (gpointer
) c
);
1323 GtkWidget
* slider
= gtk_vscale_new (GTK_ADJUSTMENT(adj
));
1324 gtk_range_set_inverted (GTK_RANGE(slider
), TRUE
);
1325 gtk_scale_set_digits(GTK_SCALE(slider
), precision(step
));
1326 gtk_widget_set_usize(slider
, -1, 160);
1328 openFrameBox(label
);
1329 addWidget(label
, slider
);
1333 // -------------------------- Horizontal Slider -----------------------------------
1335 void GTKUI::addHorizontalSlider(const char* label
, float* zone
, float init
, float min
, float max
, float step
)
1338 GtkObject
* adj
= gtk_adjustment_new(init
, min
, max
, step
, 10*step
, 0);
1340 uiAdjustment
* c
= new uiAdjustment(this, zone
, GTK_ADJUSTMENT(adj
));
1342 gtk_signal_connect (GTK_OBJECT (adj
), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed
), (gpointer
) c
);
1344 GtkWidget
* slider
= gtk_hscale_new (GTK_ADJUSTMENT(adj
));
1345 gtk_scale_set_digits(GTK_SCALE(slider
), precision(step
));
1346 gtk_widget_set_usize(slider
, 160, -1);
1348 openFrameBox(label
);
1349 addWidget(label
, slider
);
1354 // ------------------------------ Num Entry -----------------------------------
1356 void GTKUI::addNumEntry(const char* label
, float* zone
, float init
, float min
, float max
, float step
)
1359 GtkObject
* adj
= gtk_adjustment_new(init
, min
, max
, step
, 10*step
, step
);
1361 uiAdjustment
* c
= new uiAdjustment(this, zone
, GTK_ADJUSTMENT(adj
));
1363 gtk_signal_connect (GTK_OBJECT (adj
), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed
), (gpointer
) c
);
1365 GtkWidget
* spinner
= gtk_spin_button_new (GTK_ADJUSTMENT(adj
), 0.005, precision(step
));
1367 //gtk_widget_set_usize(slider, 160, -1);
1368 openFrameBox(label
);
1369 addWidget(label
, spinner
);
1374 // ========================== passive widgets ===============================
1377 // ------------------------------ Progress Bar -----------------------------------
1379 struct uiBargraph
: public uiItem
1381 GtkProgressBar
* fProgressBar
;
1385 uiBargraph(UI
* ui
, float* zone
, GtkProgressBar
* pbar
, float lo
, float hi
)
1386 : uiItem(ui
, zone
), fProgressBar(pbar
), fMin(lo
), fMax(hi
) {}
1388 float scale(float v
) { return (v
-fMin
)/(fMax
-fMin
); }
1390 virtual void reflectZone()
1394 gtk_progress_bar_set_fraction(fProgressBar
, scale(v
));
1400 void GTKUI::addVerticalBargraph(const char* label
, float* zone
, float lo
, float hi
)
1402 GtkWidget
* pb
= gtk_progress_bar_new();
1403 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb
), GTK_PROGRESS_BOTTOM_TO_TOP
);
1404 gtk_widget_set_size_request(pb
, 8, -1);
1405 new uiBargraph(this, zone
, GTK_PROGRESS_BAR(pb
), lo
, hi
);
1406 openFrameBox(label
);
1407 addWidget(label
, pb
);
1412 void GTKUI::addHorizontalBargraph(const char* label
, float* zone
, float lo
, float hi
)
1414 GtkWidget
* pb
= gtk_progress_bar_new();
1415 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb
), GTK_PROGRESS_LEFT_TO_RIGHT
);
1416 gtk_widget_set_size_request(pb
, -1, 8);
1417 new uiBargraph(this, zone
, GTK_PROGRESS_BAR(pb
), lo
, hi
);
1418 openFrameBox(label
);
1419 addWidget(label
, pb
);
1424 // ------------------------------ Num Display -----------------------------------
1426 struct uiNumDisplay
: public uiItem
1431 uiNumDisplay(UI
* ui
, float* zone
, GtkLabel
* label
, int precision
)
1432 : uiItem(ui
, zone
), fLabel(label
), fPrecision(precision
) {}
1434 virtual void reflectZone()
1439 if (fPrecision
<= 0) {
1440 snprintf(s
, 63, "%d", int(v
));
1441 } else if (fPrecision
>3) {
1442 snprintf(s
, 63, "%f", v
);
1444 const char* format
[] = {"%.1f", "%.2f", "%.3f"};
1445 snprintf(s
, 63, format
[fPrecision
-1], v
);
1447 gtk_label_set_text(fLabel
, s
);
1452 void GTKUI::addNumDisplay(const char* label
, float* zone
, int precision
)
1454 GtkWidget
* lw
= gtk_label_new("");
1455 new uiNumDisplay(this, zone
, GTK_LABEL(lw
), precision
);
1456 openFrameBox(label
);
1457 addWidget(label
, lw
);
1462 // ------------------------------ Text Display -----------------------------------
1464 struct uiTextDisplay
: public uiItem
1473 uiTextDisplay (UI
* ui
, float* zone
, GtkLabel
* label
, char* names
[], float lo
, float hi
)
1474 : uiItem(ui
, zone
), fLabel(label
), fNames(names
), fMin(lo
), fMax(hi
)
1477 while (fNames
[fNum
] != 0) fNum
++;
1480 virtual void reflectZone()
1485 int idx
= int(fNum
*(v
-fMin
)/(fMax
-fMin
));
1487 if (idx
< 0) idx
= 0;
1488 else if (idx
>= fNum
) idx
= fNum
-1;
1490 gtk_label_set_text(fLabel
, fNames
[idx
]);
1495 void GTKUI::addTextDisplay(const char* label
, float* zone
, char* names
[], float lo
, float hi
)
1497 GtkWidget
* lw
= gtk_label_new("");
1498 new uiTextDisplay (this, zone
, GTK_LABEL(lw
), names
, lo
, hi
);
1499 openFrameBox(label
);
1500 addWidget(label
, lw
);
1509 gtk_widget_show (fBox
[0]);
1510 gtk_widget_show (fWindow
);
1515 * Update all user items reflecting zone z
1518 static gboolean
callUpdateAllGuis(gpointer
)
1520 UI::updateAllGuis();
1528 gtk_widget_show (fBox
[0]);
1529 gtk_widget_show (fWindow
);
1530 gtk_timeout_add(40, callUpdateAllGuis
, 0);
1536 /******************************************************************************
1537 *******************************************************************************
1541 *******************************************************************************
1542 *******************************************************************************/
1545 //----------------------------------------------------------------
1546 // Definition of a Faust Digital Signal Processor
1547 //----------------------------------------------------------------
1557 virtual int getNumInputs() = 0;
1558 virtual int getNumOutputs() = 0;
1559 virtual void buildUserInterface(UI
* interface
) = 0;
1560 virtual void init(int samplingRate
) = 0;
1561 virtual void compute(int len
, float** inputs
, float** outputs
) = 0;
1562 virtual void conclude() {}
1574 /******************************************************************************
1575 *******************************************************************************
1579 *******************************************************************************
1580 *******************************************************************************/
1582 // lopt : Scan Command Line long int Arguments
1584 long lopt (int argc
, char *argv
[], const char* longname
, const char* shortname
, long def
)
1586 for (int i
=2; i
<argc
; i
++)
1587 if ( strcmp(argv
[i
-1], shortname
) == 0 || strcmp(argv
[i
-1], longname
) == 0 )
1588 return atoi(argv
[i
]);
1592 // sopt : Scan Command Line string Arguments
1594 const char* sopt (int argc
, char *argv
[], const char* longname
, const char* shortname
, const char* def
)
1596 for (int i
=2; i
<argc
; i
++)
1597 if ( strcmp(argv
[i
-1], shortname
) == 0 || strcmp(argv
[i
-1], longname
) == 0 )
1602 // fopt : Scan Command Line flag option (without argument), return true if the flag
1604 bool fopt (int argc
, char *argv
[], const char* longname
, const char* shortname
)
1606 for (int i
=1; i
<argc
; i
++)
1607 if ( strcmp(argv
[i
], shortname
) == 0 || strcmp(argv
[i
], longname
) == 0 )
1613 //-------------------------------------------------------------------------
1615 //-------------------------------------------------------------------------
1617 pthread_t guithread
;
1619 void* run_ui(void* ptr
)
1621 UI
* interface
= (UI
*) ptr
;
1627 int main(int argc
, char *argv
[] )
1631 UI
* interface
= new GTKUI(argv
[0], &argc
, &argv
);
1633 // compute rcfilename to (re)store application state
1634 char rcfilename
[256];
1635 char* home
= getenv("HOME");
1636 snprintf(rcfilename
, 255, "%s/.%src", home
, basename(argv
[0]));
1638 AudioInterface
audio (
1639 AudioParam().cardName( sopt(argc
, argv
, "--device", "-d", "hw:0") )
1640 .frequency( lopt(argc
, argv
, "--frequency", "-f", 44100) )
1641 .buffering( lopt(argc
, argv
, "--buffer", "-b", 1024) )
1642 .periods( lopt(argc
, argv
, "--periods", "-p", 2) )
1643 .inputs(DSP
.getNumInputs())
1644 .outputs(DSP
.getNumOutputs())
1650 DSP
.init(audio
.frequency());
1651 DSP
.buildUserInterface(interface
);
1653 interface
->recallState(rcfilename
);
1655 pthread_create(&guithread
, NULL
, run_ui
, interface
);
1657 bool rt
= setRealtimePriority();
1658 printf(rt
?"RT : ":"NRT: "); audio
.shortinfo();
1659 if (fopt(argc
, argv
, "--verbose", "-v")) audio
.longinfo();
1660 bool running
= true;
1667 DSP
.compute(audio
.buffering(), audio
.inputSoftChannels(), audio
.outputSoftChannels());
1670 running
= mesure
<= (KMESURE
+ KSKIP
);
1673 interface
->saveState(rcfilename
);
1675 #ifdef BENCHMARKMODE
1676 printstats(argv
[0], audio
.buffering(), DSP
.getNumInputs(), DSP
.getNumOutputs());