1 /************************************************************************
3 IMPORTANT NOTE : this file contains two clearly delimited sections :
4 the ARCHITECTURE section (in two parts) and the USER section. Each section
5 is governed by its own copyright and license. Please check individually
6 each section for license and copyright information.
7 *************************************************************************/
9 /*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/
11 /************************************************************************
12 FAUST Architecture File
13 Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale
14 ---------------------------------------------------------------------
15 This Architecture section is free software; you can redistribute it
16 and/or modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 3 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; If not, see <http://www.gnu.org/licenses/>.
28 EXCEPTION : As a special exception, you may create a larger work
29 that contains this FAUST architecture section and distribute
30 that work under terms of your choice, so long as this FAUST
31 architecture section is not modified.
34 ************************************************************************
35 ************************************************************************/
42 #include <sys/types.h>
45 #include <alsa/asoundlib.h>
50 DEFAULT ALSA PARAMETERS CONTROLLED BY ENVIRONMENT VARIABLES
52 Some default parameters of Faust's ALSA applications are controlled by the following environment variables :
54 FAUST2ALSA_DEVICE = "hw:0"
55 FAUST2ALSA_FREQUENCY= 44100
56 FAUST2ALSA_BUFFER = 1024
57 FAUST2ALSA_PERIODS = 2
63 // handle 32/64 bits int size issues
67 #define uint32 unsigned int
68 #define uint64 unsigned long int
71 #define int64 long int
75 #define uint32 unsigned int
76 #define uint64 unsigned long long int
79 #define int64 long long int
82 // check 32/64 bits issues are correctly handled
84 #define check_error(err) if (err) { printf("%s:%d, alsa error %d : %s\n", __FILE__, __LINE__, err, snd_strerror(err)); exit(1); }
85 #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); }
86 #define display_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); }
88 #define max(x,y) (((x)>(y)) ? (x) : (y))
89 #define min(x,y) (((x)<(y)) ? (x) : (y))
92 * Used to set the priority and scheduling of the audi#include <sys/types.h>
96 static bool setRealtimePriority ()
101 struct sched_param param
;
104 pw
= getpwnam ("root");
106 param
.sched_priority
= 50; /* 0 to 99 */
107 err
= sched_setscheduler(0, SCHED_RR
, ¶m
);
112 /******************************************************************************
113 *******************************************************************************
117 *******************************************************************************
118 *******************************************************************************/
120 enum { kRead
= 1, kWrite
= 2, kReadWrite
= 3 };
123 * A convenient class to pass parameters to AudioInterface
129 const char* fCardName
;
130 unsigned int fFrequency
;
131 unsigned int fBuffering
;
132 unsigned int fPeriods
;
134 unsigned int fSoftInputs
;
135 unsigned int fSoftOutputs
;
147 AudioParam
& cardName(const char* n
) { fCardName
= n
; return *this; }
148 AudioParam
& frequency(int f
) { fFrequency
= f
; return *this; }
149 AudioParam
& buffering(int fpb
) { fBuffering
= fpb
; return *this; }
150 AudioParam
& periods(int p
) { fPeriods
= p
; return *this; }
151 AudioParam
& inputs(int n
) { fSoftInputs
= n
; return *this; }
152 AudioParam
& outputs(int n
) { fSoftOutputs
= n
; return *this; }
156 * An ALSA audio interface
158 class AudioInterface
: public AudioParam
161 snd_pcm_t
* fOutputDevice
;
162 snd_pcm_t
* fInputDevice
;
163 snd_pcm_hw_params_t
* fInputParams
;
164 snd_pcm_hw_params_t
* fOutputParams
;
166 snd_pcm_format_t fSampleFormat
;
167 snd_pcm_access_t fSampleAccess
;
169 unsigned int fCardInputs
;
170 unsigned int fCardOutputs
;
172 unsigned int fChanInputs
;
173 unsigned int fChanOutputs
;
175 // interleaved mode audiocard buffers
176 void* fInputCardBuffer
;
177 void* fOutputCardBuffer
;
179 // non interleaved mode audiocard buffers
180 void* fInputCardChannels
[256];
181 void* fOutputCardChannels
[256];
183 // non interleaved mod, floating point software buffers
184 float* fInputSoftChannels
[256];
185 float* fOutputSoftChannels
[256];
189 const char* cardName() { return fCardName
; }
190 int frequency() { return fFrequency
; }
191 int buffering() { return fBuffering
; }
192 int periods() { return fPeriods
; }
194 float** inputSoftChannels() { return fInputSoftChannels
; }
195 float** outputSoftChannels() { return fOutputSoftChannels
; }
198 AudioInterface(const AudioParam
& ap
= AudioParam()) : AudioParam(ap
)
208 * Open the audio interface
214 // allocation d'un stream d'entree et d'un stream de sortie
215 err
= snd_pcm_open( &fInputDevice
, fCardName
, SND_PCM_STREAM_CAPTURE
, 0 ); check_error(err
)
216 err
= snd_pcm_open( &fOutputDevice
, fCardName
, SND_PCM_STREAM_PLAYBACK
, 0 ); check_error(err
)
218 // recherche des parametres d'entree
219 err
= snd_pcm_hw_params_malloc ( &fInputParams
); check_error(err
);
220 setAudioParams(fInputDevice
, fInputParams
);
222 // recherche des parametres de sortie
223 err
= snd_pcm_hw_params_malloc ( &fOutputParams
); check_error(err
)
224 setAudioParams(fOutputDevice
, fOutputParams
);
226 // set the number of physical input and output channels close to what we need
227 fCardInputs
= fSoftInputs
;
228 fCardOutputs
= fSoftOutputs
;
230 snd_pcm_hw_params_set_channels_near(fInputDevice
, fInputParams
, &fCardInputs
);
231 snd_pcm_hw_params_set_channels_near(fOutputDevice
, fOutputParams
, &fCardOutputs
);
233 printf("inputs : %u, outputs : %u\n", fCardInputs
, fCardOutputs
);
235 // enregistrement des parametres d'entree-sortie
237 err
= snd_pcm_hw_params (fInputDevice
, fInputParams
); check_error (err
);
238 err
= snd_pcm_hw_params (fOutputDevice
, fOutputParams
); check_error (err
);
240 //assert(snd_pcm_hw_params_get_period_size(fInputParams,NULL) == snd_pcm_hw_params_get_period_size(fOutputParams,NULL));
242 // allocation of alsa buffers
243 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
244 fInputCardBuffer
= calloc(interleavedBufferSize(fInputParams
), 1);
245 fOutputCardBuffer
= calloc(interleavedBufferSize(fOutputParams
), 1);
248 for (unsigned int i
= 0; i
< fCardInputs
; i
++) {
249 fInputCardChannels
[i
] = calloc(noninterleavedBufferSize(fInputParams
), 1);
251 for (unsigned int i
= 0; i
< fCardOutputs
; i
++) {
252 fOutputCardChannels
[i
] = calloc(noninterleavedBufferSize(fOutputParams
), 1);
257 // allocation of floating point buffers needed by the dsp code
259 fChanInputs
= max(fSoftInputs
, fCardInputs
); assert (fChanInputs
< 256);
260 fChanOutputs
= max(fSoftOutputs
, fCardOutputs
); assert (fChanOutputs
< 256);
262 for (unsigned int i
= 0; i
< fChanInputs
; i
++) {
263 fInputSoftChannels
[i
] = (float*) calloc (fBuffering
, sizeof(float));
264 for (unsigned int j
= 0; j
< fBuffering
; j
++) {
265 fInputSoftChannels
[i
][j
] = 0.0;
269 for (unsigned int i
= 0; i
< fChanOutputs
; i
++) {
270 fOutputSoftChannels
[i
] = (float*) calloc (fBuffering
, sizeof(float));
271 for (unsigned int j
= 0; j
< fBuffering
; j
++) {
272 fOutputSoftChannels
[i
][j
] = 0.0;
277 void setAudioParams(snd_pcm_t
* stream
, snd_pcm_hw_params_t
* params
)
281 // set params record with initial values
282 err
= snd_pcm_hw_params_any ( stream
, params
);
283 check_error_msg(err
, "unable to init parameters")
285 // set alsa access mode (and fSampleAccess field) either to non interleaved or interleaved
287 err
= snd_pcm_hw_params_set_access (stream
, params
, SND_PCM_ACCESS_RW_NONINTERLEAVED
);
289 err
= snd_pcm_hw_params_set_access (stream
, params
, SND_PCM_ACCESS_RW_INTERLEAVED
);
290 check_error_msg(err
, "unable to set access mode neither to non-interleaved or to interleaved");
292 snd_pcm_hw_params_get_access(params
, &fSampleAccess
);
295 // search for 32-bits or 16-bits format
296 err
= snd_pcm_hw_params_set_format (stream
, params
, SND_PCM_FORMAT_S32
);
298 err
= snd_pcm_hw_params_set_format (stream
, params
, SND_PCM_FORMAT_S16
);
299 check_error_msg(err
, "unable to set format to either 32-bits or 16-bits");
301 snd_pcm_hw_params_get_format(params
, &fSampleFormat
);
302 // set sample frequency
303 snd_pcm_hw_params_set_rate_near (stream
, params
, &fFrequency
, 0);
305 // set period and period size (buffering)
306 err
= snd_pcm_hw_params_set_period_size (stream
, params
, fBuffering
, 0);
307 check_error_msg(err
, "period size not available");
309 err
= snd_pcm_hw_params_set_periods (stream
, params
, fPeriods
, 0);
310 check_error_msg(err
, "number of periods not available");
313 ssize_t
interleavedBufferSize (snd_pcm_hw_params_t
* params
)
315 _snd_pcm_format format
; snd_pcm_hw_params_get_format(params
, &format
);
316 snd_pcm_uframes_t psize
; snd_pcm_hw_params_get_period_size(params
, &psize
, NULL
);
317 unsigned int channels
; snd_pcm_hw_params_get_channels(params
, &channels
);
318 ssize_t bsize
= snd_pcm_format_size (format
, psize
* channels
);
322 ssize_t
noninterleavedBufferSize (snd_pcm_hw_params_t
* params
)
324 _snd_pcm_format format
; snd_pcm_hw_params_get_format(params
, &format
);
325 snd_pcm_uframes_t psize
; snd_pcm_hw_params_get_period_size(params
, &psize
, NULL
);
326 ssize_t bsize
= snd_pcm_format_size (format
, psize
);
334 * Read audio samples from the audio card. Convert samples to floats and take
335 * care of interleaved buffers
339 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
341 int count
= snd_pcm_readi(fInputDevice
, fInputCardBuffer
, fBuffering
);
343 display_error_msg(count
, "reading samples");
344 int err
= snd_pcm_prepare(fInputDevice
);
345 check_error_msg(err
, "preparing input stream");
348 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
350 short* buffer16b
= (short*) fInputCardBuffer
;
351 for (unsigned int s
= 0; s
< fBuffering
; s
++) {
352 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
353 fInputSoftChannels
[c
][s
] = float(buffer16b
[c
+ s
*fCardInputs
])*(1.0/float(SHRT_MAX
));
357 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
359 int32
* buffer32b
= (int32
*) fInputCardBuffer
;
360 for (unsigned int s
= 0; s
< fBuffering
; s
++) {
361 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
362 fInputSoftChannels
[c
][s
] = float(buffer32b
[c
+ s
*fCardInputs
])*(1.0/float(INT_MAX
));
367 printf("unrecognized input sample format : %u\n", fSampleFormat
);
371 } else if (fSampleAccess
== SND_PCM_ACCESS_RW_NONINTERLEAVED
) {
373 int count
= snd_pcm_readn(fInputDevice
, fInputCardChannels
, fBuffering
);
375 display_error_msg(count
, "reading samples");
376 int err
= snd_pcm_prepare(fInputDevice
);
377 check_error_msg(err
, "preparing input stream");
380 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
382 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
383 short* chan16b
= (short*) fInputCardChannels
[c
];
384 for (unsigned int s
= 0; s
< fBuffering
; s
++) {
385 fInputSoftChannels
[c
][s
] = float(chan16b
[s
])*(1.0/float(SHRT_MAX
));
389 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
391 for (unsigned int c
= 0; c
< fCardInputs
; c
++) {
392 int32
* chan32b
= (int32
*) fInputCardChannels
[c
];
393 for (unsigned int s
= 0; s
< fBuffering
; s
++) {
394 fInputSoftChannels
[c
][s
] = float(chan32b
[s
])*(1.0/float(INT_MAX
));
399 printf("unrecognized input sample format : %u\n", fSampleFormat
);
404 check_error_msg(-10000, "unknow access mode");
409 * write the output soft channels to the audio card. Convert sample
410 * format and interleaves buffers when needed
416 if (fSampleAccess
== SND_PCM_ACCESS_RW_INTERLEAVED
) {
418 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
420 short* buffer16b
= (short*) fOutputCardBuffer
;
421 for (unsigned int f
= 0; f
< fBuffering
; f
++) {
422 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
423 float x
= fOutputSoftChannels
[c
][f
];
424 buffer16b
[c
+ f
*fCardOutputs
] = short( max(min(x
,1.0),-1.0) * float(SHRT_MAX
) ) ;
428 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
430 int32
* buffer32b
= (int32
*) fOutputCardBuffer
;
431 for (unsigned int f
= 0; f
< fBuffering
; f
++) {
432 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
433 float x
= fOutputSoftChannels
[c
][f
];
434 buffer32b
[c
+ f
*fCardOutputs
] = int( max(min(x
,1.0),-1.0) * float(INT_MAX
) ) ;
439 printf("unrecognized output sample format : %u\n", fSampleFormat
);
443 int count
= snd_pcm_writei(fOutputDevice
, fOutputCardBuffer
, fBuffering
);
445 display_error_msg(count
, "w3");
446 int err
= snd_pcm_prepare(fOutputDevice
);
447 check_error_msg(err
, "preparing output stream");
452 } else if (fSampleAccess
== SND_PCM_ACCESS_RW_NONINTERLEAVED
) {
454 if (fSampleFormat
== SND_PCM_FORMAT_S16
) {
456 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
457 short* chan16b
= (short*) fOutputCardChannels
[c
];
458 for (unsigned int f
= 0; f
< fBuffering
; f
++) {
459 float x
= fOutputSoftChannels
[c
][f
];
460 chan16b
[f
] = short( max(min(x
,1.0),-1.0) * float(SHRT_MAX
) ) ;
464 } else if (fSampleFormat
== SND_PCM_FORMAT_S32
) {
466 for (unsigned int c
= 0; c
< fCardOutputs
; c
++) {
467 int32
* chan32b
= (int32
*) fOutputCardChannels
[c
];
468 for (unsigned int f
= 0; f
< fBuffering
; f
++) {
469 float x
= fOutputSoftChannels
[c
][f
];
470 chan32b
[f
] = int( max(min(x
,1.0),-1.0) * float(INT_MAX
) ) ;
476 printf("unrecognized output sample format : %u\n", fSampleFormat
);
480 int count
= snd_pcm_writen(fOutputDevice
, fOutputCardChannels
, fBuffering
);
482 display_error_msg(count
, "w3");
483 int err
= snd_pcm_prepare(fOutputDevice
);
484 check_error_msg(err
, "preparing output stream");
489 check_error_msg(-10000, "unknow access mode");
494 * print short information on the audio device
499 snd_ctl_card_info_t
* card_info
;
500 snd_ctl_t
* ctl_handle
;
501 err
= snd_ctl_open (&ctl_handle
, fCardName
, 0); check_error(err
);
502 snd_ctl_card_info_alloca (&card_info
);
503 err
= snd_ctl_card_info(ctl_handle
, card_info
); check_error(err
);
504 printf("%s|%d|%d|%d|%d|%s\n",
505 snd_ctl_card_info_get_driver(card_info
),
506 fCardInputs
, fCardOutputs
,
507 fFrequency
, fBuffering
,
508 snd_pcm_format_name((_snd_pcm_format
)fSampleFormat
));
512 * print more detailled information on the audio device
517 snd_ctl_card_info_t
* card_info
;
518 snd_ctl_t
* ctl_handle
;
520 printf("Audio Interface Description :\n");
521 printf("Sampling Frequency : %d, Sample Format : %s, buffering : %d\n",
522 fFrequency
, snd_pcm_format_name((_snd_pcm_format
)fSampleFormat
), fBuffering
);
523 printf("Software inputs : %2d, Software outputs : %2d\n", fSoftInputs
, fSoftOutputs
);
524 printf("Hardware inputs : %2d, Hardware outputs : %2d\n", fCardInputs
, fCardOutputs
);
525 printf("Channel inputs : %2d, Channel outputs : %2d\n", fChanInputs
, fChanOutputs
);
527 // affichage des infos de la carte
528 err
= snd_ctl_open (&ctl_handle
, fCardName
, 0); check_error(err
);
529 snd_ctl_card_info_alloca (&card_info
);
530 err
= snd_ctl_card_info(ctl_handle
, card_info
); check_error(err
);
531 printCardInfo(card_info
);
533 // affichage des infos liees aux streams d'entree-sortie
534 if (fSoftInputs
> 0) printHWParams(fInputParams
);
535 if (fSoftOutputs
> 0) printHWParams(fOutputParams
);
538 void printCardInfo(snd_ctl_card_info_t
* ci
)
540 printf("Card info (address : %p)\n", ci
);
541 printf("\tID = %s\n", snd_ctl_card_info_get_id(ci
));
542 printf("\tDriver = %s\n", snd_ctl_card_info_get_driver(ci
));
543 printf("\tName = %s\n", snd_ctl_card_info_get_name(ci
));
544 printf("\tLongName = %s\n", snd_ctl_card_info_get_longname(ci
));
545 printf("\tMixerName = %s\n", snd_ctl_card_info_get_mixername(ci
));
546 printf("\tComponents = %s\n", snd_ctl_card_info_get_components(ci
));
547 printf("--------------\n");
550 void printHWParams( snd_pcm_hw_params_t
* params
)
552 printf("HW Params info (address : %p)\n", params
);
554 printf("\tChannels = %d\n", snd_pcm_hw_params_get_channels(params
));
555 printf("\tFormat = %s\n", snd_pcm_format_name((_snd_pcm_format
)snd_pcm_hw_params_get_format(params
)));
556 printf("\tAccess = %s\n", snd_pcm_access_name((_snd_pcm_access
)snd_pcm_hw_params_get_access(params
)));
557 printf("\tRate = %d\n", snd_pcm_hw_params_get_rate(params
, NULL
));
558 printf("\tPeriods = %d\n", snd_pcm_hw_params_get_periods(params
, NULL
));
559 printf("\tPeriod size = %d\n", (int)snd_pcm_hw_params_get_period_size(params
, NULL
));
560 printf("\tPeriod time = %d\n", snd_pcm_hw_params_get_period_time(params
, NULL
));
561 printf("\tBuffer size = %d\n", (int)snd_pcm_hw_params_get_buffer_size(params
));
562 printf("\tBuffer time = %d\n", snd_pcm_hw_params_get_buffer_time(params
, NULL
));
564 printf("--------------\n");
569 // lopt : Scan Command Line long int Arguments
570 long lopt(int argc
, char *argv
[], const char* longname
, const char* shortname
, long def
)
572 for (int i
=2; i
<argc
; i
++)
573 if ( strcmp(argv
[i
-1], shortname
) == 0 || strcmp(argv
[i
-1], longname
) == 0 )
574 return atoi(argv
[i
]);
578 // sopt : Scan Command Line string Arguments
579 const char* sopt(int argc
, char *argv
[], const char* longname
, const char* shortname
, const char* def
)
581 for (int i
=2; i
<argc
; i
++)
582 if ( strcmp(argv
[i
-1], shortname
) == 0 || strcmp(argv
[i
-1], longname
) == 0 )
587 // fopt : Scan Command Line flag option (without argument), return true if the flag
588 bool fopt(int argc
, char *argv
[], const char* longname
, const char* shortname
)
590 for (int i
=1; i
<argc
; i
++)
591 if ( strcmp(argv
[i
], shortname
) == 0 || strcmp(argv
[i
], longname
) == 0 )
597 * Return the value of an environment variable or defval if undefined.
599 static int getDefaultEnv(const char* name
, int defval
)
601 const char* str
= getenv(name
);
610 * Return the value of an environment variable or defval if undefined.
612 static const char* getDefaultEnv(const char* name
, const char* defval
)
614 const char* str
= getenv(name
);
622 /******************************************************************************
623 *******************************************************************************
627 *******************************************************************************
628 *******************************************************************************/
629 void* __run(void* ptr
);
631 class alsaaudio
: public audio
{
632 AudioInterface
* fAudio
;
634 pthread_t fAudioThread
;
638 alsaaudio(int argc
, char *argv
[], dsp
* DSP
) : fAudio(0), fDSP(DSP
), fRunning(false) {
639 fAudio
= new AudioInterface (
640 AudioParam().cardName( sopt(argc
, argv
, "--device", "-d", getDefaultEnv("FAUST2ALSA_DEVICE", "hw:0") ) )
641 .frequency( lopt(argc
, argv
, "--frequency", "-f", getDefaultEnv("FAUST2ALSA_FREQUENCY",44100) ) )
642 .buffering( lopt(argc
, argv
, "--buffer", "-b", getDefaultEnv("FAUST2ALSA_BUFFER",1024) ) )
643 .periods( lopt(argc
, argv
, "--periods", "-p", getDefaultEnv("FAUST2ALSA_PERIODS",2) ) )
644 .inputs(DSP
->getNumInputs())
645 .outputs(DSP
->getNumOutputs()));
647 virtual ~alsaaudio() { stop(); delete fAudio
; }
649 virtual bool init(const char */
*name*/
, dsp
* DSP
) {
652 DSP
->init(fAudio
->frequency());
656 virtual bool start() {
658 if (pthread_create( &fAudioThread
, 0, __run
, this))
663 virtual void stop() {
666 pthread_join (fAudioThread
, 0);
671 bool rt
= setRealtimePriority();
672 printf(rt
? "RT : ":"NRT: "); fAudio
->shortinfo();
677 fDSP
->compute(fAudio
->buffering(), fAudio
->inputSoftChannels(), fAudio
->outputSoftChannels());
683 void* __run (void* ptr
)
685 alsaaudio
* alsa
= (alsaaudio
*)ptr
;
692 /********************END ARCHITECTURE SECTION (part 2/2)****************/