summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.bin/aucat/aucat.c162
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--;