From 78f386ad45dc046d673fca5441dff188a7297059 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Jun 2004 15:18:33 +0000 Subject: more work git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@17 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/client.c | 4 + src/core.c | 4 +- src/inputstream.c | 82 ----------------- src/inputstream.h | 50 ----------- src/main.c | 12 ++- src/memblock.c | 38 ++++++++ src/memblock.h | 7 ++ src/memblockq.c | 2 + src/memblockq.h | 1 + src/module-oss.c | 26 +++--- src/module.c | 6 ++ src/outputstream.c | 57 ------------ src/outputstream.h | 27 ------ src/protocol-simple.c | 4 +- src/sample.c | 53 +++++++---- src/sample.h | 10 ++- src/sink.c | 245 +++++++++++++++++++++----------------------------- src/sink.h | 13 ++- src/sinkinput.c | 82 +++++++++++++++++ src/sinkinput.h | 50 +++++++++++ src/source.c | 5 ++ src/sourceoutput.c | 57 ++++++++++++ src/sourceoutput.h | 27 ++++++ 23 files changed, 465 insertions(+), 397 deletions(-) delete mode 100644 src/inputstream.c delete mode 100644 src/inputstream.h delete mode 100644 src/outputstream.c delete mode 100644 src/outputstream.h create mode 100644 src/sinkinput.c create mode 100644 src/sinkinput.h create mode 100644 src/sourceoutput.c create mode 100644 src/sourceoutput.h diff --git a/src/client.c b/src/client.c index 3dd37668..578d51c3 100644 --- a/src/client.c +++ b/src/client.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -19,6 +20,8 @@ struct client *client_new(struct core *core, const char *protocol_name, char *na r = idxset_put(core->clients, c, &c->index); assert(c->index != IDXSET_INVALID && r >= 0); + + fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name); return c; } @@ -27,6 +30,7 @@ void client_free(struct client *c) { assert(c && c->core); idxset_remove_by_data(c->core->clients, c, NULL); + fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name); free(c->name); free(c); } diff --git a/src/core.c b/src/core.c index 0457f4f3..2e67118a 100644 --- a/src/core.c +++ b/src/core.c @@ -60,7 +60,7 @@ struct sink* core_get_default_sink(struct core *c) { if (!(sink = idxset_first(c->sinks, &c->default_sink_index))) return NULL; - fprintf(stderr, "Default sink vanished, setting to %u\n", sink->index); + fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index); return sink; } @@ -74,6 +74,6 @@ struct source* core_get_default_source(struct core *c) { if (!(source = idxset_first(c->sources, &c->default_source_index))) return NULL; - fprintf(stderr, "Default source vanished, setting to %u\n", source->index); + fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index); return source; } diff --git a/src/inputstream.c b/src/inputstream.c deleted file mode 100644 index 6bc841ac..00000000 --- a/src/inputstream.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include - -#include "inputstream.h" - -struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec, const char *name) { - struct input_stream *i; - int r; - assert(s && spec); - - i = malloc(sizeof(struct input_stream)); - assert(i); - 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)/2, sample_size(spec), (size_t) -1); - assert(i->memblockq); - - assert(s->core); - r = idxset_put(s->core->input_streams, i, &i->index); - assert(r == 0 && i->index != IDXSET_INVALID); - r = idxset_put(s->input_streams, i, NULL); - assert(r == 0); - - return i; -} - -void input_stream_free(struct input_stream* i) { - assert(i); - - memblockq_free(i->memblockq); - - assert(i->sink && i->sink->core); - idxset_remove_by_data(i->sink->core->input_streams, i, NULL); - idxset_remove_by_data(i->sink->input_streams, i, NULL); - - free(i->name); - free(i); -} - -void input_stream_notify_sink(struct input_stream *i) { - assert(i); - - if (!memblockq_is_readable(i->memblockq)) - return; - - sink_notify(i->sink); -} - -void input_stream_set_kill_callback(struct input_stream *i, void (*kill)(struct input_stream*i, void *userdata), void *userdata) { - assert(i && kill); - i->kill = kill; - i->kill_userdata = userdata; -} - - -void input_stream_kill(struct input_stream*i) { - assert(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 deleted file mode 100644 index 544c3318..00000000 --- a/src/inputstream.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef fooinputstreamhfoo -#define fooinputstreamhfoo - -#include - -#include "sink.h" -#include "sample.h" -#include "memblockq.h" - -struct input_stream { - char *name; - uint32_t index; - - struct sink *sink; - struct sample_spec spec; - - struct memblockq *memblockq; - - 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); -void input_stream_free(struct input_stream* i); - -/* This function notifies the attached sink that new data is available - * in the memblockq */ -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 *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 *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 d54bee0a..a42eaa82 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "core.h" #include "mainloop.h" @@ -10,7 +11,7 @@ static void signal_callback(struct mainloop_source *m, int sig, void *userdata) { mainloop_quit(mainloop_source_get_mainloop(m), -1); - fprintf(stderr, "Got signal.\n"); + fprintf(stderr, "main: got signal.\n"); } int main(int argc, char *argv[]) { @@ -29,10 +30,17 @@ int main(int argc, char *argv[]) { mainloop_source_new_signal(m, SIGINT, signal_callback, NULL); signal(SIGPIPE, SIG_IGN); - module_load(c, "module-oss", NULL); + module_load(c, "module-oss", "/dev/dsp1"); module_load(c, "module-pipe-sink", NULL); module_load(c, "module-simple-protocol-tcp", NULL); + + fprintf(stderr, "main: mainloop entry.\n"); + while (mainloop_iterate(m, 1) == 0); +/* fprintf(stderr, "main: %u blocks\n", n_blocks);*/ + fprintf(stderr, "main: mainloop exit.\n"); + + mainloop_run(m); core_free(c); diff --git a/src/memblock.c b/src/memblock.c index 3bef4944..2d346769 100644 --- a/src/memblock.c +++ b/src/memblock.c @@ -1,15 +1,22 @@ +#include #include #include #include +#include +#include #include "memblock.h" +unsigned n_blocks = 0; + struct memblock *memblock_new(size_t length) { struct memblock *b = malloc(sizeof(struct memblock)+length); b->type = MEMBLOCK_APPENDED; b->ref = 1; b->length = length; b->data = b+1; + n_blocks++; + timerclear(&b->stamp); return b; } @@ -19,6 +26,8 @@ struct memblock *memblock_new_fixed(void *d, size_t length) { b->ref = 1; b->length = length; b->data = d; + n_blocks++; + timerclear(&b->stamp); return b; } @@ -28,6 +37,8 @@ struct memblock *memblock_new_dynamic(void *d, size_t length) { b->ref = 1; b->length = length; b->data = d; + n_blocks++; + timerclear(&b->stamp); return b; } @@ -45,6 +56,7 @@ void memblock_unref(struct memblock*b) { if (b->type == MEMBLOCK_DYNAMIC) free(b->data); free(b); + n_blocks--; } } @@ -65,3 +77,29 @@ void memblock_unref_fixed(struct memblock *b) { b->type = MEMBLOCK_DYNAMIC; } +void memblock_stamp(struct memblock*b) { + assert(b); + gettimeofday(&b->stamp, NULL); +} + +uint32_t memblock_age(struct memblock*b) { + assert(b); + struct timeval tv; + uint32_t r; + + if (b->stamp.tv_sec == 0) + return (suseconds_t) -1; + + gettimeofday(&tv, NULL); + + /*fprintf(stderr, "memblock: (%lu,%lu) -- (%lu,%lu)\r", b->stamp.tv_sec, b->stamp.tv_usec, tv.tv_sec, tv.tv_usec);*/ + + r = (tv.tv_sec-b->stamp.tv_sec) * 1000000; + + if (tv.tv_usec >= b->stamp.tv_usec) + r += tv.tv_usec - b->stamp.tv_usec; + else + r -= b->stamp.tv_usec - tv.tv_usec; + + return r; +} diff --git a/src/memblock.h b/src/memblock.h index 48e87286..c0fb6708 100644 --- a/src/memblock.h +++ b/src/memblock.h @@ -2,6 +2,7 @@ #define foomemblockhfoo #include +#include enum memblock_type { MEMBLOCK_FIXED, MEMBLOCK_APPENDED, MEMBLOCK_DYNAMIC }; @@ -10,6 +11,7 @@ struct memblock { unsigned ref; size_t length; void *data; + struct timeval stamp; }; struct memchunk { @@ -26,6 +28,11 @@ struct memblock* memblock_ref(struct memblock*b); void memblock_unref_fixed(struct memblock*b); +void memblock_stamp(struct memblock*b); +uint32_t memblock_age(struct memblock*b); + #define memblock_assert_exclusive(b) assert((b)->ref == 1) +extern unsigned n_blocks; + #endif diff --git a/src/memblockq.c b/src/memblockq.c index b25adc65..3c0d4326 100644 --- a/src/memblockq.c +++ b/src/memblockq.c @@ -18,6 +18,7 @@ struct memblockq { size_t prebuf; }; + struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) { struct memblockq* bq; assert(maxlength && base); @@ -35,6 +36,7 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) { bq->prebuf = bq->maxlength; assert(bq->maxlength >= base); + return bq; } diff --git a/src/memblockq.h b/src/memblockq.h index 48050d49..25c2f2d4 100644 --- a/src/memblockq.h +++ b/src/memblockq.h @@ -22,4 +22,5 @@ void memblockq_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 7a1482e7..07a407d8 100644 --- a/src/module-oss.c +++ b/src/module-oss.c @@ -31,7 +31,7 @@ static void do_write(struct userdata *u) { ssize_t r; assert(u); - if (!iochannel_is_writable(u->io)) + if (!u->sink || !iochannel_is_writable(u->io)) return; if (!u->memchunk.length) { @@ -66,7 +66,7 @@ static void do_read(struct userdata *u) { ssize_t r; assert(u); - if (!iochannel_is_readable(u->io)) + if (!u->source || !iochannel_is_readable(u->io)) return; memchunk.memblock = memblock_new(u->in_fragment_size); @@ -103,7 +103,7 @@ int module_init(struct core *c, struct module*m) { assert(c && m); p = m->argument ? m->argument : "/dev/dsp"; - if ((fd = open(p, mode = O_RDWR)) >= 0) { + if ((fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) { int caps; ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); @@ -120,15 +120,17 @@ int module_init(struct core *c, struct module*m) { } if (fd < 0) { - if ((fd = open(p, mode = O_WRONLY)) < 0) { - if ((fd = open(p, mode = O_RDONLY)) < 0) { + if ((fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) { + if ((fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) { fprintf(stderr, "open('%s'): %s\n", p, strerror(errno)); goto fail; } } } + + fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - frag_size = ((int) 0x7ffff << 4) | 10; /* nfrags = 4; frag_size = 2^10 */ + frag_size = ((int) 4 << 16) | 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; @@ -174,12 +176,12 @@ int module_init(struct core *c, struct module*m) { 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); + fprintf(stderr, "module-oss: 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); + fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); out_frag_size = info.fragsize; } @@ -239,9 +241,11 @@ void module_done(struct core *c, struct module*m) { memblock_unref(u->memchunk.memblock); if (u->silence.memblock) memblock_unref(u->silence.memblock); - - sink_free(u->sink); - source_free(u->source); + + if (u->sink) + sink_free(u->sink); + if (u->source) + source_free(u->source); iochannel_free(u->io); free(u); } diff --git a/src/module.c b/src/module.c index 62204e4c..7f2bc218 100644 --- a/src/module.c +++ b/src/module.c @@ -40,6 +40,9 @@ struct module* module_load(struct core *c, const char *name, const char *argumen assert(c->modules); r = idxset_put(c->modules, m, &m->index); assert(r >= 0 && m->index != IDXSET_INVALID); + + fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument); + return m; fail: @@ -61,6 +64,9 @@ static void module_free(struct module *m) { m->done(m->core, m); lt_dlclose(m->dl); + + fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name); + free(m->name); free(m->argument); free(m); diff --git a/src/outputstream.c b/src/outputstream.c deleted file mode 100644 index c3f68a0e..00000000 --- a/src/outputstream.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include - -#include "outputstream.h" - -struct output_stream* output_stream_new(struct source *s, struct sample_spec *spec, const char *name) { - struct output_stream *o; - int r; - assert(s && spec); - - o = malloc(sizeof(struct output_stream)); - assert(o); - o->name = name ? strdup(name) : NULL; - o->source = s; - o->spec = *spec; - o->kill = NULL; - o->kill_userdata = NULL; - - o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1); - assert(o->memblockq); - - assert(s->core); - r = idxset_put(s->core->output_streams, o, &o->index); - assert(r == 0 && o->index != IDXSET_INVALID); - r = idxset_put(s->output_streams, o, NULL); - assert(r == 0); - - return o; -} - -void output_stream_free(struct output_stream* o) { - assert(o); - - memblockq_free(o->memblockq); - - assert(o->source && o->source->core); - idxset_remove_by_data(o->source->core->output_streams, o, NULL); - idxset_remove_by_data(o->source->output_streams, o, NULL); - - free(o->name); - free(o); -} - -void output_stream_set_kill_callback(struct output_stream *i, void (*kill)(struct output_stream*i, void *userdata), void *userdata) { - assert(i && kill); - i->kill = kill; - i->kill_userdata = userdata; -} - - -void output_stream_kill(struct output_stream*i) { - assert(i); - - if (i->kill) - i->kill(i, i->kill_userdata); -} diff --git a/src/outputstream.h b/src/outputstream.h deleted file mode 100644 index c6c0a717..00000000 --- a/src/outputstream.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef foooutputstreamhfoo -#define foooutputstreamhfoo - -#include -#include "source.h" -#include "sample.h" -#include "memblockq.h" - -struct output_stream { - char *name; - uint32_t index; - - struct source *source; - struct sample_spec spec; - - struct memblockq *memblockq; - void (*kill)(struct output_stream* i, void *userdata); - void *kill_userdata; -}; - -struct output_stream* output_stream_new(struct source *s, struct sample_spec *spec, const char *name); -void output_stream_free(struct output_stream* o); - -void output_stream_set_kill_callback(struct output_stream *i, void (*kill)(struct output_stream*i, void *userdata), void *userdata); -void output_stream_kill(struct output_stream*i); - -#endif diff --git a/src/protocol-simple.c b/src/protocol-simple.c index 1c462b39..d34a5d02 100644 --- a/src/protocol-simple.c +++ b/src/protocol-simple.c @@ -78,13 +78,15 @@ static int do_read(struct connection *c) { chunk.memblock = memblock_new(BUFSIZE); assert(chunk.memblock); + + memblock_stamp(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; diff --git a/src/sample.c b/src/sample.c index 2e46eac7..6a000228 100644 --- a/src/sample.c +++ b/src/sample.c @@ -33,24 +33,6 @@ struct memblock *silence(struct memblock* b, struct sample_spec *spec) { return b; } -void add_clip(struct memchunk *target, struct memchunk *chunk, struct sample_spec *spec) { - int16_t *p, *d; - size_t i; - assert(target && target->memblock && chunk && chunk->memblock && spec); - assert(spec->format == SAMPLE_S16NE); - assert((target->length & 1) == 0); - - d = target->memblock->data + target->index; - p = chunk->memblock->data + chunk->index; - - for (i = 0; i < target->length && i < chunk->length; i++) { - int32_t r = (int32_t) *d + (int32_t) *p; - if (r < -0x8000) r = 0x8000; - if (r > 0x7FFF) r = 0x7FFF; - *d = (int16_t) r; - } -} - size_t sample_size(struct sample_spec *spec) { assert(spec); size_t b = 1; @@ -78,3 +60,38 @@ size_t bytes_per_second(struct sample_spec *spec) { return spec->rate*sample_size(spec); } +size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct sample_spec *spec) { + unsigned c, d; + assert(chunks && target && spec); + assert(spec->format == SAMPLE_S16NE); + + for (d = 0;; d += sizeof(int16_t)) { + int32_t sum = 0; + + if (d >= length) + return d; + + for (c = 0; c < nchannels; c++) { + int32_t v; + uint8_t volume = channels[c].volume; + + if (d >= channels[c].chunk.length) + return d; + + if (volume == 0) + v = 0; + else { + v = *((int16_t*) (channels[c].chunk->memblock->data + channels[c].chunk->index + d)); + + if (volume != 0xFF) + v = v*volume/0xFF; + } + + sum += v; + } + + if (sum < -0x8000) sum = -0x8000; + if (sum > 0x7FFF) sum = 0x7FFF; + *(data++) = sum; + } +} diff --git a/src/sample.h b/src/sample.h index ecbe33f2..651788ba 100644 --- a/src/sample.h +++ b/src/sample.h @@ -27,7 +27,15 @@ struct sample_spec { extern struct sample_spec default_sample_spec; struct memblock *silence(struct memblock* b, struct sample_spec *spec); -void add_clip(struct memchunk *target, struct memchunk *chunk, struct sample_spec *spec); + + +struct mix_info { + struct memchunk chunk; + uint8_t volume; + void *userdata; +}; + +size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct sample_spec *spec) { size_t bytes_per_second(struct sample_spec *spec); size_t sample_size(struct sample_spec *spec); diff --git a/src/sink.c b/src/sink.c index f2d5373d..82cde8f2 100644 --- a/src/sink.c +++ b/src/sink.c @@ -21,7 +21,7 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s s->core = core; s->sample_spec = *spec; - s->input_streams = idxset_new(NULL, NULL); + s->inputs = idxset_new(NULL, NULL); if (name) { n = malloc(strlen(name)+9); @@ -36,198 +36,153 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s s->notify = NULL; s->notify_userdata = NULL; + fprintf(stderr, "sink: created %u \"%s\".\n", s->index, s->name); + return s; } void sink_free(struct sink *s) { - struct input_stream *i, *j = NULL; + struct sink_input *i, *j = NULL; assert(s); - while ((i = idxset_first(s->input_streams, NULL))) { - assert(i != j); - input_stream_kill(i); + while ((i = idxset_first(s->inputs, NULL))) { + assert(i != j && i->kill); + i->kill(i); j = i; } - idxset_free(s->input_streams, NULL, NULL); + + idxset_free(s->inputs, NULL, NULL); idxset_remove_by_data(s->core->sinks, s, NULL); source_free(s->monitor_source); + fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name); + free(s->name); free(s); } -struct pass1_info { - size_t maxlength; - unsigned count; - struct input_stream *last_input_stream; -}; - -static int get_max_length(void *p, uint32_t index, int *del, void*userdata) { - struct memchunk chunk; - struct pass1_info *info = userdata; - struct input_stream*i = p; - assert(info && i); - - if (memblockq_peek(i->memblockq, &chunk) != 0) - return 0; - - assert(chunk.length); - - if (info->maxlength > chunk.length) - info->maxlength = chunk.length; - - info->count++; - info->last_input_stream = i; - - memblock_unref(chunk.memblock); +void sink_notify(struct sink*s) { + assert(s); - return 0; + if (s->notify) + s->notify(s, s->notify_userdata); } -struct pass2_info { - struct memchunk *chunk; - struct sample_spec *spec; -}; - -static int do_mix(void *p, uint32_t index, int *del, void*userdata) { - struct memchunk chunk; - struct pass2_info *info = userdata; - struct input_stream*i = p; - assert(info && info->chunk && info->chunk->memblock && i && info->spec); - - if (memblockq_peek(i->memblockq, &chunk) != 0) - return 0; - - memblock_assert_exclusive(info->chunk->memblock); - 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); +void sink_set_notify_callback(struct sink *s, void (*notify_callback)(struct sink*sink, void *userdata), void *userdata) { + assert(s && notify_callback); - input_stream_notify(i); - return 0; + s->notify = notify_callback; + s->notify_userdata = userdata; } -int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) { - struct pass1_info pass1_info; - struct pass2_info pass2_info; - assert(s && target && result); - memblock_assert_exclusive(target); - - /* Calculate how many bytes to mix */ - pass1_info.maxlength = target->length; - pass1_info.count = 0; - - idxset_foreach(s->input_streams, get_max_length, &pass1_info); - assert(pass1_info.maxlength); - - /* No data to mix */ - if (pass1_info.count == 0) - return -1; +static unsigned fill_mix_info(struct sink *s, struct mix_info *info, unsigned maxinfo) { + uint32_t index = IDXSET_ANY; + struct sink_input *i; + unsigned n; - /* A shortcut if only a single input stream is connected */ - if (pass1_info.count == 1) { - struct input_stream *i = pass1_info.last_input_stream; - struct memchunk chunk; - size_t l; - - assert(i); - - if (memblockq_peek(i->memblockq, &chunk) != 0) - return -1; + assert(s && info); - l = target->length < chunk.length ? target->length : chunk.length; - memcpy(target->data, result->memblock+result->index, l); - target->length = l; - memblock_unref(chunk.memblock); - memblockq_drop(i->memblockq, l); + while (maxinfo > 0 && i = idxset_rrobin(s->inputs, &index)) { + assert(i->peek); + if (i->peek(i, &info->chunk, &info->volume) < 0) + continue; - input_stream_notify(i); + assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length); + info->userdata = i; - result->memblock = target; - result->length = l; - result->index = 0; - return 0; + info++; + maxinfo--; + n++; } - /* Do the real mixing */ - result->memblock = silence(target, &s->sample_spec); - result->index = 0; - result->length = pass1_info.maxlength; - pass2_info.chunk = result; - pass2_info.spec = &s->sample_spec; - idxset_foreach(s->input_streams, do_mix, &pass2_info); + return n; +} - assert(s->monitor_source); - source_post(s->monitor_source, result); +static void inputs_drop(struct sink *s, struct mix_info *info, unsigned maxinfo, size_t length) { + assert(s && info); - return 0; + for (; maxinfo > 0; maxinfo--, info++) { + struct sink_input *i = info->userdata; + assert(i && info->chunk.memblock); + + memblock_unref(info->chunk.memblock); + assert(i->drop); + i->drop(i, length); + } } int sink_render(struct sink*s, size_t length, struct memchunk *result) { - struct pass1_info pass1_info; - struct pass2_info pass2_info; - assert(s && result); - - if (!length) - length = (size_t) -1; + struct mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + size_t l; + assert(s && length && result); - /* Calculate how many bytes to mix */ - pass1_info.maxlength = length; - pass1_info.count = 0; - - idxset_foreach(s->input_streams, get_max_length, &pass1_info); - assert(pass1_info.maxlength); + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - /* No data to mix */ - if (pass1_info.count == 0) + if (n <= 0) return -1; - if (pass1_info.count == 1) { - struct input_stream *i = pass1_info.last_input_stream; - size_t l; + if (n == 1) { + struct sink_info *i = info[0].userdata; + assert(i && b); + *result = info[0].chunk; + memblock_ref(result->memblock); - assert(i); + if (result->length > length) + result->length = length; - if (memblockq_peek(i->memblockq, result) != 0) - return -1; + l = result->length; + } else { + result->memblock = memblock_new(length); + assert(result->memblock); - l = length < result->length ? length : result->length; - result->length = l; - memblockq_drop(i->memblockq, l); - input_stream_notify(i); + result->length = l = mix_chunks(info, n, result->memblock->data, length, &s->sample_spec); + result->index = 0; - return 0; + assert(l); } - /* Do the mixing */ - result->memblock = silence(memblock_new(result->length), &s->sample_spec); - result->index = 0; - result->length = pass1_info.maxlength; - pass2_info.chunk = result; - pass2_info.spec = &s->sample_spec; - idxset_foreach(s->input_streams, do_mix, &pass2_info); - - assert(s->monitor_source); - - source_post(s->monitor_source, result); + inputs_drop(s, info, n, l); return 0; } -void sink_notify(struct sink*s) { - assert(s); +int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) { + struct mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + size_t l; + assert(s && target && target->length && target->data && result); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - if (s->notify) - s->notify(s, s->notify_userdata); -} + if (n <= 0) + return -1; -void sink_set_notify_callback(struct sink *s, void (*notify_callback)(struct sink*sink, void *userdata), void *userdata) { - assert(s && notify_callback); + if (n == 1) { + struct sink_info *i = info[0].userdata; + assert(i && b); - s->notify = notify_callback; - s->notify_userdata = userdata; -} + l = target->length; + if (l > info[0].chunk.length) + l = info[0].chunk.length; + + result->memblock = target; + memcpy(target->data, info[0].chunk.memblock->data + info[0].chunk.index, l); + result->length = target->length = l; + result->index = 0; + if (result->length > length) + result->length = length; + l = result->length; + } else { + + result->memblock = target; + result->length = l = mix_chunks(info, n, target->data, target->length, &s->sample_spec); + result->index = 0; + assert(l); + } + + inputs_drop(s, info, n, l); + return 0; +} diff --git a/src/sink.h b/src/sink.h index 1678fc75..bd43d49d 100644 --- a/src/sink.h +++ b/src/sink.h @@ -10,13 +10,24 @@ struct sink; #include "idxset.h" #include "source.h" + +struct sink_input { + int (*peek) (struct sink_input *i, struct memchunk *chunk, uint8_t *volume); + void (*drop) (struct sink_input *i, size_t length); + void (*kill) (struct sink_input *i); + + void *userdata; + int index; + struct sink *sink; +}; + struct sink { char *name; uint32_t index; struct core *core; struct sample_spec sample_spec; - struct idxset *input_streams; + struct idxset *inputs; struct source *monitor_source; diff --git a/src/sinkinput.c b/src/sinkinput.c new file mode 100644 index 00000000..6bc841ac --- /dev/null +++ b/src/sinkinput.c @@ -0,0 +1,82 @@ +#include +#include +#include + +#include "inputstream.h" + +struct input_stream* input_stream_new(struct sink *s, struct sample_spec *spec, const char *name) { + struct input_stream *i; + int r; + assert(s && spec); + + i = malloc(sizeof(struct input_stream)); + assert(i); + 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)/2, sample_size(spec), (size_t) -1); + assert(i->memblockq); + + assert(s->core); + r = idxset_put(s->core->input_streams, i, &i->index); + assert(r == 0 && i->index != IDXSET_INVALID); + r = idxset_put(s->input_streams, i, NULL); + assert(r == 0); + + return i; +} + +void input_stream_free(struct input_stream* i) { + assert(i); + + memblockq_free(i->memblockq); + + assert(i->sink && i->sink->core); + idxset_remove_by_data(i->sink->core->input_streams, i, NULL); + idxset_remove_by_data(i->sink->input_streams, i, NULL); + + free(i->name); + free(i); +} + +void input_stream_notify_sink(struct input_stream *i) { + assert(i); + + if (!memblockq_is_readable(i->memblockq)) + return; + + sink_notify(i->sink); +} + +void input_stream_set_kill_callback(struct input_stream *i, void (*kill)(struct input_stream*i, void *userdata), void *userdata) { + assert(i && kill); + i->kill = kill; + i->kill_userdata = userdata; +} + + +void input_stream_kill(struct input_stream*i) { + assert(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/sinkinput.h b/src/sinkinput.h new file mode 100644 index 00000000..544c3318 --- /dev/null +++ b/src/sinkinput.h @@ -0,0 +1,50 @@ +#ifndef fooinputstreamhfoo +#define fooinputstreamhfoo + +#include + +#include "sink.h" +#include "sample.h" +#include "memblockq.h" + +struct input_stream { + char *name; + uint32_t index; + + struct sink *sink; + struct sample_spec spec; + + struct memblockq *memblockq; + + 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); +void input_stream_free(struct input_stream* i); + +/* This function notifies the attached sink that new data is available + * in the memblockq */ +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 *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 *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/source.c b/src/source.c index 2d5e9bbd..a7fc9a64 100644 --- a/src/source.c +++ b/src/source.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -24,6 +25,8 @@ struct source* source_new(struct core *core, const char *name, const struct samp s->link_change_callback = NULL; s->userdata = NULL; + fprintf(stderr, "source: created %u \"%s\"\n", s->index, s->name); + return s; } @@ -40,6 +43,8 @@ void source_free(struct source *s) { idxset_remove_by_data(s->core->sources, s, NULL); + fprintf(stderr, "source: freed %u \"%s\"\n", s->index, s->name); + free(s->name); free(s); } diff --git a/src/sourceoutput.c b/src/sourceoutput.c new file mode 100644 index 00000000..c3f68a0e --- /dev/null +++ b/src/sourceoutput.c @@ -0,0 +1,57 @@ +#include +#include +#include + +#include "outputstream.h" + +struct output_stream* output_stream_new(struct source *s, struct sample_spec *spec, const char *name) { + struct output_stream *o; + int r; + assert(s && spec); + + o = malloc(sizeof(struct output_stream)); + assert(o); + o->name = name ? strdup(name) : NULL; + o->source = s; + o->spec = *spec; + o->kill = NULL; + o->kill_userdata = NULL; + + o->memblockq = memblockq_new(bytes_per_second(spec)*5, sample_size(spec), (size_t) -1); + assert(o->memblockq); + + assert(s->core); + r = idxset_put(s->core->output_streams, o, &o->index); + assert(r == 0 && o->index != IDXSET_INVALID); + r = idxset_put(s->output_streams, o, NULL); + assert(r == 0); + + return o; +} + +void output_stream_free(struct output_stream* o) { + assert(o); + + memblockq_free(o->memblockq); + + assert(o->source && o->source->core); + idxset_remove_by_data(o->source->core->output_streams, o, NULL); + idxset_remove_by_data(o->source->output_streams, o, NULL); + + free(o->name); + free(o); +} + +void output_stream_set_kill_callback(struct output_stream *i, void (*kill)(struct output_stream*i, void *userdata), void *userdata) { + assert(i && kill); + i->kill = kill; + i->kill_userdata = userdata; +} + + +void output_stream_kill(struct output_stream*i) { + assert(i); + + if (i->kill) + i->kill(i, i->kill_userdata); +} diff --git a/src/sourceoutput.h b/src/sourceoutput.h new file mode 100644 index 00000000..c6c0a717 --- /dev/null +++ b/src/sourceoutput.h @@ -0,0 +1,27 @@ +#ifndef foooutputstreamhfoo +#define foooutputstreamhfoo + +#include +#include "source.h" +#include "sample.h" +#include "memblockq.h" + +struct output_stream { + char *name; + uint32_t index; + + struct source *source; + struct sample_spec spec; + + struct memblockq *memblockq; + void (*kill)(struct output_stream* i, void *userdata); + void *kill_userdata; +}; + +struct output_stream* output_stream_new(struct source *s, struct sample_spec *spec, const char *name); +void output_stream_free(struct output_stream* o); + +void output_stream_set_kill_callback(struct output_stream *i, void (*kill)(struct output_stream*i, void *userdata), void *userdata); +void output_stream_kill(struct output_stream*i); + +#endif -- cgit