]> git.alsa-project.org Git - alsa-utils.git/blob - speaker-test/speaker-test.c
d3fec2b3adc96214da81c8aa1ba47185b8533ff2
[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    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
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 };
147
148 static const int        channels4[] = {
149   0, /* Front Left  */
150   1, /* Front Right */
151   3, /* Rear Right  */
152   2, /* Rear Left   */
153 };
154 static 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 };
162 static 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 */
175 static 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
210 static 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
234 static 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
254 static 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
278 static 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
291 static 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
305 typedef union {
306   float f;
307   int32_t i;
308 } value_t;
309
310 static 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  */
383 typedef struct {
384   double a;
385   double s;
386   double c;
387 } sine_t;
388
389 static 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
397 static 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  */
415 static 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  */
429 static 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  */
443 static 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
454 static 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
574 static 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
609 static 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
642 static 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
670 static const char *wav_file[MAX_CHANNELS];
671 static int wav_file_size[MAX_CHANNELS];
672
673 struct 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
703 static 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
714 static 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
771 static 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
806 static 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
854 static 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
880 static int pattern;
881 static sine_t sine;
882 static pink_noise_t pink;
883 static st2095_noise_t st2095;
884
885 static 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
903 static 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
953 static int prg_exit(int code)
954 {
955   if (pcm_handle)
956     snd_pcm_close(pcm_handle);
957   exit(code);
958   return code;
959 }
960
961 static 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
977 static 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
1011 int 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 }