xref: /netbsd/usr.bin/audio/play/play.c (revision c4a72b64)
1 /*	$NetBSD: play.c,v 1.41 2002/12/08 10:49:22 mrg Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 Matthew R. Green
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/audioio.h>
33 #include <sys/ioctl.h>
34 #include <sys/mman.h>
35 #include <sys/stat.h>
36 
37 #include <err.h>
38 #include <fcntl.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include <paths.h>
46 
47 #include "libaudio.h"
48 
49 int main(int, char *[]);
50 void usage(void);
51 void play(char *);
52 void play_fd(const char *, int);
53 ssize_t audioctl_write_fromhdr(void *, size_t, int, size_t *, const char *);
54 void cleanup(int) __attribute__((__noreturn__));
55 
56 audio_info_t	info;
57 int	volume;
58 int	balance;
59 int	port;
60 int	fflag;
61 int	qflag;
62 int	verbose;
63 int	sample_rate;
64 int	encoding;
65 char	*encoding_str;
66 int	precision;
67 int	channels;
68 
69 char	const *play_errstring = NULL;
70 size_t	bufsize;
71 int	audiofd;
72 int	exitstatus = EXIT_SUCCESS;
73 
74 int
75 main(argc, argv)
76 	int argc;
77 	char *argv[];
78 {
79 	size_t	len;
80 	int	ch;
81 	int	iflag = 0;
82 	const char *defdevice = _PATH_SOUND;
83 	const char *device = NULL;
84 
85 	while ((ch = getopt(argc, argv, "b:C:c:d:e:fhip:P:qs:Vv:")) != -1) {
86 		switch (ch) {
87 		case 'b':
88 			decode_int(optarg, &balance);
89 			if (balance < 0 || balance > 64)
90 				errx(1, "balance must be between 0 and 63");
91 			break;
92 		case 'c':
93 			decode_int(optarg, &channels);
94 			if (channels < 0)
95 				errx(1, "channels must be positive");
96 			break;
97 		case 'C':
98 			/* Ignore, compatibility */
99 			break;
100 		case 'd':
101 			device = optarg;
102 			break;
103 		case 'e':
104 			encoding_str = optarg;
105 			break;
106 		case 'f':
107 			fflag = 1;
108 			break;
109 		case 'i':
110 			iflag++;
111 			break;
112 		case 'q':
113 			qflag++;
114 			break;
115 		case 'P':
116 			decode_int(optarg, &precision);
117 			if (precision != 4 && precision != 8 &&
118 			    precision != 16 && precision != 24 &&
119 			    precision != 32)
120 				errx(1, "precision must be between 4, 8, 16, 24 or 32");
121 			break;
122 		case 'p':
123 			len = strlen(optarg);
124 
125 			if (strncmp(optarg, "speaker", len) == 0)
126 				port |= AUDIO_SPEAKER;
127 			else if (strncmp(optarg, "headphone", len) == 0)
128 				port |= AUDIO_HEADPHONE;
129 			else if (strncmp(optarg, "line", len) == 0)
130 				port |= AUDIO_LINE_OUT;
131 			else
132 				errx(1,
133 			    "port must be `speaker', `headphone', or `line'");
134 			break;
135 		case 's':
136 			decode_int(optarg, &sample_rate);
137 			if (sample_rate < 0 || sample_rate > 48000 * 2)	/* XXX */
138 				errx(1, "sample rate must be between 0 and 96000");
139 			break;
140 		case 'V':
141 			verbose++;
142 			break;
143 		case 'v':
144 			volume = atoi(optarg);
145 			if (volume < 0 || volume > 255)
146 				errx(1, "volume must be between 0 and 255");
147 			break;
148 		/* case 'h': */
149 		default:
150 			usage();
151 			/* NOTREACHED */
152 		}
153 	}
154 	argc -= optind;
155 	argv += optind;
156 
157 	if (encoding_str) {
158 		encoding = audio_enc_to_val(encoding_str);
159 		if (encoding == -1)
160 			errx(1, "unknown encoding, bailing...");
161 	}
162 
163 	if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL &&
164 	    (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */
165 		device = defdevice;
166 
167 	audiofd = open(device, O_WRONLY);
168 	if (audiofd < 0 && device == defdevice) {
169 		device = _PATH_SOUND0;
170 		audiofd = open(device, O_WRONLY);
171 	}
172 
173 	if (audiofd < 0)
174 		err(1, "failed to open %s", device);
175 
176 	if (ioctl(audiofd, AUDIO_GETINFO, &info) < 0)
177 		err(1, "failed to get audio info");
178 	bufsize = info.play.buffer_size;
179 	if (bufsize < 32 * 1024)
180 		bufsize = 32 * 1024;
181 
182 	signal(SIGINT, cleanup);
183 	signal(SIGTERM, cleanup);
184 	signal(SIGHUP, cleanup);
185 
186 	if (*argv)
187 		do
188 			play(*argv++);
189 		while (*argv);
190 	else
191 		play_fd("standard input", STDIN_FILENO);
192 
193 	cleanup(0);
194 }
195 
196 void
197 cleanup(signo)
198 	int signo;
199 {
200 
201 	(void)ioctl(audiofd, AUDIO_FLUSH, NULL);
202 	(void)ioctl(audiofd, AUDIO_SETINFO, &info);
203 	close(audiofd);
204 	exit(exitstatus);
205 }
206 
207 void
208 play(file)
209 	char *file;
210 {
211 	struct stat sb;
212 	void *addr, *oaddr;
213 	off_t	filesize;
214 	size_t	sizet_filesize;
215 	size_t datasize = 0;
216 	ssize_t	hdrlen;
217 	int fd;
218 
219 	if (file[0] == '-' && file[1] == 0) {
220 		play_fd("standard input", STDIN_FILENO);
221 		return;
222 	}
223 
224 	fd = open(file, O_RDONLY);
225 	if (fd < 0) {
226 		if (!qflag)
227 			warn("could not open %s", file);
228 		exitstatus = EXIT_FAILURE;
229 		return;
230 	}
231 
232 	if (fstat(fd, &sb) < 0)
233 		err(1, "could not fstat %s", file);
234 	filesize = sb.st_size;
235 	sizet_filesize = (size_t)filesize;
236 
237 	/*
238 	 * if the file is not a regular file, doesn't fit in a size_t,
239 	 * or if we failed to mmap the file, try to read it instead, so
240 	 * that filesystems, etc, that do not support mmap() work
241 	 */
242 	if (S_ISREG(sb.st_rdev & S_IFMT) == 0 ||
243 	    ((off_t)sizet_filesize != filesize) ||
244 	    (oaddr = addr = mmap(0, sizet_filesize, PROT_READ,
245 	    MAP_SHARED, fd, 0)) == MAP_FAILED) {
246 		play_fd(file, fd);
247 		close(fd);
248 		return;
249 	}
250 
251 	/*
252 	 * give the VM system a bit of a hint about the type
253 	 * of accesses we will make.
254 	 */
255 	if (madvise(addr, sizet_filesize, MADV_SEQUENTIAL) < 0 &&
256 	    !qflag)
257 		warn("madvise failed, ignoring");
258 
259 	/*
260 	 * get the header length and set up the audio device
261 	 */
262 	if ((hdrlen = audioctl_write_fromhdr(addr,
263 	    sizet_filesize, audiofd, &datasize, file)) < 0) {
264 		if (play_errstring)
265 			errx(1, "%s: %s", play_errstring, file);
266 		else
267 			errx(1, "unknown audio file: %s", file);
268 	}
269 
270 	filesize -= hdrlen;
271 	addr = (char *)addr + hdrlen;
272 	if (filesize < datasize || datasize == 0) {
273 		if (filesize < datasize)
274 			warnx("bogus datasize: %ld", (u_long)datasize);
275 		datasize = filesize;
276 	}
277 
278 	while (datasize > bufsize) {
279 		if (write(audiofd, addr, bufsize) != bufsize)
280 			err(1, "write failed");
281 		addr = (char *)addr + bufsize;
282 		datasize -= bufsize;
283 	}
284 	if (write(audiofd, addr, (size_t)datasize) != (ssize_t)datasize)
285 		err(1, "final write failed");
286 
287 	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
288 		warn("audio drain ioctl failed");
289 	if (munmap(oaddr, sizet_filesize) < 0)
290 		err(1, "munmap failed");
291 
292 	close(fd);
293 }
294 
295 /*
296  * play the file on the file descriptor fd
297  */
298 void
299 play_fd(file, fd)
300 	const char *file;
301 	int     fd;
302 {
303 	char    *buffer = malloc(bufsize);
304 	ssize_t hdrlen;
305 	int     nr, nw;
306 	size_t	datasize = 0;
307 	size_t	dataout = 0;
308 
309 	if (buffer == NULL)
310 		err(1, "malloc of read buffer failed");
311 
312 	nr = read(fd, buffer, bufsize);
313 	if (nr < 0)
314 		goto read_error;
315 	if (nr == 0) {
316 		if (fflag)
317 			return;
318 		err(1, "unexpected EOF");
319 	}
320 	hdrlen = audioctl_write_fromhdr(buffer, nr, audiofd, &datasize, file);
321 	if (hdrlen < 0) {
322 		if (play_errstring)
323 			errx(1, "%s: %s", play_errstring, file);
324 		else
325 			errx(1, "unknown audio file: %s", file);
326 	}
327 	if (hdrlen > 0) {
328 		if (hdrlen >= nr)	/* shouldn't happen */
329 			errx(1, "header seems really large");
330 		memmove(buffer, buffer + hdrlen, nr - hdrlen);
331 		nr -= hdrlen;
332 	}
333 	while (datasize == 0 || dataout < datasize) {
334 		if (datasize != 0 && dataout + nr > datasize)
335 			nr = datasize - dataout;
336 		nw = write(audiofd, buffer, nr);
337 		if (nw != nr)
338 			goto write_error;
339 		dataout += nw;
340 		nr = read(fd, buffer, bufsize);
341 		if (nr == -1)
342 			goto read_error;
343 		if (nr == 0)
344 			break;
345 	}
346 	/* something to think about: no message given for dataout < datasize */
347 	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
348 		warn("audio drain ioctl failed");
349 	return;
350 read_error:
351 	err(1, "read of standard input failed");
352 write_error:
353 	err(1, "audio device write failed");
354 }
355 
356 /*
357  * only support sun and wav audio files so far ...
358  *
359  * XXX this should probably be mostly part of libaudio, but it
360  * uses the local "info" variable. blah... fix me!
361  */
362 ssize_t
363 audioctl_write_fromhdr(hdr, fsz, fd, datasize, file)
364 	void	*hdr;
365 	size_t	fsz;
366 	int	fd;
367 	size_t	*datasize;
368 	const char	*file;
369 {
370 	sun_audioheader	*sunhdr;
371 	ssize_t	hdr_len;
372 
373 	AUDIO_INITINFO(&info);
374 	sunhdr = hdr;
375 	if (ntohl(sunhdr->magic) == AUDIO_FILE_MAGIC) {
376 		if (audio_sun_to_encoding(ntohl(sunhdr->encoding),
377 		    &info.play.encoding, &info.play.precision)) {
378 			if (!qflag)
379 				warnx("unknown unsupported Sun audio encoding"
380 				      " format %d", ntohl(sunhdr->encoding));
381 			if (fflag)
382 				goto set_audio_mode;
383 			return (-1);
384 		}
385 
386 		info.play.sample_rate = ntohl(sunhdr->sample_rate);
387 		info.play.channels = ntohl(sunhdr->channels);
388 		hdr_len = ntohl(sunhdr->hdr_size);
389 
390 		*datasize = ntohl(sunhdr->data_size);
391 		goto set_audio_mode;
392 	}
393 
394 	hdr_len = audio_wav_parse_hdr(hdr, fsz, &info.play.encoding,
395 	    &info.play.precision, &info.play.sample_rate, &info.play.channels,
396 	    datasize);
397 
398 	switch (hdr_len) {
399 	case AUDIO_ESHORTHDR:
400 	case AUDIO_EWAVUNSUPP:
401 	case AUDIO_EWAVBADPCM:
402 	case AUDIO_EWAVNODATA:
403 		play_errstring = audio_errstring(hdr_len);
404 		/* FALL THROUGH */
405 	case AUDIO_ENOENT:
406 		break;
407 	default:
408 		if (hdr_len < 1)
409 			break;
410 		goto set_audio_mode;
411 	}
412 	/*
413 	 * if we don't know it, bail unless we are forcing.
414 	 */
415 	if (fflag == 0)
416 		return (-1);
417 set_audio_mode:
418 	if (port)
419 		info.play.port = port;
420 	if (volume)
421 		info.play.gain = volume;
422 	if (balance)
423 		info.play.balance = balance;
424 	if (fflag) {
425 		if (sample_rate)
426 			info.play.sample_rate = sample_rate;
427 		if (channels)
428 			info.play.channels = channels;
429 		if (encoding)
430 			info.play.encoding = encoding;
431 		if (precision)
432 			info.play.precision = precision;
433 		hdr_len = 0;
434 	}
435 	info.mode = AUMODE_PLAY_ALL;
436 
437 	if (verbose) {
438 		const char *enc = audio_enc_from_val(info.play.encoding);
439 
440 		printf("%s: sample_rate=%d channels=%d "
441 		   "precision=%d%s%s\n", file,
442 		   info.play.sample_rate,
443 		   info.play.channels,
444 		   info.play.precision,
445 		   enc ? " encoding=" : "",
446 		   enc ? enc : "");
447 	}
448 
449 	if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
450 		err(1, "failed to set audio info");
451 
452 	return (hdr_len);
453 }
454 
455 void
456 usage()
457 {
458 
459 	fprintf(stderr, "Usage: %s [-hiqV] [options] files\n", getprogname());
460 	fprintf(stderr, "Options:\n\t"
461 	    "-C audio control device\n\t"
462 	    "-b balance (0-63)\n\t"
463 	    "-d audio device\n\t"
464 	    "-f force settings\n\t"
465 	    "\t-c forced channels\n\t"
466 	    "\t-e forced encoding\n\t"
467 	    "\t-P forced precision\n\t"
468 	    "\t-s forced sample rate\n\t"
469 	    "-i header information\n\t"
470 	    "-m monitor volume\n\t"
471 	    "-p output port\n\t"
472 	    "-v volume\n");
473 	exit(EXIT_FAILURE);
474 }
475