diff options
Diffstat (limited to 'src/pulsecore')
64 files changed, 3475 insertions, 1358 deletions
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index f64931a5..67f661fe 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -163,14 +163,14 @@ static int push(pa_asyncq*l, void *p, pa_bool_t wait) { return 0; } -static pa_bool_t flush_postq(pa_asyncq *l) { +static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait) { struct localq *q; pa_assert(l); while ((q = l->last_localq)) { - if (push(l, q->data, FALSE) < 0) + if (push(l, q->data, wait) < 0) return FALSE; l->last_localq = q->prev; @@ -187,7 +187,7 @@ static pa_bool_t flush_postq(pa_asyncq *l) { int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) { pa_assert(l); - if (!flush_postq(l)) + if (!flush_postq(l, wait)) return -1; return push(l, p, wait); @@ -199,13 +199,15 @@ void pa_asyncq_post(pa_asyncq*l, void *p) { pa_assert(l); pa_assert(p); - if (pa_asyncq_push(l, p, FALSE) >= 0) - return; + if (flush_postq(l, FALSE)) + if (pa_asyncq_push(l, p, FALSE) >= 0) + return; /* OK, we couldn't push anything in the queue. So let's queue it * locally and push it later */ - pa_log("q overrun, queuing locally"); + if (pa_log_ratelimit()) + pa_log_warn("q overrun, queuing locally"); if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq)))) q = pa_xnew(struct localq, 1); @@ -299,7 +301,7 @@ void pa_asyncq_write_before_poll(pa_asyncq *l) { for (;;) { - if (flush_postq(l)) + if (flush_postq(l, FALSE)) break; if (pa_fdsem_before_poll(l->read_fdsem) >= 0) { diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index 9c58c661..6e33a0e6 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -107,6 +107,79 @@ static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, v return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p); } +#elif defined(__NetBSD__) && defined(HAVE_SYS_ATOMIC_H) + +/* NetBSD 5.0+ atomic_ops(3) implementation */ + +#include <sys/atomic.h> + +typedef struct pa_atomic { + volatile unsigned int value; +} pa_atomic_t; + +#define PA_ATOMIC_INIT(v) { .value = (unsigned int) (v) } + +static inline int pa_atomic_load(const pa_atomic_t *a) { + membar_sync(); + return (int) a->value; +} + +static inline void pa_atomic_store(pa_atomic_t *a, int i) { + a->value = (unsigned int) i; + membar_sync(); +} + +/* Returns the previously set value */ +static inline int pa_atomic_add(pa_atomic_t *a, int i) { + int nv = (int) atomic_add_int_nv(&a->value, i); + return nv - i; +} + +/* Returns the previously set value */ +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { + int nv = (int) atomic_add_int_nv(&a->value, -i); + return nv + i; +} + +/* Returns the previously set value */ +static inline int pa_atomic_inc(pa_atomic_t *a) { + int nv = (int) atomic_inc_uint_nv(&a->value); + return nv - 1; +} + +/* Returns the previously set value */ +static inline int pa_atomic_dec(pa_atomic_t *a) { + int nv = (int) atomic_dec_uint_nv(&a->value); + return nv + 1; +} + +/* Returns TRUE when the operation was successful. */ +static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { + unsigned int r = atomic_cas_uint(&a->value, (unsigned int) old_i, (unsigned int) new_i); + return (int) r == old_i; +} + +typedef struct pa_atomic_ptr { + volatile void *value; +} pa_atomic_ptr_t; + +#define PA_ATOMIC_PTR_INIT(v) { .value = (v) } + +static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { + membar_sync(); + return (void *) a->value; +} + +static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { + a->value = p; + membar_sync(); +} + +static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { + void *r = atomic_cas_ptr(&a->value, old_p, new_p); + return r == old_p; +} + #elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__)) #warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC." diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c deleted file mode 100644 index 8c84cee5..00000000 --- a/src/pulsecore/autoload.c +++ /dev/null @@ -1,202 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - Copyright 2006 Pierre Ossman <ossman@cendio.se> 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, - 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 <config.h> -#endif - -#include <stdlib.h> -#include <string.h> - -#include <pulse/xmalloc.h> - -#include <pulsecore/module.h> -#include <pulsecore/memchunk.h> -#include <pulsecore/sound-file.h> -#include <pulsecore/log.h> -#include <pulsecore/macro.h> -#include <pulsecore/core-scache.h> -#include <pulsecore/core-subscribe.h> - -#include "autoload.h" - -static void entry_free(pa_autoload_entry *e) { - pa_assert(e); - pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX); - pa_xfree(e->name); - pa_xfree(e->module); - pa_xfree(e->argument); - pa_xfree(e); -} - -static void entry_remove_and_free(pa_autoload_entry *e) { - pa_assert(e); - pa_assert(e->core); - - pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); - pa_hashmap_remove(e->core->autoload_hashmap, e->name); - entry_free(e); -} - -static pa_autoload_entry* entry_new(pa_core *c, const char *name) { - pa_autoload_entry *e = NULL; - - pa_core_assert_ref(c); - pa_assert(name); - - if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name))) - return NULL; - - e = pa_xnew(pa_autoload_entry, 1); - e->core = c; - e->name = pa_xstrdup(name); - e->module = e->argument = NULL; - e->in_action = 0; - - if (!c->autoload_hashmap) - c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - pa_assert(c->autoload_hashmap); - - pa_hashmap_put(c->autoload_hashmap, e->name, e); - - if (!c->autoload_idxset) - c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - pa_idxset_put(c->autoload_idxset, e, &e->index); - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index); - - return e; -} - -int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) { - pa_autoload_entry *e = NULL; - - pa_assert(c); - pa_assert(name); - pa_assert(module); - pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE); - - if (!(e = entry_new(c, name))) - return -1; - - e->module = pa_xstrdup(module); - e->argument = pa_xstrdup(argument); - e->type = type; - - if (idx) - *idx = e->index; - - return 0; -} - -int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { - pa_autoload_entry *e; - - pa_assert(c); - pa_assert(name); - pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) - return -1; - - entry_remove_and_free(e); - return 0; -} - -int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) { - pa_autoload_entry *e; - - pa_assert(c); - pa_assert(idx != PA_IDXSET_INVALID); - - if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) - return -1; - - entry_remove_and_free(e); - return 0; -} - -void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) { - pa_autoload_entry *e; - pa_module *m; - - pa_assert(c); - pa_assert(name); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type)) - return; - - if (e->in_action) - return; - - e->in_action = 1; - - if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) { - if ((m = pa_module_load(c, e->module, e->argument))) - m->auto_unload = 1; - } - - e->in_action = 0; -} - -static void free_func(void *p, void *userdata) { - pa_autoload_entry *e = p; - pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); - entry_free(e); -} - -void pa_autoload_free(pa_core *c) { - - if (c->autoload_hashmap) { - pa_hashmap_free(c->autoload_hashmap, free_func, NULL); - c->autoload_hashmap = NULL; - } - - if (c->autoload_idxset) { - pa_idxset_free(c->autoload_idxset, NULL, NULL); - c->autoload_idxset = NULL; - } -} - -const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { - pa_autoload_entry *e; - - pa_core_assert_ref(c); - pa_assert(name); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) - return NULL; - - return e; -} - -const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) { - pa_autoload_entry *e; - - pa_core_assert_ref(c); - pa_assert(idx != PA_IDXSET_INVALID); - - if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) - return NULL; - - return e; -} diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h deleted file mode 100644 index 3926351f..00000000 --- a/src/pulsecore/autoload.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef fooautoloadhfoo -#define fooautoloadhfoo - -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - 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. -***/ - -#include <pulsecore/namereg.h> - -/* Using the autoloading facility, modules by be loaded on-demand and - * synchronously. The user may register a "ghost sink" or "ghost - * source". Whenever this sink/source is requested but not available a - * specified module is loaded. */ - -/* An autoload entry, or "ghost" sink/source */ -typedef struct pa_autoload_entry { - pa_core *core; - uint32_t index; - char *name; - pa_namereg_type_t type; /* Type of the autoload entry */ - int in_action; /* The module is currently being loaded */ - char *module, *argument; -} pa_autoload_entry; - -/* Add a new autoload entry of the given time, with the speicified - * sink/source name, module name and argument. Return the entry's - * index in *index */ -int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx); - -/* Free all autoload entries */ -void pa_autoload_free(pa_core *c); -int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type); -int pa_autoload_remove_by_index(pa_core *c, uint32_t idx); - -/* Request an autoload entry by its name, effectively causing a module to be loaded */ -void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type); - -const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type); -const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx); - -#endif diff --git a/src/pulsecore/bitset.c b/src/pulsecore/bitset.c new file mode 100644 index 00000000..4beeb1cc --- /dev/null +++ b/src/pulsecore/bitset.c @@ -0,0 +1,67 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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 <config.h> +#endif + +#include <string.h> + +#include <pulse/xmalloc.h> + +#include "bitset.h" + +void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v) { + pa_assert(b); + + if (v) + b[k >> 5] |= 1 << (k & 31); + else + b[k >> 5] &= ~((uint32_t) (1 << (k & 31))); +} + +pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k) { + return !!(b[k >> 5] & (1 << (k & 31))); +} + +pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) { + va_list ap; + pa_bitset_t *a; + pa_bool_t equal; + + a = pa_xnew0(pa_bitset_t, PA_BITSET_ELEMENTS(n)); + + va_start(ap, n); + for (;;) { + int j = va_arg(ap, int); + + if (j < 0) + break; + + pa_bitset_set(a, j, TRUE); + } + va_end(ap); + + equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0; + pa_xfree(a); + + return equal; +} diff --git a/src/pulsecore/bitset.h b/src/pulsecore/bitset.h new file mode 100644 index 00000000..95f5cfce --- /dev/null +++ b/src/pulsecore/bitset.h @@ -0,0 +1,37 @@ +#ifndef foopulsecorebitsethfoo +#define foopulsecorebitsethfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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. +***/ + +#include <inttypes.h> +#include <pulsecore/macro.h> + +#define PA_BITSET_ELEMENTS(n) (((n)+31)/32) +#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4) + +typedef uint32_t pa_bitset_t; + +void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v); +pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k); +pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...); + +#endif diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c new file mode 100644 index 00000000..8e1ba536 --- /dev/null +++ b/src/pulsecore/card.c @@ -0,0 +1,254 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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 <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <pulse/xmalloc.h> +#include <pulse/util.h> + +#include <pulsecore/log.h> +#include <pulsecore/macro.h> +#include <pulsecore/core-util.h> +#include <pulsecore/namereg.h> + +#include "card.h" + +pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) { + pa_card_profile *c; + + pa_assert(name); + + c = pa_xmalloc(PA_ALIGN(sizeof(pa_card_profile)) + extra); + c->name = pa_xstrdup(name); + c->description = pa_xstrdup(description); + + c->priority = 0; + c->n_sinks = c->n_sources = 0; + c->max_sink_channels = c->max_source_channels = 0; + + return c; +} + +void pa_card_profile_free(pa_card_profile *c) { + pa_assert(c); + + pa_xfree(c->name); + pa_xfree(c->description); + pa_xfree(c); +} + +pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) { + pa_assert(data); + + pa_xfree(data->name); + data->name = pa_xstrdup(name); +} + +void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) { + pa_assert(data); + + pa_xfree(data->active_profile); + data->active_profile = pa_xstrdup(profile); +} + +void pa_card_new_data_done(pa_card_new_data *data) { + + pa_assert(data); + + pa_proplist_free(data->proplist); + + if (data->profiles) { + pa_card_profile *c; + + while ((c = pa_hashmap_steal_first(data->profiles))) + pa_card_profile_free(c); + + pa_hashmap_free(data->profiles, NULL, NULL); + } + + pa_xfree(data->name); + pa_xfree(data->active_profile); +} + +pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { + pa_card *c; + const char *name; + + pa_core_assert_ref(core); + pa_assert(data); + pa_assert(data->name); + + c = pa_xnew(pa_card, 1); + + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) { + pa_xfree(c); + return NULL; + } + + pa_card_new_data_set_name(data, name); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data) < 0) { + pa_xfree(c); + pa_namereg_unregister(core, name); + return NULL; + } + + c->core = core; + c->name = pa_xstrdup(data->name); + c->proplist = pa_proplist_copy(data->proplist); + c->driver = pa_xstrdup(pa_path_get_filename(data->driver)); + c->module = data->module; + + c->sinks = pa_idxset_new(NULL, NULL); + c->sources = pa_idxset_new(NULL, NULL); + + /* As a minor optimization we just steal the list instead of + * copying it here */ + c->profiles = data->profiles; + data->profiles = NULL; + + c->active_profile = NULL; + + if (data->active_profile && c->profiles) + c->active_profile = pa_hashmap_get(c->profiles, data->active_profile); + + if (!c->active_profile && c->profiles) { + void *state = NULL; + pa_card_profile *p; + + while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) { + if (!c->active_profile || + p->priority > c->active_profile->priority) + + c->active_profile = p; + } + } + + c->userdata = NULL; + c->set_profile = NULL; + + pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0); + + pa_log_info("Created %u \"%s\"", c->index, c->name); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, c->index); + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PUT], c); + return c; +} + +void pa_card_free(pa_card *c) { + pa_core *core; + pa_card_profile *profile; + + pa_assert(c); + pa_assert(c->core); + + core = c->core; + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c); + + pa_namereg_unregister(core, c->name); + + pa_idxset_remove_by_data(c->core->cards, c, NULL); + + pa_log_info("Freed %u \"%s\"", c->index, c->name); + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + + pa_assert(pa_idxset_isempty(c->sinks)); + pa_idxset_free(c->sinks, NULL, NULL); + pa_assert(pa_idxset_isempty(c->sources)); + pa_idxset_free(c->sources, NULL, NULL); + + if (c->profiles) { + while ((profile = pa_hashmap_steal_first(c->profiles))) + pa_card_profile_free(profile); + + pa_hashmap_free(c->profiles, NULL, NULL); + } + + pa_proplist_free(c->proplist); + pa_xfree(c->driver); + pa_xfree(c->name); + pa_xfree(c); +} + +int pa_card_set_profile(pa_card *c, const char *name) { + pa_card_profile *profile; + pa_assert(c); + + if (!c->set_profile) { + pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name); + return -1; + } + + if (!c->profiles) + return -1; + + if (!(profile = pa_hashmap_get(c->profiles, name))) + return -1; + + if (c->active_profile == profile) + return 0; + + if (c->set_profile(c, profile) < 0) + return -1; + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); + + pa_log_info("Successfully changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name); + + c->active_profile = profile; + + return 0; +} + +int pa_card_suspend(pa_card *c, pa_bool_t suspend) { + pa_sink *sink; + pa_source *source; + uint32_t idx; + int ret = 0; + + pa_assert(c); + + for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) + ret -= pa_sink_suspend(sink, suspend) < 0; + + for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) + ret -= pa_source_suspend(source, suspend) < 0; + + return ret; +} diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h new file mode 100644 index 00000000..b179831e --- /dev/null +++ b/src/pulsecore/card.h @@ -0,0 +1,100 @@ +#ifndef foopulsecardhfoo +#define foopulsecardhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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. +***/ + +typedef struct pa_card pa_card; + +#include <pulse/proplist.h> +#include <pulsecore/core.h> +#include <pulsecore/module.h> +#include <pulsecore/idxset.h> + +typedef struct pa_card_profile { + char *name; + char *description; + + unsigned priority; + + /* We probably want to have different properties later on here */ + unsigned n_sinks; + unsigned n_sources; + + unsigned max_sink_channels; + unsigned max_source_channels; + + /* .. followed by some implementation specific data */ +} pa_card_profile; + +#define PA_CARD_PROFILE_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_card_profile)))) + +struct pa_card { + uint32_t index; + pa_core *core; + + char *name; + + pa_proplist *proplist; + pa_module *module; + char *driver; + + pa_idxset *sinks; + pa_idxset *sources; + + pa_hashmap *profiles; + pa_card_profile *active_profile; + + void *userdata; + + int (*set_profile)(pa_card *c, pa_card_profile *profile); +}; + +typedef struct pa_card_new_data { + char *name; + char *description; + + pa_proplist *proplist; + const char *driver; + pa_module *module; + + pa_hashmap *profiles; + char *active_profile; + + pa_bool_t namereg_fail:1; +} pa_card_new_data; + +pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra); +void pa_card_profile_free(pa_card_profile *c); + +pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data); +void pa_card_new_data_set_name(pa_card_new_data *data, const char *name); +void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile); +void pa_card_new_data_done(pa_card_new_data *data); + +pa_card *pa_card_new(pa_core *c, pa_card_new_data *data); +void pa_card_free(pa_card *c); + +int pa_card_set_profile(pa_card *c, const char *name); + +int pa_card_suspend(pa_card *c, pa_bool_t suspend); + +#endif diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index b5ff98db..1df0bd63 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -47,11 +47,11 @@ #include <pulsecore/sample-util.h> #include <pulsecore/sound-file.h> #include <pulsecore/play-memchunk.h> -#include <pulsecore/autoload.h> #include <pulsecore/sound-file-stream.h> #include <pulsecore/shared.h> #include <pulsecore/core-util.h> #include <pulsecore/core-error.h> +#include <pulsecore/modinfo.h> #include "cli-command.h" @@ -80,6 +80,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); @@ -106,9 +107,6 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); -static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); -static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); -static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); @@ -121,6 +119,11 @@ static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); /* A method table for all available commands */ @@ -133,6 +136,7 @@ static const struct command commands[] = { { "list-clients", pa_cli_command_clients, "List loaded clients", 1 }, { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, + { "list-cards", pa_cli_command_cards, "List cards", 1 }, { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, { "info", pa_cli_command_info, "Show comprehensive status", 1 }, { "ls", pa_cli_command_info, NULL, 1 }, @@ -146,6 +150,10 @@ static const struct command commands[] = { { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, bool)", 3}, { "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index, bool)", 3}, { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, bool)", 3}, + { "update-sink-proplist", pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3}, + { "update-source-proplist", pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3}, + { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3}, + { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source_output (args: index, properties)", 3}, { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, @@ -158,11 +166,6 @@ static const struct command commands[] = { { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3}, { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, - { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, - { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4}, - { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4}, - { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2}, - { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2}, { "dump", pa_cli_command_dump, "Dump daemon configuration", 1}, { "shared", pa_cli_command_list_shared_props, NULL, 1}, { "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3}, @@ -171,6 +174,7 @@ static const struct command commands[] = { { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3}, { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3}, { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2}, + { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (aargs: index, name)", 3}, { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, @@ -246,6 +250,20 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p return 0; } +static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + char *s; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + pa_assert_se(s = pa_card_list_to_string(c)); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { char *s; @@ -306,7 +324,8 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b char s[256]; const pa_mempool_stat *stat; unsigned k; - const char *def_sink, *def_source; + pa_sink *def_sink; + pa_source *def_source; static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = { [PA_MEMBLOCK_POOL] = "POOL", @@ -346,12 +365,12 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b pa_strbuf_printf(buf, "Default sample spec: %s\n", pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec)); - def_sink = pa_namereg_get_default_sink_name(c); - def_source = pa_namereg_get_default_source_name(c); + def_sink = pa_namereg_get_default_sink(c); + def_source = pa_namereg_get_default_source(c); pa_strbuf_printf(buf, "Default sink name: %s\n" "Default source name: %s\n", - def_sink ? def_sink : "none", - def_source ? def_source : "none"); + def_sink ? def_sink->name : "none", + def_source ? def_source->name : "none"); for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++) pa_strbuf_printf(buf, @@ -374,10 +393,10 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b pa_cli_command_sinks(c, t, buf, fail); pa_cli_command_sources(c, t, buf, fail); pa_cli_command_clients(c, t, buf, fail); + pa_cli_command_cards(c, t, buf, fail); pa_cli_command_sink_inputs(c, t, buf, fail); pa_cli_command_source_outputs(c, t, buf, fail); pa_cli_command_scache_list(c, t, buf, fail); -/* pa_cli_command_autoload_list(c, t, buf, fail); */ return 0; } @@ -494,13 +513,13 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink found by this name or index.\n"); return -1; } pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); - pa_sink_set_volume(sink, &cvolume); + pa_sink_set_volume(sink, &cvolume, TRUE, TRUE); return 0; } @@ -542,7 +561,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb } pa_cvolume_set(&cvolume, si->sample_spec.channels, volume); - pa_sink_input_set_volume(si, &cvolume); + pa_sink_input_set_volume(si, &cvolume, TRUE); return 0; } @@ -572,7 +591,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf * return -1; } - if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { pa_strbuf_puts(buf, "No source found by this name or index.\n"); return -1; } @@ -607,7 +626,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return -1; } - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink found by this name or index.\n"); return -1; } @@ -641,7 +660,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { pa_strbuf_puts(buf, "No sink found by this name or index.\n"); return -1; } @@ -650,6 +669,154 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return 0; } +static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *s; + pa_sink *sink; + pa_proplist *p; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(s = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + p = pa_proplist_from_string(s); + + pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p); + + pa_proplist_free(p); + + return 0; +} + +static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *s; + pa_source *source; + pa_proplist *p; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + if (!(s = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n"); + return -1; + } + + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { + pa_strbuf_puts(buf, "No source found by this name or index.\n"); + return -1; + } + + p = pa_proplist_from_string(s); + + pa_source_update_proplist(source, PA_UPDATE_REPLACE, p); + + pa_proplist_free(p); + + return 0; +} + +static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *s; + pa_sink_input *si; + uint32_t idx; + pa_proplist *p; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(s = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n"); + return -1; + } + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } + + p = pa_proplist_from_string(s); + + pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p); + + pa_proplist_free(p); + + return 0; +} + +static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *s; + pa_source_output *so; + uint32_t idx; + pa_proplist *p; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(s = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n"); + return -1; + } + + if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No source output found with this index.\n"); + return -1; + } + + p = pa_proplist_from_string(s); + + pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p); + + pa_proplist_free(p); + + return 0; +} + static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { const char *n, *v; pa_sink_input *si; @@ -686,12 +853,13 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - pa_sink_input_set_mute(si, mute); + pa_sink_input_set_mute(si, mute, TRUE); return 0; } static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { const char *n; + pa_sink *s; pa_core_assert_ref(c); pa_assert(t); @@ -703,12 +871,17 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b return -1; } - pa_namereg_set_default(c, n, PA_NAMEREG_SINK); + if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK))) + pa_namereg_set_default_sink(c, s); + else + pa_strbuf_printf(buf, "Sink %s does not exist.\n", n); + return 0; } static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { const char *n; + pa_source *s; pa_core_assert_ref(c); pa_assert(t); @@ -720,7 +893,10 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE); + if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) + pa_namereg_set_default_source(c, s); + else + pa_strbuf_printf(buf, "Source %s does not exist.\n", n); return 0; } @@ -841,7 +1017,7 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink by that name.\n"); return -1; } @@ -937,7 +1113,7 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return -1; } - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink by that name.\n"); return -1; } @@ -946,66 +1122,6 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return pa_play_file(sink, fname, NULL); } -static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { - const char *a, *b; - - pa_core_assert_ref(c); - pa_assert(t); - pa_assert(buf); - pa_assert(fail); - - pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); - - if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); - return -1; - } - - pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL); - - return 0; -} - -static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { - const char *name; - - pa_core_assert_ref(c); - pa_assert(t); - pa_assert(buf); - pa_assert(fail); - - pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); - - if (!(name = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a device name\n"); - return -1; - } - - if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) { - pa_strbuf_puts(buf, "Failed to remove autload entry\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { - char *s; - - pa_core_assert_ref(c); - pa_assert(t); - pa_assert(buf); - pa_assert(fail); - - pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); - - pa_assert_se(s = pa_autoload_list_to_string(c)); - pa_strbuf_puts(buf, s); - pa_xfree(s); - - return 0; -} - static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { pa_core_assert_ref(c); pa_assert(t); @@ -1058,12 +1174,12 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink found by this name or index.\n"); return -1; } - if (pa_sink_input_move_to(si, sink) < 0) { + if (pa_sink_input_move_to(si, sink, TRUE) < 0) { pa_strbuf_puts(buf, "Moved failed.\n"); return -1; } @@ -1101,12 +1217,12 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str return -1; } - if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) { pa_strbuf_puts(buf, "No source found by this name or index.\n"); return -1; } - if (pa_source_output_move_to(so, source) < 0) { + if (pa_source_output_move_to(so, source, TRUE) < 0) { pa_strbuf_puts(buf, "Moved failed.\n"); return -1; } @@ -1138,7 +1254,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b return -1; } - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { pa_strbuf_puts(buf, "No sink found by this name or index.\n"); return -1; } @@ -1172,7 +1288,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { pa_strbuf_puts(buf, "No source found by this name or index.\n"); return -1; } @@ -1307,17 +1423,47 @@ static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf * return 0; } +static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *n, *p; + pa_card *card; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n"); + return -1; + } + + if (!(p = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a profile by its name.\n"); + return -1; + } + + if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) { + pa_strbuf_puts(buf, "No card found by this name or index.\n"); + return -1; + } + + if (pa_card_set_profile(card, p) < 0) { + pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p); + return -1; + } + + return 0; +} + static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { pa_module *m; pa_sink *sink; pa_source *source; + pa_card *card; int nl; - const char *p; uint32_t idx; char txt[256]; time_t now; - void *i; - pa_autoload_entry *a; pa_core_assert_ref(c); pa_assert(t); @@ -1333,8 +1479,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b #endif for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { - if (m->auto_unload) - continue; pa_strbuf_printf(buf, "load-module %s", m->name); @@ -1347,8 +1491,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b nl = 0; for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { - if (sink->module && sink->module->auto_unload) - continue; if (!nl) { pa_strbuf_puts(buf, "\n"); @@ -1361,8 +1503,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b } for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { - if (source->module && source->module->auto_unload) - continue; if (!nl) { pa_strbuf_puts(buf, "\n"); @@ -1374,43 +1514,33 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED)); } + for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) { - if (c->autoload_hashmap) { - nl = 0; - - i = NULL; - while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) { - - if (!nl) { - pa_strbuf_puts(buf, "\n"); - nl = 1; - } - - pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module); - - if (a->argument) - pa_strbuf_printf(buf, " %s", a->argument); - + if (!nl) { pa_strbuf_puts(buf, "\n"); + nl = 1; } + + if (card->active_profile) + pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name); } nl = 0; - if ((p = pa_namereg_get_default_sink_name(c))) { + if ((sink = pa_namereg_get_default_sink(c))) { if (!nl) { pa_strbuf_puts(buf, "\n"); nl = 1; } - pa_strbuf_printf(buf, "set-default-sink %s\n", p); + pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name); } - if ((p = pa_namereg_get_default_source_name(c))) { + if ((source = pa_namereg_get_default_source(c))) { if (!nl) { pa_strbuf_puts(buf, "\n"); nl = 1; } - pa_strbuf_printf(buf, "set-default-source %s\n", p); + pa_strbuf_printf(buf, "set-default-source %s\n", source->name); } pa_strbuf_puts(buf, "\n### EOF\n"); @@ -1513,7 +1643,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b size_t l; if (ifstate && *ifstate == IFSTATE_FALSE) - return 0; + return 0; l = strcspn(cs, whitespace); diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 362a9791..57129d0c 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -38,7 +38,6 @@ #include <pulsecore/strbuf.h> #include <pulsecore/sample-util.h> #include <pulsecore/core-scache.h> -#include <pulsecore/autoload.h> #include <pulsecore/macro.h> #include <pulsecore/core-util.h> @@ -55,13 +54,22 @@ char *pa_module_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { + char *t; + pa_strbuf_printf(s, " index: %u\n" "\tname: <%s>\n" "\targument: <%s>\n" "\tused: %i\n" - "\tauto unload: %s\n", - m->index, m->name, m->argument ? m->argument : "", m->n_used, - pa_yes_no(m->auto_unload)); + "\tload once: %s\n", + m->index, + m->name, + pa_strempty(m->argument), + pa_module_get_n_used(m), + pa_yes_no(m->load_once)); + + t = pa_proplist_to_string_sep(m->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -89,25 +97,116 @@ char *pa_client_list_to_string(pa_core *c) { if (client->module) pa_strbuf_printf(s, "\towner module: %u\n", client->module->index); - t = pa_proplist_to_string(client->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(client->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } return pa_strbuf_tostring_free(s); } +char *pa_card_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_card *card; + uint32_t idx = PA_IDXSET_INVALID; + pa_assert(c); + + s = pa_strbuf_new(); + + pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards)); + + for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) { + char *t; + pa_sink *sink; + pa_source *source; + uint32_t sidx; + + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n", + card->index, + card->name, + card->driver); + + if (card->module) + pa_strbuf_printf(s, "\towner module: %u\n", card->module->index); + + t = pa_proplist_to_string_sep(card->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + + if (card->profiles) { + pa_card_profile *p; + void *state = NULL; + + pa_strbuf_puts(s, "\tprofiles:\n"); + + while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) + pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority); + } + + if (card->active_profile) + pa_strbuf_printf( + s, + "\tactive profile: <%s>\n", + card->active_profile->name); + + if (!pa_idxset_isempty(card->sinks)) { + pa_strbuf_puts(s, "\tsinks:\n"); + for (sink = pa_idxset_first(card->sinks, &sidx); sink; sink = pa_idxset_next(card->sinks, &sidx)) + pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", sink->name, sink->index, pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); + } + + if (!pa_idxset_isempty(card->sources)) { + pa_strbuf_puts(s, "\tsources:\n"); + for (source = pa_idxset_first(card->sources, &sidx); source; source = pa_idxset_next(card->sources, &sidx)) + pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", source->name, source->index, pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))); + } + } + + return pa_strbuf_tostring_free(s); +} + +static const char *sink_state_to_string(pa_sink_state_t state) { + switch (state) { + case PA_SINK_INIT: + return "INIT"; + case PA_SINK_RUNNING: + return "RUNNING"; + case PA_SINK_SUSPENDED: + return "SUSPENDED"; + case PA_SINK_IDLE: + return "IDLE"; + case PA_SINK_UNLINKED: + return "UNLINKED"; + default: + return "INVALID"; + } +} + +static const char *source_state_to_string(pa_source_state_t state) { + switch (state) { + case PA_SOURCE_INIT: + return "INIT"; + case PA_SOURCE_RUNNING: + return "RUNNING"; + case PA_SOURCE_SUSPENDED: + return "SUSPENDED"; + case PA_SOURCE_IDLE: + return "IDLE"; + case PA_SOURCE_UNLINKED: + return "UNLINKED"; + default: + return "INVALID"; + } +} + char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; - static const char* const state_table[] = { - [PA_SINK_INIT] = "INIT", - [PA_SINK_RUNNING] = "RUNNING", - [PA_SINK_SUSPENDED] = "SUSPENDED", - [PA_SINK_IDLE] = "IDLE", - [PA_SINK_UNLINKED] = "UNLINKED" - }; pa_assert(c); s = pa_strbuf_new(); @@ -122,6 +221,9 @@ char *pa_sink_list_to_string(pa_core *c) { vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; pa_usec_t min_latency, max_latency; + const char *cmn; + + cmn = pa_channel_map_to_pretty_name(&sink->channel_map); pa_sink_get_latency_range(sink, &min_latency, &max_latency); @@ -130,10 +232,12 @@ char *pa_sink_list_to_string(pa_core *c) { " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" - "\tflags: %s%s%s%s%s%s\n" + "\tflags: %s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: %s%s%s\n" + "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" + "\tvolume steps: %u\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" @@ -141,10 +245,10 @@ char *pa_sink_list_to_string(pa_core *c) { "\tmax rewind: %lu KiB\n" "\tmonitor source: %u\n" "\tsample spec: %s\n" - "\tchannel map: %s\n" + "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", - c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', + sink == c->default_sink ? '*' : ' ', sink->index, sink->name, sink->driver, @@ -154,13 +258,16 @@ char *pa_sink_list_to_string(pa_core *c) { sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", - state_table[pa_sink_get_state(sink)], + sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "", + sink_state_to_string(pa_sink_get_state(sink)), pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "", + pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map), pa_volume_snprint(v, sizeof(v), sink->base_volume), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "", + sink->n_volume_steps, pa_yes_no(pa_sink_get_mute(sink, FALSE)), (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC, (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC, @@ -171,14 +278,18 @@ char *pa_sink_list_to_string(pa_core *c) { sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), + cmn ? "\n\t " : "", + cmn ? cmn : "", pa_sink_used_by(sink), pa_sink_linked_by(sink)); + if (sink->card) + pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name); if (sink->module) pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index); - t = pa_proplist_to_string(sink->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -189,13 +300,6 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf *s; pa_source *source; uint32_t idx = PA_IDXSET_INVALID; - static const char* const state_table[] = { - [PA_SOURCE_INIT] = "INIT", - [PA_SOURCE_RUNNING] = "RUNNING", - [PA_SOURCE_SUSPENDED] = "SUSPENDED", - [PA_SOURCE_IDLE] = "IDLE", - [PA_SOURCE_UNLINKED] = "UNLINKED" - }; pa_assert(c); s = pa_strbuf_new(); @@ -210,6 +314,9 @@ char *pa_source_list_to_string(pa_core *c) { vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; pa_usec_t min_latency, max_latency; + const char *cmn; + + cmn = pa_channel_map_to_pretty_name(&source->channel_map); pa_source_get_latency_range(source, &min_latency, &max_latency); @@ -221,16 +328,18 @@ char *pa_source_list_to_string(pa_core *c) { "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: %s%s%s\n" + "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" + "\tvolume steps: %u\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" "\tmax rewind: %lu KiB\n" "\tsample spec: %s\n" - "\tchannel map: %s\n" + "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", - c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', + c->default_source == source ? '*' : ' ', source->index, source->name, source->driver, @@ -240,13 +349,15 @@ char *pa_source_list_to_string(pa_core *c) { source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", - state_table[pa_source_get_state(source)], + source_state_to_string(pa_source_get_state(source)), pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "", + pa_cvolume_get_balance(pa_source_get_volume(source, FALSE), &source->channel_map), pa_volume_snprint(v, sizeof(v), source->base_volume), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "", + source->n_volume_steps, pa_yes_no(pa_source_get_mute(source, FALSE)), (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC, (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, @@ -255,16 +366,20 @@ char *pa_source_list_to_string(pa_core *c) { (unsigned long) pa_source_get_max_rewind(source) / 1024, pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), + cmn ? "\n\t " : "", + cmn ? cmn : "", pa_source_used_by(source), pa_source_linked_by(source)); if (source->monitor_of) pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index); + if (source->card) + pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name); if (source->module) pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index); - t = pa_proplist_to_string(source->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(source->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -291,6 +406,9 @@ char *pa_source_output_list_to_string(pa_core *c) { for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; pa_usec_t cl; + const char *cmn; + + cmn = pa_channel_map_to_pretty_name(&o->channel_map); if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1) pa_snprintf(clt, sizeof(clt), "n/a"); @@ -309,7 +427,7 @@ char *pa_source_output_list_to_string(pa_core *c) { "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" "\tsample spec: %s\n" - "\tchannel map: %s\n" + "\tchannel map: %s%s%s\n" "\tresample method: %s\n", o->index, o->driver, @@ -327,6 +445,8 @@ char *pa_source_output_list_to_string(pa_core *c) { clt, pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), + cmn ? "\n\t " : "", + cmn ? cmn : "", pa_resample_method_to_string(pa_source_output_get_resample_method(o))); if (o->module) pa_strbuf_printf(s, "\towner module: %u\n", o->module->index); @@ -335,8 +455,8 @@ char *pa_source_output_list_to_string(pa_core *c) { if (o->direct_on_input) pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index); - t = pa_proplist_to_string(o->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(o->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -361,8 +481,11 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; pa_usec_t cl; + const char *cmn; + + cmn = pa_channel_map_to_pretty_name(&i->channel_map); if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1) pa_snprintf(clt, sizeof(clt), "n/a"); @@ -379,11 +502,13 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tstate: %s\n" "\tsink: %u <%s>\n" "\tvolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" "\tsample spec: %s\n" - "\tchannel map: %s\n" + "\tchannel map: %s%s%s\n" "\tresample method: %s\n", i->index, i->driver, @@ -398,11 +523,15 @@ char *pa_sink_input_list_to_string(pa_core *c) { state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)), + pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map), pa_yes_no(pa_sink_input_get_mute(i)), (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC, clt, pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + cmn ? "\n\t " : "", + cmn ? cmn : "", pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); if (i->module) @@ -410,8 +539,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { if (i->client) pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME))); - t = pa_proplist_to_string(i->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(i->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -424,7 +553,7 @@ char *pa_scache_list_to_string(pa_core *c) { s = pa_strbuf_new(); - pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); + pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0); if (c->scache) { pa_scache_entry *e; @@ -432,7 +561,10 @@ char *pa_scache_list_to_string(pa_core *c) { for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { double l = 0; - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; + const char *cmn; + + cmn = pa_channel_map_to_pretty_name(&e->channel_map); if (e->memchunk.memblock) { pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); @@ -445,24 +577,30 @@ char *pa_scache_list_to_string(pa_core *c) { " name: <%s>\n" "\tindex: %u\n" "\tsample spec: %s\n" - "\tchannel map: %s\n" + "\tchannel map: %s%s%s\n" "\tlength: %lu\n" "\tduration: %0.1f s\n" "\tvolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" "\tlazy: %s\n" "\tfilename: <%s>\n", e->name, e->index, ss, cm, + cmn ? "\n\t " : "", + cmn ? cmn : "", (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0), l, - pa_cvolume_snprint(cv, sizeof(cv), &e->volume), + e->volume_is_set ? pa_cvolume_snprint(cv, sizeof(cv), &e->volume) : "n/a", + e->volume_is_set ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume) : "n/a", + (e->memchunk.memblock && e->volume_is_set) ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f, pa_yes_no(e->lazy), e->filename ? e->filename : "n/a"); - t = pa_proplist_to_string(e->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(e->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } } @@ -470,38 +608,6 @@ char *pa_scache_list_to_string(pa_core *c) { return pa_strbuf_tostring_free(s); } -char *pa_autoload_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_assert(c); - - s = pa_strbuf_new(); - - pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0); - - if (c->autoload_hashmap) { - pa_autoload_entry *e; - void *state = NULL; - - while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) { - pa_strbuf_printf( - s, - " name: <%s>\n" - "\ttype: %s\n" - "\tindex: %u\n" - "\tmodule_name: <%s>\n" - "\targuments: <%s>\n", - e->name, - e->type == PA_NAMEREG_SOURCE ? "source" : "sink", - e->index, - e->module, - e->argument ? e->argument : ""); - - } - } - - return pa_strbuf_tostring_free(s); -} - char *pa_full_status_string(pa_core *c) { pa_strbuf *s; int i; @@ -528,13 +634,13 @@ char *pa_full_status_string(pa_core *c) { t = pa_client_list_to_string(c); break; case 5: - t = pa_module_list_to_string(c); + t = pa_card_list_to_string(c); break; case 6: - t = pa_scache_list_to_string(c); + t = pa_module_list_to_string(c); break; case 7: - t = pa_autoload_list_to_string(c); + t = pa_scache_list_to_string(c); break; } diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h index f4cb97a5..aad51648 100644 --- a/src/pulsecore/cli-text.h +++ b/src/pulsecore/cli-text.h @@ -31,12 +31,11 @@ char *pa_sink_input_list_to_string(pa_core *c); char *pa_source_output_list_to_string(pa_core *c); char *pa_sink_list_to_string(pa_core *core); char *pa_source_list_to_string(pa_core *c); +char *pa_card_list_to_string(pa_core *c); char *pa_client_list_to_string(pa_core *c); char *pa_module_list_to_string(pa_core *c); char *pa_scache_list_to_string(pa_core *c); -char *pa_autoload_list_to_string(pa_core *c); char *pa_full_status_string(pa_core *c); #endif - diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c index 67bf1e73..25a4f748 100644 --- a/src/pulsecore/cli.c +++ b/src/pulsecore/cli.c @@ -67,20 +67,33 @@ static void client_kill(pa_client *c); pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { char cname[256]; pa_cli *c; + pa_client_new_data data; + pa_client *client; + pa_assert(io); + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + + pa_client_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_proplist_sets(data.proplist, PA_PROP_APPLICATION_NAME, cname); + client = pa_client_new(core, &data); + pa_client_new_data_done(&data); + + if (!client) + return NULL; + c = pa_xnew(pa_cli, 1); c->core = core; + c->client = client; pa_assert_se(c->line = pa_ioline_new(io)); c->userdata = NULL; c->eof_callback = NULL; - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - pa_assert_se(c->client = pa_client_new(core, __FILE__, cname)); c->client->kill = client_kill; c->client->userdata = c; - c->client->module = m; pa_ioline_set_callback(c->line, line_callback, c); pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT); diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index ab6e5df4..18004412 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -29,6 +29,7 @@ #include <string.h> #include <pulse/xmalloc.h> +#include <pulse/util.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/log.h> @@ -37,27 +38,49 @@ #include "client.h" -pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) { +pa_client_new_data* pa_client_new_data_init(pa_client_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_client_new_data_done(pa_client_new_data *data) { + pa_assert(data); + + pa_proplist_free(data->proplist); +} + +pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) { pa_client *c; pa_core_assert_ref(core); + pa_assert(data); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0) + return NULL; c = pa_xnew(pa_client, 1); c->core = core; - c->proplist = pa_proplist_new(); - if (name) - pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); - c->driver = pa_xstrdup(driver); - c->module = NULL; + c->proplist = pa_proplist_copy(data->proplist); + c->driver = pa_xstrdup(pa_path_get_filename(data->driver)); + c->module = data->module; + + c->sink_inputs = pa_idxset_new(NULL, NULL); + c->source_outputs = pa_idxset_new(NULL, NULL); - c->kill = NULL; c->userdata = NULL; + c->kill = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); - pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name)); + pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c); + pa_core_check_idle(core); return c; @@ -70,10 +93,19 @@ void pa_client_free(pa_client *c) { pa_assert(c->core); core = c->core; + + pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c); + pa_idxset_remove_by_data(c->core->clients, c, NULL); pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + + pa_assert(pa_idxset_isempty(c->sink_inputs)); + pa_idxset_free(c->sink_inputs, NULL, NULL); + pa_assert(pa_idxset_isempty(c->source_outputs)); + pa_idxset_free(c->source_outputs, NULL, NULL); + pa_proplist_free(c->proplist); pa_xfree(c->driver); pa_xfree(c); diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index 28d1fe5f..48e9bc7a 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -42,11 +42,24 @@ struct pa_client { pa_module *module; char *driver; - void (*kill)(pa_client *c); + pa_idxset *sink_inputs; + pa_idxset *source_outputs; + void *userdata; + + void (*kill)(pa_client *c); }; -pa_client *pa_client_new(pa_core *c, const char *driver, const char *name); +typedef struct pa_client_new_data { + pa_proplist *proplist; + const char *driver; + pa_module *module; +} pa_client_new_data; + +pa_client_new_data *pa_client_new_data_init(pa_client_new_data *data); +void pa_client_new_data_done(pa_client_new_data *data); + +pa_client *pa_client_new(pa_core *c, pa_client_new_data *data); /* This function should be called only by the code that created the client */ void pa_client_free(pa_client *c); diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 1d080e11..6d2ae932 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -98,7 +98,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_assert(c); pa_assert(name); - if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) { + if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) { if (e->memchunk.memblock) pa_memblock_unref(e->memchunk.memblock); @@ -137,6 +137,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); + e->volume_is_set = FALSE; pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event"); @@ -175,6 +176,7 @@ int pa_scache_add_item( pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); + e->volume_is_set = FALSE; if (ss) { e->sample_spec = *ss; @@ -273,7 +275,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) { pa_assert(c); pa_assert(name); - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return -1; pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e); @@ -308,12 +310,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t pa_scache_entry *e; pa_cvolume r; pa_proplist *merged; + pa_bool_t pass_volume; pa_assert(c); pa_assert(name); pa_assert(sink); - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return -1; if (e->lazy && !e->memchunk.memblock) { @@ -324,10 +327,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); - if (pa_cvolume_valid(&e->volume)) - pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map); - else - pa_cvolume_reset(&e->volume, e->sample_spec.channels); + if (e->volume_is_set) { + if (pa_cvolume_valid(&e->volume)) + pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map); + else + pa_cvolume_reset(&e->volume, e->sample_spec.channels); + } } if (!e->memchunk.memblock) @@ -335,19 +340,26 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name); - pa_cvolume_set(&r, e->volume.channels, volume); - pa_sw_cvolume_multiply(&r, &r, &e->volume); + pass_volume = TRUE; - merged = pa_proplist_new(); + if (e->volume_is_set && volume != (pa_volume_t) -1) { + pa_cvolume_set(&r, e->sample_spec.channels, volume); + pa_sw_cvolume_multiply(&r, &r, &e->volume); + } else if (e->volume_is_set) + r = e->volume; + else if (volume != (pa_volume_t) -1) + pa_cvolume_set(&r, e->sample_spec.channels, volume); + else + pass_volume = FALSE; + merged = pa_proplist_new(); pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name); - pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); if (p) pa_proplist_update(merged, PA_UPDATE_REPLACE, p); - if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) { + if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) { pa_proplist_free(merged); return -1; } @@ -360,13 +372,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t return 0; } -int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_sink *sink; pa_assert(c); pa_assert(name); - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload))) + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) return -1; return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx); @@ -390,7 +402,7 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) { pa_assert(c); pa_assert(name); - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return PA_IDXSET_INVALID; return e->index; diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h index 80e0fd04..a75f8aca 100644 --- a/src/pulsecore/core-scache.h +++ b/src/pulsecore/core-scache.h @@ -36,6 +36,7 @@ typedef struct pa_scache_entry { char *name; pa_cvolume volume; + pa_bool_t volume_is_set; pa_sample_spec sample_spec; pa_channel_map channel_map; pa_memchunk memchunk; @@ -56,7 +57,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname); int pa_scache_remove_item(pa_core *c, const char *name); int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx); -int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx); +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx); void pa_scache_free(pa_core *c); const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 6f315666..e65b1796 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -935,7 +935,7 @@ static int is_group(gid_t gid, const char *name) { #else n = -1; #endif - if (n < 0) + if (n <= 0) n = 512; data = pa_xmalloc((size_t) n); @@ -959,7 +959,7 @@ finish: * support getgrgid_r. */ errno = 0; - if ((result = getgrgid(gid)) == NULL) { + if (!(result = getgrgid(gid))) { pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno)); if (!errno) @@ -1026,18 +1026,35 @@ int pa_uid_in_group(uid_t uid, const char *name) { char **i; int r = -1; +#ifdef _SC_GETGR_R_SIZE_MAX g_n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + g_n = -1; +#endif + if (g_n <= 0) + g_n = 512; + g_buf = pa_xmalloc((size_t) g_n); +#ifdef _SC_GETPW_R_SIZE_MAX p_n = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + p_n = -1; +#endif + if (p_n <= 0) + p_n = 512; + p_buf = pa_xmalloc((size_t) p_n); errno = 0; - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { - +#ifdef HAVE_GETGRNAM_R + if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) +#else + if (!(gr = getgrnam(name))) +#endif + { if (!errno) errno = ENOENT; - goto finish; } @@ -1045,8 +1062,11 @@ int pa_uid_in_group(uid_t uid, const char *name) { for (i = gr->gr_mem; *i; i++) { struct passwd pwbuf, *pw; - errno = 0; +#ifdef HAVE_GETPWNAM_R if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw) +#else + if (!(pw = getpwnam(*i))) +#endif continue; if (pw->pw_uid == uid) { @@ -1069,15 +1089,25 @@ gid_t pa_get_gid_of_group(const char *name) { long g_n; struct group grbuf, *gr; +#ifdef _SC_GETGR_R_SIZE_MAX g_n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + g_n = -1; +#endif + if (g_n <= 0) + g_n = 512; + g_buf = pa_xmalloc((size_t) g_n); errno = 0; - if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) { - +#ifdef HAVE_GETGRNAM_R + if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) +#else + if (!(gr = getgrnam(name))) +#endif + { if (!errno) errno = ENOENT; - goto finish; } @@ -2511,3 +2541,15 @@ void pa_reduce(unsigned *num, unsigned *den) { pa_assert(pa_gcd(*num, *den) == 1); } + +unsigned pa_ncpus(void) { + long ncpus; + +#ifdef _SC_NPROCESSORS_CONF + ncpus = sysconf(_SC_NPROCESSORS_CONF); +#else + ncpus = 1; +#endif + + return ncpus <= 0 ? 1 : (unsigned) ncpus; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index d9fad11e..18901f47 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -27,6 +27,7 @@ #include <inttypes.h> #include <stdarg.h> #include <stdio.h> +#include <string.h> #ifdef HAVE_SYS_RESOURCE_H #include <sys/resource.h> @@ -96,6 +97,10 @@ static inline const char *pa_strempty(const char *x) { return x ? x : ""; } +static inline const char *pa_strna(const char *x) { + return x ? x : "n/a"; +} + char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); @@ -197,7 +202,6 @@ pa_bool_t pa_in_system_mode(void); char *pa_machine_id(void); char *pa_uname_string(void); - #ifdef HAVE_VALGRIND_MEMCHECK_H pa_bool_t pa_in_valgrind(void); #else @@ -209,4 +213,6 @@ static inline pa_bool_t pa_in_valgrind(void) { unsigned pa_gcd(unsigned a, unsigned b); void pa_reduce(unsigned *num, unsigned *den); +unsigned pa_ncpus(void); + #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 5761bbc7..689fc8f8 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -37,7 +37,6 @@ #include <pulsecore/namereg.h> #include <pulsecore/core-util.h> #include <pulsecore/core-scache.h> -#include <pulsecore/autoload.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/shared.h> #include <pulsecore/random.h> @@ -91,20 +90,21 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { c->parent.parent.free = core_free; c->parent.process_msg = core_process_msg; + c->state = PA_CORE_STARTUP; c->mainloop = m; c->clients = pa_idxset_new(NULL, NULL); c->sinks = pa_idxset_new(NULL, NULL); c->sources = pa_idxset_new(NULL, NULL); c->source_outputs = pa_idxset_new(NULL, NULL); c->sink_inputs = pa_idxset_new(NULL, NULL); + c->cards = pa_idxset_new(NULL, NULL); - c->default_source_name = c->default_sink_name = NULL; + c->default_source = NULL; + c->default_sink = NULL; c->modules = NULL; c->namereg = NULL; c->scache = NULL; - c->autoload_idxset = NULL; - c->autoload_hashmap = NULL; c->running_as_daemon = FALSE; c->default_sample_spec.format = PA_SAMPLE_S16NE; @@ -113,7 +113,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { c->default_n_fragments = 4; c->default_fragment_size_msec = 25; - c->module_auto_unload_event = NULL; c->module_defer_unload_event = NULL; c->scache_auto_unload_event = NULL; @@ -128,8 +127,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { c->exit_event = NULL; c->exit_idle_time = -1; - c->module_idle_time = 20; c->scache_idle_time = 20; + c->flat_volumes = TRUE; c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3; @@ -153,6 +152,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { pa_core_check_idle(c); + c->state = PA_CORE_RUNNING; + return c; } @@ -161,12 +162,17 @@ static void core_free(pa_object *o) { int j; pa_assert(c); + c->state = PA_CORE_SHUTDOWN; + pa_module_unload_all(c); pa_assert(!c->modules); pa_assert(pa_idxset_isempty(c->clients)); pa_idxset_free(c->clients, NULL, NULL); + pa_assert(pa_idxset_isempty(c->cards)); + pa_idxset_free(c->cards, NULL, NULL); + pa_assert(pa_idxset_isempty(c->sinks)); pa_idxset_free(c->sinks, NULL, NULL); @@ -181,14 +187,13 @@ static void core_free(pa_object *o) { pa_scache_free(c); pa_namereg_free(c); - pa_autoload_free(c); pa_subscription_free_all(c); if (c->exit_event) c->mainloop->time_free(c->exit_event); - pa_xfree(c->default_source_name); - pa_xfree(c->default_sink_name); + pa_assert(!c->default_source); + pa_assert(!c->default_sink); pa_silence_cache_done(&c->silence_cache); pa_mempool_free(c->mempool); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index f796fb93..b349c6fc 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -25,6 +25,8 @@ #include <pulse/mainloop-api.h> #include <pulse/sample.h> +typedef struct pa_core pa_core; + #include <pulsecore/idxset.h> #include <pulsecore/hashmap.h> #include <pulsecore/memblock.h> @@ -34,13 +36,18 @@ #include <pulsecore/hook-list.h> #include <pulsecore/asyncmsgq.h> #include <pulsecore/sample-util.h> - -typedef struct pa_core pa_core; - +#include <pulsecore/sink.h> +#include <pulsecore/source.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/sink-input.h> #include <pulsecore/msgobject.h> +typedef enum pa_core_state { + PA_CORE_STARTUP, + PA_CORE_RUNNING, + PA_CORE_SHUTDOWN +} pa_core_state_t; + typedef enum pa_core_hook { PA_CORE_HOOK_SINK_NEW, PA_CORE_HOOK_SINK_FIXATE, @@ -49,7 +56,6 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_UNLINK_POST, PA_CORE_HOOK_SINK_STATE_CHANGED, PA_CORE_HOOK_SINK_PROPLIST_CHANGED, - PA_CORE_HOOK_SINK_SET_VOLUME, PA_CORE_HOOK_SOURCE_NEW, PA_CORE_HOOK_SOURCE_FIXATE, PA_CORE_HOOK_SOURCE_PUT, @@ -62,8 +68,8 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_INPUT_PUT, PA_CORE_HOOK_SINK_INPUT_UNLINK, PA_CORE_HOOK_SINK_INPUT_UNLINK_POST, - PA_CORE_HOOK_SINK_INPUT_MOVE, - PA_CORE_HOOK_SINK_INPUT_MOVE_POST, + PA_CORE_HOOK_SINK_INPUT_MOVE_START, + PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH, PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, PA_CORE_HOOK_SINK_INPUT_SET_VOLUME, @@ -72,10 +78,16 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_OUTPUT_PUT, PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK, PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST, - PA_CORE_HOOK_SOURCE_OUTPUT_MOVE, - PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST, + PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START, + PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH, PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_CLIENT_NEW, + PA_CORE_HOOK_CLIENT_PUT, + PA_CORE_HOOK_CLIENT_UNLINK, + PA_CORE_HOOK_CARD_NEW, + PA_CORE_HOOK_CARD_PUT, + PA_CORE_HOOK_CARD_UNLINK, PA_CORE_HOOK_MAX } pa_core_hook_t; @@ -86,6 +98,8 @@ typedef enum pa_core_hook { struct pa_core { pa_msgobject parent; + pa_core_state_t state; + /* A random value which may be used to identify this instance of * PulseAudio. Not cryptographically secure in any way. */ uint32_t cookie; @@ -93,18 +107,18 @@ struct pa_core { pa_mainloop_api *mainloop; /* idxset of all kinds of entities */ - pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; + pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache; /* Some hashmaps for all sorts of entities */ - pa_hashmap *namereg, *autoload_hashmap, *shared; + pa_hashmap *namereg, *shared; /* The name of the default sink/source */ - char *default_source_name, *default_sink_name; + pa_source *default_source; + pa_sink *default_sink; pa_sample_spec default_sample_spec; unsigned default_n_fragments, default_fragment_size_msec; - pa_time_event *module_auto_unload_event; pa_defer_event *module_defer_unload_event; pa_defer_event *subscription_defer_event; @@ -115,7 +129,8 @@ struct pa_core { pa_mempool *mempool; pa_silence_cache silence_cache; - int exit_idle_time, module_idle_time, scache_idle_time; + int exit_idle_time, scache_idle_time; + pa_bool_t flat_volumes; pa_time_event *exit_event; diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h index 85bebd68..eea1c743 100644 --- a/src/pulsecore/endianmacros.h +++ b/src/pulsecore/endianmacros.h @@ -45,6 +45,32 @@ #define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) ) #endif +static inline uint32_t PA_READ24LE(const uint8_t *p) { + return + ((uint32_t) p[0] << 16) | + ((uint32_t) p[1] << 8) | + ((uint32_t) p[2]); +} + +static inline uint32_t PA_READ24BE(const uint8_t *p) { + return + ((uint32_t) p[2] << 16) | + ((uint32_t) p[1] << 8) | + ((uint32_t) p[0]); +} + +static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) { + p[0] = (uint8_t) (u >> 16); + p[1] = (uint8_t) (u >> 8); + p[2] = (uint8_t) u; +} + +static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) { + p[2] = (uint8_t) (u >> 16); + p[1] = (uint8_t) (u >> 8); + p[0] = (uint8_t) u; +} + static inline float PA_FLOAT32_SWAP(float x) { union { float f; @@ -91,6 +117,12 @@ static inline float PA_FLOAT32_SWAP(float x) { #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x) #define PA_FLOAT32_TO_BE(x) ((float) (x)) + + #define PA_READ24NE(x) PA_READ24BE(x) + #define PA_WRITE24NE(x,y) PA_WRITE24BE((x),(y)) + + #define PA_READ24RE(x) PA_READ24LE(x) + #define PA_WRITE24RE(x,y) PA_WRITE24LE((x),(y)) #else #define PA_INT16_FROM_LE(x) ((int16_t)(x)) #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x) @@ -118,6 +150,12 @@ static inline float PA_FLOAT32_SWAP(float x) { #define PA_FLOAT32_TO_LE(x) ((float) (x)) #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x) + + #define PA_READ24NE(x) PA_READ24LE(x) + #define PA_WRITE24NE(x,y) PA_WRITE24LE((x),(y)) + + #define PA_READ24RE(x) PA_READ24BE(x) + #define PA_WRITE24RE(x,y) PA_WRITE24BE((x),(y)) #endif #endif diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index 7f2252e9..f872d347 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -600,7 +600,6 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) { switch (e->sample_spec.format) { - case PA_SAMPLE_U8: { uint8_t *t; diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index adf2f112..9a7f7cac 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -47,6 +47,7 @@ #include <pulsecore/core-util.h> #include <pulsecore/rtclock.h> #include <pulsecore/once.h> +#include <pulsecore/ratelimit.h> #include "log.h" @@ -375,3 +376,10 @@ void pa_log_level(pa_log_level_t level, const char *format, ...) { pa_log_levelv_meta(level, NULL, 0, NULL, format, ap); va_end(ap); } + +pa_bool_t pa_log_ratelimit(void) { + /* Not more than 10 messages every 5s */ + static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10); + + return pa_ratelimit_test(&ratelimit); +} diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index 3d66e903..77adb791 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -109,4 +109,6 @@ LOG_FUNC(error, PA_LOG_ERROR) #define pa_log pa_log_error +pa_bool_t pa_log_ratelimit(void); + #endif diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index 56ed2c5d..d470bb0b 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -33,39 +33,26 @@ #include <pulse/timeval.h> #include <pulse/xmalloc.h> +#include <pulse/proplist.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/log.h> #include <pulsecore/core-util.h> #include <pulsecore/macro.h> #include <pulsecore/ltdl-helper.h> +#include <pulsecore/modinfo.h> #include "module.h" #define PA_SYMBOL_INIT "pa__init" #define PA_SYMBOL_DONE "pa__done" #define PA_SYMBOL_LOAD_ONCE "pa__load_once" - -#define UNLOAD_POLL_TIME 2 - -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { - pa_core *c = PA_CORE(userdata); - struct timeval ntv; - - pa_core_assert_ref(c); - pa_assert(c->mainloop == m); - pa_assert(c->module_auto_unload_event == e); - - pa_module_unload_unused(c); - - pa_gettimeofday(&ntv); - pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC); - m->time_restart(e, &ntv); -} +#define PA_SYMBOL_GET_N_USED "pa__get_n_used" pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_module *m = NULL; pa_bool_t (*load_once)(void); + pa_modinfo *mi; pa_assert(c); pa_assert(name); @@ -77,6 +64,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { m->name = pa_xstrdup(name); m->argument = pa_xstrdup(argument); m->load_once = FALSE; + m->proplist = pa_proplist_new(); if (!(m->dl = lt_dlopenext(name))) { pa_log("Failed to open module \"%s\": %s", name, lt_dlerror()); @@ -107,10 +95,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { } m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE); + m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED); m->userdata = NULL; m->core = c; - m->n_used = -1; - m->auto_unload = FALSE; m->unload_requested = FALSE; if (m->init(m) < 0) { @@ -121,13 +108,6 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { if (!c->modules) c->modules = pa_idxset_new(NULL, NULL); - if (m->auto_unload && !c->module_auto_unload_event) { - struct timeval ntv; - pa_gettimeofday(&ntv); - pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC); - c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); - } - pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0); pa_assert(m->index != PA_IDXSET_INVALID); @@ -135,11 +115,28 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); + if ((mi = pa_modinfo_get_by_handle(m->dl, name))) { + + if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author); + + if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description); + + if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version); + + pa_modinfo_free(mi); + } + return m; fail: if (m) { + if (m->proplist) + pa_proplist_free(m->proplist); + pa_xfree(m->argument); pa_xfree(m->name); @@ -161,6 +158,9 @@ static void pa_module_free(pa_module *m) { if (m->done) m->done(m); + if (m->proplist) + pa_proplist_free(m->proplist); + lt_dlclose(m->dl); pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index); @@ -212,44 +212,12 @@ void pa_module_unload_all(pa_core *c) { c->modules = NULL; } - if (c->module_auto_unload_event) { - c->mainloop->time_free(c->module_auto_unload_event); - c->module_auto_unload_event = NULL; - } - if (c->module_defer_unload_event) { c->mainloop->defer_free(c->module_defer_unload_event); c->module_defer_unload_event = NULL; } } -void pa_module_unload_unused(pa_core *c) { - void *state = NULL; - time_t now; - pa_module *m; - - pa_assert(c); - - if (!c->modules) - return; - - time(&now); - - while ((m = pa_idxset_iterate(c->modules, &state, NULL))) { - - if (m->n_used > 0) - continue; - - if (!m->auto_unload) - continue; - - if (m->last_used_time + m->core->module_idle_time > now) - continue; - - pa_module_unload(c, m, FALSE); - } -} - static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { void *state = NULL; pa_core *c = PA_CORE(userdata); @@ -290,20 +258,11 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force pa_module_unload_request(m, force); } -void pa_module_set_used(pa_module*m, int used) { +int pa_module_get_n_used(pa_module*m) { pa_assert(m); - if (m->n_used != used) - pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); - - if (used == 0 && m->n_used > 0) - time(&m->last_used_time); - - m->n_used = used; -} - -pa_modinfo *pa_module_get_info(pa_module *m) { - pa_assert(m); + if (!m->get_n_used) + return -1; - return pa_modinfo_get_by_handle(m->dl, m->name); + return m->get_n_used(m); } diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 661b2dd6..6ab43dcf 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -27,8 +27,9 @@ typedef struct pa_module pa_module; +#include <pulse/proplist.h> + #include <pulsecore/core.h> -#include <pulsecore/modinfo.h> struct pa_module { pa_core *core; @@ -39,16 +40,14 @@ struct pa_module { int (*init)(pa_module*m); void (*done)(pa_module*m); + int (*get_n_used)(pa_module *m); void *userdata; - int n_used; - - pa_bool_t auto_unload:1; pa_bool_t load_once:1; pa_bool_t unload_requested:1; - time_t last_used_time; + pa_proplist *proplist; }; pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); @@ -60,9 +59,8 @@ void pa_module_unload_request(pa_module *m, pa_bool_t force); void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force); void pa_module_unload_all(pa_core *c); -void pa_module_unload_unused(pa_core *c); -void pa_module_set_used(pa_module*m, int used); +int pa_module_get_n_used(pa_module*m); #define PA_MODULE_AUTHOR(s) \ const char *pa__get_author(void) { return s; } \ @@ -84,6 +82,4 @@ void pa_module_set_used(pa_module*m, int used); pa_bool_t pa__load_once(void) { return b; } \ struct __stupid_useless_struct_to_allow_trailing_semicolon -pa_modinfo *pa_module_get_info(pa_module *m); - #endif diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c index 35465b7b..c3ead97d 100644 --- a/src/pulsecore/mutex-posix.c +++ b/src/pulsecore/mutex-posix.c @@ -138,3 +138,23 @@ int pa_cond_wait(pa_cond *c, pa_mutex *m) { return pthread_cond_wait(&c->cond, &m->mutex); } + +pa_mutex* pa_static_mutex_get(pa_static_mutex *s, pa_bool_t recursive, pa_bool_t inherit_priority) { + pa_mutex *m; + + pa_assert(s); + + /* First, check if already initialized and short cut */ + if ((m = pa_atomic_ptr_load(&s->ptr))) + return m; + + /* OK, not initialized, so let's allocate, and fill in */ + m = pa_mutex_new(recursive, inherit_priority); + if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m))) + return m; + + /* Him, filling in failed, so someone else must have filled in + * already */ + pa_assert_se(m = pa_atomic_ptr_load(&s->ptr)); + return m; +} diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h index 36e1d635..8e0b1f2e 100644 --- a/src/pulsecore/mutex.h +++ b/src/pulsecore/mutex.h @@ -23,6 +23,7 @@ ***/ #include <pulsecore/macro.h> +#include <pulsecore/atomic.h> typedef struct pa_mutex pa_mutex; @@ -45,4 +46,10 @@ void pa_cond_free(pa_cond *c); void pa_cond_signal(pa_cond *c, int broadcast); int pa_cond_wait(pa_cond *c, pa_mutex *m); +typedef struct pa_static_mutex { + pa_atomic_ptr_t ptr; +} pa_static_mutex; + +pa_mutex* pa_static_mutex_get(pa_static_mutex *m, pa_bool_t recursive, pa_bool_t inherit_priority); + #endif diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index ecd8def8..f3d5a8f8 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -30,7 +30,6 @@ #include <pulse/xmalloc.h> -#include <pulsecore/autoload.h> #include <pulsecore/source.h> #include <pulsecore/sink.h> #include <pulsecore/core-subscribe.h> @@ -109,7 +108,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t if (!*name) return NULL; - if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) && + if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) && !pa_namereg_is_valid_name(name)) { if (fail) @@ -174,59 +173,49 @@ void pa_namereg_unregister(pa_core *c, const char *name) { pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); + if (c->default_sink == e->data) + pa_namereg_set_default_sink(c, NULL); + else if (c->default_source == e->data) + pa_namereg_set_default_source(c, NULL); + pa_xfree(e->name); pa_xfree(e); } -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) { +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { struct namereg_entry *e; uint32_t idx; pa_assert(c); - if (!name) { + if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) { + pa_source *s; - if (type == PA_NAMEREG_SOURCE) - name = pa_namereg_get_default_source_name(c); - else if (type == PA_NAMEREG_SINK) - name = pa_namereg_get_default_sink_name(c); + if ((s = pa_namereg_get_default_source(c))) + return s; - } else if (strcmp(name, "@DEFAULT_SINK@") == 0) { - if (type == PA_NAMEREG_SINK) - name = pa_namereg_get_default_sink_name(c); + } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) { + pa_sink *s; - } else if (strcmp(name, "@DEFAULT_SOURCE@") == 0) { - if (type == PA_NAMEREG_SOURCE) - name = pa_namereg_get_default_source_name(c); + if ((s = pa_namereg_get_default_sink(c))) + return s; - } else if (strcmp(name, "@DEFAULT_MONITOR@") == 0) { - if (type == PA_NAMEREG_SOURCE) { - pa_sink *k; + } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) { + pa_sink *s; - if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, autoload))) - return k->monitor_source; - } - } else if (*name == '@') - name = NULL; + if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) + return s->monitor_source; - if (!name) + } + + if (*name == '@' || !name || !pa_namereg_is_valid_name(name)) return NULL; if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) if (e->type == type) return e->data; - if (pa_atou(name, &idx) < 0) { - - if (autoload) { - pa_autoload_request(c, name, type); - - if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) - if (e->type == type) - return e->data; - } - + if (pa_atou(name, &idx) < 0) return NULL; - } if (type == PA_NAMEREG_SINK) return pa_idxset_get_by_index(c->sinks, idx); @@ -234,66 +223,63 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bo return pa_idxset_get_by_index(c->sources, idx); else if (type == PA_NAMEREG_SAMPLE && c->scache) return pa_idxset_get_by_index(c->scache, idx); + else if (type == PA_NAMEREG_CARD) + return pa_idxset_get_by_index(c->cards, idx); return NULL; } -int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) { - char **s; - +pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) { pa_assert(c); - pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE); - - s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name; - if (!name && !*s) - return 0; + if (c->default_sink != s) { + c->default_sink = s; + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + } - if (name && *s && !strcmp(name, *s)) - return 0; + return s; +} - if (!pa_namereg_is_valid_name(name)) - return -1; +pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { + pa_assert(c); - pa_xfree(*s); - *s = pa_xstrdup(name); - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + if (c->default_source != s) { + c->default_source = s; + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + } - return 0; + return s; } -const char *pa_namereg_get_default_sink_name(pa_core *c) { +pa_sink *pa_namereg_get_default_sink(pa_core *c) { pa_sink *s; pa_assert(c); - if (c->default_sink_name) - return c->default_sink_name; + if (c->default_sink) + return c->default_sink; if ((s = pa_idxset_first(c->sinks, NULL))) - pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK); + return pa_namereg_set_default_sink(c, s); - return c->default_sink_name; + return NULL; } -const char *pa_namereg_get_default_source_name(pa_core *c) { +pa_source *pa_namereg_get_default_source(pa_core *c) { pa_source *s; uint32_t idx; pa_assert(c); - if (c->default_source_name) - return c->default_source_name; + if (c->default_source) + return c->default_source; - for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx)) - if (!s->monitor_of) { - pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); - break; - } + for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx))) + if (!s->monitor_of) + return pa_namereg_set_default_source(c, s); - if (!c->default_source_name) - if ((s = pa_idxset_first(c->sources, NULL))) - pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); + if ((s = pa_idxset_first(c->sources, NULL))) + return pa_namereg_set_default_source(c, s); - return c->default_source_name; + return NULL; } diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index f4581006..b91dd52d 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -30,18 +30,21 @@ typedef enum pa_namereg_type { PA_NAMEREG_SINK, PA_NAMEREG_SOURCE, - PA_NAMEREG_SAMPLE + PA_NAMEREG_SAMPLE, + PA_NAMEREG_CARD } pa_namereg_type_t; void pa_namereg_free(pa_core *c); const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail); void pa_namereg_unregister(pa_core *c, const char *name); -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload); -int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type); -const char *pa_namereg_get_default_sink_name(pa_core *c); -const char *pa_namereg_get_default_source_name(pa_core *c); +pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s); +pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s); + +pa_sink *pa_namereg_get_default_sink(pa_core *c); +pa_source *pa_namereg_get_default_source(pa_core *c); pa_bool_t pa_namereg_is_valid_name(const char *name); char* pa_namereg_make_valid_name(const char *name); diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 8138b7a4..b31a5da1 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -94,10 +94,11 @@ enum { PA_COMMAND_LOAD_MODULE, PA_COMMAND_UNLOAD_MODULE, - PA_COMMAND_ADD_AUTOLOAD, - PA_COMMAND_REMOVE_AUTOLOAD, - PA_COMMAND_GET_AUTOLOAD_INFO, - PA_COMMAND_GET_AUTOLOAD_INFO_LIST, + /* Obsolete */ + PA_COMMAND_ADD_AUTOLOAD___OBSOLETE, + PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE, + PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE, + PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE, PA_COMMAND_GET_RECORD_LATENCY, PA_COMMAND_CORK_RECORD_STREAM, @@ -151,6 +152,11 @@ enum { /* Supported since protocol v14 (0.9.12) */ PA_COMMAND_EXTENSION, + /* Supported since protocol v15 (0.9.15*/ + PA_COMMAND_GET_CARD_INFO, + PA_COMMAND_GET_CARD_INFO_LIST, + PA_COMMAND_SET_CARD_PROFILE, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index a5b33d62..305941a3 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -110,10 +110,10 @@ static const char *command_names[PA_COMMAND_MAX] = { [PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE", [PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE", - [PA_COMMAND_ADD_AUTOLOAD] = "ADD_AUTOLOAD", - [PA_COMMAND_REMOVE_AUTOLOAD] = "REMOVE_AUTOLOAD", - [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO", - [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST", + [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)", + [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)", + [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)", + [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)", [PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY", [PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM", diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 86edfe98..eac21de5 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -109,7 +109,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { @@ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new( data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); - pa_sink_input_new_data_set_volume(&data, volume); + pa_sink_input_new_data_set_virtual_volume(&data, volume); pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); u->sink_input = pa_sink_input_new(sink->core, &data, 0); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 460119a9..1311e678 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -403,7 +403,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification"); if (c->options->default_sink) { - sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK); CHECK_VALIDITY(sink, "No such sink: %s", c->options->default_sink); } @@ -422,7 +422,6 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void sdata.module = c->options->module; sdata.client = c->client; sdata.sink = sink; - pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_sink_input_new_data_set_sample_spec(&sdata, &ss); c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0); @@ -490,7 +489,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi if (request == ESD_PROTO_STREAM_MON) { pa_sink* sink; - if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) { pa_log("no such sink."); return -1; } @@ -503,7 +502,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi pa_assert(request == ESD_PROTO_STREAM_REC); if (c->options->default_source) { - if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE))) { pa_log("no such source."); return -1; } @@ -525,7 +524,6 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi sdata.module = c->options->module; sdata.client = c->client; sdata.source = source; - pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_source_output_new_data_set_sample_spec(&sdata, &ss); c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0); @@ -569,7 +567,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void pa_assert(!data); pa_assert(length == 0); - if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) + if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) latency = 0; else { double usec = (double) pa_sink_get_latency(sink); @@ -590,7 +588,7 @@ static int esd_proto_server_info(connection *c, esd_proto_t request, const void pa_assert(data); pa_assert(length == sizeof(int32_t)); - if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) { + if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) { rate = (int32_t) sink->sample_spec.rate; format = format_native2esd(&sink->sample_spec); } @@ -762,7 +760,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void * volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; volume.channels = 2; - pa_sink_input_set_volume(conn->sink_input, &volume); + pa_sink_input_set_volume(conn->sink_input, &volume, TRUE); ok = 1; } else ok = 0; @@ -865,7 +863,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con if (request == ESD_PROTO_SAMPLE_PLAY) { pa_sink *sink; - if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) + if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0) ok = (int32_t) idx + 1; } else { @@ -1238,7 +1236,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { pa_log_debug("Requesting rewind due to end of underrun."); - pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE); } /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ @@ -1377,7 +1375,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) { connection *c; - char cname[256], pname[128]; + char pname[128]; + pa_client_new_data data; + pa_client *client; pa_assert(p); pa_assert(io); @@ -1389,6 +1389,18 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou return; } + pa_client_new_data_init(&data); + data.module = o->module; + data.driver = __FILE__; + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname); + pa_proplist_sets(data.proplist, "esound-protocol.peer", pname); + client = pa_client_new(p->core, &data); + pa_client_new_data_done(&data); + + if (!client) + return; + c = pa_msgobject_new(connection); c->parent.parent.free = connection_free; c->parent.process_msg = connection_process_msg; @@ -1396,11 +1408,7 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou c->io = io; pa_iochannel_set_callback(c->io, io_callback, c); - pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); - c->client = pa_client_new(p->core, __FILE__, cname); - pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname); - c->client->module = o->module; + c->client = client; c->client->kill = client_kill_cb; c->client->userdata = c; diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index c89d48b6..5379a36c 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -156,6 +156,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) { if (!strcmp(c->url, URL_ROOT)) { char txt[256]; + pa_sink *def_sink; + pa_source *def_source; http_response(c, 200, "OK", "text/html"); pa_ioline_puts(c->line, @@ -173,8 +175,12 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) { PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt))); PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt))); PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec)); - PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core)); - PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core)); + + def_sink = pa_namereg_get_default_sink(c->protocol->core); + def_source = pa_namereg_get_default_source(c->protocol->core); + + PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a"); + PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a"); pa_ioline_puts(c->line, "</table>"); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 3c1e5761..c9621652 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -49,7 +49,6 @@ #include <pulsecore/core-scache.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/log.h> -#include <pulsecore/autoload.h> #include <pulsecore/strlist.h> #include <pulsecore/shared.h> #include <pulsecore/sample-util.h> @@ -248,10 +247,6 @@ static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); @@ -261,6 +256,7 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = NULL, @@ -288,6 +284,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_GET_SINK_INFO] = command_get_info, [PA_COMMAND_GET_SOURCE_INFO] = command_get_info, [PA_COMMAND_GET_CLIENT_INFO] = command_get_info, + [PA_COMMAND_GET_CARD_INFO] = command_get_info, [PA_COMMAND_GET_MODULE_INFO] = command_get_info, [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info, [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info, @@ -296,6 +293,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list, @@ -330,10 +328,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill, [PA_COMMAND_LOAD_MODULE] = command_load_module, [PA_COMMAND_UNLOAD_MODULE] = command_unload_module, - [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info, - [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list, - [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload, - [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload, + + [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL, + [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL, + [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL, + [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL, [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream, [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream, @@ -352,6 +351,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist, [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist, + [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile, + [PA_COMMAND_EXTENSION] = command_extension }; @@ -605,7 +606,6 @@ static record_stream* record_stream_new( pa_source_output_new_data_init(&data); pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); - pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); data.driver = __FILE__; data.module = c->options->module; data.client = c->client; @@ -1005,7 +1005,6 @@ static playback_stream* playback_stream_new( pa_sink_input_new_data_init(&data); pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); - pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); data.driver = __FILE__; data.module = c->options->module; data.client = c->client; @@ -1013,7 +1012,7 @@ static playback_stream* playback_stream_new( pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); if (volume) - pa_sink_input_new_data_set_volume(&data, volume); + pa_sink_input_new_data_set_virtual_volume(&data, volume); if (muted_set) pa_sink_input_new_data_set_muted(&data, muted); data.sync_base = ssync ? ssync->sink_input : NULL; @@ -1240,7 +1239,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { pa_log_debug("Requesting rewind due to end of underrun."); pa_sink_input_request_rewind(s->sink_input, (size_t) (s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for), - FALSE, TRUE); + FALSE, TRUE, FALSE); } } else { @@ -1253,7 +1252,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { * let's have it usk us again */ pa_log_debug("Requesting rewind due to rewrite."); - pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE); + pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE, FALSE); } } @@ -1799,7 +1798,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u } else if (sink_name) { - if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); pa_proplist_free(p); return; @@ -2040,7 +2039,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin } else if (source_name) { - if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) { + if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); pa_proplist_free(p); return; @@ -2315,12 +2314,12 @@ static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ if (command == PA_COMMAND_LOOKUP_SINK) { pa_sink *sink; - if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1))) + if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK))) idx = sink->index; } else { pa_source *source; pa_assert(command == PA_COMMAND_LOOKUP_SOURCE); - if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1))) + if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE))) idx = source->index; } @@ -2490,7 +2489,9 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin p = pa_proplist_new(); - if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) { + if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) || + !pa_tagstruct_eof(t)) { + protocol_error(c); pa_proplist_free(p); return; @@ -2575,7 +2576,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag if (sink_index != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); else - sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK); CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); @@ -2646,6 +2647,13 @@ static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, co if (fixed->format == PA_SAMPLE_S32BE) fixed->format = PA_SAMPLE_FLOAT32BE; } + + if (c->version < 15) { + if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE) + fixed->format = PA_SAMPLE_FLOAT32LE; + if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE) + fixed->format = PA_SAMPLE_FLOAT32BE; + } } static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) { @@ -2678,8 +2686,13 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink)); } - if (c->version >= 15) + if (c->version >= 15) { pa_tagstruct_put_volume(t, sink->base_volume); + if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE)) + pa_log_error("Internal sink state is invalid."); + pa_tagstruct_putu32(t, pa_sink_get_state(sink)); + pa_tagstruct_putu32(t, sink->n_volume_steps); + } } static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) { @@ -2712,8 +2725,13 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source)); } - if (c->version >= 15) + if (c->version >= 15) { pa_tagstruct_put_volume(t, source->base_volume); + if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE)) + pa_log_error("Internal source state is invalid."); + pa_tagstruct_putu32(t, pa_source_get_state(source)); + pa_tagstruct_putu32(t, source->n_volume_steps); + } } static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) { @@ -2727,18 +2745,47 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c if (c->version >= 13) pa_tagstruct_put_proplist(t, client->proplist); +} + +static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) { + void *state = NULL; + pa_card_profile *p; + + pa_assert(t); + pa_assert(card); + + pa_tagstruct_putu32(t, card->index); + pa_tagstruct_puts(t, card->name); + pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX); + pa_tagstruct_puts(t, card->driver); + + pa_tagstruct_putu32(t, card->profiles ? pa_hashmap_size(card->profiles) : 0); + if (card->profiles) { + while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) { + pa_tagstruct_puts(t, p->name); + pa_tagstruct_puts(t, p->description); + } + } + + pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL); + pa_tagstruct_put_proplist(t, card->proplist); } -static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { +static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) { pa_assert(t); pa_assert(module); pa_tagstruct_putu32(t, module->index); pa_tagstruct_puts(t, module->name); pa_tagstruct_puts(t, module->argument); - pa_tagstruct_putu32(t, (uint32_t) module->n_used); - pa_tagstruct_put_boolean(t, module->auto_unload); + pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module)); + + if (c->version < 15) + pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */ + + if (c->version >= 15) + pa_tagstruct_put_proplist(t, module->proplist); } static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) { @@ -2795,6 +2842,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct * static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) { pa_sample_spec fixed_ss; + pa_cvolume v; pa_assert(t); pa_assert(e); @@ -2806,7 +2854,13 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s pa_tagstruct_putu32(t, e->index); pa_tagstruct_puts(t, e->name); - pa_tagstruct_put_cvolume(t, &e->volume); + + if (e->volume_is_set) + v = e->volume; + else + pa_cvolume_init(&v); + + pa_tagstruct_put_cvolume(t, &v); pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &e->channel_map); @@ -2824,6 +2878,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p pa_sink *sink = NULL; pa_source *source = NULL; pa_client *client = NULL; + pa_card *card = NULL; pa_module *module = NULL; pa_sink_input *si = NULL; pa_source_output *so = NULL; @@ -2836,6 +2891,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p if (pa_tagstruct_getu32(t, &idx) < 0 || (command != PA_COMMAND_GET_CLIENT_INFO && + command != PA_COMMAND_GET_CARD_INFO && command != PA_COMMAND_GET_MODULE_INFO && command != PA_COMMAND_GET_SINK_INPUT_INFO && command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO && @@ -2855,12 +2911,17 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p if (idx != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); } else if (command == PA_COMMAND_GET_SOURCE_INFO) { if (idx != PA_INVALID_INDEX) source = pa_idxset_get_by_index(c->protocol->core->sources, idx); else - source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + } else if (command == PA_COMMAND_GET_CARD_INFO) { + if (idx != PA_INVALID_INDEX) + card = pa_idxset_get_by_index(c->protocol->core->cards, idx); + else + card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD); } else if (command == PA_COMMAND_GET_CLIENT_INFO) client = pa_idxset_get_by_index(c->protocol->core->clients, idx); else if (command == PA_COMMAND_GET_MODULE_INFO) @@ -2874,10 +2935,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p if (idx != PA_INVALID_INDEX) sce = pa_idxset_get_by_index(c->protocol->core->scache, idx); else - sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0); + sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE); } - if (!sink && !source && !client && !module && !si && !so && !sce) { + if (!sink && !source && !client && !card && !module && !si && !so && !sce) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); return; } @@ -2889,8 +2950,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p source_fill_tagstruct(c, reply, source); else if (client) client_fill_tagstruct(c, reply, client); + else if (client) + card_fill_tagstruct(c, reply, card); else if (module) - module_fill_tagstruct(reply, module); + module_fill_tagstruct(c, reply, module); else if (si) sink_input_fill_tagstruct(c, reply, si); else if (so) @@ -2925,6 +2988,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t i = c->protocol->core->sources; else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) i = c->protocol->core->clients; + else if (command == PA_COMMAND_GET_CARD_INFO_LIST) + i = c->protocol->core->cards; else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) i = c->protocol->core->modules; else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) @@ -2944,8 +3009,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t source_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) client_fill_tagstruct(c, reply, p); + else if (command == PA_COMMAND_GET_CARD_INFO_LIST) + card_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) - module_fill_tagstruct(reply, p); + module_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) sink_input_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) @@ -2964,7 +3031,8 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); pa_tagstruct *reply; char txt[256]; - const char *n; + pa_sink *def_sink; + pa_source *def_source; pa_sample_spec fixed_ss; pa_native_connection_assert_ref(c); @@ -2986,10 +3054,10 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec); pa_tagstruct_put_sample_spec(reply, &fixed_ss); - n = pa_namereg_get_default_sink_name(c->protocol->core); - pa_tagstruct_puts(reply, n); - n = pa_namereg_get_default_source_name(c->protocol->core); - pa_tagstruct_puts(reply, n); + def_sink = pa_namereg_get_default_sink(c->protocol->core); + pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL); + def_source = pa_namereg_get_default_source(c->protocol->core); + pa_tagstruct_puts(reply, def_source ? def_source->name : NULL); pa_tagstruct_putu32(reply, c->protocol->core->cookie); @@ -3078,14 +3146,14 @@ static void command_set_volume( if (idx != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); break; case PA_COMMAND_SET_SOURCE_VOLUME: if (idx != PA_INVALID_INDEX) source = pa_idxset_get_by_index(c->protocol->core->sources, idx); else - source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); break; case PA_COMMAND_SET_SINK_INPUT_VOLUME: @@ -3099,11 +3167,11 @@ static void command_set_volume( CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); if (sink) - pa_sink_set_volume(sink, &volume); + pa_sink_set_volume(sink, &volume, TRUE, TRUE); else if (source) pa_source_set_volume(source, &volume); else if (si) - pa_sink_input_set_volume(si, &volume); + pa_sink_input_set_volume(si, &volume, TRUE); pa_pstream_send_simple_ack(c->pstream, tag); } @@ -3148,7 +3216,7 @@ static void command_set_mute( if (idx != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); break; @@ -3156,7 +3224,7 @@ static void command_set_mute( if (idx != PA_INVALID_INDEX) source = pa_idxset_get_by_index(c->protocol->core->sources, idx); else - source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); break; @@ -3175,7 +3243,7 @@ static void command_set_mute( else if (source) pa_source_set_mute(source, mute); else if (si) - pa_sink_input_set_mute(si, mute); + pa_sink_input_set_mute(si, mute, TRUE); pa_pstream_send_simple_ack(c->pstream, tag); } @@ -3604,7 +3672,23 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID); - pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK); + if (command == PA_COMMAND_SET_DEFAULT_SOURCE) { + pa_source *source; + + source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE); + CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); + + pa_namereg_set_default_source(c->protocol->core, source); + } else { + pa_sink *sink; + pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK); + + sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK); + CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); + + pa_namereg_set_default_sink(c->protocol->core, sink); + } + pa_pstream_send_simple_ack(c->pstream, tag); } @@ -3748,143 +3832,6 @@ static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t t pa_pstream_send_simple_ack(c->pstream, tag); } -static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); - const char *name, *module, *argument; - uint32_t type; - uint32_t idx; - pa_tagstruct *reply; - - pa_native_connection_assert_ref(c); - pa_assert(t); - - if (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_getu32(t, &type) < 0 || - pa_tagstruct_gets(t, &module) < 0 || - pa_tagstruct_gets(t, &argument) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, type == 0 || type == 1, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, module && *module && pa_utf8_valid(module), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID); - - if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST); - return; - } - - reply = reply_new(tag); - pa_tagstruct_putu32(reply, idx); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); - const char *name = NULL; - uint32_t type, idx = PA_IDXSET_INVALID; - int r; - - pa_native_connection_assert_ref(c); - pa_assert(t); - - if ((pa_tagstruct_getu32(t, &idx) < 0 && - (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_getu32(t, &type) < 0)) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, !name || (*name && pa_utf8_valid(name) && (type == 0 || type == 1)), tag, PA_ERR_INVALID); - - if (name) - r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); - else - r = pa_autoload_remove_by_index(c->protocol->core, idx); - - CHECK_VALIDITY(c->pstream, r >= 0, tag, PA_ERR_NOENTITY); - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) { - pa_assert(t && e); - - pa_tagstruct_putu32(t, e->index); - pa_tagstruct_puts(t, e->name); - pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0U : 1U); - pa_tagstruct_puts(t, e->module); - pa_tagstruct_puts(t, e->argument); -} - -static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); - const pa_autoload_entry *a = NULL; - uint32_t type, idx; - const char *name; - pa_tagstruct *reply; - - pa_native_connection_assert_ref(c); - pa_assert(t); - - if ((pa_tagstruct_getu32(t, &idx) < 0 && - (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_getu32(t, &type) < 0)) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, !name || (*name && (type == 0 || type == 1) && pa_utf8_valid(name)), tag, PA_ERR_INVALID); - - if (name) - a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); - else - a = pa_autoload_get_by_index(c->protocol->core, idx); - - CHECK_VALIDITY(c->pstream, a, tag, PA_ERR_NOENTITY); - - reply = reply_new(tag); - autoload_fill_tagstruct(reply, a); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); - pa_tagstruct *reply; - - pa_native_connection_assert_ref(c); - pa_assert(t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - - reply = reply_new(tag); - - if (c->protocol->core->autoload_hashmap) { - pa_autoload_entry *a; - void *state = NULL; - - while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL))) - autoload_fill_tagstruct(reply, a); - } - - pa_pstream_send_tagstruct(c->pstream, reply); -} - static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX; @@ -3918,11 +3865,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag if (idx_device != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device); else - sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK); CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY); - if (pa_sink_input_move_to(si, sink) < 0) { + if (pa_sink_input_move_to(si, sink, TRUE) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } @@ -3937,11 +3884,11 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag if (idx_device != PA_INVALID_INDEX) source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device); else - source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE, 1); + source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE); CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY); - if (pa_source_output_move_to(so, source) < 0) { + if (pa_source_output_move_to(so, source, TRUE) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } @@ -3989,7 +3936,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa if (idx != PA_INVALID_INDEX) sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); @@ -4017,7 +3964,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa if (idx != PA_INVALID_INDEX) source = pa_idxset_get_by_index(c->protocol->core->sources, idx); else - source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); @@ -4064,13 +4011,50 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION); CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID); - cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m); + cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m); CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION); if (cb(c->protocol, m, c, tag, t) < 0) protocol_error(c); } +static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); + uint32_t idx = PA_INVALID_INDEX; + const char *name = NULL, *profile = NULL; + pa_card *card = NULL; + + pa_native_connection_assert_ref(c); + pa_assert(t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_gets(t, &profile) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID); + + if (idx != PA_INVALID_INDEX) + card = pa_idxset_get_by_index(c->protocol->core->cards, idx); + else + card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD); + + CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY); + + if (pa_card_set_profile(card, profile) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} /*** pstream callbacks ***/ @@ -4214,7 +4198,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) { pa_native_connection *c; - char cname[256], pname[128]; + char pname[128]; + pa_client *client; + pa_client_new_data data; pa_assert(p); pa_assert(io); @@ -4226,6 +4212,18 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati return; } + pa_client_new_data_init(&data); + data.module = o->module; + data.driver = __FILE__; + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname); + pa_proplist_sets(data.proplist, "native-protocol.peer", pname); + client = pa_client_new(p->core, &data); + pa_client_new_data_done(&data); + + if (!client) + return; + c = pa_msgobject_new(pa_native_connection); c->parent.parent.free = native_connection_free; c->parent.process_msg = native_connection_process_msg; @@ -4257,13 +4255,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati c->is_local = pa_iochannel_socket_is_local(io); c->version = 8; - pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname); - c->client = pa_client_new(p->core, __FILE__, cname); - pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname); + c->client = client; c->client->kill = client_kill_cb; c->client->userdata = c; - c->client->module = o->module; c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); @@ -4412,7 +4406,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ pa_assert(cb); pa_assert(!pa_hashmap_get(p->extensions, m)); - pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) cb) == 0); + pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0); return 0; } diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 743bf2ee..dd8002a3 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -323,7 +323,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { pa_log_debug("Requesting rewind due to end of underrun."); - pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE); } /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ @@ -476,7 +476,8 @@ static void io_callback(pa_iochannel*io, void *userdata) { void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) { connection *c = NULL; - char cname[256], pname[128]; + char pname[128]; + pa_client_new_data client_data; pa_assert(p); pa_assert(io); @@ -505,11 +506,18 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp c->playback.underrun = TRUE; pa_atomic_store(&c->playback.missing, 0); + pa_client_new_data_init(&client_data); + client_data.module = o->module; + client_data.driver = __FILE__; pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); - pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname); - pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname)); - pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname); - c->client->module = o->module; + pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname); + pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname); + c->client = pa_client_new(p->core, &client_data); + pa_client_new_data_done(&client_data); + + if (!c->client) + goto fail; + c->client->kill = client_kill_cb; c->client->userdata = c; @@ -518,7 +526,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp size_t l; pa_sink *sink; - if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK, TRUE))) { + if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK))) { pa_log("Failed to get sink."); goto fail; } @@ -570,7 +578,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp size_t l; pa_source *source; - if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE, TRUE))) { + if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE))) { pa_log("Failed to get source."); goto fail; } diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c new file mode 100644 index 00000000..8ce78579 --- /dev/null +++ b/src/pulsecore/ratelimit.c @@ -0,0 +1,75 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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 <config.h> +#endif + +#include <pulsecore/rtclock.h> +#include <pulsecore/log.h> +#include <pulsecore/mutex.h> + +#include "ratelimit.h" + +static pa_static_mutex mutex; + +/* Modelled after Linux' lib/ratelimit.c by Dave Young + * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */ + +pa_bool_t pa_ratelimit_test(pa_ratelimit *r) { + pa_usec_t now; + pa_mutex *m; + + now = pa_rtclock_usec(); + + m = pa_static_mutex_get(&mutex, FALSE, FALSE); + pa_mutex_lock(m); + + pa_assert(r); + pa_assert(r->interval > 0); + pa_assert(r->burst > 0); + + if (r->begin <= 0 || + r->begin + r->interval < now) { + + if (r->n_missed > 0) + pa_log_warn("%u events suppressed", r->n_missed); + + r->begin = now; + + /* Reset counters */ + r->n_printed = 0; + r->n_missed = 0; + goto good; + } + + if (r->n_printed <= r->burst) + goto good; + + r->n_missed++; + pa_mutex_unlock(m); + return FALSE; + +good: + r->n_printed++; + pa_mutex_unlock(m); + return TRUE; +} diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h new file mode 100644 index 00000000..e652c520 --- /dev/null +++ b/src/pulsecore/ratelimit.h @@ -0,0 +1,46 @@ +#ifndef foopulsecoreratelimithfoo +#define foopulsecoreratelimithfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + 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. +***/ + +#include <pulse/sample.h> +#include <pulsecore/macro.h> + +typedef struct pa_ratelimit { + const pa_usec_t interval; + const unsigned burst; + unsigned n_printed, n_missed; + pa_usec_t begin; +} pa_ratelimit; + +#define PA_DEFINE_RATELIMIT(_name, _interval, _burst) \ + pa_ratelimit _name = { \ + .interval = _interval, \ + .burst = _burst, \ + .n_printed = 0, \ + .n_missed = 0, \ + .begin = 0 \ + } + +pa_bool_t pa_ratelimit_test(pa_ratelimit *r); + +#endif diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index f0515ebe..be390db7 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -25,7 +25,7 @@ #include <string.h> -#if HAVE_LIBSAMPLERATE +#ifdef HAVE_LIBSAMPLERATE #include <samplerate.h> #endif @@ -257,8 +257,12 @@ pa_resampler* pa_resampler_new( if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE || a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE || + a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE || + a->format == PA_SAMPLE_S24_32NE || a->format == PA_SAMPLE_S24_32RE || b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE || - b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE) + b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE || + b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE || + b->format == PA_SAMPLE_S24_32NE || b->format == PA_SAMPLE_S24_32RE) r->work_format = PA_SAMPLE_FLOAT32NE; else r->work_format = PA_SAMPLE_S16NE; @@ -399,6 +403,30 @@ pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { return r->method; } +const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r) { + pa_assert(r); + + return &r->i_cm; +} + +const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r) { + pa_assert(r); + + return &r->i_ss; +} + +const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r) { + pa_assert(r); + + return &r->o_cm; +} + +const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r) { + pa_assert(r); + + return &r->o_ss; +} + static const char * const resample_methods[] = { "src-sinc-best-quality", "src-sinc-medium-quality", diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 87110cc2..54dfa559 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -99,5 +99,9 @@ const char *pa_resample_method_to_string(pa_resample_method_t m); /* Return 1 when the specified resampling method is supported */ int pa_resample_method_supported(pa_resample_method_t m); +const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r); +const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r); +const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r); +const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r); #endif diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c index f33de830..5fc6da2b 100644 --- a/src/pulsecore/rtclock.c +++ b/src/pulsecore/rtclock.c @@ -27,9 +27,12 @@ #include <stddef.h> #include <time.h> #include <sys/time.h> +#include <sys/prctl.h> +#include <errno.h> #include <pulse/timeval.h> #include <pulsecore/macro.h> +#include <pulsecore/core-error.h> #include "rtclock.h" @@ -89,6 +92,29 @@ pa_bool_t pa_rtclock_hrtimer(void) { #endif } +void pa_rtclock_hrtimer_enable(void) { +#ifdef PR_SET_TIMERSLACK + int slack_ns; + + if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) { + pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported."); + return; + } + + pa_log_debug("Timer slack set to %i us.", slack_ns/1000); + + slack_ns = 500000000; + + pa_log_debug("Setting timer slack to %i us.", slack_ns/1000); + + if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) { + pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno)); + return; + } + +#endif +} + pa_usec_t pa_rtclock_usec(void) { struct timeval tv; diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h index aa2cdace..281461df 100644 --- a/src/pulsecore/rtclock.h +++ b/src/pulsecore/rtclock.h @@ -35,6 +35,7 @@ pa_usec_t pa_rtclock_usec(void); pa_usec_t pa_rtclock_age(const struct timeval *tv); pa_bool_t pa_rtclock_hrtimer(void); +void pa_rtclock_hrtimer_enable(void); /* timer with a resolution better than this are considered high-resolution */ #define PA_HRTIMER_THRESHOLD_USEC 10 diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 3be08efd..905ba5df 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -83,6 +83,10 @@ static uint8_t silence_byte(pa_sample_format_t format) { case PA_SAMPLE_S32BE: case PA_SAMPLE_FLOAT32LE: case PA_SAMPLE_FLOAT32BE: + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: return 0; case PA_SAMPLE_ALAW: return 0xd5; @@ -209,13 +213,22 @@ size_t pa_mix( for (i = 0; i < nstreams; i++) { pa_mix_info *m = streams + i; - int32_t v, cv = m->linear[channel].i; + int32_t v, lo, hi, cv = m->linear[channel].i; if (PA_UNLIKELY(cv <= 0)) continue; + /* Multiplying the 32bit volume factor with the + * 16bit sample might result in an 48bit value. We + * want to do without 64 bit integers and hence do + * the multiplication independantly for the HI and + * LO part of the volume. */ + + hi = cv >> 16; + lo = cv & 0xFFFF; + v = *((int16_t*) m->ptr); - v = (v * cv) / 0x10000; + v = ((v * lo) >> 16) + (v * hi); sum += v; m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); @@ -244,13 +257,16 @@ size_t pa_mix( for (i = 0; i < nstreams; i++) { pa_mix_info *m = streams + i; - int32_t v, cv = m->linear[channel].i; + int32_t v, lo, hi, cv = m->linear[channel].i; if (PA_UNLIKELY(cv <= 0)) continue; + hi = cv >> 16; + lo = cv & 0xFFFF; + v = PA_INT16_SWAP(*((int16_t*) m->ptr)); - v = (v * cv) / 0x10000; + v = ((v * lo) >> 16) + (v * hi); sum += v; m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); @@ -286,7 +302,7 @@ size_t pa_mix( continue; v = *((int32_t*) m->ptr); - v = (v * cv) / 0x10000; + v = (v * cv) >> 16; sum += v; m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); @@ -322,7 +338,7 @@ size_t pa_mix( continue; v = PA_INT32_SWAP(*((int32_t*) m->ptr)); - v = (v * cv) / 0x10000; + v = (v * cv) >> 16; sum += v; m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); @@ -340,6 +356,150 @@ size_t pa_mix( break; } + case PA_SAMPLE_S24NE: { + unsigned channel = 0; + + calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); + + while (data < end) { + int64_t sum = 0; + unsigned i; + + for (i = 0; i < nstreams; i++) { + pa_mix_info *m = streams + i; + int32_t cv = m->linear[channel].i; + int64_t v; + + if (PA_UNLIKELY(cv <= 0)) + continue; + + v = (int32_t) (PA_READ24NE(m->ptr) << 8); + v = (v * cv) >> 16; + sum += v; + + m->ptr = (uint8_t*) m->ptr + 3; + } + + sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); + PA_WRITE24NE(data, ((uint32_t) sum) >> 8); + + data = (uint8_t*) data + 3; + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + + break; + } + + case PA_SAMPLE_S24RE: { + unsigned channel = 0; + + calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); + + while (data < end) { + int64_t sum = 0; + unsigned i; + + for (i = 0; i < nstreams; i++) { + pa_mix_info *m = streams + i; + int32_t cv = m->linear[channel].i; + int64_t v; + + if (PA_UNLIKELY(cv <= 0)) + continue; + + v = (int32_t) (PA_READ24RE(m->ptr) << 8); + v = (v * cv) >> 16; + sum += v; + + m->ptr = (uint8_t*) m->ptr + 3; + } + + sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); + PA_WRITE24RE(data, ((uint32_t) sum) >> 8); + + data = (uint8_t*) data + 3; + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + + break; + } + + case PA_SAMPLE_S24_32NE: { + unsigned channel = 0; + + calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); + + while (data < end) { + int64_t sum = 0; + unsigned i; + + for (i = 0; i < nstreams; i++) { + pa_mix_info *m = streams + i; + int32_t cv = m->linear[channel].i; + int64_t v; + + if (PA_UNLIKELY(cv <= 0)) + continue; + + v = (int32_t) (*((uint32_t*)m->ptr) << 8); + v = (v * cv) >> 16; + sum += v; + + m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); + } + + sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); + *((uint32_t*) data) = ((uint32_t) (int32_t) sum) >> 8; + + data = (uint8_t*) data + sizeof(uint32_t); + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + + break; + } + + case PA_SAMPLE_S24_32RE: { + unsigned channel = 0; + + calc_linear_integer_stream_volumes(streams, nstreams, volume, spec); + + while (data < end) { + int64_t sum = 0; + unsigned i; + + for (i = 0; i < nstreams; i++) { + pa_mix_info *m = streams + i; + int32_t cv = m->linear[channel].i; + int64_t v; + + if (PA_UNLIKELY(cv <= 0)) + continue; + + v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8); + v = (v * cv) >> 16; + sum += v; + + m->ptr = (uint8_t*) m->ptr + 3; + } + + sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); + *((uint32_t*) data) = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8); + + data = (uint8_t*) data + sizeof(uint32_t); + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + + break; + } + case PA_SAMPLE_U8: { unsigned channel = 0; @@ -357,7 +517,7 @@ size_t pa_mix( continue; v = (int32_t) *((uint8_t*) m->ptr) - 0x80; - v = (v * cv) / 0x10000; + v = (v * cv) >> 16; sum += v; m->ptr = (uint8_t*) m->ptr + 1; @@ -386,13 +546,16 @@ size_t pa_mix( for (i = 0; i < nstreams; i++) { pa_mix_info *m = streams + i; - int32_t v, cv = m->linear[channel].i; + int32_t v, hi, lo, cv = m->linear[channel].i; if (PA_UNLIKELY(cv <= 0)) continue; + hi = cv >> 16; + lo = cv & 0xFFFF; + v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr)); - v = (v * cv) / 0x10000; + v = ((v * lo) >> 16) + (v * hi); sum += v; m->ptr = (uint8_t*) m->ptr + 1; @@ -421,13 +584,16 @@ size_t pa_mix( for (i = 0; i < nstreams; i++) { pa_mix_info *m = streams + i; - int32_t v, cv = m->linear[channel].i; + int32_t v, hi, lo, cv = m->linear[channel].i; if (PA_UNLIKELY(cv <= 0)) continue; + hi = cv >> 16; + lo = cv & 0xFFFF; + v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr)); - v = (v * cv) / 0x10000; + v = ((v * lo) >> 16) + (v * hi); sum += v; m->ptr = (uint8_t*) m->ptr + 1; @@ -562,16 +728,26 @@ void pa_volume_memchunk( e = (int16_t*) ptr + c->length/sizeof(int16_t); for (channel = 0, d = ptr; d < e; d++) { - int32_t t; + int32_t t, hi, lo; + + /* Multiplying the 32bit volume factor with the 16bit + * sample might result in an 48bit value. We want to + * do without 64 bit integers and hence do the + * multiplication independantly for the HI and LO part + * of the volume. */ + + hi = linear[channel] >> 16; + lo = linear[channel] & 0xFFFF; t = (int32_t)(*d); - t = (t * linear[channel]) / 0x10000; + t = ((t * lo) >> 16) + (t * hi); t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); *d = (int16_t) t; if (PA_UNLIKELY(++channel >= spec->channels)) channel = 0; } + break; } @@ -585,10 +761,13 @@ void pa_volume_memchunk( e = (int16_t*) ptr + c->length/sizeof(int16_t); for (channel = 0, d = ptr; d < e; d++) { - int32_t t; + int32_t t, hi, lo; + + hi = linear[channel] >> 16; + lo = linear[channel] & 0xFFFF; t = (int32_t) PA_INT16_SWAP(*d); - t = (t * linear[channel]) / 0x10000; + t = ((t * lo) >> 16) + (t * hi); t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); *d = PA_INT16_SWAP((int16_t) t); @@ -612,7 +791,7 @@ void pa_volume_memchunk( int64_t t; t = (int64_t)(*d); - t = (t * linear[channel]) / 0x10000; + t = (t * linear[channel]) >> 16; t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); *d = (int32_t) t; @@ -635,14 +814,105 @@ void pa_volume_memchunk( int64_t t; t = (int64_t) PA_INT32_SWAP(*d); - t = (t * linear[channel]) / 0x10000; + t = (t * linear[channel]) >> 16; t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); *d = PA_INT32_SWAP((int32_t) t); if (PA_UNLIKELY(++channel >= spec->channels)) channel = 0; } + break; + } + + case PA_SAMPLE_S24NE: { + uint8_t *d, *e; + unsigned channel; + int32_t linear[PA_CHANNELS_MAX]; + + calc_linear_integer_volume(linear, volume); + e = (uint8_t*) ptr + c->length/3; + + for (channel = 0, d = ptr; d < e; d++) { + int64_t t; + + t = (int64_t)((int32_t) (PA_READ24NE(d) << 8)); + t = (t * linear[channel]) >> 16; + t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); + PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8); + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + break; + } + + case PA_SAMPLE_S24RE: { + uint8_t *d, *e; + unsigned channel; + int32_t linear[PA_CHANNELS_MAX]; + + calc_linear_integer_volume(linear, volume); + + e = (uint8_t*) ptr + c->length/3; + + for (channel = 0, d = ptr; d < e; d++) { + int64_t t; + + t = (int64_t)((int32_t) (PA_READ24RE(d) << 8)); + t = (t * linear[channel]) >> 16; + t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); + PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8); + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + break; + } + + case PA_SAMPLE_S24_32NE: { + uint32_t *d, *e; + unsigned channel; + int32_t linear[PA_CHANNELS_MAX]; + + calc_linear_integer_volume(linear, volume); + + e = (uint32_t*) ptr + c->length/sizeof(uint32_t); + + for (channel = 0, d = ptr; d < e; d++) { + int64_t t; + + t = (int64_t) ((int32_t) (*d << 8)); + t = (t * linear[channel]) >> 16; + t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); + *d = ((uint32_t) ((int32_t) t)) >> 8; + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } + break; + } + + case PA_SAMPLE_S24_32RE: { + uint32_t *d, *e; + unsigned channel; + int32_t linear[PA_CHANNELS_MAX]; + + calc_linear_integer_volume(linear, volume); + + e = (uint32_t*) ptr + c->length/sizeof(uint32_t); + + for (channel = 0, d = ptr; d < e; d++) { + int64_t t; + + t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8)); + t = (t * linear[channel]) >> 16; + t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL); + *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8); + + if (PA_UNLIKELY(++channel >= spec->channels)) + channel = 0; + } break; } @@ -656,10 +926,13 @@ void pa_volume_memchunk( e = (uint8_t*) ptr + c->length; for (channel = 0, d = ptr; d < e; d++) { - int32_t t; + int32_t t, hi, lo; + + hi = linear[channel] >> 16; + lo = linear[channel] & 0xFFFF; t = (int32_t) *d - 0x80; - t = (t * linear[channel]) / 0x10000; + t = ((t * lo) >> 16) + (t * hi); t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F); *d = (uint8_t) (t + 0x80); @@ -679,10 +952,13 @@ void pa_volume_memchunk( e = (uint8_t*) ptr + c->length; for (channel = 0, d = ptr; d < e; d++) { - int32_t t; + int32_t t, hi, lo; + + hi = linear[channel] >> 16; + lo = linear[channel] & 0xFFFF; t = (int32_t) st_ulaw2linear16(*d); - t = (t * linear[channel]) / 0x10000; + t = ((t * lo) >> 16) + (t * hi); t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2); @@ -702,10 +978,13 @@ void pa_volume_memchunk( e = (uint8_t*) ptr + c->length; for (channel = 0, d = ptr; d < e; d++) { - int32_t t; + int32_t t, hi, lo; + + hi = linear[channel] >> 16; + lo = linear[channel] & 0xFFFF; t = (int32_t) st_alaw2linear16(*d); - t = (t * linear[channel]) / 0x10000; + t = ((t * lo) >> 16) + (t * hi); t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF); *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3); @@ -900,12 +1179,16 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, case PA_SAMPLE_S16BE: case PA_SAMPLE_S32LE: case PA_SAMPLE_S32BE: + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: case PA_SAMPLE_FLOAT32LE: case PA_SAMPLE_FLOAT32BE: cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0); cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b); cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b); cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b); cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b); cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b); break; diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c index e7e1449a..0d5146aa 100644 --- a/src/pulsecore/sconv-s16be.c +++ b/src/pulsecore/sconv-s16be.c @@ -27,28 +27,52 @@ #define INT16_FROM PA_INT16_FROM_BE #define INT16_TO PA_INT16_TO_BE +#define UINT16_FROM PA_UINT16_FROM_BE +#define UINT16_TO PA_UINT16_TO_BE #define INT32_FROM PA_INT32_FROM_BE #define INT32_TO PA_INT32_TO_BE +#define UINT32_FROM PA_UINT32_FROM_BE +#define UINT32_TO PA_UINT32_TO_BE + +#define READ24 PA_READ24BE +#define WRITE24 PA_WRITE24BE #define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne #define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne - #define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re #define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re #define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne #define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne - #define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re #define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re +#define pa_sconv_s24le_to_float32ne pa_sconv_s24be_to_float32ne +#define pa_sconv_s24le_from_float32ne pa_sconv_s24be_from_float32ne +#define pa_sconv_s24le_to_float32re pa_sconv_s24be_to_float32re +#define pa_sconv_s24le_from_float32re pa_sconv_s24be_from_float32re + +#define pa_sconv_s24_32le_to_float32ne pa_sconv_s24_32be_to_float32ne +#define pa_sconv_s24_32le_from_float32ne pa_sconv_s24_32be_from_float32ne +#define pa_sconv_s24_32le_to_float32re pa_sconv_s24_32be_to_float32re +#define pa_sconv_s24_32le_from_float32re pa_sconv_s24_32be_from_float32re + #define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne #define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne - #define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re #define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re +#define pa_sconv_s24le_to_s16ne pa_sconv_s24be_to_s16ne +#define pa_sconv_s24le_from_s16ne pa_sconv_s24be_from_s16ne +#define pa_sconv_s24le_to_s16re pa_sconv_s24be_to_s16re +#define pa_sconv_s24le_from_s16re pa_sconv_s24be_from_s16re + +#define pa_sconv_s24_32le_to_s16ne pa_sconv_s24_32be_to_s16ne +#define pa_sconv_s24_32le_from_s16ne pa_sconv_s24_32be_from_s16ne +#define pa_sconv_s24_32le_to_s16re pa_sconv_s24_32be_to_s16re +#define pa_sconv_s24_32le_from_s16re pa_sconv_s24_32be_from_s16re + #ifdef WORDS_BIGENDIAN #define SWAP_WORDS 0 #else diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h index 60580815..02633333 100644 --- a/src/pulsecore/sconv-s16be.h +++ b/src/pulsecore/sconv-s16be.h @@ -34,26 +34,36 @@ void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b); void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b); void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b); +void pa_sconv_s24be_to_float32ne(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24be_from_float32ne(unsigned n, const float *a, uint8_t *b); +void pa_sconv_s24be_to_float32re(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24be_from_float32re(unsigned n, const float *a, uint8_t *b); + +void pa_sconv_s24_32be_to_float32ne(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24_32be_from_float32ne(unsigned n, const float *a, uint8_t *b); +void pa_sconv_s24_32be_to_float32re(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24_32be_from_float32re(unsigned n, const float *a, uint8_t *b); + void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b); void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b); void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b); void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b); +void pa_sconv_s24be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b); +void pa_sconv_s24be_to_s16re(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24be_from_s16re(unsigned n, const int16_t *a, uint8_t *b); + +void pa_sconv_s24_32be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24_32be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b); +void pa_sconv_s24_32be_to_s16re(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24_32be_from_s16re(unsigned n, const int16_t *a, uint8_t *b); + #ifdef WORDS_BIGENDIAN #define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne #define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne #define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re #define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re - -#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne -#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne -#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re -#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re - -#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne -#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne -#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re -#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re #endif #endif diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c index 159c4655..79f0391c 100644 --- a/src/pulsecore/sconv-s16le.c +++ b/src/pulsecore/sconv-s16le.c @@ -23,7 +23,7 @@ #include <config.h> #endif -/* Despite the name of this file we implement S32 handling here, too. */ +/* Despite the name of this file we implement S32 and S24 handling here, too. */ #include <inttypes.h> #include <stdio.h> @@ -41,18 +41,37 @@ #ifndef INT16_FROM #define INT16_FROM PA_INT16_FROM_LE #endif +#ifndef UINT16_FROM +#define UINT16_FROM PA_UINT16_FROM_LE +#endif #ifndef INT16_TO #define INT16_TO PA_INT16_TO_LE #endif +#ifndef UINT16_TO +#define UINT16_TO PA_UINT16_TO_LE +#endif #ifndef INT32_FROM #define INT32_FROM PA_INT32_FROM_LE #endif +#ifndef UINT32_FROM +#define UINT32_FROM PA_UINT32_FROM_LE +#endif #ifndef INT32_TO #define INT32_TO PA_INT32_TO_LE #endif +#ifndef UINT32_TO +#define UINT32_TO PA_UINT32_TO_LE +#endif + +#ifndef READ24 +#define READ24 PA_READ24LE +#endif +#ifndef WRITE24 +#define WRITE24 PA_WRITE24LE +#endif #ifndef SWAP_WORDS #ifdef WORDS_BIGENDIAN @@ -243,3 +262,207 @@ void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) { b++; } } + +void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + *b = (int16_t) (READ24(a) >> 8); + a += 3; + b++; + } +} + +void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + WRITE24(b, ((uint32_t) *a) << 8); + a++; + b += 3; + } +} + +void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int16_t s = (int16_t) (READ24(a) >> 8); + *b = PA_INT16_SWAP(s); + a += 3; + b++; + } +} + +void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + uint32_t s = ((uint32_t) PA_INT16_SWAP(*a)) << 8; + WRITE24(b, s); + a++; + b += 3; + } +} + +void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s = READ24(a) << 8; + *b = ((float) s) / 0x7FFFFFFF; + a += 3; + b ++; + } +} + +void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s; + float v = *a; + v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); + s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF); + WRITE24(b, ((uint32_t) s) >> 8); + a++; + b+=3; + } +} + +void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s = READ24(a) << 8; + float k = ((float) s) / 0x7FFFFFFF; + *b = PA_FLOAT32_SWAP(k); + a += 3; + b ++; + } +} + +void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s; + float v = *a; + v = PA_FLOAT32_SWAP(v); + v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); + s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF); + WRITE24(b, ((uint32_t) s) >> 8); + a++; + b+=3; + } +} + +void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + *b = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16); + a++; + b++; + } +} + +void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int16_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16); + *b = PA_INT16_SWAP(s); + a++; + b++; + } +} + +void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + *b = UINT32_TO(((uint32_t) ((int32_t) *a << 16)) >> 8); + a++; + b++; + } +} + +void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + uint32_t s = ((uint32_t) ((int32_t) PA_INT16_SWAP(*a) << 16)) >> 8; + *b = UINT32_TO(s); + a++; + b++; + } +} + +void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8)); + *b = ((float) s) / 0x7FFFFFFF; + a ++; + b ++; + } +} + +void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8)); + float k = ((float) s) / 0x7FFFFFFF; + *b = PA_FLOAT32_SWAP(k); + a ++; + b ++; + } +} + +void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s; + float v = *a; + v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); + s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF); + *b = UINT32_TO(((uint32_t) s) >> 8); + a++; + b++; + } +} + +void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b) { + pa_assert(a); + pa_assert(b); + + for (; n > 0; n--) { + int32_t s; + float v = *a; + v = PA_FLOAT32_SWAP(v); + v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f); + s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF); + *b = UINT32_TO(((uint32_t) s) >> 8); + a++; + b++; + } +} diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h index 8d4c87c3..f7b00645 100644 --- a/src/pulsecore/sconv-s16le.h +++ b/src/pulsecore/sconv-s16le.h @@ -34,26 +34,36 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b); void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b); void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b); +void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b); +void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b); +void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b); + +void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b); +void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b); +void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b); +void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b); + void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b); void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b); void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b); void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b); +void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b); +void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b); +void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b); + +void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b); +void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b); +void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b); +void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b); + #ifndef WORDS_BIGENDIAN #define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re #define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re #define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne #define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne - -#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re -#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re -#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne -#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne - -#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re -#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re -#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne -#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne #endif #endif diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c index 6c4d420e..fcd0309c 100644 --- a/src/pulsecore/sconv.c +++ b/src/pulsecore/sconv.c @@ -198,6 +198,10 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) { [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_to_float32ne, [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_float32ne, [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_float32ne, + [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_float32ne, + [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_float32ne, + [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne, + [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne, [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne, [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne, }; @@ -216,6 +220,10 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) { [PA_SAMPLE_S16BE] = (pa_convert_func_t) pa_sconv_s16be_from_float32ne, [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_float32ne, [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_float32ne, + [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_float32ne, + [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_float32ne, + [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne, + [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne, [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne, [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne, [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_float32ne, @@ -238,6 +246,10 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) { [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne, [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_to_s16ne, [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_to_s16ne, + [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_to_s16ne, + [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_to_s16ne, + [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne, + [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne, [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_to_s16ne, [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_to_s16ne }; @@ -258,6 +270,10 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) { [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne, [PA_SAMPLE_S32BE] = (pa_convert_func_t) pa_sconv_s32be_from_s16ne, [PA_SAMPLE_S32LE] = (pa_convert_func_t) pa_sconv_s32le_from_s16ne, + [PA_SAMPLE_S24BE] = (pa_convert_func_t) pa_sconv_s24be_from_s16ne, + [PA_SAMPLE_S24LE] = (pa_convert_func_t) pa_sconv_s24le_from_s16ne, + [PA_SAMPLE_S24_32BE] = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne, + [PA_SAMPLE_S24_32LE] = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne, [PA_SAMPLE_ALAW] = (pa_convert_func_t) alaw_from_s16ne, [PA_SAMPLE_ULAW] = (pa_convert_func_t) ulaw_from_s16ne, }; diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c index 77d919d3..d6e42dd8 100644 --- a/src/pulsecore/shared.c +++ b/src/pulsecore/shared.c @@ -111,7 +111,14 @@ void pa_shared_cleanup(pa_core *c) { if (!c->shared) return; - pa_assert(pa_hashmap_isempty(c->shared)); + if (!pa_hashmap_isempty(c->shared)) { + pa_strbuf *s = pa_strbuf_new(); + + pa_shared_dump(c, s); + pa_log_debug("%s", pa_strbuf_tostring(s)); + pa_strbuf_free(s); + pa_assert(pa_hashmap_isempty(c->shared)); + } pa_hashmap_free(c->shared, NULL, NULL); c->shared = NULL; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d25cd797..d4d11194 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -30,6 +30,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> +#include <pulse/util.h> #include <pulsecore/sample-util.h> #include <pulsecore/core-subscribe.h> @@ -71,11 +72,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } -void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { +void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); - if ((data->volume_is_set = !!volume)) - data->volume = data->virtual_volume = *volume; + if ((data->soft_volume_is_set = !!volume)) + data->soft_volume = *volume; +} + +void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { + pa_assert(data); + + if ((data->virtual_volume_is_set = !!volume)) + data->virtual_volume = *volume; } void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -130,8 +138,10 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); - if (!data->sink) - data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE); + if (!data->sink) { + data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); + data->save_sink = FALSE; + } pa_return_null_if_fail(data->sink); pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED); @@ -152,17 +162,36 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); - if (!data->volume_is_set) { - pa_cvolume_reset(&data->volume, data->sample_spec.channels); - pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); - } + if (!data->virtual_volume_is_set) { + + if (data->sink->flags & PA_SINK_FLAT_VOLUME) { + data->virtual_volume = data->sink->virtual_volume; + pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map); + } else + pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); + + data->save_volume = FALSE; - pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); - pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); + } else if (!data->virtual_volume_is_absolute) { + + /* When the 'absolute' bool is set then we'll treat the volume + * as relative to the sink volume even in flat volume mode */ + if (data->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume t = data->sink->virtual_volume; + pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); + pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t); + } + } pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume)); pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec)); + if (!data->soft_volume_is_set) + data->soft_volume = data->virtual_volume; + + pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume)); + pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec)); + if (!data->muted_is_set) data->muted = FALSE; @@ -183,13 +212,17 @@ pa_sink_input* pa_sink_input_new( pa_assert(pa_channel_map_valid(&data->channel_map)); /* Due to the fixing of the sample spec the volume might not match anymore */ - pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0) return NULL; @@ -214,8 +247,6 @@ pa_sink_input* pa_sink_input_new( pa_log_warn("Unsupported resampling operation."); return NULL; } - - data->resample_method = pa_resampler_get_method(resampler); } i = pa_msgobject_new(pa_sink_input); @@ -226,17 +257,21 @@ pa_sink_input* pa_sink_input_new( i->state = PA_SINK_INPUT_INIT; i->flags = flags; i->proplist = pa_proplist_copy(data->proplist); - i->driver = pa_xstrdup(data->driver); + i->driver = pa_xstrdup(pa_path_get_filename(data->driver)); i->module = data->module; i->sink = data->sink; i->client = data->client; - i->resample_method = data->resample_method; + i->requested_resample_method = data->resample_method; + i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; i->virtual_volume = data->virtual_volume; - i->volume = data->volume; + i->soft_volume = data->soft_volume; + i->save_volume = data->save_volume; + i->save_sink = data->save_sink; + i->save_muted = data->save_muted; i->muted = data->muted; @@ -260,11 +295,12 @@ pa_sink_input* pa_sink_input_new( pa_atomic_store(&i->thread_info.drained, 1); i->thread_info.sample_spec = i->sample_spec; i->thread_info.resampler = resampler; - i->thread_info.volume = i->volume; + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -282,6 +318,9 @@ pa_sink_input* pa_sink_input_new( pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); + if (i->client) + pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0); + pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), @@ -298,6 +337,9 @@ pa_sink_input* pa_sink_input_new( static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) { pa_assert(i); + if (!i->sink) + return; + if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) pa_assert_se(i->sink->n_corked -- >= 1); else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED) @@ -330,13 +372,13 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) { } if (state != PA_SINK_INPUT_UNLINKED) { - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i); for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync); } pa_sink_update_status(i->sink); @@ -358,7 +400,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { linked = PA_SINK_INPUT_IS_LINKED(i->state); if (linked) - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); if (i->sync_prev) i->sync_prev->sync_next = i->sync_next; @@ -367,9 +409,14 @@ void pa_sink_input_unlink(pa_sink_input *i) { i->sync_prev = i->sync_next = NULL; - pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); - if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) - pa_sink_input_unref(i); + pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL); + + if (i->sink) + if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) + pa_sink_input_unref(i); + + if (i->client) + pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL); while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); @@ -380,20 +427,30 @@ void pa_sink_input_unlink(pa_sink_input *i) { update_n_corked(i, PA_SINK_INPUT_UNLINKED); i->state = PA_SINK_INPUT_UNLINKED; - if (linked) + if (linked && i->sink) { + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + if (i->sink->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); + } reset_callbacks(i); if (linked) { - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i); } - pa_sink_update_status(i->sink); + if (i->sink) { + pa_sink_update_status(i->sink); + i->sink = NULL; + } - i->sink = NULL; pa_sink_input_unref(i); } @@ -442,7 +499,7 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->process_rewind); pa_assert(i->kill); - i->thread_info.volume = i->volume; + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; @@ -450,10 +507,17 @@ void pa_sink_input_put(pa_sink_input *i) { update_n_corked(i, state); i->state = state; + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); pa_sink_update_status(i->sink); } @@ -508,9 +572,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa block_size_max_sink_input = i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : - pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec); + pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sample_spec); - block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec); + block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sink->sample_spec); /* Default buffer size */ if (slength <= 0) @@ -535,7 +599,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa * it after and leave it for the sink code */ do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); - volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted; + volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted; while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) { pa_memchunk tchunk; @@ -581,7 +645,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa if (i->thread_info.muted) pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); else - pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume); + pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume); } if (!i->thread_info.resampler) @@ -627,7 +691,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa /* We've both the same channel map, so let's have the sink do the adjustment for us*/ pa_cvolume_mute(volume, i->sink->sample_spec.channels); else - *volume = i->thread_info.volume; + *volume = i->thread_info.soft_volume; return 0; } @@ -658,7 +722,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); - if (nbytes > 0) { + if (nbytes > 0 && !i->thread_info.dont_rewind_render) { pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); } @@ -714,6 +778,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_flush = FALSE; + i->thread_info.dont_rewind_render = FALSE; } /* Called from thread context */ @@ -797,35 +862,43 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { } /* Called from main context */ -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { - pa_sink_input_set_volume_data data; - +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_assert(volume); pa_assert(pa_cvolume_valid(volume)); pa_assert(pa_cvolume_compatible(volume, &i->sample_spec)); - data.sink_input = i; - data.virtual_volume = *volume; - data.volume = *volume; + if (pa_cvolume_equal(volume, &i->virtual_volume)) + return; - /* If you change something here, consider looking into - * module-flat-volume.c as well since it uses very similar - * code. */ + i->virtual_volume = *volume; + i->save_volume = save; - if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0) - return; + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; - if (!pa_cvolume_equal(&i->volume, &data.volume)) { - i->volume = data.volume; - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0); - } + /* We are in flat volume mode, so let's update all sink input + * volumes and update the flat volume of the sink */ - if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) { - i->virtual_volume = data.virtual_volume; - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE); + + } else { + + /* OK, we are in normal volume mode. The volume only affects + * ourselves */ + + i->soft_volume = *volume; + + /* Hooks have the ability to play games with i->soft_volume */ + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); } + + /* The virtual volume changed, let's tell people so */ + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } /* Called from main context */ @@ -837,7 +910,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { } /* Called from main context */ -void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { +void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) { pa_assert(i); pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -846,9 +919,10 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { return; i->muted = mute; + i->save_muted = save; - pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } /* Called from main context */ @@ -859,6 +933,21 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) { return i->muted; } +/* Called from main thread */ +pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) { + + pa_sink_input_assert_ref(i); + + pa_proplist_update(i->proplist, mode, p); + + if (PA_SINK_IS_LINKED(i->state)) { + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + + return TRUE; +} + /* Called from main context */ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { pa_sink_input_assert_ref(i); @@ -880,7 +969,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); return 0; } @@ -903,8 +992,8 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); if (PA_SINK_INPUT_IS_LINKED(i->state)) { - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } } @@ -912,17 +1001,13 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { pa_sink_input_assert_ref(i); - return i->resample_method; + return i->actual_resample_method; } /* Called from main context */ -pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { +pa_bool_t pa_sink_input_may_move(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - pa_sink_assert_ref(dest); - - if (dest == i->sink) - return TRUE; if (i->flags & PA_SINK_INPUT_DONT_MOVE) return FALSE; @@ -932,6 +1017,21 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return FALSE; } + return TRUE; +} + +/* Called from main context */ +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_sink_assert_ref(dest); + + if (dest == i->sink) + return TRUE; + + if (!pa_sink_input_may_move(i)) + return FALSE; + if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { pa_log_warn("Failed to move sink input: too many inputs per sink."); return FALSE; @@ -945,34 +1045,72 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { } /* Called from main context */ -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { - pa_resampler *new_resampler; - pa_sink *origin; - pa_sink_input_move_hook_data hook_data; +int pa_sink_input_start_move(pa_sink_input *i) { pa_source_output *o, *p = NULL; + pa_sink *origin; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - pa_sink_assert_ref(dest); + pa_assert(i->sink); - origin = i->sink; - - if (dest == origin) - return 0; + if (!pa_sink_input_may_move(i)) + return -1; - if (!pa_sink_input_may_move_to(i, dest)) + if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0) return -1; + origin = i->sink; + /* Kill directly connected outputs */ while ((o = pa_idxset_first(i->direct_outputs, NULL))) { pa_assert(o != p); pa_source_output_kill(o); p = o; } + pa_assert(pa_idxset_isempty(i->direct_outputs)); + + pa_idxset_remove_by_data(i->sink->inputs, i, NULL); + + if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) + pa_assert_se(i->sink->n_corked-- >= 1); + + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); + + pa_sink_update_status(i->sink); + i->sink = NULL; + + return 0; +} + +/* Called from main context */ +int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { + pa_resampler *new_resampler; + + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(!i->sink); + pa_sink_assert_ref(dest); + + if (!pa_sink_input_may_move_to(i, dest)) + return -1; + + i->sink = dest; + i->save_sink = save; + pa_idxset_put(dest->inputs, i, NULL); + + if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) + i->sink->n_corked++; if (i->thread_info.resampler && - pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && - pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) + pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) && + pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map)) /* Try to reuse the old resampler if possible */ new_resampler = i->thread_info.resampler; @@ -984,10 +1122,10 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { /* Okey, we need a new resampler for the new sink */ if (!(new_resampler = pa_resampler_new( - dest->core->mempool, + i->core->mempool, &i->sample_spec, &i->channel_map, &dest->sample_spec, &dest->channel_map, - i->resample_method, + i->requested_resample_method, ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { @@ -997,21 +1135,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { } else new_resampler = NULL; - hook_data.sink_input = i; - hook_data.destination = dest; - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data); - - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); - - pa_idxset_remove_by_data(origin->inputs, i, NULL); - pa_idxset_put(dest->inputs, i, NULL); - i->sink = dest; - - if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) { - pa_assert_se(origin->n_corked-- >= 1); - dest->n_corked++; - } - /* Replace resampler and render queue */ if (new_resampler != i->thread_info.resampler) { @@ -1032,20 +1155,47 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { &i->sink->silence); } - pa_sink_update_status(origin); pa_sink_update_status(dest); + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (i->sink->flags & PA_SINK_FLAT_VOLUME) { + pa_cvolume new_volume; + pa_sink_update_flat_volume(i->sink, &new_volume); + pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); + } + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); + pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); + + /* Notify everyone */ if (i->moved) i->moved(i); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i); + pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i); + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); - pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name); + return 0; +} - /* Notify everyone */ - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +/* Called from main context */ +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(i->sink); + pa_sink_assert_ref(dest); + + if (dest == i->sink) + return 0; + + if (!pa_sink_input_may_move_to(i, dest)) + return -1; + + if (pa_sink_input_start_move(i) < 0) + return -1; + + if (pa_sink_input_finish_move(i, dest, save) < 0) + return -1; return 0; } @@ -1076,7 +1226,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE); } else if (uncorking) { @@ -1087,7 +1237,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } } @@ -1098,14 +1248,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { - case PA_SINK_INPUT_MESSAGE_SET_VOLUME: - i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME: + if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) { + i->thread_info.soft_volume = i->soft_volume; + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); + } return 0; - case PA_SINK_INPUT_MESSAGE_SET_MUTE: - i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, TRUE, FALSE); + case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: + if (i->thread_info.muted != i->muted) { + i->thread_info.muted = i->muted; + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); + } return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1180,7 +1334,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { } /* Called from IO context */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) { size_t lbq; /* If 'rewrite' is TRUE the sink is rewound as far as requested @@ -1191,7 +1345,9 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam * If 'rewrite' is FALSE the sink is rewound as far as requested * and possible and the already rendered data is dropped so that * in the next iteration we read new data from the - * implementor. This implies 'flush' is TRUE. */ + * implementor. This implies 'flush' is TRUE. If + * dont_rewind_render is TRUE then the render memblockq is not + * rewound. */ pa_sink_input_assert_ref(i); @@ -1204,6 +1360,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam return; pa_assert(rewrite || flush); + pa_assert(!dont_rewind_render || !rewrite); /* Calculate how much we can rewind locally without having to * touch the sink */ @@ -1238,6 +1395,10 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam i->thread_info.rewrite_flush || (flush && i->thread_info.rewrite_nbytes != 0); + i->thread_info.dont_rewind_render = + i->thread_info.dont_rewind_render || + dont_rewind_render; + if (nbytes != (size_t) -1) { /* Transform to sink domain */ @@ -1258,8 +1419,8 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { pa_assert(ret); pa_silence_memchunk_get( - &i->sink->core->silence_cache, - i->sink->core->mempool, + &i->core->silence_cache, + i->core->mempool, ret, &i->sample_spec, i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 27125988..893d8690 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -78,7 +78,7 @@ struct pa_sink_input { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_sink *sink; + pa_sink *sink; /* NULL while we are being moved */ /* A sink input may be connected to multiple source outputs * directly, so that they don't get mixed data of the entire @@ -90,12 +90,16 @@ struct pa_sink_input { pa_sink_input *sync_prev, *sync_next; - pa_cvolume virtual_volume; + pa_cvolume virtual_volume, soft_volume; + pa_bool_t muted:1; - pa_cvolume volume; - pa_bool_t muted; + /* if TRUE then the source we are connected to and/or the volume + * set is worth remembering, i.e. was explicitly chosen by the + * user and not automatically. module-stream-restore looks for + * this.*/ + pa_bool_t save_sink:1, save_volume:1, save_muted:1; - pa_resample_method_t resample_method; + pa_resample_method_t requested_resample_method, actual_resample_method; /* Returns the chunk of audio data and drops it from the * queue. Returns -1 on failure. Called from IO thread context. If @@ -170,7 +174,15 @@ struct pa_sink_input { pa_sink_input_state_t state; pa_atomic_t drained; - pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ + pa_cvolume soft_volume; + pa_bool_t muted:1; + + pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */ + + /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */ + pa_bool_t rewrite_flush:1, dont_rewind_render:1; + size_t rewrite_nbytes; + uint64_t underrun_for, playing_for; pa_sample_spec sample_spec; @@ -179,15 +191,8 @@ struct pa_sink_input { /* We maintain a history of resampled audio data here. */ pa_memblockq *render_memblockq; - size_t rewrite_nbytes; - pa_bool_t rewrite_flush; - uint64_t underrun_for, playing_for; - pa_sink_input *sync_prev, *sync_next; - pa_cvolume volume; - pa_bool_t muted; - /* The requested latency for the sink */ pa_usec_t requested_sink_latency; @@ -201,8 +206,8 @@ PA_DECLARE_CLASS(pa_sink_input); #define PA_SINK_INPUT(o) pa_sink_input_cast(o) enum { - PA_SINK_INPUT_MESSAGE_SET_VOLUME, - PA_SINK_INPUT_MESSAGE_SET_MUTE, + PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, + PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, PA_SINK_INPUT_MESSAGE_GET_LATENCY, PA_SINK_INPUT_MESSAGE_SET_RATE, PA_SINK_INPUT_MESSAGE_SET_STATE, @@ -227,35 +232,28 @@ typedef struct pa_sink_input_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; - pa_cvolume virtual_volume; - - pa_cvolume volume; + pa_cvolume virtual_volume, soft_volume; pa_bool_t muted:1; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; - pa_bool_t volume_is_set:1; + + pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1; pa_bool_t muted_is_set:1; + + pa_bool_t virtual_volume_is_absolute:1; + + pa_bool_t save_sink:1, save_volume:1, save_muted:1; } pa_sink_input_new_data; pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); -void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); void pa_sink_input_new_data_done(pa_sink_input_new_data *data); -typedef struct pa_sink_input_move_hook_data { - pa_sink_input *sink_input; - pa_sink *destination; -} pa_sink_input_move_hook_data; - -typedef struct pa_sink_set_input_volume_data { - pa_sink_input *sink_input; - pa_cvolume virtual_volume; - pa_cvolume volume; -} pa_sink_input_set_volume_data; - /* To be called by the implementing module only */ pa_sink_input* pa_sink_input_new( @@ -277,7 +275,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render); void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b); @@ -290,15 +288,23 @@ void pa_sink_input_kill(pa_sink_input*i); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save); const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); -void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute); +void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save); pa_bool_t pa_sink_input_get_mute(pa_sink_input *i); +pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest); -pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save); +pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */ +pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */ + +/* The same as pa_sink_input_move_to() but in two seperate steps, + * first the detaching from the old sink, then the attaching to the + * new sink */ +int pa_sink_input_start_move(pa_sink_input *i); +int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index a4d993cd..3afeadb0 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -32,6 +32,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> #include <pulse/timeval.h> +#include <pulse/util.h> #include <pulsecore/sink-input.h> #include <pulsecore/namereg.h> @@ -167,6 +168,9 @@ pa_sink* pa_sink_new( if (!data->muted_is_set) data->muted = FALSE; + if (data->card) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); @@ -181,8 +185,9 @@ pa_sink* pa_sink_new( s->flags = flags; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); - s->driver = pa_xstrdup(data->driver); + s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); s->module = data->module; + s->card = data->card; s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; @@ -190,8 +195,10 @@ pa_sink* pa_sink_new( s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; - s->volume = data->volume; + s->virtual_volume = data->volume; + pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); s->base_volume = PA_VOLUME_NORM; + s->n_volume_steps = PA_VOLUME_NORM+1; s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; @@ -209,8 +216,8 @@ pa_sink* pa_sink_new( 0); s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); - s->thread_info.soft_muted = FALSE; + s->thread_info.soft_volume = s->soft_volume; + s->thread_info.soft_muted = s->muted; s->thread_info.state = s->state; s->thread_info.rewind_nbytes = 0; s->thread_info.rewind_requested = FALSE; @@ -223,6 +230,9 @@ pa_sink* pa_sink_new( pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); + if (s->card) + pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0); + pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s", s->index, s->name, @@ -235,6 +245,7 @@ pa_sink* pa_sink_new( source_data.name = pa_sprintf_malloc("%s.monitor", name); source_data.driver = data->driver; source_data.module = data->module; + source_data.card = data->card; dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name); @@ -301,8 +312,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { 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 */ + 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); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } return 0; } @@ -322,10 +335,17 @@ void pa_sink_put(pa_sink* s) { if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) { s->flags |= PA_SINK_DECIBEL_VOLUME; - s->thread_info.soft_volume = s->volume; + s->thread_info.soft_volume = s->soft_volume; s->thread_info.soft_muted = s->muted; } + if (s->flags & PA_SINK_DECIBEL_VOLUME) + s->n_volume_steps = PA_VOLUME_NORM+1; + + if (s->core->flat_volumes) + if (s->flags & PA_SINK_DECIBEL_VOLUME) + s->flags |= PA_SINK_FLAT_VOLUME; + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); pa_source_put(s->monitor_source); @@ -358,6 +378,9 @@ void pa_sink_unlink(pa_sink* s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sinks, s, NULL); + if (s->card) + pa_idxset_remove_by_data(s->card->sinks, s, NULL); + while ((i = pa_idxset_first(s->inputs, NULL))) { pa_assert(i != j); pa_sink_input_kill(i); @@ -458,6 +481,58 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); } +/* Called from main context */ +pa_queue *pa_sink_move_all_start(pa_sink *s) { + pa_queue *q; + pa_sink_input *i, *n; + uint32_t idx; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_IS_LINKED(s->state)); + + q = pa_queue_new(); + + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) { + n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)); + + if (pa_sink_input_start_move(i) >= 0) + pa_queue_push(q, pa_sink_input_ref(i)); + } + + return q; +} + +/* Called from main context */ +void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) { + pa_sink_input *i; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(q); + + while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) { + if (pa_sink_input_finish_move(i, s, save) < 0) + pa_sink_input_unlink(i); + + pa_sink_input_unref(i); + } + + pa_queue_free(q, NULL, NULL); +} + +/* Called from main context */ +void pa_sink_move_all_fail(pa_queue *q) { + pa_sink_input *i; + pa_assert(q); + + while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) { + pa_sink_input_unlink(i); + pa_sink_input_unref(i); + } + + pa_queue_free(q, NULL, NULL); +} + /* Called from IO thread context */ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input *i; @@ -642,7 +717,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_assert(length > 0); - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; + n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS); if (n == 0) { @@ -685,8 +760,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { result->index = 0; } - if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, result); + inputs_drop(s, info, n, result); pa_sink_unref(s); } @@ -716,7 +790,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_assert(length > 0); - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; + n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS); if (n == 0) { if (target->length > length) @@ -765,8 +839,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_memblock_release(target->memblock); } - if (s->thread_info.state == PA_SINK_RUNNING) - inputs_drop(s, info, n, target); + inputs_drop(s, info, n, target); pa_sink_unref(s); } @@ -845,9 +918,100 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { } /* Called from main thread */ -void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { - pa_bool_t changed; - pa_sink_set_volume_data data; +void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) { + pa_sink_input *i; + uint32_t idx; + + /* This is called whenever a sink input volume changes and we + * might need to fix up the sink volume accordingly. Please note + * that we don't actually update the sinks volume here, we only + * return how it needs to be updated. The caller should then call + * pa_sink_set_flat_volume().*/ + + pa_cvolume_mute(new_volume, s->channel_map.channels); + + /* First let's determine the new maximum volume of all inputs + * connected to this sink */ + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + unsigned c; + pa_cvolume remapped_volume; + + remapped_volume = i->virtual_volume; + pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map); + + for (c = 0; c < new_volume->channels; c++) + if (remapped_volume.values[c] > new_volume->values[c]) + new_volume->values[c] = remapped_volume.values[c]; + } + + /* Then, let's update the soft volumes of all inputs connected + * to this sink */ + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + pa_cvolume remapped_new_volume; + + remapped_new_volume = *new_volume; + pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map); + pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume); + + /* Hooks have the ability to play games with i->soft_volume */ + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); + + /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because + * we want the update to have atomically with the sink + * volume update, hence we do it within the + * pa_sink_set_flat_volume() call below*/ + } +} + +/* Called from main thread */ +void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) { + pa_sink_input *i; + uint32_t idx; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_IS_LINKED(s->state)); + pa_assert(old_volume); + pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + + /* This is called whenever the sink volume changes that is not + * caused by a sink input volume change. We need to fix up the + * sink input volumes accordingly */ + + for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { + pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume; + unsigned c; + + remapped_new_volume = s->virtual_volume; + pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map); + + remapped_old_volume = *old_volume; + pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map); + + for (c = 0; c < i->sample_spec.channels; c++) + + if (remapped_old_volume.values[c] == PA_VOLUME_MUTED) + fixed_volume.values[c] = PA_VOLUME_MUTED; + else + fixed_volume.values[c] = (pa_volume_t) + ((uint64_t) i->virtual_volume.values[c] * + (uint64_t) remapped_new_volume.values[c] / + (uint64_t) remapped_old_volume.values[c]); + + fixed_volume.channels = i->virtual_volume.channels; + + if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) { + i->virtual_volume = fixed_volume; + + /* The virtual volume changed, let's tell people so */ + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + } +} + +/* Called from main thread */ +void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) { + pa_cvolume old_virtual_volume; + pa_bool_t virtual_volume_changed; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); @@ -855,37 +1019,45 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { pa_assert(pa_cvolume_valid(volume)); pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); - data.sink = s; - data.volume = *volume; + old_virtual_volume = s->virtual_volume; + s->virtual_volume = *volume; + virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); - changed = !pa_cvolume_equal(&data.volume, &s->volume); + /* Propagate this volume change back to the inputs */ + if (virtual_volume_changed) + if (propagate && (s->flags & PA_SINK_FLAT_VOLUME)) + pa_sink_propagate_flat_volume(s, &old_virtual_volume); - if (changed) { - if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0) - return; - - changed = !pa_cvolume_equal(&data.volume, &s->volume); - } + if (s->set_volume) { + /* If we have a function set_volume(), then we do not apply a + * soft volume by default. However, set_volume() is apply one + * to s->soft_volume */ - s->volume = data.volume; + pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); + s->set_volume(s); - if (s->set_volume && s->set_volume(s) < 0) - s->set_volume = NULL; + } else + /* If we have no function set_volume(), then the soft volume + * becomes the virtual volume */ + s->soft_volume = s->virtual_volume; - if (!s->set_volume) - pa_sink_set_soft_volume(s, volume); + /* This tells the sink that soft and/or virtual volume changed */ + if (sendmsg) + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); - if (changed) + if (virtual_volume_changed) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -/* Called from main thread */ +/* Called from main thread. Only to be called by sink implementor */ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { pa_sink_assert_ref(s); pa_assert(volume); + s->soft_volume = *volume; + if (PA_SINK_IS_LINKED(s->state)) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); else s->thread_info.soft_volume = *volume; } @@ -893,41 +1065,43 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { /* Called from main thread */ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_IS_LINKED(s->state)); if (s->refresh_volume || force_refresh) { - struct pa_cvolume old_volume = s->volume; + struct pa_cvolume old_virtual_volume = s->virtual_volume; + + if (s->get_volume) + s->get_volume(s); - if (s->get_volume && s->get_volume(s) < 0) - s->get_volume = NULL; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); - if (!s->get_volume) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); + if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) { + + if (s->flags & PA_SINK_FLAT_VOLUME) + pa_sink_propagate_flat_volume(s, &old_virtual_volume); - if (!pa_cvolume_equal(&old_volume, &s->volume)) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } } - return &s->volume; + return &s->virtual_volume; } /* Called from main thread */ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { - pa_bool_t changed; + pa_bool_t old_muted; pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->state)); - changed = s->muted != mute; + old_muted = s->muted; s->muted = mute; - if (s->set_mute && s->set_mute(s) < 0) - s->set_mute = NULL; + if (s->set_mute) + s->set_mute(s); - 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); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); - if (changed) + if (old_muted != s->muted) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } @@ -935,16 +1109,14 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_IS_LINKED(s->state)); if (s->refresh_muted || force_refresh) { pa_bool_t old_muted = s->muted; - if (s->get_mute && s->get_mute(s) < 0) - s->get_mute = NULL; + if (s->get_mute) + s->get_mute(s); - if (!s->get_mute) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); if (old_muted != s->muted) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); @@ -954,6 +1126,21 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { } /* Called from main thread */ +pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) { + + pa_sink_assert_ref(s); + + pa_proplist_update(s->proplist, mode, p); + + if (PA_SINK_IS_LINKED(s->state)) { + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + + return TRUE; +} + +/* Called from main thread */ void pa_sink_set_description(pa_sink *s, const char *description) { const char *old; pa_sink_assert_ref(s); @@ -1053,6 +1240,22 @@ unsigned pa_sink_check_suspend(pa_sink *s) { return ret; } +/* Called from the IO thread */ +static void sync_input_volumes_within_thread(pa_sink *s) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + + while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) { + if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) + continue; + + i->thread_info.soft_volume = i->soft_volume; + pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); + } +} + /* Called from IO thread, except when it is not */ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink *s = PA_SINK(o); @@ -1107,7 +1310,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * slow start, i.e. need some time to buffer client * samples before beginning streaming. */ - return 0; + /* In flat volume mode we need to update the volume as + * well */ + return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_REMOVE_INPUT: { @@ -1148,7 +1353,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_invalidate_requested_latency(s); pa_sink_request_rewind(s, (size_t) -1); - return 0; + /* In flat volume mode we need to update the volume as + * well */ + return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_START_MOVE: { @@ -1194,7 +1401,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_log_debug("Requesting rewind due to started move"); pa_sink_request_rewind(s, (size_t) -1); - return 0; + /* In flat volume mode we need to update the volume as + * well */ + return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_FINISH_MOVE: { @@ -1238,27 +1447,36 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_request_rewind(s, nbytes); } - return 0; + /* In flat volume mode we need to update the volume as + * well */ + return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); } case PA_SINK_MESSAGE_SET_VOLUME: - s->thread_info.soft_volume = *((pa_cvolume*) userdata); - pa_sink_request_rewind(s, (size_t) -1); - return 0; + if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) { + s->thread_info.soft_volume = s->soft_volume; + pa_sink_request_rewind(s, (size_t) -1); + } - case PA_SINK_MESSAGE_SET_MUTE: - s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + if (s->flags & PA_SINK_FLAT_VOLUME) + sync_input_volumes_within_thread(s); - pa_sink_request_rewind(s, (size_t) -1); return 0; case PA_SINK_MESSAGE_GET_VOLUME: - *((pa_cvolume*) userdata) = s->thread_info.soft_volume; + return 0; + + case PA_SINK_MESSAGE_SET_MUTE: + + if (!s->thread_info.soft_muted != s->muted) { + s->thread_info.soft_muted = s->muted; + pa_sink_request_rewind(s, (size_t) -1); + } + return 0; case PA_SINK_MESSAGE_GET_MUTE: - *((pa_bool_t*) userdata) = s->thread_info.soft_muted; return 0; case PA_SINK_MESSAGE_SET_STATE: @@ -1591,6 +1809,7 @@ void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t m pa_source_update_latency_range(s->monitor_source, min_latency, max_latency); } +/* Called from main context */ size_t pa_sink_get_max_rewind(pa_sink *s) { size_t r; pa_sink_assert_ref(s); @@ -1603,6 +1822,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) { return r; } +/* Called from main context */ size_t pa_sink_get_max_request(pa_sink *s) { size_t r; pa_sink_assert_ref(s); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 254be3b0..124b4e11 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -27,6 +27,7 @@ typedef struct pa_sink pa_sink; #include <inttypes.h> +#include <pulse/def.h> #include <pulse/sample.h> #include <pulse/channelmap.h> #include <pulse/volume.h> @@ -38,21 +39,12 @@ typedef struct pa_sink pa_sink; #include <pulsecore/refcnt.h> #include <pulsecore/msgobject.h> #include <pulsecore/rtpoll.h> +#include <pulsecore/card.h> +#include <pulsecore/queue.h> #define PA_MAX_INPUTS_PER_SINK 32 -typedef enum pa_sink_state { - PA_SINK_INIT, - PA_SINK_RUNNING, - PA_SINK_SUSPENDED, - PA_SINK_IDLE, - PA_SINK_UNLINKED -} pa_sink_state_t; - -static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) { - return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; -} - +/* Returns true if sink is linked: registered and accessible from client side. */ static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; } @@ -70,6 +62,7 @@ struct pa_sink { pa_proplist *proplist; pa_module *module; /* may be NULL */ + pa_card *card; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -78,10 +71,11 @@ struct pa_sink { unsigned n_corked; pa_source *monitor_source; - pa_cvolume volume; - pa_bool_t muted; + pa_volume_t base_volume; /* shall be constant */ + unsigned n_volume_steps; /* shall be constant */ - pa_volume_t base_volume; /* shall be constant */ + pa_cvolume virtual_volume, soft_volume; + pa_bool_t muted:1; pa_bool_t refresh_volume:1; pa_bool_t refresh_muted:1; @@ -100,23 +94,23 @@ struct pa_sink { * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message * will be sent to the IO thread instead. If refresh_volume is * FALSE neither this function is called nor a message is sent. */ - int (*get_volume)(pa_sink *s); /* may be NULL */ + void (*get_volume)(pa_sink *s); /* may be NULL */ /* Called when the volume shall be changed. Called from main loop * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message * will be sent to the IO thread instead. */ - int (*set_volume)(pa_sink *s); /* dito */ + void (*set_volume)(pa_sink *s); /* dito */ /* Called when the mute setting is queried. Called from main loop * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message * will be sent to the IO thread instead. If refresh_mute is * FALSE neither this function is called nor a message is sent.*/ - int (*get_mute)(pa_sink *s); /* dito */ + void (*get_mute)(pa_sink *s); /* dito */ /* Called when the mute setting shall be changed. Called from main * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE * message will be sent to the IO thread instead. */ - int (*set_mute)(pa_sink *s); /* dito */ + void (*set_mute)(pa_sink *s); /* dito */ /* Called when a rewind request is issued. Called from IO thread * context. */ @@ -131,6 +125,7 @@ struct pa_sink { struct { pa_sink_state_t state; pa_hashmap *inputs; + pa_cvolume soft_volume; pa_bool_t soft_muted:1; @@ -186,6 +181,7 @@ typedef struct pa_sink_new_data { const char *driver; pa_module *module; + pa_card *card; pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -208,12 +204,7 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); void pa_sink_new_data_done(pa_sink_new_data *data); -typedef struct pa_sink_set_volume_data { - pa_sink *sink; - pa_cvolume volume; -} pa_sink_set_volume_data; - -/* To be called exclusively by the sink driver, from main context */ +/*** To be called exclusively by the sink driver, from main context */ pa_sink* pa_sink_new( pa_core *core, @@ -232,7 +223,9 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_ void pa_sink_detach(pa_sink *s); void pa_sink_attach(pa_sink *s); -/* May be called by everyone, from main context */ +void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); + +/**** May be called by everyone, from main context */ /* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_sink_get_latency(pa_sink *s); @@ -246,18 +239,27 @@ int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend); -void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume); -void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); +void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); +void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume); + +void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh); void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); -pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres); +pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh); + +pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */ unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_sink_get_state(s) ((s)->state) -/* To be called exclusively by the sink driver, from IO context */ +/* Moves all inputs away, and stores them in pa_queue */ +pa_queue *pa_sink_move_all_start(pa_sink *s); +void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); +void pa_sink_move_all_fail(pa_queue *q); + +/*** To be called exclusively by the sink driver, from IO context */ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); @@ -278,7 +280,7 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request); void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency); -/* To be called exclusively by sink input drivers, from IO context */ +/*** To be called exclusively by sink input drivers, from IO context */ void pa_sink_request_rewind(pa_sink*s, size_t nbytes); diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c index f721f699..a0920025 100644 --- a/src/pulsecore/socket-util.c +++ b/src/pulsecore/socket-util.c @@ -202,9 +202,11 @@ void pa_make_udp_socket_low_delay(int fd) { } int pa_socket_set_rcvbuf(int fd, size_t l) { + int bufsz = (int)l; + pa_assert(fd >= 0); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)) < 0) { pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno)); return -1; } @@ -213,9 +215,11 @@ int pa_socket_set_rcvbuf(int fd, size_t l) { } int pa_socket_set_sndbuf(int fd, size_t l) { + int bufsz = (int)l; + pa_assert(fd >= 0); - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsz, sizeof(bufsz)) < 0) { pa_log("SO_SNDBUF: %s", pa_cstrerror(errno)); return -1; } diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index c30c16eb..1be421f1 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -133,7 +133,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s * we are heard right-away. */ if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) - pa_sink_input_request_rewind(i, 0, FALSE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); } /* Called from IO thread context */ @@ -322,7 +322,7 @@ int pa_play_file( data.sink = sink; data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); - pa_sink_input_new_data_set_volume(&data, volume); + pa_sink_input_new_data_set_virtual_volume(&data, volume); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c92c5ab7..1e1ac4e1 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -29,6 +29,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> +#include <pulse/util.h> #include <pulsecore/sample-util.h> #include <pulsecore/core-subscribe.h> @@ -111,8 +112,10 @@ pa_source_output* pa_source_output_new( pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); - if (!data->source) - data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE); + if (!data->source) { + data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE); + data->save_source = FALSE; + } pa_return_null_if_fail(data->source); pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED); @@ -153,6 +156,9 @@ pa_source_output* pa_source_output_new( pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0) return NULL; @@ -177,8 +183,6 @@ pa_source_output* pa_source_output_new( pa_log_warn("Unsupported resampling operation."); return NULL; } - - data->resample_method = pa_resampler_get_method(resampler); } o = pa_msgobject_new(pa_source_output); @@ -189,17 +193,20 @@ pa_source_output* pa_source_output_new( o->state = PA_SOURCE_OUTPUT_INIT; o->flags = flags; o->proplist = pa_proplist_copy(data->proplist); - o->driver = pa_xstrdup(data->driver); + o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; o->source = data->source; o->client = data->client; - o->resample_method = data->resample_method; + o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; + o->requested_resample_method = data->resample_method; o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; o->direct_on_input = data->direct_on_input; + o->save_source = data->save_source; + reset_callbacks(o); o->userdata = NULL; @@ -223,6 +230,9 @@ pa_source_output* pa_source_output_new( pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); + if (o->client) + pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0); + if (o->direct_on_input) pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); @@ -242,11 +252,13 @@ pa_source_output* pa_source_output_new( static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); + if (!o->source) + return; + if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED) pa_assert_se(o->source->n_corked -- >= 1); else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; - } /* Called from main context */ @@ -262,7 +274,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t o->state = state; if (state != PA_SOURCE_OUTPUT_UNLINKED) - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); pa_source_update_status(o->source); @@ -282,31 +294,39 @@ void pa_source_output_unlink(pa_source_output*o) { linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state); if (linked) - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); if (o->direct_on_input) pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL); - pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); - if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) - pa_source_output_unref(o); + + pa_idxset_remove_by_data(o->core->source_outputs, o, NULL); + + if (o->source) + if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) + pa_source_output_unref(o); + + if (o->client) + pa_idxset_remove_by_data(o->client->source_outputs, o, NULL); update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED); o->state = PA_SOURCE_OUTPUT_UNLINKED; - if (linked) + if (linked && o->source) if (o->source->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); reset_callbacks(o); if (linked) { - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); } - pa_source_update_status(o->source); + if (o->source) { + pa_source_update_status(o->source); + o->source = NULL; + } - o->source = NULL; pa_source_output_unref(o); } @@ -355,8 +375,8 @@ void pa_source_output_put(pa_source_output *o) { pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); pa_source_update_status(o->source); } @@ -564,7 +584,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); return 0; } @@ -587,18 +607,48 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } } +/* Called from main thread */ +pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { + + pa_source_output_assert_ref(o); + + pa_proplist_update(o->proplist, mode, p); + + if (PA_SINK_IS_LINKED(o->state)) { + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + } + + return TRUE; +} + /* Called from main context */ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { pa_source_output_assert_ref(o); - return o->resample_method; + return o->actual_resample_method; +} + +/* Called from main context */ +pa_bool_t pa_source_output_may_move(pa_source_output *o) { + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + + if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) + return FALSE; + + if (o->direct_on_input) + return FALSE; + + return TRUE; } +/* Called from main context */ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); @@ -607,10 +657,7 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { if (dest == o->source) return TRUE; - if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) - return FALSE; - - if (o->direct_on_input) + if (!pa_source_output_may_move(o)) return FALSE; if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { @@ -626,26 +673,56 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { } /* Called from main context */ -int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { +int pa_source_output_start_move(pa_source_output *o) { pa_source *origin; - pa_resampler *new_resampler; - pa_source_output_move_hook_data hook_data; pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); - pa_source_assert_ref(dest); + pa_assert(o->source); + + if (!pa_source_output_may_move(o)) + return -1; + + if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o) < 0) + return -1; origin = o->source; - if (dest == origin) - return 0; + pa_idxset_remove_by_data(o->source->outputs, o, NULL); + + if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) + pa_assert_se(origin->n_corked-- >= 1); + + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); + + pa_source_update_status(o->source); + o->source = NULL; + + return 0; +} + +/* Called from main context */ +int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) { + pa_resampler *new_resampler; + + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_assert(!o->source); + pa_source_assert_ref(dest); if (!pa_source_output_may_move_to(o, dest)) return -1; + o->source = dest; + o->save_source = save; + pa_idxset_put(o->source->outputs, o, NULL); + + if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) + o->source->n_corked++; + if (o->thread_info.resampler && - pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) && - pa_channel_map_equal(&origin->channel_map, &dest->channel_map)) + pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) && + pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map)) /* Try to reuse the old resampler if possible */ new_resampler = o->thread_info.resampler; @@ -657,10 +734,10 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { /* Okey, we need a new resampler for the new source */ if (!(new_resampler = pa_resampler_new( - dest->core->mempool, + o->core->mempool, &dest->sample_spec, &dest->channel_map, &o->sample_spec, &o->channel_map, - o->resample_method, + o->requested_resample_method, ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { @@ -670,22 +747,6 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { } else new_resampler = NULL; - hook_data.source_output = o; - hook_data.destination = dest; - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data); - - /* Okey, let's move it */ - pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); - - pa_idxset_remove_by_data(origin->outputs, o, NULL); - pa_idxset_put(dest->outputs, o, NULL); - o->source = dest; - - if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) { - pa_assert_se(origin->n_corked-- >= 1); - dest->n_corked++; - } - /* Replace resampler */ if (new_resampler != o->thread_info.resampler) { if (o->thread_info.resampler) @@ -705,20 +766,40 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { &o->source->silence); } - pa_source_update_status(origin); pa_source_update_status(dest); - pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); + pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name); + + /* Notify everyone */ if (o->moved) o->moved(o); - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o); + pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o); + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); - pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name); + return 0; +} - /* Notify everyone */ - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +/* Called from main context */ +int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) { + + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_assert(o->source); + pa_source_assert_ref(dest); + + if (dest == o->source) + return 0; + + if (!pa_source_output_may_move_to(o, dest)) + return -1; + + if (pa_source_output_start_move(o) < 0) + return -1; + + if (pa_source_output_finish_move(o, dest, save) < 0) + return -1; return 0; } diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index f011f9bd..79b4926b 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -32,6 +32,7 @@ typedef struct pa_source_output pa_source_output; #include <pulsecore/resampler.h> #include <pulsecore/module.h> #include <pulsecore/client.h> +#include <pulsecore/sink-input.h> typedef enum pa_source_output_state { PA_SOURCE_OUTPUT_INIT, @@ -71,7 +72,7 @@ struct pa_source_output { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_source *source; + pa_source *source; /* NULL while being moved */ /* A source output can monitor just a single input of a sink, in which case we find it here */ pa_sink_input *direct_on_input; /* may be NULL */ @@ -79,7 +80,12 @@ struct pa_source_output { pa_sample_spec sample_spec; pa_channel_map channel_map; - pa_resample_method_t resample_method; + /* if TRUE then the source we are connected to is worth + * remembering, i.e. was explicitly chosen by the user and not + * automatically. module-stream-restore looks for this.*/ + pa_bool_t save_source:1; + + pa_resample_method_t requested_resample_method, actual_resample_method; /* Pushes a new memchunk into the output. Called from IO thread * context. */ @@ -139,7 +145,7 @@ struct pa_source_output { struct { pa_source_output_state_t state; - pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ + pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */ pa_sample_spec sample_spec; @@ -187,6 +193,8 @@ typedef struct pa_source_output_new_data { pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; + + pa_bool_t save_source:1; } pa_source_output_new_data; pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); @@ -194,11 +202,6 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map); void pa_source_output_new_data_done(pa_source_output_new_data *data); -typedef struct pa_source_output_move_hook_data { - pa_source_output *source_output; - pa_source *destination; -} pa_source_output_move_hook_data; - /* To be called by the implementing module only */ pa_source_output* pa_source_output_new( @@ -209,11 +212,11 @@ pa_source_output* pa_source_output_new( void pa_source_output_put(pa_source_output *o); void pa_source_output_unlink(pa_source_output*o); -void pa_source_output_set_name(pa_source_output *i, const char *name); +void pa_source_output_set_name(pa_source_output *o, const char *name); -pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec); +pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec); -void pa_source_output_cork(pa_source_output *i, pa_bool_t b); +void pa_source_output_cork(pa_source_output *o, pa_bool_t b); int pa_source_output_set_rate(pa_source_output *o, uint32_t rate); @@ -222,12 +225,21 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate); /* External code may request disconnection with this funcion */ void pa_source_output_kill(pa_source_output*o); -pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency); +pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency); + +pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p); pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); +pa_bool_t pa_source_output_may_move(pa_source_output *o); pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest); -int pa_source_output_move_to(pa_source_output *o, pa_source *dest); +int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save); + +/* The same as pa_source_output_move_to() but in two seperate steps, + * first the detaching from the old source, then the attaching to the + * new source */ +int pa_source_output_start_move(pa_source_output *o); +int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save); #define pa_source_output_get_state(o) ((o)->state) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 815ec271..0152b082 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -31,6 +31,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> #include <pulse/timeval.h> +#include <pulse/util.h> #include <pulsecore/source-output.h> #include <pulsecore/namereg.h> @@ -158,6 +159,9 @@ pa_source* pa_source_new( if (!data->muted_is_set) data->muted = FALSE; + if (data->card) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); @@ -172,8 +176,9 @@ pa_source* pa_source_new( s->flags = flags; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); - s->driver = pa_xstrdup(data->driver); + s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); s->module = data->module; + s->card = data->card; s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; @@ -182,10 +187,12 @@ pa_source* pa_source_new( s->n_corked = 0; s->monitor_of = NULL; - s->volume = data->volume; + s->virtual_volume = data->volume; + pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); + s->base_volume = PA_VOLUME_NORM; + s->n_volume_steps = PA_VOLUME_NORM+1; s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; - s->base_volume = PA_VOLUME_NORM; reset_callbacks(s); s->userdata = NULL; @@ -201,8 +208,8 @@ pa_source* pa_source_new( 0); s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); - s->thread_info.soft_muted = FALSE; + s->thread_info.soft_volume = s->soft_volume; + s->thread_info.soft_muted = s->muted; s->thread_info.state = s->state; s->thread_info.max_rewind = 0; s->thread_info.requested_latency_valid = FALSE; @@ -212,6 +219,9 @@ pa_source* pa_source_new( pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); + if (s->card) + pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0); + pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s", s->index, s->name, @@ -285,10 +295,13 @@ void pa_source_put(pa_source *s) { if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) { s->flags |= PA_SOURCE_DECIBEL_VOLUME; - s->thread_info.soft_volume = s->volume; + s->thread_info.soft_volume = s->soft_volume; s->thread_info.soft_muted = s->muted; } + if (s->flags & PA_SOURCE_DECIBEL_VOLUME) + s->n_volume_steps = PA_VOLUME_NORM+1; + pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); @@ -314,6 +327,9 @@ void pa_source_unlink(pa_source *s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sources, s, NULL); + if (s->card) + pa_idxset_remove_by_data(s->card->sources, s, NULL); + while ((o = pa_idxset_first(s->outputs, NULL))) { pa_assert(o != j); pa_source_output_kill(o); @@ -401,6 +417,58 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) { return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE); } +/* Called from main context */ +pa_queue *pa_source_move_all_start(pa_source *s) { + pa_queue *q; + pa_source_output *o, *n; + uint32_t idx; + + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); + + q = pa_queue_new(); + + for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) { + n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)); + + if (pa_source_output_start_move(o) >= 0) + pa_queue_push(q, pa_source_output_ref(o)); + } + + return q; +} + +/* Called from main context */ +void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) { + pa_source_output *o; + + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); + pa_assert(q); + + while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) { + if (pa_source_output_finish_move(o, s, save) < 0) + pa_source_output_unlink(o); + + pa_source_output_unref(o); + } + + pa_queue_free(q, NULL, NULL); +} + +/* Called from main context */ +void pa_source_move_all_fail(pa_queue *q) { + pa_source_output *o; + pa_assert(q); + + while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) { + pa_source_output_unlink(o); + pa_source_output_unref(o); + } + + pa_queue_free(q, NULL, NULL); +} + /* Called from IO thread context */ void pa_source_process_rewind(pa_source *s, size_t nbytes) { pa_source_output *o; @@ -429,9 +497,6 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); pa_assert(chunk); - if (s->thread_info.state != PA_SOURCE_RUNNING) - return; - if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; @@ -470,9 +535,6 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk * pa_assert(o->thread_info.direct_on_input); pa_assert(chunk); - if (s->thread_info.state != PA_SOURCE_RUNNING) - return; - if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; @@ -511,32 +573,38 @@ pa_usec_t pa_source_get_latency(pa_source *s) { /* Called from main thread */ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { - pa_bool_t changed; + pa_cvolume old_virtual_volume; + pa_bool_t virtual_volume_changed; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(volume); + pa_assert(pa_cvolume_valid(volume)); + pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); - changed = !pa_cvolume_equal(volume, &s->volume); - s->volume = *volume; + old_virtual_volume = s->virtual_volume; + s->virtual_volume = *volume; + virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); - if (s->set_volume && s->set_volume(s) < 0) - s->set_volume = NULL; + if (s->set_volume) { + pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); + s->set_volume(s); + } else + s->soft_volume = s->virtual_volume; - if (!s->set_volume) - pa_source_set_soft_volume(s, volume); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); - if (changed) + if (virtual_volume_changed) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } -/* Called from main thread */ +/* Called from main thread. Only to be called by source implementor */ void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) { pa_source_assert_ref(s); pa_assert(volume); if (PA_SOURCE_IS_LINKED(s->state)) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); else s->thread_info.soft_volume = *volume; } @@ -547,38 +615,36 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) { pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (s->refresh_volume || force_refresh) { - pa_cvolume old_volume = s->volume; + pa_cvolume old_virtual_volume = s->virtual_volume; - if (s->get_volume && s->get_volume(s) < 0) - s->get_volume = NULL; + if (s->get_volume) + s->get_volume(s); - if (!s->get_volume) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); - if (!pa_cvolume_equal(&old_volume, &s->volume)) + if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } - return &s->volume; + return &s->virtual_volume; } /* Called from main thread */ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { - pa_bool_t changed; + pa_bool_t old_muted; pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - changed = s->muted != mute; + old_muted = s->muted; s->muted = mute; - if (s->set_mute && s->set_mute(s) < 0) - s->set_mute = NULL; + if (s->set_mute) + s->set_mute(s); - if (!s->set_mute) - pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); - if (changed) + if (old_muted != s->muted) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } @@ -591,11 +657,10 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { if (s->refresh_muted || force_refresh) { pa_bool_t old_muted = s->muted; - if (s->get_mute && s->get_mute(s) < 0) - s->get_mute = NULL; + if (s->get_mute) + s->get_mute(s); - if (!s->get_mute) - pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); if (old_muted != s->muted) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); @@ -605,6 +670,21 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { } /* Called from main thread */ +pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) { + + pa_source_assert_ref(s); + + pa_proplist_update(s->proplist, mode, p); + + if (PA_SOURCE_IS_LINKED(s->state)) { + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + + return TRUE; +} + +/* Called from main thread */ void pa_source_set_description(pa_source *s, const char *description) { const char *old; pa_source_assert_ref(s); @@ -743,19 +823,17 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ } case PA_SOURCE_MESSAGE_SET_VOLUME: - s->thread_info.soft_volume = *((pa_cvolume*) userdata); + s->thread_info.soft_volume = s->soft_volume; return 0; - case PA_SOURCE_MESSAGE_SET_MUTE: - s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + case PA_SOURCE_MESSAGE_GET_VOLUME: return 0; - case PA_SOURCE_MESSAGE_GET_VOLUME: - *((pa_cvolume*) userdata) = s->thread_info.soft_volume; + case PA_SOURCE_MESSAGE_SET_MUTE: + s->thread_info.soft_muted = s->muted; return 0; case PA_SOURCE_MESSAGE_GET_MUTE: - *((pa_bool_t*) userdata) = s->thread_info.soft_muted; return 0; case PA_SOURCE_MESSAGE_SET_STATE: diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index b93e4ad0..8a91016a 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -41,21 +41,12 @@ typedef struct pa_source pa_source; #include <pulsecore/msgobject.h> #include <pulsecore/rtpoll.h> #include <pulsecore/source-output.h> +#include <pulsecore/card.h> +#include <pulsecore/queue.h> #define PA_MAX_OUTPUTS_PER_SOURCE 32 -typedef enum pa_source_state { - PA_SOURCE_INIT, - PA_SOURCE_RUNNING, - PA_SOURCE_SUSPENDED, - PA_SOURCE_IDLE, - PA_SOURCE_UNLINKED -} pa_source_state_t; - -static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) { - return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; -} - +/* Returns true if source is linked: registered and accessible from client side. */ static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED; } @@ -73,6 +64,7 @@ struct pa_source { pa_proplist *proplist; pa_module *module; /* may be NULL */ + pa_card *card; /* may be NULL */ pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -81,10 +73,11 @@ struct pa_source { unsigned n_corked; pa_sink *monitor_of; /* may be NULL */ - pa_cvolume volume; - pa_bool_t muted; - pa_volume_t base_volume; /* shall be constant */ + unsigned n_volume_steps; /* shall be constant */ + + pa_cvolume virtual_volume, soft_volume; + pa_bool_t muted:1; pa_bool_t refresh_volume:1; pa_bool_t refresh_muted:1; @@ -103,23 +96,23 @@ struct pa_source { * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message * will be sent to the IO thread instead. If refresh_volume is * FALSE neither this function is called nor a message is sent. */ - int (*get_volume)(pa_source *s); /* dito */ + void (*get_volume)(pa_source *s); /* dito */ /* Called when the volume shall be changed. Called from main loop * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message * will be sent to the IO thread instead. */ - int (*set_volume)(pa_source *s); /* dito */ + void (*set_volume)(pa_source *s); /* dito */ /* Called when the mute setting is queried. Called from main loop * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message * will be sent to the IO thread instead. If refresh_mute is * FALSE neither this function is called nor a message is sent.*/ - int (*get_mute)(pa_source *s); /* dito */ + void (*get_mute)(pa_source *s); /* dito */ /* Called when the mute setting shall be changed. Called from main * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE * message will be sent to the IO thread instead. */ - int (*set_mute)(pa_source *s); /* dito */ + void (*set_mute)(pa_source *s); /* dito */ /* Called when a the requested latency is changed. Called from IO * thread context. */ @@ -130,6 +123,7 @@ struct pa_source { struct { pa_source_state_t state; pa_hashmap *outputs; + pa_cvolume soft_volume; pa_bool_t soft_muted:1; @@ -174,6 +168,7 @@ typedef struct pa_source_new_data { const char *driver; pa_module *module; + pa_card *card; pa_sample_spec sample_spec; pa_channel_map channel_map; @@ -196,7 +191,7 @@ void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *v void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); void pa_source_new_data_done(pa_source_new_data *data); -/* To be called exclusively by the source driver, from main context */ +/*** To be called exclusively by the source driver, from main context */ pa_source* pa_source_new( pa_core *core, @@ -215,7 +210,9 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t void pa_source_detach(pa_source *s); void pa_source_attach(pa_source *s); -/* May be called by everyone, from main context */ +void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); + +/*** May be called by everyone, from main context */ /* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_source_get_latency(pa_source *s); @@ -229,17 +226,23 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend); int pa_source_suspend_all(pa_core *c, pa_bool_t suspend); void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); -void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); void pa_source_set_mute(pa_source *source, pa_bool_t mute); pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); +pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p); + unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */ unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */ #define pa_source_get_state(s) ((pa_source_state_t) (s)->state) -/* To be called exclusively by the source driver, from IO context */ +/* Moves all inputs away, and stores them in pa_queue */ +pa_queue *pa_source_move_all_start(pa_source *s); +void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save); +void pa_source_move_all_fail(pa_queue *q); + +/*** To be called exclusively by the source driver, from IO context */ void pa_source_post(pa_source*s, const pa_memchunk *chunk); void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk); @@ -255,7 +258,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s); void pa_source_set_max_rewind(pa_source *s, size_t max_rewind); void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency); -/* To be called exclusively by source output drivers, from IO context */ +/*** To be called exclusively by source output drivers, from IO context */ void pa_source_invalidate_requested_latency(pa_source *s); diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index 540faef9..8b952788 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -180,3 +180,9 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { size *= 2; } } + +pa_bool_t pa_strbuf_isempty(pa_strbuf *sb) { + pa_assert(sb); + + return sb->length <= 0; +} diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h index ac68d7be..1d2a588f 100644 --- a/src/pulsecore/strbuf.h +++ b/src/pulsecore/strbuf.h @@ -23,6 +23,7 @@ ***/ #include <pulse/gccmacro.h> +#include <pulsecore/macro.h> typedef struct pa_strbuf pa_strbuf; @@ -35,4 +36,6 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_A void pa_strbuf_puts(pa_strbuf *sb, const char *t); void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m); +pa_bool_t pa_strbuf_isempty(pa_strbuf *sb); + #endif diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index 19288eeb..1a99e924 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -37,6 +37,10 @@ typedef struct pa_tagstruct pa_tagstruct; +/* Due to a stupid design flaw, proplists may only be at the END of a + * packet or not before a STRING! Don't forget that! We can't really + * fix this without breaking compat. */ + enum { PA_TAG_INVALID = 0, PA_TAG_STRING = 't', diff --git a/src/pulsecore/vector.h b/src/pulsecore/vector.h new file mode 100644 index 00000000..076bd6c0 --- /dev/null +++ b/src/pulsecore/vector.h @@ -0,0 +1,97 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman <ossman@cendio.se> 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, + 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. +***/ + +#include <inttypes.h> + +/* First, define HAVE_VECTOR if we have the gcc vector extensions at all */ +#if defined(__SSE2__) || defined(__ALTIVEC__) +#define HAVE_VECTOR + + +/* This is supposed to be portable to different SIMD instruction + * sets. We define vector types for different base types: uint8_t, + * int16_t, int32_t, float. The vector type is a union. The fields .i, + * .u, .f are arrays for accessing the separate elements of a + * vector. .v is a gcc vector type of the right format. .m is the + * vector in the type the SIMD extenstion specific intrinsics API + * expects. PA_xxx_VECTOR_SIZE is the size of the + * entries. PA_xxxx_VECTOR_MAKE constructs a gcc vector variable with + * the same value in all elements. */ + +#ifdef __SSE2__ + +#include <xmmintrin.h> +#include <emmintrin.h> + +#define PA_UINT8_VECTOR_SIZE 16 +#define PA_INT16_VECTOR_SIZE 8 +#define PA_INT32_VECTOR_SIZE 4 +#define PA_FLOAT_VECTOR_SIZE 4 + +#define PA_UINT8_VECTOR_MAKE(x) (pa_v16qi) { x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x } +#define PA_INT16_VECTOR_MAKE(x) (pa_v8hi) { x, x, x, x, x, x, x, x } +#define PA_INT32_VECTOR_MAKE(x) (pa_v4si) { x, x, x, x } +#define PA_FLOAT_VECTOR_MAKE(x) (pa_v4fi) { x, x, x, x } + +#endif + +/* uint8_t vector */ +typedef uint8_t pa_v16qi __attribute__ ((vector_size (PA_UINT8_VECTOR_SIZE * sizeof(uint8_t)))); +typedef union pa_uint8_vector { + uint8_t u[PA_UINT8_VECTOR_SIZE]; + pa_v16qi v; +#ifdef __SSE2__ + __m128i m; +#endif +} pa_uint8_vector_t; + +/* int16_t vector*/ +typedef int16_t pa_v8hi __attribute__ ((vector_size (PA_INT16_VECTOR_SIZE * sizeof(int16_t)))); +typedef union pa_int16_vector { + int16_t i[PA_INT16_VECTOR_SIZE]; + pa_v8hi v; +#ifdef __SSE2__ + __m128i m; +#endif +} pa_int16_vector_t; + +/* int32_t vector */ +typedef int32_t pa_v4si __attribute__ ((vector_size (PA_INT32_VECTOR_SIZE * sizeof(int32_t)))); +typedef union pa_int32_vector { + int32_t i[PA_INT32_VECTOR_SIZE]; + pa_v4si v; +#ifdef __SSE2__ + __m128i m; +#endif +} pa_int32_vector_t; + +/* float vector */ +typedef float pa_v4sf __attribute__ ((vector_size (PA_FLOAT_VECTOR_SIZE * sizeof(float)))); +typedef union pa_float_vector { + float f[PA_FLOAT_VECTOR_SIZE]; + pa_v4sf v; +#ifdef __SSE2__ + __m128 m; +#endif +} pa_float_vector_t; + +#endif |