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