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