xref: /titanic_52/usr/src/cmd/audio/audiotest/audiotest.c (revision 88447a05f537aabe9a1bc3d5313f22581ec992a7)
1*88447a05SGarrett D'Amore /*
2*88447a05SGarrett D'Amore  * CDDL HEADER START
3*88447a05SGarrett D'Amore  *
4*88447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
5*88447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
6*88447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
7*88447a05SGarrett D'Amore  *
8*88447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*88447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10*88447a05SGarrett D'Amore  * See the License for the specific language governing permissions
11*88447a05SGarrett D'Amore  * and limitations under the License.
12*88447a05SGarrett D'Amore  *
13*88447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14*88447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*88447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16*88447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17*88447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18*88447a05SGarrett D'Amore  *
19*88447a05SGarrett D'Amore  * CDDL HEADER END
20*88447a05SGarrett D'Amore  */
21*88447a05SGarrett D'Amore /*
22*88447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
23*88447a05SGarrett D'Amore  *
24*88447a05SGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25*88447a05SGarrett D'Amore  * Use is subject to license terms.
26*88447a05SGarrett D'Amore  */
27*88447a05SGarrett D'Amore /*
28*88447a05SGarrett D'Amore  * This program is a general purpose test facility for audio output.
29*88447a05SGarrett D'Amore  * It does not test record.
30*88447a05SGarrett D'Amore  *
31*88447a05SGarrett D'Amore  * The wavedata.c and wavedata.h files contain the actual samples compressed
32*88447a05SGarrett D'Amore  * using the MS ADPCM algorithm.
33*88447a05SGarrett D'Amore  */
34*88447a05SGarrett D'Amore 
35*88447a05SGarrett D'Amore #include <stdio.h>
36*88447a05SGarrett D'Amore #include <stdlib.h>
37*88447a05SGarrett D'Amore #include <unistd.h>
38*88447a05SGarrett D'Amore #include <fcntl.h>
39*88447a05SGarrett D'Amore #include <string.h>
40*88447a05SGarrett D'Amore #include <errno.h>
41*88447a05SGarrett D'Amore #include <unistd.h>
42*88447a05SGarrett D'Amore #include <sys/time.h>
43*88447a05SGarrett D'Amore #include <sys/ioctl.h>
44*88447a05SGarrett D'Amore #include <sys/utsname.h>
45*88447a05SGarrett D'Amore #include <sys/soundcard.h>
46*88447a05SGarrett D'Amore #include <inttypes.h>
47*88447a05SGarrett D'Amore #include <locale.h>
48*88447a05SGarrett D'Amore 
49*88447a05SGarrett D'Amore #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
50*88447a05SGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
51*88447a05SGarrett D'Amore #endif
52*88447a05SGarrett D'Amore 
53*88447a05SGarrett D'Amore #define	_(s)	gettext(s)
54*88447a05SGarrett D'Amore 
55*88447a05SGarrett D'Amore /*
56*88447a05SGarrett D'Amore  * Channel selectors
57*88447a05SGarrett D'Amore  */
58*88447a05SGarrett D'Amore #define	CH_LEFT		(1 << 0)
59*88447a05SGarrett D'Amore #define	CH_RIGHT	(1 << 1)
60*88447a05SGarrett D'Amore #define	CH_LREAR4	(1 << 2)	/* quadraphonic */
61*88447a05SGarrett D'Amore #define	CH_RREAR4	(1 << 3)	/* quadraphonic */
62*88447a05SGarrett D'Amore #define	CH_CENTER	(1 << 2)
63*88447a05SGarrett D'Amore #define	CH_LFE		(1 << 3)
64*88447a05SGarrett D'Amore #define	CH_LSURR	(1 << 4)
65*88447a05SGarrett D'Amore #define	CH_RSURR	(1 << 5)
66*88447a05SGarrett D'Amore #define	CH_LREAR	(1 << 6)
67*88447a05SGarrett D'Amore #define	CH_RREAR	(1 << 7)
68*88447a05SGarrett D'Amore #define	CH_STEREO	(CH_LEFT|CH_RIGHT)
69*88447a05SGarrett D'Amore #define	CH_4		(CH_STEREO | CH_LREAR4 | CH_RREAR4)
70*88447a05SGarrett D'Amore #define	CH_5		(CH_STEREO | CH_CENTER | CH_LSURR | CH_RSURR)
71*88447a05SGarrett D'Amore #define	CH_7		(CH_5 | CH_LREAR | CH_RREAR)
72*88447a05SGarrett D'Amore 
73*88447a05SGarrett D'Amore typedef struct chancfg {
74*88447a05SGarrett D'Amore 	int		mask;
75*88447a05SGarrett D'Amore 	const char	*name;
76*88447a05SGarrett D'Amore 	unsigned	flags;
77*88447a05SGarrett D'Amore 	int16_t		*data;
78*88447a05SGarrett D'Amore 	int		len;
79*88447a05SGarrett D'Amore } chancfg_t;
80*88447a05SGarrett D'Amore 
81*88447a05SGarrett D'Amore typedef struct testcfg {
82*88447a05SGarrett D'Amore 	int		nchan;
83*88447a05SGarrett D'Amore 	chancfg_t	*tests[16];
84*88447a05SGarrett D'Amore } testcfg_t;
85*88447a05SGarrett D'Amore 
86*88447a05SGarrett D'Amore #define	CFLAG_LFE	0x1	/* lfe channel - not full range */
87*88447a05SGarrett D'Amore 
88*88447a05SGarrett D'Amore /*
89*88447a05SGarrett D'Amore  * TRANSLATION_NOTE : The following strings are displayed during progress.
90*88447a05SGarrett D'Amore  * Its important for alignment that they have the same displayed length.
91*88447a05SGarrett D'Amore  */
92*88447a05SGarrett D'Amore #define	NM_LEFT		"\t<left> ................"
93*88447a05SGarrett D'Amore #define	NM_RIGHT	"\t<right> ..............."
94*88447a05SGarrett D'Amore #define	NM_LREAR	"\t<left rear> ..........."
95*88447a05SGarrett D'Amore #define	NM_RREAR	"\t<right rear> .........."
96*88447a05SGarrett D'Amore #define	NM_LSIDE	"\t<left side> ..........."
97*88447a05SGarrett D'Amore #define	NM_RSIDE	"\t<right side> .........."
98*88447a05SGarrett D'Amore #define	NM_CENTER	"\t<center> .............."
99*88447a05SGarrett D'Amore #define	NM_LFE		"\t<lfe> ................."
100*88447a05SGarrett D'Amore #define	NM_STEREO	"\t<stereo> .............."
101*88447a05SGarrett D'Amore #define	NM_40		"\t<4.0 surround> ........"
102*88447a05SGarrett D'Amore #define	NM_50		"\t<5.0 surround> ........"
103*88447a05SGarrett D'Amore #define	NM_70		"\t<7.0 surround> ........"
104*88447a05SGarrett D'Amore 
105*88447a05SGarrett D'Amore chancfg_t ch_left = { CH_LEFT, NM_LEFT, 0 };
106*88447a05SGarrett D'Amore chancfg_t ch_right = { CH_RIGHT, NM_RIGHT, 0 };
107*88447a05SGarrett D'Amore chancfg_t ch_stereo = { CH_STEREO, NM_STEREO, 0 };
108*88447a05SGarrett D'Amore 
109*88447a05SGarrett D'Amore chancfg_t ch_center = { CH_CENTER, NM_CENTER, 0 };
110*88447a05SGarrett D'Amore chancfg_t ch_lfe = { CH_LFE, NM_LFE, CFLAG_LFE };
111*88447a05SGarrett D'Amore 
112*88447a05SGarrett D'Amore chancfg_t ch_lsurr_4 = { (1 << 2), NM_LREAR, 0 };
113*88447a05SGarrett D'Amore chancfg_t ch_rsurr_4 = { (1 << 3), NM_RREAR, 0 };
114*88447a05SGarrett D'Amore chancfg_t ch_4 = { CH_4, NM_40, 0 };
115*88447a05SGarrett D'Amore 
116*88447a05SGarrett D'Amore chancfg_t ch_lsurr_5 = { CH_LSURR, NM_LREAR, 0 };
117*88447a05SGarrett D'Amore chancfg_t ch_rsurr_5 = { CH_RSURR, NM_RREAR, 0 };
118*88447a05SGarrett D'Amore chancfg_t ch_5 = { CH_5, NM_50, 0 };
119*88447a05SGarrett D'Amore 
120*88447a05SGarrett D'Amore chancfg_t ch_lsurr_7 = { CH_LSURR, NM_LSIDE, 0 };
121*88447a05SGarrett D'Amore chancfg_t ch_rsurr_7 = { CH_RSURR, NM_RSIDE, 0 };
122*88447a05SGarrett D'Amore chancfg_t ch_lrear_7 = { CH_LREAR, NM_LREAR, 0 };
123*88447a05SGarrett D'Amore chancfg_t ch_rrear_7 = { CH_RREAR, NM_RREAR, 0 };
124*88447a05SGarrett D'Amore chancfg_t ch_7 = { CH_7, NM_70, 0 };
125*88447a05SGarrett D'Amore 
126*88447a05SGarrett D'Amore testcfg_t test_stereo = {
127*88447a05SGarrett D'Amore 	2, { &ch_left, &ch_right, &ch_stereo, NULL }
128*88447a05SGarrett D'Amore };
129*88447a05SGarrett D'Amore 
130*88447a05SGarrett D'Amore testcfg_t test_quad = {
131*88447a05SGarrett D'Amore 	4, { &ch_left, &ch_right, &ch_stereo,
132*88447a05SGarrett D'Amore 	&ch_lsurr_4, &ch_rsurr_4, &ch_4, NULL }
133*88447a05SGarrett D'Amore };
134*88447a05SGarrett D'Amore 
135*88447a05SGarrett D'Amore testcfg_t test_51 = {
136*88447a05SGarrett D'Amore 	6, { &ch_left, &ch_right, &ch_stereo,
137*88447a05SGarrett D'Amore 	&ch_lsurr_5, &ch_rsurr_5, &ch_center, &ch_lfe, &ch_5, NULL }
138*88447a05SGarrett D'Amore };
139*88447a05SGarrett D'Amore 
140*88447a05SGarrett D'Amore testcfg_t test_71 = {
141*88447a05SGarrett D'Amore 	8, { &ch_left, &ch_right, &ch_stereo,
142*88447a05SGarrett D'Amore 	&ch_lsurr_7, &ch_rsurr_7, &ch_lrear_7, &ch_rrear_7,
143*88447a05SGarrett D'Amore 	&ch_center, &ch_lfe, &ch_7, NULL }
144*88447a05SGarrett D'Amore };
145*88447a05SGarrett D'Amore 
146*88447a05SGarrett D'Amore /*
147*88447a05SGarrett D'Amore  * uncompress_wave() is defined in wavedata.c. It expands the audio
148*88447a05SGarrett D'Amore  * samples stored in wavedata.h and returns the lenghth of the
149*88447a05SGarrett D'Amore  * uncompressed version in bytes.
150*88447a05SGarrett D'Amore  *
151*88447a05SGarrett D'Amore  * The uncompressed wave data format is 16 bit (native) stereo
152*88447a05SGarrett D'Amore  * recorded at 48000 Hz.
153*88447a05SGarrett D'Amore  */
154*88447a05SGarrett D'Amore extern int uncompress_wave(short *outbuf);
155*88447a05SGarrett D'Amore 
156*88447a05SGarrett D'Amore static int data_len;
157*88447a05SGarrett D'Amore 
158*88447a05SGarrett D'Amore #define	MAXDEVICE   64
159*88447a05SGarrett D'Amore extern void describe_error(int);
160*88447a05SGarrett D'Amore 
161*88447a05SGarrett D'Amore #define	SAMPLE_RATE 48000
162*88447a05SGarrett D'Amore 
163*88447a05SGarrett D'Amore /*
164*88447a05SGarrett D'Amore  * Operating mode flags (set from the command line).
165*88447a05SGarrett D'Amore  */
166*88447a05SGarrett D'Amore #define	TF_LOOP		0x00000010	/* Loop until interrupted */
167*88447a05SGarrett D'Amore 
168*88447a05SGarrett D'Amore static int mixerfd;
169*88447a05SGarrett D'Amore static int num_devices_tested = 0;
170*88447a05SGarrett D'Amore 
171*88447a05SGarrett D'Amore static short *sample_buf;
172*88447a05SGarrett D'Amore 
173*88447a05SGarrett D'Amore void
174*88447a05SGarrett D'Amore prepare(testcfg_t *tcfg)
175*88447a05SGarrett D'Amore {
176*88447a05SGarrett D'Amore 	int	nsamples;
177*88447a05SGarrett D'Amore 	int	i;
178*88447a05SGarrett D'Amore 	chancfg_t	*ccfg;
179*88447a05SGarrett D'Amore 	if ((sample_buf = malloc(2000000)) == NULL) {
180*88447a05SGarrett D'Amore 		perror("malloc");
181*88447a05SGarrett D'Amore 		exit(-1);
182*88447a05SGarrett D'Amore 	}
183*88447a05SGarrett D'Amore 
184*88447a05SGarrett D'Amore 	data_len = uncompress_wave(sample_buf);
185*88447a05SGarrett D'Amore 	nsamples = (data_len / sizeof (int16_t)) / 2;
186*88447a05SGarrett D'Amore 
187*88447a05SGarrett D'Amore 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
188*88447a05SGarrett D'Amore 		int16_t		*src, *dst;
189*88447a05SGarrett D'Amore 		int		ch;
190*88447a05SGarrett D'Amore 		int		samp;
191*88447a05SGarrett D'Amore 
192*88447a05SGarrett D'Amore 		src = sample_buf;
193*88447a05SGarrett D'Amore 
194*88447a05SGarrett D'Amore 		if (ccfg->flags != CFLAG_LFE) {
195*88447a05SGarrett D'Amore 			ccfg->len = nsamples * tcfg->nchan * sizeof (int16_t);
196*88447a05SGarrett D'Amore 			ccfg->data = malloc(ccfg->len);
197*88447a05SGarrett D'Amore 			if ((dst = ccfg->data) == NULL) {
198*88447a05SGarrett D'Amore 				perror("malloc");
199*88447a05SGarrett D'Amore 				exit(-1);
200*88447a05SGarrett D'Amore 			}
201*88447a05SGarrett D'Amore 			for (samp = 0; samp < nsamples; samp++) {
202*88447a05SGarrett D'Amore 				for (ch = 0; ch < tcfg->nchan; ch++) {
203*88447a05SGarrett D'Amore 					*dst = ((1U << ch) & ccfg->mask) ?
204*88447a05SGarrett D'Amore 					    *src : 0;
205*88447a05SGarrett D'Amore 					dst++;
206*88447a05SGarrett D'Amore 				}
207*88447a05SGarrett D'Amore 				src += 2;
208*88447a05SGarrett D'Amore 			}
209*88447a05SGarrett D'Amore 		} else {
210*88447a05SGarrett D'Amore 			/* Skip LFE for now */
211*88447a05SGarrett D'Amore 			ccfg->len = 0;
212*88447a05SGarrett D'Amore 		}
213*88447a05SGarrett D'Amore 	}
214*88447a05SGarrett D'Amore }
215*88447a05SGarrett D'Amore 
216*88447a05SGarrett D'Amore /*
217*88447a05SGarrett D'Amore  * The testdsp() routine checks the capabilities of a given audio device number
218*88447a05SGarrett D'Amore  * (parameter n) and decides if the test sound needs to be played.
219*88447a05SGarrett D'Amore  */
220*88447a05SGarrett D'Amore 
221*88447a05SGarrett D'Amore /*ARGSUSED*/
222*88447a05SGarrett D'Amore int
223*88447a05SGarrett D'Amore testdsp(int hd, int flags, testcfg_t *tcfg)
224*88447a05SGarrett D'Amore {
225*88447a05SGarrett D'Amore 	float ratio;
226*88447a05SGarrett D'Amore 	struct timeval t1, t2;
227*88447a05SGarrett D'Amore 	unsigned long t;
228*88447a05SGarrett D'Amore 	int sample_rate;
229*88447a05SGarrett D'Amore 	int delay;
230*88447a05SGarrett D'Amore 	long long total_bytes = 0;
231*88447a05SGarrett D'Amore 	unsigned int tmp, caps;
232*88447a05SGarrett D'Amore 	int i;
233*88447a05SGarrett D'Amore 	chancfg_t *ccfg;
234*88447a05SGarrett D'Amore 
235*88447a05SGarrett D'Amore 	caps = 0;
236*88447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
237*88447a05SGarrett D'Amore 		perror("SNDCTL_DSP_GETCAPS");
238*88447a05SGarrett D'Amore 		return (-1);
239*88447a05SGarrett D'Amore 	}
240*88447a05SGarrett D'Amore 
241*88447a05SGarrett D'Amore 	/*
242*88447a05SGarrett D'Amore 	 * Setup the sample format. Since OSS will support AFMT_S16_NE
243*88447a05SGarrett D'Amore 	 * regardless of the device we do not need to support any
244*88447a05SGarrett D'Amore 	 * other formats.
245*88447a05SGarrett D'Amore 	 */
246*88447a05SGarrett D'Amore 
247*88447a05SGarrett D'Amore 	tmp = AFMT_S16_NE;
248*88447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_SETFMT, &tmp) == -1 || tmp != AFMT_S16_NE) {
249*88447a05SGarrett D'Amore 		(void) printf(_("Device doesn't support native 16-bit PCM\n"));
250*88447a05SGarrett D'Amore 		return (-1);
251*88447a05SGarrett D'Amore 	}
252*88447a05SGarrett D'Amore 
253*88447a05SGarrett D'Amore 	/*
254*88447a05SGarrett D'Amore 	 * Setup the device for channels. Once again we can simply
255*88447a05SGarrett D'Amore 	 * assume that stereo will always work before OSS takes care
256*88447a05SGarrett D'Amore 	 * of this by emulation if necessary.
257*88447a05SGarrett D'Amore 	 */
258*88447a05SGarrett D'Amore 	tmp = tcfg->nchan;
259*88447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_CHANNELS, &tmp) == -1 || tmp != tcfg->nchan) {
260*88447a05SGarrett D'Amore 		(void) printf(_("The device doesn't support %d channels\n"),
261*88447a05SGarrett D'Amore 		    tcfg->nchan);
262*88447a05SGarrett D'Amore 		return (-2);
263*88447a05SGarrett D'Amore 	}
264*88447a05SGarrett D'Amore 
265*88447a05SGarrett D'Amore 	/*
266*88447a05SGarrett D'Amore 	 * Set up the sample rate.
267*88447a05SGarrett D'Amore 	 */
268*88447a05SGarrett D'Amore 
269*88447a05SGarrett D'Amore 	tmp = SAMPLE_RATE;
270*88447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_SPEED, &tmp) == -1) {
271*88447a05SGarrett D'Amore 		perror("SNDCTL_DSP_SPEED");
272*88447a05SGarrett D'Amore 		return (-3);
273*88447a05SGarrett D'Amore 	}
274*88447a05SGarrett D'Amore 
275*88447a05SGarrett D'Amore 	sample_rate = tmp;
276*88447a05SGarrett D'Amore 	if (sample_rate != SAMPLE_RATE) {
277*88447a05SGarrett D'Amore 		(void) printf(_("The device doesn't support %d Hz\n"),
278*88447a05SGarrett D'Amore 		    SAMPLE_RATE);
279*88447a05SGarrett D'Amore 		return (-3);
280*88447a05SGarrett D'Amore 	}
281*88447a05SGarrett D'Amore 	(void) printf("\n");
282*88447a05SGarrett D'Amore 
283*88447a05SGarrett D'Amore 	/*
284*88447a05SGarrett D'Amore 	 * This program will measure the real sampling rate by
285*88447a05SGarrett D'Amore 	 * computing the total time required to play the sample.
286*88447a05SGarrett D'Amore 	 *
287*88447a05SGarrett D'Amore 	 * This is not terribly presice with short test sounds but it
288*88447a05SGarrett D'Amore 	 * can be used to detect if the sampling rate badly
289*88447a05SGarrett D'Amore 	 * wrong. Errors of few percents is more likely to be caused
290*88447a05SGarrett D'Amore 	 * by poor accuracy of the system clock rather than problems
291*88447a05SGarrett D'Amore 	 * with the sampling rate.
292*88447a05SGarrett D'Amore 	 */
293*88447a05SGarrett D'Amore 	(void) gettimeofday(&t1, NULL);
294*88447a05SGarrett D'Amore 
295*88447a05SGarrett D'Amore 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
296*88447a05SGarrett D'Amore 		(void) fputs(_(ccfg->name), stdout);
297*88447a05SGarrett D'Amore 		(void) fflush(stdout);
298*88447a05SGarrett D'Amore 		if (ccfg->flags & CFLAG_LFE) {
299*88447a05SGarrett D'Amore 			(void) printf(_("SKIPPED\n"));
300*88447a05SGarrett D'Amore 			continue;
301*88447a05SGarrett D'Amore 		}
302*88447a05SGarrett D'Amore 
303*88447a05SGarrett D'Amore 		if (write(hd, ccfg->data, ccfg->len) < 0) {
304*88447a05SGarrett D'Amore 			(void) printf(_("ERROR: %s\n"),
305*88447a05SGarrett D'Amore 			    strerror(errno));
306*88447a05SGarrett D'Amore 			return (-3);
307*88447a05SGarrett D'Amore 		}
308*88447a05SGarrett D'Amore 		(void) printf(_("OK\n"));
309*88447a05SGarrett D'Amore 		total_bytes += ccfg->len;
310*88447a05SGarrett D'Amore 	}
311*88447a05SGarrett D'Amore 
312*88447a05SGarrett D'Amore 	(void) gettimeofday(&t2, NULL);
313*88447a05SGarrett D'Amore 	delay = 0;
314*88447a05SGarrett D'Amore 	(void) ioctl(hd, SNDCTL_DSP_GETODELAY, &delay);	/* Ignore errors */
315*88447a05SGarrett D'Amore 
316*88447a05SGarrett D'Amore 	/*
317*88447a05SGarrett D'Amore 	 * Perform the time computations using milliseconds.
318*88447a05SGarrett D'Amore 	 */
319*88447a05SGarrett D'Amore 
320*88447a05SGarrett D'Amore 	t = t2.tv_sec - t1.tv_sec;
321*88447a05SGarrett D'Amore 	t *= 1000;
322*88447a05SGarrett D'Amore 
323*88447a05SGarrett D'Amore 	t += t2.tv_usec / 1000;
324*88447a05SGarrett D'Amore 	t -= t1.tv_usec / 1000;
325*88447a05SGarrett D'Amore 
326*88447a05SGarrett D'Amore 	total_bytes -= delay;
327*88447a05SGarrett D'Amore 	total_bytes *= 1000;
328*88447a05SGarrett D'Amore 
329*88447a05SGarrett D'Amore 	total_bytes /= t;
330*88447a05SGarrett D'Amore 	total_bytes /= (tcfg->nchan * sizeof (int16_t));
331*88447a05SGarrett D'Amore 
332*88447a05SGarrett D'Amore 	ratio = ((float)total_bytes / (float)sample_rate) * 100.0;
333*88447a05SGarrett D'Amore 	(void) printf(_("\t<measured sample rate %8.2f Hz (%4.2f%%)>\n"),
334*88447a05SGarrett D'Amore 	    (float)sample_rate * ratio / 100.0, ratio - 100.0);
335*88447a05SGarrett D'Amore 	num_devices_tested++;
336*88447a05SGarrett D'Amore 
337*88447a05SGarrett D'Amore 	return (1);
338*88447a05SGarrett D'Amore }
339*88447a05SGarrett D'Amore 
340*88447a05SGarrett D'Amore static int
341*88447a05SGarrett D'Amore find_num_devices(void)
342*88447a05SGarrett D'Amore {
343*88447a05SGarrett D'Amore 	oss_sysinfo info;
344*88447a05SGarrett D'Amore 	struct utsname un;
345*88447a05SGarrett D'Amore 	/*
346*88447a05SGarrett D'Amore 	 * Find out the number of available audio devices by calling
347*88447a05SGarrett D'Amore 	 * SNDCTL_SYSINFO.
348*88447a05SGarrett D'Amore 	 */
349*88447a05SGarrett D'Amore 
350*88447a05SGarrett D'Amore 	if (ioctl(mixerfd, SNDCTL_SYSINFO, &info) == -1) {
351*88447a05SGarrett D'Amore 		if (errno == ENXIO) {
352*88447a05SGarrett D'Amore 			(void) fprintf(stderr,
353*88447a05SGarrett D'Amore 			    _("No supported sound hardware detected.\n"));
354*88447a05SGarrett D'Amore 			exit(-1);
355*88447a05SGarrett D'Amore 		} else {
356*88447a05SGarrett D'Amore 			perror("SNDCTL_SYSINFO");
357*88447a05SGarrett D'Amore 			(void) printf(_("Cannot get system information.\n"));
358*88447a05SGarrett D'Amore 			exit(-1);
359*88447a05SGarrett D'Amore 		}
360*88447a05SGarrett D'Amore 	}
361*88447a05SGarrett D'Amore 	(void) printf(_("Sound subsystem and version: %s %s (0x%08X)\n"),
362*88447a05SGarrett D'Amore 	    info.product, info.version, info.versionnum);
363*88447a05SGarrett D'Amore 
364*88447a05SGarrett D'Amore 	if (uname(&un) != -1)
365*88447a05SGarrett D'Amore 		(void) printf(_("Platform: %s %s %s %s\n"),
366*88447a05SGarrett D'Amore 		    un.sysname, un.release, un.version, un.machine);
367*88447a05SGarrett D'Amore 
368*88447a05SGarrett D'Amore 	return (info.numaudios);
369*88447a05SGarrett D'Amore }
370*88447a05SGarrett D'Amore 
371*88447a05SGarrett D'Amore /*
372*88447a05SGarrett D'Amore  * The test_device() routine checks certain information about the device
373*88447a05SGarrett D'Amore  * and calls testdsp() to play the test sound.
374*88447a05SGarrett D'Amore  */
375*88447a05SGarrett D'Amore 
376*88447a05SGarrett D'Amore int
377*88447a05SGarrett D'Amore test_device(char *dn, int flags, testcfg_t *tcfg)
378*88447a05SGarrett D'Amore {
379*88447a05SGarrett D'Amore 	oss_audioinfo ainfo;
380*88447a05SGarrett D'Amore 	int code;
381*88447a05SGarrett D'Amore 	int fd;
382*88447a05SGarrett D'Amore 
383*88447a05SGarrett D'Amore 	fd = open(dn, O_WRONLY, 0);
384*88447a05SGarrett D'Amore 	if (fd == -1) {
385*88447a05SGarrett D'Amore 		int err = errno;
386*88447a05SGarrett D'Amore 		perror(dn);
387*88447a05SGarrett D'Amore 		errno = err;
388*88447a05SGarrett D'Amore 		describe_error(errno);
389*88447a05SGarrett D'Amore 		return (0);
390*88447a05SGarrett D'Amore 	}
391*88447a05SGarrett D'Amore 
392*88447a05SGarrett D'Amore 	ainfo.dev = -1;
393*88447a05SGarrett D'Amore 	if (ioctl(fd, SNDCTL_AUDIOINFO, &ainfo) == -1) {
394*88447a05SGarrett D'Amore 		perror("SNDCTL_AUDIOINFO");
395*88447a05SGarrett D'Amore 		(void) close(fd);
396*88447a05SGarrett D'Amore 		return (1);
397*88447a05SGarrett D'Amore 	}
398*88447a05SGarrett D'Amore 
399*88447a05SGarrett D'Amore 	(void) printf(_("\n*** Scanning sound adapter #%d ***\n"),
400*88447a05SGarrett D'Amore 	    ainfo.card_number);
401*88447a05SGarrett D'Amore 
402*88447a05SGarrett D'Amore 	(void) printf(_("%s (audio engine %d): %s\n"), ainfo.devnode, ainfo.dev,
403*88447a05SGarrett D'Amore 	    ainfo.name);
404*88447a05SGarrett D'Amore 
405*88447a05SGarrett D'Amore 	if (!ainfo.enabled) {
406*88447a05SGarrett D'Amore 		(void) printf(_("  - Device not present - Skipping\n"));
407*88447a05SGarrett D'Amore 		(void) close(fd);
408*88447a05SGarrett D'Amore 		return (1);
409*88447a05SGarrett D'Amore 	}
410*88447a05SGarrett D'Amore 
411*88447a05SGarrett D'Amore 	if (!(ainfo.caps & PCM_CAP_OUTPUT)) {
412*88447a05SGarrett D'Amore 		(void) printf(_("  - Skipping input only device\n"));
413*88447a05SGarrett D'Amore 		(void) close(fd);
414*88447a05SGarrett D'Amore 		return (1);
415*88447a05SGarrett D'Amore 	}
416*88447a05SGarrett D'Amore 
417*88447a05SGarrett D'Amore 	(void) printf(_("  - Performing audio playback test... "));
418*88447a05SGarrett D'Amore 	(void) fflush(stdout);
419*88447a05SGarrett D'Amore 
420*88447a05SGarrett D'Amore 	code = testdsp(fd, flags, tcfg);
421*88447a05SGarrett D'Amore 	(void) close(fd);
422*88447a05SGarrett D'Amore 
423*88447a05SGarrett D'Amore 	return (code == 1);
424*88447a05SGarrett D'Amore }
425*88447a05SGarrett D'Amore 
426*88447a05SGarrett D'Amore void
427*88447a05SGarrett D'Amore describe_error(int err)
428*88447a05SGarrett D'Amore {
429*88447a05SGarrett D'Amore 	switch (err) {
430*88447a05SGarrett D'Amore 	case ENODEV:
431*88447a05SGarrett D'Amore 		(void) fprintf(stderr,
432*88447a05SGarrett D'Amore 		    _("The device file was found in /dev but\n"
433*88447a05SGarrett D'Amore 		    "the driver was not loaded.\n"));
434*88447a05SGarrett D'Amore 		break;
435*88447a05SGarrett D'Amore 
436*88447a05SGarrett D'Amore 	case ENXIO:
437*88447a05SGarrett D'Amore 		(void) fprintf(stderr,
438*88447a05SGarrett D'Amore 		    _("There are no sound devices available.\n"
439*88447a05SGarrett D'Amore 		    "The most likely reason is that the device you have\n"
440*88447a05SGarrett D'Amore 		    "is malfunctioning or it's not supported.\n"
441*88447a05SGarrett D'Amore 		    "It's also possible that you are trying to use the wrong "
442*88447a05SGarrett D'Amore 		    "device file.\n"));
443*88447a05SGarrett D'Amore 		break;
444*88447a05SGarrett D'Amore 
445*88447a05SGarrett D'Amore 	case ENOSPC:
446*88447a05SGarrett D'Amore 		(void) fprintf(stderr,
447*88447a05SGarrett D'Amore 		    _("Your system cannot allocate memory for the device\n"
448*88447a05SGarrett D'Amore 		    "buffers. Reboot your machine and try again.\n"));
449*88447a05SGarrett D'Amore 		break;
450*88447a05SGarrett D'Amore 
451*88447a05SGarrett D'Amore 	case ENOENT:
452*88447a05SGarrett D'Amore 		(void) fprintf(stderr,
453*88447a05SGarrett D'Amore 		    _("The device file is missing from /dev.\n"));
454*88447a05SGarrett D'Amore 		break;
455*88447a05SGarrett D'Amore 
456*88447a05SGarrett D'Amore 
457*88447a05SGarrett D'Amore 	case EBUSY:
458*88447a05SGarrett D'Amore 		(void) fprintf(stderr,
459*88447a05SGarrett D'Amore 		    _("The device is busy. There is some other application\n"
460*88447a05SGarrett D'Amore 		    "using it.\n"));
461*88447a05SGarrett D'Amore 		break;
462*88447a05SGarrett D'Amore 
463*88447a05SGarrett D'Amore 	default:
464*88447a05SGarrett D'Amore 		break;
465*88447a05SGarrett D'Amore 	}
466*88447a05SGarrett D'Amore }
467*88447a05SGarrett D'Amore 
468*88447a05SGarrett D'Amore int
469*88447a05SGarrett D'Amore main(int argc, char *argv[])
470*88447a05SGarrett D'Amore {
471*88447a05SGarrett D'Amore 	int t, i;
472*88447a05SGarrett D'Amore 	int maxdev;
473*88447a05SGarrett D'Amore 	int flags = 0;
474*88447a05SGarrett D'Amore 	int status = 0;
475*88447a05SGarrett D'Amore 	int numdev;
476*88447a05SGarrett D'Amore 	extern int optind;
477*88447a05SGarrett D'Amore 	testcfg_t	*tcfg;
478*88447a05SGarrett D'Amore 
479*88447a05SGarrett D'Amore 	(void) setlocale(LC_ALL, "");
480*88447a05SGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
481*88447a05SGarrett D'Amore 
482*88447a05SGarrett D'Amore 	tcfg = &test_stereo;
483*88447a05SGarrett D'Amore 
484*88447a05SGarrett D'Amore 	/*
485*88447a05SGarrett D'Amore 	 * Simple command line switch handling.
486*88447a05SGarrett D'Amore 	 */
487*88447a05SGarrett D'Amore 
488*88447a05SGarrett D'Amore 	while ((i = getopt(argc, argv, "l2457")) != EOF) {
489*88447a05SGarrett D'Amore 		switch (i) {
490*88447a05SGarrett D'Amore 		case 'l':
491*88447a05SGarrett D'Amore 			flags |= TF_LOOP;
492*88447a05SGarrett D'Amore 			break;
493*88447a05SGarrett D'Amore 		case '2':
494*88447a05SGarrett D'Amore 			tcfg = &test_stereo;
495*88447a05SGarrett D'Amore 			break;
496*88447a05SGarrett D'Amore 		case '4':
497*88447a05SGarrett D'Amore 			tcfg = &test_quad;
498*88447a05SGarrett D'Amore 			break;
499*88447a05SGarrett D'Amore 		case '5':
500*88447a05SGarrett D'Amore 			tcfg = &test_51;
501*88447a05SGarrett D'Amore 			break;
502*88447a05SGarrett D'Amore 		case '7':
503*88447a05SGarrett D'Amore 			tcfg = &test_71;
504*88447a05SGarrett D'Amore 			break;
505*88447a05SGarrett D'Amore 		default:
506*88447a05SGarrett D'Amore 			(void) printf(_("Usage: %s [options...] [device]\n"
507*88447a05SGarrett D'Amore 			    "	-2	Stereo test\n"
508*88447a05SGarrett D'Amore 			    "	-4	Quadraphonic 4.0 test\n"
509*88447a05SGarrett D'Amore 			    "	-5	Surround 5.1 test\n"
510*88447a05SGarrett D'Amore 			    "	-7	Surround 7.1 test\n"
511*88447a05SGarrett D'Amore 			    "	-l	Loop test\n"), argv[0]);
512*88447a05SGarrett D'Amore 			exit(-1);
513*88447a05SGarrett D'Amore 		}
514*88447a05SGarrett D'Amore 	}
515*88447a05SGarrett D'Amore 
516*88447a05SGarrett D'Amore 	/*
517*88447a05SGarrett D'Amore 	 * Open the mixer device used for calling SNDCTL_SYSINFO and
518*88447a05SGarrett D'Amore 	 * SNDCTL_AUDIOINFO.
519*88447a05SGarrett D'Amore 	 */
520*88447a05SGarrett D'Amore 	if ((mixerfd = open("/dev/mixer", O_RDWR, 0)) == -1) {
521*88447a05SGarrett D'Amore 		int err = errno;
522*88447a05SGarrett D'Amore 		perror("/dev/mixer");
523*88447a05SGarrett D'Amore 		errno = err;
524*88447a05SGarrett D'Amore 		describe_error(errno);
525*88447a05SGarrett D'Amore 		exit(-1);
526*88447a05SGarrett D'Amore 	}
527*88447a05SGarrett D'Amore 
528*88447a05SGarrett D'Amore 	prepare(tcfg);			/* Prepare the wave data */
529*88447a05SGarrett D'Amore 
530*88447a05SGarrett D'Amore 	/*
531*88447a05SGarrett D'Amore 	 * Enumerate all devices and play the test sounds.
532*88447a05SGarrett D'Amore 	 */
533*88447a05SGarrett D'Amore 	maxdev = find_num_devices();
534*88447a05SGarrett D'Amore 	if (maxdev < 1) {
535*88447a05SGarrett D'Amore 		(void) printf(_("\n*** No audio hardware available ***\n"));
536*88447a05SGarrett D'Amore 		exit(-1);
537*88447a05SGarrett D'Amore 	}
538*88447a05SGarrett D'Amore 
539*88447a05SGarrett D'Amore 	numdev = (argc - optind);
540*88447a05SGarrett D'Amore 	do {
541*88447a05SGarrett D'Amore 		char *dn;
542*88447a05SGarrett D'Amore 		oss_audioinfo	ainfo;
543*88447a05SGarrett D'Amore 
544*88447a05SGarrett D'Amore 		if (numdev > 0) {
545*88447a05SGarrett D'Amore 			for (t = 0; t < numdev; t++) {
546*88447a05SGarrett D'Amore 				dn = argv[optind + t];
547*88447a05SGarrett D'Amore 				if (!test_device(dn, flags, tcfg))
548*88447a05SGarrett D'Amore 					status++;
549*88447a05SGarrett D'Amore 			}
550*88447a05SGarrett D'Amore 		} else {
551*88447a05SGarrett D'Amore 			for (t = 0; t < maxdev; t++) {
552*88447a05SGarrett D'Amore 				ainfo.dev = t;
553*88447a05SGarrett D'Amore 				if (ioctl(mixerfd, SNDCTL_AUDIOINFO,
554*88447a05SGarrett D'Amore 				    &ainfo) == -1) {
555*88447a05SGarrett D'Amore 					perror("SNDCTL_AUDIOINFO");
556*88447a05SGarrett D'Amore 					status++;
557*88447a05SGarrett D'Amore 					continue;
558*88447a05SGarrett D'Amore 				}
559*88447a05SGarrett D'Amore 				dn = ainfo.devnode;
560*88447a05SGarrett D'Amore 				if (!test_device(dn, flags, tcfg))
561*88447a05SGarrett D'Amore 					status++;
562*88447a05SGarrett D'Amore 			}
563*88447a05SGarrett D'Amore 		}
564*88447a05SGarrett D'Amore 
565*88447a05SGarrett D'Amore 		if (status == 0)
566*88447a05SGarrett D'Amore 			(void) printf(_("\n*** All tests completed OK ***\n"));
567*88447a05SGarrett D'Amore 		else
568*88447a05SGarrett D'Amore 			(void) printf(_("\n*** Errors were detected ***\n"));
569*88447a05SGarrett D'Amore 
570*88447a05SGarrett D'Amore 	} while (flags & TF_LOOP);
571*88447a05SGarrett D'Amore 
572*88447a05SGarrett D'Amore 	(void) close(mixerfd);
573*88447a05SGarrett D'Amore 
574*88447a05SGarrett D'Amore 	return (status);
575*88447a05SGarrett D'Amore }
576