summaryrefslogtreecommitdiff
path: root/regress/lib/libsa/safd/safd.c
diff options
context:
space:
mode:
Diffstat (limited to 'regress/lib/libsa/safd/safd.c')
-rw-r--r--regress/lib/libsa/safd/safd.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/regress/lib/libsa/safd/safd.c b/regress/lib/libsa/safd/safd.c
new file mode 100644
index 00000000000..8a6e37d27b0
--- /dev/null
+++ b/regress/lib/libsa/safd/safd.c
@@ -0,0 +1,363 @@
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libsa.h"
+
+struct buf { /* simple circular fifo */
+ unsigned start; /* first used byte */
+ unsigned used; /* number of used bytes */
+#define BUF_LEN (240 * 0x1000) /* i/o buffer size */
+ unsigned char data[BUF_LEN];
+};
+
+char *xstr[] = SA_XSTRINGS;
+struct sa_par par;
+struct buf playbuf, recbuf;
+
+long long pos = 0;
+int plat = 0, rlat = 0;
+
+void
+cb(void *addr, int delta)
+{
+ pos += delta;
+ fprintf(stderr, "cb: delta = %+7d, pos = %+7lld, "
+ "plat = %+7d, rlat = %+7d\n",
+ delta, pos, plat, rlat);
+ plat -= delta;
+ rlat += delta;
+}
+
+/*
+ * read buffer contents from a file without blocking
+ */
+void
+buf_read(struct buf *buf, int fd) {
+ unsigned count, end, avail;
+ int n;
+
+ for (;;) {
+ avail = BUF_LEN - buf->used;
+ if (avail == 0)
+ break;
+ end = buf->start + buf->used;
+ if (end >= BUF_LEN)
+ end -= BUF_LEN;
+ count = BUF_LEN - end;
+ if (count > avail)
+ count = avail;
+ n = read(fd, buf->data + end, count);
+ if (n < 0) {
+ perror("buf_read: read");
+ exit(1);
+ }
+ if (n == 0) {
+ bzero(buf->data + end, count);
+ n = count;
+ }
+ buf->used += n;
+ }
+}
+
+/*
+ * write buffer contents to file, without blocking
+ */
+void
+buf_write(struct buf *buf, int fd)
+{
+ unsigned count;
+ int n;
+
+ while (buf->used) {
+ count = BUF_LEN - buf->start;
+ if (count > buf->used)
+ count = buf->used;
+ n = write(fd, buf->data + buf->start, count);
+ if (n < 0) {
+ perror("buf_write: write");
+ exit(1);
+ }
+ buf->used -= n;
+ buf->start += n;
+ if (buf->start >= BUF_LEN)
+ buf->start -= BUF_LEN;
+ }
+}
+
+/*
+ * read buffer contents from a file without blocking
+ */
+unsigned
+buf_rec(struct buf *buf, struct sa_hdl *hdl)
+{
+ unsigned count, end, avail, done = 0;
+ int bpf = par.rchan * par.bps;
+ int n;
+
+ for (;;) {
+ avail = BUF_LEN - buf->used;
+ if (avail == 0)
+ break;
+ end = buf->start + buf->used;
+ if (end >= BUF_LEN)
+ end -= BUF_LEN;
+ count = BUF_LEN - end;
+ if (count > avail)
+ count = avail;
+ n = sa_read(hdl, buf->data + end, count);
+ if (n == 0) {
+ if (sa_eof(hdl)) {
+ fprintf(stderr, "sa_read() failed\n");
+ exit(1);
+ }
+ break;
+ }
+ if (n % bpf) {
+ fprintf(stderr, "rec: bad align: %u bytes\n", n);
+ exit(1);
+ }
+ rlat -= n / bpf;
+ buf->used += n;
+ done += n;
+ }
+ return done;
+}
+
+/*
+ * write buffer contents to file, without blocking
+ */
+unsigned
+buf_play(struct buf *buf, struct sa_hdl *hdl)
+{
+ unsigned count, done = 0;
+ int bpf = par.pchan * par.bps;
+ int n;
+
+ while (buf->used) {
+ count = BUF_LEN - buf->start;
+ if (count > buf->used)
+ count = buf->used;
+ /* try to confuse the server */
+ //count = 1 + (rand() % count);
+ n = sa_write(hdl, buf->data + buf->start, count);
+ if (n == 0) {
+ if (sa_eof(hdl)) {
+ fprintf(stderr, "sa_write() failed\n");
+ exit(1);
+ }
+ break;
+ }
+ if (n % bpf) {
+ fprintf(stderr, "play: bad align: %u bytes\n", n);
+ exit(1);
+ }
+ plat += n / bpf;
+ //write(STDOUT_FILENO, buf->data + buf->start, n);
+ buf->used -= n;
+ buf->start += n;
+ if (buf->start >= BUF_LEN)
+ buf->start -= BUF_LEN;
+ done += n;
+ }
+ return done;
+}
+
+void
+usage(void) {
+ fprintf(stderr,
+ "usage: safd [-v] [-r rate] [-c ichan] [-C ochan] [-e enc] "
+ "[-i file] [-o file]\n");
+}
+
+int
+main(int argc, char **argv) {
+ int ch, recfd, playfd, events, revents;
+ char *recpath, *playpath;
+ struct sa_hdl *hdl;
+ struct pollfd pfd;
+ struct timeval tv, otv, ntv;
+ unsigned mode, done;
+
+ recpath = NULL;
+ playpath = NULL;
+
+ /*
+ * defaults parameters
+ */
+ sa_initpar(&par);
+ par.sig = 1;
+ par.bits = 16;
+ par.pchan = par.rchan = 2;
+ par.rate = 44100;
+
+ while ((ch = getopt(argc, argv, "r:c:C:e:i:o:b:x:")) != -1) {
+ switch(ch) {
+ case 'r':
+ if (sscanf(optarg, "%u", &par.rate) != 1) {
+ fprintf(stderr, "%s: bad rate\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'c':
+ if (sscanf(optarg, "%u", &par.pchan) != 1) {
+ fprintf(stderr, "%s: bad play chans\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'C':
+ if (sscanf(optarg, "%u", &par.rchan) != 1) {
+ fprintf(stderr, "%s: bad rec chans\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'e':
+ if (!sa_strtoenc(&par, optarg)) {
+ fprintf(stderr, "%s: unknown encoding\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'o':
+ recpath = optarg;
+ break;
+ case 'i':
+ playpath = optarg;
+ break;
+ case 'b':
+ if (sscanf(optarg, "%u", &par.bufsz) != 1) {
+ fprintf(stderr, "%s: bad buf size\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'x':
+ for (par.xrun = 0;; par.xrun++) {
+ if (par.xrun == sizeof(xstr) / sizeof(char *)) {
+ fprintf(stderr,
+ "%s: bad xrun mode\n", optarg);
+ exit(1);
+ }
+ if (strcmp(xstr[par.xrun], optarg) == 0)
+ break;
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+ mode = 0;
+ if (recpath)
+ mode |= SA_REC;
+ if (playpath)
+ mode |= SA_PLAY;
+ if (mode == 0) {
+ fprintf(stderr, "-i or -o option required\n");
+ exit(0);
+ }
+ hdl = sa_open(NULL, mode, 1);
+ if (hdl == NULL) {
+ fprintf(stderr, "sa_open() failed\n");
+ exit(1);
+ }
+ sa_onmove(hdl, cb, NULL);
+ if (!sa_setpar(hdl, &par)) {
+ fprintf(stderr, "sa_setpar() failed\n");
+ exit(1);
+ }
+ if (!sa_getpar(hdl, &par)) {
+ fprintf(stderr, "sa_setpar() failed\n");
+ exit(1);
+ }
+ fprintf(stderr, "using %u%%%u frame buffer\n", par.bufsz, par.round);
+ if (!sa_start(hdl)) {
+ fprintf(stderr, "sa_start() failed\n");
+ exit(1);
+ }
+
+ events = 0;
+ if (recpath > 0) {
+ recfd = open(recpath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+ if (recfd < 0) {
+ perror(recpath);
+ exit(1);
+ }
+ events |= POLLIN;
+ }
+ if (playpath > 0) {
+ playfd = open(playpath, O_RDONLY, 0);
+ if (playfd < 0) {
+ perror(playpath);
+ exit(1);
+ }
+ events |= POLLOUT;
+ buf_read(&playbuf, playfd);
+ buf_play(&playbuf, hdl);
+ }
+ gettimeofday(&otv, NULL);
+ for (;;) {
+ gettimeofday(&ntv, NULL);
+ timersub(&ntv, &otv, &tv);
+#if 0 /* trigger underrun */
+ if (playpath && (tv.tv_sec % 10) < 7) {
+ events |= POLLOUT;
+ } else
+ events &= ~POLLOUT;
+#endif
+#if 0 /* trigger overrun */
+ if (recpath && (tv.tv_sec % 10) < 7) {
+ events |= POLLIN;
+ } else
+ events &= ~POLLIN;
+#endif
+ //fprintf(stderr, "%ld.%06ld: polling for %d\n",
+ // tv.tv_sec, tv.tv_usec, events);
+ sa_pollfd(hdl, &pfd, events);
+ while (poll(&pfd, 1, 1000) < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("poll");
+ exit(1);
+ }
+ revents = sa_revents(hdl, &pfd);
+ gettimeofday(&ntv, NULL);
+ timersub(&ntv, &otv, &tv);
+ //fprintf(stderr, "%ld.%06ld: got %d\n",
+ // tv.tv_sec, tv.tv_usec, revents);
+ if (revents & POLLHUP) {
+ fprintf(stderr, "device hangup\n");
+ exit(0);
+ }
+ if (revents & POLLIN) {
+ done = buf_rec(&recbuf, hdl);
+ buf_write(&recbuf, recfd);
+ //fprintf(stderr, "%ld.%06ld: recored %u\n",
+ // tv.tv_sec, tv.tv_usec, done);
+ }
+ if (revents & POLLOUT) {
+ done = buf_play(&playbuf, hdl);
+ buf_read(&playbuf, playfd);
+ }
+#if 0
+ if (pos / par.rate > 2) {
+ if (!sa_stop(hdl)) {
+ fprintf(stderr, "sa_stop failed\n");
+ exit(1);
+ }
+ pos = plat = rlat = 0;
+ fprintf(stderr, "pausing...\n");
+ sleep(1);
+ if (!sa_start(hdl)) {
+ fprintf(stderr, "sa_start failed\n");
+ exit(1);
+ }
+ }
+#endif
+ }
+ sa_close(hdl);
+ return 0;
+}