version 1.25, 2013/11/18 17:37:45 |
version 1.26, 2015/01/21 08:43:55 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> |
* Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
*/ |
*/ |
/* |
/* |
* Simple byte fifo. It has one reader and one writer. The abuf |
* Simple byte fifo. |
* structure is used to interconnect audio processing units (aproc |
|
* structures). |
|
* |
* |
* The abuf data is split in two parts: (1) valid data available to the reader |
* The abuf data is split in two parts: (1) valid data available to the reader |
* (2) space available to the writer, which is not necessarily unused. It works |
* (2) space available to the writer, which is not necessarily unused. It works |
* as follows: the write starts filling at offset (start + used), once the data |
* as follows: the write starts filling at offset (start + used), once the data |
* is ready, the writer adds to used the count of bytes available. |
* is ready, the writer adds to used the count of bytes available. |
*/ |
*/ |
/* |
|
* TODO |
|
* |
|
* use blocks instead of frames for WOK and ROK macros. If necessary |
|
* (unlikely) define reader block size and writer blocks size to |
|
* ease pipe/socket implementation |
|
*/ |
|
#include <err.h> |
|
#include <stdarg.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
#include "abuf.h" |
#include "abuf.h" |
#include "aparams.h" |
#include "utils.h" |
#include "aproc.h" |
|
#include "conf.h" |
|
#ifdef DEBUG |
|
#include "dbg.h" |
|
#endif |
|
|
|
void abuf_dump(struct abuf *); |
|
int abuf_flush_do(struct abuf *); |
|
int abuf_fill_do(struct abuf *); |
|
void abuf_eof_do(struct abuf *); |
|
void abuf_hup_do(struct abuf *); |
|
|
|
#ifdef DEBUG |
#ifdef DEBUG |
void |
void |
abuf_dbg(struct abuf *buf) |
abuf_log(struct abuf *buf) |
{ |
{ |
if (buf->wproc) { |
log_putu(buf->start); |
aproc_dbg(buf->wproc); |
log_puts("+"); |
} else { |
log_putu(buf->used); |
dbg_puts("none"); |
log_puts("/"); |
} |
log_putu(buf->len); |
dbg_puts(buf->inuse ? "=>" : "->"); |
|
if (buf->rproc) { |
|
aproc_dbg(buf->rproc); |
|
} else { |
|
dbg_puts("none"); |
|
} |
|
} |
} |
|
#endif |
|
|
void |
void |
abuf_dump(struct abuf *buf) |
abuf_init(struct abuf *buf, unsigned int len) |
{ |
{ |
abuf_dbg(buf); |
buf->data = xmalloc(len); |
dbg_puts(": used = "); |
buf->len = len; |
dbg_putu(buf->used); |
|
dbg_puts("/"); |
|
dbg_putu(buf->len); |
|
dbg_puts(" start = "); |
|
dbg_putu(buf->start); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
|
|
struct abuf * |
|
abuf_new(unsigned int nfr, struct aparams *par) |
|
{ |
|
struct abuf *buf; |
|
unsigned int len, bpf; |
|
|
|
bpf = aparams_bpf(par); |
|
len = nfr * bpf; |
|
buf = malloc(sizeof(struct abuf) + len); |
|
if (buf == NULL) { |
|
#ifdef DEBUG |
|
dbg_puts("couldn't allocate abuf of "); |
|
dbg_putu(nfr); |
|
dbg_puts("fr * "); |
|
dbg_putu(bpf); |
|
dbg_puts("bpf\n"); |
|
dbg_panic(); |
|
#else |
|
err(1, "malloc"); |
|
#endif |
|
} |
|
buf->bpf = bpf; |
|
buf->cmin = par->cmin; |
|
buf->cmax = par->cmax; |
|
buf->inuse = 0; |
|
|
|
/* |
|
* fill fifo pointers |
|
*/ |
|
buf->len = nfr; |
|
buf->used = 0; |
buf->used = 0; |
buf->start = 0; |
buf->start = 0; |
buf->rproc = NULL; |
|
buf->wproc = NULL; |
|
buf->duplex = NULL; |
|
return buf; |
|
} |
} |
|
|
void |
void |
abuf_del(struct abuf *buf) |
abuf_done(struct abuf *buf) |
{ |
{ |
if (buf->duplex) |
#ifdef DEBUG |
buf->duplex->duplex = NULL; |
if (buf->used > 0) { |
#ifdef DEBUG |
if (log_level >= 3) { |
if (buf->rproc || buf->wproc) { |
log_puts("deleting non-empty buffer, used = "); |
abuf_dbg(buf); |
log_putu(buf->used); |
dbg_puts(": can't delete referenced buffer\n"); |
log_puts("\n"); |
dbg_panic(); |
|
} |
|
if (ABUF_ROK(buf)) { |
|
/* |
|
* XXX: we should call abort(), here. |
|
* However, poll() doesn't seem to return POLLHUP, |
|
* so the reader is never destroyed; instead it appears |
|
* as blocked. Fix file_poll(), if fixable, and add |
|
* a call to abord() here. |
|
*/ |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": deleting non-empty buffer, used = "); |
|
dbg_putu(buf->used); |
|
dbg_puts("\n"); |
|
} |
} |
} |
} |
#endif |
#endif |
free(buf); |
xfree(buf->data); |
|
buf->data = (void *)0xdeadbeef; |
} |
} |
|
|
/* |
/* |
* Clear buffer contents. |
* return the reader pointer and the number of bytes available |
*/ |
*/ |
void |
|
abuf_clear(struct abuf *buf) |
|
{ |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": cleared\n"); |
|
} |
|
#endif |
|
buf->used = 0; |
|
buf->start = 0; |
|
} |
|
|
|
/* |
|
* Get a pointer to the readable block at the given offset. |
|
*/ |
|
unsigned char * |
unsigned char * |
abuf_rgetblk(struct abuf *buf, unsigned int *rsize, unsigned int ofs) |
abuf_rgetblk(struct abuf *buf, int *rsize) |
{ |
{ |
unsigned int count, start, used; |
int count; |
|
|
start = buf->start + ofs; |
count = buf->len - buf->start; |
used = buf->used - ofs; |
if (count > buf->used) |
if (start >= buf->len) |
count = buf->used; |
start -= buf->len; |
|
#ifdef DEBUG |
|
if (start >= buf->len || used > buf->used) { |
|
abuf_dump(buf); |
|
dbg_puts(": rgetblk: bad ofs = "); |
|
dbg_putu(ofs); |
|
dbg_puts("\n"); |
|
dbg_panic(); |
|
} |
|
#endif |
|
count = buf->len - start; |
|
if (count > used) |
|
count = used; |
|
*rsize = count; |
*rsize = count; |
return (unsigned char *)buf + sizeof(struct abuf) + start * buf->bpf; |
return buf->data + buf->start; |
} |
} |
|
|
/* |
/* |
* Discard the block at the start postion. |
* discard "count" bytes at the start postion. |
*/ |
*/ |
void |
void |
abuf_rdiscard(struct abuf *buf, unsigned int count) |
abuf_rdiscard(struct abuf *buf, int count) |
{ |
{ |
#ifdef DEBUG |
#ifdef DEBUG |
if (count > buf->used) { |
if (count < 0 || count > buf->used) { |
abuf_dump(buf); |
log_puts("abuf_rdiscard: bad count = "); |
dbg_puts(": rdiscard: bad count = "); |
log_putu(count); |
dbg_putu(count); |
log_puts("\n"); |
dbg_puts("\n"); |
panic(); |
dbg_panic(); |
|
} |
} |
if (debug_level >= 4) { |
|
abuf_dbg(buf); |
|
dbg_puts(": discard("); |
|
dbg_putu(count); |
|
dbg_puts(")\n"); |
|
} |
|
#endif |
#endif |
buf->used -= count; |
buf->used -= count; |
buf->start += count; |
buf->start += count; |
|
|
} |
} |
|
|
/* |
/* |
* Commit the data written at the end postion. |
* advance the writer pointer by "count" bytes |
*/ |
*/ |
void |
void |
abuf_wcommit(struct abuf *buf, unsigned int count) |
abuf_wcommit(struct abuf *buf, int count) |
{ |
{ |
#ifdef DEBUG |
#ifdef DEBUG |
if (count > (buf->len - buf->used)) { |
if (count < 0 || count > (buf->len - buf->used)) { |
abuf_dump(buf); |
log_puts("abuf_wcommit: bad count = "); |
dbg_puts(": rdiscard: bad count = "); |
log_putu(count); |
dbg_putu(count); |
log_puts("\n"); |
dbg_puts("\n"); |
panic(); |
dbg_panic(); |
|
} |
} |
if (debug_level >= 4) { |
|
abuf_dbg(buf); |
|
dbg_puts(": commit("); |
|
dbg_putu(count); |
|
dbg_puts(")\n"); |
|
} |
|
#endif |
#endif |
buf->used += count; |
buf->used += count; |
} |
} |
|
|
/* |
/* |
* Get a pointer to the writable block at offset ofs. |
* get writer pointer and the number of bytes writable |
*/ |
*/ |
unsigned char * |
unsigned char * |
abuf_wgetblk(struct abuf *buf, unsigned int *rsize, unsigned int ofs) |
abuf_wgetblk(struct abuf *buf, int *rsize) |
{ |
{ |
unsigned int end, avail, count; |
int end, avail, count; |
|
|
|
end = buf->start + buf->used; |
end = buf->start + buf->used + ofs; |
|
if (end >= buf->len) |
if (end >= buf->len) |
end -= buf->len; |
end -= buf->len; |
#ifdef DEBUG |
avail = buf->len - buf->used; |
if (end >= buf->len) { |
|
abuf_dump(buf); |
|
dbg_puts(": wgetblk: bad ofs = "); |
|
dbg_putu(ofs); |
|
dbg_puts("\n"); |
|
dbg_panic(); |
|
} |
|
#endif |
|
avail = buf->len - (buf->used + ofs); |
|
count = buf->len - end; |
count = buf->len - end; |
if (count > avail) |
if (count > avail) |
count = avail; |
count = avail; |
*rsize = count; |
*rsize = count; |
return (unsigned char *)buf + sizeof(struct abuf) + end * buf->bpf; |
return buf->data + end; |
} |
|
|
|
/* |
|
* Flush buffer either by dropping samples or by calling the aproc |
|
* call-back to consume data. Return 0 if blocked, 1 otherwise. |
|
*/ |
|
int |
|
abuf_flush_do(struct abuf *buf) |
|
{ |
|
struct aproc *p; |
|
|
|
p = buf->rproc; |
|
if (!p) |
|
return 0; |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
aproc_dbg(p); |
|
dbg_puts(": in\n"); |
|
} |
|
#endif |
|
return p->ops->in(p, buf); |
|
} |
|
|
|
/* |
|
* Fill the buffer either by generating silence or by calling the aproc |
|
* call-back to provide data. Return 0 if blocked, 1 otherwise. |
|
*/ |
|
int |
|
abuf_fill_do(struct abuf *buf) |
|
{ |
|
struct aproc *p; |
|
|
|
p = buf->wproc; |
|
if (!p) |
|
return 0; |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
aproc_dbg(p); |
|
dbg_puts(": out\n"); |
|
} |
|
#endif |
|
return p->ops->out(p, buf); |
|
} |
|
|
|
/* |
|
* Notify the reader that there will be no more input (producer |
|
* disappeared) and destroy the buffer. |
|
*/ |
|
void |
|
abuf_eof_do(struct abuf *buf) |
|
{ |
|
struct aproc *p; |
|
|
|
p = buf->rproc; |
|
if (p) { |
|
buf->rproc = NULL; |
|
LIST_REMOVE(buf, ient); |
|
buf->inuse++; |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
aproc_dbg(p); |
|
dbg_puts(": eof\n"); |
|
} |
|
#endif |
|
p->ops->eof(p, buf); |
|
buf->inuse--; |
|
} |
|
abuf_del(buf); |
|
} |
|
|
|
/* |
|
* Notify the writer that the buffer has no more consumer, |
|
* and destroy the buffer. |
|
*/ |
|
void |
|
abuf_hup_do(struct abuf *buf) |
|
{ |
|
struct aproc *p; |
|
|
|
if (ABUF_ROK(buf)) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": hup: lost "); |
|
dbg_putu(buf->used); |
|
dbg_puts(" bytes\n"); |
|
} |
|
#endif |
|
buf->used = 0; |
|
} |
|
p = buf->wproc; |
|
if (p != NULL) { |
|
buf->wproc = NULL; |
|
LIST_REMOVE(buf, oent); |
|
buf->inuse++; |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
aproc_dbg(p); |
|
dbg_puts(": hup\n"); |
|
} |
|
#endif |
|
p->ops->hup(p, buf); |
|
buf->inuse--; |
|
} |
|
abuf_del(buf); |
|
} |
|
|
|
/* |
|
* Notify the read end of the buffer that there is input available |
|
* and that data can be processed again. |
|
*/ |
|
int |
|
abuf_flush(struct abuf *buf) |
|
{ |
|
if (buf->inuse) { |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
abuf_dbg(buf); |
|
dbg_puts(": flush blocked (inuse)\n"); |
|
} |
|
#endif |
|
} else { |
|
buf->inuse++; |
|
for (;;) { |
|
if (!abuf_flush_do(buf)) |
|
break; |
|
} |
|
buf->inuse--; |
|
if (ABUF_HUP(buf)) { |
|
abuf_hup_do(buf); |
|
return 0; |
|
} |
|
} |
|
return 1; |
|
} |
|
|
|
/* |
|
* Notify the write end of the buffer that there is room and data can be |
|
* written again. This routine can only be called from the out() |
|
* call-back of the reader. |
|
* |
|
* Return 1 if the buffer was filled, and 0 if eof condition occured. The |
|
* reader must detach the buffer on EOF condition, since its aproc->eof() |
|
* call-back will never be called. |
|
*/ |
|
int |
|
abuf_fill(struct abuf *buf) |
|
{ |
|
if (buf->inuse) { |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
abuf_dbg(buf); |
|
dbg_puts(": fill blocked (inuse)\n"); |
|
} |
|
#endif |
|
} else { |
|
buf->inuse++; |
|
for (;;) { |
|
if (!abuf_fill_do(buf)) |
|
break; |
|
} |
|
buf->inuse--; |
|
if (ABUF_EOF(buf)) { |
|
abuf_eof_do(buf); |
|
return 0; |
|
} |
|
} |
|
return 1; |
|
} |
|
|
|
/* |
|
* Run a read/write loop on the buffer until either the reader or the |
|
* writer blocks, or until the buffer reaches eofs. We can not get hup here, |
|
* since hup() is only called from terminal nodes, from the main loop. |
|
* |
|
* NOTE: The buffer may disappear (ie. be free()ed) if eof is reached, so |
|
* do not keep references to the buffer or to its writer or reader. |
|
*/ |
|
void |
|
abuf_run(struct abuf *buf) |
|
{ |
|
int canfill = 1, canflush = 1; |
|
|
|
if (buf->inuse) { |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
abuf_dbg(buf); |
|
dbg_puts(": run blocked (inuse)\n"); |
|
} |
|
#endif |
|
return; |
|
} |
|
buf->inuse++; |
|
for (;;) { |
|
if (canfill) { |
|
if (!abuf_fill_do(buf)) |
|
canfill = 0; |
|
else |
|
canflush = 1; |
|
} else if (canflush) { |
|
if (!abuf_flush_do(buf)) |
|
canflush = 0; |
|
else |
|
canfill = 1; |
|
} else |
|
break; |
|
} |
|
buf->inuse--; |
|
if (ABUF_EOF(buf)) { |
|
abuf_eof_do(buf); |
|
return; |
|
} |
|
if (ABUF_HUP(buf)) { |
|
abuf_hup_do(buf); |
|
return; |
|
} |
|
} |
|
|
|
/* |
|
* Notify the reader that there will be no more input (producer |
|
* disappeared). The buffer is flushed and eof() is called only if all |
|
* data is flushed. |
|
*/ |
|
void |
|
abuf_eof(struct abuf *buf) |
|
{ |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": eof requested\n"); |
|
} |
|
if (buf->wproc == NULL) { |
|
abuf_dbg(buf); |
|
dbg_puts(": eof, no writer\n"); |
|
dbg_panic(); |
|
} |
|
#endif |
|
LIST_REMOVE(buf, oent); |
|
buf->wproc = NULL; |
|
if (buf->rproc != NULL) { |
|
if (!abuf_flush(buf)) |
|
return; |
|
if (ABUF_ROK(buf)) { |
|
/* |
|
* Could not flush everything, the reader will |
|
* have a chance to delete the abuf later. |
|
*/ |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": eof, blocked (drain)\n"); |
|
} |
|
#endif |
|
return; |
|
} |
|
} |
|
if (buf->inuse) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": eof, blocked (inuse)\n"); |
|
} |
|
#endif |
|
return; |
|
} |
|
abuf_eof_do(buf); |
|
} |
|
|
|
/* |
|
* Notify the writer that the buffer has no more consumer, |
|
* and that no more data will accepted. |
|
*/ |
|
void |
|
abuf_hup(struct abuf *buf) |
|
{ |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": hup requested\n"); |
|
} |
|
if (buf->rproc == NULL) { |
|
abuf_dbg(buf); |
|
dbg_puts(": hup, no reader\n"); |
|
dbg_panic(); |
|
} |
|
#endif |
|
buf->rproc = NULL; |
|
LIST_REMOVE(buf, ient); |
|
if (buf->wproc != NULL) { |
|
if (buf->inuse) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
abuf_dbg(buf); |
|
dbg_puts(": eof, blocked (inuse)\n"); |
|
} |
|
#endif |
|
return; |
|
} |
|
} |
|
abuf_hup_do(buf); |
|
} |
|
|
|
/* |
|
* Notify the reader of the change of its real-time position |
|
*/ |
|
void |
|
abuf_ipos(struct abuf *buf, int delta) |
|
{ |
|
struct aproc *p = buf->rproc; |
|
|
|
if (p && p->ops->ipos) { |
|
buf->inuse++; |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
aproc_dbg(p); |
|
dbg_puts(": ipos delta = "); |
|
dbg_puti(delta); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
p->ops->ipos(p, buf, delta); |
|
buf->inuse--; |
|
} |
|
if (ABUF_HUP(buf)) |
|
abuf_hup_do(buf); |
|
} |
|
|
|
/* |
|
* Notify the writer of the change of its real-time position |
|
*/ |
|
void |
|
abuf_opos(struct abuf *buf, int delta) |
|
{ |
|
struct aproc *p = buf->wproc; |
|
|
|
if (p && p->ops->opos) { |
|
buf->inuse++; |
|
#ifdef DEBUG |
|
if (debug_level >= 4) { |
|
aproc_dbg(p); |
|
dbg_puts(": opos delta = "); |
|
dbg_puti(delta); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
p->ops->opos(p, buf, delta); |
|
buf->inuse--; |
|
} |
|
if (ABUF_HUP(buf)) |
|
abuf_hup_do(buf); |
|
} |
} |