diff options
-rw-r--r-- | usr.bin/aucat/aucat.c | 162 |
1 files changed, 151 insertions, 11 deletions
diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index 38b1f640586..a6410e594ef 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.12 2006/12/20 06:45:10 steven Exp $ */ +/* $OpenBSD: aucat.c,v 1.13 2007/03/20 23:35:15 uwe Exp $ */ /* * Copyright (c) 1997 Kenneth Stailey. All rights reserved. * @@ -29,6 +29,9 @@ */ #include <sys/types.h> +#include <sys/audioio.h> +#include <sys/ioctl.h> + #include <fcntl.h> #include <stdio.h> #include <stdlib.h> @@ -38,22 +41,25 @@ #define _PATH_AUDIO "/dev/audio" -/* - * aucat: concatenate and play Sun 8-bit .au files +/* + * aucat: concatenate and play Sun 8-bit .au files or 8/16-bit + * uncompressed WAVE RIFF files */ -int playfile(int, char *); +int playfile(int, char *, audio_info_t *); +int readwaveheader(int, audio_info_t *); void usage(void) __attribute__((__noreturn__)); +int afd = -1; + /* * function playfile: given a file which is positioned at the beginning * of what is assumed to be an .au data stream copy it out to the audio * device. Return 0 on success, -1 on failure. */ int -playfile(int fd, char *dev) +playfile(int fd, char *dev, audio_info_t *audioinfo) { - static int afd = -1; ssize_t rd; char buf[5120]; @@ -61,6 +67,19 @@ playfile(int fd, char *dev) warn("can't open %s", dev); return(-1); } + + /* + * If we don't wait here, the AUDIO_SETINFO ioctl interrupts + * the playback of the previous file. + */ + if (ioctl(afd, AUDIO_DRAIN, NULL) == -1) + warn("AUDIO_DRAIN"); + + if (ioctl(afd, AUDIO_SETINFO, audioinfo) == -1) { + warn("AUDIO_SETINFO"); + return -1; + } + while ((rd = read(fd, buf, sizeof(buf))) > 0) if (write(afd, buf, rd) != rd) warn("write"); @@ -70,6 +89,103 @@ playfile(int fd, char *dev) return (0); } +/* + * function readwaveheader: given a file which is positioned at four + * bytes into a RIFF file header, read the rest of the header, check + * to see if it is a simple WAV file that we can handle, seek to the + * beginning of the audio data, and set the playback parameters in + * the audio_info_t structure. Return 0 on success, -1 on failure. + */ +int +readwaveheader(int fd, audio_info_t *audioinfo) +{ + /* + * The simplest form of a RIFF file... + */ + struct { + /* u_int32_t riff_chunkid; -- this is read before in main()! */ + u_int32_t riff_chunksize; + u_int32_t riff_format; + + u_int32_t fmt_subchunkid; + u_int32_t fmt_subchunksize; + + u_int16_t fmt_format; /* 1 = PCM uncompressed */ + u_int16_t fmt_channels; /* 1 = mono, 2 = stereo */ + u_int32_t fmt_samplespersec; /* 8000, 22050, 44100 etc. */ + u_int32_t fmt_byterate; /* total bytes per second */ + u_int16_t fmt_blockalign; /* channels * bitspersample/8 */ + u_int16_t fmt_bitspersample; /* 8 = 8 bits, 16 = 16 bits etc. */ + } header; + u_int datatag; + char c; + + /* + * Is it an uncompressed wave file? + */ + if (read(fd, &header, sizeof(header)) != sizeof(header)) { + warn("read"); + return -1; + } + if (strncmp((char *) &header.riff_format, "WAVE", 4) || + letoh16(header.fmt_format) != 1 || + strncmp((char *) &header.fmt_subchunkid, "fmt ", 4) || + (letoh16(header.fmt_bitspersample) != 8 && + letoh16(header.fmt_bitspersample) != 16)) + return -1; + + /* + * Seek to the data chunk. + */ + for (datatag = 0; datatag < 4; ) { + if (read(fd, &c, 1) != 1) { + warn("read"); + return -1; + } + + switch(datatag) { + case 0: + if (c == 'd') + ++datatag; + break; + case 1: + if (c == 'a') + ++datatag; + break; + case 2: + if (c == 't') + ++datatag; + break; + case 3: + if (c == 'a') + ++datatag; + break; + default: + datatag = 0; + break; + } + } + if (datatag != 4) { + warnx("no data chunk found in wave file"); + return -1; + } + + /* + * Ignore the size of the data chunk. + */ + if (lseek(fd, 4, SEEK_CUR) == -1) { + warn("lseek"); + return -1; + } + + audioinfo->play.sample_rate = letoh32(header.fmt_samplespersec); + audioinfo->play.channels = letoh16(header.fmt_channels); + audioinfo->play.precision = letoh16(header.fmt_bitspersample); + audioinfo->play.encoding = audioinfo->play.precision == 8 ? + AUDIO_ENCODING_ULINEAR : AUDIO_ENCODING_SLINEAR_LE; + return 0; +} + int main(int argc, char *argv[]) { @@ -77,6 +193,8 @@ main(int argc, char *argv[]) u_int32_t data; char magic[4]; char *dev; + audio_info_t ai; + audio_info_t ai_defaults; dev = getenv("AUDIODEVICE"); if (dev == NULL) @@ -97,19 +215,40 @@ main(int argc, char *argv[]) if (argc == 0) usage(); + + if (afd == -1 && (afd = open(dev, O_WRONLY)) < 0) + err(1, "can't open %s", dev); + + if (ioctl(afd, AUDIO_GETINFO, &ai_defaults) == -1) + err(1, "AUDIO_GETINFO"); + while (argc) { if ((fd = open(*argv, O_RDONLY)) < 0) err(1, "cannot open %s", *argv); + AUDIO_INITINFO(&ai); + + ai.play.sample_rate = ai_defaults.play.sample_rate; + ai.play.channels = ai_defaults.play.channels; + ai.play.encoding = ai_defaults.play.encoding; + ai.play.precision = ai_defaults.play.precision; + if (read(fd, magic, sizeof(magic)) != sizeof(magic) || strncmp(magic, ".snd", 4)) { /* * not an .au file, bad header. - * Assume raw audio data since that's - * what /dev/audio generates by default. + * Check if it could be a .wav file and set + * the playback parameters in ai. */ - if (lseek(fd, 0, SEEK_SET) == -1) - warn("lseek"); + if (strncmp(magic, "RIFF", 4) || + readwaveheader(fd, &ai)) { + /* + * Assume raw audio data since that's + * what /dev/audio generates by default. + */ + if (lseek(fd, 0, SEEK_SET) == -1) + warn("lseek"); + } } else { if (read(fd, &data, sizeof(data)) == sizeof(data)) { data = ntohl(data); @@ -117,7 +256,8 @@ main(int argc, char *argv[]) warn("lseek"); } } - if (playfile(fd, dev) < 0) + + if (playfile(fd, dev, &ai) < 0) exit(1); (void) close(fd); argc--; |