From 9cb0b933e260008c6a03e24a4a149f726b8d86b2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Jun 2004 23:54:24 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@3 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/sink.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/sink.c (limited to 'src/sink.c') diff --git a/src/sink.c b/src/sink.c new file mode 100644 index 00000000..ac387c78 --- /dev/null +++ b/src/sink.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +#include "sink.h" +#include "inputstream.h" + +struct sink* sink_new(struct core *core, const char *name, const struct sample_spec *spec) { + struct sink *s; + char *n = NULL; + int r; + assert(core && spec); + + s = malloc(sizeof(struct sink)); + assert(s); + + s->name = name ? strdup(name) : NULL; + r = idxset_put(core->sinks, s, &s->index); + assert(s->index != IDXSET_INVALID && r >= 0); + + s->core = core; + s->sample_spec = *spec; + s->input_streams = idxset_new(NULL, NULL); + + if (name) { + n = malloc(strlen(name)+9); + sprintf(n, "%s_monitor", name); + } + + s->monitor_source = source_new(core, n, spec); + s->volume = 0xFF; + + s->notify_callback = NULL; + s->userdata = NULL; + + return s; +} + +void sink_free(struct sink *s) { + struct input_stream *i; + assert(s); + + idxset_remove_by_data(s->core->sinks, s, NULL); + source_free(s->monitor_source); + + while ((i = idxset_rrobin(s->input_streams, NULL))) + input_stream_free(i); + + 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; + + return 0; +} + +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); + return 0; +} + +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; + + /* 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; + + 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); + + result->memblock = target; + result->length = l; + result->index = 0; + return 0; + } + + /* 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); + + assert(s->monitor_source); + source_post(s->monitor_source, result); + + return 0; +} + +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; + + /* 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); + + /* No data to mix */ + if (pass1_info.count == 0) + return -1; + + if (pass1_info.count == 1) { + struct input_stream *i = pass1_info.last_input_stream; + size_t l; + + assert(i); + + if (memblockq_peek(i->memblockq, result) != 0) + return -1; + + l = length < result->length ? length : result->length; + result->length = l; + memblockq_drop(i->memblockq, l); + return 0; + } + + /* 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); + return 0; +} + +void sink_notify(struct sink*s) { + assert(s); + + if (s->notify_callback) + s->notify_callback(s, s->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; +} + + -- cgit