comparison alsacap.c @ 1:9cadc470e3da

reorganize, add more
author pmeerw@pan
date Fri, 30 Oct 2009 23:07:52 +0100
parents
children
comparison
equal deleted inserted replaced
0:deadffdf5d60 1:9cadc470e3da
1 /*
2 * ALSA parameter test program
3 *
4 * compile with: gcc -o alsacap alsacap.c -lasound
5 */
6
7
8 /*============================================================================
9 Includes
10 ============================================================================*/
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <alsa/asoundlib.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 /*============================================================================
20 Constant and type definitions
21 ============================================================================*/
22
23 #define RATE_KHZ_LIMIT 200
24
25 #define HWP_END 0
26 #define HWP_RATE 1
27 #define HWP_NCH 2
28 #define HWP_FORMAT 3
29 #define SIZE_HWP 7
30
31 typedef struct {
32 int recdevices, verbose, card, dev;
33 char *device;
34 int hwparams[SIZE_HWP];
35 }
36 aiopts;
37
38
39 /*============================================================================
40 Global variables
41 ============================================================================*/
42
43 static snd_ctl_t *handle= NULL;
44 static snd_pcm_t *pcm= NULL;
45 static snd_ctl_card_info_t *info;
46 static snd_pcm_info_t *pcminfo;
47 static snd_pcm_hw_params_t *pars;
48 static snd_pcm_format_mask_t *fmask;
49
50
51 /*============================================================================
52 Prototypes
53 ============================================================================*/
54
55 void usagemsg(int code);
56 void errnumarg(char optchar);
57 void errarg(char optchar);
58 void errtoomany();
59
60 void scancards(snd_pcm_stream_t stream, int thecard, int thedev);
61 int sc_errcheck(int retval, const char *doingwhat, int cardnr, int devnr);
62
63 void testconfig(snd_pcm_stream_t stream, const char *device, const int *hwpars);
64 void tc_errcheck(int retval, const char *doingwhat);
65
66 const char *alsaerrstr(const int errcode);
67 const char *dirstr(int dir);
68
69 int parse_alsaformat(const char *fmtstr);
70 const char *alsafmtstr(int fmtnum);
71
72 void printfmtmask(const snd_pcm_format_mask_t *fmask);
73
74
75 /*============================================================================
76 Main program
77 ============================================================================*/
78
79 int main(int argc, char **argv)
80 {
81 aiopts options= { 0, 1, -1, -1, NULL };
82 snd_pcm_stream_t stream;
83 char *argpar;
84 int argind, hwpind;
85
86 snd_ctl_card_info_alloca(&info);
87 snd_pcm_info_alloca(&pcminfo);
88 snd_pcm_hw_params_alloca(&pars);
89 snd_pcm_format_mask_alloca(&fmask);
90
91 hwpind= 0;
92 for( argind= 1; argind< argc; ++argind )
93 {
94 if( argv[argind][0]!='-' ) {
95 fprintf(stderr, "Unrecognised command-line argument `%s'.\n", argv[argind]);
96 usagemsg(1);
97 }
98 if( argv[argind][2] ) argpar= argv[argind]+2;
99 else {
100 if( argind+1 >= argc ) argpar= NULL;
101 else argpar= argv[argind+1];
102 }
103 if( argv[argind][1]=='h' || !strcmp(argv[argind]+1, "-help") )
104 usagemsg(0);
105 else if( argv[argind][1]=='R' ) {
106 options.recdevices= 1;
107 argpar= NULL; // set to NULL if unused to keep track of next arg index
108 }
109 else if( argv[argind][1]=='C' ) {
110 if( !argpar || !isdigit(*argpar) ) errnumarg('C');
111 options.card= strtol(argpar, NULL, 0);
112 }
113 else if( argv[argind][1]=='D' ) {
114 if( !argpar || !isdigit(*argpar) ) errnumarg('D');
115 options.dev= strtol(argpar, NULL, 0);
116 }
117 else if( argv[argind][1]=='d' ) {
118 if( !argpar ) errarg('d');
119 options.device= argpar;
120 }
121 else if( argv[argind][1]=='r' ) {
122 if( !argpar || !isdigit(*argpar) ) errnumarg('r');
123 if( hwpind+3 > SIZE_HWP ) errtoomany();
124 options.hwparams[hwpind++]= HWP_RATE;
125 options.hwparams[hwpind]= strtol(argpar, NULL, 0);
126 if( options.hwparams[hwpind] <= RATE_KHZ_LIMIT )
127 options.hwparams[hwpind] *= 1000; // sanity check: Hz or kHz ?
128 ++hwpind;
129 }
130 else if( argv[argind][1]=='c' ) {
131 if( !argpar || !isdigit(*argpar) ) errnumarg('c');
132 if( hwpind+3 > SIZE_HWP ) errtoomany();
133 options.hwparams[hwpind++]= HWP_NCH;
134 options.hwparams[hwpind++]= strtol(argpar, NULL, 0);
135 }
136 else if( argv[argind][1]=='f' ) {
137 if( !argpar ) errarg('f');
138 if( hwpind+3 > SIZE_HWP ) errtoomany();
139 options.hwparams[hwpind++]= HWP_FORMAT;
140 options.hwparams[hwpind++]= parse_alsaformat(argpar);
141 }
142 else {
143 fprintf(stderr, "Unrecognised command-line option `%s'.\n", argv[argind]);
144 usagemsg(1);
145 }
146 if( argpar && !argv[argind][2] )
147 ++argind; // additional increment if separate parameter argument was used
148 }
149 options.hwparams[hwpind]= HWP_END;
150 if( options.dev >= 0 && options.card < 0 ) {
151 fprintf(stderr, "The card has to be specified with -C if a device number is given (-D).\n");
152 exit(1);
153 }
154 if( options.device && (options.card>=0 || options.dev>=0) ) {
155 fprintf(stderr, "Specifying a device name (-d) and a card and possibly device number (-C, -D) is mutually exclusive.\n");
156 exit(1);
157 }
158 stream= options.recdevices? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
159
160 if( !options.device )
161 scancards(stream, options.card, options.dev);
162 else
163 testconfig(stream, options.device, options.hwparams);
164
165 exit(0);
166 }
167
168
169
170 /*============================================================================
171 Usage message and command-line argument error functions
172 ============================================================================*/
173
174 void usagemsg(int code)
175 {
176 fprintf(stderr, "Usage: alsacap [-R] [-C <card #> [-D <device #>]]\n"
177 " alsacap [-R] -d <device name> [-r <rate>|-c <# of channels>|-f <sample format>]...\n"
178 "ALSA capability lister.\n"
179 "First form: Scans one or all soundcards known to ALSA for devices, \n"
180 "subdevices and parameter ranges. -R causes a scan for recording\n"
181 "rather than playback devices. The other options specify the sound\n"
182 "card and possibly the device by number.\n"
183 "Second form: Displays ranges of configuration parameters for the given\n"
184 "ALSA device. Unlike with the first form, a non-hardware device may be\n"
185 "given. Up to three optional command-line arguments fix the rate,\n"
186 "number of channels and sample format in the order in which they are\n"
187 "given. The remaining parameter ranges are output. If unique, the\n"
188 "number of significant bits of the sample values is output. (Some\n"
189 "sound cards ignore some of the bits.)\n");
190 exit(code);
191 }
192
193 void errnumarg(char optchar)
194 {
195 fprintf(stderr, "The -%c option requires a numerical argument! Aborting.\n", optchar);
196 exit(1);
197 }
198
199 void errarg(char optchar)
200 {
201 fprintf(stderr, "The -%c option requires an argument! Aborting.\n", optchar);
202 exit(1);
203 }
204
205 void errtoomany()
206 {
207 fprintf(stderr, "Too many -r/-c/-f options given! (Maximum is %d.) Aborting.\n", (SIZE_HWP-1)/2);
208 exit(1);
209 }
210
211
212 /*============================================================================
213 Function for scanning all cards
214 ============================================================================*/
215
216 #define HWCARDTEMPL "hw:%d"
217 #define HWDEVTEMPL "hw:%d,%d"
218 #define HWDEVLEN 32
219
220 void scancards(snd_pcm_stream_t stream, int thecard, int thedev)
221 {
222 char hwdev[HWDEVLEN+1];
223 unsigned min, max;
224 int card, err, dev, subd, nsubd;
225
226 printf("*** Scanning for %s devices", stream==SND_PCM_STREAM_CAPTURE? "recording" : "playback");
227 if( thecard >= 0 )
228 printf(" on card %d", thecard);
229 if( thedev >= 0 )
230 printf(", device %d", thedev);
231 printf(" ***\n");
232 hwdev[HWDEVLEN]= 0;
233 if( thecard >= 0 )
234 card= thecard;
235 else {
236 card= -1;
237 if( snd_card_next(&card) < 0 )
238 return;
239 }
240 while( card >= 0 )
241 {
242 snprintf(hwdev, HWDEVLEN, HWCARDTEMPL, card);
243 err= snd_ctl_open(&handle, hwdev, 0);
244 if( sc_errcheck(err, "opening control interface", card, -1) ) goto nextcard;
245 err= snd_ctl_card_info(handle, info);
246 if( sc_errcheck(err, "obtaining card info", card, -1) ) {
247 snd_ctl_close(handle);
248 goto nextcard;
249 }
250 printf("Card %d, ID `%s', name `%s'\n", card, snd_ctl_card_info_get_id(info),
251 snd_ctl_card_info_get_name(info));
252 if( thedev >= 0 )
253 dev= thedev;
254 else {
255 dev= -1;
256 if( snd_ctl_pcm_next_device(handle, &dev) < 0 ) {
257 snd_ctl_close(handle);
258 goto nextcard;
259 }
260 }
261 while( dev >= 0 )
262 {
263 snd_pcm_info_set_device(pcminfo, dev);
264 snd_pcm_info_set_subdevice(pcminfo, 0);
265 snd_pcm_info_set_stream(pcminfo, stream);
266 err= snd_ctl_pcm_info(handle, pcminfo);
267 if( thedev<0 && err == -ENOENT ) goto nextdev;
268 if( sc_errcheck(err, "obtaining device info", card, dev) ) goto nextdev;
269 nsubd= snd_pcm_info_get_subdevices_count(pcminfo);
270 if( sc_errcheck(nsubd, "obtaining device info", card, dev) ) goto nextdev;
271 printf(" Device %d, ID `%s', name `%s', %d subdevices (%d available)\n",
272 dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo),
273 nsubd, snd_pcm_info_get_subdevices_avail(pcminfo));
274 snprintf(hwdev, HWDEVLEN, HWDEVTEMPL, card, dev);
275 err= snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK);
276 if( sc_errcheck(err, "opening sound device", card, dev) ) goto nextdev;
277 err= snd_pcm_hw_params_any(pcm, pars);
278 if( sc_errcheck(err, "obtaining hardware parameters", card, dev) ) {
279 snd_pcm_close(pcm);
280 goto nextdev;
281 }
282 snd_pcm_hw_params_get_channels_min(pars, &min);
283 snd_pcm_hw_params_get_channels_max(pars, &max);
284 if( min == max )
285 if( min == 1 ) printf(" 1 channel, ");
286 else printf(" %d channels, ", min);
287 else printf(" %u..%u channels, ", min, max);
288 snd_pcm_hw_params_get_rate_min(pars, &min, NULL);
289 snd_pcm_hw_params_get_rate_max(pars, &max, NULL);
290 printf("sampling rate %u..%u Hz\n Sample formats: ", min, max);
291 snd_pcm_hw_params_get_format_mask(pars, fmask);
292 printfmtmask(fmask);
293 snd_pcm_close(pcm);
294 printf("\n");
295 pcm= NULL;
296 for( subd= 0; subd< nsubd; ++subd ) {
297 snd_pcm_info_set_subdevice(pcminfo, subd);
298 err= snd_ctl_pcm_info(handle, pcminfo);
299 if( sc_errcheck(err, "obtaining subdevice info", card, dev) ) goto nextdev;
300 printf(" Subdevice %d, name `%s'\n", subd, snd_pcm_info_get_subdevice_name(pcminfo));
301 }
302 nextdev:
303 if( thedev >= 0 || snd_ctl_pcm_next_device(handle, &dev) < 0 )
304 break;
305 }
306 snd_ctl_close(handle);
307 nextcard:
308 if( thecard >= 0 || snd_card_next(&card) < 0 )
309 break;
310 }
311 }
312
313
314 int sc_errcheck(int retval, const char *doingwhat, int cardnr, int devnr)
315 {
316 if( retval<0 ) {
317 if( devnr>= 0 )
318 fprintf(stderr, "Error %s for card %d, device %d: %s. Skipping.\n", doingwhat, cardnr, devnr, alsaerrstr(retval));
319 else
320 fprintf(stderr, "Error %s for card %d: %s. Skipping.\n", doingwhat, cardnr, alsaerrstr(retval));
321 return 1;
322 }
323 return 0;
324 }
325
326
327
328 /*============================================================================
329 Function for investigating device configurations
330 ============================================================================*/
331
332 void testconfig(snd_pcm_stream_t stream, const char *device, const int *hwpars)
333 {
334 unsigned min, max, param;
335 int err, count, dir, result;
336
337 printf("*** Exploring configuration space of device `%s' for %s ***\n", device,
338 stream==SND_PCM_STREAM_CAPTURE? "recording" : "playback");
339
340 if (stream==SND_PCM_STREAM_CAPTURE && stream==SND_PCM_STREAM_PLAYBACK)
341 dir = 0;
342 else if (stream==SND_PCM_STREAM_CAPTURE)
343 dir = -1;
344 else
345 dir = 1;
346
347 err= snd_pcm_open(&pcm, device, stream, SND_PCM_NONBLOCK);
348 tc_errcheck(err, "opening sound device");
349 err= snd_pcm_hw_params_any(pcm, pars);
350 tc_errcheck(err, "initialising hardware parameters");
351 for( count= 0; hwpars[count]!=HWP_END; count += 2 )
352
353 switch(hwpars[count])
354 {
355 case HWP_RATE:param= hwpars[count+1];
356 err= snd_pcm_hw_params_set_rate_near(pcm, pars, &param, &result);
357 if( err<0 )
358 fprintf(stderr, "Could not set sampling rate to %d Hz: %s. "
359 "Continuing regardless.\n", hwpars[count+1], alsaerrstr(err));
360 else
361 printf("Set sampling rate %d Hz --> got %u Hz, %s requested.\n", hwpars[count+1], param, dirstr(dir));
362 break;
363 case HWP_NCH:err= snd_pcm_hw_params_set_channels(pcm, pars, hwpars[count+1]);
364 if( err<0 )
365 fprintf(stderr, "Could not set # of channels to %d: %s. "
366 "Continuing regardless.\n", hwpars[count+1], alsaerrstr(err));
367 else
368 printf("Set number of channels to %d.\n", hwpars[count+1]);
369 break;
370 case HWP_FORMAT:err= snd_pcm_hw_params_set_format(pcm, pars, hwpars[count+1]);
371 if( err<0 )
372 fprintf(stderr, "Could not set sample format to %s: %s."
373 " Continuing regardless.\n", alsafmtstr(hwpars[count+1]), alsaerrstr(err));
374 else
375 printf("Set sample format to %s.\n", alsafmtstr(hwpars[count+1]));
376 break;
377 default:
378 break;
379 }
380 if( count>0 )
381 printf("Parameter ranges remaining after these settings:\n");
382 snd_pcm_hw_params_get_channels_min(pars, &min);
383 snd_pcm_hw_params_get_channels_max(pars, &max);
384 if( min==max )
385 if( min==1 )
386 printf("1 channel\n");
387 else
388 printf("%u channels\n", min);
389 else
390 printf("%u..%u channels\n", min, max);
391 snd_pcm_hw_params_get_rate_min(pars, &min, NULL);
392 snd_pcm_hw_params_get_rate_max(pars, &max, NULL);
393 if( min==max )
394 printf("Sampling rate %u Hz\nSample formats: ", min);
395 else
396 printf("Sampling rate %u..%u Hz\nSample formats: ", min, max);
397 snd_pcm_hw_params_get_format_mask(pars, fmask);
398 printfmtmask(fmask);
399 printf("\n");
400 result= snd_pcm_hw_params_get_sbits(pars);
401 if( result >= 0 ) // only available if bit width of all formats is the same
402 printf("Significant bits: %d\n", result);
403 snd_pcm_close(pcm);
404 }
405
406
407 void tc_errcheck(int retval, const char *doingwhat)
408 {
409 if( retval<0 ) {
410 fprintf(stderr, "Error %s: %s. Aborting.\n", doingwhat, alsaerrstr(retval));
411 if( pcm )
412 snd_pcm_close(pcm);
413 exit(1);
414 }
415 }
416
417
418 /*============================================================================
419 String-building functions
420 ============================================================================*/
421
422 struct alsaerr { int err; char *msg; };
423 struct alsaerr aelist[]= {
424 {-EBADFD, "PCM device is in a bad state"},
425 {-EPIPE, "An underrun occurred"},
426 {-ESTRPIPE, "A suspend event occurred"},
427 {-ENOTTY, "Hotplug device has been removed"},
428 {-ENODEV, "Hotplug device has been removed"},
429 {-ENOENT, "Device does not exist"},
430 {0, NULL}
431 };
432 const char *alsaerrstr(const int errcode)
433 {
434 struct alsaerr *search;
435
436 if( errcode >= 0 )
437 return "No error";
438 for( search= aelist; search->msg && search->err!=errcode; ++search);
439 if( search->msg )
440 return search->msg;
441 else
442 return strerror(-errcode);
443 }
444
445
446 const char *dirstr(int dir)
447 {
448 if( !dir )
449 return "=";
450 else if( dir<0 )
451 return "<";
452 else
453 return ">";
454 }
455
456
457 /*============================================================================
458 Functions for parsing and string output of ALSA sample formats
459 ============================================================================*/
460
461 struct fmtdef { char *fmtname; int format; };
462 static struct fmtdef fmtlist[]= {
463 {"S8", SND_PCM_FORMAT_S8},
464 {"U8", SND_PCM_FORMAT_U8},
465 {"S16_LE", SND_PCM_FORMAT_S16_LE},
466 {"S16_BE", SND_PCM_FORMAT_S16_BE},
467 {"U16_LE", SND_PCM_FORMAT_U16_LE},
468 {"U16_BE", SND_PCM_FORMAT_U16_BE},
469 {"S24_LE", SND_PCM_FORMAT_S24_LE},
470 {"S24_BE", SND_PCM_FORMAT_S24_BE},
471 {"U24_LE", SND_PCM_FORMAT_U24_LE},
472 {"U24_BE", SND_PCM_FORMAT_U24_BE},
473 {"S32_LE", SND_PCM_FORMAT_S32_LE},
474 {"S32_BE", SND_PCM_FORMAT_S32_BE},
475 {"U32_LE", SND_PCM_FORMAT_U32_LE},
476 {"U32_BE", SND_PCM_FORMAT_U32_BE},
477 {"FLOAT_LE", SND_PCM_FORMAT_FLOAT_LE},
478 {"FLOAT_BE", SND_PCM_FORMAT_FLOAT_BE},
479 {"FLOAT64_LE", SND_PCM_FORMAT_FLOAT64_LE},
480 {"FLOAT64_BE", SND_PCM_FORMAT_FLOAT64_BE},
481 {"IEC958_SUBFRAME_LE", SND_PCM_FORMAT_IEC958_SUBFRAME_LE},
482 {"IEC958_SUBFRAME_BE", SND_PCM_FORMAT_IEC958_SUBFRAME_BE},
483 {"MU_LAW", SND_PCM_FORMAT_MU_LAW},
484 {"A_LAW", SND_PCM_FORMAT_A_LAW},
485 {"IMA_ADPCM", SND_PCM_FORMAT_IMA_ADPCM},
486 {"MPEG", SND_PCM_FORMAT_MPEG},
487 {"GSM", SND_PCM_FORMAT_GSM},
488 {"SPECIAL", SND_PCM_FORMAT_SPECIAL},
489 {"S24_3LE", SND_PCM_FORMAT_S24_3LE},
490 {"S24_3BE", SND_PCM_FORMAT_S24_3BE},
491 {"U24_3LE", SND_PCM_FORMAT_U24_3LE},
492 {"U24_3BE", SND_PCM_FORMAT_U24_3BE},
493 {"S20_3LE", SND_PCM_FORMAT_S20_3LE},
494 {"S20_3BE", SND_PCM_FORMAT_S20_3BE},
495 {"U20_3LE", SND_PCM_FORMAT_U20_3LE},
496 {"U20_3BE", SND_PCM_FORMAT_U20_3BE},
497 {"S18_3LE", SND_PCM_FORMAT_S18_3LE},
498 {"S18_3BE", SND_PCM_FORMAT_S18_3BE},
499 {"U18_3LE", SND_PCM_FORMAT_U18_3LE},
500 {"U18_3BE", SND_PCM_FORMAT_U18_3BE},
501 {"S16", SND_PCM_FORMAT_S16},
502 {"U16", SND_PCM_FORMAT_U16},
503 {"S24", SND_PCM_FORMAT_S24},
504 {"U24", SND_PCM_FORMAT_U24},
505 {"S32", SND_PCM_FORMAT_S32},
506 {"U32", SND_PCM_FORMAT_U32},
507 {"FLOAT", SND_PCM_FORMAT_FLOAT},
508 {"FLOAT64", SND_PCM_FORMAT_FLOAT64},
509 {"IEC958_SUBFRAME", SND_PCM_FORMAT_IEC958_SUBFRAME},
510 {NULL, 0}
511 };
512
513 int parse_alsaformat(const char *fmtstr)
514 {
515 struct fmtdef *search;
516
517 for( search= fmtlist; search->fmtname && strcmp(search->fmtname, fmtstr); ++search );
518 if( !search->fmtname ) {
519 fprintf(stderr, "Unknown sample format `%s'. Aborting.\n", fmtstr);
520 exit(1);
521 }
522 return search->format;
523 }
524
525 const char *alsafmtstr(int fmtnum)
526 {
527 struct fmtdef *search;
528
529 for( search= fmtlist; search->fmtname && search->format!=fmtnum; ++search );
530 if( !search->fmtname )
531 return "(unknown)";
532 else
533 return search->fmtname;
534 }
535
536
537 /*============================================================================
538 Printout functions
539 ============================================================================*/
540
541 void printfmtmask(const snd_pcm_format_mask_t *fmask)
542 {
543 int fmt, prevformat= 0;
544
545 for( fmt= 0; fmt <= SND_PCM_FORMAT_LAST; ++fmt )
546 if( snd_pcm_format_mask_test(fmask, (snd_pcm_format_t)fmt) ) {
547 if( prevformat )
548 printf(", ");
549 printf("%s", snd_pcm_format_name((snd_pcm_format_t)fmt));
550 prevformat= 1;
551 }
552 if( !prevformat )
553 printf("(none)");
554 }
555
556

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.