Annotation of src/usr.bin/aucat/abuf.c, Revision 1.7
1.7 ! ratchov 1: /* $OpenBSD: abuf.c,v 1.6 2008/08/14 10:02:10 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: /*
18: * Simple byte fifo. It has one reader and one writer. The abuf
19: * structure is used to interconnect audio processing units (aproc
20: * structures).
21: *
22: * The abuf data is split in two parts: (1) valid data available to the reader
23: * (2) space available to the writer, which is not necessarily unused. It works
24: * as follows: the write starts filling at offset (start + used), once the data
25: * is ready, the writer adds to used the count of bytes available.
1.7 ! ratchov 26: */
! 27: /*
! 28: * TODO
1.1 ratchov 29: *
1.7 ! ratchov 30: * use blocks instead of frames for WOK and ROK macros. If necessary
! 31: * (unlikely) define reader block size and writer blocks size to
! 32: * ease pipe/socket implementation
1.1 ratchov 33: */
34: #include <err.h>
35: #include <stdio.h>
36: #include <stdlib.h>
1.4 ratchov 37: #include <string.h>
1.7 ! ratchov 38: #include <stdarg.h>
1.1 ratchov 39:
40: #include "conf.h"
41: #include "aproc.h"
42: #include "abuf.h"
43:
1.7 ! ratchov 44: #ifdef DEBUG
! 45: void
! 46: abuf_dprn(int n, struct abuf *buf, char *fmt, ...)
! 47: {
! 48: va_list ap;
! 49:
! 50: if (debug_level < n)
! 51: return;
! 52: fprintf(stderr, "%s->%s: ",
! 53: buf->wproc ? buf->wproc->name : "none",
! 54: buf->rproc ? buf->rproc->name : "none");
! 55: va_start(ap, fmt);
! 56: vfprintf(stderr, fmt, ap);
! 57: }
! 58: #define ABUF_DPRN(n, buf, ...) abuf_dprn((n), (buf), __VA_ARGS__)
! 59: #define ABUF_DPR(buf, ...) abuf_dprn(1, (buf), __VA_ARGS__)
! 60: #else
! 61: #define ABUF_DPRN(n, buf, ...) do {} while (0)
! 62: #define ABUF_DPR(buf, ...) do {} while (0)
! 63: #endif
! 64:
1.1 ratchov 65: struct abuf *
66: abuf_new(unsigned nfr, unsigned bpf)
67: {
68: struct abuf *buf;
69: unsigned len;
70:
71: len = nfr * bpf;
72: buf = malloc(sizeof(struct abuf) + len);
73: if (buf == NULL) {
1.7 ! ratchov 74: fprintf(stderr, "abuf_new: out of mem: %u * %u\n", nfr, bpf);
! 75: abort();
1.1 ratchov 76: }
77: buf->bpf = bpf;
1.7 ! ratchov 78: buf->inuse = 0;
1.1 ratchov 79:
80: /*
81: * fill fifo pointers
82: */
83: buf->len = len;
84: buf->used = 0;
85: buf->start = 0;
1.5 ratchov 86: buf->abspos = 0;
1.4 ratchov 87: buf->silence = 0;
88: buf->drop = 0;
1.1 ratchov 89: buf->rproc = NULL;
90: buf->wproc = NULL;
1.7 ! ratchov 91: buf->duplex = NULL;
1.2 ratchov 92: buf->data = (unsigned char *)buf + sizeof(*buf);
1.1 ratchov 93: return buf;
94: }
95:
96: void
97: abuf_del(struct abuf *buf)
98: {
1.7 ! ratchov 99: if (buf->duplex)
! 100: buf->duplex->duplex = NULL;
! 101: #ifdef DEBUG
! 102: if (buf->rproc || buf->wproc || ABUF_ROK(buf)) {
! 103: ABUF_DPRN(0, buf, "abuf_del: used = %u\n", buf->used);
1.1 ratchov 104: abort();
105: }
1.7 ! ratchov 106: #endif
1.1 ratchov 107: free(buf);
108: }
109:
110: /*
111: * Get a pointer to the readable block at the given offset.
112: */
113: unsigned char *
114: abuf_rgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
115: {
116: unsigned count, start, used;
117:
118: start = buf->start + ofs;
119: used = buf->used - ofs;
1.6 ratchov 120: if (start >= buf->len)
121: start -= buf->len;
1.7 ! ratchov 122: #ifdef DEBUG
! 123: if (start >= buf->len || used > buf->used) {
! 124: ABUF_DPRN(0, buf, "abuf_rgetblk: "
! 125: "bad ofs: start = %u used = %u/%u, ofs = %u\n",
! 126: buf->start, buf->used, buf->len, ofs);
! 127: abort();
! 128: }
! 129: #endif
1.1 ratchov 130: count = buf->len - start;
131: if (count > used)
132: count = used;
133: *rsize = count;
134: return buf->data + start;
1.3 ratchov 135: }
136:
137: /*
138: * Discard the block at the start postion
139: */
140: void
141: abuf_rdiscard(struct abuf *buf, unsigned count)
142: {
1.7 ! ratchov 143: #ifdef DEBUG
! 144: if (count > buf->used) {
! 145: ABUF_DPRN(0, buf, "abuf_rdiscard: bad count %u\n", count);
! 146: abort();
! 147: }
! 148: #endif
1.3 ratchov 149: buf->used -= count;
150: buf->start += count;
151: if (buf->start >= buf->len)
152: buf->start -= buf->len;
1.5 ratchov 153: buf->abspos += count;
1.3 ratchov 154: }
155:
156: /*
157: * Commit the data written at the end postion
158: */
159: void
160: abuf_wcommit(struct abuf *buf, unsigned count)
161: {
1.7 ! ratchov 162: #ifdef DEBUG
! 163: if (count > (buf->len - buf->used)) {
! 164: ABUF_DPR(buf, "abuf_wcommit: bad count\n");
! 165: abort();
! 166: }
! 167: #endif
1.3 ratchov 168: buf->used += count;
1.1 ratchov 169: }
170:
171: /*
172: * Get a pointer to the writable block at offset ofs.
173: */
174: unsigned char *
175: abuf_wgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs)
176: {
177: unsigned end, avail, count;
178:
179:
180: end = buf->start + buf->used + ofs;
181: if (end >= buf->len)
182: end -= buf->len;
183: #ifdef DEBUG
184: if (end >= buf->len) {
1.7 ! ratchov 185: ABUF_DPR(buf, "abuf_wgetblk: %s -> %s: bad ofs, "
1.1 ratchov 186: "start = %u, used = %u, len = %u, ofs = %u\n",
187: buf->wproc->name, buf->rproc->name,
188: buf->start, buf->used, buf->len, ofs);
189: abort();
190: }
191: #endif
192: avail = buf->len - (buf->used + ofs);
193: count = buf->len - end;
194: if (count > avail)
195: count = avail;
196: *rsize = count;
197: return buf->data + end;
198: }
199:
200: /*
1.4 ratchov 201: * flush buffer either by dropping samples or by calling the aproc
202: * call-back to consume data. Return 0 if blocked, 1 otherwise
203: */
204: int
205: abuf_flush_do(struct abuf *buf)
206: {
207: struct aproc *p;
208: unsigned count;
209:
210: if (buf->drop > 0) {
211: count = buf->drop;
212: if (count > buf->used)
213: count = buf->used;
1.7 ! ratchov 214: if (count == 0) {
! 215: ABUF_DPR(buf, "abuf_flush_do: no data to drop\n");
! 216: return 0;
! 217: }
1.4 ratchov 218: abuf_rdiscard(buf, count);
219: buf->drop -= count;
1.7 ! ratchov 220: ABUF_DPR(buf, "abuf_flush_do: drop = %u\n", buf->drop);
! 221: p = buf->rproc;
1.4 ratchov 222: } else {
1.7 ! ratchov 223: ABUF_DPRN(4, buf, "abuf_flush_do: in ready\n");
1.4 ratchov 224: p = buf->rproc;
1.7 ! ratchov 225: if (!p || !p->ops->in(p, buf))
1.4 ratchov 226: return 0;
227: }
228: return 1;
229: }
230:
231: /*
232: * fill the buffer either by generating silence or by calling the aproc
233: * call-back to provide data. Return 0 if blocked, 1 otherwise
234: */
235: int
236: abuf_fill_do(struct abuf *buf)
237: {
238: struct aproc *p;
239: unsigned char *data;
240: unsigned count;
241:
242: if (buf->silence > 0) {
243: data = abuf_wgetblk(buf, &count, 0);
244: if (count >= buf->silence)
245: count = buf->silence;
1.7 ! ratchov 246: if (count == 0) {
! 247: ABUF_DPR(buf, "abuf_fill_do: no space for silence\n");
! 248: return 0;
! 249: }
1.4 ratchov 250: memset(data, 0, count);
251: abuf_wcommit(buf, count);
252: buf->silence -= count;
1.7 ! ratchov 253: ABUF_DPR(buf, "abuf_fill_do: silence = %u\n", buf->silence);
! 254: p = buf->wproc;
1.4 ratchov 255: } else {
1.7 ! ratchov 256: ABUF_DPRN(4, buf, "abuf_fill_do: out avail\n");
1.4 ratchov 257: p = buf->wproc;
1.7 ! ratchov 258: if (p == NULL || !p->ops->out(p, buf)) {
1.4 ratchov 259: return 0;
1.7 ! ratchov 260: }
! 261: }
! 262: return 1;
! 263: }
! 264:
! 265: /*
! 266: * Notify the reader that there will be no more input (producer
! 267: * disappeared) and destroy the buffer
! 268: */
! 269: void
! 270: abuf_eof_do(struct abuf *buf)
! 271: {
! 272: struct aproc *p;
! 273:
! 274: p = buf->rproc;
! 275: if (p) {
! 276: ABUF_DPRN(2, buf, "abuf_eof_do: signaling reader\n");
! 277: buf->rproc = NULL;
! 278: LIST_REMOVE(buf, ient);
! 279: buf->inuse++;
! 280: p->ops->eof(p, buf);
! 281: buf->inuse--;
! 282: } else
! 283: ABUF_DPR(buf, "abuf_eof_do: no reader, freeng buf\n");
! 284: abuf_del(buf);
! 285: }
! 286:
! 287: /*
! 288: * Notify the writer that the buffer has no more consumer,
! 289: * and destroy the buffer
! 290: */
! 291: void
! 292: abuf_hup_do(struct abuf *buf)
! 293: {
! 294: struct aproc *p;
! 295:
! 296: if (ABUF_ROK(buf)) {
! 297: ABUF_DPR(buf, "abuf_hup_do: lost %u bytes\n", buf->used);
! 298: buf->used = 0;
! 299: }
! 300: p = buf->wproc;
! 301: if (p != NULL) {
! 302: ABUF_DPRN(2, buf, "abuf_hup_do: signaling writer\n");
! 303: buf->wproc = NULL;
! 304: LIST_REMOVE(buf, oent);
! 305: buf->inuse++;
! 306: p->ops->hup(p, buf);
! 307: buf->inuse--;
! 308: } else
! 309: ABUF_DPR(buf, "abuf_hup_do: no writer, freeng buf\n");
! 310: abuf_del(buf);
! 311: }
! 312:
! 313: /*
! 314: * Notify the read end of the buffer that there is input available
! 315: * and that data can be processed again.
! 316: */
! 317: int
! 318: abuf_flush(struct abuf *buf)
! 319: {
! 320: if (buf->inuse) {
! 321: ABUF_DPRN(4, buf, "abuf_flush: blocked\n");
! 322: } else {
! 323: buf->inuse++;
! 324: for (;;) {
! 325: if (!abuf_flush_do(buf))
! 326: break;
! 327: }
! 328: buf->inuse--;
! 329: if (ABUF_HUP(buf)) {
! 330: abuf_hup_do(buf);
! 331: return 0;
! 332: }
1.4 ratchov 333: }
334: return 1;
335: }
336:
337: /*
1.1 ratchov 338: * Notify the write end of the buffer that there is room and data can be
339: * written again. This routine can only be called from the out()
340: * call-back of the reader.
341: *
1.7 ! ratchov 342: * Return 1 if the buffer was filled, and 0 if eof condition occured. The
! 343: * reader must detach the buffer on EOF condition, since it's aproc->eof()
! 344: * call-back will never be called.
1.1 ratchov 345: */
1.7 ! ratchov 346: int
1.1 ratchov 347: abuf_fill(struct abuf *buf)
348: {
1.7 ! ratchov 349: if (buf->inuse) {
! 350: ABUF_DPRN(4, buf, "abuf_fill: blocked\n");
! 351: } else {
! 352: buf->inuse++;
! 353: for (;;) {
! 354: if (!abuf_fill_do(buf))
! 355: break;
! 356: }
! 357: buf->inuse--;
! 358: if (ABUF_EOF(buf)) {
! 359: abuf_eof_do(buf);
! 360: return 0;
! 361: }
1.1 ratchov 362: }
1.7 ! ratchov 363: return 1;
1.1 ratchov 364: }
365:
366: /*
367: * Run a read/write loop on the buffer until either the reader or the
368: * writer blocks, or until the buffer reaches eofs. We can not get hup hear,
369: * since hup() is only called from terminal nodes, from the main loop.
370: *
371: * NOTE: The buffer may disappear (ie. be free()ed) if eof is reached, so
372: * do not keep references to the buffer or to its writer or reader.
373: */
374: void
375: abuf_run(struct abuf *buf)
376: {
377: int canfill = 1, canflush = 1;
378:
1.7 ! ratchov 379: if (buf->inuse) {
! 380: ABUF_DPRN(4, buf, "abuf_run: blocked\n");
! 381: return;
! 382: }
! 383: buf->inuse++;
1.1 ratchov 384: for (;;) {
1.7 ! ratchov 385: if (canfill) {
! 386: if (!abuf_fill_do(buf))
! 387: canfill = 0;
! 388: else
! 389: canflush = 1;
! 390: } else if (canflush) {
! 391: if (!abuf_flush_do(buf))
! 392: canflush = 0;
! 393: else
! 394: canfill = 1;
1.1 ratchov 395: } else
1.7 ! ratchov 396: break;
! 397: }
! 398: buf->inuse--;
! 399: if (ABUF_EOF(buf)) {
! 400: abuf_eof_do(buf);
! 401: return;
! 402: }
! 403: if (ABUF_HUP(buf)) {
! 404: abuf_hup_do(buf);
! 405: return;
1.1 ratchov 406: }
407: }
408:
409: /*
410: * Notify the reader that there will be no more input (producer
411: * disappeared). The buffer is flushed and eof() is called only if all
412: * data is flushed.
413: */
414: void
415: abuf_eof(struct abuf *buf)
416: {
417: #ifdef DEBUG
418: if (buf->wproc == NULL) {
1.7 ! ratchov 419: ABUF_DPR(buf, "abuf_eof: no writer\n");
1.1 ratchov 420: abort();
421: }
422: #endif
1.7 ! ratchov 423: ABUF_DPRN(2, buf, "abuf_eof: requested\n");
! 424: LIST_REMOVE(buf, oent);
1.1 ratchov 425: buf->wproc = NULL;
426: if (buf->rproc != NULL) {
1.7 ! ratchov 427: if (!abuf_flush(buf))
! 428: return;
1.1 ratchov 429: if (ABUF_ROK(buf)) {
430: /*
431: * Could not flush everything, the reader will
432: * have a chance to delete the abuf later.
433: */
1.7 ! ratchov 434: ABUF_DPRN(2, buf, "abuf_eof: will drain later\n");
1.1 ratchov 435: return;
436: }
437: }
1.7 ! ratchov 438: if (buf->inuse) {
! 439: ABUF_DPRN(2, buf, "abuf_eof: signal blocked\n");
! 440: return;
! 441: }
! 442: abuf_eof_do(buf);
1.1 ratchov 443: }
444:
445: /*
446: * Notify the writer that the buffer has no more consumer,
447: * and that no more data will accepted.
448: */
449: void
450: abuf_hup(struct abuf *buf)
451: {
452: #ifdef DEBUG
453: if (buf->rproc == NULL) {
1.7 ! ratchov 454: ABUF_DPR(buf, "abuf_hup: no reader\n");
1.1 ratchov 455: abort();
456: }
457: #endif
1.7 ! ratchov 458: ABUF_DPRN(2, buf, "abuf_hup: initiated\n");
! 459:
1.1 ratchov 460: buf->rproc = NULL;
1.7 ! ratchov 461: LIST_REMOVE(buf, ient);
1.1 ratchov 462: if (buf->wproc != NULL) {
1.7 ! ratchov 463: if (buf->inuse) {
! 464: ABUF_DPRN(2, buf, "abuf_hup: signal blocked\n");
! 465: return;
1.1 ratchov 466: }
467: }
1.7 ! ratchov 468: abuf_hup_do(buf);
! 469: }
! 470:
! 471: /*
! 472: * Notify the reader of the change of its real-time position
! 473: */
! 474: void
! 475: abuf_ipos(struct abuf *buf, int delta)
! 476: {
! 477: struct aproc *p = buf->rproc;
! 478:
! 479: if (p && p->ops->ipos) {
! 480: buf->inuse++;
! 481: p->ops->ipos(p, buf, delta);
! 482: buf->inuse--;
! 483: }
! 484: if (ABUF_HUP(buf))
! 485: abuf_hup_do(buf);
! 486: }
! 487:
! 488: /*
! 489: * Notify the writer of the change of its real-time position
! 490: */
! 491: void
! 492: abuf_opos(struct abuf *buf, int delta)
! 493: {
! 494: struct aproc *p = buf->wproc;
! 495:
! 496:
! 497: if (p && p->ops->opos) {
! 498: buf->inuse++;
! 499: p->ops->opos(p, buf, delta);
! 500: buf->inuse--;
! 501: }
! 502: if (ABUF_HUP(buf))
! 503: abuf_hup_do(buf);
1.1 ratchov 504: }