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