diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/client.c | 42 | ||||
-rw-r--r-- | src/pulsecore/client.h | 12 | ||||
-rw-r--r-- | src/pulsecore/conf-parser.c | 55 | ||||
-rw-r--r-- | src/pulsecore/conf-parser.h | 15 | ||||
-rw-r--r-- | src/pulsecore/core-util.c | 51 | ||||
-rw-r--r-- | src/pulsecore/core-util.h | 4 | ||||
-rw-r--r-- | src/pulsecore/core.h | 4 | ||||
-rw-r--r-- | src/pulsecore/envelope.c | 7 | ||||
-rw-r--r-- | src/pulsecore/modargs.c | 179 | ||||
-rw-r--r-- | src/pulsecore/native-common.h | 4 | ||||
-rw-r--r-- | src/pulsecore/play-memblockq.c | 2 | ||||
-rw-r--r-- | src/pulsecore/proplist-util.c | 25 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 89 | ||||
-rw-r--r-- | src/pulsecore/resampler.c | 12 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 137 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 26 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 1 | ||||
-rw-r--r-- | src/pulsecore/sound-file-stream.c | 2 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 52 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 14 |
20 files changed, 516 insertions, 217 deletions
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 18004412..7ca7b97c 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -73,6 +73,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) { c->userdata = NULL; c->kill = NULL; + c->send_event = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); @@ -126,8 +127,49 @@ void pa_client_kill(pa_client *c) { void pa_client_set_name(pa_client *c, const char *name) { pa_assert(c); + pa_assert(name); pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name); pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); + + pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c); + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); +} + +void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p) { + pa_assert(c); + pa_assert(p); + + pa_proplist_update(c->proplist, mode, p); + + pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); } + +void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_client_send_event_hook_data hook_data; + + pa_assert(c); + pa_assert(event); + + if (!c->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.client = c; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0) + goto finish; + + c->send_event(c, event, data); + +finish: + + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index 48e9bc7a..845a8bab 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -48,6 +48,8 @@ struct pa_client { void *userdata; void (*kill)(pa_client *c); + + void (*send_event)(pa_client *c, const char *name, pa_proplist *data); }; typedef struct pa_client_new_data { @@ -71,4 +73,14 @@ void pa_client_kill(pa_client *c); /* Rename the client */ void pa_client_set_name(pa_client *c, const char *name); +void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p); + +void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data); + +typedef struct pa_client_send_event_hook_data { + pa_client *client; + const char *event; + pa_proplist *data; +} pa_client_send_event_hook_data; + #endif diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index ef6d6bb6..b7ec2b3c 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -40,17 +40,19 @@ #define COMMENTS "#;\n" /* Run the user supplied parser for an assignment */ -static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { +static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { pa_assert(filename); pa_assert(t); pa_assert(lvalue); pa_assert(rvalue); for (; t->parse; t++) - if (!strcmp(lvalue, t->lvalue)) - return t->parse(filename, line, lvalue, rvalue, t->data, userdata); + if (!t->lvalue || + (pa_streq(lvalue, t->lvalue) && + ((!section && !t->section) || pa_streq(section, t->section)))) + return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata); - pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue); + pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section)); return -1; } @@ -83,8 +85,10 @@ static char *strip(char *s) { } /* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) { - char *e, *c, *b = l+strspn(l, WHITESPACE); +static int parse_line(const char *filename, unsigned line, char **section, const pa_config_item *t, char *l, void *userdata) { + char *e, *c, *b; + + b = l+strspn(l, WHITESPACE); if ((c = strpbrk(b, COMMENTS))) *c = 0; @@ -92,6 +96,22 @@ static int parse_line(const char *filename, unsigned line, const pa_config_item if (!*b) return 0; + if (*b == '[') { + size_t k; + + k = strlen(b); + pa_assert(k > 0); + + if (b[k-1] != ']') { + pa_log("[%s:%u] Invalid section header.", filename, line); + return -1; + } + + pa_xfree(*section); + *section = pa_xstrndup(b+1, k-2); + return 0; + } + if (!(e = strchr(b, '='))) { pa_log("[%s:%u] Missing '='.", filename, line); return -1; @@ -100,7 +120,7 @@ static int parse_line(const char *filename, unsigned line, const pa_config_item *e = 0; e++; - return next_assignment(filename, line, t, strip(b), strip(e), userdata); + return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata); } /* Go through the file and parse each line */ @@ -108,6 +128,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void int r = -1; unsigned line = 0; int do_close = !f; + char *section = NULL; pa_assert(filename); pa_assert(t); @@ -118,29 +139,29 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void goto finish; } - pa_log_warn("Failed to open configuration file '%s': %s", - filename, pa_cstrerror(errno)); + pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); goto finish; } while (!feof(f)) { char l[256]; + if (!fgets(l, sizeof(l), f)) { if (feof(f)) break; - pa_log_warn("Failed to read configuration file '%s': %s", - filename, pa_cstrerror(errno)); + pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno)); goto finish; } - if (parse_line(filename, ++line, t, l, userdata) < 0) + if (parse_line(filename, ++line, §ion, t, l, userdata) < 0) goto finish; } r = 0; finish: + pa_xfree(section); if (do_close && f) fclose(f); @@ -148,7 +169,7 @@ finish: return r; } -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +int pa_config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { int *i = data; int32_t k; @@ -166,7 +187,7 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, return 0; } -int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { unsigned *u = data; uint32_t k; @@ -184,7 +205,7 @@ int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lv return 0; } -int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { size_t *i = data; uint32_t k; @@ -202,7 +223,7 @@ int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue return 0; } -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { int k; pa_bool_t *b = data; @@ -221,7 +242,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue return 0; } -int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { +int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { char **s = data; pa_assert(filename); diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h index 48a0fd26..f8f059fe 100644 --- a/src/pulsecore/conf-parser.h +++ b/src/pulsecore/conf-parser.h @@ -27,11 +27,14 @@ /* An abstract parser for simple, line based, shallow configuration * files consisting of variable assignments only. */ +typedef int (*pa_config_parser_cb_t)(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); + /* Wraps info for parsing a specific configuration variable */ typedef struct pa_config_item { const char *lvalue; /* name of the variable */ - int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */ + pa_config_parser_cb_t parse; /* Function that is called to parse the variable's value */ void *data; /* Where to store the variable's data */ + const char *section; } pa_config_item; /* The configuration file parsing routine. Expects a table of @@ -40,10 +43,10 @@ typedef struct pa_config_item { int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata); /* Generic parsers for integers, size_t, booleans and strings */ -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); #endif diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index e65b1796..ad6c6ca9 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -88,6 +88,10 @@ #include <samplerate.h> #endif +#ifdef __APPLE__ +#include <xlocale.h> +#endif + #include <pulse/xmalloc.h> #include <pulse/util.h> #include <pulse/utf8.h> @@ -97,6 +101,7 @@ #include <pulsecore/log.h> #include <pulsecore/macro.h> #include <pulsecore/thread.h> +#include <pulsecore/strbuf.h> #include "core-util.h" @@ -2553,3 +2558,49 @@ unsigned pa_ncpus(void) { return ncpus <= 0 ? 1 : (unsigned) ncpus; } + +char *pa_replace(const char*s, const char*a, const char *b) { + pa_strbuf *sb; + size_t an; + + pa_assert(s); + pa_assert(a); + pa_assert(b); + + an = strlen(a); + sb = pa_strbuf_new(); + + for (;;) { + const char *p; + + if (!(p = strstr(s, a))) + break; + + pa_strbuf_putsn(sb, s, p-s); + pa_strbuf_puts(sb, b); + s = p + an; + } + + pa_strbuf_puts(sb, s); + + return pa_strbuf_tostring_free(sb); +} + +char *pa_unescape(char *p) { + char *s, *d; + pa_bool_t escaped = FALSE; + + for (s = p, d = p; *s; s++) { + if (!escaped && *s == '\\') { + escaped = TRUE; + continue; + } + + *(d++) = *s; + escaped = FALSE; + } + + *d = 0; + + return p; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 18901f47..442815f1 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -215,4 +215,8 @@ void pa_reduce(unsigned *num, unsigned *den); unsigned pa_ncpus(void); +char *pa_replace(const char*s, const char*a, const char *b); + +char *pa_unescape(char *p); + #endif diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 8fa9761f..53e2d2c3 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -74,6 +74,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, PA_CORE_HOOK_SINK_INPUT_SET_VOLUME, + PA_CORE_HOOK_SINK_INPUT_SEND_EVENT, PA_CORE_HOOK_SOURCE_OUTPUT_NEW, PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, PA_CORE_HOOK_SOURCE_OUTPUT_PUT, @@ -84,9 +85,12 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL, PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT, PA_CORE_HOOK_CLIENT_NEW, PA_CORE_HOOK_CLIENT_PUT, PA_CORE_HOOK_CLIENT_UNLINK, + PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED, + PA_CORE_HOOK_CLIENT_SEND_EVENT, PA_CORE_HOOK_CARD_NEW, PA_CORE_HOOK_CARD_PUT, PA_CORE_HOOK_CARD_UNLINK, diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index b57b6435..fd6a9487 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -744,6 +744,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) { break; } + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + /* FIXME */ + pa_assert_not_reached(); + case PA_SAMPLE_MAX: case PA_SAMPLE_INVALID: pa_assert_not_reached(); diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index 9e60125e..866e6e0c 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -79,106 +79,111 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co } pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { - pa_hashmap *map = NULL; + enum { + WHITESPACE, + KEY, + VALUE_START, + VALUE_SIMPLE, + VALUE_DOUBLE_QUOTES, + VALUE_TICKS + } state; + + const char *p, *key = NULL, *value = NULL; + size_t key_len = 0, value_len = 0; + pa_hashmap *map; map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (args) { - enum { - WHITESPACE, - KEY, - VALUE_START, - VALUE_SIMPLE, - VALUE_DOUBLE_QUOTES, - VALUE_TICKS - } state; - - const char *p, *key, *value; - size_t key_len = 0, value_len = 0; - - key = value = NULL; - state = WHITESPACE; - for (p = args; *p; p++) { - switch (state) { - case WHITESPACE: - if (*p == '=') + if (!args) + return (pa_modargs*) map; + + state = WHITESPACE; + + for (p = args; *p; p++) { + switch (state) { + + case WHITESPACE: + if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; + + case KEY: + if (*p == '=') + state = VALUE_START; + else if (isspace(*p)) + goto fail; + else + key_len++; + break; + + case VALUE_START: + if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) goto fail; - else if (!isspace(*p)) { - key = p; - state = KEY; - key_len = 1; - } - break; - case KEY: - if (*p == '=') - state = VALUE_START; - else if (isspace(*p)) + state = WHITESPACE; + } else { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; + + case VALUE_SIMPLE: + if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) goto fail; - else - key_len++; - break; - case VALUE_START: - if (*p == '\'') { - state = VALUE_TICKS; - value = p+1; - value_len = 0; - } else if (*p == '"') { - state = VALUE_DOUBLE_QUOTES; - value = p+1; - value_len = 0; - } else if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else { - state = VALUE_SIMPLE; - value = p; - value_len = 1; - } - break; - case VALUE_SIMPLE: - if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_DOUBLE_QUOTES: - if (*p == '"') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_TICKS: - if (*p == '\'') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - } + state = WHITESPACE; + } else + value_len++; + break; + + case VALUE_DOUBLE_QUOTES: + if (*p == '"') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + + case VALUE_TICKS: + if (*p == '\'') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; } + } - if (state == VALUE_START) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - } else if (state == VALUE_SIMPLE) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) - goto fail; - } else if (state != WHITESPACE) + if (state == VALUE_START) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) goto fail; - } + } else if (state == VALUE_SIMPLE) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) + goto fail; + } else if (state != WHITESPACE) + goto fail; return (pa_modargs*) map; fail: - if (map) - pa_modargs_free((pa_modargs*) map); + pa_modargs_free((pa_modargs*) map); return NULL; } diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index b31a5da1..6951e10a 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -157,6 +157,10 @@ enum { PA_COMMAND_GET_CARD_INFO_LIST, PA_COMMAND_SET_CARD_PROFILE, + PA_COMMAND_CLIENT_EVENT, + PA_COMMAND_PLAYBACK_STREAM_EVENT, + PA_COMMAND_RECORD_STREAM_EVENT, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index d1631264..44aa6bf0 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -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_virtual_volume(&data, volume); + pa_sink_input_new_data_set_volume(&data, volume); pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); pa_sink_input_new(&u->sink_input, sink->core, &data, 0); diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c index 4920c27e..8a447cf7 100644 --- a/src/pulsecore/proplist-util.c +++ b/src/pulsecore/proplist-util.c @@ -27,6 +27,13 @@ #include <locale.h> #include <dlfcn.h> +#ifdef __APPLE__ +#include <crt_externs.h> +#define environ (*_NSGetEnviron()) +#elif !HAVE_DECL_ENVIRON +extern char **environ; +#endif + #include <pulse/proplist.h> #include <pulse/utf8.h> #include <pulse/xmalloc.h> @@ -37,10 +44,8 @@ #include "proplist-util.h" void pa_init_proplist(pa_proplist *p) { -#if !HAVE_DECL_ENVIRON - extern char **environ; -#endif char **e; + const char *pp; pa_assert(p); @@ -64,17 +69,21 @@ void pa_init_proplist(pa_proplist *p) { k = pa_xstrndup(*e+11, kl); - if (pa_proplist_contains(p, k)) { - pa_xfree(k); - continue; - } - pa_proplist_sets(p, k, *e+11+kl+1); pa_xfree(k); } } } + if ((pp = getenv("PULSE_PROP"))) { + pa_proplist *t; + + if ((t = pa_proplist_from_string(pp))) { + pa_proplist_update(p, PA_UPDATE_REPLACE, t); + pa_proplist_free(t); + } + } + if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) { char t[32]; pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid()); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 21a2cfbc..c3032618 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i); static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl); static void native_connection_send_memblock(pa_native_connection *c); static void playback_stream_request_bytes(struct playback_stream*s); @@ -216,6 +217,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend); static void source_output_moved_cb(pa_source_output *o); static pa_usec_t source_output_get_latency_cb(pa_source_output *o); +static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl); static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); @@ -595,7 +597,7 @@ static record_stream* record_stream_new( int *ret) { record_stream *s; - pa_source_output *source_output; + pa_source_output *source_output = NULL; size_t base; pa_source_output_new_data data; @@ -618,7 +620,7 @@ static record_stream* record_stream_new( if (peak_detect) data.resample_method = PA_RESAMPLER_PEAKS; - *ret = pa_source_output_new(&source_output, c->protocol->core, &data, flags); + *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags); pa_source_output_new_data_done(&data); @@ -636,6 +638,7 @@ static record_stream* record_stream_new( s->source_output->get_latency = source_output_get_latency_cb; s->source_output->moved = source_output_moved_cb; s->source_output->suspend = source_output_suspend_cb; + s->source_output->send_event = source_output_send_event_cb; s->source_output->userdata = s; fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize); @@ -971,7 +974,7 @@ static playback_stream* playback_stream_new( int *ret) { playback_stream *s, *ssync; - pa_sink_input *sink_input; + pa_sink_input *sink_input = NULL; pa_memchunk silence; uint32_t idx; int64_t start_index; @@ -1018,12 +1021,12 @@ 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_virtual_volume(&data, volume); + pa_sink_input_new_data_set_volume(&data, volume); if (muted_set) pa_sink_input_new_data_set_muted(&data, muted); data.sync_base = ssync ? ssync->sink_input : NULL; - *ret = pa_sink_input_new(&sink_input, c->protocol->core, &data, flags); + *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags); pa_sink_input_new_data_done(&data); @@ -1048,6 +1051,7 @@ static playback_stream* playback_stream_new( s->sink_input->kill = sink_input_kill_cb; s->sink_input->moved = sink_input_moved_cb; s->sink_input->suspend = sink_input_suspend_cb; + s->sink_input->send_event = sink_input_send_event_cb; s->sink_input->userdata = s; start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; @@ -1494,6 +1498,27 @@ static void sink_input_kill_cb(pa_sink_input *i) { } /* Called from main context */ +static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) { + playback_stream *s; + pa_tagstruct *t; + + pa_sink_input_assert_ref(i); + s = PLAYBACK_STREAM(i->userdata); + playback_stream_assert_ref(s); + + if (s->connection->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(s->connection->pstream, t); +} + +/* Called from main context */ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) { playback_stream *s; pa_tagstruct *t; @@ -1595,6 +1620,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { } /* Called from main context */ +static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) { + record_stream *s; + pa_tagstruct *t; + + pa_source_output_assert_ref(o); + s = RECORD_STREAM(o->userdata); + record_stream_assert_ref(s); + + if (s->connection->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(s->connection->pstream, t); +} + +/* Called from main context */ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) { record_stream *s; pa_tagstruct *t; @@ -2295,11 +2341,9 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t return; } - pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p); + pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p); pa_proplist_free(p); - pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); - reply = reply_new(tag); if (c->version >= 13) @@ -3551,8 +3595,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY); - pa_proplist_update(s->sink_input->proplist, mode, p); - pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index); + pa_sink_input_update_proplist(s->sink_input, mode, p); } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) { record_stream *s; @@ -3560,13 +3603,11 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t s = pa_idxset_get_by_index(c->record_streams, idx); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); - pa_proplist_update(s->source_output->proplist, mode, p); - pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index); + pa_source_output_update_proplist(s->source_output, mode, p); } else { pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST); - pa_proplist_update(c->client->proplist, mode, p); - pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + pa_client_update_proplist(c->client, mode, p); } pa_pstream_send_simple_ack(c->pstream, tag); @@ -4193,6 +4234,25 @@ static void client_kill_cb(pa_client *c) { pa_log_info("Connection killed."); } +static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) { + pa_tagstruct *t; + pa_native_connection *c; + + pa_assert(client); + c = PA_NATIVE_CONNECTION(client->userdata); + pa_native_connection_assert_ref(c); + + if (c->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(c->pstream, t); +} + /*** module entry points ***/ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { @@ -4270,6 +4330,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati c->client = client; c->client->kill = client_kill_cb; + c->client->send_event = client_send_event_cb; c->client->userdata = c; c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index be390db7..78ad5530 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -156,16 +156,6 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_PEAKS] = peaks_init, }; -static inline size_t sample_size(pa_sample_format_t f) { - pa_sample_spec ss = { - .format = f, - .rate = 0, - .channels = 1 - }; - - return pa_sample_size(&ss); -} - pa_resampler* pa_resampler_new( pa_mempool *pool, const pa_sample_spec *a, @@ -275,7 +265,7 @@ pa_resampler* pa_resampler_new( pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format)); - r->w_sz = sample_size(r->work_format); + r->w_sz = pa_sample_size_of_format(r->work_format); if (r->i_ss.format == r->work_format) r->to_work_format_func = NULL; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index fc87d5d8..22419ee5 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -72,18 +72,23 @@ 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_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { +void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); - if ((data->soft_volume_is_set = !!volume)) - data->soft_volume = *volume; + if ((data->volume_is_set = !!volume)) + data->volume = *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_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) { pa_assert(data); + pa_assert(volume_factor); - if ((data->virtual_volume_is_set = !!volume)) - data->virtual_volume = *volume; + if (data->volume_factor_is_set) + pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor); + else { + data->volume_factor_is_set = TRUE; + data->volume_factor = *volume_factor; + } } void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -117,6 +122,7 @@ static void reset_callbacks(pa_sink_input *i) { i->get_latency = NULL; i->state_change = NULL; i->may_move_to = NULL; + i->send_event = NULL; } /* Called from main context */ @@ -136,6 +142,9 @@ int pa_sink_input_new( pa_assert(core); pa_assert(data); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0) return r; @@ -166,35 +175,28 @@ int pa_sink_input_new( pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); - if (!data->virtual_volume_is_set) { + if (!data->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->volume = *pa_sink_get_volume(data->sink, FALSE); + pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map); + data->volume_is_absolute = TRUE; + } else { + pa_cvolume_reset(&data->volume, data->sample_spec.channels); + data->volume_is_absolute = FALSE; + } data->save_volume = FALSE; - - } 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_val_if_fail(pa_cvolume_valid(&data->virtual_volume), -PA_ERR_INVALID); - pa_return_val_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID); - if (!data->soft_volume_is_set) - data->soft_volume = data->virtual_volume; + if (!data->volume_factor_is_set) + pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels); - pa_return_val_if_fail(pa_cvolume_valid(&data->soft_volume), -PA_ERR_INVALID); - pa_return_val_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_valid(&data->volume_factor), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID); if (!data->muted_is_set) data->muted = FALSE; @@ -216,17 +218,13 @@ int 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->soft_volume, &original_cm, &data->channel_map); - pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); - if (data->client) - pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); - if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0) return r; @@ -271,8 +269,19 @@ int pa_sink_input_new( i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; - i->virtual_volume = data->virtual_volume; - i->soft_volume = data->soft_volume; + if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) { + /* When the 'absolute' bool is not set then we'll treat the volume + * as relative to the sink volume even in flat volume mode */ + + pa_cvolume t = *pa_sink_get_volume(data->sink, FALSE); + pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); + + pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t); + } else + i->virtual_volume = data->volume; + + i->volume_factor = data->volume_factor; + pa_cvolume_init(&i->soft_volume); i->save_volume = data->save_volume; i->save_sink = data->save_sink; i->save_muted = data->save_muted; @@ -502,9 +511,6 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->process_rewind); pa_assert(i->kill); - 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; update_n_corked(i, state); @@ -515,7 +521,11 @@ void pa_sink_input_put(pa_sink_input *i) { pa_cvolume new_volume; pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); - } + } else + pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor); + + i->thread_info.soft_volume = i->soft_volume; + i->thread_info.muted = i->muted; pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); @@ -886,8 +896,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo /* OK, we are in normal volume mode. The volume only affects * ourselves */ - - i->soft_volume = *volume; + pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor); /* 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); @@ -932,18 +941,16 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) { } /* 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); +void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) { + pa_sink_input_assert_ref(i); + pa_assert(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); - } + pa_proplist_update(i->proplist, mode, p); - return TRUE; + 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); + } } /* Called from main context */ @@ -1078,7 +1085,7 @@ int pa_sink_input_start_move(pa_sink_input *i) { /* Make the absolute volume relative */ i->virtual_volume = i->soft_volume; - pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels); + i->soft_volume = i->volume_factor; /* We might need to update the sink's volume if we are in flat * volume mode. */ @@ -1440,3 +1447,31 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { return ret; } + +/* Called from main context */ +void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_sink_input_send_event_hook_data hook_data; + + pa_sink_input_assert_ref(i); + pa_assert(event); + + if (!i->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.sink_input = i; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0) + goto finish; + + i->send_event(i, event, data); + +finish: + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index d82a3a62..b4f05319 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -91,7 +91,7 @@ struct pa_sink_input { pa_sink_input *sync_prev, *sync_next; - pa_cvolume virtual_volume, soft_volume; + pa_cvolume virtual_volume, soft_volume, volume_factor; pa_bool_t muted:1; /* if TRUE then the source we are connected to and/or the volume @@ -171,6 +171,10 @@ struct pa_sink_input { * be allowed */ pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */ + /* If non-NULL this function is used to dispatch asynchronous + * control events. */ + void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data); + struct { pa_sink_input_state_t state; pa_atomic_t drained; @@ -217,6 +221,12 @@ enum { PA_SINK_INPUT_MESSAGE_MAX }; +typedef struct pa_sink_input_send_event_hook_data { + pa_sink_input *sink_input; + const char *event; + pa_proplist *data; +} pa_sink_input_send_event_hook_data; + typedef struct pa_sink_input_new_data { pa_proplist *proplist; @@ -233,16 +243,16 @@ typedef struct pa_sink_input_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; - pa_cvolume virtual_volume, soft_volume; + pa_cvolume volume, volume_factor; pa_bool_t muted:1; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; - pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1; + pa_bool_t volume_is_set:1, volume_factor_is_set:1; pa_bool_t muted_is_set:1; - pa_bool_t virtual_volume_is_absolute:1; + pa_bool_t volume_is_absolute:1; pa_bool_t save_sink:1, save_volume:1, save_muted:1; } pa_sink_input_new_data; @@ -250,8 +260,8 @@ typedef struct 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_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_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); 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); @@ -294,10 +304,12 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo 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, 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); +void 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); +void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data); + 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? */ diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 558da8c6..eadef809 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -992,6 +992,7 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *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); + pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor); /* 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); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 67b072b6..c3de3067 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -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_virtual_volume(&data, volume); + pa_sink_input_new_data_set_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 c5cb16d8..382fb88c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -92,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) { o->get_latency = NULL; o->state_change = NULL; o->may_move_to = NULL; + o->send_event = NULL; } /* Called from main context */ @@ -110,6 +111,9 @@ int pa_source_output_new( pa_assert(core); pa_assert(data); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0) return r; @@ -159,9 +163,6 @@ int pa_source_output_new( pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); - if (data->client) - pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); - if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0) return r; @@ -615,18 +616,16 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { } /* 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); +void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { + pa_source_output_assert_ref(o); + pa_assert(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); - } + pa_proplist_update(o->proplist, mode, p); - return TRUE; + 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); + } } /* Called from main context */ @@ -869,3 +868,30 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return -PA_ERR_NOTIMPLEMENTED; } + +void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_source_output_send_event_hook_data hook_data; + + pa_source_output_assert_ref(o); + pa_assert(event); + + if (!o->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.source_output = o; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0) + goto finish; + + o->send_event(o, event, data); + +finish: + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 91f28f94..9369568c 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -143,6 +143,10 @@ struct pa_source_output { * will not be allowed */ pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */ + /* If non-NULL this function is used to dispatch asynchronous + * control events. */ + void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data); + struct { pa_source_output_state_t state; @@ -177,6 +181,12 @@ enum { PA_SOURCE_OUTPUT_MESSAGE_MAX }; +typedef struct pa_source_output_send_event_hook_data { + pa_source_output *source_output; + const char *event; + pa_proplist *data; +} pa_source_output_send_event_hook_data; + typedef struct pa_source_output_new_data { pa_proplist *proplist; pa_sink_input *direct_on_input; @@ -229,10 +239,12 @@ void pa_source_output_kill(pa_source_output*o); 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); +void 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); +void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data); + 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, pa_bool_t save); |