From 1a5060720d67fa6e4d4cb3b08c5067ec5216b0fd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Jun 2004 00:29:01 +0000 Subject: oss output works git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@15 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 23 +++++++------ src/inputstream.c | 20 +++++++++-- src/inputstream.h | 14 ++++++-- src/main.c | 5 +-- src/memblockq.c | 30 +++++++++++++--- src/memblockq.h | 5 +-- src/module-oss.c | 63 ++++++++++++++++++++-------------- src/outputstream.c | 2 +- src/protocol-simple.c | 95 +++++++++++++++++++++++++++++++-------------------- src/sink.c | 20 +++++++---- src/sink.h | 4 +-- src/todo | 16 +++++++++ 12 files changed, 202 insertions(+), 95 deletions(-) create mode 100644 src/todo diff --git a/src/Makefile.am b/src/Makefile.am index d249964b..f0953846 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,8 +20,9 @@ AM_CFLAGS=-ansi -D_GNU_SOURCE bin_PROGRAMS = polypaudio -pkglib_LTLIBRARIES=libprotocol-simple.la protocol-simple-tcp.la \ - libsocket-server.la sink-pipe.la libpstream.la libiochannel.la libpacket.la +pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \ + libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \ + libpacket.la module-oss.la polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c \ memblock.c sample.c memblockq.c client.c \ @@ -49,14 +50,14 @@ libiochannel_la_LDFLAGS = -avoid-version libpacket_la_SOURCES = packet.c libpacket_la_LDFLAGS = -avoid-version -protocol_simple_tcp_la_SOURCES = protocol-simple-tcp.c -protocol_simple_tcp_la_LDFLAGS = -module -avoid-version -protocol_simple_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la +module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c +module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la -sink_pipe_la_SOURCES = sink-pipe.c -sink_pipe_la_LDFLAGS = -module -avoid-version -sink_pipe_la_LIBADD = libiochannel.la +module_pipe_sink_la_SOURCES = module-pipe-sink.c +module_pipe_sink_la_LDFLAGS = -module -avoid-version +module_pipe_sink_la_LIBADD = libiochannel.la -sink_oss_la_SOURCES = sink-pipe.c -sink_oss_la_LDFLAGS = -module -avoid-version -sink_oss_la_LIBADD = libiochannel.la +module_oss_la_SOURCES = module-oss.c +module_oss_la_LDFLAGS = -module -avoid-version +module_oss_la_LIBADD = libiochannel.la diff --git a/src/inputstream.c b/src/inputstream.c index 81719288..7ece3b5c 100644 --- a/src/inputstream.c +++ b/src/inputstream.c @@ -14,10 +14,13 @@ struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec, i->name = name ? strdup(name) : NULL; i->sink = s; i->spec = *spec; + i->kill = NULL; i->kill_userdata = NULL; + i->notify = NULL; + i->notify_userdata = NULL; - i->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec)); + i->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1); assert(i->memblockq); assert(s->core); @@ -45,7 +48,7 @@ void input_stream_free(struct input_stream* i) { void input_stream_notify_sink(struct input_stream *i) { assert(i); - if (memblockq_is_empty(i->memblockq)) + if (!memblockq_is_readable(i->memblockq)) return; sink_notify(i->sink); @@ -64,3 +67,16 @@ void input_stream_kill(struct input_stream*i) { if (i->kill) i->kill(i, i->kill_userdata); } + +void input_stream_set_notify_callback(struct input_stream *i, void (*notify)(struct input_stream*i, void *userdata), void *userdata) { + assert(i && notify); + + i->notify = notify; + i->notify_userdata = userdata; +} + +void input_stream_notify(struct input_stream *i) { + assert(i); + if (i->notify) + i->notify(i, i->notify_userdata); +} diff --git a/src/inputstream.h b/src/inputstream.h index a258c3d1..544c3318 100644 --- a/src/inputstream.h +++ b/src/inputstream.h @@ -18,6 +18,9 @@ struct input_stream { void (*kill)(struct input_stream* i, void *userdata); void *kill_userdata; + + void (*notify)(struct input_stream*i, void *userdata); + void *notify_userdata; }; struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec, const char *name); @@ -31,10 +34,17 @@ void input_stream_notify_sink(struct input_stream *i); /* The registrant of the input stream should call this function to set a * callback function which is called when destruction of the input stream is * requested */ -void input_stream_set_kill_callback(struct input_stream *c, void (*kill)(struct input_stream*i, void *userdata), void *userdata); +void input_stream_set_kill_callback(struct input_stream *i, void (*kill)(struct input_stream*i, void *userdata), void *userdata); /* Code that didn't create the input stream should call this function to * request destruction of it */ -void input_stream_kill(struct input_stream *c); +void input_stream_kill(struct input_stream *i); + +/* Notify the code that created this input stream that some data has + * been removed from the memblockq */ +void input_stream_set_notify_callback(struct input_stream *i, void (*notify)(struct input_stream*i, void *userdata), void *userdata); + +void input_stream_notify(struct input_stream *i); + #endif diff --git a/src/main.c b/src/main.c index 436dd30b..d54bee0a 100644 --- a/src/main.c +++ b/src/main.c @@ -29,8 +29,9 @@ int main(int argc, char *argv[]) { mainloop_source_new_signal(m, SIGINT, signal_callback, NULL); signal(SIGPIPE, SIG_IGN); - module_load(c, "sink-pipe", NULL); - module_load(c, "protocol-simple-tcp", NULL); + module_load(c, "module-oss", NULL); + module_load(c, "module-pipe-sink", NULL); + module_load(c, "module-simple-protocol-tcp", NULL); mainloop_run(m); diff --git a/src/memblockq.c b/src/memblockq.c index 6437dd5b..b25adc65 100644 --- a/src/memblockq.c +++ b/src/memblockq.c @@ -1,3 +1,4 @@ +#include #include #include @@ -14,9 +15,10 @@ struct memblockq { size_t total_length; size_t maxlength; size_t base; + size_t prebuf; }; -struct memblockq* memblockq_new(size_t maxlength, size_t base) { +struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) { struct memblockq* bq; assert(maxlength && base); @@ -27,6 +29,11 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base) { bq->total_length = 0; bq->base = base; bq->maxlength = ((maxlength+base-1)/base)*base; + bq->prebuf = prebuf == (size_t) -1 ? bq->maxlength/2 : prebuf; + + if (bq->prebuf > bq->maxlength) + bq->prebuf = bq->maxlength; + assert(bq->maxlength >= base); return bq; } @@ -72,9 +79,11 @@ void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta) int memblockq_peek(struct memblockq* bq, struct memchunk *chunk) { assert(bq && chunk); - if (!bq->blocks) + if (!bq->blocks || bq->total_length < bq->prebuf) return -1; + bq->prebuf = 0; + *chunk = bq->blocks->chunk; memblock_ref(chunk->memblock); return 0; @@ -85,9 +94,11 @@ int memblockq_pop(struct memblockq* bq, struct memchunk *chunk) { assert(bq && chunk); - if (!bq->blocks) + if (!bq->blocks || bq->total_length < bq->prebuf) return -1; + bq->prebuf = 0; + q = bq->blocks; bq->blocks = bq->blocks->next; @@ -138,6 +149,8 @@ void memblockq_shorten(struct memblockq *bq, size_t length) { if (bq->total_length <= length) return; + fprintf(stderr, "Warning! memblockq_shorten()\n"); + l = bq->total_length - length; l /= bq->base; l *= bq->base; @@ -151,8 +164,15 @@ void memblockq_empty(struct memblockq *bq) { memblockq_shorten(bq, 0); } -int memblockq_is_empty(struct memblockq *bq) { +int memblockq_is_readable(struct memblockq *bq) { + assert(bq); + + return bq->total_length >= bq->prebuf; +} + +int memblockq_is_writable(struct memblockq *bq, size_t length) { assert(bq); - return bq->total_length < bq->base; + assert(length <= bq->maxlength); + return bq->total_length + length <= bq->maxlength; } diff --git a/src/memblockq.h b/src/memblockq.h index 75c5e59e..48050d49 100644 --- a/src/memblockq.h +++ b/src/memblockq.h @@ -7,7 +7,7 @@ struct memblockq; -struct memblockq* memblockq_new(size_t maxlength, size_t base); +struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf); void memblockq_free(struct memblockq* bq); void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta); @@ -19,6 +19,7 @@ void memblockq_drop(struct memblockq *bq, size_t length); void memblockq_shorten(struct memblockq *bq, size_t length); void memblockq_empty(struct memblockq *bq); -int memblockq_is_empty(struct memblockq *bq); +int memblockq_is_readable(struct memblockq *bq); +int memblockq_is_writable(struct memblockq *bq, size_t length); #endif diff --git a/src/module-oss.c b/src/module-oss.c index b2b106b4..7a1482e7 100644 --- a/src/module-oss.c +++ b/src/module-oss.c @@ -1,4 +1,5 @@ -#include +#include +#include #include #include #include @@ -22,7 +23,7 @@ struct userdata { struct memchunk memchunk, silence; - uint32_t fragment_size; + uint32_t in_fragment_size, out_fragment_size, sample_size; }; static void do_write(struct userdata *u) { @@ -34,7 +35,7 @@ static void do_write(struct userdata *u) { return; if (!u->memchunk.length) { - if (sink_render(u->sink, fragment_size, &u->memchunk) < 0) + if (sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0) memchunk = &u->silence; else memchunk = &u->memchunk; @@ -68,15 +69,15 @@ static void do_read(struct userdata *u) { if (!iochannel_is_readable(u->io)) return; - memchunk.memblock = memblock_new(u->fragment_size); + memchunk.memblock = memblock_new(u->in_fragment_size); assert(memchunk.memblock); - if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk->memblock->length)) < 0) { + if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { memblock_unref(memchunk.memblock); fprintf(stderr, "read() failed: %s\n", strerror(errno)); return; } - assert(r < memchunk->memblock->length); + assert(r <= memchunk.memblock->length); memchunk.length = memchunk.memblock->length = r; memchunk.index = 0; @@ -92,17 +93,17 @@ static void io_callback(struct iochannel *io, void*userdata) { } int module_init(struct core *c, struct module*m) { + struct audio_buf_info info; struct userdata *u = NULL; - struct stat st; char *p; int fd = -1; - int format, channels, speed, frag_size; - int m; - const static struct sample_spec ss; + int format, channels, speed, frag_size, in_frag_size, out_frag_size; + int mode; + struct sample_spec ss; assert(c && m); p = m->argument ? m->argument : "/dev/dsp"; - if ((fd = open(p, m = O_RDWR)) >= 0) { + if ((fd = open(p, mode = O_RDWR)) >= 0) { int caps; ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); @@ -119,16 +120,16 @@ int module_init(struct core *c, struct module*m) { } if (fd < 0) { - if ((fd = open(p, m = O_WRONLY)) < 0) { - if ((fd = open(p, m = O_RDONLY)) < 0) { + if ((fd = open(p, mode = O_WRONLY)) < 0) { + if ((fd = open(p, mode = O_RDONLY)) < 0) { fprintf(stderr, "open('%s'): %s\n", p, strerror(errno)); goto fail; } } } - - frags = 0x7fff0000 | (10 << 16); /* 1024 bytes fragment size */ - if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frags) < 0) { + + frag_size = ((int) 0x7ffff << 4) | 10; /* nfrags = 4; frag_size = 2^10 */ + if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) { fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); goto fail; } @@ -165,25 +166,35 @@ int module_init(struct core *c, struct module*m) { assert(speed); ss.rate = speed; - if (ioctl(fd, SNCTL_DSP_GETBLKSIZE, &frag_size) < 0) { + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); goto fail; } - assert(frag_size); - + in_frag_size = out_frag_size = frag_size; + + if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { + fprintf(stderr, "INPUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize); + in_frag_size = info.fragsize; + } + + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { + fprintf(stderr, "OUTUT: %u fragments of size %u.\n", info.fragstotal, info.fragsize); + out_frag_size = info.fragsize; + } + u = malloc(sizeof(struct userdata)); assert(u); u->core = c; - if (m != O_RDONLY) { + if (mode != O_RDONLY) { u->sink = sink_new(c, "dsp", &ss); assert(u->sink); } else u->sink = NULL; - if (m != O_WRONLY) { + if (mode != O_WRONLY) { u->source = source_new(c, "dsp", &ss); assert(u->source); } else @@ -197,11 +208,13 @@ int module_init(struct core *c, struct module*m) { u->memchunk.memblock = NULL; u->memchunk.length = 0; + u->sample_size = sample_size(&ss); - u->fragment_size = frag_size; - u->silence.memblock = memblock_new(u->fragment_size); - assert(u->silence); - u->silence.length = u->fragment_size; + u->out_fragment_size = out_frag_size; + u->in_fragment_size = in_frag_size; + u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size); + assert(u->silence.memblock); + silence(u->silence.memblock, &ss); u->silence.index = 0; m->userdata = u; diff --git a/src/outputstream.c b/src/outputstream.c index c6681d29..c3f68a0e 100644 --- a/src/outputstream.c +++ b/src/outputstream.c @@ -17,7 +17,7 @@ struct output_stream* output_stream_new(struct source *s, struct sample_spec *sp o->kill = NULL; o->kill_userdata = NULL; - o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec)); + o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1); assert(o->memblockq); assert(s->core); diff --git a/src/protocol-simple.c b/src/protocol-simple.c index ec121faa..1c462b39 100644 --- a/src/protocol-simple.c +++ b/src/protocol-simple.c @@ -66,53 +66,73 @@ static void client_kill_cb(struct client *client, void*userdata) { destroy_connection(c); } -static void io_callback(struct iochannel*io, void *userdata) { - struct connection *c = userdata; - assert(io && c); - - if (c->istream && iochannel_is_readable(io)) { - struct memchunk chunk; - ssize_t r; - - chunk.memblock = memblock_new(BUFSIZE); - assert(chunk.memblock); +static int do_read(struct connection *c) { + struct memchunk chunk; + ssize_t r; - if ((r = iochannel_read(io, chunk.memblock->data, BUFSIZE)) <= 0) { - fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno)); - memblock_unref(chunk.memblock); - goto fail; - } - - chunk.memblock->length = r; - chunk.length = r; - chunk.index = 0; - - memblockq_push(c->istream->memblockq, &chunk, 0); - input_stream_notify_sink(c->istream); + if (!iochannel_is_readable(c->io)) + return 0; + + if (!c->istream || !memblockq_is_writable(c->istream->memblockq, BUFSIZE)) + return 0; + + chunk.memblock = memblock_new(BUFSIZE); + assert(chunk.memblock); + + if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) { + fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno)); memblock_unref(chunk.memblock); + return -1; } + + chunk.memblock->length = r; + chunk.length = r; + chunk.index = 0; + + memblockq_push(c->istream->memblockq, &chunk, 0); + input_stream_notify_sink(c->istream); + memblock_unref(chunk.memblock); + return 0; +} - if (c->ostream && iochannel_is_writable(io)) { - struct memchunk chunk; - ssize_t r; +static int do_write(struct connection *c) { + struct memchunk chunk; + ssize_t r; - memblockq_peek(c->ostream->memblockq, &chunk); - assert(chunk.memblock && chunk.length); + if (!iochannel_is_writable(c->io)) + return 0; + + if (!c->ostream) + return 0; - if ((r = iochannel_write(io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { - fprintf(stderr, "write(): %s\n", strerror(errno)); - memblock_unref(chunk.memblock); - goto fail; - } - - memblockq_drop(c->ostream->memblockq, r); + memblockq_peek(c->ostream->memblockq, &chunk); + assert(chunk.memblock && chunk.length); + + if ((r = iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { + fprintf(stderr, "write(): %s\n", strerror(errno)); memblock_unref(chunk.memblock); + return -1; } + + memblockq_drop(c->ostream->memblockq, r); + memblock_unref(chunk.memblock); + return 0; +} - return; +static void io_callback(struct iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + + if (do_read(c) < 0 || do_write(c) < 0) + destroy_connection(c); +} + +static void istream_notify_cb(struct input_stream *i, void *userdata) { + struct connection*c = userdata; + assert(i && c && c->istream == i); -fail: - destroy_connection(c); + if (do_read(c) < 0) + destroy_connection(c); } static void on_connection(struct socket_server*s, struct iochannel *io, void *userdata) { @@ -155,6 +175,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us c->istream = input_stream_new(sink, &DEFAULT_SAMPLE_SPEC, c->client->name); assert(c->istream); input_stream_set_kill_callback(c->istream, istream_kill_cb, c); + input_stream_set_notify_callback(c->istream, istream_notify_cb, c); } diff --git a/src/sink.c b/src/sink.c index dfe1bcb9..f2d5373d 100644 --- a/src/sink.c +++ b/src/sink.c @@ -33,8 +33,8 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s s->volume = 0xFF; - s->notify_callback = NULL; - s->userdata = NULL; + s->notify = NULL; + s->notify_userdata = NULL; return s; } @@ -103,6 +103,10 @@ static int do_mix(void *p, uint32_t index, int *del, void*userdata) { assert(chunk.length && chunk.length <= info->chunk->memblock->length - info->chunk->index); add_clip(info->chunk, &chunk, info->spec); + memblock_unref(chunk.memblock); + memblockq_drop(i->memblockq, info->chunk->length); + + input_stream_notify(i); return 0; } @@ -139,6 +143,8 @@ int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *re target->length = l; memblock_unref(chunk.memblock); memblockq_drop(i->memblockq, l); + + input_stream_notify(i); result->memblock = target; result->length = l; @@ -191,6 +197,8 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) { l = length < result->length ? length : result->length; result->length = l; memblockq_drop(i->memblockq, l); + input_stream_notify(i); + return 0; } @@ -211,15 +219,15 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) { void sink_notify(struct sink*s) { assert(s); - if (s->notify_callback) - s->notify_callback(s, s->userdata); + if (s->notify) + s->notify(s, s->notify_userdata); } void sink_set_notify_callback(struct sink *s, void (*notify_callback)(struct sink*sink, void *userdata), void *userdata) { assert(s && notify_callback); - s->notify_callback = notify_callback; - s->userdata = userdata; + s->notify = notify_callback; + s->notify_userdata = userdata; } diff --git a/src/sink.h b/src/sink.h index a6f98005..1678fc75 100644 --- a/src/sink.h +++ b/src/sink.h @@ -22,8 +22,8 @@ struct sink { uint8_t volume; - void (*notify_callback)(struct sink*sink, void *userdata); - void *userdata; + void (*notify)(struct sink*sink, void *userdata); + void *notify_userdata; }; struct sink* sink_new(struct core *core, const char *name, const struct sample_spec *spec); diff --git a/src/todo b/src/todo new file mode 100644 index 00000000..d4cc5f18 --- /dev/null +++ b/src/todo @@ -0,0 +1,16 @@ +- mixing +- resampling +- native protocol/library +- oss/mmap +- esound prodocol +- config-parser +-- 0.1 +- future cancellation +- client-ui + +drivers: +- libao +- xmms +- portaudio +- mplayer +- python -- cgit