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