speaker-test.c (35756B)
1 /* 2 * Copyright (C) 2000-2004 James Courtier-Dutton 3 * Copyright (C) 2005 Nathan Hurst 4 * 5 * This file is part of the speaker-test tool. 6 * 7 * This small program sends a simple sinusoidal wave to your speakers. 8 * 9 * speaker-test is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * speaker-test is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 22 * 23 * 24 * Main program by James Courtier-Dutton (including some source code fragments from the alsa project.) 25 * Some cleanup from Daniel Caujolle-Bert <segfault@club-internet.fr> 26 * Pink noise option added Nathan Hurst, 27 * based on generator by Phil Burk (pink.c) 28 * 29 * Changelog: 30 * 0.0.8 Added support for pink noise output. 31 * Changelog: 32 * 0.0.7 Added support for more than 6 channels. 33 * Changelog: 34 * 0.0.6 Added support for different sample formats. 35 * 36 * $Id: speaker_test.c,v 1.00 2003/11/26 19:43:38 jcdutton Exp $ 37 */ 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sched.h> 43 #include <errno.h> 44 #include <getopt.h> 45 #include <inttypes.h> 46 #include <ctype.h> 47 #include <byteswap.h> 48 #include <signal.h> 49 50 #define ALSA_PCM_NEW_HW_PARAMS_API 51 #define ALSA_PCM_NEW_SW_PARAMS_API 52 #include <alsa/asoundlib.h> 53 #include <sys/time.h> 54 #include <math.h> 55 #include "pink.h" 56 #include "aconfig.h" 57 #include "gettext.h" 58 #include "version.h" 59 60 #ifdef ENABLE_NLS 61 #include <locale.h> 62 #endif 63 64 #ifdef SND_CHMAP_API_VERSION 65 #define CONFIG_SUPPORT_CHMAP 1 66 #endif 67 68 enum { 69 TEST_PINK_NOISE = 1, 70 TEST_SINE, 71 TEST_WAV, 72 TEST_PATTERN, 73 }; 74 75 #define MAX_CHANNELS 16 76 77 #if __BYTE_ORDER == __LITTLE_ENDIAN 78 #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) 79 #define LE_SHORT(v) (v) 80 #define LE_INT(v) (v) 81 #define BE_SHORT(v) bswap_16(v) 82 #define BE_INT(v) bswap_32(v) 83 #else /* __BIG_ENDIAN */ 84 #define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) 85 #define LE_SHORT(v) bswap_16(v) 86 #define LE_INT(v) bswap_32(v) 87 #define BE_SHORT(v) (v) 88 #define BE_INT(v) (v) 89 #endif 90 91 #define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0])) 92 93 static char *device = "default"; /* playback device */ 94 static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ 95 static unsigned int rate = 48000; /* stream rate */ 96 static unsigned int channels = 1; /* count of channels */ 97 static unsigned int speaker = 0; /* count of channels */ 98 static unsigned int buffer_time = 0; /* ring buffer length in us */ 99 static unsigned int period_time = 0; /* period time in us */ 100 static unsigned int nperiods = 4; /* number of periods */ 101 static double freq = 440.0; /* sinusoidal wave frequency in Hz */ 102 static int test_type = TEST_PINK_NOISE; /* Test type. 1 = noise, 2 = sine wave */ 103 static float generator_scale = 0.8; /* Scale to use for sine volume */ 104 static pink_noise_t pink; 105 static snd_pcm_uframes_t buffer_size; 106 static snd_pcm_uframes_t period_size; 107 static const char *given_test_wav_file = NULL; 108 static char *wav_file_dir = SOUNDSDIR; 109 static int debug = 0; 110 static int force_frequency = 0; 111 static int in_aborting = 0; 112 static snd_pcm_t *pcm_handle = NULL; 113 114 #ifdef CONFIG_SUPPORT_CHMAP 115 static snd_pcm_chmap_t *channel_map; 116 static int channel_map_set; 117 static int *ordered_channels; 118 #endif 119 120 static const char *const channel_name[MAX_CHANNELS] = { 121 /* 0 */ N_("Front Left"), 122 /* 1 */ N_("Front Right"), 123 /* 2 */ N_("Rear Left"), 124 /* 3 */ N_("Rear Right"), 125 /* 4 */ N_("Center"), 126 /* 5 */ N_("LFE"), 127 /* 6 */ N_("Side Left"), 128 /* 7 */ N_("Side Right"), 129 /* 8 */ N_("Channel 9"), 130 /* 9 */ N_("Channel 10"), 131 /* 10 */ N_("Channel 11"), 132 /* 11 */ N_("Channel 12"), 133 /* 12 */ N_("Channel 13"), 134 /* 13 */ N_("Channel 14"), 135 /* 14 */ N_("Channel 15"), 136 /* 15 */ N_("Channel 16") 137 }; 138 139 static const int channels4[] = { 140 0, /* Front Left */ 141 1, /* Front Right */ 142 3, /* Rear Right */ 143 2, /* Rear Left */ 144 }; 145 static const int channels6[] = { 146 0, /* Front Left */ 147 4, /* Center */ 148 1, /* Front Right */ 149 3, /* Rear Right */ 150 2, /* Rear Left */ 151 5, /* LFE */ 152 }; 153 static const int channels8[] = { 154 0, /* Front Left */ 155 4, /* Center */ 156 1, /* Front Right */ 157 7, /* Side Right */ 158 3, /* Rear Right */ 159 2, /* Rear Left */ 160 6, /* Side Left */ 161 5, /* LFE */ 162 }; 163 164 #ifdef CONFIG_SUPPORT_CHMAP 165 /* circular clockwise and bottom-to-top order */ 166 static const int channel_order[] = { 167 [SND_CHMAP_FLW] = 10, 168 [SND_CHMAP_FL] = 20, 169 [SND_CHMAP_TFL] = 30, 170 [SND_CHMAP_FLC] = 40, 171 [SND_CHMAP_TFLC] = 50, 172 [SND_CHMAP_FC] = 60, 173 [SND_CHMAP_TFC] = 70, 174 [SND_CHMAP_FRC] = 80, 175 [SND_CHMAP_TFRC] = 90, 176 [SND_CHMAP_FR] = 100, 177 [SND_CHMAP_TFR] = 110, 178 [SND_CHMAP_FRW] = 120, 179 [SND_CHMAP_SR] = 130, 180 [SND_CHMAP_TSR] = 140, 181 [SND_CHMAP_RR] = 150, 182 [SND_CHMAP_TRR] = 160, 183 [SND_CHMAP_RRC] = 170, 184 [SND_CHMAP_RC] = 180, 185 [SND_CHMAP_TRC] = 190, 186 [SND_CHMAP_RLC] = 200, 187 [SND_CHMAP_RL] = 210, 188 [SND_CHMAP_TRL] = 220, 189 [SND_CHMAP_SL] = 230, 190 [SND_CHMAP_TSL] = 240, 191 [SND_CHMAP_BC] = 250, 192 [SND_CHMAP_TC] = 260, 193 [SND_CHMAP_LLFE] = 270, 194 [SND_CHMAP_LFE] = 280, 195 [SND_CHMAP_RLFE] = 290, 196 /* not in table = 10000 */ 197 [SND_CHMAP_UNKNOWN] = 20000, 198 [SND_CHMAP_NA] = 30000, 199 }; 200 201 static int chpos_cmp(const void *chnum1p, const void *chnum2p) 202 { 203 int chnum1 = *(int *)chnum1p; 204 int chnum2 = *(int *)chnum2p; 205 int chpos1 = channel_map->pos[chnum1]; 206 int chpos2 = channel_map->pos[chnum2]; 207 int weight1 = 10000; 208 int weight2 = 10000; 209 210 if (chpos1 < ARRAY_SIZE(channel_order) && channel_order[chpos1]) 211 weight1 = channel_order[chpos1]; 212 if (chpos2 < ARRAY_SIZE(channel_order) && channel_order[chpos2]) 213 weight2 = channel_order[chpos2]; 214 215 if (weight1 == weight2) { 216 /* order by channel number if both have the same position (e.g. UNKNOWN) 217 * or if neither is in channel_order[] */ 218 return chnum1 - chnum2; 219 } 220 221 /* order according to channel_order[] */ 222 return weight1 - weight2; 223 } 224 225 static int *order_channels(void) 226 { 227 /* create a (playback order => channel number) table with channels ordered 228 * according to channel_order[] values */ 229 int i; 230 int *ordered_chs; 231 232 ordered_chs = calloc(channel_map->channels, sizeof(*ordered_chs)); 233 if (!ordered_chs) 234 return NULL; 235 236 for (i = 0; i < channel_map->channels; i++) 237 ordered_chs[i] = i; 238 239 qsort(ordered_chs, channel_map->channels, sizeof(*ordered_chs), chpos_cmp); 240 241 return ordered_chs; 242 } 243 #endif 244 245 static int get_speaker_channel(int chn) 246 { 247 #ifdef CONFIG_SUPPORT_CHMAP 248 if (channel_map_set || (ordered_channels && chn >= channel_map->channels)) 249 return chn; 250 if (ordered_channels) 251 return ordered_channels[chn]; 252 #endif 253 254 switch (channels) { 255 case 4: 256 chn = channels4[chn]; 257 break; 258 case 6: 259 chn = channels6[chn]; 260 break; 261 case 8: 262 chn = channels8[chn]; 263 break; 264 } 265 266 return chn; 267 } 268 269 static const char *get_channel_name(int chn) 270 { 271 #ifdef CONFIG_SUPPORT_CHMAP 272 if (channel_map) { 273 const char *name = NULL; 274 if (chn < channel_map->channels) 275 name = snd_pcm_chmap_long_name(channel_map->pos[chn]); 276 return name ? name : "Unknown"; 277 } 278 #endif 279 return gettext(channel_name[chn]); 280 } 281 282 static const int supported_formats[] = { 283 SND_PCM_FORMAT_S8, 284 SND_PCM_FORMAT_S16_LE, 285 SND_PCM_FORMAT_S16_BE, 286 SND_PCM_FORMAT_FLOAT_LE, 287 SND_PCM_FORMAT_S32_LE, 288 SND_PCM_FORMAT_S32_BE, 289 -1 290 }; 291 292 static void generate_sine(uint8_t *frames, int channel, int count, double *_phase) { 293 double phase = *_phase; 294 double max_phase = 1.0 / freq; 295 double step = 1.0 / (double)rate; 296 double res; 297 float fres; 298 int chn; 299 int32_t ires; 300 int8_t *samp8 = (int8_t*) frames; 301 int16_t *samp16 = (int16_t*) frames; 302 int32_t *samp32 = (int32_t*) frames; 303 float *samp_f = (float*) frames; 304 305 while (count-- > 0) { 306 for(chn=0;chn<channels;chn++) { 307 switch (format) { 308 case SND_PCM_FORMAT_S8: 309 if (chn==channel) { 310 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale * 0x7fffffff; 311 ires = res; 312 *samp8++ = ires >> 24; 313 } else { 314 *samp8++ = 0; 315 } 316 break; 317 case SND_PCM_FORMAT_S16_LE: 318 if (chn==channel) { 319 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale * 0x7fffffff; 320 ires = res; 321 *samp16++ = LE_SHORT(ires >> 16); 322 } else { 323 *samp16++ = 0; 324 } 325 break; 326 case SND_PCM_FORMAT_S16_BE: 327 if (chn==channel) { 328 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale * 0x7fffffff; 329 ires = res; 330 *samp16++ = BE_SHORT(ires >> 16); 331 } else { 332 *samp16++ = 0; 333 } 334 break; 335 case SND_PCM_FORMAT_FLOAT_LE: 336 if (chn==channel) { 337 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale; 338 fres = res; 339 *samp_f++ = fres; 340 } else { 341 *samp_f++ = 0.0; 342 } 343 break; 344 case SND_PCM_FORMAT_S32_LE: 345 if (chn==channel) { 346 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale * 0x7fffffff; 347 ires = res; 348 *samp32++ = LE_INT(ires); 349 } else { 350 *samp32++ = 0; 351 } 352 break; 353 case SND_PCM_FORMAT_S32_BE: 354 if (chn==channel) { 355 res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * generator_scale * 0x7fffffff; 356 ires = res; 357 *samp32++ = BE_INT(ires); 358 } else { 359 *samp32++ = 0; 360 } 361 break; 362 default: 363 ; 364 } 365 } 366 367 phase += step; 368 if (phase >= max_phase) 369 phase -= max_phase; 370 } 371 372 *_phase = phase; 373 } 374 375 /* Pink noise is a better test than sine wave because we can tell 376 * where pink noise is coming from more easily that a sine wave. 377 */ 378 379 380 static void generate_pink_noise( uint8_t *frames, int channel, int count) { 381 double res; 382 int chn; 383 int32_t ires; 384 int8_t *samp8 = (int8_t*) frames; 385 int16_t *samp16 = (int16_t*) frames; 386 int32_t *samp32 = (int32_t*) frames; 387 388 while (count-- > 0) { 389 for(chn=0;chn<channels;chn++) { 390 switch (format) { 391 case SND_PCM_FORMAT_S8: 392 if (chn==channel) { 393 res = generate_pink_noise_sample(&pink) * generator_scale * 0x07fffffff; 394 ires = res; 395 *samp8++ = ires >> 24; 396 } else { 397 *samp8++ = 0; 398 } 399 break; 400 case SND_PCM_FORMAT_S16_LE: 401 if (chn==channel) { 402 res = generate_pink_noise_sample(&pink) * generator_scale * 0x07fffffff; 403 ires = res; 404 *samp16++ = LE_SHORT(ires >> 16); 405 } else { 406 *samp16++ = 0; 407 } 408 break; 409 case SND_PCM_FORMAT_S16_BE: 410 if (chn==channel) { 411 res = generate_pink_noise_sample(&pink) * generator_scale * 0x07fffffff; 412 ires = res; 413 *samp16++ = BE_SHORT(ires >> 16); 414 } else { 415 *samp16++ = 0; 416 } 417 break; 418 case SND_PCM_FORMAT_S32_LE: 419 if (chn==channel) { 420 res = generate_pink_noise_sample(&pink) * generator_scale * 0x07fffffff; 421 ires = res; 422 *samp32++ = LE_INT(ires); 423 } else { 424 *samp32++ = 0; 425 } 426 break; 427 case SND_PCM_FORMAT_S32_BE: 428 if (chn==channel) { 429 res = generate_pink_noise_sample(&pink) * generator_scale * 0x07fffffff; 430 ires = res; 431 *samp32++ = BE_INT(ires); 432 } else { 433 *samp32++ = 0; 434 } 435 break; 436 default: 437 ; 438 } 439 } 440 } 441 } 442 443 /* 444 * useful for tests 445 */ 446 static void generate_pattern(uint8_t *frames, int channel, int count, int *_pattern) { 447 int pattern = *_pattern; 448 int chn; 449 int8_t *samp8 = (int8_t*) frames; 450 int16_t *samp16 = (int16_t*) frames; 451 int32_t *samp32 = (int32_t*) frames; 452 float *samp_f = (float*) frames; 453 454 while (count-- > 0) { 455 for(chn=0;chn<channels;chn++,pattern++) { 456 switch (format) { 457 case SND_PCM_FORMAT_S8: 458 if (chn==channel) { 459 *samp8++ = pattern & 0xff; 460 } else { 461 *samp8++ = 0; 462 } 463 break; 464 case SND_PCM_FORMAT_S16_LE: 465 if (chn==channel) { 466 *samp16++ = LE_SHORT(pattern & 0xfffff); 467 } else { 468 *samp16++ = 0; 469 } 470 break; 471 case SND_PCM_FORMAT_S16_BE: 472 if (chn==channel) { 473 *samp16++ = BE_SHORT(pattern & 0xffff); 474 } else { 475 *samp16++ = 0; 476 } 477 break; 478 case SND_PCM_FORMAT_FLOAT_LE: 479 if (chn==channel) { 480 *samp_f++ = LE_INT(((double)pattern) / INT32_MAX); 481 } else { 482 *samp_f++ = 0.0; 483 } 484 break; 485 case SND_PCM_FORMAT_S32_LE: 486 if (chn==channel) { 487 *samp32++ = LE_INT(pattern); 488 } else { 489 *samp32++ = 0; 490 } 491 break; 492 case SND_PCM_FORMAT_S32_BE: 493 if (chn==channel) { 494 *samp32++ = BE_INT(pattern); 495 } else { 496 *samp32++ = 0; 497 } 498 break; 499 default: 500 ; 501 } 502 } 503 } 504 505 *_pattern = pattern; 506 } 507 508 static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { 509 unsigned int rrate; 510 int err; 511 snd_pcm_uframes_t period_size_min; 512 snd_pcm_uframes_t period_size_max; 513 snd_pcm_uframes_t buffer_size_min; 514 snd_pcm_uframes_t buffer_size_max; 515 516 /* choose all parameters */ 517 err = snd_pcm_hw_params_any(handle, params); 518 if (err < 0) { 519 fprintf(stderr, _("Broken configuration for playback: no configurations available: %s\n"), snd_strerror(err)); 520 return err; 521 } 522 523 /* set the interleaved read/write format */ 524 err = snd_pcm_hw_params_set_access(handle, params, access); 525 if (err < 0) { 526 fprintf(stderr, _("Access type not available for playback: %s\n"), snd_strerror(err)); 527 return err; 528 } 529 530 /* set the sample format */ 531 err = snd_pcm_hw_params_set_format(handle, params, format); 532 if (err < 0) { 533 fprintf(stderr, _("Sample format not available for playback: %s\n"), snd_strerror(err)); 534 return err; 535 } 536 537 /* set the count of channels */ 538 err = snd_pcm_hw_params_set_channels(handle, params, channels); 539 if (err < 0) { 540 fprintf(stderr, _("Channels count (%i) not available for playbacks: %s\n"), channels, snd_strerror(err)); 541 return err; 542 } 543 544 /* set the stream rate */ 545 rrate = rate; 546 err = snd_pcm_hw_params_set_rate(handle, params, rate, 0); 547 if (err < 0) { 548 fprintf(stderr, _("Rate %iHz not available for playback: %s\n"), rate, snd_strerror(err)); 549 return err; 550 } 551 552 if (rrate != rate) { 553 fprintf(stderr, _("Rate doesn't match (requested %iHz, get %iHz, err %d)\n"), rate, rrate, err); 554 return -EINVAL; 555 } 556 557 printf(_("Rate set to %iHz (requested %iHz)\n"), rrate, rate); 558 /* set the buffer time */ 559 err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min); 560 err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max); 561 err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL); 562 err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL); 563 printf(_("Buffer size range from %lu to %lu\n"),buffer_size_min, buffer_size_max); 564 printf(_("Period size range from %lu to %lu\n"),period_size_min, period_size_max); 565 if (period_time > 0) { 566 printf(_("Requested period time %u us\n"), period_time); 567 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, NULL); 568 if (err < 0) { 569 fprintf(stderr, _("Unable to set period time %u us for playback: %s\n"), 570 period_time, snd_strerror(err)); 571 return err; 572 } 573 } 574 if (buffer_time > 0) { 575 printf(_("Requested buffer time %u us\n"), buffer_time); 576 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, NULL); 577 if (err < 0) { 578 fprintf(stderr, _("Unable to set buffer time %u us for playback: %s\n"), 579 buffer_time, snd_strerror(err)); 580 return err; 581 } 582 } 583 if (! buffer_time && ! period_time) { 584 buffer_size = buffer_size_max; 585 if (! period_time) 586 buffer_size = (buffer_size / nperiods) * nperiods; 587 printf(_("Using max buffer size %lu\n"), buffer_size); 588 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); 589 if (err < 0) { 590 fprintf(stderr, _("Unable to set buffer size %lu for playback: %s\n"), 591 buffer_size, snd_strerror(err)); 592 return err; 593 } 594 } 595 if (! buffer_time || ! period_time) { 596 printf(_("Periods = %u\n"), nperiods); 597 err = snd_pcm_hw_params_set_periods_near(handle, params, &nperiods, NULL); 598 if (err < 0) { 599 fprintf(stderr, _("Unable to set nperiods %u for playback: %s\n"), 600 nperiods, snd_strerror(err)); 601 return err; 602 } 603 } 604 605 /* write the parameters to device */ 606 err = snd_pcm_hw_params(handle, params); 607 if (err < 0) { 608 fprintf(stderr, _("Unable to set hw params for playback: %s\n"), snd_strerror(err)); 609 return err; 610 } 611 612 snd_pcm_hw_params_get_buffer_size(params, &buffer_size); 613 snd_pcm_hw_params_get_period_size(params, &period_size, NULL); 614 printf(_("was set period_size = %lu\n"),period_size); 615 printf(_("was set buffer_size = %lu\n"),buffer_size); 616 if (2*period_size > buffer_size) { 617 fprintf(stderr, _("buffer to small, could not use\n")); 618 return -EINVAL; 619 } 620 621 return 0; 622 } 623 624 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) { 625 int err; 626 627 /* get the current swparams */ 628 err = snd_pcm_sw_params_current(handle, swparams); 629 if (err < 0) { 630 fprintf(stderr, _("Unable to determine current swparams for playback: %s\n"), snd_strerror(err)); 631 return err; 632 } 633 634 /* start the transfer when a buffer is full */ 635 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size); 636 if (err < 0) { 637 fprintf(stderr, _("Unable to set start threshold mode for playback: %s\n"), snd_strerror(err)); 638 return err; 639 } 640 641 /* allow the transfer when at least period_size frames can be processed */ 642 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); 643 if (err < 0) { 644 fprintf(stderr, _("Unable to set avail min for playback: %s\n"), snd_strerror(err)); 645 return err; 646 } 647 648 /* write the parameters to the playback device */ 649 err = snd_pcm_sw_params(handle, swparams); 650 if (err < 0) { 651 fprintf(stderr, _("Unable to set sw params for playback: %s\n"), snd_strerror(err)); 652 return err; 653 } 654 655 return 0; 656 } 657 658 #ifdef CONFIG_SUPPORT_CHMAP 659 static int config_chmap(snd_pcm_t *handle, const char *mapstr) 660 { 661 int err; 662 663 if (mapstr) { 664 channel_map = snd_pcm_chmap_parse_string(mapstr); 665 if (!channel_map) { 666 fprintf(stderr, _("Unable to parse channel map string: %s\n"), mapstr); 667 return -EINVAL; 668 } 669 err = snd_pcm_set_chmap(handle, channel_map); 670 if (err < 0) { 671 fprintf(stderr, _("Unable to set channel map: %s\n"), mapstr); 672 return err; 673 } 674 channel_map_set = 1; 675 return 0; 676 } 677 678 channel_map = snd_pcm_get_chmap(handle); 679 680 /* create a channel order table for default layouts */ 681 if (channel_map) 682 ordered_channels = order_channels(); 683 684 return 0; 685 } 686 #endif 687 688 /* 689 * Underrun and suspend recovery 690 */ 691 692 static int xrun_recovery(snd_pcm_t *handle, int err) { 693 if (err == -EPIPE) { /* under-run */ 694 err = snd_pcm_prepare(handle); 695 if (err < 0) 696 fprintf(stderr, _("Can't recovery from underrun, prepare failed: %s\n"), snd_strerror(err)); 697 return 0; 698 } 699 else if (err == -ESTRPIPE) { 700 701 while ((err = snd_pcm_resume(handle)) == -EAGAIN) 702 sleep(1); /* wait until the suspend flag is released */ 703 704 if (err < 0) { 705 err = snd_pcm_prepare(handle); 706 if (err < 0) 707 fprintf(stderr, _("Can't recovery from suspend, prepare failed: %s\n"), snd_strerror(err)); 708 } 709 710 return 0; 711 } 712 713 return err; 714 } 715 716 /* 717 * Handle WAV files 718 */ 719 720 static const char *wav_file[MAX_CHANNELS]; 721 static int wav_file_size[MAX_CHANNELS]; 722 723 struct wave_header { 724 struct { 725 uint32_t magic; 726 uint32_t length; 727 uint32_t type; 728 } hdr; 729 struct { 730 uint32_t type; 731 uint32_t length; 732 } chunk1; 733 struct { 734 uint16_t format; 735 uint16_t channels; 736 uint32_t rate; 737 uint32_t bytes_per_sec; 738 uint16_t sample_size; 739 uint16_t sample_bits; 740 } body; 741 struct { 742 uint32_t type; 743 uint32_t length; 744 } chunk; 745 }; 746 747 #define WAV_RIFF COMPOSE_ID('R','I','F','F') 748 #define WAV_WAVE COMPOSE_ID('W','A','V','E') 749 #define WAV_FMT COMPOSE_ID('f','m','t',' ') 750 #define WAV_DATA COMPOSE_ID('d','a','t','a') 751 #define WAV_PCM_CODE 1 752 753 static const char *search_for_file(const char *name) 754 { 755 char *file; 756 if (*name == '/') 757 return strdup(name); 758 file = malloc(strlen(wav_file_dir) + strlen(name) + 2); 759 if (file) 760 sprintf(file, "%s/%s", wav_file_dir, name); 761 return file; 762 } 763 764 static int check_wav_file(int channel, const char *name) 765 { 766 struct wave_header header; 767 int fd; 768 769 wav_file[channel] = search_for_file(name); 770 if (! wav_file[channel]) { 771 fprintf(stderr, _("No enough memory\n")); 772 return -ENOMEM; 773 } 774 775 if ((fd = open(wav_file[channel], O_RDONLY)) < 0) { 776 fprintf(stderr, _("Cannot open WAV file %s\n"), wav_file[channel]); 777 return -EINVAL; 778 } 779 if (read(fd, &header, sizeof(header)) < (int)sizeof(header)) { 780 fprintf(stderr, _("Invalid WAV file %s\n"), wav_file[channel]); 781 goto error; 782 } 783 784 if (header.hdr.magic != WAV_RIFF || header.hdr.type != WAV_WAVE) { 785 fprintf(stderr, _("Not a WAV file: %s\n"), wav_file[channel]); 786 goto error; 787 } 788 if (header.body.format != LE_SHORT(WAV_PCM_CODE)) { 789 fprintf(stderr, _("Unsupported WAV format %d for %s\n"), 790 LE_SHORT(header.body.format), wav_file[channel]); 791 goto error; 792 } 793 if (header.body.channels != LE_SHORT(1)) { 794 fprintf(stderr, _("%s is not a mono stream (%d channels)\n"), 795 wav_file[channel], LE_SHORT(header.body.channels)); 796 goto error; 797 } 798 if (header.body.rate != LE_INT(rate)) { 799 fprintf(stderr, _("Sample rate doesn't match (%d) for %s\n"), 800 LE_INT(header.body.rate), wav_file[channel]); 801 goto error; 802 } 803 if (header.body.sample_bits != LE_SHORT(16)) { 804 fprintf(stderr, _("Unsupported sample format bits %d for %s\n"), 805 LE_SHORT(header.body.sample_bits), wav_file[channel]); 806 goto error; 807 } 808 if (header.chunk.type != WAV_DATA) { 809 fprintf(stderr, _("Invalid WAV file %s\n"), wav_file[channel]); 810 goto error; 811 } 812 wav_file_size[channel] = LE_INT(header.chunk.length); 813 close(fd); 814 return 0; 815 816 error: 817 close(fd); 818 return -EINVAL; 819 } 820 821 static int setup_wav_file(int chn) 822 { 823 static const char *const wavs[MAX_CHANNELS] = { 824 "Front_Left.wav", 825 "Front_Right.wav", 826 "Rear_Left.wav", 827 "Rear_Right.wav", 828 "Front_Center.wav", 829 "Rear_Center.wav", /* FIXME: should be "Bass" or so */ 830 "Side_Left.wav", 831 "Side_Right.wav", 832 "Channel_9.wav", 833 "Channel_10.wav", 834 "Channel_11.wav", 835 "Channel_12.wav", 836 "Channel_13.wav", 837 "Channel_14.wav", 838 "Channel_15.wav", 839 "Channel_16.wav" 840 }; 841 842 if (given_test_wav_file) 843 return check_wav_file(chn, given_test_wav_file); 844 845 #ifdef CONFIG_SUPPORT_CHMAP 846 if (channel_map && chn < channel_map->channels) { 847 int channel = channel_map->pos[chn] - SND_CHMAP_FL; 848 if (channel >= 0 && channel < MAX_CHANNELS) 849 return check_wav_file(chn, wavs[channel]); 850 } 851 #endif 852 853 return check_wav_file(chn, wavs[chn]); 854 } 855 856 static int read_wav(uint16_t *buf, int channel, int offset, int bufsize) 857 { 858 static FILE *wavfp = NULL; 859 int size; 860 861 if (in_aborting) 862 return -EFAULT; 863 864 if (! wav_file[channel]) { 865 fprintf(stderr, _("Undefined channel %d\n"), channel); 866 return -EINVAL; 867 } 868 869 if (offset >= wav_file_size[channel]) 870 return 0; /* finished */ 871 872 if (! offset) { 873 if (wavfp) 874 fclose(wavfp); 875 wavfp = fopen(wav_file[channel], "r"); 876 if (! wavfp) 877 return -errno; 878 if (fseek(wavfp, sizeof(struct wave_header), SEEK_SET) < 0) 879 return -errno; 880 } 881 if (offset + bufsize > wav_file_size[channel]) 882 bufsize = wav_file_size[channel] - offset; 883 bufsize /= channels; 884 for (size = 0; size < bufsize; size += 2) { 885 int chn; 886 for (chn = 0; chn < channels; chn++) { 887 if (chn == channel) { 888 if (fread(buf, 2, 1, wavfp) != 1) 889 return size; 890 } 891 else 892 *buf = 0; 893 buf++; 894 } 895 } 896 return size; 897 } 898 899 900 /* 901 * Transfer method - write only 902 */ 903 904 static int write_buffer(snd_pcm_t *handle, uint8_t *ptr, int cptr) 905 { 906 int err; 907 908 while (cptr > 0 && !in_aborting) { 909 910 err = snd_pcm_writei(handle, ptr, cptr); 911 912 if (err == -EAGAIN) 913 continue; 914 915 if (err < 0) { 916 fprintf(stderr, _("Write error: %d,%s\n"), err, snd_strerror(err)); 917 if ((err = xrun_recovery(handle, err)) < 0) { 918 fprintf(stderr, _("xrun_recovery failed: %d,%s\n"), err, snd_strerror(err)); 919 return err; 920 } 921 break; /* skip one period */ 922 } 923 924 ptr += snd_pcm_frames_to_bytes(handle, err); 925 cptr -= err; 926 } 927 return 0; 928 } 929 930 static int write_loop(snd_pcm_t *handle, int channel, int periods, uint8_t *frames) 931 { 932 double phase = 0; 933 int pattern = 0; 934 int err, n; 935 936 fflush(stdout); 937 if (test_type == TEST_WAV) { 938 int bufsize = snd_pcm_frames_to_bytes(handle, period_size); 939 n = 0; 940 while ((err = read_wav((uint16_t *)frames, channel, n, bufsize)) > 0 && !in_aborting) { 941 n += err; 942 if ((err = write_buffer(handle, frames, 943 snd_pcm_bytes_to_frames(handle, err * channels))) < 0) 944 break; 945 } 946 if (buffer_size > n && !in_aborting) { 947 snd_pcm_drain(handle); 948 snd_pcm_prepare(handle); 949 } 950 return err; 951 } 952 953 954 if (periods <= 0) 955 periods = 1; 956 957 for(n = 0; n < periods && !in_aborting; n++) { 958 if (test_type == TEST_PINK_NOISE) 959 generate_pink_noise(frames, channel, period_size); 960 else if (test_type == TEST_PATTERN) 961 generate_pattern(frames, channel, period_size, &pattern); 962 else 963 generate_sine(frames, channel, period_size, &phase); 964 965 if ((err = write_buffer(handle, frames, period_size)) < 0) 966 return err; 967 } 968 if (buffer_size > n * period_size && !in_aborting) { 969 snd_pcm_drain(handle); 970 snd_pcm_prepare(handle); 971 } 972 return 0; 973 } 974 975 static int prg_exit(int code) 976 { 977 if (pcm_handle) 978 snd_pcm_close(pcm_handle); 979 exit(code); 980 return code; 981 } 982 983 static void signal_handler(int sig) 984 { 985 if (in_aborting) 986 return; 987 988 in_aborting = 1; 989 990 if (pcm_handle) 991 snd_pcm_abort(pcm_handle); 992 if (sig == SIGABRT) { 993 pcm_handle = NULL; 994 prg_exit(EXIT_FAILURE); 995 } 996 signal(sig, signal_handler); 997 } 998 999 static void help(void) 1000 { 1001 const int *fmt; 1002 1003 printf( 1004 _("Usage: speaker-test [OPTION]... \n" 1005 "-h,--help help\n" 1006 "-D,--device playback device\n" 1007 "-r,--rate stream rate in Hz\n" 1008 "-c,--channels count of channels in stream\n" 1009 "-f,--frequency sine wave frequency in Hz\n" 1010 "-F,--format sample format\n" 1011 "-b,--buffer ring buffer size in us\n" 1012 "-p,--period period size in us\n" 1013 "-P,--nperiods number of periods\n" 1014 "-t,--test pink=use pink noise, sine=use sine wave, wav=WAV file\n" 1015 "-l,--nloops specify number of loops to test, 0 = infinite\n" 1016 "-s,--speaker single speaker test. Values 1=Left, 2=right, etc\n" 1017 "-w,--wavfile Use the given WAV file as a test sound\n" 1018 "-W,--wavdir Specify the directory containing WAV files\n" 1019 "-m,--chmap Specify the channel map to override\n" 1020 "-X,--force-frequency force frequencies outside the 30-8000hz range\n" 1021 "-S,--scale Scale of generated test tones in percent (default=80)\n" 1022 "\n")); 1023 printf(_("Recognized sample formats are:")); 1024 for (fmt = supported_formats; *fmt >= 0; fmt++) { 1025 const char *s = snd_pcm_format_name(*fmt); 1026 if (s) 1027 printf(" %s", s); 1028 } 1029 1030 printf("\n\n"); 1031 } 1032 1033 int main(int argc, char *argv[]) { 1034 snd_pcm_t *handle; 1035 int err, morehelp; 1036 snd_pcm_hw_params_t *hwparams; 1037 snd_pcm_sw_params_t *swparams; 1038 uint8_t *frames; 1039 int chn; 1040 const int *fmt; 1041 double time1,time2,time3; 1042 unsigned int n, nloops; 1043 struct timeval tv1,tv2; 1044 int speakeroptset = 0; 1045 #ifdef CONFIG_SUPPORT_CHMAP 1046 const char *chmap = NULL; 1047 #endif 1048 1049 static const struct option long_option[] = { 1050 {"help", 0, NULL, 'h'}, 1051 {"device", 1, NULL, 'D'}, 1052 {"rate", 1, NULL, 'r'}, 1053 {"channels", 1, NULL, 'c'}, 1054 {"frequency", 1, NULL, 'f'}, 1055 {"format", 1, NULL, 'F'}, 1056 {"buffer", 1, NULL, 'b'}, 1057 {"period", 1, NULL, 'p'}, 1058 {"nperiods", 1, NULL, 'P'}, 1059 {"test", 1, NULL, 't'}, 1060 {"nloops", 1, NULL, 'l'}, 1061 {"speaker", 1, NULL, 's'}, 1062 {"wavfile", 1, NULL, 'w'}, 1063 {"wavdir", 1, NULL, 'W'}, 1064 {"debug", 0, NULL, 'd'}, 1065 {"force-frequency", 0, NULL, 'X'}, 1066 {"scale", 1, NULL, 'S'}, 1067 #ifdef CONFIG_SUPPORT_CHMAP 1068 {"chmap", 1, NULL, 'm'}, 1069 #endif 1070 {NULL, 0, NULL, 0 }, 1071 }; 1072 1073 #ifdef ENABLE_NLS 1074 setlocale(LC_ALL, ""); 1075 textdomain(PACKAGE); 1076 #endif 1077 1078 snd_pcm_hw_params_alloca(&hwparams); 1079 snd_pcm_sw_params_alloca(&swparams); 1080 1081 nloops = 0; 1082 morehelp = 0; 1083 1084 printf("\nspeaker-test %s\n\n", SND_UTIL_VERSION_STR); 1085 while (1) { 1086 int c; 1087 1088 if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:P:t:l:s:w:W:d:XS:" 1089 #ifdef CONFIG_SUPPORT_CHMAP 1090 "m:" 1091 #endif 1092 , long_option, NULL)) < 0) 1093 break; 1094 1095 switch (c) { 1096 case 'h': 1097 morehelp++; 1098 break; 1099 case 'D': 1100 device = strdup(optarg); 1101 break; 1102 case 'F': 1103 format = snd_pcm_format_value(optarg); 1104 for (fmt = supported_formats; *fmt >= 0; fmt++) 1105 if (*fmt == format) 1106 break; 1107 if (*fmt < 0) { 1108 fprintf(stderr, "Format %s is not supported...\n", snd_pcm_format_name(format)); 1109 exit(EXIT_FAILURE); 1110 } 1111 break; 1112 case 'r': 1113 rate = atoi(optarg); 1114 rate = rate < 4000 ? 4000 : rate; 1115 rate = rate > 384000 ? 384000 : rate; 1116 break; 1117 case 'c': 1118 channels = atoi(optarg); 1119 channels = channels < 1 ? 1 : channels; 1120 channels = channels > 1024 ? 1024 : channels; 1121 break; 1122 case 'f': 1123 freq = atof(optarg); 1124 break; 1125 case 'b': 1126 buffer_time = atoi(optarg); 1127 buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time; 1128 break; 1129 case 'p': 1130 period_time = atoi(optarg); 1131 period_time = period_time > 1000000 ? 1000000 : period_time; 1132 break; 1133 case 'P': 1134 nperiods = atoi(optarg); 1135 if (nperiods < 2 || nperiods > 1024) { 1136 fprintf(stderr, _("Invalid number of periods %d\n"), nperiods); 1137 exit(1); 1138 } 1139 break; 1140 case 't': 1141 if (*optarg == 'p') 1142 test_type = TEST_PINK_NOISE; 1143 else if (*optarg == 's') 1144 test_type = TEST_SINE; 1145 else if (*optarg == 'w') 1146 test_type = TEST_WAV; 1147 else if (*optarg == 't') 1148 test_type = TEST_PATTERN; 1149 else if (isdigit(*optarg)) { 1150 test_type = atoi(optarg); 1151 if (test_type < TEST_PINK_NOISE || test_type > TEST_PATTERN) { 1152 fprintf(stderr, _("Invalid test type %s\n"), optarg); 1153 exit(1); 1154 } 1155 } else { 1156 fprintf(stderr, _("Invalid test type %s\n"), optarg); 1157 exit(1); 1158 } 1159 break; 1160 case 'l': 1161 nloops = atoi(optarg); 1162 break; 1163 case 's': 1164 speaker = atoi(optarg); 1165 speaker = speaker < 1 ? 0 : speaker; 1166 speakeroptset = 1; 1167 break; 1168 case 'w': 1169 given_test_wav_file = optarg; 1170 break; 1171 case 'W': 1172 wav_file_dir = optarg; 1173 break; 1174 case 'd': 1175 debug = 1; 1176 break; 1177 case 'X': 1178 force_frequency = 1; 1179 break; 1180 #ifdef CONFIG_SUPPORT_CHMAP 1181 case 'm': 1182 chmap = optarg; 1183 break; 1184 #endif 1185 case 'S': 1186 generator_scale = atoi(optarg) / 100.0; 1187 break; 1188 default: 1189 fprintf(stderr, _("Unknown option '%c'\n"), c); 1190 exit(EXIT_FAILURE); 1191 break; 1192 } 1193 } 1194 1195 if (morehelp) { 1196 help(); 1197 exit(EXIT_SUCCESS); 1198 } 1199 1200 if (speakeroptset) { 1201 speaker = speaker > channels ? 0 : speaker; 1202 if (speaker==0) { 1203 fprintf(stderr, _("Invalid parameter for -s option.\n")); 1204 exit(EXIT_FAILURE); 1205 } 1206 } 1207 1208 if (!force_frequency) { 1209 freq = freq < 30.0 ? 30.0 : freq; 1210 freq = freq > 8000.0 ? 8000.0 : freq; 1211 } else { 1212 freq = freq < 1.0 ? 1.0 : freq; 1213 } 1214 1215 if (test_type == TEST_WAV) 1216 format = SND_PCM_FORMAT_S16_LE; /* fixed format */ 1217 1218 printf(_("Playback device is %s\n"), device); 1219 printf(_("Stream parameters are %iHz, %s, %i channels\n"), rate, snd_pcm_format_name(format), channels); 1220 switch (test_type) { 1221 case TEST_PINK_NOISE: 1222 printf(_("Using 16 octaves of pink noise\n")); 1223 break; 1224 case TEST_SINE: 1225 printf(_("Sine wave rate is %.4fHz\n"), freq); 1226 break; 1227 case TEST_WAV: 1228 printf(_("WAV file(s)\n")); 1229 break; 1230 1231 } 1232 1233 signal(SIGINT, signal_handler); 1234 signal(SIGTERM, signal_handler); 1235 signal(SIGABRT, signal_handler); 1236 1237 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 1238 printf(_("Playback open error: %d,%s\n"), err,snd_strerror(err)); 1239 prg_exit(EXIT_FAILURE); 1240 } 1241 pcm_handle = handle; 1242 1243 if ((err = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 1244 printf(_("Setting of hwparams failed: %s\n"), snd_strerror(err)); 1245 prg_exit(EXIT_FAILURE); 1246 } 1247 if ((err = set_swparams(handle, swparams)) < 0) { 1248 printf(_("Setting of swparams failed: %s\n"), snd_strerror(err)); 1249 prg_exit(EXIT_FAILURE); 1250 } 1251 1252 #ifdef CONFIG_SUPPORT_CHMAP 1253 err = config_chmap(handle, chmap); 1254 if (err < 0) 1255 prg_exit(EXIT_FAILURE); 1256 #endif 1257 1258 if (debug) { 1259 snd_output_t *log; 1260 err = snd_output_stdio_attach(&log, stderr, 0); 1261 if (err >= 0) { 1262 snd_pcm_dump(handle, log); 1263 snd_output_close(log); 1264 } 1265 } 1266 1267 frames = malloc(snd_pcm_frames_to_bytes(handle, period_size)); 1268 if (test_type == TEST_PINK_NOISE) 1269 initialize_pink_noise(&pink, 16); 1270 1271 if (frames == NULL) { 1272 fprintf(stderr, _("No enough memory\n")); 1273 prg_exit(EXIT_FAILURE); 1274 } 1275 1276 if (speaker==0) { 1277 1278 if (test_type == TEST_WAV) { 1279 for (chn = 0; chn < channels; chn++) { 1280 if (setup_wav_file(get_speaker_channel(chn)) < 0) 1281 prg_exit(EXIT_FAILURE); 1282 } 1283 } 1284 1285 for (n = 0; (! nloops || n < nloops) && !in_aborting; n++) { 1286 1287 gettimeofday(&tv1, NULL); 1288 for(chn = 0; chn < channels; chn++) { 1289 int channel = get_speaker_channel(chn); 1290 printf(" %d - %s\n", channel, get_channel_name(channel)); 1291 1292 err = write_loop(handle, channel, ((rate*3)/period_size), frames); 1293 1294 if (err < 0) { 1295 fprintf(stderr, _("Transfer failed: %s\n"), snd_strerror(err)); 1296 prg_exit(EXIT_SUCCESS); 1297 } 1298 } 1299 gettimeofday(&tv2, NULL); 1300 time1 = (double)tv1.tv_sec + ((double)tv1.tv_usec / 1000000.0); 1301 time2 = (double)tv2.tv_sec + ((double)tv2.tv_usec / 1000000.0); 1302 time3 = time2 - time1; 1303 printf(_("Time per period = %lf\n"), time3 ); 1304 } 1305 } else { 1306 chn = get_speaker_channel(speaker - 1); 1307 1308 if (test_type == TEST_WAV) { 1309 if (setup_wav_file(chn) < 0) 1310 prg_exit(EXIT_FAILURE); 1311 } 1312 1313 printf(" - %s\n", get_channel_name(chn)); 1314 err = write_loop(handle, chn, ((rate*5)/period_size), frames); 1315 1316 if (err < 0) { 1317 fprintf(stderr, _("Transfer failed: %s\n"), snd_strerror(err)); 1318 } 1319 } 1320 1321 snd_pcm_drain(handle); 1322 1323 free(frames); 1324 #ifdef CONFIG_SUPPORT_CHMAP 1325 free(ordered_channels); 1326 #endif 1327 1328 return prg_exit(EXIT_SUCCESS); 1329 }