From 143dfa505dfdb77cc304a6e71b00b01e56063ee4 Mon Sep 17 00:00:00 2001 From: Jacob Meuser Date: Fri, 6 Jul 2007 00:43:39 +0000 Subject: regression tests for various audio read(2) and write(2) scenarios --- regress/sys/dev/audio_rw/Makefile | 99 +++++ regress/sys/dev/audio_rw/audiotest_rw.1 | 250 +++++++++++++ regress/sys/dev/audio_rw/audiotest_rw.c | 615 ++++++++++++++++++++++++++++++++ 3 files changed, 964 insertions(+) create mode 100644 regress/sys/dev/audio_rw/Makefile create mode 100644 regress/sys/dev/audio_rw/audiotest_rw.1 create mode 100644 regress/sys/dev/audio_rw/audiotest_rw.c 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 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 +.\" +.\" 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3