1 /* $NetBSD: play.c,v 1.49 2008/05/29 14:51:27 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 * 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.49 2008/05/29 14:51:27 mrg 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: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 'c': 98 decode_int(optarg, &channels); 99 if (channels < 0) 100 errx(1, "channels must be positive"); 101 break; 102 case 'C': 103 /* Ignore, compatibility */ 104 break; 105 case 'd': 106 device = optarg; 107 break; 108 case 'e': 109 encoding_str = optarg; 110 break; 111 case 'f': 112 fflag = 1; 113 break; 114 case 'i': 115 iflag++; 116 break; 117 case 'q': 118 qflag++; 119 break; 120 case 'P': 121 decode_int(optarg, &precision); 122 if (precision != 4 && precision != 8 && 123 precision != 16 && precision != 24 && 124 precision != 32) 125 errx(1, "precision must be between 4, 8, 16, 24 or 32"); 126 break; 127 case 'p': 128 len = strlen(optarg); 129 130 if (strncmp(optarg, "speaker", len) == 0) 131 port |= AUDIO_SPEAKER; 132 else if (strncmp(optarg, "headphone", len) == 0) 133 port |= AUDIO_HEADPHONE; 134 else if (strncmp(optarg, "line", len) == 0) 135 port |= AUDIO_LINE_OUT; 136 else 137 errx(1, 138 "port must be `speaker', `headphone', or `line'"); 139 break; 140 case 's': 141 decode_int(optarg, &sample_rate); 142 if (sample_rate < 0 || sample_rate > 48000 * 2) /* XXX */ 143 errx(1, "sample rate must be between 0 and 96000"); 144 break; 145 case 'V': 146 verbose++; 147 break; 148 case 'v': 149 volume = atoi(optarg); 150 if (volume < 0 || volume > 255) 151 errx(1, "volume must be between 0 and 255"); 152 break; 153 /* case 'h': */ 154 default: 155 usage(); 156 /* NOTREACHED */ 157 } 158 } 159 argc -= optind; 160 argv += optind; 161 162 if (encoding_str) { 163 encoding = audio_enc_to_val(encoding_str); 164 if (encoding == -1) 165 errx(1, "unknown encoding, bailing..."); 166 } 167 168 if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL && 169 (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */ 170 device = defdevice; 171 172 audiofd = open(device, O_WRONLY); 173 if (audiofd < 0 && device == defdevice) { 174 device = _PATH_SOUND0; 175 audiofd = open(device, O_WRONLY); 176 } 177 178 if (audiofd < 0) 179 err(1, "failed to open %s", device); 180 181 if (ioctl(audiofd, AUDIO_GETINFO, &info) < 0) 182 err(1, "failed to get audio info"); 183 bufsize = info.play.buffer_size; 184 if (bufsize < 32 * 1024) 185 bufsize = 32 * 1024; 186 187 signal(SIGINT, cleanup); 188 signal(SIGTERM, cleanup); 189 signal(SIGHUP, cleanup); 190 191 if (*argv) 192 do 193 play(*argv++); 194 while (*argv); 195 else 196 play_fd("standard input", STDIN_FILENO); 197 198 cleanup(0); 199 } 200 201 void 202 cleanup(signo) 203 int signo; 204 { 205 206 (void)ioctl(audiofd, AUDIO_FLUSH, NULL); 207 (void)ioctl(audiofd, AUDIO_SETINFO, &info); 208 close(audiofd); 209 if (signo != 0) { 210 (void)raise_default_signal(signo); 211 } 212 exit(exitstatus); 213 } 214 215 void 216 play(file) 217 char *file; 218 { 219 struct stat sb; 220 void *addr, *oaddr; 221 off_t filesize; 222 size_t sizet_filesize; 223 size_t datasize = 0; 224 ssize_t hdrlen; 225 int fd; 226 227 if (file[0] == '-' && file[1] == 0) { 228 play_fd("standard input", STDIN_FILENO); 229 return; 230 } 231 232 fd = open(file, O_RDONLY); 233 if (fd < 0) { 234 if (!qflag) 235 warn("could not open %s", file); 236 exitstatus = EXIT_FAILURE; 237 return; 238 } 239 240 if (fstat(fd, &sb) < 0) 241 err(1, "could not fstat %s", file); 242 filesize = sb.st_size; 243 sizet_filesize = (size_t)filesize; 244 245 /* 246 * if the file is not a regular file, doesn't fit in a size_t, 247 * or if we failed to mmap the file, try to read it instead, so 248 * that filesystems, etc, that do not support mmap() work 249 */ 250 if (S_ISREG(sb.st_rdev & S_IFMT) == 0 || 251 ((off_t)sizet_filesize != filesize) || 252 (oaddr = addr = mmap(0, sizet_filesize, PROT_READ, 253 MAP_SHARED, fd, 0)) == MAP_FAILED) { 254 play_fd(file, fd); 255 close(fd); 256 return; 257 } 258 259 /* 260 * give the VM system a bit of a hint about the type 261 * of accesses we will make. 262 */ 263 if (madvise(addr, sizet_filesize, MADV_SEQUENTIAL) < 0 && 264 !qflag) 265 warn("madvise failed, ignoring"); 266 267 /* 268 * get the header length and set up the audio device 269 */ 270 if ((hdrlen = audioctl_write_fromhdr(addr, 271 sizet_filesize, audiofd, &datasize, file)) < 0) { 272 if (play_errstring) 273 errx(1, "%s: %s", play_errstring, file); 274 else 275 errx(1, "unknown audio file: %s", file); 276 } 277 278 filesize -= hdrlen; 279 addr = (char *)addr + hdrlen; 280 if (filesize < datasize || datasize == 0) { 281 if (filesize < datasize) 282 warnx("bogus datasize: %ld", (u_long)datasize); 283 datasize = filesize; 284 } 285 286 while (datasize > bufsize) { 287 if (write(audiofd, addr, bufsize) != bufsize) 288 err(1, "write failed"); 289 addr = (char *)addr + bufsize; 290 datasize -= bufsize; 291 } 292 if (write(audiofd, addr, (size_t)datasize) != (ssize_t)datasize) 293 err(1, "final write failed"); 294 295 if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag) 296 warn("audio drain ioctl failed"); 297 if (munmap(oaddr, sizet_filesize) < 0) 298 err(1, "munmap failed"); 299 300 close(fd); 301 } 302 303 /* 304 * play the file on the file descriptor fd 305 */ 306 void 307 play_fd(file, fd) 308 const char *file; 309 int fd; 310 { 311 char *buffer = malloc(bufsize); 312 ssize_t hdrlen; 313 int nr, nw; 314 size_t datasize = 0; 315 size_t dataout = 0; 316 317 if (buffer == NULL) 318 err(1, "malloc of read buffer failed"); 319 320 nr = read(fd, buffer, bufsize); 321 if (nr < 0) 322 goto read_error; 323 if (nr == 0) { 324 if (fflag) { 325 free(buffer); 326 return; 327 } 328 err(1, "unexpected EOF"); 329 } 330 hdrlen = audioctl_write_fromhdr(buffer, nr, audiofd, &datasize, file); 331 if (hdrlen < 0) { 332 if (play_errstring) 333 errx(1, "%s: %s", play_errstring, file); 334 else 335 errx(1, "unknown audio file: %s", file); 336 } 337 if (hdrlen > 0) { 338 if (hdrlen > nr) /* shouldn't happen */ 339 errx(1, "header seems really large: %lld", (long long)hdrlen); 340 memmove(buffer, buffer + hdrlen, nr - hdrlen); 341 nr -= hdrlen; 342 } 343 while (datasize == 0 || dataout < datasize) { 344 if (datasize != 0 && dataout + nr > datasize) 345 nr = datasize - dataout; 346 nw = write(audiofd, buffer, nr); 347 if (nw != nr) 348 goto write_error; 349 dataout += nw; 350 nr = read(fd, buffer, bufsize); 351 if (nr == -1) 352 goto read_error; 353 if (nr == 0) 354 break; 355 } 356 /* something to think about: no message given for dataout < datasize */ 357 if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag) 358 warn("audio drain ioctl failed"); 359 return; 360 read_error: 361 err(1, "read of standard input failed"); 362 write_error: 363 err(1, "audio device write failed"); 364 } 365 366 /* 367 * only support sun and wav audio files so far ... 368 * 369 * XXX this should probably be mostly part of libaudio, but it 370 * uses the local "info" variable. blah... fix me! 371 */ 372 ssize_t 373 audioctl_write_fromhdr(hdr, fsz, fd, datasize, file) 374 void *hdr; 375 size_t fsz; 376 int fd; 377 size_t *datasize; 378 const char *file; 379 { 380 sun_audioheader *sunhdr; 381 ssize_t hdr_len = 0; 382 383 AUDIO_INITINFO(&info); 384 sunhdr = hdr; 385 if (ntohl(sunhdr->magic) == AUDIO_FILE_MAGIC) { 386 if (audio_sun_to_encoding(ntohl(sunhdr->encoding), 387 &info.play.encoding, &info.play.precision)) { 388 if (!qflag) 389 warnx("unknown unsupported Sun audio encoding" 390 " format %d", ntohl(sunhdr->encoding)); 391 if (fflag) 392 goto set_audio_mode; 393 return (-1); 394 } 395 396 info.play.sample_rate = ntohl(sunhdr->sample_rate); 397 info.play.channels = ntohl(sunhdr->channels); 398 hdr_len = ntohl(sunhdr->hdr_size); 399 400 *datasize = ntohl(sunhdr->data_size); 401 goto set_audio_mode; 402 } 403 404 hdr_len = audio_wav_parse_hdr(hdr, fsz, &info.play.encoding, 405 &info.play.precision, &info.play.sample_rate, &info.play.channels, 406 datasize); 407 408 switch (hdr_len) { 409 case AUDIO_ESHORTHDR: 410 case AUDIO_EWAVUNSUPP: 411 case AUDIO_EWAVBADPCM: 412 case AUDIO_EWAVNODATA: 413 play_errstring = audio_errstring(hdr_len); 414 /* FALL THROUGH */ 415 case AUDIO_ENOENT: 416 break; 417 default: 418 if (hdr_len < 1) 419 break; 420 goto set_audio_mode; 421 } 422 /* 423 * if we don't know it, bail unless we are forcing. 424 */ 425 if (fflag == 0) 426 return (-1); 427 set_audio_mode: 428 if (port) 429 info.play.port = port; 430 if (volume) 431 info.play.gain = volume; 432 if (balance) 433 info.play.balance = balance; 434 if (fflag) { 435 if (sample_rate) 436 info.play.sample_rate = sample_rate; 437 if (channels) 438 info.play.channels = channels; 439 if (encoding) 440 info.play.encoding = encoding; 441 if (precision) 442 info.play.precision = precision; 443 hdr_len = 0; 444 } 445 info.mode = AUMODE_PLAY_ALL; 446 447 if (verbose) { 448 const char *enc = audio_enc_from_val(info.play.encoding); 449 450 printf("%s: sample_rate=%d channels=%d " 451 "datasize=%lld " 452 "precision=%d%s%s\n", file, 453 info.play.sample_rate, 454 info.play.channels, 455 (long long)*datasize, 456 info.play.precision, 457 enc ? " encoding=" : "", 458 enc ? enc : ""); 459 } 460 461 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) 462 err(1, "failed to set audio info"); 463 464 return (hdr_len); 465 } 466 467 void 468 usage() 469 { 470 471 fprintf(stderr, "Usage: %s [-hiqV] [options] files\n", getprogname()); 472 fprintf(stderr, "Options:\n\t" 473 "-C audio control device\n\t" 474 "-b balance (0-63)\n\t" 475 "-d audio device\n\t" 476 "-f force settings\n\t" 477 "\t-c forced channels\n\t" 478 "\t-e forced encoding\n\t" 479 "\t-P forced precision\n\t" 480 "\t-s forced sample rate\n\t" 481 "-i header information\n\t" 482 "-p output port\n\t" 483 "-v volume\n"); 484 exit(EXIT_FAILURE); 485 } 486