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