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