From 69ad904b63954257da772af1bea8deea27fca369 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 12 Aug 2019 12:31:55 +0200 Subject: [PATCH] pulse: update per channel volumes Use the channelVolume property to update the individual channel volumes. Update property values to match pulseaudio. Handle the case where the number of channels changes. In pulse this can normally never happen. Emit a remove and add of the node when this happens as to not confuse pulse clients. --- src/context.c | 74 +++++++++++----- src/internal.h | 5 +- src/introspect.c | 221 +++++++++++++++++++++++++++++++---------------- src/stream.c | 15 ++-- 4 files changed, 211 insertions(+), 104 deletions(-) diff --git a/src/context.c b/src/context.c index 5e502e92b..099b147c0 100644 --- a/src/context.c +++ b/src/context.c @@ -190,6 +190,15 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty } } +static void update_device_props(struct global *g) +{ + pa_card_info *i = &g->card_info.info; + const char *s; + + if ((s = pa_proplist_gets(i->proplist, PW_KEY_DEVICE_ICON_NAME))) + pa_proplist_sets(i->proplist, PA_PROP_DEVICE_ICON_NAME, s); +} + static void device_event_info(void *object, const struct pw_device_info *info) { struct global *g = object; @@ -200,15 +209,19 @@ static void device_event_info(void *object, const struct pw_device_info *info) info = g->info = pw_device_info_update(g->info, info); i->index = g->id; - i->name = info->name; + i->name = info->props ? + spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME) : "unknown"; i->owner_module = g->parent_id; if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { i->driver = info->props ? spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : NULL; + if (i->proplist) pa_proplist_update_dict(i->proplist, info->props); - else + else { i->proplist = pa_proplist_new_dict(info->props); + } + update_device_props(g); } if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { for (n = 0; n < info->n_params; n++) { @@ -333,6 +346,39 @@ static void node_event_info(void *object, const struct pw_node_info *info) g->pending_seq = pw_proxy_sync(g->proxy, 0); } +static void parse_props(struct global *g, const struct spa_pod *param) +{ + struct spa_pod_prop *prop; + struct spa_pod_object *obj = (struct spa_pod_object *) param; + + SPA_POD_OBJECT_FOREACH(obj, prop) { + switch (prop->key) { + case SPA_PROP_volume: + spa_pod_get_float(&prop->value, &g->node_info.volume); + break; + case SPA_PROP_mute: + spa_pod_get_bool(&prop->value, &g->node_info.mute); + break; + case SPA_PROP_channelVolumes: + { + uint32_t n_vals; + + n_vals = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, + g->node_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS); + + if (n_vals != g->node_info.n_channel_volumes) { + emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_REMOVE); + emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_NEW); + g->node_info.n_channel_volumes = n_vals; + } + break; + } + default: + break; + } + } +} + static void node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) @@ -342,24 +388,8 @@ static void node_event_param(void *object, int seq, switch (id) { case SPA_PARAM_Props: - { - struct spa_pod_prop *prop; - struct spa_pod_object *obj = (struct spa_pod_object *) param; - - SPA_POD_OBJECT_FOREACH(obj, prop) { - switch (prop->key) { - case SPA_PROP_volume: - spa_pod_get_float(&prop->value, &g->node_info.volume); - break; - case SPA_PROP_mute: - spa_pod_get_bool(&prop->value, &g->node_info.mute); - break; - default: - break; - } - } + parse_props(g, param); break; - } default: break; } @@ -395,10 +425,8 @@ static void module_event_info(void *object, const struct pw_module_info *info) i->proplist = pa_proplist_new_dict(info->props); } - if (info->change_mask & PW_MODULE_CHANGE_MASK_NAME) - i->name = info->name; - if (info->change_mask & PW_MODULE_CHANGE_MASK_ARGS) - i->argument = info->args; + i->name = info->name; + i->argument = info->args; i->n_used = -1; i->auto_unload = false; g->pending_seq = pw_proxy_sync(g->proxy, 0); diff --git a/src/internal.h b/src/internal.h index a6a8048ea..d93d3eebb 100644 --- a/src/internal.h +++ b/src/internal.h @@ -246,6 +246,8 @@ struct global { uint32_t monitor; float volume; bool mute; + uint32_t n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; } node_info; /* for devices */ struct { @@ -382,7 +384,8 @@ struct pa_stream { uint32_t buffer_size; uint32_t buffer_offset; - float volume; + uint32_t n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; bool mute; pa_operation *drain; uint64_t queued; diff --git a/src/introspect.c b/src/introspect.c index 0567e4421..20f110687 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -91,22 +91,35 @@ static void sink_callback(struct sink_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; + const char *str; + uint32_t n; pa_sink_info i; pa_format_info ii[1]; pa_format_info *ip[1]; - pw_log_debug("sink %d %s monitor %d", g->id, info->name, g->node_info.monitor); - spa_zero(i); - i.name = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + i.name = str; + else + i.name = "unknown"; + pw_log_debug("sink %d %s monitor %d", g->id, i.name, g->node_info.monitor); i.index = g->id; - i.description = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + i.description = str; + else + i.description = "unknown"; + i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); + if (g->node_info.n_channel_volumes) + i.sample_spec.channels = g->node_info.n_channel_volumes; + else + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; i.mute = g->node_info.mute; i.monitor_source = g->node_info.monitor; i.monitor_source_name = "unknown"; @@ -243,43 +256,87 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, return o; } -static void set_stream_volume(pa_context *c, pa_stream *s, float volume, bool mute) +static void set_stream_volume(pa_context *c, pa_stream *s, const pa_cvolume *volume, bool mute) { - if (s->volume != volume || s->mute != mute) { - s->volume = volume; + uint32_t i, n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; + float *vols; + + if (volume) { + for (i = 0; i < volume->channels; i++) + channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + vols = channel_volumes; + n_channel_volumes = volume->channels; + } else { + vols = s->channel_volumes; + n_channel_volumes = s->n_channel_volumes; + } + + if (n_channel_volumes != s->n_channel_volumes || + !memcmp(s->channel_volumes, vols, n_channel_volumes * sizeof(float)) || + s->mute != mute) { + float val; + + memcpy(s->channel_volumes, vols, n_channel_volumes * sizeof(float)); + s->n_channel_volumes = n_channel_volumes; s->mute = mute; + val = s->mute ? 1.0f : 0.0f; + pw_stream_set_control(s->stream, - SPA_PROP_volume, s->volume, - SPA_PROP_mute, s->mute ? 1.0f : 0.0f, + SPA_PROP_mute, 1, &val, + SPA_PROP_channelVolumes, n_channel_volumes, channel_volumes, 0); } } -static void set_node_volume(pa_context *c, struct global *g, float volume, bool mute) +static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute) { char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + uint32_t i, n_channel_volumes; + float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; + float *vols; - if (g->node_info.volume != volume || g->node_info.mute != mute) { - g->node_info.volume = volume; - g->node_info.mute = mute; - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, - SPA_PARAM_Props, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_volume, SPA_POD_Float(volume), - SPA_PROP_mute, SPA_POD_Bool(mute))); + if (volume) { + for (i = 0; i < volume->channels; i++) + channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + vols = channel_volumes; + n_channel_volumes = volume->channels; + + if (n_channel_volumes == g->node_info.n_channel_volumes && + memcmp(g->node_info.channel_volumes, vols, n_channel_volumes * sizeof(float)) == 0 && + mute == g->node_info.mute) + return; + + memcpy(g->node_info.channel_volumes, vols, n_channel_volumes * sizeof(float)); + g->node_info.n_channel_volumes = n_channel_volumes; + } else { + n_channel_volumes = g->node_info.n_channel_volumes; + vols = g->node_info.channel_volumes; + if (mute == g->node_info.mute) + return; } + g->node_info.mute = mute; + + pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_mute, SPA_POD_Bool(mute), + SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float), + SPA_TYPE_Float, + n_channel_volumes, + vols))); } + SPA_EXPORT pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -295,8 +352,7 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -312,7 +368,6 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -326,8 +381,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -357,7 +411,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -385,7 +439,7 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -488,6 +542,8 @@ static void source_callback(struct source_data *d) { struct global *g = d->global; struct pw_node_info *info = g->info; + const char *str; + uint32_t n; pa_source_info i; pa_format_info ii[1]; pa_format_info *ip[1]; @@ -497,15 +553,26 @@ static void source_callback(struct source_data *d) PA_SOURCE_DECIBEL_VOLUME; spa_zero(i); - i.name = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + i.name = str; + else + i.name = "unknown"; i.index = g->id; - i.description = info->name; + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + i.description = str; + else + i.description = "unknown"; i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, 2, PA_CHANNEL_MAP_DEFAULT); + if (g->node_info.n_channel_volumes) + i.sample_spec.channels = g->node_info.n_channel_volumes; + else + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = g->parent_id; - pa_cvolume_set(&i.volume, 2, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; i.mute = g->node_info.mute; if (g->mask & PA_SUBSCRIPTION_MASK_SINK) { i.monitor_of_sink = g->id; @@ -654,7 +721,6 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -670,8 +736,7 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -687,7 +752,6 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na pa_operation *o; struct global *g; struct success_ack *d; - float v; pa_assert(c); pa_assert(c->refcount >= 1); @@ -701,8 +765,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -732,7 +795,7 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -760,7 +823,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -858,7 +921,7 @@ static void server_callback(struct server_data *d) i.default_sink_name = "unknown"; i.default_source_name = "unknown"; i.cookie = info->cookie; - pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + pa_channel_map_init_extend(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); d->cb(d->context, &i, d->userdata); } @@ -1427,6 +1490,7 @@ static void sink_input_callback(struct sink_input_data *d) struct global *g = d->global, *cl; struct pw_node_info *info = g->info; const char *name; + uint32_t n; pa_sink_input_info i; pa_format_info ii[1]; pa_stream *s; @@ -1438,17 +1502,18 @@ static void sink_input_callback(struct sink_input_data *d) if (info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && - (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) - name = info->name; + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) + name = "unknown"; } else - name = info->name; + name = "unknown"; cl = pa_context_find_global(d->context, g->parent_id); spa_zero(i); i.index = g->id; - i.name = name ? name : "Unknown"; + i.name = name; i.owner_module = PA_INVALID_INDEX; i.client = g->parent_id; if (s) { @@ -1465,20 +1530,25 @@ static void sink_input_callback(struct sink_input_data *d) i.channel_map = s->channel_map; else pa_channel_map_init_auto(&i.channel_map, - i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.format = s->format; } else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels = g->node_info.n_channel_volumes; + if (i.sample_spec.channels == 0) + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } pa_cvolume_init(&i.volume); - pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->node_info.mute; i.buffer_usec = 0; i.sink_usec = 0; @@ -1620,7 +1690,6 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons struct global *g; pa_operation *o; struct success_ack *d; - float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1631,13 +1700,11 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons return NULL; } - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - if (s) { - set_stream_volume(c, s, v, s->mute); + set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1666,10 +1733,10 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu } if (s) { - set_stream_volume(c, s, s->volume, mute); + set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1721,7 +1788,8 @@ static void source_output_callback(struct source_output_data *d) { struct global *g = d->global, *l, *cl; struct pw_node_info *info = g->info; - const char *name; + const char *name = NULL; + uint32_t n; pa_source_output_info i; pa_format_info ii[1]; pa_stream *s; @@ -1734,11 +1802,12 @@ static void source_output_callback(struct source_output_data *d) if (info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && - (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL) - name = info->name; + (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && + (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) + name = NULL; } - else - name = info->name; + if (name == NULL) + name = "unknown"; cl = pa_context_find_global(d->context, g->parent_id); @@ -1760,20 +1829,25 @@ static void source_output_callback(struct source_output_data *d) i.channel_map = s->channel_map; else pa_channel_map_init_auto(&i.channel_map, - i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.format = s->format; } else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = 2; - pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + i.sample_spec.channels = g->node_info.n_channel_volumes; + if (i.sample_spec.channels == 0) + i.sample_spec.channels = 2; + pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); ii[0].encoding = PA_ENCODING_PCM; ii[0].plist = pa_proplist_new(); i.format = ii; } pa_cvolume_init(&i.volume); - pa_cvolume_set(&i.volume, i.sample_spec.channels, g->node_info.volume * PA_VOLUME_NORM); + i.volume.channels = i.sample_spec.channels; + for (n = 0; n < i.volume.channels; n++) + i.volume.values[n] = g->node_info.volume * g->node_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->node_info.mute; i.buffer_usec = 0; i.source_usec = 0; @@ -1910,7 +1984,6 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c struct global *g; pa_operation *o; struct success_ack *d; - float v; pw_log_debug("contex %p: index %d", c, idx); @@ -1921,13 +1994,11 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c return NULL; } - v = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - if (s) { - set_stream_volume(c, s, v, s->mute); + set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, v, g->node_info.mute); + set_node_volume(c, g, volume, g->node_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1954,10 +2025,10 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int } if (s) { - set_stream_volume(c, s, s->volume, mute); + set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, g->node_info.volume, mute); + set_node_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; diff --git a/src/stream.c b/src/stream.c index d81022ea8..35297f346 100644 --- a/src/stream.c +++ b/src/stream.c @@ -828,7 +828,7 @@ static int create_stream(pa_stream_direction_t direction, int res; enum pw_stream_flags fl; const struct spa_pod *params[16]; - uint32_t n_params = 0; + uint32_t i, n_params = 0; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); uint32_t sample_rate = 0, stride = 0; @@ -847,10 +847,15 @@ static int create_stream(pa_stream_direction_t direction, s->direction = direction; s->timing_info_valid = false; s->disconnecting = false; - if (volume) - s->volume = pa_cvolume_avg(volume) / (float) PA_VOLUME_NORM; - else - s->volume = 1.0; + if (volume) { + for (i = 0; i < volume->channels; i++) + s->channel_volumes[i] = volume->values[i] / (float) PA_VOLUME_NORM; + s->n_channel_volumes = volume->channels; + } else { + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + s->channel_volumes[i] = 1.0; + s->n_channel_volumes = 0; + } s->mute = false; pa_stream_set_state(s, PA_STREAM_CREATING);