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