Tizen 2.1 base
[external/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  *
29  * Changelog:
30  *   0.0.8 Added support for pink noise output.
31  * Changelog:
32  *   0.0.7 Added support for more than 6 channels.
33  * Changelog:
34  *   0.0.6 Added support for different sample formats.
35  *
36  * $Id: speaker_test.c,v 1.00 2003/11/26 19:43:38 jcdutton Exp $
37  */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sched.h>
43 #include <errno.h>
44 #include <getopt.h>
45 #include <inttypes.h>
46 #include <ctype.h>
47 #include <byteswap.h>
48
49 #define ALSA_PCM_NEW_HW_PARAMS_API
50 #define ALSA_PCM_NEW_SW_PARAMS_API
51 #include <alsa/asoundlib.h>
52 #include <sys/time.h>
53 #include <math.h>
54 #include "pink.h"
55 #include "aconfig.h"
56 #include "gettext.h"
57 #include "version.h"
58
59 #ifdef ENABLE_NLS
60 #include <locale.h>
61 #endif
62
63 enum {
64   TEST_PINK_NOISE = 1,
65   TEST_SINE,
66   TEST_WAV,
67   TEST_PATTERN,
68 };
69
70 #define MAX_CHANNELS    16
71
72 #if __BYTE_ORDER == __LITTLE_ENDIAN
73 #define COMPOSE_ID(a,b,c,d)     ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
74 #define LE_SHORT(v)             (v)
75 #define LE_INT(v)               (v)
76 #define BE_SHORT(v)             bswap_16(v)
77 #define BE_INT(v)               bswap_32(v)
78 #else /* __BIG_ENDIAN */
79 #define COMPOSE_ID(a,b,c,d)     ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
80 #define LE_SHORT(v)             bswap_16(v)
81 #define LE_INT(v)               bswap_32(v)
82 #define BE_SHORT(v)             (v)
83 #define BE_INT(v)               (v)
84 #endif
85
86 static char              *device      = "default";       /* playback device */
87 static snd_pcm_format_t   format      = SND_PCM_FORMAT_S16; /* sample format */
88 static unsigned int       rate        = 48000;              /* stream rate */
89 static unsigned int       channels    = 1;                  /* count of channels */
90 static unsigned int       speaker     = 0;                  /* count of channels */
91 static unsigned int       buffer_time = 0;                  /* ring buffer length in us */
92 static unsigned int       period_time = 0;                  /* period time in us */
93 static unsigned int       nperiods    = 4;                  /* number of periods */
94 static double             freq        = 440.0;              /* sinusoidal wave frequency in Hz */
95 static int                test_type   = TEST_PINK_NOISE;    /* Test type. 1 = noise, 2 = sine wave */
96 static pink_noise_t pink;
97 static snd_pcm_uframes_t  buffer_size;
98 static snd_pcm_uframes_t  period_size;
99 static const char *given_test_wav_file = NULL;
100 static char *wav_file_dir = SOUNDSDIR;
101 static int debug = 0;
102
103 static const char *const channel_name[MAX_CHANNELS] = {
104   /*  0 */ N_("Front Left"),
105   /*  1 */ N_("Front Right"),
106   /*  2 */ N_("Rear Left"),
107   /*  3 */ N_("Rear Right"),
108   /*  4 */ N_("Center"),
109   /*  5 */ N_("LFE"),
110   /*  6 */ N_("Side Left"),
111   /*  7 */ N_("Side Right"),
112   /*  8 */ N_("Channel 9"),
113   /*  9 */ N_("Channel 10"),
114   /* 10 */ N_("Channel 11"),
115   /* 11 */ N_("Channel 12"),
116   /* 12 */ N_("Channel 13"),
117   /* 13 */ N_("Channel 14"),
118   /* 14 */ N_("Channel 15"),
119   /* 15 */ N_("Channel 16")
120 };
121
122 static const int        channels4[] = {
123   0, /* Front Left  */
124   1, /* Front Right */
125   3, /* Rear Right  */
126   2, /* Rear Left   */
127 };
128 static const int        channels6[] = {
129   0, /* Front Left  */
130   4, /* Center      */
131   1, /* Front Right */
132   3, /* Rear Right  */
133   2, /* Rear Left   */
134   5, /* LFE         */
135 };
136 static const int        channels8[] = {
137   0, /* Front Left  */
138   4, /* Center      */
139   1, /* Front Right */
140   7, /* Side Right  */
141   3, /* Rear Right  */
142   2, /* Rear Left   */
143   6, /* Side Left   */
144   5, /* LFE         */
145 };
146 static const int        supported_formats[] = {
147   SND_PCM_FORMAT_S8,
148   SND_PCM_FORMAT_S16_LE,
149   SND_PCM_FORMAT_S16_BE,
150   SND_PCM_FORMAT_FLOAT_LE,
151   SND_PCM_FORMAT_S32_LE,
152   SND_PCM_FORMAT_S32_BE,
153   -1
154 };
155
156 static void generate_sine(uint8_t *frames, int channel, int count, double *_phase) {
157   double phase = *_phase;
158   double max_phase = 1.0 / freq;
159   double step = 1.0 / (double)rate;
160   double res;
161   float fres;
162   int    chn;
163   int32_t  ires;
164   int8_t *samp8 = (int8_t*) frames;
165   int16_t *samp16 = (int16_t*) frames;
166   int32_t *samp32 = (int32_t*) frames;
167   float   *samp_f = (float*) frames;
168
169   while (count-- > 0) {
170     for(chn=0;chn<channels;chn++) {
171       switch (format) {
172       case SND_PCM_FORMAT_S8:
173         if (chn==channel) {
174           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
175           ires = res;
176           *samp8++ = ires >> 24;
177         } else {
178           *samp8++ = 0;
179         }
180         break;
181       case SND_PCM_FORMAT_S16_LE:
182         if (chn==channel) {
183           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
184           ires = res;
185           *samp16++ = LE_SHORT(ires >> 16);
186         } else {
187           *samp16++ = 0;
188         }
189         break;
190       case SND_PCM_FORMAT_S16_BE:
191         if (chn==channel) {
192           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
193           ires = res;
194           *samp16++ = BE_SHORT(ires >> 16);
195         } else {
196           *samp16++ = 0;
197         }
198         break;
199       case SND_PCM_FORMAT_FLOAT_LE:
200         if (chn==channel) {
201           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0.75 ; /* Don't use MAX volume */
202           fres = res;
203           *samp_f++ = fres;
204         } else {
205           *samp_f++ = 0.0;
206         }
207         break;
208       case SND_PCM_FORMAT_S32_LE:
209         if (chn==channel) {
210           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
211           ires = res;
212           *samp32++ = LE_INT(ires);
213         } else {
214           *samp32++ = 0;
215         }
216         break;
217       case SND_PCM_FORMAT_S32_BE:
218         if (chn==channel) {
219           res = (sin((phase * 2 * M_PI) / max_phase - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
220           ires = res;
221           *samp32++ = BE_INT(ires);
222         } else {
223           *samp32++ = 0;
224         }
225         break;
226       default:
227         ;
228       }
229     }
230
231     phase += step;
232     if (phase >= max_phase)
233       phase -= max_phase;
234   }
235
236   *_phase = phase;
237 }
238
239 /* Pink noise is a better test than sine wave because we can tell
240  * where pink noise is coming from more easily that a sine wave.
241  */
242
243
244 static void generate_pink_noise( uint8_t *frames, int channel, int count) {
245   double   res;
246   int      chn;
247   int32_t  ires;
248   int8_t  *samp8 = (int8_t*) frames;
249   int16_t *samp16 = (int16_t*) frames;
250   int32_t *samp32 = (int32_t*) frames;
251
252   while (count-- > 0) {
253     for(chn=0;chn<channels;chn++) {
254       switch (format) {
255       case SND_PCM_FORMAT_S8:
256         if (chn==channel) {
257           res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */
258           ires = res;
259           *samp8++ = ires >> 24;
260         } else {
261           *samp8++ = 0;
262         }
263         break;
264       case SND_PCM_FORMAT_S16_LE:
265         if (chn==channel) {
266           res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */
267           ires = res;
268           *samp16++ = LE_SHORT(ires >> 16);
269         } else {
270           *samp16++ = 0;
271         }
272         break;
273       case SND_PCM_FORMAT_S16_BE:
274         if (chn==channel) {
275           res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */
276           ires = res;
277           *samp16++ = BE_SHORT(ires >> 16);
278         } else {
279           *samp16++ = 0;
280         }
281         break;
282       case SND_PCM_FORMAT_S32_LE:
283         if (chn==channel) {
284           res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */
285           ires = res;
286           *samp32++ = LE_INT(ires);
287         } else {
288           *samp32++ = 0;
289         }
290         break;
291       case SND_PCM_FORMAT_S32_BE:
292         if (chn==channel) {
293           res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */
294           ires = res;
295           *samp32++ = BE_INT(ires);
296         } else {
297           *samp32++ = 0;
298         }
299         break;
300       default:
301         ;
302       }
303     }
304   }
305 }
306
307 /*
308  * useful for tests
309  */
310 static void generate_pattern(uint8_t *frames, int channel, int count, int *_pattern) {
311   int pattern = *_pattern;
312   int    chn;
313   int8_t *samp8 = (int8_t*) frames;
314   int16_t *samp16 = (int16_t*) frames;
315   int32_t *samp32 = (int32_t*) frames;
316   float   *samp_f = (float*) frames;
317
318   while (count-- > 0) {
319     for(chn=0;chn<channels;chn++,pattern++) {
320       switch (format) {
321       case SND_PCM_FORMAT_S8:
322         if (chn==channel) {
323           *samp8++ = pattern & 0xff;
324         } else {
325           *samp8++ = 0;
326         }
327         break;
328       case SND_PCM_FORMAT_S16_LE:
329         if (chn==channel) {
330           *samp16++ = LE_SHORT(pattern & 0xfffff);
331         } else {
332           *samp16++ = 0;
333         }
334         break;
335       case SND_PCM_FORMAT_S16_BE:
336         if (chn==channel) {
337           *samp16++ = BE_SHORT(pattern & 0xffff);
338         } else {
339           *samp16++ = 0;
340         }
341         break;
342       case SND_PCM_FORMAT_FLOAT_LE:
343         if (chn==channel) {
344           *samp_f++ = LE_INT(((double)pattern) / INT32_MAX);
345         } else {
346           *samp_f++ = 0.0;
347         }
348         break;
349       case SND_PCM_FORMAT_S32_LE:
350         if (chn==channel) {
351           *samp32++ = LE_INT(pattern);
352         } else {
353           *samp32++ = 0;
354         }
355         break;
356       case SND_PCM_FORMAT_S32_BE:
357         if (chn==channel) {
358           *samp32++ = BE_INT(pattern);
359         } else {
360           *samp32++ = 0;
361         }
362         break;
363       default:
364         ;
365       }
366     }
367   }
368
369   *_pattern = pattern;
370 }
371
372 static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) {
373   unsigned int rrate;
374   int          err;
375   snd_pcm_uframes_t     period_size_min;
376   snd_pcm_uframes_t     period_size_max;
377   snd_pcm_uframes_t     buffer_size_min;
378   snd_pcm_uframes_t     buffer_size_max;
379
380   /* choose all parameters */
381   err = snd_pcm_hw_params_any(handle, params);
382   if (err < 0) {
383     fprintf(stderr, _("Broken configuration for playback: no configurations available: %s\n"), snd_strerror(err));
384     return err;
385   }
386
387   /* set the interleaved read/write format */
388   err = snd_pcm_hw_params_set_access(handle, params, access);
389   if (err < 0) {
390     fprintf(stderr, _("Access type not available for playback: %s\n"), snd_strerror(err));
391     return err;
392   }
393
394   /* set the sample format */
395   err = snd_pcm_hw_params_set_format(handle, params, format);
396   if (err < 0) {
397     fprintf(stderr, _("Sample format not available for playback: %s\n"), snd_strerror(err));
398     return err;
399   }
400
401   /* set the count of channels */
402   err = snd_pcm_hw_params_set_channels(handle, params, channels);
403   if (err < 0) {
404     fprintf(stderr, _("Channels count (%i) not available for playbacks: %s\n"), channels, snd_strerror(err));
405     return err;
406   }
407
408   /* set the stream rate */
409   rrate = rate;
410   err = snd_pcm_hw_params_set_rate(handle, params, rate, 0);
411   if (err < 0) {
412     fprintf(stderr, _("Rate %iHz not available for playback: %s\n"), rate, snd_strerror(err));
413     return err;
414   }
415
416   if (rrate != rate) {
417     fprintf(stderr, _("Rate doesn't match (requested %iHz, get %iHz, err %d)\n"), rate, rrate, err);
418     return -EINVAL;
419   }
420
421   printf(_("Rate set to %iHz (requested %iHz)\n"), rrate, rate);
422   /* set the buffer time */
423   err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
424   err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
425   err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL);
426   err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL);
427   printf(_("Buffer size range from %lu to %lu\n"),buffer_size_min, buffer_size_max);
428   printf(_("Period size range from %lu to %lu\n"),period_size_min, period_size_max);
429   if (period_time > 0) {
430     printf(_("Requested period time %u us\n"), period_time);
431     err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, NULL);
432     if (err < 0) {
433       fprintf(stderr, _("Unable to set period time %u us for playback: %s\n"),
434              period_time, snd_strerror(err));
435       return err;
436     }
437   }
438   if (buffer_time > 0) {
439     printf(_("Requested buffer time %u us\n"), buffer_time);
440     err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, NULL);
441     if (err < 0) {
442       fprintf(stderr, _("Unable to set buffer time %u us for playback: %s\n"),
443              buffer_time, snd_strerror(err));
444       return err;
445     }
446   }
447   if (! buffer_time && ! period_time) {
448     buffer_size = buffer_size_max;
449     if (! period_time)
450       buffer_size = (buffer_size / nperiods) * nperiods;
451     printf(_("Using max buffer size %lu\n"), buffer_size);
452     err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
453     if (err < 0) {
454       fprintf(stderr, _("Unable to set buffer size %lu for playback: %s\n"),
455              buffer_size, snd_strerror(err));
456       return err;
457     }
458   }
459   if (! buffer_time || ! period_time) {
460     printf(_("Periods = %u\n"), nperiods);
461     err = snd_pcm_hw_params_set_periods_near(handle, params, &nperiods, NULL);
462     if (err < 0) {
463       fprintf(stderr, _("Unable to set nperiods %u for playback: %s\n"),
464              nperiods, snd_strerror(err));
465       return err;
466     }
467   }
468
469   /* write the parameters to device */
470   err = snd_pcm_hw_params(handle, params);
471   if (err < 0) {
472     fprintf(stderr, _("Unable to set hw params for playback: %s\n"), snd_strerror(err));
473     return err;
474   }
475
476   snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
477   snd_pcm_hw_params_get_period_size(params, &period_size, NULL);
478   printf(_("was set period_size = %lu\n"),period_size);
479   printf(_("was set buffer_size = %lu\n"),buffer_size);
480   if (2*period_size > buffer_size) {
481     fprintf(stderr, _("buffer to small, could not use\n"));
482     return -EINVAL;
483   }
484
485   return 0;
486 }
487
488 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) {
489   int err;
490
491   /* get the current swparams */
492   err = snd_pcm_sw_params_current(handle, swparams);
493   if (err < 0) {
494     fprintf(stderr, _("Unable to determine current swparams for playback: %s\n"), snd_strerror(err));
495     return err;
496   }
497
498   /* start the transfer when a buffer is full */
499   err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
500   if (err < 0) {
501     fprintf(stderr, _("Unable to set start threshold mode for playback: %s\n"), snd_strerror(err));
502     return err;
503   }
504
505   /* allow the transfer when at least period_size frames can be processed */
506   err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
507   if (err < 0) {
508     fprintf(stderr, _("Unable to set avail min for playback: %s\n"), snd_strerror(err));
509     return err;
510   }
511
512   /* write the parameters to the playback device */
513   err = snd_pcm_sw_params(handle, swparams);
514   if (err < 0) {
515     fprintf(stderr, _("Unable to set sw params for playback: %s\n"), snd_strerror(err));
516     return err;
517   }
518
519   return 0;
520 }
521
522 /*
523  *   Underrun and suspend recovery
524  */
525
526 static int xrun_recovery(snd_pcm_t *handle, int err) {
527   if (err == -EPIPE) {  /* under-run */
528     err = snd_pcm_prepare(handle);
529     if (err < 0)
530       fprintf(stderr, _("Can't recovery from underrun, prepare failed: %s\n"), snd_strerror(err));
531     return 0;
532   } 
533   else if (err == -ESTRPIPE) {
534
535     while ((err = snd_pcm_resume(handle)) == -EAGAIN)
536       sleep(1); /* wait until the suspend flag is released */
537
538     if (err < 0) {
539       err = snd_pcm_prepare(handle);
540       if (err < 0)
541         fprintf(stderr, _("Can't recovery from suspend, prepare failed: %s\n"), snd_strerror(err));
542     }
543
544     return 0;
545   }
546
547   return err;
548 }
549
550 /*
551  * Handle WAV files
552  */
553
554 static const char *wav_file[MAX_CHANNELS];
555 static int wav_file_size[MAX_CHANNELS];
556
557 struct wave_header {
558   struct {
559     uint32_t magic;
560     uint32_t length;
561     uint32_t type;
562   } hdr;
563   struct {
564     uint32_t type;
565     uint32_t length;
566   } chunk1;
567   struct {
568     uint16_t format;
569     uint16_t channels;
570     uint32_t rate;
571     uint32_t bytes_per_sec;
572     uint16_t sample_size;
573     uint16_t sample_bits;
574   } body;
575   struct {
576     uint32_t type;
577     uint32_t length;
578   } chunk;
579 };
580
581 #define WAV_RIFF                COMPOSE_ID('R','I','F','F')
582 #define WAV_WAVE                COMPOSE_ID('W','A','V','E')
583 #define WAV_FMT                 COMPOSE_ID('f','m','t',' ')
584 #define WAV_DATA                COMPOSE_ID('d','a','t','a')
585 #define WAV_PCM_CODE            1
586
587 static const char *search_for_file(const char *name)
588 {
589   char *file;
590   if (*name == '/')
591     return strdup(name);
592   file = malloc(strlen(wav_file_dir) + strlen(name) + 2);
593   if (file)
594     sprintf(file, "%s/%s", wav_file_dir, name);
595   return file;
596 }
597
598 static int check_wav_file(int channel, const char *name)
599 {
600   struct wave_header header;
601   int fd;
602
603   wav_file[channel] = search_for_file(name);
604   if (! wav_file[channel]) {
605     fprintf(stderr, _("No enough memory\n"));
606     return -ENOMEM;
607   }
608
609   if ((fd = open(wav_file[channel], O_RDONLY)) < 0) {
610     fprintf(stderr, _("Cannot open WAV file %s\n"), wav_file[channel]);
611     return -EINVAL;
612   }
613   if (read(fd, &header, sizeof(header)) < (int)sizeof(header)) {
614     fprintf(stderr, _("Invalid WAV file %s\n"), wav_file[channel]);
615     goto error;
616   }
617   
618   if (header.hdr.magic != WAV_RIFF || header.hdr.type != WAV_WAVE) {
619     fprintf(stderr, _("Not a WAV file: %s\n"), wav_file[channel]);
620     goto error;
621   }
622   if (header.body.format != LE_SHORT(WAV_PCM_CODE)) {
623     fprintf(stderr, _("Unsupported WAV format %d for %s\n"),
624             LE_SHORT(header.body.format), wav_file[channel]);
625     goto error;
626   }
627   if (header.body.channels != LE_SHORT(1)) {
628     fprintf(stderr, _("%s is not a mono stream (%d channels)\n"),
629             wav_file[channel], LE_SHORT(header.body.channels)); 
630     goto error;
631   }
632   if (header.body.rate != LE_INT(rate)) {
633     fprintf(stderr, _("Sample rate doesn't match (%d) for %s\n"),
634             LE_INT(header.body.rate), wav_file[channel]);
635     goto error;
636   }
637   if (header.body.sample_bits != LE_SHORT(16)) {
638     fprintf(stderr, _("Unsupported sample format bits %d for %s\n"),
639             LE_SHORT(header.body.sample_bits), wav_file[channel]);
640     goto error;
641   }
642   if (header.chunk.type != WAV_DATA) {
643     fprintf(stderr, _("Invalid WAV file %s\n"), wav_file[channel]);
644     goto error;
645   }
646   wav_file_size[channel] = LE_INT(header.chunk.length);
647   close(fd);
648   return 0;
649
650  error:
651   close(fd);
652   return -EINVAL;
653 }
654
655 static int setup_wav_file(int chn)
656 {
657   static const char *const wavs[MAX_CHANNELS] = {
658     "Front_Left.wav",
659     "Front_Right.wav",
660     "Rear_Left.wav",
661     "Rear_Right.wav",
662     "Front_Center.wav",
663     "Rear_Center.wav", /* FIXME: should be "Bass" or so */
664     "Side_Left.wav",
665     "Side_Right.wav",
666     "Channel_9.wav",
667     "Channel_10.wav",
668     "Channel_11.wav",
669     "Channel_12.wav",
670     "Channel_13.wav",
671     "Channel_14.wav",
672     "Channel_15.wav",
673     "Channel_16.wav"
674   };
675
676   if (given_test_wav_file)
677     return check_wav_file(chn, given_test_wav_file);
678   else
679     return check_wav_file(chn, wavs[chn]);
680 }
681
682 static int read_wav(uint16_t *buf, int channel, int offset, int bufsize)
683 {
684   static FILE *wavfp = NULL;
685   int size;
686
687   if (! wav_file[channel]) {
688     fprintf(stderr, _("Undefined channel %d\n"), channel);
689     return -EINVAL;
690   }
691
692   if (offset >= wav_file_size[channel])
693    return 0; /* finished */
694
695   if (! offset) {
696     if (wavfp)
697       fclose(wavfp);
698     wavfp = fopen(wav_file[channel], "r");
699     if (! wavfp)
700       return -errno;
701     if (fseek(wavfp, sizeof(struct wave_header), SEEK_SET) < 0)
702       return -errno;
703   }
704   if (offset + bufsize > wav_file_size[channel])
705     bufsize = wav_file_size[channel] - offset;
706   bufsize /= channels;
707   for (size = 0; size < bufsize; size += 2) {
708     int chn;
709     for (chn = 0; chn < channels; chn++) {
710       if (chn == channel) {
711         if (fread(buf, 2, 1, wavfp) != 1)
712           return size;
713       }
714       else
715         *buf = 0;
716       buf++;
717     }
718   }
719   return size;
720 }
721
722
723 /*
724  *   Transfer method - write only
725  */
726
727 static int write_buffer(snd_pcm_t *handle, uint8_t *ptr, int cptr)
728 {
729   int err;
730
731   while (cptr > 0) {
732
733     err = snd_pcm_writei(handle, ptr, cptr);
734
735     if (err == -EAGAIN)
736       continue;
737
738     if (err < 0) {
739       fprintf(stderr, _("Write error: %d,%s\n"), err, snd_strerror(err));
740       if (xrun_recovery(handle, err) < 0) {
741         fprintf(stderr, _("xrun_recovery failed: %d,%s\n"), err, snd_strerror(err));
742         return -1;
743       }
744       break;    /* skip one period */
745     }
746
747     ptr += snd_pcm_frames_to_bytes(handle, err);
748     cptr -= err;
749   }
750   return 0;
751 }
752
753 static int write_loop(snd_pcm_t *handle, int channel, int periods, uint8_t *frames)
754 {
755   double phase = 0;
756   int    pattern = 0;
757   int    err, n;
758
759   fflush(stdout);
760   if (test_type == TEST_WAV) {
761     int bufsize = snd_pcm_frames_to_bytes(handle, period_size);
762     n = 0;
763     while ((err = read_wav((uint16_t *)frames, channel, n, bufsize)) > 0) {
764       n += err;
765       if ((err = write_buffer(handle, frames,
766                               snd_pcm_bytes_to_frames(handle, err * channels))) < 0)
767         break;
768     }
769     if (buffer_size > n) {
770       snd_pcm_drain(handle);
771       snd_pcm_prepare(handle);
772     }
773     return err;
774   }
775     
776
777   if (periods <= 0)
778     periods = 1;
779
780   for(n = 0; n < periods; n++) {
781     if (test_type == TEST_PINK_NOISE)
782       generate_pink_noise(frames, channel, period_size);
783     else if (test_type == TEST_PATTERN)
784       generate_pattern(frames, channel, period_size, &pattern);
785     else
786       generate_sine(frames, channel, period_size, &phase);
787
788     if ((err = write_buffer(handle, frames, period_size)) < 0)
789       return err;
790   }
791   if (buffer_size > n * period_size) {
792     snd_pcm_drain(handle);
793     snd_pcm_prepare(handle);
794   }
795   return 0;
796 }
797
798 static void help(void)
799 {
800   const int *fmt;
801
802   printf(
803          _("Usage: speaker-test [OPTION]... \n"
804            "-h,--help   help\n"
805            "-D,--device playback device\n"
806            "-r,--rate   stream rate in Hz\n"
807            "-c,--channels       count of channels in stream\n"
808            "-f,--frequency      sine wave frequency in Hz\n"
809            "-F,--format sample format\n"
810            "-b,--buffer ring buffer size in us\n"
811            "-p,--period period size in us\n"
812            "-P,--nperiods       number of periods\n"
813            "-t,--test   pink=use pink noise, sine=use sine wave, wav=WAV file\n"
814            "-l,--nloops specify number of loops to test, 0 = infinite\n"
815            "-s,--speaker        single speaker test. Values 1=Left, 2=right, etc\n"
816            "-w,--wavfile        Use the given WAV file as a test sound\n"
817            "-W,--wavdir Specify the directory containing WAV files\n"
818            "\n"));
819   printf(_("Recognized sample formats are:"));
820   for (fmt = supported_formats; *fmt >= 0; fmt++) {
821     const char *s = snd_pcm_format_name(*fmt);
822     if (s)
823       printf(" %s", s);
824   }
825
826   printf("\n\n");
827 }
828
829 int main(int argc, char *argv[]) {
830   snd_pcm_t            *handle;
831   int                   err, morehelp;
832   snd_pcm_hw_params_t  *hwparams;
833   snd_pcm_sw_params_t  *swparams;
834   uint8_t              *frames;
835   int                   chn;
836   const int            *fmt;
837   double                time1,time2,time3;
838   unsigned int          n, nloops;
839   struct   timeval      tv1,tv2;
840
841   static const struct option long_option[] = {
842     {"help",      0, NULL, 'h'},
843     {"device",    1, NULL, 'D'},
844     {"rate",      1, NULL, 'r'},
845     {"channels",  1, NULL, 'c'},
846     {"frequency", 1, NULL, 'f'},
847     {"format",    1, NULL, 'F'},
848     {"buffer",    1, NULL, 'b'},
849     {"period",    1, NULL, 'p'},
850     {"nperiods",  1, NULL, 'P'},
851     {"test",      1, NULL, 't'},
852     {"nloops",    1, NULL, 'l'},
853     {"speaker",   1, NULL, 's'},
854     {"wavfile",   1, NULL, 'w'},
855     {"wavdir",    1, NULL, 'W'},
856     {"debug",     0, NULL, 'd'},
857     {NULL,        0, NULL, 0  },
858   };
859
860 #ifdef ENABLE_NLS
861   setlocale(LC_ALL, "");
862   textdomain(PACKAGE);
863 #endif
864
865   snd_pcm_hw_params_alloca(&hwparams);
866   snd_pcm_sw_params_alloca(&swparams);
867  
868   nloops = 0;
869   morehelp = 0;
870
871   printf("\nspeaker-test %s\n\n", SND_UTIL_VERSION_STR);
872   while (1) {
873     int c;
874     
875     if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:P:t:l:s:w:W:d", long_option, NULL)) < 0)
876       break;
877     
878     switch (c) {
879     case 'h':
880       morehelp++;
881       break;
882     case 'D':
883       device = strdup(optarg);
884       break;
885     case 'F':
886       format = snd_pcm_format_value(optarg);
887       for (fmt = supported_formats; *fmt >= 0; fmt++)
888         if (*fmt == format)
889           break;
890       if (*fmt < 0) {
891         fprintf(stderr, "Format %s is not supported...\n", snd_pcm_format_name(format));
892         exit(EXIT_FAILURE);
893       }
894       break;
895     case 'r':
896       rate = atoi(optarg);
897       rate = rate < 4000 ? 4000 : rate;
898       rate = rate > 196000 ? 196000 : rate;
899       break;
900     case 'c':
901       channels = atoi(optarg);
902       channels = channels < 1 ? 1 : channels;
903       channels = channels > 1024 ? 1024 : channels;
904       break;
905     case 'f':
906       freq = atof(optarg);
907       freq = freq < 30.0 ? 30.0 : freq;
908       freq = freq > 5000.0 ? 5000.0 : freq;
909       break;
910     case 'b':
911       buffer_time = atoi(optarg);
912       buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
913       break;
914     case 'p':
915       period_time = atoi(optarg);
916       period_time = period_time > 1000000 ? 1000000 : period_time;
917       break;
918     case 'P':
919       nperiods = atoi(optarg);
920       if (nperiods < 2 || nperiods > 1024) {
921         fprintf(stderr, _("Invalid number of periods %d\n"), nperiods);
922         exit(1);
923       }
924       break;
925     case 't':
926       if (*optarg == 'p')
927         test_type = TEST_PINK_NOISE;
928       else if (*optarg == 's')
929         test_type = TEST_SINE;
930       else if (*optarg == 'w')
931         test_type = TEST_WAV;
932       else if (*optarg == 't')
933         test_type = TEST_PATTERN;
934       else if (isdigit(*optarg)) {
935         test_type = atoi(optarg);
936         if (test_type < TEST_PINK_NOISE || test_type > TEST_PATTERN) {
937           fprintf(stderr, _("Invalid test type %s\n"), optarg);
938           exit(1);
939         }
940       } else {
941         fprintf(stderr, _("Invalid test type %s\n"), optarg);
942         exit(1);
943       }
944       break;
945     case 'l':
946       nloops = atoi(optarg);
947       break;
948     case 's':
949       speaker = atoi(optarg);
950       speaker = speaker < 1 ? 0 : speaker;
951       speaker = speaker > channels ? 0 : speaker;
952       if (speaker==0) {
953         fprintf(stderr, _("Invalid parameter for -s option.\n"));
954         exit(EXIT_FAILURE);
955       }  
956       break;
957     case 'w':
958       given_test_wav_file = optarg;
959       break;
960     case 'W':
961       wav_file_dir = optarg;
962       break;
963     case 'd':
964       debug = 1;
965       break;
966     default:
967       fprintf(stderr, _("Unknown option '%c'\n"), c);
968       exit(EXIT_FAILURE);
969       break;
970     }
971   }
972
973   if (morehelp) {
974     help();
975     exit(EXIT_SUCCESS);
976   }
977
978   if (test_type == TEST_WAV)
979     format = SND_PCM_FORMAT_S16_LE; /* fixed format */
980
981   printf(_("Playback device is %s\n"), device);
982   printf(_("Stream parameters are %iHz, %s, %i channels\n"), rate, snd_pcm_format_name(format), channels);
983   switch (test_type) {
984   case TEST_PINK_NOISE:
985     printf(_("Using 16 octaves of pink noise\n"));
986     break;
987   case TEST_SINE:
988     printf(_("Sine wave rate is %.4fHz\n"), freq);
989     break;
990   case TEST_WAV:
991     printf(_("WAV file(s)\n"));
992     break;
993
994   }
995
996   if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
997     printf(_("Playback open error: %d,%s\n"), err,snd_strerror(err));
998     exit(EXIT_FAILURE);
999   }
1000
1001   if ((err = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
1002     printf(_("Setting of hwparams failed: %s\n"), snd_strerror(err));
1003     snd_pcm_close(handle);
1004     exit(EXIT_FAILURE);
1005   }
1006   if ((err = set_swparams(handle, swparams)) < 0) {
1007     printf(_("Setting of swparams failed: %s\n"), snd_strerror(err));
1008     snd_pcm_close(handle);
1009     exit(EXIT_FAILURE);
1010   }
1011   if (debug) {
1012     snd_output_t *log;
1013     err = snd_output_stdio_attach(&log, stderr, 0);
1014     if (err >= 0) {
1015       snd_pcm_dump(handle, log);
1016       snd_output_close(log);
1017     }
1018   }
1019
1020   frames = malloc(snd_pcm_frames_to_bytes(handle, period_size));
1021   if (test_type == TEST_PINK_NOISE)
1022     initialize_pink_noise(&pink, 16);
1023   
1024   if (frames == NULL) {
1025     fprintf(stderr, _("No enough memory\n"));
1026     exit(EXIT_FAILURE);
1027   }
1028   if (speaker==0) {
1029
1030     if (test_type == TEST_WAV) {
1031       for (chn = 0; chn < channels; chn++) {
1032         if (setup_wav_file(chn) < 0)
1033           exit(EXIT_FAILURE);
1034       }
1035     }
1036
1037     for (n = 0; ! nloops || n < nloops; n++) {
1038
1039       gettimeofday(&tv1, NULL);
1040       for(chn = 0; chn < channels; chn++) {
1041         int channel=chn;
1042         if (channels == 4) {
1043             channel=channels4[chn];
1044         }
1045         if (channels == 6) {
1046             channel=channels6[chn];
1047         }
1048         if (channels == 8) {
1049             channel=channels8[chn];
1050         }
1051         printf(" %d - %s\n", channel, gettext(channel_name[channel]));
1052
1053         err = write_loop(handle, channel, ((rate*3)/period_size), frames);
1054
1055         if (err < 0) {
1056           fprintf(stderr, _("Transfer failed: %s\n"), snd_strerror(err));
1057           free(frames);
1058           snd_pcm_close(handle);
1059           exit(EXIT_SUCCESS);
1060         }
1061       }
1062       gettimeofday(&tv2, NULL);
1063       time1 = (double)tv1.tv_sec + ((double)tv1.tv_usec / 1000000.0);
1064       time2 = (double)tv2.tv_sec + ((double)tv2.tv_usec / 1000000.0);
1065       time3 = time2 - time1;
1066       printf(_("Time per period = %lf\n"), time3 );
1067     }
1068   } else {
1069     if (test_type == TEST_WAV) {
1070       if (setup_wav_file(speaker - 1) < 0)
1071         exit(EXIT_FAILURE);
1072     }
1073
1074     printf("  - %s\n", gettext(channel_name[speaker-1]));
1075     err = write_loop(handle, speaker-1, ((rate*5)/period_size), frames);
1076
1077     if (err < 0) {
1078       fprintf(stderr, _("Transfer failed: %s\n"), snd_strerror(err));
1079     }
1080   }
1081
1082
1083   free(frames);
1084   snd_pcm_close(handle);
1085
1086   exit(EXIT_SUCCESS);
1087 }