1f153e440Sratchov #include <sys/time.h>
2f153e440Sratchov #include <errno.h>
3f153e440Sratchov #include <fcntl.h>
4f153e440Sratchov #include <poll.h>
5f153e440Sratchov #include <stdio.h>
6f153e440Sratchov #include <string.h>
7f153e440Sratchov #include <stdlib.h>
8f153e440Sratchov #include <unistd.h>
9f153e440Sratchov #include <sndio.h>
1078ccc913Sratchov #include "tools.h"
11f153e440Sratchov
12f153e440Sratchov struct buf { /* simple circular fifo */
13f153e440Sratchov unsigned start; /* first used byte */
14f153e440Sratchov unsigned used; /* number of used bytes */
15f153e440Sratchov #define BUF_LEN (240 * 0x1000) /* i/o buffer size */
16f153e440Sratchov unsigned char data[BUF_LEN];
17f153e440Sratchov };
18f153e440Sratchov
1992516d8eSratchov void cb(void *, int);
2092516d8eSratchov void buf_read(struct buf *, int);
2192516d8eSratchov void buf_write(struct buf *, int);
2292516d8eSratchov unsigned buf_rec(struct buf *, struct sio_hdl *);
2392516d8eSratchov unsigned buf_play(struct buf *, struct sio_hdl *);
2492516d8eSratchov void usage(void);
2592516d8eSratchov
26f153e440Sratchov char *xstr[] = SIO_XSTRINGS;
27f153e440Sratchov struct sio_par par;
28f153e440Sratchov struct buf playbuf, recbuf;
29f153e440Sratchov
30f153e440Sratchov long long pos = 0;
31f153e440Sratchov int plat = 0, rlat = 0;
32f153e440Sratchov
33f153e440Sratchov void
cb(void * addr,int delta)34f153e440Sratchov cb(void *addr, int delta)
35f153e440Sratchov {
36f153e440Sratchov pos += delta;
37f153e440Sratchov fprintf(stderr, "cb: delta = %+7d, pos = %+7lld, "
38f153e440Sratchov "plat = %+7d, rlat = %+7d\n",
39f153e440Sratchov delta, pos, plat, rlat);
40f153e440Sratchov plat -= delta;
41f153e440Sratchov rlat += delta;
42f153e440Sratchov }
43f153e440Sratchov
44f153e440Sratchov /*
45f153e440Sratchov * read buffer contents from a file without blocking
46f153e440Sratchov */
47f153e440Sratchov void
buf_read(struct buf * buf,int fd)4892516d8eSratchov buf_read(struct buf *buf, int fd)
4992516d8eSratchov {
50f153e440Sratchov unsigned count, end, avail;
51f153e440Sratchov int n;
52f153e440Sratchov
53f153e440Sratchov for (;;) {
54f153e440Sratchov avail = BUF_LEN - buf->used;
55f153e440Sratchov if (avail == 0)
56f153e440Sratchov break;
57f153e440Sratchov end = buf->start + buf->used;
58f153e440Sratchov if (end >= BUF_LEN)
59f153e440Sratchov end -= BUF_LEN;
60f153e440Sratchov count = BUF_LEN - end;
61f153e440Sratchov if (count > avail)
62f153e440Sratchov count = avail;
63f153e440Sratchov n = read(fd, buf->data + end, count);
64f153e440Sratchov if (n < 0) {
65f153e440Sratchov perror("buf_read: read");
66f153e440Sratchov exit(1);
67f153e440Sratchov }
68f153e440Sratchov if (n == 0) {
69f153e440Sratchov bzero(buf->data + end, count);
70f153e440Sratchov n = count;
71f153e440Sratchov }
72f153e440Sratchov buf->used += n;
73f153e440Sratchov }
74f153e440Sratchov }
75f153e440Sratchov
76f153e440Sratchov /*
77f153e440Sratchov * write buffer contents to file, without blocking
78f153e440Sratchov */
79f153e440Sratchov void
buf_write(struct buf * buf,int fd)80f153e440Sratchov buf_write(struct buf *buf, int fd)
81f153e440Sratchov {
82f153e440Sratchov unsigned count;
83f153e440Sratchov int n;
84f153e440Sratchov
85f153e440Sratchov while (buf->used) {
86f153e440Sratchov count = BUF_LEN - buf->start;
87f153e440Sratchov if (count > buf->used)
88f153e440Sratchov count = buf->used;
89f153e440Sratchov n = write(fd, buf->data + buf->start, count);
90f153e440Sratchov if (n < 0) {
91f153e440Sratchov perror("buf_write: write");
92f153e440Sratchov exit(1);
93f153e440Sratchov }
94f153e440Sratchov buf->used -= n;
95f153e440Sratchov buf->start += n;
96f153e440Sratchov if (buf->start >= BUF_LEN)
97f153e440Sratchov buf->start -= BUF_LEN;
98f153e440Sratchov }
99f153e440Sratchov }
100f153e440Sratchov
101f153e440Sratchov /*
102f153e440Sratchov * read buffer contents from a file without blocking
103f153e440Sratchov */
104f153e440Sratchov unsigned
buf_rec(struct buf * buf,struct sio_hdl * hdl)105f153e440Sratchov buf_rec(struct buf *buf, struct sio_hdl *hdl)
106f153e440Sratchov {
107f153e440Sratchov unsigned count, end, avail, done = 0;
108f153e440Sratchov int bpf = par.rchan * par.bps;
109f153e440Sratchov int n;
110f153e440Sratchov
111f153e440Sratchov for (;;) {
112f153e440Sratchov avail = BUF_LEN - buf->used;
113f153e440Sratchov if (avail == 0)
114f153e440Sratchov break;
115f153e440Sratchov end = buf->start + buf->used;
116f153e440Sratchov if (end >= BUF_LEN)
117f153e440Sratchov end -= BUF_LEN;
118f153e440Sratchov count = BUF_LEN - end;
119f153e440Sratchov if (count > avail)
120f153e440Sratchov count = avail;
121f153e440Sratchov n = sio_read(hdl, buf->data + end, count);
122f153e440Sratchov if (n == 0) {
123f153e440Sratchov if (sio_eof(hdl)) {
124f153e440Sratchov fprintf(stderr, "sio_read() failed\n");
125f153e440Sratchov exit(1);
126f153e440Sratchov }
127f153e440Sratchov break;
128f153e440Sratchov }
129f153e440Sratchov if (n % bpf) {
130f153e440Sratchov fprintf(stderr, "rec: bad align: %u bytes\n", n);
131f153e440Sratchov exit(1);
132f153e440Sratchov }
133f153e440Sratchov rlat -= n / bpf;
134f153e440Sratchov buf->used += n;
135f153e440Sratchov done += n;
136f153e440Sratchov }
137f153e440Sratchov return done;
138f153e440Sratchov }
139f153e440Sratchov
140f153e440Sratchov /*
141f153e440Sratchov * write buffer contents to file, without blocking
142f153e440Sratchov */
143f153e440Sratchov unsigned
buf_play(struct buf * buf,struct sio_hdl * hdl)144f153e440Sratchov buf_play(struct buf *buf, struct sio_hdl *hdl)
145f153e440Sratchov {
146f153e440Sratchov unsigned count, done = 0;
147f153e440Sratchov int bpf = par.pchan * par.bps;
148f153e440Sratchov int n;
149f153e440Sratchov
150f153e440Sratchov while (buf->used) {
151f153e440Sratchov count = BUF_LEN - buf->start;
152f153e440Sratchov if (count > buf->used)
153f153e440Sratchov count = buf->used;
154f153e440Sratchov /* try to confuse the server */
155f153e440Sratchov //count = 1 + (rand() % count);
156f153e440Sratchov n = sio_write(hdl, buf->data + buf->start, count);
157f153e440Sratchov if (n == 0) {
158f153e440Sratchov if (sio_eof(hdl)) {
159f153e440Sratchov fprintf(stderr, "sio_write() failed\n");
160f153e440Sratchov exit(1);
161f153e440Sratchov }
162f153e440Sratchov break;
163f153e440Sratchov }
164f153e440Sratchov if (n % bpf) {
165f153e440Sratchov fprintf(stderr, "play: bad align: %u bytes\n", n);
166f153e440Sratchov exit(1);
167f153e440Sratchov }
168f153e440Sratchov plat += n / bpf;
169f153e440Sratchov //write(STDOUT_FILENO, buf->data + buf->start, n);
170f153e440Sratchov buf->used -= n;
171f153e440Sratchov buf->start += n;
172f153e440Sratchov if (buf->start >= BUF_LEN)
173f153e440Sratchov buf->start -= BUF_LEN;
174f153e440Sratchov done += n;
175f153e440Sratchov }
176f153e440Sratchov return done;
177f153e440Sratchov }
178f153e440Sratchov
179f153e440Sratchov void
usage(void)18092516d8eSratchov usage(void)
18192516d8eSratchov {
182f153e440Sratchov fprintf(stderr,
183f153e440Sratchov "usage: fd [-v] [-r rate] [-c ichan] [-C ochan] [-e enc] "
184f153e440Sratchov "[-i file] [-o file]\n");
185f153e440Sratchov }
186f153e440Sratchov
187f153e440Sratchov int
main(int argc,char ** argv)18892516d8eSratchov main(int argc, char **argv)
18992516d8eSratchov {
19092516d8eSratchov int ch, recfd, playfd, nfds, events, revents;
191f153e440Sratchov char *recpath, *playpath;
192f153e440Sratchov struct sio_hdl *hdl;
19392516d8eSratchov #define NFDS 16
19492516d8eSratchov struct pollfd pfd[NFDS];
19592516d8eSratchov unsigned mode;
196f153e440Sratchov
19792516d8eSratchov recfd = -1;
198f153e440Sratchov recpath = NULL;
19992516d8eSratchov playfd = -1;
200f153e440Sratchov playpath = NULL;
201f153e440Sratchov
202f153e440Sratchov /*
203f153e440Sratchov * defaults parameters
204f153e440Sratchov */
205f153e440Sratchov sio_initpar(&par);
206f153e440Sratchov par.sig = 1;
207f153e440Sratchov par.bits = 16;
208f153e440Sratchov par.pchan = par.rchan = 2;
209f153e440Sratchov par.rate = 44100;
210f153e440Sratchov
211f153e440Sratchov while ((ch = getopt(argc, argv, "r:c:C:e:i:o:b:x:")) != -1) {
212f153e440Sratchov switch(ch) {
213f153e440Sratchov case 'r':
214f153e440Sratchov if (sscanf(optarg, "%u", &par.rate) != 1) {
215f153e440Sratchov fprintf(stderr, "%s: bad rate\n", optarg);
216f153e440Sratchov exit(1);
217f153e440Sratchov }
218f153e440Sratchov break;
219f153e440Sratchov case 'c':
220f153e440Sratchov if (sscanf(optarg, "%u", &par.pchan) != 1) {
221f153e440Sratchov fprintf(stderr, "%s: bad play chans\n", optarg);
222f153e440Sratchov exit(1);
223f153e440Sratchov }
224f153e440Sratchov break;
225f153e440Sratchov case 'C':
226f153e440Sratchov if (sscanf(optarg, "%u", &par.rchan) != 1) {
227f153e440Sratchov fprintf(stderr, "%s: bad rec chans\n", optarg);
228f153e440Sratchov exit(1);
229f153e440Sratchov }
230f153e440Sratchov break;
231f153e440Sratchov case 'e':
232f153e440Sratchov if (!sio_strtoenc(&par, optarg)) {
233f153e440Sratchov fprintf(stderr, "%s: unknown encoding\n", optarg);
234f153e440Sratchov exit(1);
235f153e440Sratchov }
236f153e440Sratchov break;
237f153e440Sratchov case 'o':
238f153e440Sratchov recpath = optarg;
239f153e440Sratchov break;
240f153e440Sratchov case 'i':
241f153e440Sratchov playpath = optarg;
242f153e440Sratchov break;
243f153e440Sratchov case 'b':
244dd80057dSratchov if (sscanf(optarg, "%u", &par.appbufsz) != 1) {
245f153e440Sratchov fprintf(stderr, "%s: bad buf size\n", optarg);
246f153e440Sratchov exit(1);
247f153e440Sratchov }
248f153e440Sratchov break;
249f153e440Sratchov case 'x':
250f153e440Sratchov for (par.xrun = 0;; par.xrun++) {
251f153e440Sratchov if (par.xrun == sizeof(xstr) / sizeof(char *)) {
252f153e440Sratchov fprintf(stderr,
253f153e440Sratchov "%s: bad xrun mode\n", optarg);
254f153e440Sratchov exit(1);
255f153e440Sratchov }
256f153e440Sratchov if (strcmp(xstr[par.xrun], optarg) == 0)
257f153e440Sratchov break;
258f153e440Sratchov }
259f153e440Sratchov break;
260f153e440Sratchov default:
261f153e440Sratchov usage();
262f153e440Sratchov exit(1);
263f153e440Sratchov break;
264f153e440Sratchov }
265f153e440Sratchov }
266f153e440Sratchov mode = 0;
267f153e440Sratchov if (recpath)
268f153e440Sratchov mode |= SIO_REC;
269f153e440Sratchov if (playpath)
270f153e440Sratchov mode |= SIO_PLAY;
271f153e440Sratchov if (mode == 0) {
272f153e440Sratchov fprintf(stderr, "-i or -o option required\n");
273f153e440Sratchov exit(0);
274f153e440Sratchov }
27592516d8eSratchov hdl = sio_open(SIO_DEVANY, mode, 1);
276f153e440Sratchov if (hdl == NULL) {
277f153e440Sratchov fprintf(stderr, "sio_open() failed\n");
278f153e440Sratchov exit(1);
279f153e440Sratchov }
28092516d8eSratchov if (sio_nfds(hdl) > NFDS) {
28192516d8eSratchov fprintf(stderr, "too many descriptors to poll\n");
28292516d8eSratchov exit(1);
28392516d8eSratchov }
284f153e440Sratchov sio_onmove(hdl, cb, NULL);
285f153e440Sratchov if (!sio_setpar(hdl, &par)) {
286f153e440Sratchov fprintf(stderr, "sio_setpar() failed\n");
287f153e440Sratchov exit(1);
288f153e440Sratchov }
289f153e440Sratchov if (!sio_getpar(hdl, &par)) {
290f153e440Sratchov fprintf(stderr, "sio_setpar() failed\n");
291f153e440Sratchov exit(1);
292f153e440Sratchov }
293f153e440Sratchov fprintf(stderr, "using %u%%%u frame buffer\n", par.bufsz, par.round);
294f153e440Sratchov if (!sio_start(hdl)) {
295f153e440Sratchov fprintf(stderr, "sio_start() failed\n");
296f153e440Sratchov exit(1);
297f153e440Sratchov }
298f153e440Sratchov
299f153e440Sratchov events = 0;
30092516d8eSratchov if (recpath) {
301f153e440Sratchov recfd = open(recpath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
302f153e440Sratchov if (recfd < 0) {
303f153e440Sratchov perror(recpath);
304f153e440Sratchov exit(1);
305f153e440Sratchov }
306f153e440Sratchov events |= POLLIN;
307f153e440Sratchov }
30892516d8eSratchov if (playpath) {
309*b7041c07Sderaadt playfd = open(playpath, O_RDONLY);
310f153e440Sratchov if (playfd < 0) {
311f153e440Sratchov perror(playpath);
312f153e440Sratchov exit(1);
313f153e440Sratchov }
314f153e440Sratchov events |= POLLOUT;
315f153e440Sratchov buf_read(&playbuf, playfd);
316f153e440Sratchov buf_play(&playbuf, hdl);
317f153e440Sratchov }
318f153e440Sratchov for (;;) {
31992516d8eSratchov nfds = sio_pollfd(hdl, pfd, events);
32092516d8eSratchov while (poll(pfd, nfds, 1000) < 0) {
321f153e440Sratchov if (errno == EINTR)
322f153e440Sratchov continue;
323f153e440Sratchov perror("poll");
324f153e440Sratchov exit(1);
325f153e440Sratchov }
32692516d8eSratchov revents = sio_revents(hdl, pfd);
327f153e440Sratchov if (revents & POLLHUP) {
328f153e440Sratchov fprintf(stderr, "device hangup\n");
329f153e440Sratchov exit(0);
330f153e440Sratchov }
331f153e440Sratchov if (revents & POLLIN) {
33292516d8eSratchov buf_rec(&recbuf, hdl);
333f153e440Sratchov buf_write(&recbuf, recfd);
334f153e440Sratchov }
335f153e440Sratchov if (revents & POLLOUT) {
33692516d8eSratchov buf_play(&playbuf, hdl);
337f153e440Sratchov buf_read(&playbuf, playfd);
338f153e440Sratchov }
339f153e440Sratchov }
340f153e440Sratchov sio_close(hdl);
341f153e440Sratchov return 0;
342f153e440Sratchov }
343