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