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