summaryrefslogtreecommitdiff
path: root/regress/sys
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2007-07-06 00:43:39 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2007-07-06 00:43:39 +0000
commit143dfa505dfdb77cc304a6e71b00b01e56063ee4 (patch)
treeef4d5545c11cfb95f95996bab6274b9262969dd7 /regress/sys
parentd5aca3c4fbca59367ae7e4186ab0ecd9a4fcd046 (diff)
regression tests for various audio read(2) and write(2) scenarios
Diffstat (limited to 'regress/sys')
-rw-r--r--regress/sys/dev/audio_rw/Makefile99
-rw-r--r--regress/sys/dev/audio_rw/audiotest_rw.1250
-rw-r--r--regress/sys/dev/audio_rw/audiotest_rw.c615
3 files changed, 964 insertions, 0 deletions
diff --git a/regress/sys/dev/audio_rw/Makefile b/regress/sys/dev/audio_rw/Makefile
new file mode 100644
index 00000000000..14a18e4f30b
--- /dev/null
+++ b/regress/sys/dev/audio_rw/Makefile
@@ -0,0 +1,99 @@
+# $OpenBSD: Makefile,v 1.1 2007/07/06 00:43:38 jakemsr Exp $
+
+PROG= audiotest_rw
+CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes
+MAN1= audiotest_rw.1
+
+.ifndef DO_AUTEST
+REGRESS_SKIP =
+.else
+REGRESS_TARGETS =
+REGRESS_TARGETS += run-regress-record
+REGRESS_TARGETS += run-regress-record-duplex
+REGRESS_TARGETS += run-regress-record-poll
+REGRESS_TARGETS += run-regress-record-select
+REGRESS_TARGETS += run-regress-record-poll-duplex
+REGRESS_TARGETS += run-regress-record-select-duplex
+REGRESS_TARGETS += run-regress-play
+REGRESS_TARGETS += run-regress-play-duplex
+REGRESS_TARGETS += run-regress-play-poll
+REGRESS_TARGETS += run-regress-play-select
+REGRESS_TARGETS += run-regress-play-poll-duplex
+REGRESS_TARGETS += run-regress-play-select-duplex
+REGRESS_TARGETS += run-regress-duplex
+REGRESS_TARGETS += run-regress-duplex-poll
+REGRESS_TARGETS += run-regress-duplex-select
+.endif
+
+
+# audio data file for playing tests
+
+master.pcm: ${PROG}
+ @echo "creating master input file"
+ ./audiotest_rw -o master.pcm
+
+
+# recording tests
+
+run-regress-record: ${PROG}
+ ./audiotest_rw -o test.pcm
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-record-duplex: ${PROG}
+ ./audiotest_rw -o test.pcm -d
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-record-poll: ${PROG}
+ ./audiotest_rw -o test.pcm -p
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-record-select: ${PROG}
+ ./audiotest_rw -o test.pcm -s
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-record-poll-duplex: ${PROG}
+ ./audiotest_rw -o test.pcm -p -d
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-record-select-duplex: ${PROG}
+ ./audiotest_rw -o test.pcm -s -d
+ @test -s test.pcm || (echo "no output" && false)
+
+
+# playing tests
+
+run-regress-play: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm
+
+run-regress-play-duplex: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -d
+
+run-regress-play-poll: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -p
+
+run-regress-play-select: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -s
+
+run-regress-play-poll-duplex: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -p -d
+
+run-regress-play-select-duplex: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -s -d
+
+
+# full-duplex tests
+
+run-regress-duplex: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -o test.pcm
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-duplex-poll: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -o test.pcm -p
+ @test -s test.pcm || (echo "no output" && false)
+
+run-regress-duplex-select: ${PROG} master.pcm
+ ./audiotest_rw -i master.pcm -o test.pcm -s
+ @test -s test.pcm || (echo "no output" && false)
+
+
+.include <bsd.regress.mk>
diff --git a/regress/sys/dev/audio_rw/audiotest_rw.1 b/regress/sys/dev/audio_rw/audiotest_rw.1
new file mode 100644
index 00000000000..5c3e62d84c6
--- /dev/null
+++ b/regress/sys/dev/audio_rw/audiotest_rw.1
@@ -0,0 +1,250 @@
+.\"
+.\" Copyright (c) 2007 Jacob Meuser <jakemsr@sdf.lonestar.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $Id: audiotest_rw.1,v 1.1 2007/07/06 00:43:38 jakemsr Exp $
+.\"
+.Dd $Mdocdate: June 13 2007
+.Dt AUDIOTEST_RW 1
+.Os
+.Sh NAME
+.Nm audiotest_rw
+.Nd test
+.Xr read 2
+and
+.Xr write 2
+with
+.Xr audio 4
+.Sh SYNOPSIS
+.Nm audiotest_rw
+.Bk -words
+.Op Fl dpsv
+.Op Fl b Ar buffersize
+.Op Fl c Ar channels
+.Op Fl e Ar encoding
+.Op Fl f Ar device
+.Op Fl i Ar input
+.Op Fl l Ar loops
+.Op Fl o Ar output
+.Op Fl r Ar samplerate
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+command reads data from and writes data to
+.Xr audio 4
+device
+.Ar device .
+The default
+.Ar device
+is
+.Pa /dev/audio .
+If the
+.Fl i
+option is used
+.Ar device
+will be opened write-only and
+.Nm
+will
+.Xr fread 3
+data from
+.Ar input
+and
+.Xr write 2
+it to
+.Ar device .
+If the
+.Fl o
+option is used
+.Ar device
+will be opened read-only and
+.Nm
+will
+.Xr read 2
+data from
+.Ar device
+and
+.Xr fwrite 3
+it to
+.Ar output .
+If both
+.Fl i
+and
+.Fl o
+are specified and
+.Ar device
+supports full-duplex operation,
+.Ar device
+will be opened read-write and
+.Nm
+will both
+.Xr fread 3
+data from
+.Ar input
+and
+.Xr write 2
+it to
+.Ar device
+and
+.Xr read 2
+data from
+.Ar device
+and
+.Xr fwrite 3
+it to
+.Ar output .
+In neither
+.Fl i
+nor
+.Fl o
+are used
+.Nm
+will exit with an error.
+.Pp
+The options are as follows:
+.Bl -tag -width "-b buffersize"
+.It Fl b Ar buffersize
+Size of buffer for
+.Xr read 2
+and
+.Xr write 2
+operations, in bytes.
+Valid arguments are 32 to 65536 inclusive.
+Defaults to 8192.
+.It Fl c Ar channels
+Number of audio channels.
+Valid arguments are 1 (mono) and 2 (stereo).
+Defaults to 2.
+.It Fl d
+Opens
+.Ar device
+read-write and sets full-duplex mode, regardless of
+.Fl i
+and
+.Fl o
+options.
+If
+.Ar device
+does not support full-duplex operation
+.Nm
+will exit with an error.
+.It Fl e Ar encoding
+Audio encoding to use.
+.Ar encoding
+is the index of the encoding to use in the list of encodings supported by
+.Xr device ,
+starting at 0.
+Defaults to 0.
+The list of upported encodings can be viewed with
+.Xr audioctl 1 :
+.Bd -literal -offset indent
+$ audioctl encodings
+.Ed
+.It Fl f Ar device
+.Xr audio 4
+device to use.
+Defaults to
+.Dq /dev/audio .
+.It Fl i Ar input
+File from which raw (headerless) audio data will be read.
+.It Fl l Ar loops
+Number of times to read and/or write.
+Defaults to 64.
+.It Fl o Ar output
+File to which raw (headerless) audio data will be written.
+.It Fl p
+Use
+.Xr poll 2
+to wait until data may be read without blocking on each
+.Xr read 2
+and to wait until data may be written without blocking on each
+.Xr write 2 .
+Using this option also causes
+.Ar device
+to be opened for non-blocking I/O.
+.It Fl r Ar samplerate
+Audio data sample rate in samples per second.
+Defaults to 48000.
+.It Fl s
+Use
+.Xr select 2
+to wait until data may be read without blocking on each
+.Xr read 2
+and to wait until data may be written without blocking on each
+.Xr write 2 .
+Using this option also causes
+.Ar device
+to be opened for non-blocking I/O.
+.El
+.Pp
+.Nm
+was written as a strict interpretation of
+.Xr audio 4 .
+Problems encountered while using
+.Nm
+are likely due to errors in
+.Xr audio 4
+documentation, the kernel's audio layer implementation, or audio
+device drivers.
+.Pp
+.Sh EXAMPLES
+The following command will open /dev/audio read-only, set /dev/audio
+to record mode with default parameters (channels:2
+encoding:0 sample rate 48000), and read data from /dev/audio and
+write it to the file test.pcm.
+.Bd -literal -offset indent
+$ audiotest_rw -o test.pcm
+.Ed
+.Pp
+The following command will open /dev/audio write-only, set /dev/audio
+to play mode with default parameters (channels:2
+encoding:0 sample rate 48000), and read data from the file test.pcm
+and write it to /dev/audio.
+.Bd -literal -offset indent
+$ audiotest_rw -i test.pcm
+.Ed
+.Pp
+The following command will open /dev/audio read-write, set /dev/audio
+to full-duplex mode with default parameters (channels:2
+encoding:0 sample rate 48000), read data from the file test.pcm
+and write it to /dev/audio, and read data from /dev/audio and
+write it to the file test2.pcm.
+.Bd -literal -offset indent
+$ audiotest_rw -i test.pcm -o test2.pcm
+.Ed
+.Pp
+The following command will open /dev/audio read-only with non-blocking
+I/O, set /dev/audio to record mode with default parameters (channels:2
+encoding:0 sample rate 48000), and read data from /dev/audio and
+write it to the file test.pcm, using poll() to determine when data
+is ready to be read.
+.Bd -literal -offset indent
+$ audiotest_rw -o test.pcm -p
+.Ed
+.Pp
+The following command will open /dev/audio read-write with non-blocking
+I/o, set /dev/audio to full-duplex mode with default parameters (channels:2
+encoding:0 sample rate 48000), read data from the file test.pcm
+and write it to /dev/audio, and read data from /dev/audio and
+write it to the file test2.pcm, using select() to determine when data
+is ready for reading and/or writing on /dev/audio.
+.Bd -literal -offset indent
+$ audiotest_rw -i test.pcm -o test2.pcm -s
+.Ed
+.Sh SEE ALSO
+.Xr audio 4
+.Sh AUTHORS
+.Nm
+and this manual page were written by
+.An Jacob Meuser Aq jakemsr@sdf.lonestar.org .
diff --git a/regress/sys/dev/audio_rw/audiotest_rw.c b/regress/sys/dev/audio_rw/audiotest_rw.c
new file mode 100644
index 00000000000..186f967a542
--- /dev/null
+++ b/regress/sys/dev/audio_rw/audiotest_rw.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2007 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: audiotest_rw.c,v 1.1 2007/07/06 00:43:38 jakemsr Exp $
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/audioio.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+
+extern char *__progname;
+/* extern int errno; */
+
+void useage(void);
+int audio_set_duplex(int, char *, int);
+int audio_set_info(int, u_int, u_int, u_int, u_int);
+int audio_wait_frame(int, u_int, int, int);
+int audio_do_frame(int, size_t , char *, char *, u_int, int, int);
+int audio_do_test(int, size_t, char *, char *, u_int, int, int, int, int);
+
+void
+useage(void)
+{
+ fprintf(stderr,
+ "Usage: %s [-dpsv] [-b buffersize] [-c channels] [-e encoding]\n"
+ " [-f device] [-i input ] [-l loops] [-o output] [-r samplerate]\n",
+ __progname);
+ return;
+}
+
+
+int
+audio_set_duplex(int audio_fd, char *audio_device, int use_duplex)
+{
+int i, has_duplex;
+
+ if (ioctl(audio_fd, AUDIO_GETPROPS, &i) < 0) {
+ warn("AUDIO_GETPROPS");
+ return 1;
+ }
+
+ has_duplex = i & AUDIO_PROP_FULLDUPLEX ? 1 : 0;
+
+ if (use_duplex && !has_duplex) {
+ warn("%s doesn't support full-duplex", audio_device);
+ return 1;
+ }
+
+ if (ioctl(audio_fd, AUDIO_SETFD, &use_duplex) < 0) {
+ warn("AUDIO_SETFD");
+ return 1;
+ }
+
+ if (ioctl(audio_fd, AUDIO_GETFD, &i) < 0) {
+ warn("AUDIO_GETFD");
+ return 1;
+ }
+
+ if (i != use_duplex)
+ return 1;
+
+ return 0;
+}
+
+
+int
+audio_set_info(int audio_fd, u_int mode, u_int encoding, u_int sample_rate,
+ u_int channels)
+{
+audio_info_t audio_if;
+audio_encoding_t audio_enc;
+u_int precision;
+
+ audio_enc.index = encoding;
+ if (ioctl(audio_fd, AUDIO_GETENC, &audio_enc) < 0) {
+ warn("AUDIO_GETENC");
+ return 1;
+ }
+
+ precision = audio_enc.precision;
+ encoding = audio_enc.encoding;
+
+ if (encoding == AUDIO_ENCODING_ULINEAR)
+ encoding = (BYTE_ORDER == LITTLE_ENDIAN) ?
+ AUDIO_ENCODING_ULINEAR_LE : AUDIO_ENCODING_ULINEAR_BE;
+
+ if (encoding == AUDIO_ENCODING_SLINEAR)
+ encoding = (BYTE_ORDER == LITTLE_ENDIAN) ?
+ AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
+
+ AUDIO_INITINFO(&audio_if);
+
+ audio_if.mode = mode;
+
+ if (mode & AUMODE_RECORD) {
+ audio_if.record.precision = precision;
+ audio_if.record.channels = channels;
+ audio_if.record.sample_rate = sample_rate;
+ audio_if.record.encoding = encoding;
+ }
+ if (mode & AUMODE_PLAY) {
+ audio_if.play.precision = precision;
+ audio_if.play.channels = channels;
+ audio_if.play.sample_rate = sample_rate;
+ audio_if.play.encoding = encoding;
+ }
+
+ if (ioctl(audio_fd, AUDIO_SETINFO, &audio_if) < 0) {
+ warn("AUDIO_SETINFO");
+ return 1;
+ }
+
+ if (ioctl(audio_fd, AUDIO_GETINFO, &audio_if) < 0) {
+ warn("AUDIO_GETINFO");
+ return 1;
+ }
+
+ if (mode & AUMODE_RECORD) {
+ if (audio_if.record.precision != precision) {
+ warnx("unable to set record precision: tried %u, got %u",
+ precision, audio_if.record.precision);
+ return 1;
+ }
+ if (audio_if.record.channels != channels){
+ warnx("unable to set record channels: tried %u, got %u",
+ channels, audio_if.record.channels);
+ return 1;
+ }
+ if (audio_if.record.sample_rate != sample_rate) {
+ warnx("unable to set record sample_rate: tried %u, got %u",
+ sample_rate, audio_if.record.sample_rate);
+ return 1;
+ }
+ if (audio_if.record.encoding != encoding) {
+ warnx("unable to set record encoding: tried %u, got %u",
+ encoding, audio_if.record.encoding);
+ return 1;
+ }
+ }
+
+ if (mode & AUMODE_PLAY) {
+ if (audio_if.play.precision != precision) {
+ warnx("unable to set play precision: tried %u, got %u",
+ precision, audio_if.play.precision);
+ return 1;
+ }
+ if (audio_if.play.channels != channels) {
+ warnx("unable to set play channels: tried %u, got %u",
+ channels, audio_if.play.channels);
+ return 1;
+ }
+ if (audio_if.play.sample_rate != sample_rate) {
+ warnx("unable to set play sample_rate: tried %u, got %u",
+ sample_rate, audio_if.play.sample_rate);
+ return 1;
+ }
+ if (audio_if.play.encoding != encoding) {
+ warnx("unable to set play encoding: tried %u, got %u",
+ encoding, audio_if.play.encoding);
+ return 1;
+ }
+ }
+
+ if (ioctl(audio_fd, AUDIO_FLUSH) < 0) {
+ warn("AUDIO_FLUSH");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* return 0 on error, 1 if read, 2 if write, 3 if both read and write */
+int
+audio_wait_frame(int audio_fd, u_int mode, int use_select, int use_poll)
+{
+struct pollfd pfd[1];
+fd_set *sfdsr;
+fd_set *sfdsw;
+struct timeval tv;
+int nfds, max;
+int ret;
+
+ ret = 0;
+
+ if (use_select) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ max = audio_fd;
+ sfdsr = NULL;
+ sfdsw = NULL;
+ if (mode & AUMODE_RECORD) {
+ if ((sfdsr = calloc(max + 1, sizeof(fd_mask))) == NULL) {
+ warn("fd_set sfdsr");
+ return 0;
+ }
+ FD_ZERO(sfdsr);
+ FD_SET(audio_fd, sfdsr);
+ }
+ if (mode & AUMODE_PLAY) {
+ if ((sfdsw = calloc(max + 1, sizeof(fd_mask))) == NULL) {
+ warn("fd_set sfdsw");
+ return 0;
+ }
+ FD_ZERO(sfdsw);
+ FD_SET(audio_fd, sfdsw);
+ }
+ nfds = select(max + 1, sfdsr, sfdsw, NULL, &tv);
+ if (nfds == -1) {
+ warn("select() error");
+ return 0;
+ }
+ if (nfds == 0) {
+ warnx("select() timed out");
+ return 0;
+ }
+ if (mode & AUMODE_RECORD)
+ if (FD_ISSET(audio_fd, sfdsr))
+ ret |= 1;
+ if (mode & AUMODE_PLAY)
+ if (FD_ISSET(audio_fd, sfdsw))
+ ret |= 2;
+ if (sfdsr != NULL)
+ free(sfdsr);
+ if (sfdsw != NULL)
+ free(sfdsw);
+ } else if (use_poll) {
+ bzero(&pfd[0], sizeof(struct pollfd));
+ pfd[0].fd = audio_fd;
+ if (mode & AUMODE_RECORD)
+ pfd[0].events |= POLLIN;
+ if (mode & AUMODE_PLAY)
+ pfd[0].events |= POLLOUT;
+ nfds = poll(pfd, 1, 1000);
+ if (nfds == -1 || (pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL))) {
+ warn("poll() error");
+ return 0;
+ }
+ if (nfds == 0) {
+ warnx("poll() timed out");
+ return 0;
+ }
+ if (mode & AUMODE_RECORD)
+ if (pfd[0].revents & POLLIN)
+ ret |= 1;
+ if (mode & AUMODE_PLAY)
+ if (pfd[0].revents & POLLOUT)
+ ret |= 2;
+ } else {
+ if (mode & AUMODE_RECORD)
+ ret |= 1;
+ if (mode & AUMODE_PLAY)
+ ret |= 2;
+ }
+
+ return ret;
+}
+
+
+/* return 0 on error, 1 if read, 2 if write, 3 if both read and write */
+int
+audio_do_frame(int audio_fd, size_t buffer_size, char *rbuffer, char *wbuffer,
+ u_int mode, int use_poll, int use_select)
+{
+size_t offset;
+size_t left;
+ssize_t retval;
+int ret;
+
+ ret = audio_wait_frame(audio_fd, mode, use_select, use_poll);
+ if (ret == 0)
+ return 0;
+
+ if (ret & 1) {
+ for (left = buffer_size, offset = 0; left > 0;) {
+ retval = read(audio_fd, rbuffer + offset, left);
+ if (retval == 0)
+ warnx("read audio device 0 bytes");
+ if (retval < 0) {
+ warn("read audio device");
+ return 0;
+ }
+ if (retval > left) {
+ warnx("read returns more than requested: "
+ "%ld > %ld", retval, left);
+ return 0;
+ }
+ offset += retval;
+ left -= retval;
+ }
+ }
+
+ if (ret & 2) {
+ for (left = buffer_size, offset = 0; left > 0;) {
+ retval = write(audio_fd, wbuffer + offset, left);
+ if (retval == 0)
+ warnx("write audio device 0 bytes");
+ if (retval < 0) {
+ warn("write audio device");
+ return 0;
+ }
+ if (retval > left) {
+ warnx("write returns more than requested: "
+ "%ld > %ld", retval, left);
+ return 0;
+ }
+ offset += retval;
+ left -= retval;
+ }
+ }
+
+ return ret;
+}
+
+
+int
+audio_do_test(int audio_fd, size_t buffer_size, char *input_file, char *output_file,
+ u_int mode, int use_poll, int use_select, int loops, int verbose)
+{
+FILE *fout;
+FILE *fin;
+char *rbuffer;
+char *wbuffer;
+int buffs_read, buffs_written;
+int i, ret;
+
+ fin = NULL;
+ fout = NULL;
+ rbuffer = NULL;
+ wbuffer = NULL;
+
+ if ((rbuffer = malloc(buffer_size)) == NULL)
+ err(1, "malloc %lu bytes", (unsigned long)buffer_size);
+
+ if ((wbuffer = malloc(buffer_size)) == NULL)
+ err(1, "malloc %lu bytes", (unsigned long)buffer_size);
+
+ if (output_file != NULL) {
+ if ((fout = fopen(output_file, "w")) == NULL)
+ err(1, "fopen %s", output_file);
+ }
+ if (input_file != NULL) {
+ if ((fin = fopen(input_file, "r")) == NULL)
+ err(1, "fopen %s", input_file);
+ }
+
+ buffs_read = 0;
+ buffs_written = 0;
+ if (input_file != NULL) {
+ if (fread(wbuffer, buffer_size, 1, fin) < 1) {
+ warnx("fread error: %s", input_file);
+ return 1;
+ }
+ }
+ for (i = 1; mode && i <= loops; i++) {
+ ret = audio_do_frame(audio_fd, buffer_size, rbuffer,
+ wbuffer, mode, use_poll, use_select);
+ if (ret == 0)
+ return 1;
+ if (ret & 1) {
+ buffs_read++;
+ if (verbose)
+ warnx("loop %03d: read frame: %03d", i, buffs_read);
+ if (fwrite(rbuffer, buffer_size, 1, fout) < 1) {
+ warnx("fwrite error: %s", output_file);
+ return 1;
+ }
+ }
+ if (ret & 2) {
+ buffs_written++;
+ if (verbose)
+ warnx("loop %03d: write frame: %03d", i, buffs_written);
+ if (fread(wbuffer, buffer_size, 1, fin) < 1) {
+ if (feof(fin)) {
+ if (verbose)
+ warnx("input EOF");
+ mode = mode & ~AUMODE_PLAY;
+ } else {
+ warnx("fread error: %s", input_file);
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (output_file != NULL)
+ if (fileno(fout) >= 0)
+ fclose(fout);
+ if (input_file != NULL)
+ if (fileno(fin) >= 0)
+ fclose(fin);
+
+ if (rbuffer != NULL)
+ free(rbuffer);
+ if (wbuffer != NULL)
+ free(wbuffer);
+
+ return 0;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+char *audio_device;
+char *output_file;
+char *input_file;
+int audio_fd;
+size_t buffer_size;
+
+audio_device_t audio_dev;
+audio_info_t audio_if;
+u_int sample_rate;
+u_int channels;
+u_int mode;
+u_int encoding;
+
+int flags;
+int use_duplex;
+int use_nonblock;
+int use_poll;
+int use_select;
+int verbose;
+
+int loops;
+
+const char *errstr;
+
+int ch;
+extern char *optarg;
+extern int optind;
+
+
+ audio_device = "/dev/audio";
+ input_file = NULL;
+ output_file = NULL;
+
+ audio_fd = -1;
+
+ buffer_size = 8192;
+ sample_rate = 48000;
+ channels = 2;
+
+ encoding = 0;
+
+ loops = 64;
+ use_nonblock = 0;
+ use_select = 0;
+ use_poll = 0;
+ use_duplex = 0;
+ verbose = 0;
+
+ while ((ch = getopt(argc, argv, "b:c:e:f:i:l:o:r:dpsv")) != -1) {
+ switch (ch) {
+ case 'b':
+ buffer_size = (size_t)strtonum(optarg, 32, 65536, &errstr);
+ if (errstr != NULL)
+ errx(1, "could not grok buffer_size: %s", errstr);
+ break;
+ case 'c':
+ channels = (u_int)strtonum(optarg, 1, 2, &errstr);
+ if (errstr != NULL)
+ errx(1, "could not grok channels: %s", errstr);
+ break;
+ case 'd':
+ use_duplex = 1;
+ break;
+ case 'e':
+ encoding = (u_int)strtonum(optarg, 0, 24, &errstr);
+ if (errstr != NULL)
+ errx(1, "could not grok encoding: %s", errstr);
+ break;
+ case 'f':
+ audio_device = optarg;
+ break;
+ case 'i':
+ input_file = optarg;
+ break;
+ case 'l':
+ loops = (int)strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "could not grok loops: %s", errstr);
+ break;
+ case 'o':
+ output_file = optarg;
+ break;
+ case 'p':
+ use_poll = 1;
+ use_nonblock = 1;
+ break;
+ case 'r':
+ sample_rate = (u_int)strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "could not grok sample_rate: %s", errstr);
+ break;
+ case 's':
+ use_select = 1;
+ use_nonblock = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ useage();
+ exit(1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (use_select && use_poll)
+ errx(1, "can't use select and poll at the same time");
+
+ if ((input_file == NULL) && (output_file == NULL))
+ errx(1, "no input or output file specified");
+
+ if ((input_file != NULL) && (output_file != NULL))
+ use_duplex = 1;
+
+ mode = 0;
+ flags = 0;
+
+ if (output_file != NULL) {
+ mode |= AUMODE_RECORD;
+ flags = O_RDONLY;
+ }
+
+ if (input_file != NULL) {
+ mode |= AUMODE_PLAY;
+ flags = O_WRONLY;
+ }
+
+ if (use_duplex)
+ flags = O_RDWR;
+
+ if (use_nonblock)
+ flags |= O_NONBLOCK;
+
+ if ((audio_fd = open(audio_device, flags)) < 0)
+ err(1, "open %s", audio_device);
+
+ if (audio_set_duplex(audio_fd, audio_device, use_duplex))
+ errx(1, "could not set duplex mode");
+
+ if (audio_set_info(audio_fd, mode, encoding, sample_rate, channels))
+ errx(1, "could not initialize audio device");
+
+ if (verbose) {
+ AUDIO_INITINFO(&audio_if);
+ if (ioctl(audio_fd, AUDIO_GETINFO, &audio_if) < 0)
+ err(1, "AUDIO_GETINFO");
+
+ if (ioctl(audio_fd, AUDIO_GETDEV, &audio_dev) < 0)
+ err(1, "AUDIO_GETDEV");
+
+ warnx("audio device: %s: %s ver %s, config: %s", audio_device,
+ audio_dev.name, audio_dev.version, audio_dev.config);
+ warnx("blocksize: %u", audio_if.blocksize);
+ warnx("lowat: %u", audio_if.lowat);
+ warnx("hiwat: %u", audio_if.hiwat);
+ warnx("play.buffer_size: %u", audio_if.play.buffer_size);
+ warnx("record.buffer_size: %u", audio_if.record.buffer_size);
+ if (output_file != NULL)
+ warnx("output file: %s", output_file);
+ if (input_file != NULL)
+ warnx("input file: %s", input_file);
+ warnx("flags: %d", flags);
+ warnx("mode: %u", mode);
+ warnx("encoding: %u", encoding);
+ warnx("sample_rate: %u", sample_rate);
+ warnx("channels: %u", channels);
+ warnx("use_select: %d", use_select);
+ warnx("use_poll: %d", use_poll);
+ warnx("use_duplex: %d", use_duplex);
+ warnx("buffer_size: %lu", (unsigned long)buffer_size);
+ }
+
+
+ if (audio_do_test(audio_fd, buffer_size, input_file, output_file,
+ mode, use_poll, use_select, loops, verbose))
+ exit(1);
+
+ if (verbose)
+ warnx("test completed");
+
+ if (audio_fd >= 0)
+ close(audio_fd);
+
+ exit(0);
+}