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