Annotation of src/usr.bin/aucat/aucat.c, Revision 1.13
1.13 ! uwe 1: /* $OpenBSD: aucat.c,v 1.12 2006/12/20 06:45:10 steven Exp $ */
1.1 kstailey 2: /*
3: * Copyright (c) 1997 Kenneth Stailey. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by Kenneth Stailey.
16: * 4. The name of the author may not be used to endorse or promote products
17: * derived from this software without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29: */
30:
31: #include <sys/types.h>
1.13 ! uwe 32: #include <sys/audioio.h>
! 33: #include <sys/ioctl.h>
! 34:
1.1 kstailey 35: #include <fcntl.h>
36: #include <stdio.h>
1.4 millert 37: #include <stdlib.h>
1.8 david 38: #include <string.h>
1.1 kstailey 39: #include <unistd.h>
1.4 millert 40: #include <err.h>
1.1 kstailey 41:
1.11 jaredy 42: #define _PATH_AUDIO "/dev/audio"
43:
1.13 ! uwe 44: /*
! 45: * aucat: concatenate and play Sun 8-bit .au files or 8/16-bit
! 46: * uncompressed WAVE RIFF files
1.1 kstailey 47: */
1.7 deraadt 48:
1.13 ! uwe 49: int playfile(int, char *, audio_info_t *);
! 50: int readwaveheader(int, audio_info_t *);
1.11 jaredy 51: void usage(void) __attribute__((__noreturn__));
1.1 kstailey 52:
1.13 ! uwe 53: int afd = -1;
! 54:
1.11 jaredy 55: /*
56: * function playfile: given a file which is positioned at the beginning
1.1 kstailey 57: * of what is assumed to be an .au data stream copy it out to the audio
1.5 millert 58: * device. Return 0 on success, -1 on failure.
1.1 kstailey 59: */
60: int
1.13 ! uwe 61: playfile(int fd, char *dev, audio_info_t *audioinfo)
1.1 kstailey 62: {
1.11 jaredy 63: ssize_t rd;
1.4 millert 64: char buf[5120];
65:
66: if (afd == -1 && (afd = open(dev, O_WRONLY)) < 0) {
67: warn("can't open %s", dev);
68: return(-1);
69: }
1.13 ! uwe 70:
! 71: /*
! 72: * If we don't wait here, the AUDIO_SETINFO ioctl interrupts
! 73: * the playback of the previous file.
! 74: */
! 75: if (ioctl(afd, AUDIO_DRAIN, NULL) == -1)
! 76: warn("AUDIO_DRAIN");
! 77:
! 78: if (ioctl(afd, AUDIO_SETINFO, audioinfo) == -1) {
! 79: warn("AUDIO_SETINFO");
! 80: return -1;
! 81: }
! 82:
1.11 jaredy 83: while ((rd = read(fd, buf, sizeof(buf))) > 0)
84: if (write(afd, buf, rd) != rd)
85: warn("write");
86: if (rd == -1)
87: warn("read");
1.4 millert 88:
89: return (0);
1.1 kstailey 90: }
91:
1.13 ! uwe 92: /*
! 93: * function readwaveheader: given a file which is positioned at four
! 94: * bytes into a RIFF file header, read the rest of the header, check
! 95: * to see if it is a simple WAV file that we can handle, seek to the
! 96: * beginning of the audio data, and set the playback parameters in
! 97: * the audio_info_t structure. Return 0 on success, -1 on failure.
! 98: */
! 99: int
! 100: readwaveheader(int fd, audio_info_t *audioinfo)
! 101: {
! 102: /*
! 103: * The simplest form of a RIFF file...
! 104: */
! 105: struct {
! 106: /* u_int32_t riff_chunkid; -- this is read before in main()! */
! 107: u_int32_t riff_chunksize;
! 108: u_int32_t riff_format;
! 109:
! 110: u_int32_t fmt_subchunkid;
! 111: u_int32_t fmt_subchunksize;
! 112:
! 113: u_int16_t fmt_format; /* 1 = PCM uncompressed */
! 114: u_int16_t fmt_channels; /* 1 = mono, 2 = stereo */
! 115: u_int32_t fmt_samplespersec; /* 8000, 22050, 44100 etc. */
! 116: u_int32_t fmt_byterate; /* total bytes per second */
! 117: u_int16_t fmt_blockalign; /* channels * bitspersample/8 */
! 118: u_int16_t fmt_bitspersample; /* 8 = 8 bits, 16 = 16 bits etc. */
! 119: } header;
! 120: u_int datatag;
! 121: char c;
! 122:
! 123: /*
! 124: * Is it an uncompressed wave file?
! 125: */
! 126: if (read(fd, &header, sizeof(header)) != sizeof(header)) {
! 127: warn("read");
! 128: return -1;
! 129: }
! 130: if (strncmp((char *) &header.riff_format, "WAVE", 4) ||
! 131: letoh16(header.fmt_format) != 1 ||
! 132: strncmp((char *) &header.fmt_subchunkid, "fmt ", 4) ||
! 133: (letoh16(header.fmt_bitspersample) != 8 &&
! 134: letoh16(header.fmt_bitspersample) != 16))
! 135: return -1;
! 136:
! 137: /*
! 138: * Seek to the data chunk.
! 139: */
! 140: for (datatag = 0; datatag < 4; ) {
! 141: if (read(fd, &c, 1) != 1) {
! 142: warn("read");
! 143: return -1;
! 144: }
! 145:
! 146: switch(datatag) {
! 147: case 0:
! 148: if (c == 'd')
! 149: ++datatag;
! 150: break;
! 151: case 1:
! 152: if (c == 'a')
! 153: ++datatag;
! 154: break;
! 155: case 2:
! 156: if (c == 't')
! 157: ++datatag;
! 158: break;
! 159: case 3:
! 160: if (c == 'a')
! 161: ++datatag;
! 162: break;
! 163: default:
! 164: datatag = 0;
! 165: break;
! 166: }
! 167: }
! 168: if (datatag != 4) {
! 169: warnx("no data chunk found in wave file");
! 170: return -1;
! 171: }
! 172:
! 173: /*
! 174: * Ignore the size of the data chunk.
! 175: */
! 176: if (lseek(fd, 4, SEEK_CUR) == -1) {
! 177: warn("lseek");
! 178: return -1;
! 179: }
! 180:
! 181: audioinfo->play.sample_rate = letoh32(header.fmt_samplespersec);
! 182: audioinfo->play.channels = letoh16(header.fmt_channels);
! 183: audioinfo->play.precision = letoh16(header.fmt_bitspersample);
! 184: audioinfo->play.encoding = audioinfo->play.precision == 8 ?
! 185: AUDIO_ENCODING_ULINEAR : AUDIO_ENCODING_SLINEAR_LE;
! 186: return 0;
! 187: }
! 188:
1.1 kstailey 189: int
1.6 deraadt 190: main(int argc, char *argv[])
1.1 kstailey 191: {
1.4 millert 192: int fd, ch;
1.11 jaredy 193: u_int32_t data;
194: char magic[4];
1.4 millert 195: char *dev;
1.13 ! uwe 196: audio_info_t ai;
! 197: audio_info_t ai_defaults;
1.4 millert 198:
199: dev = getenv("AUDIODEVICE");
1.11 jaredy 200: if (dev == NULL)
201: dev = _PATH_AUDIO;
1.4 millert 202:
203: while ((ch = getopt(argc, argv, "f:")) != -1) {
1.11 jaredy 204: switch (ch) {
1.4 millert 205: case 'f':
206: dev = optarg;
207: break;
1.11 jaredy 208: default:
209: usage();
210: /* NOTREACHED */
1.4 millert 211: }
212: }
213: argc -= optind;
214: argv += optind;
215:
1.11 jaredy 216: if (argc == 0)
217: usage();
1.13 ! uwe 218:
! 219: if (afd == -1 && (afd = open(dev, O_WRONLY)) < 0)
! 220: err(1, "can't open %s", dev);
! 221:
! 222: if (ioctl(afd, AUDIO_GETINFO, &ai_defaults) == -1)
! 223: err(1, "AUDIO_GETINFO");
! 224:
1.4 millert 225: while (argc) {
226: if ((fd = open(*argv, O_RDONLY)) < 0)
227: err(1, "cannot open %s", *argv);
228:
1.13 ! uwe 229: AUDIO_INITINFO(&ai);
! 230:
! 231: ai.play.sample_rate = ai_defaults.play.sample_rate;
! 232: ai.play.channels = ai_defaults.play.channels;
! 233: ai.play.encoding = ai_defaults.play.encoding;
! 234: ai.play.precision = ai_defaults.play.precision;
! 235:
1.11 jaredy 236: if (read(fd, magic, sizeof(magic)) != sizeof(magic) ||
237: strncmp(magic, ".snd", 4)) {
1.4 millert 238: /*
239: * not an .au file, bad header.
1.13 ! uwe 240: * Check if it could be a .wav file and set
! 241: * the playback parameters in ai.
1.4 millert 242: */
1.13 ! uwe 243: if (strncmp(magic, "RIFF", 4) ||
! 244: readwaveheader(fd, &ai)) {
! 245: /*
! 246: * Assume raw audio data since that's
! 247: * what /dev/audio generates by default.
! 248: */
! 249: if (lseek(fd, 0, SEEK_SET) == -1)
! 250: warn("lseek");
! 251: }
1.4 millert 252: } else {
1.11 jaredy 253: if (read(fd, &data, sizeof(data)) == sizeof(data)) {
254: data = ntohl(data);
255: if (lseek(fd, (off_t)data, SEEK_SET) == -1)
256: warn("lseek");
257: }
1.4 millert 258: }
1.13 ! uwe 259:
! 260: if (playfile(fd, dev, &ai) < 0)
1.4 millert 261: exit(1);
1.10 ian 262: (void) close(fd);
1.4 millert 263: argc--;
264: argv++;
265: }
266: exit(0);
1.11 jaredy 267: }
268:
269: void
270: usage(void)
271: {
272: extern char *__progname;
273:
274: fprintf(stderr, "usage: %s [-f device] file ...\n", __progname);
275: exit(1);
1.1 kstailey 276: }