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