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