summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/asyncq.c16
-rw-r--r--src/pulsecore/atomic.h73
-rw-r--r--src/pulsecore/autoload.c202
-rw-r--r--src/pulsecore/autoload.h58
-rw-r--r--src/pulsecore/bitset.c67
-rw-r--r--src/pulsecore/bitset.h37
-rw-r--r--src/pulsecore/card.c254
-rw-r--r--src/pulsecore/card.h100
-rw-r--r--src/pulsecore/cli-command.c374
-rw-r--r--src/pulsecore/cli-text.c264
-rw-r--r--src/pulsecore/cli-text.h3
-rw-r--r--src/pulsecore/cli.c19
-rw-r--r--src/pulsecore/client.c48
-rw-r--r--src/pulsecore/client.h17
-rw-r--r--src/pulsecore/core-scache.c42
-rw-r--r--src/pulsecore/core-scache.h3
-rw-r--r--src/pulsecore/core-util.c60
-rw-r--r--src/pulsecore/core-util.h8
-rw-r--r--src/pulsecore/core.c23
-rw-r--r--src/pulsecore/core.h41
-rw-r--r--src/pulsecore/endianmacros.h38
-rw-r--r--src/pulsecore/envelope.c1
-rw-r--r--src/pulsecore/log.c8
-rw-r--r--src/pulsecore/log.h2
-rw-r--r--src/pulsecore/module.c101
-rw-r--r--src/pulsecore/module.h14
-rw-r--r--src/pulsecore/mutex-posix.c20
-rw-r--r--src/pulsecore/mutex.h7
-rw-r--r--src/pulsecore/namereg.c120
-rw-r--r--src/pulsecore/namereg.h13
-rw-r--r--src/pulsecore/native-common.h14
-rw-r--r--src/pulsecore/pdispatch.c8
-rw-r--r--src/pulsecore/play-memblockq.c4
-rw-r--r--src/pulsecore/protocol-esound.c40
-rw-r--r--src/pulsecore/protocol-http.c10
-rw-r--r--src/pulsecore/protocol-native.c386
-rw-r--r--src/pulsecore/protocol-simple.c24
-rw-r--r--src/pulsecore/ratelimit.c75
-rw-r--r--src/pulsecore/ratelimit.h46
-rw-r--r--src/pulsecore/resampler.c32
-rw-r--r--src/pulsecore/resampler.h4
-rw-r--r--src/pulsecore/rtclock.c26
-rw-r--r--src/pulsecore/rtclock.h1
-rw-r--r--src/pulsecore/sample-util.c329
-rw-r--r--src/pulsecore/sconv-s16be.c30
-rw-r--r--src/pulsecore/sconv-s16be.h30
-rw-r--r--src/pulsecore/sconv-s16le.c225
-rw-r--r--src/pulsecore/sconv-s16le.h30
-rw-r--r--src/pulsecore/sconv.c16
-rw-r--r--src/pulsecore/shared.c9
-rw-r--r--src/pulsecore/sink-input.c391
-rw-r--r--src/pulsecore/sink-input.h78
-rw-r--r--src/pulsecore/sink.c348
-rw-r--r--src/pulsecore/sink.h64
-rw-r--r--src/pulsecore/socket-util.c8
-rw-r--r--src/pulsecore/sound-file-stream.c4
-rw-r--r--src/pulsecore/source-output.c199
-rw-r--r--src/pulsecore/source-output.h38
-rw-r--r--src/pulsecore/source.c170
-rw-r--r--src/pulsecore/source.h51
-rw-r--r--src/pulsecore/strbuf.c6
-rw-r--r--src/pulsecore/strbuf.h3
-rw-r--r--src/pulsecore/tagstruct.h4
-rw-r--r--src/pulsecore/vector.h97
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