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