From f44ba092651aa75055e109e04b4164ea92ae7fdc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Jun 2006 21:53:48 +0000 Subject: big s/polyp/pulse/g git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1033 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 src/pulsecore/sink.c (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c new file mode 100644 index 00000000..ee6850f0 --- /dev/null +++ b/src/pulsecore/sink.c @@ -0,0 +1,513 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sink.h" + +#define MAX_MIX_CHANNELS 32 + +#define CHECK_VALIDITY_RETURN_NULL(condition) \ +do {\ +if (!(condition)) \ + return NULL; \ +} while (0) + +pa_sink* pa_sink_new( + pa_core *core, + const char *driver, + const char *name, + int fail, + const pa_sample_spec *spec, + const pa_channel_map *map) { + + pa_sink *s; + char *n = NULL; + char st[256]; + int r; + pa_channel_map tmap; + + assert(core); + assert(name); + assert(spec); + + CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); + + if (!map) + map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); + + CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); + CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); + CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); + CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); + + s = pa_xnew(pa_sink, 1); + + if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { + pa_xfree(s); + return NULL; + } + + s->ref = 1; + s->core = core; + s->state = PA_SINK_RUNNING; + s->name = pa_xstrdup(name); + s->description = NULL; + s->driver = pa_xstrdup(driver); + s->owner = NULL; + + s->sample_spec = *spec; + s->channel_map = *map; + + s->inputs = pa_idxset_new(NULL, NULL); + + pa_cvolume_reset(&s->sw_volume, spec->channels); + pa_cvolume_reset(&s->hw_volume, spec->channels); + s->sw_muted = 0; + s->hw_muted = 0; + + s->get_latency = NULL; + s->notify = NULL; + s->set_hw_volume = NULL; + s->get_hw_volume = NULL; + s->set_hw_mute = NULL; + s->get_hw_mute = NULL; + s->userdata = NULL; + + r = pa_idxset_put(core->sinks, s, &s->index); + assert(s->index != PA_IDXSET_INVALID && r >= 0); + + pa_sample_spec_snprint(st, sizeof(st), spec); + pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + + n = pa_sprintf_malloc("%s_monitor", name); + s->monitor_source = pa_source_new(core, driver, n, 0, spec, map); + assert(s->monitor_source); + pa_xfree(n); + s->monitor_source->monitor_of = s; + s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name); + + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); + + return s; +} + +void pa_sink_disconnect(pa_sink* s) { + pa_sink_input *i, *j = NULL; + + assert(s); + assert(s->state == PA_SINK_RUNNING); + + pa_namereg_unregister(s->core, s->name); + + while ((i = pa_idxset_first(s->inputs, NULL))) { + assert(i != j); + pa_sink_input_kill(i); + j = i; + } + + pa_source_disconnect(s->monitor_source); + + pa_idxset_remove_by_data(s->core->sinks, s, NULL); + + s->get_latency = NULL; + s->notify = NULL; + s->get_hw_volume = NULL; + s->set_hw_volume = NULL; + s->set_hw_mute = NULL; + s->get_hw_mute = NULL; + + s->state = PA_SINK_DISCONNECTED; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +} + +static void sink_free(pa_sink *s) { + assert(s); + assert(!s->ref); + + if (s->state != PA_SINK_DISCONNECTED) + pa_sink_disconnect(s); + + pa_log_info(__FILE__": freed %u \"%s\"", s->index, s->name); + + pa_source_unref(s->monitor_source); + s->monitor_source = NULL; + + pa_idxset_free(s->inputs, NULL, NULL); + + pa_xfree(s->name); + pa_xfree(s->description); + pa_xfree(s->driver); + pa_xfree(s); +} + +void pa_sink_unref(pa_sink*s) { + assert(s); + assert(s->ref >= 1); + + if (!(--s->ref)) + sink_free(s); +} + +pa_sink* pa_sink_ref(pa_sink *s) { + assert(s); + assert(s->ref >= 1); + + s->ref++; + return s; +} + +void pa_sink_notify(pa_sink*s) { + assert(s); + assert(s->ref >= 1); + + if (s->notify) + s->notify(s); +} + +static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { + uint32_t idx = PA_IDXSET_INVALID; + pa_sink_input *i; + unsigned n = 0; + + assert(s); + assert(s->ref >= 1); + assert(info); + + for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { + /* Increase ref counter, to make sure that this input doesn't + * vanish while we still need it */ + pa_sink_input_ref(i); + + if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { + pa_sink_input_unref(i); + continue; + } + + info->userdata = i; + + assert(info->chunk.memblock); + assert(info->chunk.memblock->data); + assert(info->chunk.length); + + info++; + maxinfo--; + n++; + } + + return n; +} + +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { + assert(s); + assert(s->ref >= 1); + assert(info); + + for (; maxinfo > 0; maxinfo--, info++) { + pa_sink_input *i = info->userdata; + + assert(i); + assert(info->chunk.memblock); + + /* Drop read data */ + pa_sink_input_drop(i, &info->chunk, length); + pa_memblock_unref(info->chunk.memblock); + + /* Decrease ref counter */ + pa_sink_input_unref(i); + info->userdata = NULL; + } +} + +int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { + pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + int r = -1; + + assert(s); + assert(s->ref >= 1); + assert(length); + assert(result); + + pa_sink_ref(s); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + goto finish; + + if (n == 1) { + pa_cvolume volume; + + *result = info[0].chunk; + pa_memblock_ref(result->memblock); + + if (result->length > length) + result->length = length; + + pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + + if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { + pa_memchunk_make_writable(result, s->core->memblock_stat, 0); + if (s->sw_muted) + pa_silence_memchunk(result, &s->sample_spec); + else + pa_volume_memchunk(result, &s->sample_spec, &volume); + } + } else { + result->memblock = pa_memblock_new(length, s->core->memblock_stat); + assert(result->memblock); + +/* pa_log("mixing %i", n); */ + + result->length = pa_mix(info, n, result->memblock->data, length, + &s->sample_spec, &s->sw_volume, s->sw_muted); + result->index = 0; + } + + inputs_drop(s, info, n, result->length); + pa_source_post(s->monitor_source, result); + + r = 0; + +finish: + pa_sink_unref(s); + + return r; +} + +int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + int r = -1; + + assert(s); + assert(s->ref >= 1); + assert(target); + assert(target->memblock); + assert(target->length); + assert(target->memblock->data); + + pa_sink_ref(s); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + goto finish; + + if (n == 1) { + pa_cvolume volume; + + if (target->length > info[0].chunk.length) + target->length = info[0].chunk.length; + + memcpy((uint8_t*) target->memblock->data + target->index, + (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, + target->length); + + pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + + if (s->sw_muted) + pa_silence_memchunk(target, &s->sample_spec); + else if (!pa_cvolume_is_norm(&volume)) + pa_volume_memchunk(target, &s->sample_spec, &volume); + } else + target->length = pa_mix(info, n, + (uint8_t*) target->memblock->data + target->index, + target->length, + &s->sample_spec, + &s->sw_volume, + s->sw_muted); + + inputs_drop(s, info, n, target->length); + pa_source_post(s->monitor_source, target); + + r = 0; + +finish: + pa_sink_unref(s); + + return r; +} + +void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { + pa_memchunk chunk; + size_t l, d; + + assert(s); + assert(s->ref >= 1); + assert(target); + assert(target->memblock); + assert(target->length); + assert(target->memblock->data); + + pa_sink_ref(s); + + l = target->length; + d = 0; + while (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + + if (pa_sink_render_into(s, &chunk) < 0) + break; + + d += chunk.length; + l -= chunk.length; + } + + if (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + pa_silence_memchunk(&chunk, &s->sample_spec); + } + + pa_sink_unref(s); +} + +void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { + assert(s); + assert(s->ref >= 1); + assert(length); + assert(result); + + /*** This needs optimization ***/ + + result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat); + result->index = 0; + + pa_sink_render_into_full(s, result); +} + +pa_usec_t pa_sink_get_latency(pa_sink *s) { + assert(s); + assert(s->ref >= 1); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} + +void pa_sink_set_owner(pa_sink *s, pa_module *m) { + assert(s); + assert(s->ref >= 1); + + s->owner = m; + + if (s->monitor_source) + pa_source_set_owner(s->monitor_source, m); +} + +void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { + pa_cvolume *v; + + assert(s); + assert(s->ref >= 1); + assert(volume); + + if (m == PA_MIXER_HARDWARE && s->set_hw_volume) + v = &s->hw_volume; + else + v = &s->sw_volume; + + if (pa_cvolume_equal(v, volume)) + return; + + *v = *volume; + + if (v == &s->hw_volume) + if (s->set_hw_volume(s) < 0) + s->sw_volume = *volume; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { + assert(s); + assert(s->ref >= 1); + + if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { + + if (s->get_hw_volume) + s->get_hw_volume(s); + + return &s->hw_volume; + } else + return &s->sw_volume; +} + +void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) { + int *t; + + assert(s); + assert(s->ref >= 1); + + if (m == PA_MIXER_HARDWARE && s->set_hw_mute) + t = &s->hw_muted; + else + t = &s->sw_muted; + + if (!!*t == !!mute) + return; + + *t = !!mute; + + if (t == &s->hw_muted) + if (s->set_hw_mute(s) < 0) + s->sw_muted = !!mute; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { + assert(s); + assert(s->ref >= 1); + + if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { + + if (s->get_hw_mute) + s->get_hw_mute(s); + + return s->hw_muted; + } else + return s->sw_muted; +} -- cgit From 6e38949039cca052ee0d167b92f895e4d95ee30d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Jul 2006 17:26:55 +0000 Subject: add a new boolean variable is_hardware to pa_sink/pa_source to denote wether the specific device is a hardware device or virtual/software git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1090 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index ee6850f0..8acb7715 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -102,6 +102,8 @@ pa_sink* pa_sink_new( s->sw_muted = 0; s->hw_muted = 0; + s->is_hardware = 0; + s->get_latency = NULL; s->notify = NULL; s->set_hw_volume = NULL; -- cgit From c90dd53268bec8516be3b37e8af1989290f022a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Aug 2006 17:53:34 +0000 Subject: * introduce new functions pa_sink_set_description() and pa_source_set_description() for changing the description of a sink/source * allow sinks without monitor sources attached git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1203 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 58 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 10 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 8acb7715..eecf89cc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -118,12 +118,19 @@ pa_sink* pa_sink_new( pa_sample_spec_snprint(st, sizeof(st), spec); pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); - n = pa_sprintf_malloc("%s_monitor", name); - s->monitor_source = pa_source_new(core, driver, n, 0, spec, map); - assert(s->monitor_source); + n = pa_sprintf_malloc("%s.monitor", name); + + if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) + pa_log_warn(__FILE__": failed to create monitor source."); + else { + char *d; + s->monitor_source->monitor_of = s; + d = pa_sprintf_malloc("Monitor Source of %s", s->name); + pa_source_set_description(s->monitor_source, d); + pa_xfree(d); + } + pa_xfree(n); - s->monitor_source->monitor_of = s; - s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); @@ -144,7 +151,8 @@ void pa_sink_disconnect(pa_sink* s) { j = i; } - pa_source_disconnect(s->monitor_source); + if (s->monitor_source) + pa_source_disconnect(s->monitor_source); pa_idxset_remove_by_data(s->core->sinks, s, NULL); @@ -168,8 +176,10 @@ static void sink_free(pa_sink *s) { pa_log_info(__FILE__": freed %u \"%s\"", s->index, s->name); - pa_source_unref(s->monitor_source); - s->monitor_source = NULL; + if (s->monitor_source) { + pa_source_unref(s->monitor_source); + s->monitor_source = NULL; + } pa_idxset_free(s->inputs, NULL, NULL); @@ -304,7 +314,9 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { } inputs_drop(s, info, n, result->length); - pa_source_post(s->monitor_source, result); + + if (s->monitor_source) + pa_source_post(s->monitor_source, result); r = 0; @@ -358,7 +370,9 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { s->sw_muted); inputs_drop(s, info, n, target->length); - pa_source_post(s->monitor_source, target); + + if (s->monitor_source) + pa_source_post(s->monitor_source, target); r = 0; @@ -513,3 +527,27 @@ int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { } else return s->sw_muted; } + +void pa_sink_set_description(pa_sink *s, const char *description) { + assert(s); + assert(s->ref >= 1); + + if (!description && !s->description) + return; + + if (description && s->description && !strcmp(description, s->description)) + return; + + pa_xfree(s->description); + s->description = pa_xstrdup(description); + + if (s->monitor_source) { + char *n; + + n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name); + pa_source_set_description(s->monitor_source, n); + pa_xfree(n); + } + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} -- cgit From 3aba099fc3e515db5c4b1f2990c48fdb4160ff52 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 12 Aug 2006 02:19:36 +0000 Subject: clean up event generation a little: suppress unnecessary events and generate new ones on owner change git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1212 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index eecf89cc..d1d9785a 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -446,11 +446,16 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { void pa_sink_set_owner(pa_sink *s, pa_module *m) { assert(s); assert(s->ref >= 1); - + + if (s->owner == m) + return; + s->owner = m; if (s->monitor_source) pa_source_set_owner(s->monitor_source, m); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { -- cgit From b5207fc9cac954d49132ff4e6760a60e4e6f2b51 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 12 Aug 2006 16:50:58 +0000 Subject: add pa_sink_used_by()/pa_source_used_by() git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1226 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index d1d9785a..e770950c 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -556,3 +556,17 @@ void pa_sink_set_description(pa_sink *s, const char *description) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } + +unsigned pa_sink_used_by(pa_sink *s) { + unsigned ret; + + assert(s); + assert(s->ref >= 1); + + ret = pa_idxset_size(s->inputs); + + if (s->monitor_source) + ret += pa_source_used_by(s->monitor_source); + + return ret; +} -- cgit From 818083289882e8739bbfaefd1edb252ed5629771 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 13 Aug 2006 17:33:32 +0000 Subject: properly implement a pa_sink_disconnect() hook git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1243 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index e770950c..557d5efc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -143,7 +143,10 @@ void pa_sink_disconnect(pa_sink* s) { assert(s); assert(s->state == PA_SINK_RUNNING); + s->state = PA_SINK_DISCONNECTED; pa_namereg_unregister(s->core, s->name); + + pa_hook_fire(&s->core->hook_sink_disconnect, s); while ((i = pa_idxset_first(s->inputs, NULL))) { assert(i != j); @@ -163,7 +166,6 @@ void pa_sink_disconnect(pa_sink* s) { s->set_hw_mute = NULL; s->get_hw_mute = NULL; - s->state = PA_SINK_DISCONNECTED; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); } -- cgit From 0e436a6926af56f37a74a03bb5e143e078ca0d55 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Aug 2006 19:55:18 +0000 Subject: Rework memory management to allow shared memory data transfer. The central idea is to allocate all audio memory blocks from a per-process memory pool which is available as read-only SHM segment to other local processes. Then, instead of writing the actual audio data to the socket just write references to this shared memory pool. To work optimally all memory blocks should now be of type PA_MEMBLOCK_POOL or PA_MEMBLOCK_POOL_EXTERNAL. The function pa_memblock_new() now generates memory blocks of this type by default. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1266 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 557d5efc..aacb89fd 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -298,14 +298,14 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { - pa_memchunk_make_writable(result, s->core->memblock_stat, 0); + pa_memchunk_make_writable(result, 0); if (s->sw_muted) pa_silence_memchunk(result, &s->sample_spec); else pa_volume_memchunk(result, &s->sample_spec, &volume); } } else { - result->memblock = pa_memblock_new(length, s->core->memblock_stat); + result->memblock = pa_memblock_new(s->core->mempool, length); assert(result->memblock); /* pa_log("mixing %i", n); */ @@ -429,7 +429,7 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { /*** This needs optimization ***/ - result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat); + result->memblock = pa_memblock_new(s->core->mempool, result->length = length); result->index = 0; pa_sink_render_into_full(s, result); -- cgit From e385d93e5aad6a6fce754c00c804ff1d6a6746d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Aug 2006 21:38:40 +0000 Subject: remove all occurences of pa_logXXX(__FILE__": and replace them by pa_logXXX(" git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1272 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index aacb89fd..05695254 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -116,12 +116,12 @@ pa_sink* pa_sink_new( assert(s->index != PA_IDXSET_INVALID && r >= 0); pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); n = pa_sprintf_malloc("%s.monitor", name); if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) - pa_log_warn(__FILE__": failed to create monitor source."); + pa_log_warn("failed to create monitor source."); else { char *d; s->monitor_source->monitor_of = s; @@ -176,7 +176,7 @@ static void sink_free(pa_sink *s) { if (s->state != PA_SINK_DISCONNECTED) pa_sink_disconnect(s); - pa_log_info(__FILE__": freed %u \"%s\"", s->index, s->name); + pa_log_info("freed %u \"%s\"", s->index, s->name); if (s->monitor_source) { pa_source_unref(s->monitor_source); -- cgit From d210ebbb09daddb2c8c8e8e77243e088b0b19c4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Sep 2006 23:50:56 +0000 Subject: rework memory block management to be thread-safe and mostly lock-free. pa_memblock is now an opaque structure. Access to its fields is now done through various accessor functions in a thread-safe manner. pa_memblock_acquire() and pa_memblock_release() are now used to access the attached audio data. Why? To allow safe manipulation of the memory pointer maintained by the memory block. Internally _acquire() and _release() maintain a reference counter. Please do not confuse this reference counter whith the one maintained by pa_memblock_ref()/_unref()! As a side effect this patch removes all direct usages of AO_t and replaces it with pa_atomic_xxx based code. This stuff needs some serious testing love. Especially if threads are actively used. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1404 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 05695254..04795e39 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -237,7 +237,6 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { info->userdata = i; assert(info->chunk.memblock); - assert(info->chunk.memblock->data); assert(info->chunk.length); info++; @@ -305,13 +304,16 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_volume_memchunk(result, &s->sample_spec, &volume); } } else { + void *ptr; result->memblock = pa_memblock_new(s->core->mempool, length); assert(result->memblock); /* pa_log("mixing %i", n); */ - result->length = pa_mix(info, n, result->memblock->data, length, - &s->sample_spec, &s->sw_volume, s->sw_muted); + ptr = pa_memblock_acquire(result->memblock); + result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->sw_volume, s->sw_muted); + pa_memblock_release(result->memblock); + result->index = 0; } @@ -332,13 +334,13 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; int r = -1; + void *ptr; assert(s); assert(s->ref >= 1); assert(target); assert(target->memblock); assert(target->length); - assert(target->memblock->data); pa_sink_ref(s); @@ -347,16 +349,23 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (n <= 0) goto finish; + ptr = pa_memblock_acquire(target->memblock); + if (n == 1) { + void *src; pa_cvolume volume; if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; + + src = pa_memblock_acquire(info[0].chunk.memblock); - memcpy((uint8_t*) target->memblock->data + target->index, - (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, + memcpy((uint8_t*) ptr + target->index, + (uint8_t*) src + info[0].chunk.index, target->length); + pa_memblock_release(info[0].chunk.memblock); + pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); if (s->sw_muted) @@ -365,11 +374,13 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_volume_memchunk(target, &s->sample_spec, &volume); } else target->length = pa_mix(info, n, - (uint8_t*) target->memblock->data + target->index, + (uint8_t*) ptr + target->index, target->length, &s->sample_spec, &s->sw_volume, s->sw_muted); + + pa_memblock_release(target->memblock); inputs_drop(s, info, n, target->length); @@ -393,7 +404,6 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { assert(target); assert(target->memblock); assert(target->length); - assert(target->memblock->data); pa_sink_ref(s); -- cgit From 8dc62142765249addf131b058c27f931ede1776b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 6 Nov 2006 13:06:01 +0000 Subject: Revert r1404 and keep it on a development branch until it is fully tested. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1409 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 04795e39..05695254 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -237,6 +237,7 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { info->userdata = i; assert(info->chunk.memblock); + assert(info->chunk.memblock->data); assert(info->chunk.length); info++; @@ -304,16 +305,13 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_volume_memchunk(result, &s->sample_spec, &volume); } } else { - void *ptr; result->memblock = pa_memblock_new(s->core->mempool, length); assert(result->memblock); /* pa_log("mixing %i", n); */ - ptr = pa_memblock_acquire(result->memblock); - result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->sw_volume, s->sw_muted); - pa_memblock_release(result->memblock); - + result->length = pa_mix(info, n, result->memblock->data, length, + &s->sample_spec, &s->sw_volume, s->sw_muted); result->index = 0; } @@ -334,13 +332,13 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; int r = -1; - void *ptr; assert(s); assert(s->ref >= 1); assert(target); assert(target->memblock); assert(target->length); + assert(target->memblock->data); pa_sink_ref(s); @@ -349,23 +347,16 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (n <= 0) goto finish; - ptr = pa_memblock_acquire(target->memblock); - if (n == 1) { - void *src; pa_cvolume volume; if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; - - src = pa_memblock_acquire(info[0].chunk.memblock); - memcpy((uint8_t*) ptr + target->index, - (uint8_t*) src + info[0].chunk.index, + memcpy((uint8_t*) target->memblock->data + target->index, + (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, target->length); - pa_memblock_release(info[0].chunk.memblock); - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); if (s->sw_muted) @@ -374,13 +365,11 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_volume_memchunk(target, &s->sample_spec, &volume); } else target->length = pa_mix(info, n, - (uint8_t*) ptr + target->index, + (uint8_t*) target->memblock->data + target->index, target->length, &s->sample_spec, &s->sw_volume, s->sw_muted); - - pa_memblock_release(target->memblock); inputs_drop(s, info, n, target->length); @@ -404,6 +393,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { assert(target); assert(target->memblock); assert(target->length); + assert(target->memblock->data); pa_sink_ref(s); -- cgit From 521daf6f0ac4fa6a2fbfb5d523c0c743342dca2b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 4 Jan 2007 13:43:45 +0000 Subject: Huge trailing whitespace cleanup. Let's keep the tree pure from here on, mmmkay? git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1418 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 98 ++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 05695254..cb0e54c1 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -2,17 +2,17 @@ /*** This file is part of PulseAudio. - + PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -56,7 +56,7 @@ pa_sink* pa_sink_new( int fail, const pa_sample_spec *spec, const pa_channel_map *map) { - + pa_sink *s; char *n = NULL; char st[256]; @@ -68,7 +68,7 @@ pa_sink* pa_sink_new( assert(spec); CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); - + if (!map) map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); @@ -76,7 +76,7 @@ pa_sink* pa_sink_new( CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); - + s = pa_xnew(pa_sink, 1); if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { @@ -94,7 +94,7 @@ pa_sink* pa_sink_new( s->sample_spec = *spec; s->channel_map = *map; - + s->inputs = pa_idxset_new(NULL, NULL); pa_cvolume_reset(&s->sw_volume, spec->channels); @@ -103,7 +103,7 @@ pa_sink* pa_sink_new( s->hw_muted = 0; s->is_hardware = 0; - + s->get_latency = NULL; s->notify = NULL; s->set_hw_volume = NULL; @@ -114,12 +114,12 @@ pa_sink* pa_sink_new( r = pa_idxset_put(core->sinks, s, &s->index); assert(s->index != PA_IDXSET_INVALID && r >= 0); - + pa_sample_spec_snprint(st, sizeof(st), spec); pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); n = pa_sprintf_malloc("%s.monitor", name); - + if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) pa_log_warn("failed to create monitor source."); else { @@ -131,15 +131,15 @@ pa_sink* pa_sink_new( } pa_xfree(n); - + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); - + return s; } void pa_sink_disconnect(pa_sink* s) { pa_sink_input *i, *j = NULL; - + assert(s); assert(s->state == PA_SINK_RUNNING); @@ -147,7 +147,7 @@ void pa_sink_disconnect(pa_sink* s) { pa_namereg_unregister(s->core, s->name); pa_hook_fire(&s->core->hook_sink_disconnect, s); - + while ((i = pa_idxset_first(s->inputs, NULL))) { assert(i != j); pa_sink_input_kill(i); @@ -165,24 +165,24 @@ void pa_sink_disconnect(pa_sink* s) { s->set_hw_volume = NULL; s->set_hw_mute = NULL; s->get_hw_mute = NULL; - + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); } static void sink_free(pa_sink *s) { assert(s); assert(!s->ref); - + if (s->state != PA_SINK_DISCONNECTED) pa_sink_disconnect(s); - pa_log_info("freed %u \"%s\"", s->index, s->name); + pa_log_info("freed %u \"%s\"", s->index, s->name); if (s->monitor_source) { pa_source_unref(s->monitor_source); s->monitor_source = NULL; } - + pa_idxset_free(s->inputs, NULL, NULL); pa_xfree(s->name); @@ -202,7 +202,7 @@ void pa_sink_unref(pa_sink*s) { pa_sink* pa_sink_ref(pa_sink *s) { assert(s); assert(s->ref >= 1); - + s->ref++; return s; } @@ -219,7 +219,7 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { uint32_t idx = PA_IDXSET_INVALID; pa_sink_input *i; unsigned n = 0; - + assert(s); assert(s->ref >= 1); assert(info); @@ -235,11 +235,11 @@ static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { } info->userdata = i; - + assert(info->chunk.memblock); assert(info->chunk.memblock->data); assert(info->chunk.length); - + info++; maxinfo--; n++; @@ -255,7 +255,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t for (; maxinfo > 0; maxinfo--, info++) { pa_sink_input *i = info->userdata; - + assert(i); assert(info->chunk.memblock); @@ -268,19 +268,19 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t info->userdata = NULL; } } - + int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; int r = -1; - + assert(s); assert(s->ref >= 1); assert(length); assert(result); pa_sink_ref(s); - + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); if (n <= 0) @@ -296,7 +296,7 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { result->length = length; pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); - + if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { pa_memchunk_make_writable(result, 0); if (s->sw_muted) @@ -332,7 +332,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; int r = -1; - + assert(s); assert(s->ref >= 1); assert(target); @@ -341,7 +341,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { assert(target->memblock->data); pa_sink_ref(s); - + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); if (n <= 0) @@ -352,7 +352,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; - + memcpy((uint8_t*) target->memblock->data + target->index, (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, target->length); @@ -360,7 +360,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); if (s->sw_muted) - pa_silence_memchunk(target, &s->sample_spec); + pa_silence_memchunk(target, &s->sample_spec); else if (!pa_cvolume_is_norm(&volume)) pa_volume_memchunk(target, &s->sample_spec, &volume); } else @@ -370,7 +370,7 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { &s->sample_spec, &s->sw_volume, s->sw_muted); - + inputs_drop(s, info, n, target->length); if (s->monitor_source) @@ -380,14 +380,14 @@ int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { finish: pa_sink_unref(s); - + return r; } void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_memchunk chunk; size_t l, d; - + assert(s); assert(s->ref >= 1); assert(target); @@ -396,14 +396,14 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { assert(target->memblock->data); pa_sink_ref(s); - + l = target->length; d = 0; while (l > 0) { chunk = *target; chunk.index += d; chunk.length -= d; - + if (pa_sink_render_into(s, &chunk) < 0) break; @@ -428,7 +428,7 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { assert(result); /*** This needs optimization ***/ - + result->memblock = pa_memblock_new(s->core->mempool, result->length = length); result->index = 0; @@ -451,7 +451,7 @@ void pa_sink_set_owner(pa_sink *s, pa_module *m) { if (s->owner == m) return; - + s->owner = m; if (s->monitor_source) @@ -462,19 +462,19 @@ void pa_sink_set_owner(pa_sink *s, pa_module *m) { void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { pa_cvolume *v; - + assert(s); assert(s->ref >= 1); assert(volume); - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) + if (m == PA_MIXER_HARDWARE && s->set_hw_volume) v = &s->hw_volume; else v = &s->sw_volume; if (pa_cvolume_equal(v, volume)) return; - + *v = *volume; if (v == &s->hw_volume) @@ -492,7 +492,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { if (s->get_hw_volume) s->get_hw_volume(s); - + return &s->hw_volume; } else return &s->sw_volume; @@ -500,18 +500,18 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) { int *t; - + assert(s); assert(s->ref >= 1); - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) + if (m == PA_MIXER_HARDWARE && s->set_hw_mute) t = &s->hw_muted; else t = &s->sw_muted; if (!!*t == !!mute) return; - + *t = !!mute; if (t == &s->hw_muted) @@ -529,7 +529,7 @@ int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { if (s->get_hw_mute) s->get_hw_mute(s); - + return s->hw_muted; } else return s->sw_muted; @@ -544,18 +544,18 @@ void pa_sink_set_description(pa_sink *s, const char *description) { if (description && s->description && !strcmp(description, s->description)) return; - + pa_xfree(s->description); s->description = pa_xstrdup(description); if (s->monitor_source) { char *n; - + n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name); pa_source_set_description(s->monitor_source, n); pa_xfree(n); } - + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -- cgit From 06211b7c8fd329137ae9003818543912a87d9898 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 13 Feb 2007 15:35:19 +0000 Subject: Add copyright notices to all relevant files. (based on svn log) git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1426 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index cb0e54c1..9588c2c3 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -3,6 +3,9 @@ /*** This file is part of PulseAudio. + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, -- cgit From a67c21f093202f142438689d3f7cfbdf4ea82eea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 28 Oct 2007 19:13:50 +0000 Subject: merge 'lennart' branch back into trunk. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1971 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 984 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 734 insertions(+), 250 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 9588c2c3..dccb34cc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -27,7 +27,6 @@ #endif #include -#include #include #include @@ -41,16 +40,18 @@ #include #include #include +#include +#include #include "sink.h" #define MAX_MIX_CHANNELS 32 +#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE) +#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12) -#define CHECK_VALIDITY_RETURN_NULL(condition) \ -do {\ -if (!(condition)) \ - return NULL; \ -} while (0) +static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject); + +static void sink_free(pa_object *s); pa_sink* pa_sink_new( pa_core *core, @@ -63,68 +64,71 @@ pa_sink* pa_sink_new( pa_sink *s; char *n = NULL; char st[256]; - int r; pa_channel_map tmap; - assert(core); - assert(name); - assert(spec); + pa_assert(core); + pa_assert(name); + pa_assert(spec); - CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec)); + pa_return_null_if_fail(pa_sample_spec_valid(spec)); if (!map) map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); - CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map)); - CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels); - CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver)); - CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name); + pa_return_null_if_fail(map && pa_channel_map_valid(map)); + pa_return_null_if_fail(map->channels == spec->channels); + pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); + pa_return_null_if_fail(name && pa_utf8_valid(name) && *name); - s = pa_xnew(pa_sink, 1); + s = pa_msgobject_new(pa_sink); if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { pa_xfree(s); return NULL; } - s->ref = 1; + s->parent.parent.free = sink_free; + s->parent.process_msg = pa_sink_process_msg; + s->core = core; - s->state = PA_SINK_RUNNING; + s->state = PA_SINK_INIT; + s->flags = 0; s->name = pa_xstrdup(name); s->description = NULL; s->driver = pa_xstrdup(driver); - s->owner = NULL; + s->module = NULL; s->sample_spec = *spec; s->channel_map = *map; s->inputs = pa_idxset_new(NULL, NULL); + s->n_corked = 0; - pa_cvolume_reset(&s->sw_volume, spec->channels); - pa_cvolume_reset(&s->hw_volume, spec->channels); - s->sw_muted = 0; - s->hw_muted = 0; - - s->is_hardware = 0; + pa_cvolume_reset(&s->volume, spec->channels); + s->muted = FALSE; + s->refresh_volume = s->refresh_mute = FALSE; s->get_latency = NULL; - s->notify = NULL; - s->set_hw_volume = NULL; - s->get_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->set_volume = NULL; + s->get_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->set_state = NULL; s->userdata = NULL; - r = pa_idxset_put(core->sinks, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); + s->asyncmsgq = NULL; + s->rtpoll = NULL; + s->silence = NULL; + + pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); n = pa_sprintf_malloc("%s.monitor", name); if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) - pa_log_warn("failed to create monitor source."); + pa_log_warn("Failed to create monitor source."); else { char *d; s->monitor_source->monitor_of = s; @@ -135,51 +139,124 @@ pa_sink* pa_sink_new( pa_xfree(n); - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); + s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + s->thread_info.state = s->state; return s; } -void pa_sink_disconnect(pa_sink* s) { +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { + int ret; + + pa_assert(s); + + if (s->state == state) + return 0; + + if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || + (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) { + pa_sink_input *i; + uint32_t idx; + + /* We're suspending or resuming, tell everyone about it */ + + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) + if (i->suspend) + i->suspend(i, state == PA_SINK_SUSPENDED); + } + + if (s->set_state) + if ((ret = s->set_state(s, state)) < 0) + return -1; + + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) + return -1; + + s->state = state; + + if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */ + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s); + return 0; +} + +void pa_sink_put(pa_sink* s) { + pa_sink_assert_ref(s); + + pa_assert(s->state == PA_SINK_INIT); + pa_assert(s->asyncmsgq); + pa_assert(s->rtpoll); + + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); + + pa_source_put(s->monitor_source); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], s); +} + +void pa_sink_unlink(pa_sink* s) { + pa_bool_t linked; pa_sink_input *i, *j = NULL; - assert(s); - assert(s->state == PA_SINK_RUNNING); + pa_assert(s); + + /* Please note that pa_sink_unlink() does more than simply + * reversing pa_sink_put(). It also undoes the registrations + * already done in pa_sink_new()! */ - s->state = PA_SINK_DISCONNECTED; - pa_namereg_unregister(s->core, s->name); + /* All operations here shall be idempotent, i.e. pa_sink_unlink() + * may be called multiple times on the same sink without bad + * effects. */ - pa_hook_fire(&s->core->hook_sink_disconnect, s); + linked = PA_SINK_LINKED(s->state); + + if (linked) + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); + + if (s->state != PA_SINK_UNLINKED) + pa_namereg_unregister(s->core, s->name); + pa_idxset_remove_by_data(s->core->sinks, s, NULL); while ((i = pa_idxset_first(s->inputs, NULL))) { - assert(i != j); + pa_assert(i != j); pa_sink_input_kill(i); j = i; } - if (s->monitor_source) - pa_source_disconnect(s->monitor_source); - - pa_idxset_remove_by_data(s->core->sinks, s, NULL); + if (linked) + sink_set_state(s, PA_SINK_UNLINKED); + else + s->state = PA_SINK_UNLINKED; s->get_latency = NULL; - s->notify = NULL; - s->get_hw_volume = NULL; - s->set_hw_volume = NULL; - s->set_hw_mute = NULL; - s->get_hw_mute = NULL; + s->get_volume = NULL; + s->set_volume = NULL; + s->set_mute = NULL; + s->get_mute = NULL; + s->set_state = NULL; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); + if (s->monitor_source) + pa_source_unlink(s->monitor_source); + + if (linked) { + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s); + } } -static void sink_free(pa_sink *s) { - assert(s); - assert(!s->ref); +static void sink_free(pa_object *o) { + pa_sink *s = PA_SINK(o); + pa_sink_input *i; + + pa_assert(s); + pa_assert(pa_sink_refcnt(s) == 0); - if (s->state != PA_SINK_DISCONNECTED) - pa_sink_disconnect(s); + if (PA_SINK_LINKED(s->state)) + pa_sink_unlink(s); - pa_log_info("freed %u \"%s\"", s->index, s->name); + pa_log_info("Freeing sink %u \"%s\"", s->index, s->name); if (s->monitor_source) { pa_source_unref(s->monitor_source); @@ -188,108 +265,192 @@ static void sink_free(pa_sink *s) { pa_idxset_free(s->inputs, NULL, NULL); + while ((i = pa_hashmap_steal_first(s->thread_info.inputs))) + pa_sink_input_unref(i); + + pa_hashmap_free(s->thread_info.inputs, NULL, NULL); + + if (s->silence) + pa_memblock_unref(s->silence); + pa_xfree(s->name); pa_xfree(s->description); pa_xfree(s->driver); pa_xfree(s); } -void pa_sink_unref(pa_sink*s) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { + pa_sink_assert_ref(s); + pa_assert(q); + + s->asyncmsgq = q; - if (!(--s->ref)) - sink_free(s); + if (s->monitor_source) + pa_source_set_asyncmsgq(s->monitor_source, q); } -pa_sink* pa_sink_ref(pa_sink *s) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { + pa_sink_assert_ref(s); + pa_assert(p); - s->ref++; - return s; + s->rtpoll = p; + if (s->monitor_source) + pa_source_set_rtpoll(s->monitor_source, p); +} + +int pa_sink_update_status(pa_sink*s) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + if (s->state == PA_SINK_SUSPENDED) + return 0; + + return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); +} + +int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + if (suspend) + return sink_set_state(s, PA_SINK_SUSPENDED); + else + return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); } -void pa_sink_notify(pa_sink*s) { - assert(s); - assert(s->ref >= 1); +void pa_sink_ping(pa_sink *s) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); - if (s->notify) - s->notify(s); + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL); } -static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { - uint32_t idx = PA_IDXSET_INVALID; +static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) { pa_sink_input *i; unsigned n = 0; + void *state = NULL; - assert(s); - assert(s->ref >= 1); - assert(info); + pa_sink_assert_ref(s); + pa_assert(info); - for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { - /* Increase ref counter, to make sure that this input doesn't - * vanish while we still need it */ - pa_sink_input_ref(i); + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) { + pa_sink_input_assert_ref(i); - if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { - pa_sink_input_unref(i); + if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0) continue; - } - info->userdata = i; + info->userdata = pa_sink_input_ref(i); - assert(info->chunk.memblock); - assert(info->chunk.memblock->data); - assert(info->chunk.length); + pa_assert(info->chunk.memblock); + pa_assert(info->chunk.length > 0); info++; - maxinfo--; n++; + maxinfo--; } return n; } -static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { - assert(s); - assert(s->ref >= 1); - assert(info); +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, size_t length) { + pa_sink_input *i; + void *state = NULL; + unsigned p = 0; + unsigned n_unreffed = 0; + + pa_sink_assert_ref(s); + + /* We optimize for the case where the order of the inputs has not changed */ + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { + unsigned j; + pa_mix_info* m; + + pa_sink_input_assert_ref(i); - for (; maxinfo > 0; maxinfo--, info++) { - pa_sink_input *i = info->userdata; + m = NULL; - assert(i); - assert(info->chunk.memblock); + /* Let's try to find the matching entry info the pa_mix_info array */ + for (j = 0; j < n; j ++) { + + if (info[p].userdata == i) { + m = info + p; + break; + } + + p++; + if (p >= n) + p = 0; + } /* Drop read data */ - pa_sink_input_drop(i, &info->chunk, length); - pa_memblock_unref(info->chunk.memblock); + pa_sink_input_drop(i, length); - /* Decrease ref counter */ - pa_sink_input_unref(i); - info->userdata = NULL; + if (m) { + pa_sink_input_unref(m->userdata); + m->userdata = NULL; + if (m->chunk.memblock) + pa_memblock_unref(m->chunk.memblock); + pa_memchunk_reset(&m->chunk); + + n_unreffed += 1; + } + } + + /* Now drop references to entries that are included in the + * pa_mix_info array but don't exist anymore */ + + if (n_unreffed < n) { + for (; n > 0; info++, n--) { + if (info->userdata) + pa_sink_input_unref(info->userdata); + if (info->chunk.memblock) + pa_memblock_unref(info->chunk.memblock); + } } } -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { +void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; - int r = -1; + size_t block_size_max; - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(pa_frame_aligned(length, &s->sample_spec)); + pa_assert(result); pa_sink_ref(s); - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + if (length <= 0) + length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec); + + block_size_max = pa_mempool_block_size_max(s->core->mempool); + if (length > block_size_max) + length = pa_frame_align(block_size_max, &s->sample_spec); + + pa_assert(length > 0); + + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, length, info, MAX_MIX_CHANNELS) : 0; + + if (n == 0) { + + if (length > SILENCE_BUFFER_LENGTH) + length = pa_frame_align(SILENCE_BUFFER_LENGTH, &s->sample_spec); - if (n <= 0) - goto finish; + pa_assert(length > 0); - if (n == 1) { + if (!s->silence || pa_memblock_get_length(s->silence) < length) { + if (s->silence) + pa_memblock_unref(s->silence); + s->silence = pa_silence_memblock_new(s->core->mempool, &s->sample_spec, length); + } + + result->memblock = pa_memblock_ref(s->silence); + result->length = length; + result->index = 0; + + } else if (n == 1) { pa_cvolume volume; *result = info[0].chunk; @@ -298,105 +459,112 @@ int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { if (result->length > length) result->length = length; - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); - if (s->sw_muted || !pa_cvolume_is_norm(&volume)) { + if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) { pa_memchunk_make_writable(result, 0); - if (s->sw_muted) + if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) pa_silence_memchunk(result, &s->sample_spec); else pa_volume_memchunk(result, &s->sample_spec, &volume); } } else { + void *ptr; result->memblock = pa_memblock_new(s->core->mempool, length); - assert(result->memblock); -/* pa_log("mixing %i", n); */ + ptr = pa_memblock_acquire(result->memblock); + result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted); + pa_memblock_release(result->memblock); - result->length = pa_mix(info, n, result->memblock->data, length, - &s->sample_spec, &s->sw_volume, s->sw_muted); result->index = 0; } - inputs_drop(s, info, n, result->length); + if (s->thread_info.state == PA_SINK_RUNNING) + inputs_drop(s, info, n, result->length); - if (s->monitor_source) + if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) pa_source_post(s->monitor_source, result); - r = 0; - -finish: pa_sink_unref(s); - - return r; } -int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { +void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; - int r = -1; - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(target); + pa_assert(target->memblock); + pa_assert(target->length > 0); + pa_assert(pa_frame_aligned(target->length, &s->sample_spec)); pa_sink_ref(s); - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - - if (n <= 0) - goto finish; - - if (n == 1) { - pa_cvolume volume; + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0; + if (n == 0) { + pa_silence_memchunk(target, &s->sample_spec); + } else if (n == 1) { if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; - memcpy((uint8_t*) target->memblock->data + target->index, - (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, - target->length); + if (s->thread_info.soft_muted) + pa_silence_memchunk(target, &s->sample_spec); + else { + void *src, *ptr; + pa_cvolume volume; - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + ptr = pa_memblock_acquire(target->memblock); + src = pa_memblock_acquire(info[0].chunk.memblock); + + memcpy((uint8_t*) ptr + target->index, + (uint8_t*) src + info[0].chunk.index, + target->length); + + pa_memblock_release(target->memblock); + pa_memblock_release(info[0].chunk.memblock); + + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); + + if (!pa_cvolume_is_norm(&volume)) + pa_volume_memchunk(target, &s->sample_spec, &volume); + } + + } else { + void *ptr; + + ptr = pa_memblock_acquire(target->memblock); - if (s->sw_muted) - pa_silence_memchunk(target, &s->sample_spec); - else if (!pa_cvolume_is_norm(&volume)) - pa_volume_memchunk(target, &s->sample_spec, &volume); - } else target->length = pa_mix(info, n, - (uint8_t*) target->memblock->data + target->index, + (uint8_t*) ptr + target->index, target->length, &s->sample_spec, - &s->sw_volume, - s->sw_muted); + &s->thread_info.soft_volume, + s->thread_info.soft_muted); - inputs_drop(s, info, n, target->length); + pa_memblock_release(target->memblock); + } - if (s->monitor_source) - pa_source_post(s->monitor_source, target); + if (s->thread_info.state == PA_SINK_RUNNING) + inputs_drop(s, info, n, target->length); - r = 0; + if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + pa_source_post(s->monitor_source, target); -finish: pa_sink_unref(s); - - return r; } void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_memchunk chunk; size_t l, d; - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(target); + pa_assert(target->memblock); + pa_assert(target->length > 0); + pa_assert(pa_frame_aligned(target->length, &s->sample_spec)); pa_sink_ref(s); @@ -407,140 +575,177 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { chunk.index += d; chunk.length -= d; - if (pa_sink_render_into(s, &chunk) < 0) - break; + pa_sink_render_into(s, &chunk); d += chunk.length; l -= chunk.length; } - if (l > 0) { - chunk = *target; - chunk.index += d; - chunk.length -= d; - pa_silence_memchunk(&chunk, &s->sample_spec); - } - pa_sink_unref(s); } void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(length > 0); + pa_assert(pa_frame_aligned(length, &s->sample_spec)); + pa_assert(result); /*** This needs optimization ***/ - result->memblock = pa_memblock_new(s->core->mempool, result->length = length); result->index = 0; + result->length = length; + result->memblock = pa_memblock_new(s->core->mempool, length); pa_sink_render_into_full(s, result); } +void pa_sink_skip(pa_sink *s, size_t length) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(length > 0); + pa_assert(pa_frame_aligned(length, &s->sample_spec)); + + if (pa_source_used_by(s->monitor_source)) { + pa_memchunk chunk; + + /* If something is connected to our monitor source, we have to + * pass valid data to it */ + + while (length > 0) { + pa_sink_render(s, length, &chunk); + pa_memblock_unref(chunk.memblock); + + pa_assert(chunk.length <= length); + length -= chunk.length; + } + + } else { + /* Ok, noone cares about the rendered data, so let's not even render it */ + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { + pa_sink_input_assert_ref(i); + pa_sink_input_drop(i, length); + } + } +} + pa_usec_t pa_sink_get_latency(pa_sink *s) { - assert(s); - assert(s->ref >= 1); + pa_usec_t usec = 0; - if (!s->get_latency) + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + if (!PA_SINK_OPENED(s->state)) + return 0; + + if (s->get_latency) + return s->get_latency(s); + + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) return 0; - return s->get_latency(s); + return usec; } -void pa_sink_set_owner(pa_sink *s, pa_module *m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { + int changed; - if (s->owner == m) - return; + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(volume); - s->owner = m; + changed = !pa_cvolume_equal(volume, &s->volume); + s->volume = *volume; - if (s->monitor_source) - pa_source_set_owner(s->monitor_source, m); + if (s->set_volume && s->set_volume(s) < 0) + s->set_volume = NULL; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (!s->set_volume) + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, volume, 1), 0, NULL, pa_xfree); + + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { - pa_cvolume *v; +const pa_cvolume *pa_sink_get_volume(pa_sink *s) { + struct pa_cvolume old_volume; - assert(s); - assert(s->ref >= 1); - assert(volume); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) - v = &s->hw_volume; - else - v = &s->sw_volume; + old_volume = s->volume; - if (pa_cvolume_equal(v, volume)) - return; + if (s->get_volume && s->get_volume(s) < 0) + s->get_volume = NULL; - *v = *volume; + if (!s->get_volume && s->refresh_volume) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); - if (v == &s->hw_volume) - if (s->set_hw_volume(s) < 0) - s->sw_volume = *volume; + if (!pa_cvolume_equal(&old_volume, &s->volume)) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + return &s->volume; } -const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { + int changed; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { + changed = s->muted != mute; + s->muted = mute; - if (s->get_hw_volume) - s->get_hw_volume(s); + if (s->set_mute && s->set_mute(s) < 0) + s->set_mute = NULL; - return &s->hw_volume; - } else - return &s->sw_volume; + if (!s->set_mute) + pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); + + if (changed) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) { - int *t; +pa_bool_t pa_sink_get_mute(pa_sink *s) { + pa_bool_t old_muted; - assert(s); - assert(s->ref >= 1); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) - t = &s->hw_muted; - else - t = &s->sw_muted; + old_muted = s->muted; - if (!!*t == !!mute) - return; + if (s->get_mute && s->get_mute(s) < 0) + s->get_mute = NULL; - *t = !!mute; + if (!s->get_mute && s->refresh_mute) + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); - if (t == &s->hw_muted) - if (s->set_hw_mute(s) < 0) - s->sw_muted = !!mute; + if (old_muted != s->muted) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + return s->muted; } -int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); +void pa_sink_set_module(pa_sink *s, pa_module *m) { + pa_sink_assert_ref(s); - if (m == PA_MIXER_HARDWARE && s->set_hw_mute) { + if (s->module == m) + return; + + s->module = m; - if (s->get_hw_mute) - s->get_hw_mute(s); + if (s->monitor_source) + pa_source_set_module(s->monitor_source, m); - return s->hw_muted; - } else - return s->sw_muted; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_sink_set_description(pa_sink *s, const char *description) { - assert(s); - assert(s->ref >= 1); + pa_sink_assert_ref(s); if (!description && !s->description) return; @@ -559,19 +764,298 @@ void pa_sink_set_description(pa_sink *s, const char *description) { pa_xfree(n); } - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + if (PA_SINK_LINKED(s->state)) { + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], s); + } } -unsigned pa_sink_used_by(pa_sink *s) { +unsigned pa_sink_linked_by(pa_sink *s) { unsigned ret; - assert(s); - assert(s->ref >= 1); + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); ret = pa_idxset_size(s->inputs); + /* We add in the number of streams connected to us here. Please + * not the asymmmetry to pa_sink_used_by()! */ + if (s->monitor_source) - ret += pa_source_used_by(s->monitor_source); + ret += pa_source_linked_by(s->monitor_source); return ret; } + +unsigned pa_sink_used_by(pa_sink *s) { + unsigned ret; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + ret = pa_idxset_size(s->inputs); + pa_assert(ret >= s->n_corked); + ret -= s->n_corked; + + /* Streams connected to our monitor source do not matter for + * pa_sink_used_by()!.*/ + + return ret; +} + +int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { + pa_sink *s = PA_SINK(o); + pa_sink_assert_ref(s); + pa_assert(s->thread_info.state != PA_SINK_UNLINKED); + + switch ((pa_sink_message_t) code) { + + case PA_SINK_MESSAGE_ADD_INPUT: { + pa_sink_input *i = PA_SINK_INPUT(userdata); + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); + + /* Since the caller sleeps in pa_sink_input_put(), we can + * safely access data outside of thread_info even though + * it is mutable */ + + if ((i->thread_info.sync_prev = i->sync_prev)) { + pa_assert(i->sink == i->thread_info.sync_prev->sink); + pa_assert(i->sync_prev->sync_next == i); + i->thread_info.sync_prev->thread_info.sync_next = i; + } + + if ((i->thread_info.sync_next = i->sync_next)) { + pa_assert(i->sink == i->thread_info.sync_next->sink); + pa_assert(i->sync_next->sync_prev == i); + i->thread_info.sync_next->thread_info.sync_prev = i; + } + + pa_assert(!i->thread_info.attached); + i->thread_info.attached = TRUE; + + if (i->attach) + i->attach(i); + + /* If you change anything here, make sure to change the + * ghost sink input handling a few lines down at + * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + + return 0; + } + + case PA_SINK_MESSAGE_REMOVE_INPUT: { + pa_sink_input *i = PA_SINK_INPUT(userdata); + + /* If you change anything here, make sure to change the + * sink input handling a few lines down at + * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + + if (i->detach) + i->detach(i); + + pa_assert(i->thread_info.attached); + i->thread_info.attached = FALSE; + + /* Since the caller sleeps in pa_sink_input_unlink(), + * we can safely access data outside of thread_info even + * though it is mutable */ + + pa_assert(!i->thread_info.sync_prev); + pa_assert(!i->thread_info.sync_next); + + if (i->thread_info.sync_prev) { + i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next; + i->thread_info.sync_prev = NULL; + } + + if (i->thread_info.sync_next) { + i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev; + i->thread_info.sync_next = NULL; + } + + if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index))) + pa_sink_input_unref(i); + + return 0; + } + + case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { + pa_sink_input_move_info *info = userdata; + int volume_is_norm; + + /* We don't support moving synchronized streams. */ + pa_assert(!info->sink_input->sync_prev); + pa_assert(!info->sink_input->sync_next); + pa_assert(!info->sink_input->thread_info.sync_next); + pa_assert(!info->sink_input->thread_info.sync_prev); + + if (info->sink_input->detach) + info->sink_input->detach(info->sink_input); + + pa_assert(info->sink_input->thread_info.attached); + info->sink_input->thread_info.attached = FALSE; + + if (info->ghost_sink_input) { + pa_assert(info->buffer_bytes > 0); + pa_assert(info->buffer); + + volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume); + + pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes); + + while (info->buffer_bytes > 0) { + pa_memchunk memchunk; + pa_cvolume volume; + size_t n; + + if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0) + break; + + n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length; + pa_sink_input_drop(info->sink_input, n); + memchunk.length = n; + + if (!volume_is_norm) { + pa_memchunk_make_writable(&memchunk, 0); + pa_volume_memchunk(&memchunk, &s->sample_spec, &volume); + } + + if (pa_memblockq_push(info->buffer, &memchunk) < 0) { + pa_memblock_unref(memchunk.memblock); + break; + } + + pa_memblock_unref(memchunk.memblock); + info->buffer_bytes -= n; + } + + /* Add the remaining already resampled chunk to the buffer */ + if (info->sink_input->thread_info.resampled_chunk.memblock) + pa_memblockq_push(info->buffer, &info->sink_input->thread_info.resampled_chunk); + + pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer); + + pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer)); + } + + /* Let's remove the sink input ...*/ + if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(info->sink_input->index))) + pa_sink_input_unref(info->sink_input); + + /* .. and add the ghost sink input instead */ + if (info->ghost_sink_input) { + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input)); + info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL; + + pa_assert(!info->ghost_sink_input->thread_info.attached); + info->ghost_sink_input->thread_info.attached = TRUE; + + if (info->ghost_sink_input->attach) + info->ghost_sink_input->attach(info->ghost_sink_input); + } + + return 0; + } + + case PA_SINK_MESSAGE_SET_VOLUME: + s->thread_info.soft_volume = *((pa_cvolume*) userdata); + return 0; + + case PA_SINK_MESSAGE_SET_MUTE: + s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + return 0; + + case PA_SINK_MESSAGE_GET_VOLUME: + *((pa_cvolume*) userdata) = s->thread_info.soft_volume; + return 0; + + case PA_SINK_MESSAGE_GET_MUTE: + *((pa_bool_t*) userdata) = s->thread_info.soft_muted; + return 0; + + case PA_SINK_MESSAGE_PING: + return 0; + + case PA_SINK_MESSAGE_SET_STATE: + + s->thread_info.state = PA_PTR_TO_UINT(userdata); + return 0; + + case PA_SINK_MESSAGE_DETACH: + + /* We're detaching all our input streams so that the + * asyncmsgq and rtpoll fields can be changed without + * problems */ + pa_sink_detach_within_thread(s); + break; + + case PA_SINK_MESSAGE_ATTACH: + + /* Reattach all streams */ + pa_sink_attach_within_thread(s); + break; + + case PA_SINK_MESSAGE_GET_LATENCY: + case PA_SINK_MESSAGE_MAX: + ; + } + + return -1; +} + +int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { + pa_sink *sink; + uint32_t idx; + int ret = 0; + + pa_core_assert_ref(c); + + for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) + ret -= pa_sink_suspend(sink, suspend) < 0; + + return ret; +} + +void pa_sink_detach(pa_sink *s) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL); +} + +void pa_sink_attach(pa_sink *s) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL); +} + +void pa_sink_detach_within_thread(pa_sink *s) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->thread_info.state)); + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + if (i->detach) + i->detach(i); + + if (s->monitor_source) + pa_source_detach_within_thread(s->monitor_source); +} + +void pa_sink_attach_within_thread(pa_sink *s) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->thread_info.state)); + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + if (i->attach) + i->attach(i); + + if (s->monitor_source) + pa_source_attach_within_thread(s->monitor_source); +} -- cgit From 14a9b80afbb0bddc216462b72156f14e032e1b5e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Nov 2007 01:30:40 +0000 Subject: - Check process name when dealing with PID files - Add new PA_STREAM_FIX_CHANNELS, FIX_RATE, FIX_FORMAT, DONT_MOVE, VARIABLE_RATES to pa_sream_flags_t adn implement it - Expose those flags in pacat - Add notifications about device suspend/resume to the protocol and expose them in libpulse - Allow changing of buffer_attr during playback - allow disabling for remixing globally - hookup polkit support git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2067 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index dccb34cc..fcc91cb1 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -149,23 +149,16 @@ pa_sink* pa_sink_new( static int sink_set_state(pa_sink *s, pa_sink_state_t state) { int ret; + pa_bool_t suspend_change; pa_assert(s); if (s->state == state) return 0; - if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || - (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) { - pa_sink_input *i; - uint32_t idx; - - /* We're suspending or resuming, tell everyone about it */ - - for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) - if (i->suspend) - i->suspend(i, state == PA_SINK_SUSPENDED); - } + suspend_change = + (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || + (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED); if (s->set_state) if ((ret = s->set_state(s, state)) < 0) @@ -176,8 +169,20 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { s->state = state; + if (suspend_change) { + pa_sink_input *i; + uint32_t idx; + + /* We're suspending or resuming, tell everyone about it */ + + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) + if (i->suspend) + i->suspend(i, state == PA_SINK_SUSPENDED); + } + if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s); + return 0; } -- cgit From 86b9ef8c961bed9d3a65f044741bb423c26d8005 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Feb 2008 22:13:44 +0000 Subject: deal with a possibly failing pa_channel_map_init_auto() correctly git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2105 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulsecore/sink.c') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index fcc91cb1..9adb6097 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -73,7 +73,7 @@ pa_sink* pa_sink_new( pa_return_null_if_fail(pa_sample_spec_valid(spec)); if (!map) - map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT); + pa_return_null_if_fail((map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT))); pa_return_null_if_fail(map && pa_channel_map_valid(map)); pa_return_null_if_fail(map->channels == spec->channels); -- cgit