+++ /dev/null
-FILESEXTRAPATHS_prepend := "${THISDIR}/pulseaudio_5.0:"
-
-SRC_URI += "file://change_libsystemd_to_libsystemd-login_in_configure.patch"
-#SRC_URI += "file://changes-to-pa-simple-api-samsung.patch"
-
-B="${S}"
-
-SRC_URI += "file://0001-volume-ramp-additions-to-the-low-level-infra.patch"
-SRC_URI += "file://0002-volume-ramp-add-volume-ramping-to-sink.patch"
-SRC_URI += "file://0003-volume-ramp-adding-volume-ramping-to-sink-input.patch"
-SRC_URI += "file://0004-build-sys-install-files-for-a-module-development.patch"
-SRC_URI += "file://0005-jack-detection-fix-for-wired-headset.patch"
-SRC_URI += "file://0006-make-pa_thread_mq_done-safe-for-subsequent-calls.patch"
-SRC_URI += "file://0007-node-manager-adding-external-node-manager-API.patch"
-SRC_URI += "file://0008-node-manager-adding-node-support-for-pactl.patch"
-SRC_URI += "file://0009-add-internal-corking-state-for-sink-input.patch"
-SRC_URI += "file://0010-bluetooth-Add-basic-support-for-HEADSET-profiles.patch"
-SRC_URI += "file://0011-bluetooth-Create-Handsfree-Audio-Agent-NULL-backend.patch"
-SRC_URI += "file://0012-bluetooth-Create-Handsfree-Audio-Agent-oFono-backend.patch"
-SRC_URI += "file://0013-bluetooth-Monitor-D-Bus-signals.patch"
-SRC_URI += "file://0014-bluetooth-Create-pa_bluetooth_dbus_send_and_add_to_p.patch"
-SRC_URI += "file://0015-bluetooth-Register-Unregister-Handsfree-Audio-Agent-.patch"
-SRC_URI += "file://0016-bluetooth-List-HandsfreeAudioCard-objects-from-oFono.patch"
-SRC_URI += "file://0017-bluetooth-Parse-HandsfreeAudioCard-properties.patch"
-SRC_URI += "file://0018-bluetooth-Implement-transport-acquire-for-hf_audio_a.patch"
-SRC_URI += "file://0019-bluetooth-Track-oFono-service.patch"
-SRC_URI += "file://0020-bluetooth-Handle-CardAdded-signal.patch"
-SRC_URI += "file://0021-bluetooth-Handle-CardRemoved-signal.patch"
-SRC_URI += "file://0022-bluetooth-Implement-org.ofono.HandsfreeAudioAgent.Ne.patch"
-SRC_URI += "file://0023-bluetooth-Fix-not-handle-fd-in-DEFER_SETUP-state.patch"
-SRC_URI += "file://0024-bluetooth-Suspend-sink-source-on-HFP-s-stream-HUP.patch"
-SRC_URI += "file://0025-bluetooth-Implement-transport-release-for-hf_audio_a.patch"
-SRC_URI += "file://0026-bluetooth-Fixes-HFP-audio-transfer-when-initiator.patch"
-SRC_URI += "file://0027-bluetooth-Set-off-profile-as-default-for-newly-creat.patch"
-SRC_URI += "file://0028-fix-ofono-and-pulseaudio-starting-order-assert.patch"
-SRC_URI += "file://0029-hfp-do-safe-strcmp-in-dbus-handler.patch"
-SRC_URI += "file://0030-add-parameter-to-define-key-used-in-stream-restore.patch"
-SRC_URI += "file://0031-increase-alsa-rewind-safeguard.patch"
-SRC_URI += "file://0032-fix-for-profile-change-prototype-in-bluez5-patch.patch"
-SRC_URI += "file://0033-changes-to-pa-simple-api-samsung.patch"
-SRC_URI += "file://0034-add-support-for-samsung-power-management-samsung.patch"
-SRC_URI += "file://0035-Add-preload-fileter-for-resample-samsung.patch.gz"
-SRC_URI += "file://0036-Enhance-for-echo-cancel-samsung.patch"
-SRC_URI += "file://0037-add-support-for-dlog-samsung.patch"
-SRC_URI += "file://0038-add-policy-module-samsung.patch"
-SRC_URI += "file://0039-add-bluetooth-a2dp-aptx-codec-support-samsung.patch"
-SRC_URI += "file://0040-create-pa_ready-file-samsung.patch"
-SRC_URI += "file://0041-set-alsa-suspend-timeout-to-zero-samsung.patch"
-SRC_URI += "file://0042-cope-with-possible-infinite-waiting-in-startup-samsu.patch"
-SRC_URI += "file://0043-use-udev-only-for-usb-devices-samsung.patch"
-SRC_URI += "file://0044-fixes-and-improvements-to-makefile-and-configure-in-.patch"
-SRC_URI += "file://0045-fix-warning-in-gconf-helper.patch"
-SRC_URI += "file://0046-volume-ramp-add-client-api-support-for-volume-rampin.patch"
-SRC_URI += "file://0047-adjust-default-bluetooth-profile-to-off.patch"
-SRC_URI += "file://0048-Add-bt_profile_set-patch-which-fixed-bt-a2dp-hsp-pro.patch"
-SRC_URI += "file://0049-added-pulseaudio.service-file.patch"
-SRC_URI += "file://0050-.gitignore-Add-pulsecore-config.h.patch"
-SRC_URI += "file://0051-pactl-Fix-crash-with-older-servers.patch"
-SRC_URI += "file://0052-volume-Increase-PA_SW_VOLUME_SNPRINT_DB_MAX.patch"
-SRC_URI += "file://0053-sink-input-source-output-Fix-mute-saving.patch"
-SRC_URI += "file://0054-direction-Add-a-couple-of-direction-helper-functions.patch"
-SRC_URI += "file://0055-core-util-Add-pa_join.patch"
-SRC_URI += "file://0056-dynarray-Add-pa_dynarray_get_raw_array.patch"
-SRC_URI += "file://0057-dynarray-Add-PA_DYNARRAY_FOREACH.patch"
-SRC_URI += "file://0058-dynarray-Add-pa_dynarray_remove_last.patch"
-SRC_URI += "file://0059-dynarray-Add-pa_dynarray_remove_all.patch"
-SRC_URI += "file://0060-hashmap-Add-pa_hashmap_remove_and_free.patch"
-SRC_URI += "file://0061-device-port-Add-pa_device_port.active.patch"
-SRC_URI += "file://0062-sink-source-Assign-to-reference_volume-from-only-one.patch"
-SRC_URI += "file://0063-sink-input-source-output-Assign-to-volume-from-only-.patch"
-SRC_URI += "file://0064-sink-source-Return-early-from-set_mute.patch"
-SRC_URI += "file://0065-sink-input-source-output-Add-logging-to-set_mute.patch"
-SRC_URI += "file://0066-sink-source-Allow-calling-set_mute-during-initializa.patch"
-SRC_URI += "file://0067-echo-cancel-Remove-redundant-get_mute-callback.patch"
-SRC_URI += "file://0068-sink-source-Call-set_mute-from-mute_changed.patch"
-SRC_URI += "file://0069-sink-source-Assign-to-s-muted-from-only-one-place.patch"
-SRC_URI += "file://0070-sink-input-source-output-Remove-redundant-get_mute-f.patch"
-SRC_URI += "file://0071-solaris-tunnel-Remove-some-redundant-boolean-convers.patch"
-SRC_URI += "file://0072-sink-source-Add-hooks-for-volume-changes.patch"
-SRC_URI += "file://0073-sink-input-source-output-Add-hooks-for-volume-change.patch"
-SRC_URI += "file://0074-sink-source-Add-hooks-for-mute-changes.patch"
-SRC_URI += "file://0075-sink-input-source-output-Add-hooks-for-mute-changes.patch"
-SRC_URI += "file://0076-sink-Link-monitor-source-before-activating-port.patch"
-SRC_URI += "file://0077-context-extension-Add-the-pa_extension-class.patch"
-SRC_URI += "file://0078-volume-api-Add-libvolume-api.so.patch.gz"
-SRC_URI += "file://0079-Add-module-volume-api-and-the-related-client-API.patch.gz"
-SRC_URI += "file://0080-pactl-Add-support-for-the-new-volume-API.patch"
-SRC_URI += "file://0081-Add-module-audio-groups.patch"
-SRC_URI += "file://0082-Add-module-main-volume-policy.patch"
-SRC_URI += "file://0083-configuration-Add-default-IVI-audio-group-and-main-v.patch"
-SRC_URI += "file://0084-sink-source-Initialize-port-before-fixate-hook-fixes.patch"
-SRC_URI += "file://0085-configuration-pulseaudio-tizen-configuration-in-defa.patch"
-SRC_URI += "file://0086-pactl-Fix-crash-in-pactl-list.patch"
-SRC_URI += "file://0087-configuration-x-example-x-tizen-ivi-in-volume-config.patch"
-SRC_URI += "file://0088-device-creator-stream-creator-Add-a-couple-of-assert.patch"
-SRC_URI += "file://0089-main-volume-policy-Fix-a-memory-leak.patch"
-SRC_URI += "file://0090-audio-groups-fix-issues-found-by-static-analysis.patch"
-SRC_URI += "file://0091-core-util-Add-pa_append_to_home_dir.patch"
-SRC_URI += "file://0092-core-util-Add-pa_get_config_home_dir.patch"
-SRC_URI += "file://0093-core-util-Add-pa_append_to_config_home_dir.patch"
-SRC_URI += "file://0094-core-Create-the-config-home-directory-on-startup.patch"
-SRC_URI += "file://0095-alsa-Handle-unlinking-of-uninitialized-streams-grace.patch"
-SRC_URI += "file://0096-role-cork-Handle-unlinking-of-uninitialized-streams-.patch"
-SRC_URI += "file://0097-role-ducking-Handle-unlinking-of-uninitialized-strea.patch"
-SRC_URI += "file://0098-core-util-Add-pa_boolean_to_string.patch"
-SRC_URI += "file://0099-sink-input-source-output-Assign-to-reference_ratio-f.patch"
-SRC_URI += "file://0100-sink-input-source-output-Add-hooks-for-reference-rat.patch"
-SRC_URI += "file://0101-sink-input-source-output-Use-new_data.volume-only-fo.patch"
-SRC_URI += "file://0102-sink-input-source-output-Add-the-real-object-pointer.patch"
-SRC_URI += "file://0103-audio-groups-main-volume-policy-volume-api-Various-f.patch"
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@helsinki.fi>
-Date: Wed, 8 Aug 2012 11:14:37 +0300
-Subject: volume ramp: additions to the low level infra
-
-Change-Id: Ib2be01e0bea0856ebd0256066981fe34d05a4f86
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/map-file | 4 +
- src/pulse/def.h | 13 ++-
- src/pulse/volume.c | 74 ++++++++++++-
- src/pulse/volume.h | 33 ++++++
- src/pulsecore/mix.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/pulsecore/mix.h | 27 +++++
- 6 files changed, 459 insertions(+), 2 deletions(-)
-
-diff --git a/src/map-file b/src/map-file
-index fbad1a4..9903942 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -136,6 +136,10 @@ pa_cvolume_max_mask;
- pa_cvolume_merge;
- pa_cvolume_min;
- pa_cvolume_min_mask;
-+pa_cvolume_ramp_equal;
-+pa_cvolume_ramp_init;
-+pa_cvolume_ramp_set;
-+pa_cvolume_ramp_channel_ramp_set;
- pa_cvolume_remap;
- pa_cvolume_scale;
- pa_cvolume_scale_mask;
-diff --git a/src/pulse/def.h b/src/pulse/def.h
-index d6fa912..b7854a3 100644
---- a/src/pulse/def.h
-+++ b/src/pulse/def.h
-@@ -349,11 +349,15 @@ typedef enum pa_stream_flags {
- * consider absolute when the sink is in flat volume mode,
- * relative otherwise. \since 0.9.20 */
-
-- PA_STREAM_PASSTHROUGH = 0x80000U
-+ PA_STREAM_PASSTHROUGH = 0x80000U,
- /**< Used to tag content that will be rendered by passthrough sinks.
- * The data will be left as is and not reformatted, resampled.
- * \since 1.0 */
-
-+ PA_STREAM_START_RAMP_MUTED = 0x100000U
-+ /**< Used to tag content that the stream will be started ramp volume
-+ * muted so that you can nicely fade it in */
-+
- } pa_stream_flags_t;
-
- /** \cond fulldocs */
-@@ -382,6 +386,7 @@ typedef enum pa_stream_flags {
- #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
- #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
- #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
-+#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED
-
- /** \endcond */
-
-@@ -1049,6 +1054,12 @@ typedef enum pa_port_available {
- /** \endcond */
- #endif
-
-+/** \cond fulldocs */
-+#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR
-+#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC
-+#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC
-+/** \endcond */
-+
- PA_C_DECL_END
-
- #endif
-diff --git a/src/pulse/volume.c b/src/pulse/volume.c
-index 6927392..b140cee 100644
---- a/src/pulse/volume.c
-+++ b/src/pulse/volume.c
-@@ -447,7 +447,10 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
- unsigned c;
- pa_assert(a);
-
-- pa_return_val_if_fail(pa_cvolume_valid(a), 0);
-+ if (pa_cvolume_valid(a) == 0)
-+ abort();
-+
-+ /* pa_return_val_if_fail(pa_cvolume_valid(a), 0); */
- pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
-
- for (c = 0; c < a->channels; c++)
-@@ -988,3 +991,72 @@ pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
-
- return pa_cvolume_scale(v, m);
- }
-+
-+int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b) {
-+ int i;
-+ pa_assert(a);
-+ pa_assert(b);
-+
-+ if (PA_UNLIKELY(a == b))
-+ return 1;
-+
-+ if (a->channels != b->channels)
-+ return 0;
-+
-+ for (i = 0; i < a->channels; i++) {
-+ if (a->ramps[i].type != b->ramps[i].type ||
-+ a->ramps[i].length != b->ramps[i].length ||
-+ a->ramps[i].target != b->ramps[i].target)
-+ return 0;
-+ }
-+
-+ return 1;
-+}
-+
-+pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp) {
-+ unsigned c;
-+
-+ pa_assert(ramp);
-+
-+ ramp->channels = 0;
-+
-+ for (c = 0; c < PA_CHANNELS_MAX; c++) {
-+ ramp->ramps[c].type = PA_VOLUME_RAMP_TYPE_LINEAR;
-+ ramp->ramps[c].length = 0;
-+ ramp->ramps[c].target = PA_VOLUME_INVALID;
-+ }
-+
-+ return ramp;
-+}
-+
-+pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channels, pa_volume_ramp_type_t type, long time, pa_volume_t vol) {
-+ int i;
-+
-+ pa_assert(ramp);
-+ pa_assert(channels > 0);
-+ pa_assert(time >= 0);
-+ pa_assert(channels <= PA_CHANNELS_MAX);
-+
-+ ramp->channels = (uint8_t) channels;
-+
-+ for (i = 0; i < ramp->channels; i++) {
-+ ramp->ramps[i].type = type;
-+ ramp->ramps[i].length = time;
-+ ramp->ramps[i].target = PA_CLAMP_VOLUME(vol);
-+ }
-+
-+ return ramp;
-+}
-+
-+pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol) {
-+
-+ pa_assert(ramp);
-+ pa_assert(channel <= ramp->channels);
-+ pa_assert(time >= 0);
-+
-+ ramp->ramps[channel].type = type;
-+ ramp->ramps[channel].length = time;
-+ ramp->ramps[channel].target = PA_CLAMP_VOLUME(vol);
-+
-+ return ramp;
-+}
-diff --git a/src/pulse/volume.h b/src/pulse/volume.h
-index 7806954..3ffb573 100644
---- a/src/pulse/volume.h
-+++ b/src/pulse/volume.h
-@@ -415,6 +415,39 @@ pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
- * the channels are kept. \since 0.9.16 */
- pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
-
-+/** Volume ramp type
-+*/
-+typedef enum pa_volume_ramp_type {
-+ PA_VOLUME_RAMP_TYPE_LINEAR = 0, /**< linear */
-+ PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1, /**< logarithmic */
-+ PA_VOLUME_RAMP_TYPE_CUBIC = 2,
-+} pa_volume_ramp_type_t;
-+
-+/** A structure encapsulating a volume ramp */
-+typedef struct pa_volume_ramp_t {
-+ pa_volume_ramp_type_t type;
-+ long length;
-+ pa_volume_t target;
-+} pa_volume_ramp_t;
-+
-+/** A structure encapsulating a multichannel volume ramp */
-+typedef struct pa_cvolume_ramp {
-+ uint8_t channels;
-+ pa_volume_ramp_t ramps[PA_CHANNELS_MAX];
-+} pa_cvolume_ramp;
-+
-+/** Return non-zero when *a == *b */
-+int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b);
-+
-+/** Init volume ramp struct */
-+pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp);
-+
-+/** Set first n channels of ramp struct to certain value */
-+pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol);
-+
-+/** Set individual channel in the channel struct */
-+pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol);
-+
- PA_C_DECL_END
-
- #endif
-diff --git a/src/pulsecore/mix.c b/src/pulsecore/mix.c
-index 4b789a6..c1e0c18 100644
---- a/src/pulsecore/mix.c
-+++ b/src/pulsecore/mix.c
-@@ -721,3 +721,313 @@ void pa_volume_memchunk(
-
- pa_memblock_release(c->memblock);
- }
-+
-+static void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) {
-+ unsigned channel, padding;
-+
-+ pa_assert(linear);
-+ pa_assert(volume);
-+
-+ for (channel = 0; channel < nchannels; channel++)
-+ linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U);
-+
-+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
-+ linear[channel] = linear[padding];
-+}
-+
-+static void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) {
-+ unsigned channel, padding;
-+
-+ pa_assert(linear);
-+ pa_assert(volume);
-+
-+ for (channel = 0; channel < nchannels; channel++)
-+ linear[channel] = volume[channel];
-+
-+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
-+ linear[channel] = linear[padding];
-+}
-+
-+typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels);
-+
-+static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = {
-+ [PA_SAMPLE_U8] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_ALAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_ULAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S16LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S16BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
-+ [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
-+ [PA_SAMPLE_S32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S24LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S24BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S24_32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
-+ [PA_SAMPLE_S24_32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping
-+};
-+
-+static const unsigned format_sample_size_table[] = {
-+ [PA_SAMPLE_U8] = 1,
-+ [PA_SAMPLE_ALAW] = 1,
-+ [PA_SAMPLE_ULAW] = 1,
-+ [PA_SAMPLE_S16LE] = 2,
-+ [PA_SAMPLE_S16BE] = 2,
-+ [PA_SAMPLE_FLOAT32LE] = 4,
-+ [PA_SAMPLE_FLOAT32BE] = 4,
-+ [PA_SAMPLE_S32LE] = 4,
-+ [PA_SAMPLE_S32BE] = 4,
-+ [PA_SAMPLE_S24LE] = 3,
-+ [PA_SAMPLE_S24BE] = 3,
-+ [PA_SAMPLE_S24_32LE] = 4,
-+ [PA_SAMPLE_S24_32BE] = 4
-+};
-+
-+static float calc_volume_ramp_linear(pa_volume_ramp_int_t *ramp) {
-+ pa_assert(ramp);
-+ pa_assert(ramp->length > 0);
-+
-+ /* basic linear interpolation */
-+ return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / (float) ramp->length;
-+}
-+
-+static float calc_volume_ramp_logarithmic(pa_volume_ramp_int_t *ramp) {
-+ float x_val, s, e;
-+ long temp;
-+
-+ pa_assert(ramp);
-+ pa_assert(ramp->length > 0);
-+
-+ if (ramp->end > ramp->start) {
-+ temp = ramp->left;
-+ s = ramp->end;
-+ e = ramp->start;
-+ } else {
-+ temp = ramp->length - ramp->left;
-+ s = ramp->start;
-+ e = ramp->end;
-+ }
-+
-+ x_val = temp == 0 ? 0.0 : powf(temp, 10);
-+
-+ /* base 10 logarithmic interpolation */
-+ return s + x_val * (e - s) / powf(ramp->length, 10);
-+}
-+
-+static float calc_volume_ramp_cubic(pa_volume_ramp_int_t *ramp) {
-+ float x_val, s, e;
-+ long temp;
-+
-+ pa_assert(ramp);
-+ pa_assert(ramp->length > 0);
-+
-+ if (ramp->end > ramp->start) {
-+ temp = ramp->left;
-+ s = ramp->end;
-+ e = ramp->start;
-+ } else {
-+ temp = ramp->length - ramp->left;
-+ s = ramp->start;
-+ e = ramp->end;
-+ }
-+
-+ x_val = temp == 0 ? 0.0 : cbrtf(temp);
-+
-+ /* cubic interpolation */
-+ return s + x_val * (e - s) / cbrtf(ramp->length);
-+}
-+
-+typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp_int_t *);
-+
-+static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = {
-+ [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear,
-+ [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic,
-+ [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic
-+};
-+
-+static void calc_volume_ramps(pa_cvolume_ramp_int *ram, float *vol)
-+{
-+ int i;
-+
-+ for (i = 0; i < ram->channels; i++) {
-+ if (ram->ramps[i].left <= 0) {
-+ if (ram->ramps[i].target == PA_VOLUME_NORM) {
-+ vol[i] = 1.0;
-+ }
-+ } else {
-+ vol[i] = ram->ramps[i].curr = calc_volume_ramp_table[ram->ramps[i].type] (&ram->ramps[i]);
-+ ram->ramps[i].left--;
-+ }
-+ }
-+}
-+
-+void pa_volume_ramp_memchunk(
-+ pa_memchunk *c,
-+ const pa_sample_spec *spec,
-+ pa_cvolume_ramp_int *ramp) {
-+
-+ void *ptr;
-+ volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
-+ float vol[PA_CHANNELS_MAX + VOLUME_PADDING];
-+ pa_do_volume_func_t do_volume;
-+ long length_in_frames;
-+ int i;
-+
-+ pa_assert(c);
-+ pa_assert(spec);
-+ pa_assert(pa_frame_aligned(c->length, spec));
-+ pa_assert(ramp);
-+
-+ length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels;
-+
-+ if (pa_memblock_is_silence(c->memblock)) {
-+ for (i = 0; i < ramp->channels; i++) {
-+ if (ramp->ramps[i].length > 0)
-+ ramp->ramps[i].length -= length_in_frames;
-+ }
-+ return;
-+ }
-+
-+ if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) {
-+ pa_log_warn("Unable to change volume of format");
-+ return;
-+ }
-+
-+ do_volume = pa_get_volume_func(spec->format);
-+ pa_assert(do_volume);
-+
-+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
-+
-+ for (i = 0; i < length_in_frames; i++) {
-+ calc_volume_ramps(ramp, vol);
-+ calc_volume_table_no_mapping[spec->format] ((void *)linear, vol, spec->channels);
-+
-+ /* we only process one frame per iteration */
-+ do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels);
-+
-+ /* pa_log_debug("1: %d 2: %d", linear[0], linear[1]); */
-+
-+ ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels;
-+ }
-+
-+ pa_memblock_release(c->memblock);
-+}
-+
-+pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate) {
-+
-+ int i, j, channels, remaining_channels;
-+ float temp;
-+
-+ if (dst->channels < src->channels) {
-+ channels = dst->channels;
-+ remaining_channels = 0;
-+ }
-+ else {
-+ channels = src->channels;
-+ remaining_channels = dst->channels;
-+ }
-+
-+ for (i = 0; i < channels; i++) {
-+ dst->ramps[i].type = src->ramps[i].type;
-+ /* ms to samples */
-+ dst->ramps[i].length = src->ramps[i].length * sample_rate / 1000;
-+ dst->ramps[i].left = dst->ramps[i].length;
-+ dst->ramps[i].start = dst->ramps[i].end;
-+ dst->ramps[i].target = src->ramps[i].target;
-+ /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */
-+ temp = src->ramps[i].target / (float)0x10000U;
-+ dst->ramps[i].end = temp * temp * temp;
-+ }
-+
-+ j = i;
-+
-+ for (i--; j < remaining_channels; j++) {
-+ dst->ramps[j].type = dst->ramps[i].type;
-+ dst->ramps[j].length = dst->ramps[i].length;
-+ dst->ramps[j].left = dst->ramps[i].left;
-+ dst->ramps[j].start = dst->ramps[i].start;
-+ dst->ramps[j].target = dst->ramps[i].target;
-+ dst->ramps[j].end = dst->ramps[i].end;
-+ }
-+
-+ return dst;
-+}
-+
-+bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp) {
-+ int i;
-+
-+ for (i = 0; i < ramp->channels; i++) {
-+ if (ramp->ramps[i].left > 0)
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp) {
-+ int i;
-+
-+ for (i = 0; i < ramp->channels; i++) {
-+ if (ramp->ramps[i].target != PA_VOLUME_NORM)
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume) {
-+ int i = 0;
-+
-+ volume->channels = ramp->channels;
-+
-+ for (i = 0; i < ramp->channels; i++)
-+ volume->values[i] = ramp->ramps[i].target;
-+
-+ return volume;
-+}
-+
-+pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst) {
-+ int i;
-+
-+ for (i = 0; i < src->channels; i++) {
-+ /* if new vols are invalid, copy old ramp i.e. no effect */
-+ if (dst->ramps[i].target == PA_VOLUME_INVALID)
-+ dst->ramps[i] = src->ramps[i];
-+ /* if there's some old ramp still left */
-+ else if (src->ramps[i].left > 0)
-+ dst->ramps[i].start = src->ramps[i].curr;
-+ }
-+
-+ return dst;
-+}
-+
-+pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels) {
-+ int i;
-+ float temp;
-+
-+ src->channels = channels;
-+
-+ for (i = 0; i < channels; i++) {
-+ src->ramps[i].type = PA_VOLUME_RAMP_TYPE_LINEAR;
-+ src->ramps[i].length = 0;
-+ src->ramps[i].left = 0;
-+ if (vol == PA_VOLUME_NORM) {
-+ src->ramps[i].start = 1.0;
-+ src->ramps[i].end = 1.0;
-+ src->ramps[i].curr = 1.0;
-+ }
-+ else if (vol == PA_VOLUME_MUTED) {
-+ src->ramps[i].start = 0.0;
-+ src->ramps[i].end = 0.0;
-+ src->ramps[i].curr = 0.0;
-+ }
-+ else {
-+ temp = vol / (float)0x10000U;
-+ src->ramps[i].start = temp * temp * temp;
-+ src->ramps[i].end = src->ramps[i].start;
-+ src->ramps[i].curr = src->ramps[i].start;
-+ }
-+ src->ramps[i].target = vol;
-+ }
-+
-+ return src;
-+}
-diff --git a/src/pulsecore/mix.h b/src/pulsecore/mix.h
-index a4cde01..b1ec74f 100644
---- a/src/pulsecore/mix.h
-+++ b/src/pulsecore/mix.h
-@@ -61,4 +61,31 @@ void pa_volume_memchunk(
- const pa_sample_spec *spec,
- const pa_cvolume *volume);
-
-+typedef struct pa_volume_ramp_int_t {
-+ pa_volume_ramp_type_t type;
-+ long length;
-+ long left;
-+ float start;
-+ float end;
-+ float curr;
-+ pa_volume_t target;
-+} pa_volume_ramp_int_t;
-+
-+typedef struct pa_cvolume_ramp_int {
-+ uint8_t channels;
-+ pa_volume_ramp_int_t ramps[PA_CHANNELS_MAX];
-+} pa_cvolume_ramp_int;
-+
-+pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate);
-+bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp);
-+bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp);
-+pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst);
-+pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels);
-+pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume);
-+
-+void pa_volume_ramp_memchunk(
-+ pa_memchunk *c,
-+ const pa_sample_spec *spec,
-+ pa_cvolume_ramp_int *ramp);
-+
- #endif
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@helsinki.fi>
-Date: Wed, 8 Aug 2012 11:14:39 +0300
-Subject: volume ramp: add volume ramping to sink
-
-Change-Id: I72bd64b4e4ad161ae0526a7e40d4bf9e843ae98c
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++---
- src/pulsecore/sink.h | 9 +++++
- 2 files changed, 96 insertions(+), 5 deletions(-)
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index f4647b8..61656ab 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -333,6 +333,8 @@ pa_sink* pa_sink_new(
- &s->sample_spec,
- 0);
-
-+ pa_cvolume_ramp_int_init(&s->ramp, PA_VOLUME_NORM, data->sample_spec.channels);
-+
- s->thread_info.rtpoll = NULL;
- s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
- (pa_free_cb_t) pa_sink_input_unref);
-@@ -356,6 +358,8 @@ pa_sink* pa_sink_new(
- s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
- s->thread_info.latency_offset = s->latency_offset;
-
-+ s->thread_info.ramp = s->ramp;
-+
- /* FIXME: This should probably be moved to pa_sink_put() */
- pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
-
-@@ -1189,6 +1193,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
-
- } else if (n == 1) {
- pa_cvolume volume;
-+ pa_cvolume target;
-
- *result = info[0].chunk;
- pa_memblock_ref(result->memblock);
-@@ -1205,12 +1210,25 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
- result,
- &s->sample_spec,
- result->length);
-- } else if (!pa_cvolume_is_norm(&volume)) {
-+ } else if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
- pa_memchunk_make_writable(result, 0);
-- pa_volume_memchunk(result, &s->sample_spec, &volume);
-+ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
-+ if (!pa_cvolume_is_norm(&volume))
-+ pa_volume_memchunk(result, &s->sample_spec, &volume);
-+ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
-+ }
-+ else {
-+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
-+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target);
-+ pa_sw_cvolume_multiply(&volume, &volume, &target);
-+ }
-+ pa_volume_memchunk(result, &s->sample_spec, &volume);
-+ }
- }
- } else {
- void *ptr;
-+ pa_cvolume target_vol;
-+
- result->memblock = pa_memblock_new(s->core->mempool, length);
-
- ptr = pa_memblock_acquire(result->memblock);
-@@ -1219,6 +1237,16 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
- &s->sample_spec,
- &s->thread_info.soft_volume,
- s->thread_info.soft_muted);
-+
-+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
-+ if (pa_cvolume_ramp_active(&s->thread_info.ramp))
-+ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
-+ else {
-+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
-+ pa_volume_memchunk(result, &s->sample_spec, &target_vol);
-+ }
-+ }
-+
- pa_memblock_release(result->memblock);
-
- result->index = 0;
-@@ -1279,6 +1307,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
- pa_silence_memchunk(target, &s->sample_spec);
- else {
- pa_memchunk vchunk;
-+ pa_cvolume target_vol;
-
- vchunk = info[0].chunk;
- pa_memblock_ref(vchunk.memblock);
-@@ -1286,9 +1315,20 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
- if (vchunk.length > length)
- vchunk.length = length;
-
-- if (!pa_cvolume_is_norm(&volume)) {
-+ if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
- pa_memchunk_make_writable(&vchunk, 0);
-- pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
-+ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
-+ if (!pa_cvolume_is_norm(&volume))
-+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
-+ pa_volume_ramp_memchunk(&vchunk, &s->sample_spec, &(s->thread_info.ramp));
-+ }
-+ else {
-+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
-+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
-+ pa_sw_cvolume_multiply(&volume, &volume, &target_vol);
-+ }
-+ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
-+ }
- }
-
- pa_memchunk_memcpy(target, &vchunk);
-@@ -1297,6 +1337,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
-
- } else {
- void *ptr;
-+ pa_cvolume target_vol;
-
- ptr = pa_memblock_acquire(target->memblock);
-
-@@ -1306,6 +1347,15 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
- &s->thread_info.soft_volume,
- s->thread_info.soft_muted);
-
-+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
-+ if (pa_cvolume_ramp_active(&s->thread_info.ramp))
-+ pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp));
-+ else {
-+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
-+ pa_volume_memchunk(target, &s->sample_spec, &target_vol);
-+ }
-+ }
-+
- pa_memblock_release(target->memblock);
- }
-
-@@ -2085,6 +2135,32 @@ void pa_sink_set_volume(
- pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
- }
-
-+/* Called from main thread */
-+void pa_sink_set_volume_ramp(
-+ pa_sink *s,
-+ const pa_cvolume_ramp *ramp,
-+ bool send_msg,
-+ bool save) {
-+
-+ pa_sink_assert_ref(s);
-+ pa_assert_ctl_context();
-+ pa_assert(PA_SINK_IS_LINKED(s->state));
-+ pa_assert(ramp);
-+
-+ /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
-+ * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
-+ if (pa_sink_is_passthrough(s)) {
-+ pa_log_warn("Cannot do volume ramp, Sink is connected to PASSTHROUGH input");
-+ return;
-+ }
-+
-+ pa_cvolume_ramp_convert(ramp, &s->ramp, s->sample_spec.rate);
-+
-+ /* This tells the sink that volume ramp changed */
-+ if (send_msg)
-+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0);
-+}
-+
- /* Called from the io thread if sync volume is used, otherwise from the main thread.
- * Only to be called by sink implementor */
- void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
-@@ -2737,13 +2813,19 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
- s->thread_info.soft_volume = s->soft_volume;
- pa_sink_request_rewind(s, (size_t) -1);
- }
--
- /* Fall through ... */
-
- case PA_SINK_MESSAGE_SYNC_VOLUMES:
- sync_input_volumes_within_thread(s);
- return 0;
-
-+ case PA_SINK_MESSAGE_SET_VOLUME_RAMP:
-+ /* if we have ongoing ramp where we take current start values */
-+ pa_cvolume_ramp_start_from(&s->thread_info.ramp, &s->ramp);
-+ s->thread_info.ramp = s->ramp;
-+ pa_sink_request_rewind(s, (size_t) -1);
-+ return 0;
-+
- case PA_SINK_MESSAGE_GET_VOLUME:
-
- if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
-diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
-index c7cb5f8..576f34b 100644
---- a/src/pulsecore/sink.h
-+++ b/src/pulsecore/sink.h
-@@ -37,6 +37,7 @@ typedef struct pa_sink_volume_change pa_sink_volume_change;
- #include <pulsecore/core.h>
- #include <pulsecore/idxset.h>
- #include <pulsecore/memchunk.h>
-+#include <pulsecore/mix.h>
- #include <pulsecore/source.h>
- #include <pulsecore/module.h>
- #include <pulsecore/asyncmsgq.h>
-@@ -105,6 +106,9 @@ struct pa_sink {
- pa_cvolume saved_volume;
- bool saved_save_volume:1;
-
-+ /* for volume ramps */
-+ pa_cvolume_ramp_int ramp;
-+
- pa_asyncmsgq *asyncmsgq;
-
- pa_memchunk silence;
-@@ -288,6 +292,8 @@ struct pa_sink {
- uint32_t volume_change_safety_margin;
- /* Usec delay added to all volume change events, may be negative. */
- int32_t volume_change_extra_delay;
-+
-+ pa_cvolume_ramp_int ramp;
- } thread_info;
-
- void *userdata;
-@@ -322,6 +328,7 @@ typedef enum pa_sink_message {
- PA_SINK_MESSAGE_SET_PORT,
- PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
- PA_SINK_MESSAGE_SET_LATENCY_OFFSET,
-+ PA_SINK_MESSAGE_SET_VOLUME_RAMP,
- PA_SINK_MESSAGE_MAX
- } pa_sink_message_t;
-
-@@ -443,6 +450,8 @@ bool pa_sink_get_mute(pa_sink *sink, bool force_refresh);
-
- bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
-
-+void pa_sink_set_volume_ramp(pa_sink *s, const pa_cvolume_ramp *ramp, bool send_msg, bool save);
-+
- int pa_sink_set_port(pa_sink *s, const char *name, bool save);
- void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty);
-
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@helsinki.fi>
-Date: Wed, 8 Aug 2012 11:14:38 +0300
-Subject: volume ramp: adding volume ramping to sink-input
-
-Change-Id: I9376f531f3585c9ba5fb6d1b384589a8d123b292
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
- src/pulsecore/sink-input.h | 12 +++++++++-
- 2 files changed, 70 insertions(+), 1 deletion(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index f85b2c7..05ab25d 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -527,6 +527,11 @@ int pa_sink_input_new(
- reset_callbacks(i);
- i->userdata = NULL;
-
-+ if (data->flags & PA_SINK_INPUT_START_RAMP_MUTED)
-+ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_MUTED, data->sink->sample_spec.channels);
-+ else
-+ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_NORM, data->sink->sample_spec.channels);
-+
- i->thread_info.state = i->state;
- i->thread_info.attached = false;
- pa_atomic_store(&i->thread_info.drained, 1);
-@@ -543,6 +548,8 @@ int pa_sink_input_new(
- i->thread_info.playing_for = 0;
- i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-
-+ i->thread_info.ramp = i->ramp;
-+
- pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
- pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
-
-@@ -924,6 +931,8 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa
- while (tchunk.length > 0) {
- pa_memchunk wchunk;
- bool nvfs = need_volume_factor_sink;
-+ pa_cvolume target;
-+ bool tmp;
-
- wchunk = tchunk;
- pa_memblock_ref(wchunk.memblock);
-@@ -960,6 +969,16 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa
- pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &i->volume_factor_sink);
- }
-
-+ /* check for possible volume ramp */
-+ if (pa_cvolume_ramp_active(&i->thread_info.ramp)) {
-+ pa_memchunk_make_writable(&wchunk, 0);
-+ pa_volume_ramp_memchunk(&wchunk, &i->sink->sample_spec, &(i->thread_info.ramp));
-+ } else if ((tmp = pa_cvolume_ramp_target_active(&(i->thread_info.ramp)))) {
-+ pa_memchunk_make_writable(&wchunk, 0);
-+ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target);
-+ pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &target);
-+ }
-+
- pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
- } else {
- pa_memchunk rchunk;
-@@ -976,6 +995,16 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa
- pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink);
- }
-
-+ /* check for possible volume ramp */
-+ if (pa_cvolume_ramp_active(&(i->thread_info.ramp))) {
-+ pa_memchunk_make_writable(&rchunk, 0);
-+ pa_volume_ramp_memchunk(&rchunk, &i->sink->sample_spec, &(i->thread_info.ramp));
-+ } else if (pa_cvolume_ramp_target_active(&(i->thread_info.ramp))) {
-+ pa_memchunk_make_writable(&rchunk, 0);
-+ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target);
-+ pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &target);
-+ }
-+
- pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
- pa_memblock_unref(rchunk.memblock);
- }
-@@ -1351,6 +1380,29 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) {
- return 0;
- }
-
-+/* Called from main thread */
-+void pa_sink_input_set_volume_ramp(
-+ pa_sink_input *i,
-+ const pa_cvolume_ramp *ramp,
-+ bool send_msg,
-+ bool save) {
-+
-+ pa_sink_input_assert_ref(i);
-+ pa_assert_ctl_context();
-+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-+ pa_assert(ramp);
-+
-+ pa_cvolume_ramp_convert(ramp, &i->ramp, i->sample_spec.rate);
-+
-+ pa_log_debug("setting volume ramp with target vol:%d and length:%ld",
-+ i->ramp.ramps[0].target,
-+ i->ramp.ramps[0].length);
-+
-+ /* This tells the sink that volume ramp changed */
-+ if (send_msg)
-+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0);
-+}
-+
- /* Called from main context */
- static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
- pa_sink_input_assert_ref(i);
-@@ -1968,6 +2020,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
- }
- return 0;
-
-+ case PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP:
-+ /* we have ongoing ramp where we take current start values */
-+ pa_cvolume_ramp_start_from(&i->thread_info.ramp, &i->ramp);
-+ i->thread_info.ramp = i->ramp;
-+ pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
-+ return 0;
-+
- case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE:
- if (i->thread_info.muted != i->muted) {
- i->thread_info.muted = i->muted;
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index da33717..3f74054 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -35,6 +35,7 @@ typedef struct pa_sink_input pa_sink_input;
- #include <pulsecore/client.h>
- #include <pulsecore/sink.h>
- #include <pulsecore/core.h>
-+#include <pulsecore/mix.h>
-
- typedef enum pa_sink_input_state {
- PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */
-@@ -61,7 +62,8 @@ typedef enum pa_sink_input_flags {
- PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
- PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
- PA_SINK_INPUT_KILL_ON_SUSPEND = 1024,
-- PA_SINK_INPUT_PASSTHROUGH = 2048
-+ PA_SINK_INPUT_PASSTHROUGH = 2048,
-+ PA_SINK_INPUT_START_RAMP_MUTED = 4096,
- } pa_sink_input_flags_t;
-
- struct pa_sink_input {
-@@ -124,6 +126,9 @@ struct pa_sink_input {
- * this.*/
- bool save_sink:1, save_volume:1, save_muted:1;
-
-+ /* for volume ramps */
-+ pa_cvolume_ramp_int ramp;
-+
- pa_resample_method_t requested_resample_method, actual_resample_method;
-
- /* Returns the chunk of audio data and drops it from the
-@@ -252,6 +257,8 @@ struct pa_sink_input {
- pa_usec_t requested_sink_latency;
-
- pa_hashmap *direct_outputs;
-+
-+ pa_cvolume_ramp_int ramp;
- } thread_info;
-
- void *userdata;
-@@ -268,6 +275,7 @@ enum {
- PA_SINK_INPUT_MESSAGE_SET_STATE,
- PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
- PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
-+ PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP,
- PA_SINK_INPUT_MESSAGE_MAX
- };
-
-@@ -377,6 +385,8 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool
- void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save);
- bool pa_sink_input_get_mute(pa_sink_input *i);
-
-+void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg, bool save);
-+
- 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);
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@helsinki.fi>
-Date: Tue, 5 Jun 2012 11:36:13 +0300
-Subject: build-sys: install files for a module development.
-
-Change-Id: Ib68b292e1f6bc82bb5c148ef53acf51cc571406e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- Makefile.am | 11 ++++++++++-
- configure.ac | 1 +
- pulseaudio-module-devel.pc.in | 12 ++++++++++++
- 3 files changed, 23 insertions(+), 1 deletion(-)
- create mode 100644 pulseaudio-module-devel.pc.in
-
-diff --git a/Makefile.am b/Makefile.am
-index b0b2553..a6a0b40 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -50,7 +50,13 @@ dist_vapi_DATA = \
- vala/libpulse-mainloop-glib.deps vala/libpulse-mainloop-glib.vapi
-
- pkgconfigdir = $(libdir)/pkgconfig
--pkgconfig_DATA = libpulse.pc libpulse-simple.pc
-+pkgconfig_DATA = libpulse.pc libpulse-simple.pc pulseaudio-module-devel.pc
-+
-+moduledev_DATA = pulsecore-config.h src/pulsecore/*.h
-+moduledevdir = $(includedir)/pulsemodule/pulsecore
-+
-+moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h
-+moduledevinternaldir = $(includedir)/pulsemodule/pulse
-
- if HAVE_GLIB20
- pkgconfig_DATA += \
-@@ -89,6 +95,9 @@ dist-hook:
- check-daemon:
- $(MAKE) -C src check-daemon
-
-+pulsecore-config.h: config.h
-+ cp $< $@
-+
- .PHONY: homepage distcleancheck doxygen
-
- # see git-version-gen
-diff --git a/configure.ac b/configure.ac
-index 4854711..4e9f97e 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1384,6 +1384,7 @@ man/default.pa.5.xml
- man/pulse-cli-syntax.5.xml
- man/start-pulseaudio-kde.1.xml
- man/start-pulseaudio-x11.1.xml
-+pulseaudio-module-devel.pc
- ])
-
- AC_CONFIG_FILES([src/esdcompat:src/daemon/esdcompat.in], [chmod +x src/esdcompat])
-diff --git a/pulseaudio-module-devel.pc.in b/pulseaudio-module-devel.pc.in
-new file mode 100644
-index 0000000..85aadbc
---- /dev/null
-+++ b/pulseaudio-module-devel.pc.in
-@@ -0,0 +1,12 @@
-+prefix=@prefix@
-+exec_prefix=@exec_prefix@
-+libdir=@libdir@
-+includedir=@includedir@
-+modlibexecdir=@modlibexecdir@
-+
-+Name: pulseaudio-module-devel
-+Description: PulseAudio Module Development Interface
-+Version: @PACKAGE_VERSION@
-+Libs: -L${libdir} -L${libdir}/pulseaudio -L${modlibexecdir} -lpulsecommon-@PA_MAJORMINOR@ -lpulsecore-@PA_MAJORMINOR@ -lprotocol-native
-+Libs.private:
-+Cflags: -I${includedir}/pulsemodule -D_REENTRANT
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Sun, 10 Jun 2012 15:13:11 +0300
-Subject: jack detection fix for wired headset
-
-Change-Id: I53d465bf56adc2e3e5551b43d59ff99b63bc76cc
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/alsa/mixer/paths/analog-output-headphones.conf | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
-index 89d794f..4771bc6 100644
---- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf
-+++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf
-@@ -95,7 +95,7 @@ volume = off
-
- ; On some machines Front is actually a part of the Headphone path
- [Element Front]
--switch = mute
-+switch = off
- volume = zero
-
- [Element Rear]
+++ /dev/null
-From: Janos Kovacs <jankovac503@gmail.com>
-Date: Thu, 16 Aug 2012 03:47:48 +0300
-Subject: make pa_thread_mq_done() safe for subsequent calls
-
-Change-Id: I71ab9efa72c38a2200a56b7a4d30116767bc104f
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/thread-mq.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
-diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
-index d34b22b..f3c5b6c 100644
---- a/src/pulsecore/thread-mq.c
-+++ b/src/pulsecore/thread-mq.c
-@@ -144,6 +144,14 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rt
- void pa_thread_mq_done(pa_thread_mq *q) {
- pa_assert(q);
-
-+ if (!q->main_mainloop && !q->inq && !q->outq &&
-+ !q->read_main_event && !q->write_main_event)
-+ return;
-+
-+ pa_assert(q->main_mainloop);
-+ pa_assert(q->inq && q->outq);
-+ pa_assert(q->read_main_event && q->write_main_event);
-+
- /* Since we are called from main context we can be sure that the
- * inq is empty. However, the outq might still contain messages
- * for the main loop, which we need to dispatch (e.g. release
+++ /dev/null
-From: Ismo Puustinen <ismo.puustinen@intel.com>
-Date: Wed, 28 May 2014 11:11:56 +0300
-Subject: node manager: adding external node manager API
-
-Change-Id: I4f220f05b9de513a7003b3cea761c3540fa74685
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 4 +
- src/map-file | 11 ++
- src/pulse/context.c | 5 +
- src/pulse/ext-node-manager.c | 348 +++++++++++++++++++++++++++++++++++++++++++
- src/pulse/ext-node-manager.h | 93 ++++++++++++
- src/pulse/internal.h | 6 +
- 6 files changed, 467 insertions(+)
- create mode 100644 src/pulse/ext-node-manager.c
- create mode 100644 src/pulse/ext-node-manager.h
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 857fda3..e1808e6 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -753,6 +753,8 @@ pulseinclude_HEADERS = \
- pulse/ext-device-manager.h \
- pulse/ext-device-restore.h \
- pulse/ext-stream-restore.h \
-+ pulse/ext-echo-cancel.h \
-+ pulse/ext-node-manager.h \
- pulse/format.h \
- pulse/gccmacro.h \
- pulse/introspect.h \
-@@ -798,6 +800,8 @@ libpulse_la_SOURCES = \
- pulse/ext-device-manager.c pulse/ext-device-manager.h \
- pulse/ext-device-restore.c pulse/ext-device-restore.h \
- pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
-+ pulse/ext-echo-cancel.c pulse/ext-echo-cancel.h \
-+ pulse/ext-node-manager.c pulse/ext-node-manager.h \
- pulse/format.c pulse/format.h \
- pulse/gccmacro.h \
- pulse/internal.h \
-diff --git a/src/map-file b/src/map-file
-index 9903942..7dbcd00 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -171,6 +171,17 @@ pa_ext_stream_restore_set_subscribe_cb;
- pa_ext_stream_restore_subscribe;
- pa_ext_stream_restore_test;
- pa_ext_stream_restore_write;
-+pa_ext_policy_test;
-+pa_ext_policy_set_mono;
-+pa_ext_policy_set_balance;
-+pa_ext_echo_cancel_set_volume;
-+pa_ext_echo_cancel_set_device;
-+pa_ext_node_manager_test;
-+pa_ext_node_manager_read_nodes;
-+pa_ext_node_manager_connect_nodes;
-+pa_ext_node_manager_disconnect_nodes;
-+pa_ext_node_manager_subscribe;
-+pa_ext_node_manager_set_subscribe_cb;
- pa_format_info_copy;
- pa_format_info_free;
- pa_format_info_from_string;
-diff --git a/src/pulse/context.c b/src/pulse/context.c
-index b78df27..b8688f2 100644
---- a/src/pulse/context.c
-+++ b/src/pulse/context.c
-@@ -122,6 +122,9 @@ static void reset_callbacks(pa_context *c) {
-
- c->ext_stream_restore.callback = NULL;
- c->ext_stream_restore.userdata = NULL;
-+
-+ c->ext_node_manager.callback = NULL;
-+ c->ext_node_manager.userdata = NULL;
- }
-
- pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
-@@ -1361,6 +1364,8 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
- pa_ext_device_restore_command(c, tag, t);
- else if (pa_streq(name, "module-stream-restore"))
- pa_ext_stream_restore_command(c, tag, t);
-+ else if (pa_streq(name, "module-node-manager"))
-+ pa_ext_node_manager_command(c, tag, t);
- else
- pa_log(_("Received message for unknown extension '%s'"), name);
-
-diff --git a/src/pulse/ext-node-manager.c b/src/pulse/ext-node-manager.c
-new file mode 100644
-index 0000000..5cb3feb
---- /dev/null
-+++ b/src/pulse/ext-node-manager.c
-@@ -0,0 +1,348 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2012 Jaska Uimonen
-+ Copyright 2012 Janos Kovacs
-+
-+ 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.1 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 <pulse/context.h>
-+#include <pulse/xmalloc.h>
-+#include <pulse/fork-detect.h>
-+#include <pulse/operation.h>
-+
-+#include <pulsecore/macro.h>
-+#include <pulsecore/pstream-util.h>
-+
-+#include "internal.h"
-+#include "ext-node-manager.h"
-+
-+enum {
-+ SUBCOMMAND_TEST,
-+ SUBCOMMAND_READ,
-+ SUBCOMMAND_CONNECT,
-+ SUBCOMMAND_DISCONNECT,
-+ SUBCOMMAND_SUBSCRIBE,
-+ SUBCOMMAND_EVENT
-+};
-+
-+static void ext_node_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-+ pa_operation *o = userdata;
-+ uint32_t version = PA_INVALID_INDEX;
-+
-+ pa_assert(pd);
-+ pa_assert(o);
-+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
-+
-+ if (!o->context)
-+ goto finish;
-+
-+ if (command != PA_COMMAND_REPLY) {
-+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
-+ goto finish;
-+
-+ } else if (pa_tagstruct_getu32(t, &version) < 0 ||
-+ !pa_tagstruct_eof(t)) {
-+
-+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
-+ goto finish;
-+ }
-+
-+ if (o->callback) {
-+ pa_ext_node_manager_test_cb_t cb = (pa_ext_node_manager_test_cb_t) o->callback;
-+ cb(o->context, version, o->userdata);
-+ }
-+
-+finish:
-+ pa_operation_done(o);
-+ pa_operation_unref(o);
-+}
-+
-+pa_operation *pa_ext_node_manager_test(
-+ pa_context *c,
-+ pa_ext_node_manager_test_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-murphy-ivi");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+static void ext_node_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-+ pa_operation *o = userdata;
-+ pa_ext_node_manager_info i;
-+
-+ pa_assert(pd);
-+ pa_assert(o);
-+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
-+
-+ if (!o->context)
-+ goto finish;
-+
-+ if (command != PA_COMMAND_REPLY) {
-+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
-+ goto finish;
-+ } else {
-+
-+ while (!pa_tagstruct_eof(t)) {
-+
-+ memset(&i, 0, sizeof(i));
-+
-+ i.props = pa_proplist_new();
-+
-+ if (pa_tagstruct_gets(t, &i.name) < 0 ||
-+ pa_tagstruct_get_proplist(t, i.props) < 0) {
-+
-+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
-+ goto finish;
-+ }
-+
-+ if (o->callback) {
-+ pa_ext_node_manager_read_cb_t cb = (pa_ext_node_manager_read_cb_t) o->callback;
-+ cb(o->context, &i, 0, o->userdata);
-+ }
-+
-+ pa_proplist_free(i.props);
-+ }
-+
-+ /* let's send end marker */
-+ if (o->callback) {
-+ pa_ext_node_manager_read_cb_t cb = (pa_ext_node_manager_read_cb_t) o->callback;
-+ cb(o->context, &i, 1, o->userdata);
-+ }
-+ }
-+
-+finish:
-+ pa_operation_done(o);
-+ pa_operation_unref(o);
-+}
-+
-+pa_operation *pa_ext_node_manager_read_nodes(
-+ pa_context *c,
-+ pa_ext_node_manager_read_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-murphy-ivi");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_READ);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+static void ext_node_manager_connect_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-+ pa_operation *o = userdata;
-+ uint32_t connection = PA_INVALID_INDEX;
-+
-+ pa_assert(pd);
-+ pa_assert(o);
-+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
-+
-+ if (!o->context)
-+ goto finish;
-+
-+ if (command != PA_COMMAND_REPLY) {
-+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
-+ goto finish;
-+
-+ } else if (pa_tagstruct_getu32(t, &connection) < 0 ||
-+ !pa_tagstruct_eof(t)) {
-+
-+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
-+ goto finish;
-+ }
-+
-+ if (o->callback) {
-+ pa_ext_node_manager_connect_cb_t cb = (pa_ext_node_manager_connect_cb_t) o->callback;
-+ cb(o->context, connection, o->userdata);
-+ }
-+
-+finish:
-+ pa_operation_done(o);
-+ pa_operation_unref(o);
-+}
-+
-+pa_operation *pa_ext_node_manager_connect_nodes(
-+ pa_context *c,
-+ uint32_t source_node_id,
-+ uint32_t sink_node_id,
-+ pa_ext_node_manager_connect_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-murphy-ivi");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_CONNECT);
-+
-+ pa_tagstruct_putu32(t, source_node_id);
-+ pa_tagstruct_putu32(t, sink_node_id);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_connect_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+pa_operation *pa_ext_node_manager_disconnect_nodes(
-+ pa_context *c,
-+ uint32_t conn_id,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-murphy-ivi");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_DISCONNECT);
-+
-+ pa_tagstruct_putu32(t, conn_id);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+pa_operation *pa_ext_node_manager_subscribe(
-+ pa_context *c,
-+ int enable,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-murphy-ivi");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
-+ pa_tagstruct_put_boolean(t, enable);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+void pa_ext_node_manager_set_subscribe_cb(
-+ pa_context *c,
-+ pa_ext_node_manager_subscribe_cb_t cb,
-+ void *userdata) {
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ if (pa_detect_fork())
-+ return;
-+
-+ c->ext_node_manager.callback = cb;
-+ c->ext_node_manager.userdata = userdata;
-+}
-+
-+void pa_ext_node_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
-+ uint32_t subcommand;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ pa_assert(t);
-+
-+ if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
-+ !pa_tagstruct_eof(t)) {
-+
-+ pa_context_fail(c, PA_ERR_PROTOCOL);
-+ return;
-+ }
-+
-+ if (subcommand != SUBCOMMAND_EVENT) {
-+ pa_context_fail(c, PA_ERR_PROTOCOL);
-+ return;
-+ }
-+
-+ if (c->ext_node_manager.callback)
-+ c->ext_node_manager.callback(c, c->ext_node_manager.userdata);
-+}
-diff --git a/src/pulse/ext-node-manager.h b/src/pulse/ext-node-manager.h
-new file mode 100644
-index 0000000..57b9f01
---- /dev/null
-+++ b/src/pulse/ext-node-manager.h
-@@ -0,0 +1,93 @@
-+#ifndef foopulseextnodemanagerhfoo
-+#define foopulseextnodemanagerhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2012 Jaska Uimonen
-+ Copyright 2012 Janos Kovacs
-+
-+ 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.1 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/cdecl.h>
-+#include <pulse/context.h>
-+#include <pulse/version.h>
-+
-+PA_C_DECL_BEGIN
-+
-+typedef struct pa_ext_node_manager_info {
-+ const char *name;
-+ pa_proplist *props;
-+} pa_ext_node_manager_info;
-+
-+typedef void (*pa_ext_node_manager_test_cb_t)(
-+ pa_context *c,
-+ uint32_t version,
-+ void *userdata);
-+
-+pa_operation *pa_ext_node_manager_test(
-+ pa_context *c,
-+ pa_ext_node_manager_test_cb_t cb,
-+ void *userdata);
-+
-+typedef void (*pa_ext_node_manager_read_cb_t)(
-+ pa_context *c,
-+ const pa_ext_node_manager_info *info,
-+ int eol,
-+ void *userdata);
-+
-+pa_operation *pa_ext_node_manager_read_nodes(
-+ pa_context *c,
-+ pa_ext_node_manager_read_cb_t cb,
-+ void *userdata);
-+
-+typedef void (*pa_ext_node_manager_connect_cb_t)(
-+ pa_context *c,
-+ uint32_t connection,
-+ void *userdata);
-+
-+pa_operation *pa_ext_node_manager_connect_nodes(
-+ pa_context *c,
-+ uint32_t src,
-+ uint32_t dst,
-+ pa_ext_node_manager_connect_cb_t cb,
-+ void *userdata);
-+
-+pa_operation *pa_ext_node_manager_disconnect_nodes(
-+ pa_context *c,
-+ uint32_t conn,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+pa_operation *pa_ext_node_manager_subscribe(
-+ pa_context *c,
-+ int enable,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+typedef void (*pa_ext_node_manager_subscribe_cb_t)(
-+ pa_context *c,
-+ void *userdata);
-+
-+void pa_ext_node_manager_set_subscribe_cb(
-+ pa_context *c,
-+ pa_ext_node_manager_subscribe_cb_t cb,
-+ void *userdata);
-+
-+PA_C_DECL_END
-+
-+#endif
-diff --git a/src/pulse/internal.h b/src/pulse/internal.h
-index c5084d5..61095d0 100644
---- a/src/pulse/internal.h
-+++ b/src/pulse/internal.h
-@@ -31,6 +31,7 @@
- #include <pulse/ext-device-manager.h>
- #include <pulse/ext-device-restore.h>
- #include <pulse/ext-stream-restore.h>
-+#include <pulse/ext-node-manager.h>
-
- #include <pulsecore/socket-client.h>
- #include <pulsecore/pstream.h>
-@@ -115,6 +116,10 @@ struct pa_context {
- pa_ext_stream_restore_subscribe_cb_t callback;
- void *userdata;
- } ext_stream_restore;
-+ struct {
-+ pa_ext_node_manager_subscribe_cb_t callback;
-+ void *userdata;
-+ } ext_node_manager;
- };
-
- #define PA_MAX_WRITE_INDEX_CORRECTIONS 32
-@@ -303,6 +308,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
- void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
- void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
- void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
-+void pa_ext_node_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
-
- bool pa_mainloop_is_our_api(pa_mainloop_api*m);
-
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Wed, 5 Dec 2012 09:53:12 +0200
-Subject: node-manager: adding node support for pactl
-
-Change-Id: Id6badeba2181ef4afa9842307e4c6c60f72c472f
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/utils/pactl.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
- 1 file changed, 72 insertions(+), 3 deletions(-)
-
-diff --git a/src/utils/pactl.c b/src/utils/pactl.c
-index 40e6689..1d8faa4 100644
---- a/src/utils/pactl.c
-+++ b/src/utils/pactl.c
-@@ -38,6 +38,7 @@
-
- #include <pulse/pulseaudio.h>
- #include <pulse/ext-device-restore.h>
-+#include <pulse/ext-node-manager.h>
-
- #include <pulsecore/i18n.h>
- #include <pulsecore/macro.h>
-@@ -97,6 +98,10 @@ static int actions = 1;
-
- static bool nl = false;
-
-+static uint32_t src_node_id;
-+static uint32_t dst_node_id;
-+static uint32_t conn_id;
-+
- static enum {
- NONE,
- EXIT,
-@@ -127,7 +132,9 @@ static enum {
- SET_SOURCE_OUTPUT_MUTE,
- SET_SINK_FORMATS,
- SET_PORT_LATENCY_OFFSET,
-- SUBSCRIBE
-+ SUBSCRIBE,
-+ NODE_CONNECT,
-+ NODE_DISCONNECT
- } action = NONE;
-
- static void quit(int ret) {
-@@ -1007,6 +1014,30 @@ static void source_output_toggle_mute_callback(pa_context *c, const pa_source_ou
- pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
- }
-
-+static void node_list_callback(pa_context *c,
-+ const pa_ext_node_manager_info *info,
-+ int eol,
-+ void *userdata) {
-+
-+ if (!eol) {
-+ const char *node_id = pa_proplist_gets(info->props, "index");
-+ if (node_id != NULL) {
-+ printf("Node #%s (%s)\n", node_id, info->name);
-+ printf("%s\n", pa_proplist_to_string(info->props));
-+ }
-+ } else
-+ complete_action();
-+}
-+
-+static void node_connect_callback(pa_context *c,
-+ uint32_t conne_id,
-+ void *userdata) {
-+
-+ printf("New connection id: %d\n", conne_id);
-+
-+ complete_action();
-+}
-+
- /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
- #define MAX_FORMATS 256
-
-@@ -1212,6 +1243,8 @@ static void context_state_callback(pa_context *c, void *userdata) {
- pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
- else if (pa_streq(list_type, "cards"))
- pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
-+ else if (pa_streq(list_type, "nodes"))
-+ pa_operation_unref(pa_ext_node_manager_read_nodes(c, node_list_callback, NULL));
- else
- pa_assert_not_reached();
- } else {
-@@ -1373,6 +1406,18 @@ static void context_state_callback(pa_context *c, void *userdata) {
- NULL,
- NULL));
- break;
-+ case NODE_CONNECT:
-+ pa_operation_unref(pa_ext_node_manager_connect_nodes(c,
-+ src_node_id,
-+ dst_node_id,
-+ node_connect_callback,
-+ NULL));
-+ break;
-+ case NODE_DISCONNECT:
-+ pa_operation_unref(pa_ext_node_manager_disconnect_nodes(c, conn_id,
-+ simple_callback,
-+ NULL));
-+ break;
-
- default:
- pa_assert_not_reached();
-@@ -1494,6 +1539,9 @@ static void help(const char *argv0) {
- printf("%s %s %s\n", argv0, _("[options]"), "subscribe");
- printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
- "can be used to specify the default sink, source and monitor.\n"));
-+ printf("%s %s %s\n", argv0, _("[options]"), "node-list ");
-+ printf("%s %s %s %s %s\n", argv0, _("[options]"), "node-connect ", _("#N"), _("#N"));
-+ printf("%s %s %s %s\n", argv0, _("[options]"), "node-disconnect ", _("#N"));
-
- printf(_("\n"
- " -h, --help Show this help\n"
-@@ -1590,7 +1638,7 @@ int main(int argc, char *argv[]) {
- if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
- pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
- pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
-- pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
-+ pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes")) {
- list_type = pa_xstrdup(argv[i]);
- } else if (pa_streq(argv[i], "short")) {
- short_list_format = true;
-@@ -1960,7 +2008,28 @@ int main(int argc, char *argv[]) {
- goto quit;
- }
-
-- } else if (pa_streq(argv[optind], "help")) {
-+ } else if (pa_streq(argv[optind], "node-connect")) {
-+ action = NODE_CONNECT;
-+
-+ if (argc != optind+3) {
-+ pa_log(_("You have to specify a source and destination node indexes"));
-+ goto quit;
-+ }
-+
-+ src_node_id = (uint32_t) atoi(argv[optind+1]);
-+ dst_node_id = (uint32_t) atoi(argv[optind+2]);
-+
-+ } else if (pa_streq(argv[optind], "node-disconnect")) {
-+ action = NODE_DISCONNECT;
-+
-+ if (argc != optind+2) {
-+ pa_log(_("You have to specify a connection id"));
-+ goto quit;
-+ }
-+
-+ conn_id = (uint32_t) atoi(argv[optind+1]);
-+
-+ } else if (pa_streq(argv[optind], "help")) {
- help(bn);
- ret = 0;
- goto quit;
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 7 Mar 2013 13:41:44 +0200
-Subject: add internal corking state for sink-input
-
-Change-Id: Iaa7ef16c798e06c0fdf11097690c6cf49215773d
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 30 +++++++++++++++++++++++++++++-
- src/pulsecore/sink-input.h | 4 ++++
- 2 files changed, 33 insertions(+), 1 deletion(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 05ab25d..dff6324 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -788,6 +788,9 @@ void pa_sink_input_put(pa_sink_input *i) {
- update_n_corked(i, state);
- i->state = state;
-
-+ i->corked = FALSE;
-+ i->corked_internal = FALSE;
-+
- /* We might need to update the sink's volume if we are in flat volume mode. */
- if (pa_sink_flat_volume_enabled(i->sink))
- pa_sink_set_volume(i->sink, NULL, false, i->save_volume);
-@@ -1506,13 +1509,38 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p
- }
- }
-
-+static void pa_sink_input_cork_really(pa_sink_input *i, bool b) {
-+ pa_sink_input_assert_ref(i);
-+ pa_assert_ctl_context();
-+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-+
-+ if (i->corked_internal == false && i->corked == false)
-+ b = false;
-+ else
-+ b = true;
-+
-+ sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
-+}
-+
- /* Called from main context */
- void pa_sink_input_cork(pa_sink_input *i, bool b) {
- pa_sink_input_assert_ref(i);
- pa_assert_ctl_context();
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
-- sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
-+ i->corked = b;
-+
-+ pa_sink_input_cork_really(i, b);
-+}
-+
-+void pa_sink_input_cork_internal(pa_sink_input *i, bool b) {
-+ pa_sink_input_assert_ref(i);
-+ pa_assert_ctl_context();
-+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-+
-+ i->corked_internal = b;
-+
-+ pa_sink_input_cork_really(i, b);
- }
-
- /* Called from main context */
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index 3f74054..deea348 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -129,6 +129,9 @@ struct pa_sink_input {
- /* for volume ramps */
- pa_cvolume_ramp_int ramp;
-
-+ bool corked;
-+ bool corked_internal;
-+
- pa_resample_method_t requested_resample_method, actual_resample_method;
-
- /* Returns the chunk of audio data and drops it from the
-@@ -360,6 +363,7 @@ implementing the "zero latency" write-through functionality. */
- void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, bool rewrite, bool flush, bool dont_rewind_render);
-
- void pa_sink_input_cork(pa_sink_input *i, bool b);
-+void pa_sink_input_cork_internal(pa_sink_input *i, bool b);
-
- int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
- int pa_sink_input_update_rate(pa_sink_input *i);
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Wed, 10 Jul 2013 09:45:01 -0300
-Subject: bluetooth: Add basic support for HEADSET profiles
-
-This commit adds basic support for devices implementing HSP Headset
-Unit, HSP Audio Gateway, HFP Handsfree Unit, HFP Audio Gateway to the
-BlueZ 5 bluetooth audio devices driver module (module-bluez5-device).
-
-Change-Id: I6be5bcf310a327012f382d408a70e7fdc65caab1
----
- src/modules/bluetooth/bluez5-util.c | 4 +
- src/modules/bluetooth/bluez5-util.h | 6 +
- src/modules/bluetooth/module-bluez5-device.c | 443 ++++++++++++++++++++-------
- 3 files changed, 345 insertions(+), 108 deletions(-)
-
-diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
-index 7eecc18..ae219ea 100644
---- a/src/modules/bluetooth/bluez5-util.c
-+++ b/src/modules/bluetooth/bluez5-util.c
-@@ -1093,6 +1093,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
- return "a2dp_sink";
- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
- return "a2dp_source";
-+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
-+ return "hsp";
-+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
-+ return "hfgw";
- case PA_BLUETOOTH_PROFILE_OFF:
- return "off";
- }
-diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
-index bbc5b71..841c3c6 100644
---- a/src/modules/bluetooth/bluez5-util.h
-+++ b/src/modules/bluetooth/bluez5-util.h
-@@ -26,6 +26,10 @@
-
- #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb"
- #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb"
-+#define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb"
-+#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb"
-+#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"
-+#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb"
-
- typedef struct pa_bluetooth_transport pa_bluetooth_transport;
- typedef struct pa_bluetooth_device pa_bluetooth_device;
-@@ -41,6 +45,8 @@ typedef enum pa_bluetooth_hook {
- typedef enum profile {
- PA_BLUETOOTH_PROFILE_A2DP_SINK,
- PA_BLUETOOTH_PROFILE_A2DP_SOURCE,
-+ PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT,
-+ PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY,
- PA_BLUETOOTH_PROFILE_OFF
- } pa_bluetooth_profile_t;
- #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF
-diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
-index 287e763..b511b92 100644
---- a/src/modules/bluetooth/module-bluez5-device.c
-+++ b/src/modules/bluetooth/module-bluez5-device.c
-@@ -33,6 +33,7 @@
- #include <pulse/timeval.h>
-
- #include <pulsecore/core-error.h>
-+#include <pulsecore/core-rtclock.h>
- #include <pulsecore/core-util.h>
- #include <pulsecore/i18n.h>
- #include <pulsecore/module.h>
-@@ -59,7 +60,9 @@ PA_MODULE_USAGE("path=<device object path>");
-
- #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
- #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
-+#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
- #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC)
-+#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC)
-
- #define BITPOOL_DEC_LIMIT 32
- #define BITPOOL_DEC_STEP 5
-@@ -235,6 +238,167 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir
- }
-
- /* Run from IO thread */
-+static int sco_process_render(struct userdata *u) {
-+ int ret = 0;
-+
-+ pa_assert(u);
-+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
-+ pa_assert(u->sink);
-+
-+ /* First, render some data */
-+ if (!u->write_memchunk.memblock)
-+ pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
-+
-+ pa_assert(u->write_memchunk.length == u->write_block_size);
-+
-+ for (;;) {
-+ ssize_t l;
-+ const void *p;
-+
-+ /* Now write that data to the socket. The socket is of type
-+ * SEQPACKET, and we generated the data of the MTU size, so this
-+ * should just work. */
-+
-+ p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
-+ l = pa_write(u->stream_fd, p, u->write_memchunk.length, &u->stream_write_type);
-+ pa_memblock_release(u->write_memchunk.memblock);
-+
-+ pa_assert(l != 0);
-+
-+ if (l < 0) {
-+
-+ if (errno == EINTR)
-+ /* Retry right away if we got interrupted */
-+ continue;
-+
-+ else if (errno == EAGAIN)
-+ /* Hmm, apparently the socket was not writable, give up for now */
-+ break;
-+
-+ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
-+ ret = -1;
-+ break;
-+ }
-+
-+ pa_assert((size_t) l <= u->write_memchunk.length);
-+
-+ if ((size_t) l != u->write_memchunk.length) {
-+ pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
-+ (unsigned long long) l,
-+ (unsigned long long) u->write_memchunk.length);
-+ ret = -1;
-+ break;
-+ }
-+
-+ u->write_index += (uint64_t) u->write_memchunk.length;
-+ pa_memblock_unref(u->write_memchunk.memblock);
-+ pa_memchunk_reset(&u->write_memchunk);
-+
-+ ret = 1;
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+/* Run from IO thread */
-+static int sco_process_push(struct userdata *u) {
-+ int ret = 0;
-+ pa_memchunk memchunk;
-+
-+ pa_assert(u);
-+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
-+ pa_assert(u->source);
-+ pa_assert(u->read_smoother);
-+
-+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
-+ memchunk.index = memchunk.length = 0;
-+
-+ for (;;) {
-+ ssize_t l;
-+ void *p;
-+ struct msghdr m;
-+ struct cmsghdr *cm;
-+ uint8_t aux[1024];
-+ struct iovec iov;
-+ bool found_tstamp = false;
-+ pa_usec_t tstamp;
-+
-+ memset(&m, 0, sizeof(m));
-+ memset(&aux, 0, sizeof(aux));
-+ memset(&iov, 0, sizeof(iov));
-+
-+ m.msg_iov = &iov;
-+ m.msg_iovlen = 1;
-+ m.msg_control = aux;
-+ m.msg_controllen = sizeof(aux);
-+
-+ p = pa_memblock_acquire(memchunk.memblock);
-+ iov.iov_base = p;
-+ iov.iov_len = pa_memblock_get_length(memchunk.memblock);
-+ l = recvmsg(u->stream_fd, &m, 0);
-+ pa_memblock_release(memchunk.memblock);
-+
-+ if (l <= 0) {
-+
-+ if (l < 0 && errno == EINTR)
-+ /* Retry right away if we got interrupted */
-+ continue;
-+
-+ else if (l < 0 && errno == EAGAIN)
-+ /* Hmm, apparently the socket was not readable, give up for now. */
-+ break;
-+
-+ pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
-+ ret = -1;
-+ break;
-+ }
-+
-+ pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock));
-+
-+ /* In some rare occasions, we might receive packets of a very strange
-+ * size. This could potentially be possible if the SCO packet was
-+ * received partially over-the-air, or more probably due to hardware
-+ * issues in our Bluetooth adapter. In these cases, in order to avoid
-+ * an assertion failure due to unaligned data, just discard the whole
-+ * packet */
-+ if (!pa_frame_aligned(l, &u->sample_spec)) {
-+ pa_log_warn("SCO packet received of unaligned size: %zu", l);
-+ break;
-+ }
-+
-+ memchunk.length = (size_t) l;
-+ u->read_index += (uint64_t) l;
-+
-+ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
-+ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
-+ struct timeval *tv = (struct timeval*) CMSG_DATA(cm);
-+ pa_rtclock_from_wallclock(tv);
-+ tstamp = pa_timeval_load(tv);
-+ found_tstamp = true;
-+ break;
-+ }
-+
-+ if (!found_tstamp) {
-+ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
-+ tstamp = pa_rtclock_now();
-+ }
-+
-+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
-+ pa_smoother_resume(u->read_smoother, tstamp, true);
-+
-+ pa_source_post(u->source, &memchunk);
-+
-+ ret = l;
-+ break;
-+ }
-+
-+ pa_memblock_unref(memchunk.memblock);
-+
-+ return ret;
-+}
-+
-+/* Run from IO thread */
- static void a2dp_prepare_buffer(struct userdata *u) {
- size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
-
-@@ -608,24 +772,31 @@ static void transport_release(struct userdata *u) {
-
- /* Run from I/O thread */
- static void transport_config_mtu(struct userdata *u) {
-- u->read_block_size =
-- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-- / u->sbc_info.frame_length * u->sbc_info.codesize;
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
-+ u->read_block_size = u->read_link_mtu;
-+ u->write_block_size = u->write_link_mtu;
-+ } else {
-+ u->read_block_size =
-+ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-+ / u->sbc_info.frame_length * u->sbc_info.codesize;
-
-- u->write_block_size =
-- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-- / u->sbc_info.frame_length * u->sbc_info.codesize;
-+ u->write_block_size =
-+ (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
-+ / u->sbc_info.frame_length * u->sbc_info.codesize;
-+ }
-
- if (u->sink) {
- pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
- pa_sink_set_fixed_latency_within_thread(u->sink,
-- FIXED_LATENCY_PLAYBACK_A2DP +
-+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ?
-+ FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +
- pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
- }
-
- if (u->source)
- pa_source_set_fixed_latency_within_thread(u->source,
-- FIXED_LATENCY_RECORD_A2DP +
-+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?
-+ FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +
- pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
- }
-
-@@ -752,15 +923,19 @@ static int add_source(struct userdata *u) {
- data.namereg_fail = false;
- pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
- pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
-+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
-
- connect_ports(u, &data, PA_DIRECTION_INPUT);
-
- if (!u->transport_acquired)
- switch (u->profile) {
- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
-+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
- data.suspend_cause = PA_SUSPEND_USER;
- break;
- case PA_BLUETOOTH_PROFILE_A2DP_SINK:
-+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
- case PA_BLUETOOTH_PROFILE_OFF:
- pa_assert_not_reached();
- break;
-@@ -867,14 +1042,20 @@ static int add_sink(struct userdata *u) {
- data.namereg_fail = false;
- pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
- pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
-+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
-
- connect_ports(u, &data, PA_DIRECTION_OUTPUT);
-
- if (!u->transport_acquired)
- switch (u->profile) {
-+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
-+ data.suspend_cause = PA_SUSPEND_USER;
-+ break;
- case PA_BLUETOOTH_PROFILE_A2DP_SINK:
- /* Profile switch should have failed */
- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
-+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
- case PA_BLUETOOTH_PROFILE_OFF:
- pa_assert_not_reached();
- break;
-@@ -895,111 +1076,117 @@ static int add_sink(struct userdata *u) {
-
- /* Run from main thread */
- static void transport_config(struct userdata *u) {
-- sbc_info_t *sbc_info = &u->sbc_info;
-- a2dp_sbc_t *config;
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
-+ u->sample_spec.format = PA_SAMPLE_S16LE;
-+ u->sample_spec.channels = 1;
-+ u->sample_spec.rate = 8000;
-+ } else {
-+ sbc_info_t *sbc_info = &u->sbc_info;
-+ a2dp_sbc_t *config;
-
-- pa_assert(u->transport);
-+ pa_assert(u->transport);
-
-- u->sample_spec.format = PA_SAMPLE_S16LE;
-- config = (a2dp_sbc_t *) u->transport->config;
-+ u->sample_spec.format = PA_SAMPLE_S16LE;
-+ config = (a2dp_sbc_t *) u->transport->config;
-
-- if (sbc_info->sbc_initialized)
-- sbc_reinit(&sbc_info->sbc, 0);
-- else
-- sbc_init(&sbc_info->sbc, 0);
-- sbc_info->sbc_initialized = true;
-+ if (sbc_info->sbc_initialized)
-+ sbc_reinit(&sbc_info->sbc, 0);
-+ else
-+ sbc_init(&sbc_info->sbc, 0);
-+ sbc_info->sbc_initialized = true;
-
-- switch (config->frequency) {
-- case SBC_SAMPLING_FREQ_16000:
-- sbc_info->sbc.frequency = SBC_FREQ_16000;
-- u->sample_spec.rate = 16000U;
-- break;
-- case SBC_SAMPLING_FREQ_32000:
-- sbc_info->sbc.frequency = SBC_FREQ_32000;
-- u->sample_spec.rate = 32000U;
-- break;
-- case SBC_SAMPLING_FREQ_44100:
-- sbc_info->sbc.frequency = SBC_FREQ_44100;
-- u->sample_spec.rate = 44100U;
-- break;
-- case SBC_SAMPLING_FREQ_48000:
-- sbc_info->sbc.frequency = SBC_FREQ_48000;
-- u->sample_spec.rate = 48000U;
-- break;
-- default:
-- pa_assert_not_reached();
-- }
-+ switch (config->frequency) {
-+ case SBC_SAMPLING_FREQ_16000:
-+ sbc_info->sbc.frequency = SBC_FREQ_16000;
-+ u->sample_spec.rate = 16000U;
-+ break;
-+ case SBC_SAMPLING_FREQ_32000:
-+ sbc_info->sbc.frequency = SBC_FREQ_32000;
-+ u->sample_spec.rate = 32000U;
-+ break;
-+ case SBC_SAMPLING_FREQ_44100:
-+ sbc_info->sbc.frequency = SBC_FREQ_44100;
-+ u->sample_spec.rate = 44100U;
-+ break;
-+ case SBC_SAMPLING_FREQ_48000:
-+ sbc_info->sbc.frequency = SBC_FREQ_48000;
-+ u->sample_spec.rate = 48000U;
-+ break;
-+ default:
-+ pa_assert_not_reached();
-+ }
-
-- switch (config->channel_mode) {
-- case SBC_CHANNEL_MODE_MONO:
-- sbc_info->sbc.mode = SBC_MODE_MONO;
-- u->sample_spec.channels = 1;
-- break;
-- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-- sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;
-- u->sample_spec.channels = 2;
-- break;
-- case SBC_CHANNEL_MODE_STEREO:
-- sbc_info->sbc.mode = SBC_MODE_STEREO;
-- u->sample_spec.channels = 2;
-- break;
-- case SBC_CHANNEL_MODE_JOINT_STEREO:
-- sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;
-- u->sample_spec.channels = 2;
-- break;
-- default:
-- pa_assert_not_reached();
-- }
-+ switch (config->channel_mode) {
-+ case SBC_CHANNEL_MODE_MONO:
-+ sbc_info->sbc.mode = SBC_MODE_MONO;
-+ u->sample_spec.channels = 1;
-+ break;
-+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-+ sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL;
-+ u->sample_spec.channels = 2;
-+ break;
-+ case SBC_CHANNEL_MODE_STEREO:
-+ sbc_info->sbc.mode = SBC_MODE_STEREO;
-+ u->sample_spec.channels = 2;
-+ break;
-+ case SBC_CHANNEL_MODE_JOINT_STEREO:
-+ sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO;
-+ u->sample_spec.channels = 2;
-+ break;
-+ default:
-+ pa_assert_not_reached();
-+ }
-
-- switch (config->allocation_method) {
-- case SBC_ALLOCATION_SNR:
-- sbc_info->sbc.allocation = SBC_AM_SNR;
-- break;
-- case SBC_ALLOCATION_LOUDNESS:
-- sbc_info->sbc.allocation = SBC_AM_LOUDNESS;
-- break;
-- default:
-- pa_assert_not_reached();
-- }
-+ switch (config->allocation_method) {
-+ case SBC_ALLOCATION_SNR:
-+ sbc_info->sbc.allocation = SBC_AM_SNR;
-+ break;
-+ case SBC_ALLOCATION_LOUDNESS:
-+ sbc_info->sbc.allocation = SBC_AM_LOUDNESS;
-+ break;
-+ default:
-+ pa_assert_not_reached();
-+ }
-
-- switch (config->subbands) {
-- case SBC_SUBBANDS_4:
-- sbc_info->sbc.subbands = SBC_SB_4;
-- break;
-- case SBC_SUBBANDS_8:
-- sbc_info->sbc.subbands = SBC_SB_8;
-- break;
-- default:
-- pa_assert_not_reached();
-- }
-+ switch (config->subbands) {
-+ case SBC_SUBBANDS_4:
-+ sbc_info->sbc.subbands = SBC_SB_4;
-+ break;
-+ case SBC_SUBBANDS_8:
-+ sbc_info->sbc.subbands = SBC_SB_8;
-+ break;
-+ default:
-+ pa_assert_not_reached();
-+ }
-
-- switch (config->block_length) {
-- case SBC_BLOCK_LENGTH_4:
-- sbc_info->sbc.blocks = SBC_BLK_4;
-- break;
-- case SBC_BLOCK_LENGTH_8:
-- sbc_info->sbc.blocks = SBC_BLK_8;
-- break;
-- case SBC_BLOCK_LENGTH_12:
-- sbc_info->sbc.blocks = SBC_BLK_12;
-- break;
-- case SBC_BLOCK_LENGTH_16:
-- sbc_info->sbc.blocks = SBC_BLK_16;
-- break;
-- default:
-- pa_assert_not_reached();
-- }
-+ switch (config->block_length) {
-+ case SBC_BLOCK_LENGTH_4:
-+ sbc_info->sbc.blocks = SBC_BLK_4;
-+ break;
-+ case SBC_BLOCK_LENGTH_8:
-+ sbc_info->sbc.blocks = SBC_BLK_8;
-+ break;
-+ case SBC_BLOCK_LENGTH_12:
-+ sbc_info->sbc.blocks = SBC_BLK_12;
-+ break;
-+ case SBC_BLOCK_LENGTH_16:
-+ sbc_info->sbc.blocks = SBC_BLK_16;
-+ break;
-+ default:
-+ pa_assert_not_reached();
-+ }
-
-- sbc_info->min_bitpool = config->min_bitpool;
-- sbc_info->max_bitpool = config->max_bitpool;
-+ sbc_info->min_bitpool = config->min_bitpool;
-+ sbc_info->max_bitpool = config->max_bitpool;
-
-- /* Set minimum bitpool for source to get the maximum possible block_size */
-- sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool;
-- sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
-- sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
-+ /* Set minimum bitpool for source to get the maximum possible block_size */
-+ sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool;
-+ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
-+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
-
-- pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u",
-- sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
-+ pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u",
-+ sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
-+ }
- }
-
- /* Run from main thread */
-@@ -1019,7 +1206,7 @@ static int setup_transport(struct userdata *u) {
-
- u->transport = t;
-
-- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
-+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
- transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
- else if (transport_acquire(u, false) < 0)
- return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
-@@ -1040,11 +1227,13 @@ static int init_profile(struct userdata *u) {
-
- pa_assert(u->transport);
-
-- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
-+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
-+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
- if (add_sink(u) < 0)
- r = -1;
-
-- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
-+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
-+ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
- if (add_source(u) < 0)
- r = -1;
-
-@@ -1090,7 +1279,10 @@ static void thread_func(void *userdata) {
- if (pollfd && (pollfd->revents & POLLIN)) {
- int n_read;
-
-- n_read = a2dp_process_push(u);
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
-+ n_read = sco_process_push(u);
-+ else
-+ n_read = a2dp_process_push(u);
-
- if (n_read < 0)
- goto io_fail;
-@@ -1159,8 +1351,13 @@ static void thread_func(void *userdata) {
- if (u->write_index <= 0)
- u->started_at = pa_rtclock_now();
-
-- if ((n_written = a2dp_process_render(u)) < 0)
-- goto io_fail;
-+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
-+ if ((n_written = a2dp_process_render(u)) < 0)
-+ goto io_fail;
-+ } else {
-+ if ((n_written = sco_process_render(u)) < 0)
-+ goto io_fail;
-+ }
-
- if (n_written == 0)
- pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
-@@ -1364,6 +1561,8 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {
- static const pa_direction_t profile_direction[] = {
- [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT,
- [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT,
-+ [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
-+ [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT,
- [PA_BLUETOOTH_PROFILE_OFF] = 0
- };
-
-@@ -1543,6 +1742,34 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid
-
- p = PA_CARD_PROFILE_DATA(cp);
- *p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
-+ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) {
-+ /* TODO: Change this profile's name to headset_head_unit, to reflect the remote
-+ * device's role and be consistent with the other profiles */
-+ cp = pa_card_profile_new("hsp", _("Headset Head Unit (HSP/HFP)"), sizeof(enum profile));
-+ cp->priority = 20;
-+ cp->n_sinks = 1;
-+ cp->n_sources = 1;
-+ cp->max_sink_channels = 1;
-+ cp->max_source_channels = 1;
-+ pa_hashmap_put(input_port->profiles, cp->name, cp);
-+ pa_hashmap_put(output_port->profiles, cp->name, cp);
-+
-+ p = PA_CARD_PROFILE_DATA(cp);
-+ *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
-+ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) {
-+ /* TODO: Change this profile's name to headset_audio_gateway, to reflect the remote
-+ * device's role and be consistent with the other profiles */
-+ cp = pa_card_profile_new("hfgw", _("Headset Audio Gateway (HSP/HFP)"), sizeof(enum profile));
-+ cp->priority = 20;
-+ cp->n_sinks = 1;
-+ cp->n_sources = 1;
-+ cp->max_sink_channels = 1;
-+ cp->max_source_channels = 1;
-+ pa_hashmap_put(input_port->profiles, cp->name, cp);
-+ pa_hashmap_put(output_port->profiles, cp->name, cp);
-+
-+ p = PA_CARD_PROFILE_DATA(cp);
-+ *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
- }
-
- if (cp && u->device->transports[*p])
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 19:08:15 -0300
-Subject: bluetooth: Create Handsfree Audio Agent NULL backend
-
-Change-Id: I1f562a336f2cd2d0ab91c25fd5128da3cc7fe6df
----
- configure.ac | 10 +++++++++
- src/Makefile.am | 4 +++-
- src/modules/bluetooth/bluez5-util.c | 6 +++++
- src/modules/bluetooth/hfaudioagent-null.c | 37 +++++++++++++++++++++++++++++++
- src/modules/bluetooth/hfaudioagent.h | 31 ++++++++++++++++++++++++++
- 5 files changed, 87 insertions(+), 1 deletion(-)
- create mode 100644 src/modules/bluetooth/hfaudioagent-null.c
- create mode 100644 src/modules/bluetooth/hfaudioagent.h
-
-diff --git a/configure.ac b/configure.ac
-index 4e9f97e..a35918c 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1022,6 +1022,15 @@ AS_IF([test "x$HAVE_BLUEZ_4" = "x1" || test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ
- AC_SUBST(HAVE_BLUEZ)
- AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1])
-
-+## Headset profiles backend ##
-+AC_ARG_WITH(bluetooth_headset_backend, AS_HELP_STRING([--with-bluetooth-headset-backend=<null|ofono>],[Backend for Bluetooth headset profiles (null)]))
-+if test -z "$with_bluetooth_headset_backend" ; then
-+ BLUETOOTH_HEADSET_BACKEND=null
-+else
-+ BLUETOOTH_HEADSET_BACKEND=$with_bluetooth_headset_backend
-+fi
-+AC_SUBST(BLUETOOTH_HEADSET_BACKEND)
-+
- #### UDEV support (optional) ####
-
- AC_ARG_ENABLE([udev],
-@@ -1486,6 +1495,7 @@ echo "
- Enable D-Bus: ${ENABLE_DBUS}
- Enable BlueZ 4: ${ENABLE_BLUEZ_4}
- Enable BlueZ 5: ${ENABLE_BLUEZ_5}
-+ headset backed: ${BLUETOOTH_HEADSET_BACKEND}
- Enable udev: ${ENABLE_UDEV}
- Enable HAL->udev compat: ${ENABLE_HAL_COMPAT}
- Enable systemd login: ${ENABLE_SYSTEMD}
-diff --git a/src/Makefile.am b/src/Makefile.am
-index e1808e6..2edce5f 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -2074,7 +2074,9 @@ module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
- libbluez5_util_la_SOURCES = \
- modules/bluetooth/bluez5-util.c \
- modules/bluetooth/bluez5-util.h \
-- modules/bluetooth/a2dp-codecs.h
-+ modules/bluetooth/a2dp-codecs.h \
-+ modules/bluetooth/hfaudioagent.h \
-+ modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c
- libbluez5_util_la_LDFLAGS = -avoid-version
- libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
- libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
-index ae219ea..35c8adf 100644
---- a/src/modules/bluetooth/bluez5-util.c
-+++ b/src/modules/bluetooth/bluez5-util.c
-@@ -34,6 +34,7 @@
- #include <pulsecore/shared.h>
-
- #include "a2dp-codecs.h"
-+#include "hfaudioagent.h"
-
- #include "bluez5-util.h"
-
-@@ -87,6 +88,7 @@ struct pa_bluetooth_discovery {
- pa_hashmap *devices;
- pa_hashmap *transports;
-
-+ hf_audio_agent_data *hf_audio_agent;
- PA_LLIST_HEAD(pa_dbus_pending, pending);
- };
-
-@@ -1574,6 +1576,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
-
- endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
- endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
-+ y->hf_audio_agent = hf_audio_agent_init(c);
-
- get_managed_objects(y);
-
-@@ -1615,6 +1618,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
- pa_hashmap_free(y->transports);
- }
-
-+ if (y->hf_audio_agent)
-+ hf_audio_agent_done(y->hf_audio_agent);
-+
- if (y->connection) {
-
- if (y->matches_added)
-diff --git a/src/modules/bluetooth/hfaudioagent-null.c b/src/modules/bluetooth/hfaudioagent-null.c
-new file mode 100644
-index 0000000..96fca06
---- /dev/null
-+++ b/src/modules/bluetooth/hfaudioagent-null.c
-@@ -0,0 +1,37 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2013 João Paulo Rechi Vita
-+
-+ 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.1 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/log.h>
-+
-+#include "hfaudioagent.h"
-+
-+hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
-+ pa_log_debug("HandsfreeAudioAgent API support disabled");
-+ return NULL;
-+}
-+
-+void hf_audio_agent_done(hf_audio_agent_data *data) {
-+ /* Nothing to do here */
-+}
-diff --git a/src/modules/bluetooth/hfaudioagent.h b/src/modules/bluetooth/hfaudioagent.h
-new file mode 100644
-index 0000000..2982034
---- /dev/null
-+++ b/src/modules/bluetooth/hfaudioagent.h
-@@ -0,0 +1,31 @@
-+#ifndef foohfagenthfoo
-+#define foohfagenthfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2013 João Paulo Rechi Vita
-+
-+ 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.1 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/core.h>
-+
-+typedef struct hf_audio_agent_data hf_audio_agent_data;
-+
-+hf_audio_agent_data *hf_audio_agent_init(pa_core *c);
-+void hf_audio_agent_done(hf_audio_agent_data *data);
-+#endif
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 20:22:17 -0300
-Subject: bluetooth: Create Handsfree Audio Agent oFono backend
-
-Change-Id: I2d5793e997205a04b37b9389ab75fc8643adcece
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 145 +++++++++++++++++++++++++++++
- 1 file changed, 145 insertions(+)
- create mode 100644 src/modules/bluetooth/hfaudioagent-ofono.c
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-new file mode 100644
-index 0000000..af78d4d
---- /dev/null
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -0,0 +1,145 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2013 João Paulo Rechi Vita
-+
-+ 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.1 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/core-util.h>
-+#include <pulsecore/dbus-shared.h>
-+
-+#include "hfaudioagent.h"
-+
-+#define OFONO_SERVICE "org.ofono"
-+#define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
-+
-+#define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
-+
-+#define HF_AUDIO_AGENT_XML \
-+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
-+ "<node>" \
-+ " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
-+ " <method name=\"Introspect\">" \
-+ " <arg direction=\"out\" type=\"s\" />" \
-+ " </method>" \
-+ " </interface>" \
-+ " <interface name=\"org.ofono.HandsfreeAudioAgent\">" \
-+ " <method name=\"Release\">" \
-+ " </method>" \
-+ " <method name=\"NewConnection\">" \
-+ " <arg direction=\"in\" type=\"o\" name=\"card_path\" />" \
-+ " <arg direction=\"in\" type=\"h\" name=\"sco_fd\" />" \
-+ " <arg direction=\"in\" type=\"y\" name=\"codec\" />" \
-+ " </method>" \
-+ " </interface>" \
-+ "</node>"
-+
-+struct hf_audio_agent_data {
-+ pa_core *core;
-+ pa_dbus_connection *connection;
-+};
-+
-+static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
-+ DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ return r;
-+}
-+
-+static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
-+ DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ return r;
-+}
-+
-+static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) {
-+ hf_audio_agent_data *hfdata = data;
-+ DBusMessage *r = NULL;
-+ const char *path, *interface, *member;
-+
-+ pa_assert(hfdata);
-+
-+ path = dbus_message_get_path(m);
-+ interface = dbus_message_get_interface(m);
-+ member = dbus_message_get_member(m);
-+
-+ if (!pa_streq(path, HF_AUDIO_AGENT_PATH))
-+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-+
-+ pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
-+
-+ if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-+ const char *xml = HF_AUDIO_AGENT_XML;
-+
-+ pa_assert_se(r = dbus_message_new_method_return(m));
-+ pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
-+
-+ } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection"))
-+ r = hf_audio_agent_new_connection(c, m, data);
-+ else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release"))
-+ r = hf_audio_agent_release(c, m, data);
-+ else
-+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-+
-+ if (r) {
-+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), r, NULL));
-+ dbus_message_unref(r);
-+ }
-+
-+ return DBUS_HANDLER_RESULT_HANDLED;
-+}
-+
-+hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
-+ hf_audio_agent_data *hfdata;
-+ DBusError err;
-+ static const DBusObjectPathVTable vtable_hf_audio_agent = {
-+ .message_function = hf_audio_agent_handler,
-+ };
-+
-+ pa_assert(c);
-+
-+ hfdata = pa_xnew0(hf_audio_agent_data, 1);
-+ hfdata->core = c;
-+
-+ dbus_error_init(&err);
-+
-+ if (!(hfdata->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
-+ pa_log("Failed to get D-Bus connection: %s", err.message);
-+ dbus_error_free(&err);
-+ return NULL;
-+ }
-+
-+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH,
-+ &vtable_hf_audio_agent, hfdata));
-+
-+ return hfdata;
-+}
-+
-+void hf_audio_agent_done(hf_audio_agent_data *data) {
-+ hf_audio_agent_data *hfdata = data;
-+
-+ pa_assert(hfdata);
-+
-+ if (hfdata->connection) {
-+ dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH);
-+
-+ pa_dbus_connection_unref(hfdata->connection);
-+ }
-+
-+ pa_xfree(hfdata);
-+}
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Wed, 10 Jul 2013 12:18:07 -0300
-Subject: bluetooth: Monitor D-Bus signals
-
-Change-Id: Ibfd4ab9acaf49df0fdfaff55609c560ab31f4df4
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 48 ++++++++++++++++++++++++++++++
- 1 file changed, 48 insertions(+)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index af78d4d..0e3ffc4 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -30,6 +30,7 @@
-
- #define OFONO_SERVICE "org.ofono"
- #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
-+#define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
-
- #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
-
-@@ -55,8 +56,18 @@
- struct hf_audio_agent_data {
- pa_core *core;
- pa_dbus_connection *connection;
-+
-+ bool filter_added;
-+ pa_hashmap *hf_audio_cards;
- };
-
-+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
-+ pa_assert(bus);
-+ pa_assert(m);
-+
-+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-+}
-+
- static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
- DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
- return r;
-@@ -115,6 +126,7 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
-
- hfdata = pa_xnew0(hf_audio_agent_data, 1);
- hfdata->core = c;
-+ hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-
- dbus_error_init(&err);
-
-@@ -124,6 +136,25 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
- return NULL;
- }
-
-+ /* dynamic detection of handsfree audio cards */
-+ if (!dbus_connection_add_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata, NULL)) {
-+ pa_log_error("Failed to add filter function");
-+ hf_audio_agent_done(hfdata);
-+ return NULL;
-+ }
-+ hfdata->filter_added = true;
-+
-+ if (pa_dbus_add_matches(pa_dbus_connection_get(hfdata->connection), &err,
-+ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
-+ "arg0='" OFONO_SERVICE "'",
-+ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
-+ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
-+ NULL) < 0) {
-+ pa_log("Failed to add oFono D-Bus matches: %s", err.message);
-+ hf_audio_agent_done(hfdata);
-+ return NULL;
-+ }
-+
- pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH,
- &vtable_hf_audio_agent, hfdata));
-
-@@ -135,7 +166,24 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
-
- pa_assert(hfdata);
-
-+ if (hfdata->hf_audio_cards) {
-+ pa_hashmap_free(hfdata->hf_audio_cards, NULL);
-+ hfdata->hf_audio_cards = NULL;
-+ }
-+
- if (hfdata->connection) {
-+
-+ pa_dbus_remove_matches(
-+ pa_dbus_connection_get(hfdata->connection),
-+ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
-+ "arg0='" OFONO_SERVICE "'",
-+ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
-+ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
-+ NULL);
-+
-+ if (hfdata->filter_added)
-+ dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata);
-+
- dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH);
-
- pa_dbus_connection_unref(hfdata->connection);
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Wed, 10 Jul 2013 13:00:16 -0300
-Subject: bluetooth: Create pa_bluetooth_dbus_send_and_add_to_pending() for
- oFono backend
-
-Change-Id: I9b6ea22d6bd1059e5d7b3a12ec13b2768cacce0d
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 21 +++++++++++++++++++++
- 1 file changed, 21 insertions(+)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 0e3ffc4..38975b2 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -59,8 +59,27 @@ struct hf_audio_agent_data {
-
- bool filter_added;
- pa_hashmap *hf_audio_cards;
-+
-+ PA_LLIST_HEAD(pa_dbus_pending, pending);
- };
-
-+static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent_data *hfdata, DBusMessage *m,
-+ DBusPendingCallNotifyFunction func, void *call_data) {
-+ pa_dbus_pending *p;
-+ DBusPendingCall *call;
-+
-+ pa_assert(hfdata);
-+ pa_assert(m);
-+
-+ pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(hfdata->connection), m, &call, -1));
-+
-+ p = pa_dbus_pending_new(pa_dbus_connection_get(hfdata->connection), m, call, hfdata, call_data);
-+ PA_LLIST_PREPEND(pa_dbus_pending, hfdata->pending, p);
-+ dbus_pending_call_set_notify(call, func, p, NULL);
-+
-+ return p;
-+}
-+
- static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
- pa_assert(bus);
- pa_assert(m);
-@@ -166,6 +185,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
-
- pa_assert(hfdata);
-
-+ pa_dbus_free_pending_list(&hfdata->pending);
-+
- if (hfdata->hf_audio_cards) {
- pa_hashmap_free(hfdata->hf_audio_cards, NULL);
- hfdata->hf_audio_cards = NULL;
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 20:59:12 -0300
-Subject: bluetooth: Register/Unregister Handsfree Audio Agent with oFono
-
-Register as a HandsfreeAudioAgent with oFono during backend
-initialization and unregiter during backend finalization. This commit
-also adds a check when receiving method calls or signals to make sure
-the sender matches with the D-Bus service we're registered with.
-
-Change-Id: I0a49935702ffb52d6d2ba1baded8eb568262c5b1
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 107 ++++++++++++++++++++++++++++-
- 1 file changed, 105 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 38975b2..de58e7d 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -28,6 +28,9 @@
-
- #include "hfaudioagent.h"
-
-+#define HFP_AUDIO_CODEC_CVSD 0x01
-+#define HFP_AUDIO_CODEC_MSBC 0x02
-+
- #define OFONO_SERVICE "org.ofono"
- #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
- #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
-@@ -58,6 +61,7 @@ struct hf_audio_agent_data {
- pa_dbus_connection *connection;
-
- bool filter_added;
-+ char *ofono_bus_id;
- pa_hashmap *hf_audio_cards;
-
- PA_LLIST_HEAD(pa_dbus_pending, pending);
-@@ -80,20 +84,115 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent
- return p;
- }
-
-+static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) {
-+ DBusMessage *r;
-+ pa_dbus_pending *p;
-+ hf_audio_agent_data *hfdata;
-+
-+ pa_assert_se(p = userdata);
-+ pa_assert_se(hfdata = p->context_data);
-+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
-+
-+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
-+ pa_log_error("Failed to register as a handsfree audio agent with ofono: %s: %s",
-+ dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
-+ goto finish;
-+ }
-+
-+ hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r));
-+
-+ /* TODO: List all HandsfreeAudioCard objects */
-+
-+finish:
-+ dbus_message_unref(r);
-+
-+ PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
-+ pa_dbus_pending_free(p);
-+}
-+
-+static void hf_audio_agent_register(hf_audio_agent_data *hfdata) {
-+ DBusMessage *m;
-+ unsigned char codecs[2];
-+ const unsigned char *pcodecs = codecs;
-+ int ncodecs = 0;
-+ const char *path = HF_AUDIO_AGENT_PATH;
-+
-+ pa_assert(hfdata);
-+
-+ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register"));
-+
-+ codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
-+ codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC;
-+
-+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
-+ DBUS_TYPE_INVALID));
-+
-+ pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_register_reply, NULL);
-+}
-+
-+static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) {
-+ DBusMessage *m;
-+ const char *path = HF_AUDIO_AGENT_PATH;
-+
-+ pa_assert(hfdata);
-+ pa_assert(hfdata->connection);
-+
-+ if (hfdata->ofono_bus_id) {
-+ pa_assert_se(m = dbus_message_new_method_call(hfdata->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister"));
-+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID));
-+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL));
-+
-+ pa_xfree(hfdata->ofono_bus_id);
-+ hfdata->ofono_bus_id = NULL;
-+ }
-+}
-+
- static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
-+ const char *sender;
-+ hf_audio_agent_data *hfdata = data;
-+
- pa_assert(bus);
- pa_assert(m);
-+ pa_assert(hfdata);
-+
-+ sender = dbus_message_get_sender(m);
-+ if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
-+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
-- DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ DBusMessage *r;
-+ const char *sender;
-+ hf_audio_agent_data *hfdata = data;
-+
-+ pa_assert(hfdata);
-+
-+ sender = dbus_message_get_sender(m);
-+ if (!pa_streq(hfdata->ofono_bus_id, sender)) {
-+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
-+ return r;
-+ }
-+
-+ r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
- return r;
- }
-
- static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
-- DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ DBusMessage *r;
-+ const char *sender;
-+ hf_audio_agent_data *hfdata = data;
-+
-+ pa_assert(hfdata);
-+
-+ sender = dbus_message_get_sender(m);
-+ if (!pa_streq(hfdata->ofono_bus_id, sender)) {
-+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
-+ return r;
-+ }
-+
-+ r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
- return r;
- }
-
-@@ -177,6 +276,8 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
- pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH,
- &vtable_hf_audio_agent, hfdata));
-
-+ hf_audio_agent_register(hfdata);
-+
- return hfdata;
- }
-
-@@ -205,6 +306,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
- if (hfdata->filter_added)
- dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata);
-
-+ hf_audio_agent_unregister(hfdata);
-+
- dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH);
-
- pa_dbus_connection_unref(hfdata->connection);
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 21:03:28 -0300
-Subject: bluetooth: List HandsfreeAudioCard objects from oFono
-
-Change-Id: Idb2423d8b5640486976df5347e2f7d05a9e6dd71
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 71 +++++++++++++++++++++++++++++-
- 1 file changed, 70 insertions(+), 1 deletion(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index de58e7d..6b27f80 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -84,6 +84,75 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent
- return p;
- }
-
-+static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) {
-+ DBusMessage *r;
-+ pa_dbus_pending *p;
-+ hf_audio_agent_data *hfdata;
-+ DBusMessageIter i, array_i, struct_i, props_i;
-+ char c;
-+
-+ pa_assert_se(p = userdata);
-+ pa_assert_se(hfdata = p->context_data);
-+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
-+
-+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
-+ pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s",
-+ dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
-+ goto finish;
-+ }
-+
-+ dbus_message_iter_init(r, &i);
-+ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_ARRAY) {
-+ pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c);
-+ goto finish;
-+ }
-+
-+ dbus_message_iter_recurse(&i, &array_i);
-+ while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
-+ const char *path;
-+
-+ if ((c = dbus_message_iter_get_arg_type(&array_i)) != DBUS_TYPE_STRUCT) {
-+ pa_log_error("Invalid arguments in GetCards() reply: expected \'r\', received \'%c\'", c);
-+ goto finish;
-+ }
-+
-+ dbus_message_iter_recurse(&array_i, &struct_i);
-+ if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_OBJECT_PATH) {
-+ pa_log_error("Invalid arguments in GetCards() reply: expected \'o\', received \'%c\'", c);
-+ goto finish;
-+ }
-+
-+ dbus_message_iter_get_basic(&struct_i, &path);
-+
-+ dbus_message_iter_next(&struct_i);
-+ if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_ARRAY) {
-+ pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c);
-+ goto finish;
-+ }
-+
-+ dbus_message_iter_recurse(&struct_i, &props_i);
-+
-+ /* TODO: Parse HandsfreeAudioCard properties */
-+
-+ dbus_message_iter_next(&array_i);
-+ }
-+
-+finish:
-+ dbus_message_unref(r);
-+
-+ PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
-+ pa_dbus_pending_free(p);
-+}
-+
-+static void hf_audio_agent_get_cards(hf_audio_agent_data *hfdata) {
-+ DBusMessage *m;
-+
-+ pa_assert(hfdata);
-+
-+ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards"));
-+ pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_get_cards_reply, NULL);
-+}
-+
- static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) {
- DBusMessage *r;
- pa_dbus_pending *p;
-@@ -101,7 +170,7 @@ static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userda
-
- hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r));
-
-- /* TODO: List all HandsfreeAudioCard objects */
-+ hf_audio_agent_get_cards(hfdata);
-
- finish:
- dbus_message_unref(r);
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 21:37:26 -0300
-Subject: bluetooth: Parse HandsfreeAudioCard properties
-
-Change-Id: I37600ca3ab60d1b82608f4e395fa427fdaf62164
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 128 ++++++++++++++++++++++++++++-
- 1 file changed, 126 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 6b27f80..2e16a48 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -25,6 +25,9 @@
-
- #include <pulsecore/core-util.h>
- #include <pulsecore/dbus-shared.h>
-+#include <pulsecore/shared.h>
-+
-+#include "bluez5-util.h"
-
- #include "hfaudioagent.h"
-
-@@ -56,9 +59,21 @@
- " </interface>" \
- "</node>"
-
-+typedef struct hf_audio_card {
-+ char *path;
-+ char *remote;
-+ char *local;
-+
-+ int fd;
-+ uint8_t codec;
-+
-+ pa_bluetooth_transport *transport;
-+} hf_audio_card;
-+
- struct hf_audio_agent_data {
- pa_core *core;
- pa_dbus_connection *connection;
-+ pa_bluetooth_discovery *discovery;
-
- bool filter_added;
- char *ofono_bus_id;
-@@ -84,6 +99,111 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent
- return p;
- }
-
-+static hf_audio_card *hf_audio_card_new(hf_audio_agent_data *hfdata, const char *path) {
-+ hf_audio_card *hfac = pa_xnew0(hf_audio_card, 1);
-+
-+ hfac->path = pa_xstrdup(path);
-+ hfac->fd = -1;
-+
-+ return hfac;
-+}
-+
-+static void hf_audio_card_free(void *data) {
-+ hf_audio_card *hfac = data;
-+
-+ pa_assert(hfac);
-+
-+ pa_bluetooth_transport_free(hfac->transport);
-+ pa_xfree(hfac->path);
-+ pa_xfree(hfac->remote);
-+ pa_xfree(hfac->local);
-+ pa_xfree(hfac);
-+}
-+
-+static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
-+ return -1;
-+}
-+
-+static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
-+}
-+
-+static void hf_audio_agent_card_found(hf_audio_agent_data *hfdata, const char *path, DBusMessageIter *props_i) {
-+ DBusMessageIter i, value_i;
-+ const char *key, *value;
-+ hf_audio_card *hfac;
-+ pa_bluetooth_device *d;
-+
-+ pa_assert(hfdata);
-+ pa_assert(path);
-+ pa_assert(props_i);
-+
-+ pa_log_debug("New HF card found: %s", path);
-+
-+ hfac = hf_audio_card_new(hfdata, path);
-+
-+ while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) {
-+ char c;
-+
-+ if ((c = dbus_message_iter_get_arg_type(props_i)) != DBUS_TYPE_DICT_ENTRY) {
-+ pa_log_error("Invalid properties for %s: expected \'e\', received \'%c\'", path, c);
-+ goto fail;
-+ }
-+
-+ dbus_message_iter_recurse(props_i, &i);
-+
-+ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_STRING) {
-+ pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c);
-+ goto fail;
-+ }
-+
-+ dbus_message_iter_get_basic(&i, &key);
-+ dbus_message_iter_next(&i);
-+
-+ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_VARIANT) {
-+ pa_log_error("Invalid properties for %s: expected \'v\', received \'%c\'", path, c);
-+ goto fail;
-+ }
-+
-+ dbus_message_iter_recurse(&i, &value_i);
-+
-+ if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) {
-+ pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c);
-+ goto fail;
-+ }
-+
-+ dbus_message_iter_get_basic(&value_i, &value);
-+
-+ if (pa_streq(key, "RemoteAddress"))
-+ hfac->remote = pa_xstrdup(value);
-+ else if (pa_streq(key, "LocalAddress"))
-+ hfac->local = pa_xstrdup(value);
-+
-+ pa_log_debug("%s: %s", key, value);
-+
-+ dbus_message_iter_next(props_i);
-+ }
-+
-+ pa_hashmap_put(hfdata->hf_audio_cards, hfac->path, hfac);
-+
-+ d = pa_bluetooth_discovery_get_device_by_address(hfdata->discovery, hfac->remote, hfac->local);
-+ if (d) {
-+ hfac->transport = pa_bluetooth_transport_new(d, hfdata->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0);
-+ hfac->transport->acquire = hf_audio_agent_transport_acquire;
-+ hfac->transport->release = hf_audio_agent_transport_release;
-+ hfac->transport->userdata = hfdata;
-+
-+ d->transports[PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = hfac->transport;
-+
-+ pa_bluetooth_transport_put(hfac->transport);
-+ } else
-+ pa_log_error("Device doesnt exist for %s", path);
-+
-+ return;
-+
-+fail:
-+ pa_xfree(hfac);
-+}
-+
- static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) {
- DBusMessage *r;
- pa_dbus_pending *p;
-@@ -132,7 +252,7 @@ static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userd
-
- dbus_message_iter_recurse(&struct_i, &props_i);
-
-- /* TODO: Parse HandsfreeAudioCard properties */
-+ hf_audio_agent_card_found(hfdata, path, &props_i);
-
- dbus_message_iter_next(&array_i);
- }
-@@ -314,6 +434,7 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
- hfdata = pa_xnew0(hf_audio_agent_data, 1);
- hfdata->core = c;
- hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+ hfdata->discovery = pa_shared_get(c, "bluetooth-discovery");
-
- dbus_error_init(&err);
-
-@@ -358,7 +479,7 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
- pa_dbus_free_pending_list(&hfdata->pending);
-
- if (hfdata->hf_audio_cards) {
-- pa_hashmap_free(hfdata->hf_audio_cards, NULL);
-+ pa_hashmap_free(hfdata->hf_audio_cards, hf_audio_card_free);
- hfdata->hf_audio_cards = NULL;
- }
-
-@@ -382,5 +503,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
- pa_dbus_connection_unref(hfdata->connection);
- }
-
-+ if (hfdata->discovery)
-+ hfdata->discovery = NULL;
-+
- pa_xfree(hfdata);
- }
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Tue, 9 Jul 2013 22:22:28 -0300
-Subject: bluetooth: Implement transport acquire for hf_audio_agent transports
-
-Change-Id: Ia092e75c51f79d1a594ba6dacda55ec03a5dae93
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 28 +++++++++++++++++++++++++++-
- 1 file changed, 27 insertions(+), 1 deletion(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 2e16a48..3684bed 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -121,7 +121,33 @@ static void hf_audio_card_free(void *data) {
- }
-
- static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
-- return -1;
-+ hf_audio_agent_data *hfdata = t->userdata;
-+ hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path);
-+
-+ if (!optional) {
-+ DBusMessage *m;
-+
-+ pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect"));
-+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL));
-+
-+ return -1;
-+ }
-+
-+ /* The correct block size should take into account the SCO MTU from
-+ * the Bluetooth adapter and (for adapters in the USB bus) the MxPS
-+ * value from the Isoc USB endpoint in use by btusb and should be
-+ * made available to userspace by the Bluetooth kernel subsystem.
-+ * Meanwhile the empiric value 48 will be used. */
-+ if (imtu)
-+ *imtu = 48;
-+ if (omtu)
-+ *omtu = 48;
-+
-+ if (hfac) {
-+ t->codec = hfac->codec;
-+ return hfac->fd;
-+ } else
-+ return -1;
- }
-
- static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Thu, 11 Jul 2013 11:43:48 -0300
-Subject: bluetooth: Track oFono service
-
-Change-Id: I6752f21b848757dbea20c92c68b581141cefbc67
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 61 ++++++++++++++++++++++++++++--
- 1 file changed, 58 insertions(+), 3 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 3684bed..97e0fa8 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -364,6 +364,7 @@ static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) {
-
- static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
- const char *sender;
-+ DBusError err;
- hf_audio_agent_data *hfdata = data;
-
- pa_assert(bus);
-@@ -374,6 +375,46 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da
- if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-+ dbus_error_init(&err);
-+
-+ if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
-+ const char *name, *old_owner, *new_owner;
-+
-+ if (!dbus_message_get_args(m, &err,
-+ DBUS_TYPE_STRING, &name,
-+ DBUS_TYPE_STRING, &old_owner,
-+ DBUS_TYPE_STRING, &new_owner,
-+ DBUS_TYPE_INVALID)) {
-+ pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
-+ goto fail;
-+ }
-+
-+ if (pa_streq(name, OFONO_SERVICE)) {
-+
-+ if (old_owner && *old_owner) {
-+ pa_log_debug("oFono disappeared");
-+
-+ if (hfdata->hf_audio_cards) {
-+ pa_hashmap_free(hfdata->hf_audio_cards);
-+ hfdata->hf_audio_cards = NULL;
-+ }
-+
-+ if(hfdata->ofono_bus_id) {
-+ pa_xfree(hfdata->ofono_bus_id);
-+ hfdata->ofono_bus_id = NULL;
-+ }
-+ }
-+
-+ if (new_owner && *new_owner) {
-+ pa_log_debug("oFono appeared");
-+ hf_audio_agent_register(hfdata);
-+ }
-+ }
-+
-+ }
-+
-+fail:
-+ dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
-@@ -390,7 +431,20 @@ static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, vo
- return r;
- }
-
-- r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ pa_log_debug("HF audio agent has been unregistered by oFono (%s)", hfdata->ofono_bus_id);
-+
-+ if (hfdata->hf_audio_cards) {
-+ pa_hashmap_free(hfdata->hf_audio_cards);
-+ hfdata->hf_audio_cards = NULL;
-+ }
-+
-+ if(hfdata->ofono_bus_id) {
-+ pa_xfree(hfdata->ofono_bus_id);
-+ hfdata->ofono_bus_id = NULL;
-+ }
-+
-+ pa_assert_se(r = dbus_message_new_method_return(m));
-+
- return r;
- }
-
-@@ -459,7 +513,8 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
-
- hfdata = pa_xnew0(hf_audio_agent_data, 1);
- hfdata->core = c;
-- hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+ hfdata->hf_audio_cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
-+ NULL, hf_audio_card_free);
- hfdata->discovery = pa_shared_get(c, "bluetooth-discovery");
-
- dbus_error_init(&err);
-@@ -505,7 +560,7 @@ void hf_audio_agent_done(hf_audio_agent_data *data) {
- pa_dbus_free_pending_list(&hfdata->pending);
-
- if (hfdata->hf_audio_cards) {
-- pa_hashmap_free(hfdata->hf_audio_cards, hf_audio_card_free);
-+ pa_hashmap_free(hfdata->hf_audio_cards);
- hfdata->hf_audio_cards = NULL;
- }
-
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Thu, 11 Jul 2013 11:47:37 -0300
-Subject: bluetooth: Handle CardAdded signal
-
-Change-Id: I695b97e26ce369d76503980c73fc3849252b4c91
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 97e0fa8..be20257 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -411,6 +411,24 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da
- }
- }
-
-+ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) {
-+ const char *p;
-+ DBusMessageIter arg_i, props_i;
-+
-+ if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
-+ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded");
-+ goto fail;
-+ }
-+
-+ dbus_message_iter_get_basic(&arg_i, &p);
-+
-+ pa_assert_se(dbus_message_iter_next(&arg_i));
-+ pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
-+
-+ dbus_message_iter_recurse(&arg_i, &props_i);
-+
-+ hf_audio_agent_card_found(hfdata, p, &props_i);
-+
- }
-
- fail:
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Thu, 11 Jul 2013 12:53:10 -0300
-Subject: bluetooth: Handle CardRemoved signal
-
-Change-Id: I41083b3b5d8a318927dcdb373e871032c3f52748
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 23 +++++++++++++++++++++++
- 1 file changed, 23 insertions(+)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index be20257..ee158af 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -429,6 +429,29 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da
-
- hf_audio_agent_card_found(hfdata, p, &props_i);
-
-+ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) {
-+ const char *p;
-+ hf_audio_card *hfac;
-+ bool old_any_connected;
-+
-+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) {
-+ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message);
-+ goto fail;
-+ }
-+
-+ if ((hfac = pa_hashmap_remove(hfdata->hf_audio_cards, p)) != NULL) {
-+ old_any_connected = pa_bluetooth_device_any_transport_connected(hfac->transport->device);
-+
-+ hfac->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
-+ hfac->transport->device->transports[hfac->transport->profile] = NULL;
-+ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), hfac->transport);
-+
-+ if (old_any_connected != pa_bluetooth_device_any_transport_connected(hfac->transport->device)) {
-+ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), hfac->transport->device);
-+ }
-+
-+ hf_audio_card_free(hfac);
-+ }
- }
-
- fail:
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Thu, 11 Jul 2013 13:23:31 -0300
-Subject: bluetooth: Implement org.ofono.HandsfreeAudioAgent.NewConnection()
-
-Change-Id: Idaff2e3a96ce83e7ee6f961543273429b044be8e
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 29 +++++++++++++++++++++++++++--
- 1 file changed, 27 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index ee158af..ba0acaf 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -491,7 +491,10 @@ static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, vo
-
- static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
- DBusMessage *r;
-- const char *sender;
-+ const char *sender, *card;
-+ int fd;
-+ uint8_t codec;
-+ hf_audio_card *hfac;
- hf_audio_agent_data *hfdata = data;
-
- pa_assert(hfdata);
-@@ -502,7 +505,29 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
- return r;
- }
-
-- r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
-+ if (dbus_message_get_args(m, NULL,
-+ DBUS_TYPE_OBJECT_PATH, &card,
-+ DBUS_TYPE_UNIX_FD, &fd,
-+ DBUS_TYPE_BYTE, &codec,
-+ DBUS_TYPE_INVALID) == FALSE) {
-+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
-+ return r;
-+ }
-+
-+ if ( !(hfac = pa_hashmap_get(hfdata->hf_audio_cards, card)) ) {
-+ pa_log_warn("New audio connection on unknown card %s (fd=%d, codec=%d)", card, fd, codec);
-+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Unknown card"));
-+ return r;
-+ } else
-+ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec);
-+
-+ hfac->fd = fd;
-+ hfac->codec = codec;
-+ hfac->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
-+ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), hfac->transport);
-+
-+ pa_assert_se(r = dbus_message_new_method_return(m));
-+
- return r;
- }
-
+++ /dev/null
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 23 Aug 2013 12:56:40 +0300
-Subject: bluetooth: Fix not handle fd in DEFER_SETUP state
-
-The fd passed over NewConnection is in DEFER_SETUP and need to be read to
-be accept otherwise it wont work.
-
-Change-Id: I2f6df033d3c1602064a39bb40a5bbd60e014c8f7
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 14 ++++++++++++--
- 1 file changed, 12 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index ba0acaf..90d1348 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -23,6 +23,8 @@
- #include <config.h>
- #endif
-
-+#include <errno.h>
-+
- #include <pulsecore/core-util.h>
- #include <pulsecore/dbus-shared.h>
- #include <pulsecore/shared.h>
-@@ -518,8 +520,16 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
- pa_log_warn("New audio connection on unknown card %s (fd=%d, codec=%d)", card, fd, codec);
- pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Unknown card"));
- return r;
-- } else
-- pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec);
-+ }
-+
-+ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec);
-+
-+ /* Do the socket defered setup */
-+ if (recv(fd, NULL, 0, 0) < 0) {
-+ const char *strerr = strerror(errno);
-+ pa_log_warn("Defered setup failed: %d (%s)", errno, strerr);
-+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", strerr));
-+ }
-
- hfac->fd = fd;
- hfac->codec = codec;
+++ /dev/null
-From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= <jprvita@openbossa.org>
-Date: Wed, 27 Mar 2013 01:43:42 -0300
-Subject: bluetooth: Suspend sink/source on HFP's stream HUP
-
-When the Audio Connection is disconnected the sink and source should be
-suspended.
-
-Change-Id: Ifedd5e7fe70ee74e509b82270ce84aba762f2412
----
- src/modules/bluetooth/module-bluez5-device.c | 17 ++++++++++++++---
- 1 file changed, 14 insertions(+), 3 deletions(-)
-
-diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
-index b511b92..ae5ec1d 100644
---- a/src/modules/bluetooth/module-bluez5-device.c
-+++ b/src/modules/bluetooth/module-bluez5-device.c
-@@ -74,6 +74,7 @@ static const char* const valid_modargs[] = {
-
- enum {
- BLUETOOTH_MESSAGE_IO_THREAD_FAILED,
-+ BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED,
- BLUETOOTH_MESSAGE_MAX
- };
-
-@@ -1427,6 +1428,12 @@ io_fail:
- pending_read_bytes = 0;
- writable = false;
-
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
-+ u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
-+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0,
-+ NULL, NULL);
-+ }
-+
- teardown_stream(u);
- }
-
-@@ -1994,15 +2001,19 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
-
- /* Run from main thread context */
- static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-- struct bluetooth_msg *u = BLUETOOTH_MSG(obj);
-+ struct bluetooth_msg *b = BLUETOOTH_MSG(obj);
-+ struct userdata *u = data;
-
- switch (code) {
-+ case BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED:
-+ handle_transport_state_change(u, u->transport);
-+ break;
- case BLUETOOTH_MESSAGE_IO_THREAD_FAILED:
-- if (u->card->module->unload_requested)
-+ if (b->card->module->unload_requested)
- break;
-
- pa_log_debug("Switching the profile to off due to IO thread failure.");
-- pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
-+ pa_assert_se(pa_card_set_profile(b->card, "off", false) >= 0);
- break;
- }
-
+++ /dev/null
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 23 Aug 2013 19:19:34 +0300
-Subject: bluetooth: Implement transport release for hf_audio_agent transports
-
-Change-Id: I496b3ab1c2f8e347c41262818ec3b9a35ed7262e
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 90d1348..a0474df 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -153,6 +153,13 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
- }
-
- static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
-+ hf_audio_agent_data *hfdata = t->userdata;
-+ hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path);
-+
-+ if (hfac) {
-+ shutdown(hfac->fd, SHUT_RDWR);
-+ hfac->fd = -1;
-+ }
- }
-
- static void hf_audio_agent_card_found(hf_audio_agent_data *hfdata, const char *path, DBusMessageIter *props_i) {
+++ /dev/null
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Fri, 23 Aug 2013 19:44:23 +0300
-Subject: bluetooth: Fixes HFP audio transfer when initiator
-
-This makes sure org.ofono.HandsfreeAudioCard.Connect is called regardless
-of the optional flag and also makes sure to update the profile state
-whenever SCO is disconnected.
-
-Change-Id: I4400753333f14a2381eb75d5b62d2ea51d1c7139
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 2 +-
- src/modules/bluetooth/module-bluez5-device.c | 14 +++++++-------
- 2 files changed, 8 insertions(+), 8 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index a0474df..59eafdb 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -126,7 +126,7 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
- hf_audio_agent_data *hfdata = t->userdata;
- hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path);
-
-- if (!optional) {
-+ if (hfac->fd < 0) {
- DBusMessage *m;
-
- pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect"));
-diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
-index ae5ec1d..39fa5d0 100644
---- a/src/modules/bluetooth/module-bluez5-device.c
-+++ b/src/modules/bluetooth/module-bluez5-device.c
-@@ -768,6 +768,12 @@ static void transport_release(struct userdata *u) {
-
- u->transport_acquired = false;
-
-+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
-+ u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
-+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0,
-+ NULL, NULL);
-+ }
-+
- teardown_stream(u);
- }
-
-@@ -1428,13 +1434,7 @@ io_fail:
- pending_read_bytes = 0;
- writable = false;
-
-- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
-- u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
-- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0,
-- NULL, NULL);
-- }
--
-- teardown_stream(u);
-+ transport_release(u);
- }
-
- fail:
+++ /dev/null
-From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-Date: Mon, 16 Sep 2013 15:10:51 +0300
-Subject: bluetooth: Set 'off' profile as default for newly create cards
-
-This makes sure that pa_card_new doesn't select one profile based on
-priority which may conflict with audio policy.
-
-Change-Id: Ifa5ad111d0c9f58701f93bcfd85b70d1f096efac
----
- src/modules/bluetooth/module-bluez4-device.c | 1 +
- src/modules/bluetooth/module-bluez5-device.c | 1 +
- 2 files changed, 2 insertions(+)
-
-diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
-index 83e603f..b0b12f8 100644
---- a/src/modules/bluetooth/module-bluez4-device.c
-+++ b/src/modules/bluetooth/module-bluez4-device.c
-@@ -2295,6 +2295,7 @@ static int add_card(struct userdata *u) {
- d = PA_CARD_PROFILE_DATA(p);
- *d = PA_BLUEZ4_PROFILE_OFF;
- pa_hashmap_put(data.profiles, p->name, p);
-+ pa_card_new_data_set_profile(&data, "off");
-
- if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
- if (pa_hashmap_get(data.profiles, default_profile))
-diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
-index 39fa5d0..a0776c8 100644
---- a/src/modules/bluetooth/module-bluez5-device.c
-+++ b/src/modules/bluetooth/module-bluez5-device.c
-@@ -1888,6 +1888,7 @@ static int add_card(struct userdata *u) {
- p = PA_CARD_PROFILE_DATA(cp);
- *p = PA_BLUETOOTH_PROFILE_OFF;
- pa_hashmap_put(data.profiles, cp->name, cp);
-+ pa_card_new_data_set_profile(&data, "off");
-
- u->card = pa_card_new(u->core, &data);
- pa_card_new_data_done(&data);
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 2 Jan 2014 15:37:02 +0200
-Subject: fix ofono and pulseaudio starting order assert
-
-Change-Id: I743c6e1fb5c65cc0702f073d6c2beb4e6868d7bb
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 59eafdb..7f93c6b 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -403,10 +403,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da
- if (old_owner && *old_owner) {
- pa_log_debug("oFono disappeared");
-
-- if (hfdata->hf_audio_cards) {
-- pa_hashmap_free(hfdata->hf_audio_cards);
-- hfdata->hf_audio_cards = NULL;
-- }
-+ if (hfdata->hf_audio_cards)
-+ pa_hashmap_remove_all(hfdata->hf_audio_cards);
-
- if(hfdata->ofono_bus_id) {
- pa_xfree(hfdata->ofono_bus_id);
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Mon, 30 Dec 2013 18:03:18 +0200
-Subject: hfp do safe strcmp in dbus handler
-
-Change-Id: I4ba64d22b2b807530263b5f274cd89f208c675ac
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/bluetooth/hfaudioagent-ofono.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c
-index 7f93c6b..2ac8a82 100644
---- a/src/modules/bluetooth/hfaudioagent-ofono.c
-+++ b/src/modules/bluetooth/hfaudioagent-ofono.c
-@@ -381,7 +381,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da
- pa_assert(hfdata);
-
- sender = dbus_message_get_sender(m);
-- if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
-+ if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_safe_streq("org.freedesktop.DBus", sender))
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- dbus_error_init(&err);
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 5 Sep 2013 12:21:19 +0300
-Subject: add parameter to define key used in stream restore.
-
-It is possible now to use preferred_stream_group
-command line parameter when loading module stream
-restore. This key will be searched from the stream's
-proplist and if found it is used as key when
-restoring the volumes and other values. There's also
-special value media.role.within.appication.name you
-can use to enable restoring stream roles within
-application. So different streams with different
-roles within application will save their volumes.
-If the preferred stream group parameter is left out
-module stream restore will fallback to old default
-functionality.
-
-Change-Id: I636f47b43476f3d4cd6c14244eafcd050683bb69
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/module-stream-restore.c | 34 +++++++++++++---------
- src/pulsecore/proplist-util.c | 58 +++++++++++++++++++++++++++++++++++++
- src/pulsecore/proplist-util.h | 1 +
- 3 files changed, 80 insertions(+), 13 deletions(-)
-
-diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
-index 9689e79..68968c9 100644
---- a/src/modules/module-stream-restore.c
-+++ b/src/modules/module-stream-restore.c
-@@ -70,7 +70,8 @@ PA_MODULE_USAGE(
- "restore_muted=<Save/restore muted states?> "
- "on_hotplug=<When new device becomes available, recheck streams?> "
- "on_rescue=<When device becomes unavailable, recheck streams?> "
-- "fallback_table=<filename>");
-+ "fallback_table=<filename>"
-+ "preferred_stream_group=<prefer certain stream group in restore?> ");
-
- #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
- #define IDENTIFICATION_PROPERTY "module-stream-restore.id"
-@@ -87,6 +88,7 @@ static const char* const valid_modargs[] = {
- "on_hotplug",
- "on_rescue",
- "fallback_table",
-+ "preferred_stream_group",
- NULL
- };
-
-@@ -112,6 +114,7 @@ struct userdata {
- bool restore_muted:1;
- bool on_hotplug:1;
- bool on_rescue:1;
-+ char *preferred_stream_group;
-
- pa_native_protocol *protocol;
- pa_idxset *subscribed;
-@@ -1277,7 +1280,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
- if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx)))
- return;
-
-- if (!(name = pa_proplist_get_stream_group(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return;
-
- if ((old = entry_read(u, name))) {
-@@ -1327,7 +1330,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
- if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx)))
- return;
-
-- if (!(name = pa_proplist_get_stream_group(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return;
-
- if ((old = entry_read(u, name))) {
-@@ -1420,7 +1423,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
- pa_assert(u);
- pa_assert(u->restore_device);
-
-- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return PA_HOOK_OK;
-
- if (new_data->sink)
-@@ -1462,7 +1465,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
- pa_assert(u);
- pa_assert(u->restore_volume || u->restore_muted);
-
-- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return PA_HOOK_OK;
-
- if ((e = entry_read(u, name))) {
-@@ -1516,7 +1519,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
- if (new_data->direct_on_input)
- return PA_HOOK_OK;
-
-- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return PA_HOOK_OK;
-
- if (new_data->source)
-@@ -1559,7 +1562,7 @@ static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source
- pa_assert(u);
- pa_assert(u->restore_volume || u->restore_muted);
-
-- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- return PA_HOOK_OK;
-
- if ((e = entry_read(u, name))) {
-@@ -1631,7 +1634,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
- if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
- continue;
-
-- if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if ((e = entry_read(u, name))) {
-@@ -1679,7 +1682,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
- if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
- continue;
-
-- if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if ((e = entry_read(u, name))) {
-@@ -1715,7 +1718,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
- if (!si->sink)
- continue;
-
-- if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if ((e = entry_read(u, name))) {
-@@ -1761,7 +1764,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
- if (!so->source)
- continue;
-
-- if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(name = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if ((e = entry_read(u, name))) {
-@@ -1880,7 +1883,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
- char *n;
- pa_sink *s;
-
-- if (!(n = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
-+ if (!(n = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if (!pa_streq(name, n)) {
-@@ -1928,7 +1931,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
- char *n;
- pa_source *s;
-
-- if (!(n = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY)))
-+ if (!(n = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group)))
- continue;
-
- if (!pa_streq(name, n)) {
-@@ -2411,6 +2414,8 @@ int pa__init(pa_module*m) {
-
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
-
-+ u->preferred_stream_group = pa_xstrdup(pa_modargs_get_value(ma, "preferred_stream_group", NULL));
-+
- if (restore_device) {
- /* A little bit earlier than module-intended-roles ... */
- u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
-@@ -2554,5 +2559,8 @@ void pa__done(pa_module*m) {
- if (u->subscribed)
- pa_idxset_free(u->subscribed, NULL);
-
-+ if (u->preferred_stream_group)
-+ pa_xfree(u->preferred_stream_group);
-+
- pa_xfree(u);
- }
-diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
-index 473290f..0a8dbca 100644
---- a/src/pulsecore/proplist-util.c
-+++ b/src/pulsecore/proplist-util.c
-@@ -274,3 +274,61 @@ char *pa_proplist_get_stream_group(pa_proplist *p, const char *prefix, const cha
-
- return t;
- }
-+
-+char *pa_proplist_get_stream_group_extended(pa_proplist *p, const char *prefix, const char *cache, const char *preferred_stream_group) {
-+ const char *r = NULL;
-+ const char *q = NULL;
-+ char *t;
-+
-+ if (!p)
-+ return NULL;
-+
-+ if (cache && (r = pa_proplist_gets(p, cache)))
-+ return pa_xstrdup(r);
-+
-+ if (!prefix)
-+ prefix = "stream";
-+
-+ /* try first to get the preferred stream group, then fallback to hard coded order */
-+ if (preferred_stream_group) {
-+ if (!strcmp(preferred_stream_group, "media.role.within.application.name")) {
-+ if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)) &&
-+ (q = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE))) {
-+ t = pa_sprintf_malloc("%s-by-media-role-within-application-name:%s-%s", prefix, q, r);
-+ } else {
-+ /* make r NULL to be able to fallback to "standard" stream restore code */
-+ r = NULL;
-+ }
-+ }
-+ else if ((r = pa_proplist_gets(p, preferred_stream_group))) {
-+ if (!strcmp(preferred_stream_group, PA_PROP_MEDIA_ROLE))
-+ t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
-+ else if (!strcmp(preferred_stream_group, PA_PROP_APPLICATION_ID))
-+ t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
-+ else if (!strcmp(preferred_stream_group, PA_PROP_APPLICATION_NAME))
-+ t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
-+ else if (!strcmp(preferred_stream_group, PA_PROP_MEDIA_NAME))
-+ t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
-+ else
-+ t = pa_sprintf_malloc("%s-fallback:%s", prefix, r);
-+ }
-+ }
-+
-+ if (!r) {
-+ if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE)))
-+ t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
-+ else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID)))
-+ t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
-+ else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)))
-+ t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
-+ else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
-+ t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
-+ else
-+ t = pa_sprintf_malloc("%s-fallback:%s", prefix, r);
-+ }
-+
-+ if (cache)
-+ pa_proplist_sets(p, cache, t);
-+
-+ return t;
-+}
-diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h
-index 3d08776..47bdb57 100644
---- a/src/pulsecore/proplist-util.h
-+++ b/src/pulsecore/proplist-util.h
-@@ -26,5 +26,6 @@
-
- void pa_init_proplist(pa_proplist *p);
- char *pa_proplist_get_stream_group(pa_proplist *pl, const char *prefix, const char *cache);
-+char *pa_proplist_get_stream_group_extended(pa_proplist *p, const char *prefix, const char *cache, const char *preferred_stream_group);
-
- #endif
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Mon, 28 Apr 2014 11:26:15 +0300
-Subject: increase alsa rewind safeguard
-
-In some devices alsa drivers behaves badly
-if pulseaudio rewind safeguard is too small.
-This is not fixing the driver issues, but is
-a workaround to give time (1.33ms->5ms) for
-user space processing so that alsa is not
-getting into this weird state. This could be
-also helped by running pulseaudio in realtime.
-This is of course increasing the volume setting
-latency, but should leave it still quite
-responsive.
-
-Change-Id: Iecdf879bf8ba58e991808d2dc382def05de36ec9
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/alsa/alsa-sink.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
-index ccf1137..5a41cf6 100644
---- a/src/modules/alsa/alsa-sink.c
-+++ b/src/modules/alsa/alsa-sink.c
-@@ -87,8 +87,13 @@
-
- #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
-
-+#if 0
- #define DEFAULT_REWIND_SAFEGUARD_BYTES (256U) /* 1.33ms @48kHz, we'll never rewind less than this */
- #define DEFAULT_REWIND_SAFEGUARD_USEC (1330) /* 1.33ms, depending on channels/rate/sample we may rewind more than 256 above */
-+#endif
-+
-+#define DEFAULT_REWIND_SAFEGUARD_BYTES (1024U) /* increase safeguard 4x */
-+#define DEFAULT_REWIND_SAFEGUARD_USEC (5000) /* increase safeguard ~4x */
-
- struct userdata {
- pa_core *core;
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Tue, 11 Mar 2014 12:23:09 +0200
-Subject: fix for profile change prototype in bluez5 patch
-
-Change-Id: I2361c4eca82e6ac4a8f94e9f9c97f09cb6648049
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/bluetooth/module-bluez5-device.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
-index a0776c8..790dcf1 100644
---- a/src/modules/bluetooth/module-bluez5-device.c
-+++ b/src/modules/bluetooth/module-bluez5-device.c
-@@ -2014,7 +2014,7 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o
- break;
-
- pa_log_debug("Switching the profile to off due to IO thread failure.");
-- pa_assert_se(pa_card_set_profile(b->card, "off", false) >= 0);
-+ pa_assert_se(pa_card_set_profile(b->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
- break;
- }
-
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:10:15 +0800
-Subject: changes to pa simple api - samsung
-
-Change-Id: I997c02217a8dc14524480164aa0baeea901c7b4e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 4 +-
- src/map-file | 6 ++
- src/pulse/simple.c | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/pulse/simple.h | 28 ++++++
- 4 files changed, 324 insertions(+), 2 deletions(-)
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 2edce5f..5ec8609 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -824,7 +824,7 @@ libpulse_la_SOURCES = \
- pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
-
--libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS)
-+libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS)
- libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la
- libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
-
-@@ -834,7 +834,7 @@ libpulse_la_LIBADD += $(DBUS_LIBS)
- endif
-
- libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
--libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
-+libpulse_simple_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
- libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
- libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
-
-diff --git a/src/map-file b/src/map-file
-index 7dbcd00..95364ae 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -282,11 +282,17 @@ pa_signal_new;
- pa_signal_set_destroy;
- pa_simple_drain;
- pa_simple_flush;
-+pa_simple_mute;
- pa_simple_free;
- pa_simple_get_latency;
- pa_simple_new;
-+pa_simple_new_proplist;
- pa_simple_read;
- pa_simple_write;
-+pa_simple_set_volume;
-+pa_simple_get_stream_index;
-+pa_simple_cork;
-+pa_simple_is_corked;
- pa_stream_begin_write;
- pa_stream_cancel_write;
- pa_stream_connect_playback;
-diff --git a/src/pulse/simple.c b/src/pulse/simple.c
-index 1891131..9109ffa 100644
---- a/src/pulse/simple.c
-+++ b/src/pulse/simple.c
-@@ -32,10 +32,12 @@
- #include <pulse/thread-mainloop.h>
- #include <pulse/xmalloc.h>
-
-+#include <pulsecore/native-common.h>
- #include <pulsecore/log.h>
- #include <pulsecore/macro.h>
-
- #include "simple.h"
-+#include "internal.h"
-
- struct pa_simple {
- pa_threaded_mainloop *mainloop;
-@@ -102,6 +104,24 @@ static void context_state_cb(pa_context *c, void *userdata) {
- }
- }
-
-+static void stream_success_context_cb(pa_stream *s, int success, void *userdata) {
-+ pa_simple *p = userdata;
-+ pa_assert(s);
-+ pa_assert(p);
-+
-+ p->operation_success = success;
-+ pa_threaded_mainloop_signal(p->mainloop, 0);
-+}
-+
-+static void success_context_cb(pa_context *c, int success, void *userdata) {
-+ pa_simple *p = userdata;
-+ pa_assert(c);
-+ pa_assert(p);
-+
-+ p->operation_success = success;
-+ pa_threaded_mainloop_signal(p->mainloop, 0);
-+}
-+
- static void stream_state_cb(pa_stream *s, void * userdata) {
- pa_simple *p = userdata;
- pa_assert(s);
-@@ -251,6 +271,122 @@ fail:
- return NULL;
- }
-
-+pa_simple* pa_simple_new_proplist(
-+ const char *server,
-+ const char *name,
-+ pa_stream_direction_t dir,
-+ const char *dev,
-+ const char *stream_name,
-+ const pa_sample_spec *ss,
-+ const pa_channel_map *map,
-+ const pa_buffer_attr *attr,
-+ pa_proplist *proplist,
-+ int *rerror) {
-+
-+ pa_simple *p;
-+ int error = PA_ERR_INTERNAL, r;
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
-+
-+ p = pa_xnew0(pa_simple, 1);
-+ p->direction = dir;
-+
-+ if (!(p->mainloop = pa_threaded_mainloop_new()))
-+ goto fail;
-+
-+ if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
-+ goto fail;
-+
-+ pa_context_set_state_callback(p->context, context_state_cb, p);
-+
-+ if (pa_context_connect(p->context, server, 0, NULL) < 0) {
-+ error = pa_context_errno(p->context);
-+ goto fail;
-+ }
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ if (pa_threaded_mainloop_start(p->mainloop) < 0)
-+ goto unlock_and_fail;
-+
-+ for (;;) {
-+ pa_context_state_t state;
-+
-+ state = pa_context_get_state(p->context);
-+
-+ if (state == PA_CONTEXT_READY)
-+ break;
-+
-+ if (!PA_CONTEXT_IS_GOOD(state)) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ /* Wait until the context is ready */
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ }
-+
-+ if (!(p->stream = pa_stream_new_with_proplist(p->context, stream_name, ss, map, proplist))) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ pa_stream_set_state_callback(p->stream, stream_state_cb, p);
-+ pa_stream_set_read_callback(p->stream, stream_request_cb, p);
-+ pa_stream_set_write_callback(p->stream, stream_request_cb, p);
-+ pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
-+
-+ if (dir == PA_STREAM_PLAYBACK)
-+ r = pa_stream_connect_playback(p->stream, dev, attr,
-+ PA_STREAM_INTERPOLATE_TIMING
-+ |PA_STREAM_ADJUST_LATENCY
-+ |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
-+ else
-+ r = pa_stream_connect_record(p->stream, dev, attr,
-+ PA_STREAM_INTERPOLATE_TIMING
-+ |PA_STREAM_ADJUST_LATENCY
-+ |PA_STREAM_AUTO_TIMING_UPDATE
-+ |PA_STREAM_START_CORKED);
-+
-+ if (r < 0) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ for (;;) {
-+ pa_stream_state_t state;
-+
-+ state = pa_stream_get_state(p->stream);
-+
-+ if (state == PA_STREAM_READY)
-+ break;
-+
-+ if (!PA_STREAM_IS_GOOD(state)) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ /* Wait until the stream is ready */
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return p;
-+
-+unlock_and_fail:
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+fail:
-+ if (rerror)
-+ *rerror = error;
-+ pa_simple_free(p);
-+ return NULL;
-+}
- void pa_simple_free(pa_simple *s) {
- pa_assert(s);
-
-@@ -452,6 +588,111 @@ unlock_and_fail:
- return -1;
- }
-
-+int pa_simple_mute(pa_simple *p, int mute, int *rerror) {
-+ pa_operation *o = NULL;
-+ uint32_t idx;
-+
-+ pa_assert(p);
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail);
-+
-+
-+ o = pa_context_set_sink_input_mute (p->context, idx, mute, success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror) {
-+ pa_assert(p);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, idx != NULL, PA_ERR_INVALID, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ *idx = pa_stream_get_index(p->stream);
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return 0;
-+
-+unlock_and_fail:
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_set_volume(pa_simple *p, int volume, int *rerror) {
-+ pa_operation *o = NULL;
-+ pa_stream *s = NULL;
-+ uint32_t idx;
-+ pa_cvolume cv;
-+
-+
-+ pa_assert(p);
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, volume >= 0, PA_ERR_INVALID, -1);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, volume <= 65535, PA_ERR_INVALID, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail);
-+
-+ s = p->stream;
-+ pa_assert(s);
-+ pa_cvolume_set(&cv, s->sample_spec.channels, volume);
-+
-+ o = pa_context_set_sink_input_volume (p->context, idx, &cv, success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
- pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
- pa_usec_t t;
- int negative;
-@@ -481,3 +722,50 @@ unlock_and_fail:
- pa_threaded_mainloop_unlock(p->mainloop);
- return (pa_usec_t) -1;
- }
-+
-+int pa_simple_cork(pa_simple *p, int cork, int *rerror) {
-+ pa_operation *o = NULL;
-+
-+ pa_assert(p);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ o = pa_stream_cork(p->stream, cork, stream_success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_is_corked(pa_simple *p) {
-+ int is_cork;
-+ pa_assert(p);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ is_cork = pa_stream_is_corked(p->stream);
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return is_cork;
-+}
-diff --git a/src/pulse/simple.h b/src/pulse/simple.h
-index 54003ff..4f4a988 100644
---- a/src/pulse/simple.h
-+++ b/src/pulse/simple.h
-@@ -31,6 +31,7 @@
- #include <pulse/cdecl.h>
- #include <pulse/version.h>
-
-+#include <pulse/proplist.h>
- /** \page simple Simple API
- *
- * \section overv_sec Overview
-@@ -129,6 +130,19 @@ pa_simple* pa_simple_new(
- int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
- );
-
-+/** Create a new connection to the server with proplist */
-+pa_simple* pa_simple_new_proplist(
-+ const char *server, /**< Server name, or NULL for default */
-+ const char *name, /**< A descriptive name for this client (application name, ...) */
-+ pa_stream_direction_t dir, /**< Open this stream for recording or playback? */
-+ const char *dev, /**< Sink (resp. source) name, or NULL for default */
-+ const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */
-+ const pa_sample_spec *ss, /**< The sample type to use */
-+ const pa_channel_map *map, /**< The channel map to use, or NULL for default */
-+ const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */
-+ pa_proplist *proplist, /**< Properties, or NULL for default */
-+ int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
-+ );
- /** Close and free the connection to the server. The connection object becomes invalid when this is called. */
- void pa_simple_free(pa_simple *s);
-
-@@ -155,6 +169,20 @@ pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
-
- /** Flush the playback or record buffer. This discards any audio in the buffer. */
- int pa_simple_flush(pa_simple *s, int *error);
-+/** Mute the playback stream */
-+int pa_simple_mute(pa_simple *p, int mute, int *rerror);
-+
-+/** Volume control the playback stream */
-+int pa_simple_set_volume(pa_simple *p, int volume, int *rerror);
-+
-+/** Get stream index */
-+int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror);
-+
-+/** Cork on=1/off=0 stream */
-+int pa_simple_cork(pa_simple *p, int cork, int *rerror);
-+
-+/** Check whether stream is corked or not */
-+int pa_simple_is_corked(pa_simple *p);
-
- PA_C_DECL_END
-
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:11:16 +0800
-Subject: add support for samsung power management - samsung
-
-Change-Id: Id76a3971e36c08773848fdf1ac1cc9a9200d7330
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 12 +++
- src/Makefile.am | 3 +
- src/modules/module-suspend-on-idle.c | 179 ++++++++++++++++++++++++++++++++++-
- 3 files changed, 192 insertions(+), 2 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index a35918c..9a79b36 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -673,6 +673,18 @@ AS_IF([test "x$enable_samplerate" = "xyes" && test "x$HAVE_LIBSAMPLERATE" = "x0"
- AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
- AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], AC_DEFINE([HAVE_LIBSAMPLERATE], 1, [Have libsamplerate?]))
-
-+#### samsung PM API support ####
-+
-+AC_ARG_ENABLE(pmlock, AC_HELP_STRING([--enable-pmlock], [using Samsung power management api]),
-+[
-+ case "${enableval}" in
-+ yes) USE_PM_LOCK=yes ;;
-+ no) USE_PM_LOCK=no ;;
-+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-pmlock) ;;
-+ esac
-+ ],[USE_PM_LOCK=no])
-+AM_CONDITIONAL(USE_PM_LOCK, test "x$USE_PM_LOCK" = "xyes")
-+
- #### Database support ####
-
- AC_ARG_WITH([database],
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 5ec8609..12bb73e 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1940,6 +1940,9 @@ module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c
- module_suspend_on_idle_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_suspend_on_idle_la_LIBADD = $(MODULE_LIBADD)
- module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
-+if USE_PM_LOCK
-+module_suspend_on_idle_la_CFLAGS += -DUSE_PM_LOCK
-+endif
-
- # echo-cancel module
- module_echo_cancel_la_SOURCES = \
-diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
-index 15cbf95..8350917 100644
---- a/src/modules/module-suspend-on-idle.c
-+++ b/src/modules/module-suspend-on-idle.c
-@@ -34,7 +34,53 @@
- #include <pulsecore/modargs.h>
- #include <pulsecore/log.h>
-
-+#include <pulsecore/namereg.h>
- #include "module-suspend-on-idle-symdef.h"
-+//move to configure.ac
-+//#define USE_PM_LOCK /* Enable as default */
-+#ifdef USE_PM_LOCK
-+
-+#include <sys/types.h>
-+#include <sys/socket.h>
-+#include <sys/un.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <errno.h>
-+#include <linux/limits.h>
-+
-+#define SOCK_PATH "/tmp/pm_sock"
-+#define SHIFT_UNLOCK 4
-+#define SHIFT_UNLOCK_PARAMETER 12
-+#define SHIFT_CHANGE_STATE 8
-+#define SHIFT_HOLD_KEY_BLOCK 16
-+#define SHIFT_CHANGE_TIMEOUT 20
-+#define TIMEOUT_RESET_BIT 0x80
-+
-+#define LCD_NORMAL 0x1 /**< NORMAL state */
-+#define LCD_DIM 0x2 /**< LCD dimming state */
-+#define LCD_OFF 0x4 /**< LCD off state */
-+#define SUSPEND 0x8 /**< Sleep state */
-+#define POWER_OFF 0x16 /**< Sleep state */
-+#define SETALL (LCD_DIM | LCD_OFF | LCD_NORMAL) /*< select all state - not supported yet */
-+
-+/* parameters for pm_lock_state() */
-+#define STAY_CUR_STATE 0x0
-+#define GOTO_STATE_NOW 0x1
-+#define HOLD_KEY_BLOCK 0x2
-+
-+/* paramters for pm_unlcok_state() - details are described at 162 line */
-+#define PM_SLEEP_MARGIN 0x0 /**< keep guard time for unlock */
-+#define PM_RESET_TIMER 0x1 /**< reset timer for unlock */
-+#define PM_KEEP_TIMER 0x2 /**< keep timer for unlock */
-+
-+struct pwr_msg {
-+ pid_t pid;
-+ unsigned int cond;
-+ unsigned int timeout;
-+ unsigned int timeout2;
-+};
-+
-+#endif /* USE_PM_LOCK */
-
- PA_MODULE_AUTHOR("Lennart Poettering");
- PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
-@@ -47,6 +93,13 @@ static const char* const valid_modargs[] = {
- NULL,
- };
-
-+#ifdef USE_PM_LOCK
-+#define PM_TYPE_SINK 0x01
-+#define PM_TYPE_SOURCE 0x02
-+
-+#define UPDATE_PM_LOCK(current,type) (current |= type)
-+#define UPDATE_PM_UNLOCK(current,type) (current &= ~type)
-+#endif /* USE_PM_LOCK */
- struct userdata {
- pa_core *core;
- pa_usec_t timeout;
-@@ -70,6 +123,9 @@ struct userdata {
- *source_output_move_finish_slot,
- *sink_input_state_changed_slot,
- *source_output_state_changed_slot;
-+#ifdef USE_PM_LOCK
-+ uint32_t pm_state;
-+#endif /* USE_PM_LOCK */
- };
-
- struct device_info {
-@@ -80,10 +136,83 @@ struct device_info {
- pa_time_event *time_event;
- pa_usec_t timeout;
- };
-+#ifdef USE_PM_LOCK
-+
-+static int send_msg(unsigned int s_bits, unsigned int timeout, unsigned int timeout2)
-+{
-+ int rc = 0;
-+ int sock;
-+ struct pwr_msg p;
-+ struct sockaddr_un remote;
-+
-+ p.pid = getpid();
-+ p.cond = s_bits;
-+ p.timeout = timeout;
-+ p.timeout2 = timeout2;
-+
-+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
-+ if (sock == -1) {
-+ return -1;
-+ }
-+
-+ remote.sun_family = AF_UNIX;
-+ if(strlen(SOCK_PATH) >= sizeof(remote.sun_path)) {
-+ return -1;
-+ }
-+ strncpy(remote.sun_path, SOCK_PATH, sizeof(remote.sun_path));
-+
-+ rc = sendto(sock, (void *)&p, sizeof(p), 0, (struct sockaddr *)&remote,
-+ sizeof(struct sockaddr_un));
-+
-+ close(sock);
-+ return rc;
-+}
-+
-+static int pm_lock_state(unsigned int s_bits, unsigned int flag,
-+ unsigned int timeout)
-+{
-+ switch (s_bits) {
-+ case LCD_NORMAL:
-+ case LCD_DIM:
-+ case LCD_OFF:
-+ break;
-+ default:
-+ return -1;
-+ }
-+ if (flag & GOTO_STATE_NOW)
-+ /* if the flag is true, go to the locking state directly */
-+ s_bits = s_bits | (s_bits << SHIFT_CHANGE_STATE);
-+ if (flag & HOLD_KEY_BLOCK)
-+ s_bits = s_bits | (1 << SHIFT_HOLD_KEY_BLOCK);
-+
-+ return send_msg(s_bits, timeout, 0);
-+}
-+
-+static int pm_unlock_state(unsigned int s_bits, unsigned int flag)
-+{
-+ switch (s_bits) {
-+ case LCD_NORMAL:
-+ case LCD_DIM:
-+ case LCD_OFF:
-+ break;
-+ default:
-+ return -1;
-+ }
-+
-+ s_bits = (s_bits << SHIFT_UNLOCK);
-+ s_bits = (s_bits | (flag << SHIFT_UNLOCK_PARAMETER));
-+ return send_msg(s_bits, 0, 0);
-+}
-+
-+#endif
-
- static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
- struct device_info *d = userdata;
-
-+#ifdef USE_PM_LOCK
-+ int ret = -1;
-+#endif
-+
- pa_assert(d);
-
- d->userdata->core->mainloop->time_restart(d->time_event, NULL);
-@@ -92,12 +221,33 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval
- pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
- pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE);
- pa_core_maybe_vacuum(d->userdata->core);
-+#ifdef USE_PM_LOCK
-+ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SINK);
-+ if(!(d->userdata->pm_state)) {
-+ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN);
-+ if(ret != -1)
-+ pa_log_info("sink pm_unlock_state success [%d]", ret);
-+ else
-+ pa_log_error("sink pm_unlock_state failed [%d]", ret);
-+ }
-+#endif /* USE_PM_LOCK */
- }
-
- if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
- pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
- pa_source_suspend(d->source, true, PA_SUSPEND_IDLE);
- pa_core_maybe_vacuum(d->userdata->core);
-+#ifdef USE_PM_LOCK
-+
-+ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SOURCE);
-+ if(!(d->userdata->pm_state)) {
-+ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN);
-+ if(ret != -1)
-+ pa_log_info("source pm_unlock_state success [%d]", ret);
-+ else
-+ pa_log_error("source pm_unlock_state failed [%d]", ret);
-+ }
-+#endif /* USE_PM_LOCK */
- }
- }
-
-@@ -117,17 +267,39 @@ static void restart(struct device_info *d) {
- }
-
- static void resume(struct device_info *d) {
-+#ifdef USE_PM_LOCK
-+ int ret = -1;
-+#endif
-+
- pa_assert(d);
-
- d->userdata->core->mainloop->time_restart(d->time_event, NULL);
-
- if (d->sink) {
-- pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name);
-+#ifdef USE_PM_LOCK
-+ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SINK);
-+ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0);
-+ if(ret != -1) {
-+ pa_log_info("sink pm_lock_state success [%d]", ret);
-+ } else {
-+ pa_log_error("sink pm_lock_state failed [%d]", ret);
-+ }
-+#endif /* USE_PM_LOCK */
-+ pa_log_debug("Sink %s becomes busy.", d->sink->name);
- pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE);
- }
-
- if (d->source) {
-- pa_log_debug("Source %s becomes busy, resuming.", d->source->name);
-+#ifdef USE_PM_LOCK
-+ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SOURCE);
-+ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0);
-+ if(ret != -1) {
-+ pa_log_info("source pm_lock_state success [%d]", ret);
-+ } else {
-+ pa_log_error("source pm_lock_state failed [%d]", ret);
-+ }
-+#endif /* USE_PM_LOCK */
-+ pa_log_debug("Source %s becomes busy.", d->source->name);
- pa_source_suspend(d->source, false, PA_SUSPEND_IDLE);
- }
- }
-@@ -462,6 +634,9 @@ int pa__init(pa_module*m) {
- u->core = m->core;
- u->timeout = timeout * PA_USEC_PER_SEC;
- u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free);
-+#ifdef USE_PM_LOCK
-+ u->pm_state = 0x00;
-+#endif /* USE_PM_LOCK */
-
- PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
- device_new_hook_cb(m->core, PA_OBJECT(sink), u);
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:18:58 +0800
-Subject: Enhance for echo cancel - samsung
-
-Change-Id: Ibd59e7e033d5a6789ddc7d5ef39e23f26dcf55cc
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 2 +-
- src/map-file | 2 +
- src/modules/echo-cancel/module-echo-cancel.c | 55 +++++++++++++++
- src/pulse/ext-echo-cancel.c | 100 +++++++++++++++++++++++++++
- src/pulse/ext-echo-cancel.h | 49 +++++++++++++
- 5 files changed, 207 insertions(+), 1 deletion(-)
- create mode 100644 src/pulse/ext-echo-cancel.c
- create mode 100644 src/pulse/ext-echo-cancel.h
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 12bb73e..ae841e3 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1950,7 +1950,7 @@ module_echo_cancel_la_SOURCES = \
- modules/echo-cancel/null.c \
- modules/echo-cancel/echo-cancel.h
- module_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS)
--module_echo_cancel_la_LIBADD = $(MODULE_LIBADD) $(LIBSPEEX_LIBS)
-+module_echo_cancel_la_LIBADD = libprotocol-native.la $(MODULE_LIBADD) $(LIBSPEEX_LIBS)
- module_echo_cancel_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSPEEX_CFLAGS)
- if HAVE_ADRIAN_EC
- module_echo_cancel_la_SOURCES += \
-diff --git a/src/map-file b/src/map-file
-index 95364ae..d51596c 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -182,6 +182,8 @@ pa_ext_node_manager_connect_nodes;
- pa_ext_node_manager_disconnect_nodes;
- pa_ext_node_manager_subscribe;
- pa_ext_node_manager_set_subscribe_cb;
-+pa_ext_echo_cancel_set_volume;
-+pa_ext_echo_cancel_set_device;
- pa_format_info_copy;
- pa_format_info_free;
- pa_format_info_from_string;
-diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
-index fbdb3b3..29eed13 100644
---- a/src/modules/echo-cancel/module-echo-cancel.c
-+++ b/src/modules/echo-cancel/module-echo-cancel.c
-@@ -53,6 +53,9 @@
- #include <pulsecore/sample-util.h>
- #include <pulsecore/ltdl-helper.h>
-
-+#include <pulsecore/protocol-native.h>
-+#include <pulsecore/pstream-util.h>
-+
- #include "module-echo-cancel-symdef.h"
-
- PA_MODULE_AUTHOR("Wim Taymans");
-@@ -94,6 +97,11 @@ typedef enum {
- #endif
- } pa_echo_canceller_method_t;
-
-+enum {
-+ AEC_SET_VOLUME,
-+ AEC_SET_DEVICE,
-+};
-+
- #ifdef HAVE_WEBRTC
- #define DEFAULT_ECHO_CANCELLER "webrtc"
- #else
-@@ -255,6 +263,8 @@ struct userdata {
- struct {
- pa_cvolume current_volume;
- } thread_info;
-+
-+ pa_native_protocol *protocol;
- };
-
- static void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot);
-@@ -1602,6 +1612,43 @@ static pa_echo_canceller_method_t get_ec_method_from_string(const char *method)
- return PA_ECHO_CANCELLER_INVALID;
- }
-
-+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
-+ uint32_t command;
-+ uint32_t value;
-+ pa_tagstruct *reply = NULL;
-+ pa_assert(p);
-+ pa_assert(m);
-+ pa_assert(c);
-+ pa_assert(t);
-+
-+ if (pa_tagstruct_getu32(t, &command) < 0)
-+ goto fail;
-+
-+ reply = pa_tagstruct_new(NULL, 0);
-+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-+ pa_tagstruct_putu32(reply, tag);
-+
-+ switch (command) {
-+ case AEC_SET_VOLUME: {
-+ pa_tagstruct_getu32(t,&value);
-+ pa_log_debug("AEC_SET_VOLUME in echo cancel = %d",value);
-+ break;
-+ }
-+ case AEC_SET_DEVICE: {
-+ pa_tagstruct_getu32(t,&value);
-+ pa_log_debug("AEC_SET_DEVICE in echo cancel = %d",value);
-+ break;
-+ }
-+ default:
-+ goto fail;
-+ }
-+ pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
-+ return 0;
-+
-+fail:
-+ return -1;
-+}
-+
- /* Common initialisation bits between module-echo-cancel and the standalone
- * test program.
- *
-@@ -1992,6 +2039,9 @@ int pa__init(pa_module*m) {
-
- u->thread_info.current_volume = u->source->reference_volume;
-
-+ u->protocol = pa_native_protocol_get(m->core);
-+ pa_native_protocol_install_ext(u->protocol, m, extension_cb);
-+
- pa_sink_put(u->sink);
- pa_source_put(u->source);
-
-@@ -2069,6 +2119,11 @@ void pa__done(pa_module*m) {
- pa_xfree(u->ec);
- }
-
-+ if (u->protocol) {
-+ pa_native_protocol_remove_ext(u->protocol, m);
-+ pa_native_protocol_unref(u->protocol);
-+ }
-+
- if (u->asyncmsgq)
- pa_asyncmsgq_unref(u->asyncmsgq);
-
-diff --git a/src/pulse/ext-echo-cancel.c b/src/pulse/ext-echo-cancel.c
-new file mode 100644
-index 0000000..9339939
---- /dev/null
-+++ b/src/pulse/ext-echo-cancel.c
-@@ -0,0 +1,100 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ PulseAudio is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as published
-+ by the Free Software Foundation; either version 2.1 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 <pulse/context.h>
-+#include <pulse/gccmacro.h>
-+#include <pulse/xmalloc.h>
-+
-+#include <pulsecore/macro.h>
-+#include <pulsecore/pstream-util.h>
-+#include <pulsecore/log.h>
-+#include "internal.h"
-+#include "operation.h"
-+#include "fork-detect.h"
-+
-+#include "ext-echo-cancel.h"
-+
-+enum {
-+ AEC_SET_VOLUME,
-+ AEC_SET_DEVICE,
-+};
-+
-+pa_operation *pa_ext_echo_cancel_set_device (
-+ pa_context *c,
-+ int device,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-echo-cancel");
-+ pa_tagstruct_putu32(t, AEC_SET_DEVICE);
-+ pa_tagstruct_putu32(t, device);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+ return o;
-+}
-+
-+
-+pa_operation *pa_ext_echo_cancel_set_volume (
-+ pa_context *c,
-+ int volume,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-echo-cancel");
-+ pa_tagstruct_putu32(t, AEC_SET_VOLUME);
-+ pa_tagstruct_putu32(t, volume);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-diff --git a/src/pulse/ext-echo-cancel.h b/src/pulse/ext-echo-cancel.h
-new file mode 100644
-index 0000000..12e4eeb
---- /dev/null
-+++ b/src/pulse/ext-echo-cancel.h
-@@ -0,0 +1,49 @@
-+#ifndef foopulseechocancelfoo
-+#define foopulseechocancelfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ PulseAudio is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as published
-+ by the Free Software Foundation; either version 2.1 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/context.h>
-+#include <pulse/version.h>
-+
-+/** \file
-+ *
-+ * Routines for controlling module-echo-cancel
-+ */
-+
-+PA_C_DECL_BEGIN
-+
-+/** Set volume to AEC module */
-+pa_operation *pa_ext_echo_cancel_set_volume (
-+ pa_context *c,
-+ int volume,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+pa_operation *pa_ext_echo_cancel_set_device (
-+ pa_context *c,
-+ int device,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+
-+PA_C_DECL_END
-+
-+#endif
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:20:04 +0800
-Subject: add support for dlog - samsung
-
-Change-Id: Ieddf2f3bdab50926372e9e2b5cedb2756b6cfd5c
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 18 +++++++++
- src/Makefile.am | 9 +++++
- src/daemon/cmdline.c | 8 +++-
- src/daemon/daemon-conf.c | 10 ++++-
- src/pulsecore/cli-command.c | 8 ++++
- src/pulsecore/log.c | 95 +++++++++++++++++++++++++++++++++++++++++++++
- src/pulsecore/log.h | 4 ++
- 7 files changed, 150 insertions(+), 2 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 9a79b36..c0beac0 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [],
-
- PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
-
-+dnl use dlog --------------------------------------------------------------------------
-+AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]),
-+[
-+ case "${enableval}" in
-+ yes) USE_DLOG=yes ;;
-+ no) USE_DLOG=no ;;
-+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-dlog) ;;
-+ esac
-+ ],[USE_DLOG=no])
-+
-+if test "x$USE_DLOG" = "xyes"; then
-+ PKG_CHECK_MODULES(DLOG, dlog)
-+ AC_SUBST(DLOG_CFLAGS)
-+ AC_SUBST(DLOG_LIBS)
-+fi
-+AM_CONDITIONAL(USE_DLOG, test "x$USE_DLOG" = "xyes")
-+dnl end --------------------------------------------------------------------
-+
- #### atomic-ops ####
-
- AC_MSG_CHECKING([whether we need libatomic_ops])
-diff --git a/src/Makefile.am b/src/Makefile.am
-index ae841e3..3e41300 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -172,6 +172,10 @@ else
- pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
- endif
-
-+if USE_DLOG
-+pulseaudio_CFLAGS += -DUSE_DLOG
-+endif
-+
- ###################################
- # Utility programs #
- ###################################
-@@ -740,6 +744,11 @@ libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(DBUS_CFLAGS)
- libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(DBUS_LIBS)
- endif
-
-+if USE_DLOG
-+libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(DLOG_CFLAGS) -DUSE_DLOG
-+libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(DLOG_LIBS)
-+endif
-+
- ###################################
- # Client library #
- ###################################
-diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
-index 68579c5..5bb1a0a 100644
---- a/src/daemon/cmdline.c
-+++ b/src/daemon/cmdline.c
-@@ -140,8 +140,12 @@ void pa_cmdline_help(const char *argv0) {
- " --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
- " this time passed\n"
- " --log-level[=LEVEL] Increase or set verbosity level\n"
-- " -v --verbose Increase the verbosity level\n"
-+ " -v Increase the verbosity level\n"
-+#ifdef USE_DLOG
-+ " --log-target={auto,syslog,stderr,file:PATH,newfile:PATH,dlog,dlog-color}\n"
-+#else
- " --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
-+#endif
- " Specify the log target\n"
- " --log-meta[=BOOL] Include code location in log messages\n"
- " --log-time[=BOOL] Include timestamps in log messages\n"
-@@ -325,6 +329,8 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
- if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
- #ifdef HAVE_JOURNAL
- pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
-+#elif defined(USE_DLOG)
-+ pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>' or 'dlog' or 'dlog-color'."));
- #else
- pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
- #endif
-diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
-index ce777a6..1dde213 100644
---- a/src/daemon/daemon-conf.c
-+++ b/src/daemon/daemon-conf.c
-@@ -188,9 +188,17 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
-
- if (!log_target)
- return -1;
-+
-+ c->log_target = log_target;
- }
-
-- c->log_target = log_target;
-+#ifdef USE_DLOG
-+ else if (!strcmp(string, "dlog")) {
-+ c->log_target = PA_LOG_DLOG;
-+ } else if (!strcmp(string, "dlog-color")) {
-+ c->log_target = PA_LOG_DLOG_COLOR;
-+ }
-+#endif
-
- return 0;
- }
-diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
-index 8c956ac..2497b41 100644
---- a/src/pulsecore/cli-command.c
-+++ b/src/pulsecore/cli-command.c
-@@ -188,7 +188,11 @@ static const struct command commands[] = {
- { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
- { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
- { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
-+#ifdef USE_DLOG
-+ { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH|dlog|dlog-color)", 2},
-+#else
- { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH)", 2},
-+#endif
- { "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},
-@@ -1508,7 +1512,11 @@ static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf
- pa_assert(fail);
-
- if (!(m = pa_tokenizer_get(t, 1))) {
-+#ifdef USE_DLOG
-+ pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH|dlog|dlog-color).\n");
-+#else
- pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH).\n");
-+#endif
- return -1;
- }
-
-diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
-index cf96dce..dfb1d0f 100644
---- a/src/pulsecore/log.c
-+++ b/src/pulsecore/log.c
-@@ -61,6 +61,27 @@
-
- #include "log.h"
-
-+#ifdef USE_DLOG
-+#include <dlog.h>
-+#define DLOG_TAG "PULSEAUDIO"
-+
-+#define COLOR_BLACK 30
-+#define COLOR_RED 31
-+#define COLOR_GREEN 32
-+#define COLOR_BLUE 34
-+#define COLOR_MAGENTA 35
-+#define COLOR_CYAN 36
-+#define COLOR_WHITE 97
-+#define COLOR_B_GRAY 100
-+#define COLOR_B_RED 101
-+#define COLOR_B_GREEN 102
-+#define COLOR_B_YELLOW 103
-+#define COLOR_B_BLUE 104
-+#define COLOR_B_MAGENTA 105
-+#define COLOR_B_CYAN 106
-+#define COLOR_REVERSE 7
-+
-+#endif
- #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
- #define ENV_LOG_LEVEL "PULSE_LOG"
- #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
-@@ -545,6 +566,74 @@ void pa_log_levelv_meta(
-
- break;
- }
-+
-+#ifdef USE_DLOG
-+ case PA_LOG_DLOG: {
-+ char *local_t;
-+
-+ openlog(ident, LOG_PID, LOG_USER);
-+
-+ if ((local_t = pa_utf8_to_locale(t)))
-+ t = local_t;
-+
-+ switch (level)
-+ {
-+ case PA_LOG_DEBUG:
-+ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_INFO:
-+ case PA_LOG_NOTICE: // no notice category in dlog, use info instead.
-+ SLOG (LOG_INFO, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_WARN:
-+ SLOG (LOG_WARN, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_ERROR:
-+ SLOG (LOG_ERROR, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ default:
-+ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ }
-+
-+ pa_xfree(local_t);
-+
-+ break;
-+ }
-+ case PA_LOG_DLOG_COLOR: {
-+ char *local_t;
-+
-+ openlog(ident, LOG_PID, LOG_USER);
-+
-+ if ((local_t = pa_utf8_to_locale(t)))
-+ t = local_t;
-+
-+ switch (level)
-+ {
-+ case PA_LOG_DEBUG:
-+ SLOG (LOG_DEBUG, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_GREEN, timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_INFO:
-+ case PA_LOG_NOTICE: // no notice category in dlog, use info instead.
-+ SLOG (LOG_INFO, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_BLUE, timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_WARN:
-+ SLOG (LOG_WARN, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_MAGENTA, timestamp, location, t, pa_strempty(bt));
-+ break;
-+ case PA_LOG_ERROR:
-+ SLOG (LOG_ERROR, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_RED, timestamp, location, t, pa_strempty(bt));
-+ break;
-+ default:
-+ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-+ break;
-+ }
-+
-+ pa_xfree(local_t);
-+
-+ break;
-+ }
-+
-+#endif
- case PA_LOG_NULL:
- default:
- break;
-@@ -629,6 +718,12 @@ pa_log_target *pa_log_parse_target(const char *string) {
- t = pa_log_target_new(PA_LOG_FILE, string + 5);
- else if (pa_startswith(string, "newfile:"))
- t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
-+#ifdef USE_DLOG
-+ else if (pa_streq(string, "dlog"))
-+ t = pa_log_target_new(PA_LOG_DLOG, NULL);
-+ else if (pa_streq(string, "dlog-color"))
-+ t = pa_log_target_new(PA_LOG_DLOG_COLOR, NULL);
-+#endif
- else
- pa_log(_("Invalid log target."));
-
-diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
-index 5e9611d..031040f 100644
---- a/src/pulsecore/log.h
-+++ b/src/pulsecore/log.h
-@@ -41,6 +41,10 @@ typedef enum pa_log_target_type {
- PA_LOG_NULL, /* to /dev/null */
- PA_LOG_FILE, /* to a user specified file */
- PA_LOG_NEWFILE, /* with an automatic suffix to avoid overwriting anything */
-+#ifdef USE_DLOG
-+ PA_LOG_DLOG,
-+ PA_LOG_DLOG_COLOR,
-+#endif
- } pa_log_target_type_t;
-
- typedef enum pa_log_level {
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:21:32 +0800
-Subject: add policy module - samsung
-
-Change-Id: I2111a9c4dc0a371dbea5b347cf77adbe8f930528
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 18 +
- src/Makefile.am | 28 +-
- src/modules/module-policy.c | 926 ++++++++++++++++++++++++++++++++++++++++++++
- src/pulse/ext-policy.c | 177 +++++++++
- src/pulse/ext-policy.h | 61 +++
- src/pulse/proplist.h | 3 +
- 6 files changed, 1211 insertions(+), 2 deletions(-)
- create mode 100644 src/modules/module-policy.c
- create mode 100644 src/pulse/ext-policy.c
- create mode 100644 src/pulse/ext-policy.h
-
-diff --git a/configure.ac b/configure.ac
-index c0beac0..0e205d3 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [],
-
- PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
-
-+dnl use samsung policy module --------------------------------------------------------
-+AC_ARG_ENABLE(samsung-policy, AC_HELP_STRING([--enable-samsung-policy], [using samsung-policy]),
-+[
-+ case "${enableval}" in
-+ yes) USE_SAMSUNG_POLICY=yes ;;
-+ no) USE_SAMSUNG_POLICY=no ;;
-+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-samsung_policy) ;;
-+ esac
-+ ],[USE_SAMSUNG_POLICY=no])
-+
-+if test "x$USE_SAMSUNG_POLICY" = "xyes"; then
-+ PKG_CHECK_MODULES(VCONF, vconf)
-+ AC_SUBST(VCONF_CFLAGS)
-+ AC_SUBST(VCONF_LIBS)
-+fi
-+AM_CONDITIONAL(USE_SAMSUNG_POLICY, test "x$USE_SAMSUNG_POLICY" = "xyes")
-+dnl end --------------------------------------------------------------------
-+
- dnl use dlog --------------------------------------------------------------------------
- AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]),
- [
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 3e41300..4872dfd 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -149,7 +149,7 @@ pulseaudio_SOURCES = \
- daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
- daemon/main.c
-
--pulseaudio_CFLAGS = $(AM_CFLAGS) $(CAP_CFLAGS)
-+pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
- pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(LIBLTDL) $(CAP_LIBS)
- # This is needed because automake doesn't properly expand the foreach below
- pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(PREOPEN_LIBS)
-@@ -787,6 +787,11 @@ pulseinclude_HEADERS = \
- pulse/volume.h \
- pulse/xmalloc.h
-
-+if USE_SAMSUNG_POLICY
-+pulseinclude_HEADERS += \
-+ pulse/ext-policy.h
-+endif
-+
- lib_LTLIBRARIES = \
- libpulse.la \
- libpulse-simple.la
-@@ -833,6 +838,11 @@ libpulse_la_SOURCES = \
- pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
-
-+if USE_SAMSUNG_POLICY
-+libpulse_la_SOURCES += \
-+ pulse/ext-policy.c pulse/ext-policy.h
-+endif
-+
- libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS)
- libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la
- libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
-@@ -1088,6 +1098,10 @@ if HAVE_DBUS
- # Serveral module (e.g. libalsa-util.la)
- modlibexec_LTLIBRARIES += \
- module-console-kit.la
-+if USE_SAMSUNG_POLICY
-+modlibexec_LTLIBRARIES += \
-+ module-policy.la
-+endif
- endif
-
- modlibexec_LTLIBRARIES += \
-@@ -1470,7 +1484,10 @@ SYMDEF_FILES = \
- module-switch-on-port-available-symdef.h \
- module-filter-apply-symdef.h \
- module-filter-heuristics-symdef.h
--
-+if USE_SAMSUNG_POLICY
-+SYMDEF_FILES += \
-+ module-policy-symdef.h
-+endif
- if HAVE_ESOUND
- SYMDEF_FILES += \
- module-esound-protocol-tcp-symdef.h \
-@@ -2120,6 +2137,13 @@ module_rygel_media_server_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol-http.la
- module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-
-+if USE_SAMSUNG_POLICY
-+module_policy_la_SOURCES = modules/module-policy.c
-+module_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
-+module_policy_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
-+module_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS)
-+endif
-+
- ###################################
- # Some minor stuff #
- ###################################
-diff --git a/src/modules/module-policy.c b/src/modules/module-policy.c
-new file mode 100644
-index 0000000..2172018
---- /dev/null
-+++ b/src/modules/module-policy.c
-@@ -0,0 +1,926 @@
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#include <pulsecore/core.h>
-+#include <pulsecore/module.h>
-+#include <pulsecore/modargs.h>
-+#include <pulsecore/core-rtclock.h>
-+#include <pulsecore/core-util.h>
-+#include <pulsecore/log.h>
-+#include <stdbool.h>
-+#include <strings.h>
-+
-+#include <pulsecore/log.h>
-+#include <pulsecore/core-subscribe.h>
-+#include <pulsecore/sink-input.h>
-+#include <pulsecore/source-output.h>
-+#include <pulsecore/namereg.h>
-+#include <pulsecore/core-error.h>
-+
-+#include <pulsecore/protocol-native.h>
-+#include <pulsecore/pstream-util.h>
-+#include <vconf.h> // for mono
-+
-+#include "module-policy-symdef.h"
-+
-+PA_MODULE_AUTHOR("Seungbae Shin");
-+PA_MODULE_DESCRIPTION("Media Policy module");
-+PA_MODULE_VERSION(PACKAGE_VERSION);
-+PA_MODULE_LOAD_ONCE(true);
-+PA_MODULE_USAGE(
-+ "on_hotplug=<When new device becomes available, recheck streams?> ");
-+
-+static const char* const valid_modargs[] = {
-+ "on_hotplug",
-+ NULL
-+};
-+
-+struct userdata {
-+ pa_core *core;
-+ pa_module *module;
-+
-+ pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot;
-+
-+ pa_hook_slot *sink_input_unlink_slot,*sink_unlink_slot;
-+ pa_hook_slot *sink_input_unlink_post_slot, *sink_unlink_post_slot;
-+ pa_hook_slot *sink_input_move_start_slot,*sink_input_move_finish_slot;
-+ pa_subscription *subscription;
-+
-+ bool on_hotplug:1;
-+ int bt_off_idx;
-+
-+ int is_mono;
-+ float balance;
-+ pa_module* module_mono_bt;
-+ pa_module* module_combined;
-+ pa_module* module_mono_combined;
-+ pa_native_protocol *protocol;
-+ pa_hook_slot *source_output_new_hook_slot;
-+};
-+
-+enum {
-+ SUBCOMMAND_TEST,
-+ SUBCOMMAND_MONO,
-+ SUBCOMMAND_BALANCE,
-+};
-+
-+/* DEFINEs */
-+#define AEC_SINK "alsa_output.0.analog-stereo.echo-cancel"
-+#define AEC_SOURCE "alsa_input.0.analog-stereo.echo-cancel"
-+#define SINK_ALSA "alsa_output.0.analog-stereo"
-+#define SINK_MONO_ALSA "mono_alsa"
-+#define SINK_MONO_BT "mono_bt"
-+#define SINK_COMBINED "combined"
-+#define SINK_MONO_COMBINED "mono_combined"
-+#define POLICY_AUTO "auto"
-+#define POLICY_PHONE "phone"
-+#define POLICY_ALL "all"
-+#define POLICY_VOIP "voip"
-+#define BLUEZ_API "bluez"
-+#define ALSA_API "alsa"
-+#define MONO_KEY VCONFKEY_SETAPPL_ACCESSIBILITY_MONO_AUDIO
-+
-+/* check if this sink is bluez */
-+static bool policy_is_bluez (pa_sink* sink)
-+{
-+ const char* api_name = NULL;
-+
-+ if (sink == NULL) {
-+ pa_log_warn ("input param sink is null");
-+ return false;
-+ }
-+
-+ api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
-+ if (api_name) {
-+ if (pa_streq (api_name, BLUEZ_API)) {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
-+#endif
-+ return true;
-+ } else {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...false !!", __func__, PA_PROP_DEVICE_API);
-+#endif
-+ }
-+ } else {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
-+#endif
-+ }
-+
-+ return false;
-+}
-+
-+/* check if this sink is bluez */
-+static bool policy_is_usb_alsa (pa_sink* sink)
-+{
-+ const char* api_name = NULL;
-+ const char* device_bus_name = NULL;
-+
-+ if (sink == NULL) {
-+ pa_log_warn ("input param sink is null");
-+ return false;
-+ }
-+
-+ api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
-+ if (api_name) {
-+ if (pa_streq (api_name, ALSA_API)) {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
-+#endif
-+ device_bus_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS);
-+ if (device_bus_name) {
-+ if (pa_streq (device_bus_name, "usb")) {
-+ return true;
-+ }
-+ }
-+ } else {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] [%s] exists, but not alsa...false !!", __func__, PA_PROP_DEVICE_API);
-+#endif
-+ }
-+ } else {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
-+#endif
-+ }
-+
-+ return false;
-+}
-+
-+/* Get sink by name */
-+static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name)
-+{
-+ pa_sink *s = NULL;
-+ uint32_t idx;
-+
-+ if (c == NULL || sink_name == NULL) {
-+ pa_log_warn ("input param is null");
-+ return NULL;
-+ }
-+
-+ PA_IDXSET_FOREACH(s, c->sinks, idx) {
-+ if (pa_streq (s->name, sink_name)) {
-+ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, sink_name);
-+ return s;
-+ }
-+ }
-+ return NULL;
-+}
-+
-+/* Get bt sink if available */
-+static pa_sink* policy_get_bt_sink (pa_core *c)
-+{
-+ pa_sink *s = NULL;
-+ uint32_t idx;
-+
-+ if (c == NULL) {
-+ pa_log_warn ("input param is null");
-+ return NULL;
-+ }
-+
-+ PA_IDXSET_FOREACH(s, c->sinks, idx) {
-+ if (policy_is_bluez (s)) {
-+ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, s->name);
-+ return s;
-+ }
-+ }
-+ return NULL;
-+}
-+
-+/* Select sink for given condition */
-+static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono)
-+{
-+ pa_sink* sink = NULL;
-+ pa_sink* bt_sink = NULL;
-+ pa_sink* def = NULL;
-+
-+ if (c == NULL || policy == NULL) {
-+ pa_log_warn ("input param is null");
-+ return NULL;
-+ }
-+
-+ pa_assert (c);
-+
-+ bt_sink = policy_get_bt_sink(c);
-+ def = pa_namereg_get_default_sink(c);
-+ if (def == NULL) {
-+ pa_log_warn ("POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
-+ return NULL;
-+ }
-+
-+ pa_log_debug ("[POLICY][%s] policy[%s], is_mono[%d], current default[%s], bt sink[%s]\n",
-+ __func__, policy, is_mono, def->name, (bt_sink)? bt_sink->name:"null");
-+
-+ /* Select sink to */
-+ if (pa_streq(policy, POLICY_ALL)) {
-+ /* all */
-+ if (bt_sink) {
-+ sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED);
-+ } else {
-+ sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
-+ }
-+
-+ } else if (pa_streq(policy, POLICY_PHONE)) {
-+ /* phone */
-+ sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
-+ } else if (pa_streq(policy, POLICY_VOIP)) {
-+ /* VOIP */
-+ sink = policy_get_sink_by_name (c,AEC_SINK);
-+ } else {
-+ /* auto */
-+ if (policy_is_bluez(def)) {
-+ sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_BT) : def;
-+ } else if (policy_is_usb_alsa(def)) {
-+ sink = def;
-+ } else {
-+ sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def;
-+ }
-+ }
-+
-+ pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null");
-+ return sink;
-+}
-+
-+static bool policy_is_filter (pa_sink_input* si)
-+{
-+ const char* role = NULL;
-+
-+ if (si == NULL) {
-+ pa_log_warn ("input param sink-input is null");
-+ return false;
-+ }
-+
-+ if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role);
-+#endif
-+ if (pa_streq(role, "filter")) {
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY] no need to change of sink for %s", role);
-+#endif
-+ return true;
-+ }
-+ }
-+
-+ return false;
-+}
-+
-+
-+
-+#define EXT_VERSION 1
-+
-+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
-+ struct userdata *u = NULL;
-+ uint32_t command;
-+ pa_tagstruct *reply = NULL;
-+
-+ pa_sink_input *si = NULL;
-+ pa_sink *s = NULL;
-+ uint32_t idx;
-+ pa_sink* sink_to_move = NULL;
-+
-+ pa_assert(p);
-+ pa_assert(m);
-+ pa_assert(c);
-+ pa_assert(t);
-+
-+ u = m->userdata;
-+
-+ if (pa_tagstruct_getu32(t, &command) < 0)
-+ goto fail;
-+
-+ reply = pa_tagstruct_new(NULL, 0);
-+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-+ pa_tagstruct_putu32(reply, tag);
-+
-+ switch (command) {
-+ case SUBCOMMAND_TEST: {
-+ if (!pa_tagstruct_eof(t))
-+ goto fail;
-+
-+ pa_tagstruct_putu32(reply, EXT_VERSION);
-+ break;
-+ }
-+
-+ case SUBCOMMAND_MONO: {
-+
-+ bool enable;
-+
-+ if (pa_tagstruct_get_boolean(t, &enable) < 0)
-+ goto fail;
-+
-+ pa_log_debug ("[POLICY][%s] new mono value = %d\n", __func__, enable);
-+ if (enable == u->is_mono) {
-+ pa_log_debug ("[POLICY][%s] No changes in mono value = %d", __func__, u->is_mono);
-+ break;
-+ }
-+
-+ u->is_mono = enable;
-+
-+ /* Move current sink-input to proper mono sink */
-+ PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
-+ const char *policy = NULL;
-+
-+ /* Skip this if it is already in the process of being moved
-+ * anyway */
-+ if (!si->sink)
-+ continue;
-+
-+ /* It might happen that a stream and a sink are set up at the
-+ same time, in which case we want to make sure we don't
-+ interfere with that */
-+ if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
-+ continue;
-+
-+ /* Get role (if role is filter, skip it) */
-+ if (policy_is_filter(si))
-+ continue;
-+
-+ /* Check policy, if no policy exists, treat as AUTO */
-+ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
-+ pa_log_debug("[POLICY] set policy of sink-input[%d] from [%s] to [auto]", si->index, "null");
-+ policy = POLICY_AUTO;
-+ }
-+ pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy);
-+
-+ /* Select sink to move and move to it */
-+ sink_to_move = policy_select_proper_sink (u->core, policy, u->is_mono);
-+ if (sink_to_move) {
-+ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
-+ pa_sink_input_move_to(si, sink_to_move, false);
-+ } else {
-+ pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__);
-+ }
-+ }
-+ break;
-+ }
-+
-+ case SUBCOMMAND_BALANCE: {
-+ float balance;
-+ pa_cvolume cvol;
-+ pa_channel_map map;
-+
-+ if (pa_tagstruct_get_cvolume(t, &cvol) < 0)
-+ goto fail;
-+
-+ pa_channel_map_init_stereo(&map);
-+ balance = pa_cvolume_get_balance(&cvol, &map);
-+
-+ pa_log_debug ("[POLICY][%s] new balance value = [%f]\n", __func__, balance);
-+
-+ if (balance == u->balance) {
-+ pa_log_debug ("[POLICY][%s] No changes in balance value = [%f]", __func__, u->balance);
-+ break;
-+ }
-+
-+ u->balance = balance;
-+
-+ /* Apply balance value to each Sinks */
-+ PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
-+ pa_cvolume* cvol = pa_sink_get_volume (s, false);
-+ pa_cvolume_set_balance (cvol, &s->channel_map, u->balance);
-+ pa_sink_set_volume(s, cvol, true, true);
-+ }
-+ break;
-+ }
-+
-+ default:
-+ goto fail;
-+ }
-+
-+ pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
-+ return 0;
-+
-+ fail:
-+
-+ if (reply)
-+ pa_tagstruct_free(reply);
-+
-+ return -1;
-+}
-+
-+/* Called when new sink-input is creating */
-+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u)
-+{
-+ const char *policy = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(new_data);
-+ pa_assert(u);
-+
-+ if (!new_data->proplist) {
-+ pa_log_debug("[POLICY] New stream lacks property data.");
-+ return PA_HOOK_OK;
-+ }
-+
-+ /* If sink-input has already sink, skip */
-+ if (new_data->sink) {
-+ /* sink-input with filter role will be also here because sink is already set */
-+#ifdef DEBUG_DETAIL
-+ pa_log_debug("[POLICY] Not setting device for stream [%s], because already set.",
-+ pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
-+#endif
-+ return PA_HOOK_OK;
-+ }
-+
-+ /* If no policy exists, skip */
-+ if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) {
-+ pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.",
-+ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
-+ return PA_HOOK_OK;
-+ }
-+ pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]",
-+ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy);
-+
-+ /* Set proper sink to sink-input */
-+ pa_sink* new_sink = policy_select_proper_sink(c, policy, u->is_mono);
-+ if(new_sink != new_data->sink)
-+ {
-+ pa_sink_input_new_data_set_sink(new_data, new_sink, false);
-+ }
-+ /*new_data->save_sink = false;
-+ new_data->sink = policy_select_proper_sink (c, policy, u->is_mono);*/
-+ pa_log_debug("[POLICY][%s] set sink of sink-input to [%s]", __func__, (new_data->sink)? new_data->sink->name : "null");
-+
-+ return PA_HOOK_OK;
-+}
-+
-+/* Called when new sink is added while sink-input is existing */
-+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u)
-+{
-+ pa_sink_input *si;
-+ pa_sink *sink_to_move;
-+ uint32_t idx;
-+ char *args = NULL;
-+
-+ bool is_bt;
-+ bool is_usb_alsa;
-+
-+ pa_assert(c);
-+ pa_assert(sink);
-+ pa_assert(u);
-+ pa_assert(u->on_hotplug);
-+
-+ /* If connected sink is BLUETOOTH, set as default */
-+ /* we are checking with device.api property */
-+ is_bt = policy_is_bluez(sink);
-+ is_usb_alsa = policy_is_usb_alsa(sink);
-+
-+ if (is_bt || is_usb_alsa) {
-+ pa_log_debug("[POLICY][%s] set default sink to sink[%s][%d]", __func__, sink->name, sink->index);
-+ pa_namereg_set_default_sink (c,sink);
-+ } else {
-+ pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index);
-+ return PA_HOOK_OK;
-+ }
-+
-+ if (is_bt) {
-+ /* Load mono_bt sink */
-+ args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_BT, sink->name);
-+ u->module_mono_bt = pa_module_load(u->module->core, "module-remap-sink", args);
-+ pa_xfree(args);
-+
-+ /* load combine sink */
-+ args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_COMBINED, sink->name, SINK_ALSA);
-+ u->module_combined = pa_module_load(u->module->core, "module-combine", args);
-+ pa_xfree(args);
-+
-+ /* load mono_combine sink */
-+ args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_COMBINED, SINK_COMBINED);
-+ u->module_mono_combined = pa_module_load(u->module->core, "module-remap-sink", args);
-+ pa_xfree(args);
-+ }
-+
-+ /* Iterate each sink inputs to decide whether we should move to new sink */
-+ PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
-+ const char *policy = NULL;
-+
-+ if (si->sink == sink)
-+ continue;
-+
-+ /* Skip this if it is already in the process of being moved
-+ * anyway */
-+ if (!si->sink)
-+ continue;
-+
-+ /* It might happen that a stream and a sink are set up at the
-+ same time, in which case we want to make sure we don't
-+ interfere with that */
-+ if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
-+ continue;
-+
-+ /* Get role (if role is filter, skip it) */
-+ if (policy_is_filter(si))
-+ continue;
-+
-+ /* Check policy */
-+ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
-+ /* No policy exists, this means auto */
-+ pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
-+ policy = POLICY_AUTO;
-+ }
-+
-+ sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
-+ if (sink_to_move) {
-+ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
-+ pa_sink_input_move_to(si, sink_to_move, false);
-+ } else {
-+ pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__);
-+ }
-+ }
-+
-+ /* Reset sink volume with balance from userdata */
-+ pa_cvolume* cvol = pa_sink_get_volume(sink, false);
-+ pa_cvolume_set_balance(cvol, &sink->channel_map, u->balance);
-+ pa_sink_set_volume(sink, cvol, true, true);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
-+{
-+ struct userdata *u = userdata;
-+ pa_sink *def;
-+ pa_sink_input *si;
-+ uint32_t idx2;
-+ pa_sink *sink_to_move = NULL;
-+ pa_assert(u);
-+
-+ pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
-+
-+ /* We only handle server changes */
-+ if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
-+
-+ def = pa_namereg_get_default_sink(c);
-+ if (def == NULL) {
-+ pa_log_warn("[POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
-+ return;
-+ }
-+ pa_log_debug("[POLICY][%s] trying to move stream to current default sink = [%s]", __func__, def->name);
-+
-+ /* Iterate each sink inputs to decide whether we should move to new DEFAULT sink */
-+ PA_IDXSET_FOREACH(si, c->sink_inputs, idx2) {
-+ const char *policy = NULL;
-+
-+ if (!si->sink)
-+ continue;
-+
-+ /* Get role (if role is filter, skip it) */
-+ if (policy_is_filter(si))
-+ continue;
-+
-+ /* Get policy */
-+ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
-+ /* No policy exists, this means auto */
-+ pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
-+ policy = POLICY_AUTO;
-+ }
-+
-+ sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
-+ if (sink_to_move) {
-+ /* Move sink-input to new DEFAULT sink */
-+ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
-+ pa_sink_input_move_to(si, sink_to_move, false);
-+ }
-+ }
-+ }
-+}
-+
-+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
-+ struct userdata *u = userdata;
-+ uint32_t idx;
-+ pa_sink *sink_to_move;
-+ pa_sink_input *si;
-+
-+ pa_assert(c);
-+ pa_assert(sink);
-+ pa_assert(u);
-+
-+ /* There's no point in doing anything if the core is shut down anyway */
-+ if (c->state == PA_CORE_SHUTDOWN)
-+ return PA_HOOK_OK;
-+
-+ /* if unloading sink is not bt, just return */
-+ if (!policy_is_bluez (sink)) {
-+ pa_log_debug("[POLICY][%s] sink[%s][%d] unlinked but not a bluez....return\n", __func__, sink->name, sink->index);
-+ return PA_HOOK_OK;
-+ }
-+
-+ pa_log_debug ("[POLICY][%s] SINK unlinked ================================ sink [%s][%d], bt_off_idx was [%d]",
-+ __func__, sink->name, sink->index,u->bt_off_idx);
-+
-+ u->bt_off_idx = sink->index;
-+ pa_log_debug ("[POLICY][%s] bt_off_idx is set to [%d]", __func__, u->bt_off_idx);
-+
-+ /* BT sink is unloading, move sink-input to proper sink */
-+ PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
-+
-+ if (!si->sink)
-+ continue;
-+
-+ /* Get role (if role is filter, skip it) */
-+ if (policy_is_filter(si))
-+ continue;
-+
-+ /* Find who were using bt sink or bt related sink and move them to proper sink (alsa/mono_alsa) */
-+ if (pa_streq (si->sink->name, SINK_MONO_BT) ||
-+ pa_streq (si->sink->name, SINK_MONO_COMBINED) ||
-+ pa_streq (si->sink->name, SINK_COMBINED) ||
-+ policy_is_bluez (si->sink)) {
-+
-+ /* Move sink-input to proper sink : only alsa related sink is available now */
-+ sink_to_move = policy_get_sink_by_name (c, (u->is_mono)? SINK_MONO_ALSA : SINK_ALSA);
-+ if (sink_to_move) {
-+ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
-+ pa_sink_input_move_to(si, sink_to_move, false);
-+ } else {
-+ pa_log_warn("[POLICY][%s] No sink to move", __func__);
-+ }
-+ }
-+ }
-+
-+ pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__);
-+
-+ /* Unload mono_combine sink */
-+ if (u->module_mono_combined) {
-+ pa_module_unload(u->module->core, u->module_mono_combined, true);
-+ u->module_mono_combined = NULL;
-+ }
-+
-+ /* Unload combine sink */
-+ if (u->module_combined) {
-+ pa_module_unload(u->module->core, u->module_combined, true);
-+ u->module_combined = NULL;
-+ }
-+
-+ /* Unload mono_bt sink */
-+ if (u->module_mono_bt) {
-+ pa_module_unload(u->module->core, u->module_mono_bt, true);
-+ u->module_mono_bt = NULL;
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
-+ struct userdata *u = userdata;
-+
-+ pa_assert(c);
-+ pa_assert(sink);
-+ pa_assert(u);
-+
-+ pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index);
-+
-+ /* There's no point in doing anything if the core is shut down anyway */
-+ if (c->state == PA_CORE_SHUTDOWN)
-+ return PA_HOOK_OK;
-+
-+ /* if unloading sink is not bt, just return */
-+ if (!policy_is_bluez (sink)) {
-+ pa_log_debug("[POLICY][%s] not a bluez....return\n", __func__);
-+ return PA_HOOK_OK;
-+ }
-+
-+ u->bt_off_idx = -1;
-+ pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
-+ pa_core_assert_ref(core);
-+ pa_sink_input_assert_ref(i);
-+
-+ /* There's no point in doing anything if the core is shut down anyway */
-+ if (core->state == PA_CORE_SHUTDOWN)
-+ return PA_HOOK_OK;
-+
-+ pa_log_debug ("[POLICY][%s] sink_input_move_start_cb -------------------------------------- sink-input [%d] was sink [%s][%d] : Trying to mute!!!",
-+ __func__, i->index, i->sink->name, i->sink->index);
-+ pa_sink_input_set_mute(i, true, false);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
-+ pa_core_assert_ref(core);
-+ pa_sink_input_assert_ref(i);
-+
-+ /* There's no point in doing anything if the core is shut down anyway */
-+ if (core->state == PA_CORE_SHUTDOWN)
-+ return PA_HOOK_OK;
-+
-+ pa_log_debug("[POLICY][%s] sink_input_move_finish_cb -------------------------------------- sink-input [%d], sink [%s][%d], bt_off_idx [%d] : %s",
-+ __func__, i->index, i->sink->name, i->sink->index, u->bt_off_idx,
-+ (u->bt_off_idx == -1)? "Trying to un-mute!!!!" : "skip un-mute...");
-+
-+ /* If sink input move is caused by bt sink unlink, then skip un-mute operation */
-+ if (u->bt_off_idx == -1) {
-+ pa_sink_input_set_mute(i, false, false);
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_source* policy_get_source_by_name (pa_core *c, const char* source_name)
-+{
-+ pa_source *s = NULL;
-+ uint32_t idx;
-+
-+ if (c == NULL || source_name == NULL) {
-+ pa_log_warn ("input param is null");
-+ return NULL;
-+ }
-+
-+ PA_IDXSET_FOREACH(s, c->sources, idx) {
-+ if (pa_streq (s->name, source_name)) {
-+ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, source_name);
-+ return s;
-+ }
-+ }
-+ return NULL;
-+}
-+
-+/* Select source for given condition */
-+static pa_source* policy_select_proper_source (pa_core *c, const char* policy)
-+{
-+ pa_source* source = NULL;
-+ pa_source* def = NULL;
-+
-+ if (c == NULL || policy == NULL) {
-+ pa_log_warn ("input param is null");
-+ return NULL;
-+ }
-+
-+ pa_assert (c);
-+ def = pa_namereg_get_default_source(c);
-+ if (def == NULL) {
-+ pa_log_warn ("POLICY][%s] pa_namereg_get_default_source() returns null", __func__);
-+ return NULL;
-+ }
-+
-+ /* Select source to */
-+ if (pa_streq(policy, POLICY_VOIP)) {
-+ source = policy_get_source_by_name (c, AEC_SOURCE);
-+
-+ } else {
-+ source = def;
-+ }
-+
-+ pa_log_debug ("[POLICY][%s] selected source : [%s]\n", __func__, (source)? source->name : "null");
-+ return source;
-+}
-+
-+
-+/* Called when new source-output is creating */
-+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
-+ const char *policy = NULL;
-+ pa_assert(c);
-+ pa_assert(new_data);
-+ pa_assert(u);
-+
-+ if (!new_data->proplist) {
-+ pa_log_debug("New stream lacks property data.");
-+ return PA_HOOK_OK;
-+ }
-+
-+ if (new_data->source) {
-+ pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
-+ return PA_HOOK_OK;
-+ }
-+
-+ /* If no policy exists, skip */
-+ if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) {
-+ pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.",
-+ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
-+ return PA_HOOK_OK;
-+ }
-+ pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]",
-+ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy);
-+
-+ /* Set proper source to source-output */
-+ pa_source* new_source = policy_select_proper_source(c, policy);
-+ if(new_source != new_data->source)
-+ {
-+ pa_source_output_new_data_set_source(new_data, new_source, false);
-+ }
-+ /*new_data->save_source= false;
-+ new_data->source= policy_select_proper_source (c, policy);*/
-+ pa_log_debug("[POLICY][%s] set source of source-input to [%s]", __func__, (new_data->source)? new_data->source->name : "null");
-+
-+ return PA_HOOK_OK;
-+}
-+
-+int pa__init(pa_module *m)
-+{
-+ pa_modargs *ma = NULL;
-+ struct userdata *u;
-+ bool on_hotplug = true, on_rescue = true;
-+
-+ pa_assert(m);
-+
-+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-+ pa_log("Failed to parse module arguments");
-+ goto fail;
-+ }
-+
-+ if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
-+ pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
-+ pa_log("on_hotplug= and on_rescue= expect boolean arguments");
-+ goto fail;
-+ }
-+
-+ m->userdata = u = pa_xnew0(struct userdata, 1);
-+ u->core = m->core;
-+ u->module = m;
-+ u->on_hotplug = on_hotplug;
-+
-+
-+ /* A little bit later than module-stream-restore */
-+ u->sink_input_new_hook_slot =
-+ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u);
-+
-+ u->source_output_new_hook_slot =
-+ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u);
-+
-+ if (on_hotplug) {
-+ /* A little bit later than module-stream-restore */
-+ u->sink_put_hook_slot =
-+ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
-+ }
-+
-+ /* sink unlink comes before sink-input unlink */
-+ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_callback, u);
-+ u->sink_unlink_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_post_hook_callback, u);
-+
-+ u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
-+ u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
-+
-+ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
-+
-+
-+ u->bt_off_idx = -1; /* initial bt off sink index */
-+
-+ u->module_mono_bt = NULL;
-+ u->module_combined = NULL;
-+ u->module_mono_combined = NULL;
-+
-+ u->protocol = pa_native_protocol_get(m->core);
-+ pa_native_protocol_install_ext(u->protocol, m, extension_cb);
-+
-+ /* Get mono key value for init */
-+ vconf_get_bool(MONO_KEY, &u->is_mono);
-+
-+ pa_log_info("policy module is loaded\n");
-+
-+ if (ma)
-+ pa_modargs_free(ma);
-+
-+ return 0;
-+
-+fail:
-+ if (ma)
-+ pa_modargs_free(ma);
-+
-+ pa__done(m);
-+
-+ return -1;
-+}
-+
-+void pa__done(pa_module *m)
-+{
-+ struct userdata* u;
-+
-+ pa_assert(m);
-+
-+ if (!(u = m->userdata))
-+ return;
-+
-+ if (u->sink_input_new_hook_slot)
-+ pa_hook_slot_free(u->sink_input_new_hook_slot);
-+ if (u->sink_put_hook_slot)
-+ pa_hook_slot_free(u->sink_put_hook_slot);
-+ if (u->sink_unlink_slot)
-+ pa_hook_slot_free(u->sink_unlink_slot);
-+ if (u->sink_unlink_post_slot)
-+ pa_hook_slot_free(u->sink_unlink_post_slot);
-+ if (u->sink_input_move_start_slot)
-+ pa_hook_slot_free(u->sink_input_move_start_slot);
-+ if (u->sink_input_move_finish_slot)
-+ pa_hook_slot_free(u->sink_input_move_finish_slot);
-+ if (u->subscription)
-+ pa_subscription_free(u->subscription);
-+ if (u->protocol) {
-+ pa_native_protocol_remove_ext(u->protocol, m);
-+ pa_native_protocol_unref(u->protocol);
-+ }
-+ if (u->source_output_new_hook_slot)
-+ pa_hook_slot_free(u->source_output_new_hook_slot);
-+
-+ pa_xfree(u);
-+
-+
-+ pa_log_info("policy module is unloaded\n");
-+}
-diff --git a/src/pulse/ext-policy.c b/src/pulse/ext-policy.c
-new file mode 100644
-index 0000000..f3a3a8c
---- /dev/null
-+++ b/src/pulse/ext-policy.c
-@@ -0,0 +1,177 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ PulseAudio is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as published
-+ by the Free Software Foundation; either version 2.1 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 <pulse/context.h>
-+#include <pulse/gccmacro.h>
-+#include <pulse/xmalloc.h>
-+
-+#include <pulsecore/macro.h>
-+#include <pulsecore/pstream-util.h>
-+
-+#include "internal.h"
-+#include "operation.h"
-+#include "fork-detect.h"
-+
-+#include "ext-policy.h"
-+
-+enum {
-+ SUBCOMMAND_TEST,
-+ SUBCOMMAND_MONO,
-+ SUBCOMMAND_BALANCE,
-+};
-+
-+static void ext_policy_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-+ pa_operation *o = userdata;
-+ uint32_t version = PA_INVALID_INDEX;
-+
-+ pa_assert(pd);
-+ pa_assert(o);
-+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
-+
-+ if (!o->context)
-+ goto finish;
-+
-+ if (command != PA_COMMAND_REPLY) {
-+ if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
-+ goto finish;
-+
-+ } else if (pa_tagstruct_getu32(t, &version) < 0 ||
-+ !pa_tagstruct_eof(t)) {
-+
-+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
-+ goto finish;
-+ }
-+
-+ if (o->callback) {
-+ pa_ext_policy_test_cb_t cb = (pa_ext_policy_test_cb_t) o->callback;
-+ cb(o->context, version, o->userdata);
-+ }
-+
-+finish:
-+ pa_operation_done(o);
-+ pa_operation_unref(o);
-+}
-+
-+pa_operation *pa_ext_policy_test(
-+ pa_context *c,
-+ pa_ext_device_manager_test_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-policy");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_policy_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+pa_operation *pa_ext_policy_set_mono (
-+ pa_context *c,
-+ int enable,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-policy");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_MONO);
-+ pa_tagstruct_put_boolean(t, !!enable);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+pa_operation *pa_ext_policy_set_balance (
-+ pa_context *c,
-+ double *balance,
-+ pa_context_success_cb_t cb,
-+ void *userdata) {
-+
-+ uint32_t tag;
-+ pa_operation *o = NULL;
-+ pa_tagstruct *t = NULL;
-+ pa_cvolume cvol;
-+ pa_channel_map map;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, "module-policy");
-+ pa_tagstruct_putu32(t, SUBCOMMAND_BALANCE);
-+
-+ /* Prepare cvolume for transfer */
-+ pa_channel_map_init_stereo(&map);
-+ pa_cvolume_set(&cvol, map.channels, 65535);
-+
-+ pa_log_error ("balance = %f", *balance);
-+
-+ pa_cvolume_set_balance(&cvol, &map, *balance);
-+
-+ pa_log_error ("balance get = %f", pa_cvolume_get_balance(&cvol, &map));
-+
-+ pa_tagstruct_put_cvolume(t, &cvol);
-+
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-diff --git a/src/pulse/ext-policy.h b/src/pulse/ext-policy.h
-new file mode 100644
-index 0000000..ec62ead
---- /dev/null
-+++ b/src/pulse/ext-policy.h
-@@ -0,0 +1,61 @@
-+#ifndef foopulseextpolicyhfoo
-+#define foopulseextpolicyhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ PulseAudio is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as published
-+ by the Free Software Foundation; either version 2.1 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/context.h>
-+#include <pulse/version.h>
-+
-+/** \file
-+ *
-+ * Routines for controlling module-policy
-+ */
-+
-+PA_C_DECL_BEGIN
-+
-+/** Callback prototype for pa_ext_policy_test(). \since 0.9.21 */
-+typedef void (*pa_ext_policy_test_cb_t)(
-+ pa_context *c,
-+ uint32_t version,
-+ void *userdata);
-+
-+/** Test if this extension module is available in the server. \since 0.9.21 */
-+pa_operation *pa_ext_policy_test(
-+ pa_context *c,
-+ pa_ext_policy_test_cb_t cb,
-+ void *userdata);
-+
-+/** Enable the mono mode. \since 0.9.21 */
-+pa_operation *pa_ext_policy_set_mono (
-+ pa_context *c,
-+ int enable,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+/** Enable the balance mode. \since 0.9.21 */
-+pa_operation *pa_ext_policy_set_balance (
-+ pa_context *c,
-+ double *balance,
-+ pa_context_success_cb_t cb,
-+ void *userdata);
-+
-+PA_C_DECL_END
-+
-+#endif
-diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
-index dc3cddc..341abaa 100644
---- a/src/pulse/proplist.h
-+++ b/src/pulse/proplist.h
-@@ -65,6 +65,9 @@ PA_C_DECL_BEGIN
- /** For streams: logic role of this media. One of the strings "video", "music", "game", "event", "phone", "animation", "production", "a11y", "test" */
- #define PA_PROP_MEDIA_ROLE "media.role"
-
-+/** For streams: logic role of this media. One of the strings "auto", "phone" */
-+#define PA_PROP_MEDIA_POLICY "media.policy"
-+
- /** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". PulseAudio may choose to not apply the filter if it does not make sense (for example, applying echo-cancellation on a Bluetooth headset probably does not make sense. \since 1.0 */
- #define PA_PROP_FILTER_WANT "filter.want"
-
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:23:45 +0800
-Subject: add bluetooth a2dp aptx codec support - samsung
-
-Change-Id: I2c90198774c1e7d3e2ecb99f2dd365d56308f157
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 20 ++
- src/Makefile.am | 27 +++
- src/modules/bluetooth/a2dp-codecs.h | 39 ++++
- src/modules/bluetooth/bluetooth-util.h | 183 +++++++++++++++++
- src/modules/bluetooth/bluez4-util.c | 184 ++++++++++++++++-
- src/modules/bluetooth/module-bluetooth-discover.c | 37 ++++
- src/modules/bluetooth/module-bluez4-device.c | 236 ++++++++++++++++++++++
- 7 files changed, 724 insertions(+), 2 deletions(-)
- create mode 100644 src/modules/bluetooth/bluetooth-util.h
-
-diff --git a/configure.ac b/configure.ac
-index 0e205d3..ebff16c 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1079,6 +1079,26 @@ else
- fi
- AC_SUBST(BLUETOOTH_HEADSET_BACKEND)
-
-+#### Bluetooth A2DP aptx codec support(optional) ####
-+AC_ARG_ENABLE([bt_a2dp_aptx],
-+ AS_HELP_STRING([--enable-bt-a2dp-aptx],[Enable optional Bluetooth A2DP aptx codec support(arm only)]),
-+ [
-+ case "${enableval}" in
-+ yes) bt_a2dp_aptx=yes ;;
-+ no) bt_a2dp_aptx=no ;;
-+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bt-a2dp-aptx) ;;
-+ esac
-+ ],
-+ [bt_a2dp_aptx=false])
-+if test "x${bt_a2dp_aptx}" == xyes ; then
-+ HAVE_BT_A2DP_APTX=1
-+else
-+ HAVE_BT_A2DP_APTX=0
-+fi
-+
-+AC_SUBST(HAVE_BT_A2DP_APTX)
-+AM_CONDITIONAL([HAVE_BT_A2DP_APTX], [test "x$HAVE_BT_A2DP_APTX" = x1])
-+
- #### UDEV support (optional) ####
-
- AC_ARG_ENABLE([udev],
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 4872dfd..3c062eb 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -2078,7 +2078,11 @@ module_bluetooth_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
- module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
- module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD)
-+if HAVE_BT_A2DP_APTX
-+module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
-+else
- module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS)
-+endif
-
- # Bluetooth BlueZ 4 sink / source
- module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c
-@@ -2092,12 +2096,23 @@ libbluez4_util_la_SOURCES = \
- modules/bluetooth/bluez4-util.h
- libbluez4_util_la_LDFLAGS = -avoid-version
- libbluez4_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
-+
-+if HAVE_BT_A2DP_APTX
-+libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
-+else
- libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-+endif
-
- module_bluez4_device_la_SOURCES = modules/bluetooth/module-bluez4-device.c modules/bluetooth/rtp.h
- module_bluez4_device_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_bluez4_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) libbluez4-util.la
-+
-+if HAVE_BT_A2DP_APTX
-+module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) \
-+ -DBLUETOOTH_APTX_SUPPORT
-+else
- module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
-+endif
-
- # Bluetooth BlueZ 5 sink / source
- libbluez5_util_la_SOURCES = \
-@@ -2108,7 +2123,12 @@ libbluez5_util_la_SOURCES = \
- modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c
- libbluez5_util_la_LDFLAGS = -avoid-version
- libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
-+
-+if HAVE_BT_A2DP_APTX
-+libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
-+else
- libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-+endif
-
- module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
- module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
-@@ -2120,6 +2140,13 @@ module_bluez5_device_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la
- module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS)
-
-+if HAVE_BT_A2DP_APTX
-+module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) \
-+ -DBLUETOOTH_APTX_SUPPORT
-+else
-+module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS)
-+endif
-+
- # Apple Airtunes/RAOP
- module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c
- module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
-diff --git a/src/modules/bluetooth/a2dp-codecs.h b/src/modules/bluetooth/a2dp-codecs.h
-index 51c796a..c94812b 100644
---- a/src/modules/bluetooth/a2dp-codecs.h
-+++ b/src/modules/bluetooth/a2dp-codecs.h
-@@ -27,6 +27,7 @@
- #define A2DP_CODEC_MPEG24 0x02
- #define A2DP_CODEC_ATRAC 0x03
-
-+#define A2DP_CODEC_NON_A2DP 0xFF
- #define SBC_SAMPLING_FREQ_16000 (1 << 3)
- #define SBC_SAMPLING_FREQ_32000 (1 << 2)
- #define SBC_SAMPLING_FREQ_44100 (1 << 1)
-@@ -67,6 +68,32 @@
- #define MAX_BITPOOL 64
- #define MIN_BITPOOL 2
-
-+/*#define APTX_CHANNEL_MODE_STEREO 2 */
-+/*
-+ * aptX codec for Bluetooth only supports stereo mode with value 2
-+ * But we do have sink devices programmed to send capabilities with other channel mode support.
-+ * So to handle the case and keeping codec symmetry with SBC etc., we do define other channel mode,
-+ * and we always make sure to set configuration with APTX_CHANNEL_MODE_STEREO only.
-+ *
-+ * */
-+
-+#define APTX_CHANNEL_MODE_MONO (1 << 3)
-+#define APTX_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
-+#define APTX_CHANNEL_MODE_STEREO (1 << 1)
-+#define APTX_CHANNEL_MODE_JOINT_STEREO 1
-+
-+#define APTX_VENDOR_ID0 0x4F /*APTX codec ID 79*/
-+#define APTX_VENDOR_ID1 0x0
-+#define APTX_VENDOR_ID2 0x0
-+#define APTX_VENDOR_ID3 0x0
-+
-+#define APTX_CODEC_ID0 0x1
-+#define APTX_CODEC_ID1 0x0
-+
-+#define APTX_SAMPLING_FREQ_16000 (1 << 3)
-+#define APTX_SAMPLING_FREQ_32000 (1 << 2)
-+#define APTX_SAMPLING_FREQ_44100 (1 << 1)
-+#define APTX_SAMPLING_FREQ_48000 1
- #if __BYTE_ORDER == __LITTLE_ENDIAN
-
- typedef struct {
-@@ -89,6 +116,12 @@ typedef struct {
- uint16_t bitrate;
- } __attribute__ ((packed)) a2dp_mpeg_t;
-
-+typedef struct {
-+ uint8_t vendor_id[4];
-+ uint8_t codec_id[2];
-+ uint8_t channel_mode:4;
-+ uint8_t frequency:4;
-+} __attribute__ ((packed)) a2dp_aptx_t;
- #elif __BYTE_ORDER == __BIG_ENDIAN
-
- typedef struct {
-@@ -110,6 +143,12 @@ typedef struct {
- uint8_t frequency:6;
- uint16_t bitrate;
- } __attribute__ ((packed)) a2dp_mpeg_t;
-+typedef struct {
-+ uint8_t vendor_id[4];
-+ uint8_t codec_id[2];
-+ uint8_t frequency:4;
-+ uint8_t channel_mode:4;
-+} __attribute__ ((packed)) a2dp_aptx_t;
-
- #else
- #error "Unknown byte order"
-diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
-new file mode 100644
-index 0000000..859ad2d
---- /dev/null
-+++ b/src/modules/bluetooth/bluetooth-util.h
-@@ -0,0 +1,183 @@
-+#ifndef foobluetoothutilhfoo
-+#define foobluetoothutilhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2008-2009 Joao Paulo Rechi Vita
-+
-+ 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.1 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 <dbus/dbus.h>
-+
-+#include <pulsecore/llist.h>
-+#include <pulsecore/macro.h>
-+
-+#define PA_BLUETOOTH_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
-+
-+/* UUID copied from bluez/audio/device.h */
-+#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
-+
-+#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
-+#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb"
-+
-+#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb"
-+#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
-+
-+#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb"
-+
-+#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
-+#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb"
-+
-+#define HSP_MAX_GAIN 15
-+
-+typedef struct pa_bluetooth_uuid pa_bluetooth_uuid;
-+typedef struct pa_bluetooth_device pa_bluetooth_device;
-+typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
-+typedef struct pa_bluetooth_transport pa_bluetooth_transport;
-+
-+struct userdata;
-+
-+struct pa_bluetooth_uuid {
-+ char *uuid;
-+ PA_LLIST_FIELDS(pa_bluetooth_uuid);
-+};
-+
-+enum profile {
-+ PROFILE_A2DP,
-+ PROFILE_A2DP_SOURCE,
-+ PROFILE_HSP,
-+ PROFILE_HFGW,
-+ PROFILE_OFF
-+};
-+
-+#define PA_BLUETOOTH_PROFILE_COUNT PROFILE_OFF
-+
-+struct pa_bluetooth_hook_uuid_data {
-+ pa_bluetooth_device *device;
-+ const char *uuid;
-+};
-+
-+/* Hook data: pa_bluetooth_discovery pointer. */
-+typedef enum pa_bluetooth_hook {
-+ PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
-+ PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluetooth_hook_uuid_data */
-+ PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
-+ PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluetooth_transport */
-+ PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */
-+ PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */
-+ PA_BLUETOOTH_HOOK_MAX
-+} pa_bluetooth_hook_t;
-+
-+typedef enum pa_bluetooth_transport_state {
-+ PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED,
-+ PA_BLUETOOTH_TRANSPORT_STATE_IDLE, /* Connected but not playing */
-+ PA_BLUETOOTH_TRANSPORT_STATE_PLAYING
-+} pa_bluetooth_transport_state_t;
-+
-+struct pa_bluetooth_transport {
-+ pa_bluetooth_device *device;
-+ char *owner;
-+ char *path;
-+ enum profile profile;
-+ uint8_t codec;
-+ uint8_t *config;
-+ int config_size;
-+
-+ pa_bluetooth_transport_state_t state;
-+ bool nrec;
-+ uint16_t microphone_gain; /* Used for HSP/HFP */
-+ uint16_t speaker_gain; /* Used for HSP/HFP */
-+};
-+
-+/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */
-+typedef enum pa_bt_audio_state {
-+ PA_BT_AUDIO_STATE_INVALID = -1,
-+ PA_BT_AUDIO_STATE_DISCONNECTED,
-+ PA_BT_AUDIO_STATE_CONNECTING,
-+ PA_BT_AUDIO_STATE_CONNECTED,
-+ PA_BT_AUDIO_STATE_PLAYING
-+} pa_bt_audio_state_t;
-+
-+struct pa_bluetooth_device {
-+ pa_bluetooth_discovery *discovery;
-+ bool dead;
-+
-+ int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */
-+
-+ /* Device information */
-+ char *name;
-+ char *path;
-+ pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT];
-+ int paired;
-+ char *alias;
-+ PA_LLIST_HEAD(pa_bluetooth_uuid, uuids);
-+ char *address;
-+ int class;
-+ int trusted;
-+
-+ /* Audio state */
-+ pa_bt_audio_state_t audio_state;
-+
-+ /* AudioSink, AudioSource, Headset and HandsfreeGateway states */
-+ pa_bt_audio_state_t profile_state[PA_BLUETOOTH_PROFILE_COUNT];
-+};
-+
-+pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core);
-+pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y);
-+void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *d);
-+
-+pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path);
-+pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address);
-+
-+bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d);
-+
-+int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu);
-+void pa_bluetooth_transport_release(pa_bluetooth_transport *t);
-+
-+void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value);
-+void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value);
-+
-+pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
-+
-+typedef enum pa_bt_form_factor {
-+ PA_BT_FORM_FACTOR_UNKNOWN,
-+ PA_BT_FORM_FACTOR_HEADSET,
-+ PA_BT_FORM_FACTOR_HANDSFREE,
-+ PA_BT_FORM_FACTOR_MICROPHONE,
-+ PA_BT_FORM_FACTOR_SPEAKER,
-+ PA_BT_FORM_FACTOR_HEADPHONE,
-+ PA_BT_FORM_FACTOR_PORTABLE,
-+ PA_BT_FORM_FACTOR_CAR,
-+ PA_BT_FORM_FACTOR_HIFI,
-+ PA_BT_FORM_FACTOR_PHONE,
-+} pa_bt_form_factor_t;
-+
-+pa_bt_form_factor_t pa_bluetooth_get_form_factor(uint32_t class);
-+const char *pa_bt_form_factor_to_string(pa_bt_form_factor_t ff);
-+
-+char *pa_bluetooth_cleanup_name(const char *name);
-+
-+bool pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid);
-+const char *pa_bt_profile_to_string(enum profile profile);
-+
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+int pa_load_aptx(const char *aptx_lib_name);
-+int pa_unload_aptx(void);
-+void* pa_aptx_get_handle(void);
-+#endif
-+#endif
-diff --git a/src/modules/bluetooth/bluez4-util.c b/src/modules/bluetooth/bluez4-util.c
-index f047b73..7e7aed0 100644
---- a/src/modules/bluetooth/bluez4-util.c
-+++ b/src/modules/bluetooth/bluez4-util.c
-@@ -22,6 +22,9 @@
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+#include <dlfcn.h>
-+#endif
-
- #include <pulse/xmalloc.h>
-
-@@ -36,6 +39,9 @@
- #define ENDPOINT_PATH_HFP_HS "/MediaEndpoint/BlueZ4/HFPHS"
- #define ENDPOINT_PATH_A2DP_SOURCE "/MediaEndpoint/BlueZ4/A2DPSource"
- #define ENDPOINT_PATH_A2DP_SINK "/MediaEndpoint/BlueZ4/A2DPSink"
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+#define ENDPOINT_PATH_A2DP_APTX_SOURCE "/MediaEndpoint/Bluez4/A2DPSource_aptx"
-+#endif
-
- #define ENDPOINT_INTROSPECT_XML \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
-@@ -80,6 +86,56 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluez4_discovery *y, DBusMess
- static void found_adapter(pa_bluez4_discovery *y, const char *path);
- static pa_bluez4_device *found_device(pa_bluez4_discovery *y, const char* path);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+static void *aptx_handle = NULL;
-+
-+int pa_unload_aptx(void)
-+{
-+ if (aptx_handle == NULL) {
-+ pa_log_warn("Unable to unload apt-X library");
-+ return -1;
-+ }
-+
-+ dlclose(aptx_handle);
-+ aptx_handle = NULL;
-+
-+ pa_log_debug("unloaded apt-X library successfully");
-+ return 0;
-+}
-+
-+int pa_load_aptx(const char *aptx_lib_name)
-+{
-+ char* lib_path = NULL ;
-+
-+ if(aptx_lib_name == NULL)
-+ return -1;
-+
-+ lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name);
-+
-+ if (!lib_path)
-+ return -1;
-+
-+ pa_log_info("aptx_lib_path = [%s]", lib_path);
-+
-+ aptx_handle = dlopen(lib_path, RTLD_LAZY);
-+ if (aptx_handle == NULL) {
-+ pa_log_warn("Unable to load apt-X library [%s]", dlerror());
-+ pa_xfree(lib_path);
-+ return -1;
-+ }
-+
-+ pa_log_debug("loaded apt-X library successfully");
-+ pa_xfree(lib_path);
-+
-+ return 0;
-+}
-+
-+void* pa_aptx_get_handle(void)
-+{
-+ return aptx_handle;
-+}
-+#endif
-+
- static pa_bluez4_audio_state_t audio_state_from_string(const char* value) {
- pa_assert(value);
-
-@@ -835,6 +891,8 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch
- uint8_t capability = 0;
- pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capability, 1);
- } else {
-+ pa_log_debug("register_endpoint: codec=%d[%s]", codec, codec==A2DP_CODEC_SBC ? "A2DP_CODEC_SBC" : codec==A2DP_CODEC_NON_A2DP ? "A2DP_CODEC_NON_A2DP" : "unknown");
-+ if (codec == A2DP_CODEC_SBC) {
- a2dp_sbc_t capabilities;
-
- capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL |
-@@ -849,6 +907,23 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch
- capabilities.max_bitpool = MAX_BITPOOL;
-
- pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
-+ } else if (codec == A2DP_CODEC_NON_A2DP ) {
-+ /* aptx */
-+ a2dp_aptx_t capabilities;
-+
-+ capabilities.vendor_id[0] = APTX_VENDOR_ID0;
-+ capabilities.vendor_id[1] = APTX_VENDOR_ID1;
-+ capabilities.vendor_id[2] = APTX_VENDOR_ID2;
-+ capabilities.vendor_id[3] = APTX_VENDOR_ID3;
-+
-+ capabilities.codec_id[0] = APTX_CODEC_ID0;
-+ capabilities.codec_id[1] = APTX_CODEC_ID1;
-+
-+ capabilities.channel_mode= APTX_CHANNEL_MODE_STEREO;
-+ capabilities.frequency= APTX_SAMPLING_FREQ_44100;
-+
-+ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
-+ }
- }
-
- dbus_message_iter_close_container(&i, &d);
-@@ -857,6 +932,7 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch
- }
-
- static void found_adapter(pa_bluez4_discovery *y, const char *path) {
-+
- DBusMessage *m;
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "GetProperties"));
-@@ -866,6 +942,10 @@ static void found_adapter(pa_bluez4_discovery *y, const char *path) {
- register_endpoint(y, path, ENDPOINT_PATH_HFP_HS, HFP_HS_UUID);
- register_endpoint(y, path, ENDPOINT_PATH_A2DP_SOURCE, A2DP_SOURCE_UUID);
- register_endpoint(y, path, ENDPOINT_PATH_A2DP_SINK, A2DP_SINK_UUID);
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (aptx_handle)
-+ register_endpoint(y, path, ENDPOINT_PATH_A2DP_APTX_SOURCE, A2DP_SOURCE_UUID);
-+#endif
- }
-
- static void list_adapters(pa_bluez4_discovery *y) {
-@@ -1349,7 +1429,11 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
- p = PA_BLUEZ4_PROFILE_HSP;
- else if (dbus_message_has_path(m, ENDPOINT_PATH_HFP_HS))
- p = PA_BLUEZ4_PROFILE_HFGW;
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ else if (dbus_message_has_path(m, ENDPOINT_PATH_A2DP_SOURCE) || dbus_message_has_path(m, ENDPOINT_PATH_A2DP_APTX_SOURCE))
-+#else
- else if (dbus_message_has_path(m, ENDPOINT_PATH_A2DP_SOURCE))
-+#endif
- p = PA_BLUEZ4_PROFILE_A2DP;
- else
- p = PA_BLUEZ4_PROFILE_A2DP_SOURCE;
-@@ -1475,6 +1559,84 @@ static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
- }
- }
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+static DBusMessage *endpoint_select_configuration_for_aptx(DBusConnection *c, DBusMessage *m, void *userdata) {
-+ a2dp_aptx_t *cap;
-+ a2dp_aptx_t config;
-+ uint8_t *pconf = (uint8_t *) &config;
-+ int size;
-+ DBusMessage *r;
-+ DBusError e;
-+
-+ dbus_error_init(&e);
-+
-+ if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
-+ pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message);
-+ dbus_error_free(&e);
-+ goto fail;
-+ }
-+
-+ pa_assert(size == sizeof(config));
-+
-+ memset(&config, 0, sizeof(config));
-+
-+ if (cap->vendor_id[0] == APTX_VENDOR_ID0 &&
-+ cap->vendor_id[1] == APTX_VENDOR_ID1 &&
-+ cap->vendor_id[2] == APTX_VENDOR_ID2 &&
-+ cap->vendor_id[3] == APTX_VENDOR_ID3 &&
-+ cap->codec_id[0] == APTX_CODEC_ID0 &&
-+ cap->codec_id[1] == APTX_CODEC_ID1 )
-+ pa_log_debug("A2DP_CODEC_NON_A2DP and this is APTX Codec");
-+ else {
-+ pa_log_debug("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
-+ goto fail;
-+ }
-+
-+ memcpy(&config,cap, sizeof(config));
-+
-+/* The below code shuld be re-written by aptx */
-+/* And we should configure pulseaudio freq */
-+
-+ if (cap->frequency & APTX_SAMPLING_FREQ_44100)
-+ config.frequency = APTX_SAMPLING_FREQ_44100;
-+ else if (cap->frequency & APTX_SAMPLING_FREQ_48000)
-+ config.frequency = APTX_SAMPLING_FREQ_48000;
-+ else if (cap->frequency & APTX_SAMPLING_FREQ_32000)
-+ config.frequency = APTX_SAMPLING_FREQ_32000;
-+ else if (cap->frequency & APTX_SAMPLING_FREQ_16000)
-+ config.frequency = APTX_SAMPLING_FREQ_16000;
-+ else {
-+ pa_log_error("No aptx supported frequencies");
-+ goto fail;
-+ }
-+
-+ if (cap->channel_mode & APTX_CHANNEL_MODE_JOINT_STEREO)
-+ config.channel_mode = APTX_CHANNEL_MODE_STEREO;
-+ else if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO)
-+ config.channel_mode = APTX_CHANNEL_MODE_STEREO;
-+ else if (cap->channel_mode & APTX_CHANNEL_MODE_DUAL_CHANNEL)
-+ config.channel_mode = APTX_CHANNEL_MODE_STEREO;
-+ else {
-+ pa_log_error("No aptx supported channel modes");
-+ goto fail;
-+ }
-+
-+ pa_assert_se(r = dbus_message_new_method_return(m));
-+
-+ pa_assert_se(dbus_message_append_args(
-+ r,
-+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
-+ DBUS_TYPE_INVALID));
-+
-+ return r;
-+
-+fail:
-+ pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
-+ "Unable to select configuration")));
-+ return r;
-+}
-+#endif
-+
- static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
- pa_bluez4_discovery *y = userdata;
- a2dp_sbc_t *cap, config;
-@@ -1493,6 +1655,10 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage
- { 48000U, SBC_SAMPLING_FREQ_48000 }
- };
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (dbus_message_has_path(m, A2DP_APTX_SOURCE_ENDPOINT))
-+ return endpoint_select_configuration_for_aptx(c ,m ,userdata);
-+#endif
- dbus_error_init(&e);
-
- if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
-@@ -1614,8 +1780,13 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
-
- dbus_error_init(&e);
-
-- if (!pa_streq(path, ENDPOINT_PATH_A2DP_SOURCE) && !pa_streq(path, ENDPOINT_PATH_A2DP_SINK)
-- && !pa_streq(path, ENDPOINT_PATH_HFP_AG) && !pa_streq(path, ENDPOINT_PATH_HFP_HS))
-+ if (!pa_streq(path, ENDPOINT_PATH_A2DP_SOURCE) &&
-+ !pa_streq(path, ENDPOINT_PATH_A2DP_SINK) &&
-+ !pa_streq(path, ENDPOINT_PATH_HFP_AG) &&
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ !pa_streq(path, ENDPOINT_PATH_A2DP_APTX_SOURCE) &&
-+#endif
-+ !pa_streq(path, ENDPOINT_PATH_HFP_HS))
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-@@ -1706,6 +1877,11 @@ pa_bluez4_discovery* pa_bluez4_discovery_get(pa_core *c) {
- pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SOURCE, &vtable_endpoint, y));
- pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SINK, &vtable_endpoint, y));
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (aptx_handle)
-+ pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_APTX_SOURCE, &vtable_endpoint, y));
-+#endif
-+
- list_adapters(y);
-
- return y;
-@@ -1754,6 +1930,10 @@ void pa_bluez4_discovery_unref(pa_bluez4_discovery *y) {
- dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_HFP_HS);
- dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SOURCE);
- dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SINK);
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (aptx_handle)
-+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_APTX_SOURCE);
-+#endif
- pa_dbus_remove_matches(
- pa_dbus_connection_get(y->connection),
- "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
-diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
-index 0bcfcf9..e1dbec5 100644
---- a/src/modules/bluetooth/module-bluetooth-discover.c
-+++ b/src/modules/bluetooth/module-bluetooth-discover.c
-@@ -34,6 +34,17 @@ PA_MODULE_DESCRIPTION("Detect available Bluetooth daemon and load the correspond
- PA_MODULE_VERSION(PACKAGE_VERSION);
- PA_MODULE_LOAD_ONCE(true);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+PA_MODULE_USAGE("aptx_lib_name=<name of aptx library name>");
-+#endif
-+
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+static const char* const valid_modargs[] = {
-+ "aptx_lib_name",
-+ NULL
-+};
-+#endif
-+
- struct userdata {
- uint32_t bluez5_module_idx;
- uint32_t bluez4_module_idx;
-@@ -43,8 +54,30 @@ int pa__init(pa_module* m) {
- struct userdata *u;
- pa_module *mm;
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ pa_modargs *ma = NULL;
-+ const char *aptx_lib_name = NULL;
-+#endif
-+
- pa_assert(m);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-+ pa_log("Failed to parse module arguments");
-+ goto fail;
-+ }
-+
-+ if (pa_modargs_get_value(ma, "async", NULL))
-+ pa_log_warn("The 'async' argument is deprecated and does nothing.");
-+
-+
-+ aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL);
-+ if (aptx_lib_name)
-+ pa_load_aptx(aptx_lib_name);
-+ else
-+ pa_log("Failed to parse aptx_lib_name argument.");
-+#endif
-+
- m->userdata = u = pa_xnew0(struct userdata, 1);
- u->bluez5_module_idx = PA_INVALID_INDEX;
- u->bluez4_module_idx = PA_INVALID_INDEX;
-@@ -83,5 +116,9 @@ void pa__done(pa_module* m) {
- if (u->bluez4_module_idx != PA_INVALID_INDEX)
- pa_module_unload_by_index(m->core, u->bluez4_module_idx, true);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ pa_unload_aptx();
-+#endif
-+
- pa_xfree(u);
- }
-diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
-index b0b12f8..eba92c6 100644
---- a/src/modules/bluetooth/module-bluez4-device.c
-+++ b/src/modules/bluetooth/module-bluez4-device.c
-@@ -29,6 +29,9 @@
- #include <math.h>
- #include <linux/sockios.h>
- #include <arpa/inet.h>
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+#include <dlfcn.h>
-+#endif
-
- #include <pulse/rtclock.h>
- #include <pulse/sample.h>
-@@ -107,6 +110,10 @@ struct a2dp_info {
- bool sbc_initialized; /* Keep track if the encoder is initialized */
- size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ pa_bool_t aptx_initialized; /* Keep track if the encoder is initialized */
-+ void *aptx; /* Codec data */
-+#endif
- void* buffer; /* Codec transfer buffer */
- size_t buffer_size; /* Size of the buffer */
-
-@@ -207,6 +214,42 @@ enum {
-
- static int init_profile(struct userdata *u);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+void* (*aptx_new)(short endian);
-+int (*aptx_encode)(void* _state, void* _pcmL, void* _pcmR, void* _buffer);
-+
-+const char *aptx_new_name = "NewAptxEnc";
-+const char *aptx_encode_name = "aptxbtenc_encodestereo";
-+
-+static pa_bool_t pa_load_aptx_sym(void *handle )
-+{
-+ if (!handle)
-+ return FALSE;
-+
-+ aptx_new = (void* (*)(short endian))dlsym(handle, aptx_new_name);
-+
-+ if (aptx_new) {
-+ pa_log_debug("Load Symbol(%s)", aptx_new_name);
-+ } else {
-+ pa_log_debug("Fail to Load Symbol(%s)", aptx_new_name);
-+ return FALSE;
-+ }
-+
-+ aptx_encode = (int (*)(void* _state, void* _pcmL, void* _pcmR,
-+ void* _buffer))
-+ dlsym(handle, "aptxbtenc_encodestereo");
-+
-+ if (aptx_encode) {
-+ pa_log_debug("Load Symbol(%s)", aptx_encode_name);
-+ } else {
-+ pa_log_debug("Fail to Load Symbol(%s)", aptx_encode_name);
-+ return FALSE;
-+ }
-+
-+ return TRUE;
-+}
-+#endif
-+
- /* from IO thread */
- static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
- struct a2dp_info *a2dp;
-@@ -859,6 +902,123 @@ static int a2dp_process_render(struct userdata *u) {
- return ret;
- }
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+/* Run from IO thread */
-+static int a2dp_aptx_process_render(struct userdata *u) {
-+ struct a2dp_info *a2dp;
-+ size_t nbytes;
-+ void *d;
-+ const void *p;
-+ size_t to_write, to_encode;
-+ int ret = 0;
-+
-+ int pcmL[4],pcmR[4];
-+ int i=0;
-+ const short *mybuffer;
-+
-+ pa_assert(u);
-+ pa_assert(u->profile == PROFILE_A2DP);
-+ pa_assert(u->sink);
-+
-+ /* First, render some data */
-+ if (!u->write_memchunk.memblock)
-+ pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
-+
-+ pa_assert(u->write_memchunk.length == u->write_block_size);
-+
-+ a2dp_prepare_buffer(u);
-+
-+ a2dp = &u->a2dp;
-+
-+ /* Try to create a packet of the full MTU */
-+ p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index;
-+ to_encode = u->write_memchunk.length;
-+
-+ d = (uint8_t*) a2dp->buffer ;
-+ to_write = a2dp->buffer_size;
-+
-+ while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
-+ size_t written;
-+ ssize_t encoded;
-+
-+ mybuffer=(uint8_t *)p;
-+
-+ for (i = 0; i < 4; i += 1) {
-+ pcmL[i] = mybuffer[2*i];
-+ pcmR[i] = mybuffer[2*i+1];
-+ }
-+ /*(8 audio samples)16 bytes of audo data encoded to 4 bytes*/
-+ aptx_encode(a2dp->aptx, pcmL, pcmR, (short*)d);
-+
-+ encoded=16;
-+ written=4;
-+
-+ pa_assert_fp((size_t) encoded <= to_encode);
-+ pa_assert_fp((size_t) written <= to_write);
-+
-+ p = (const uint8_t*) p + encoded;
-+ to_encode -= encoded;
-+
-+ d = (uint8_t*) d + written;
-+ to_write -= written;
-+
-+ }
-+
-+ pa_memblock_release(u->write_memchunk.memblock);
-+
-+ pa_assert(to_encode == 0);
-+
-+ PA_ONCE_BEGIN {
-+ pa_log_debug("Using APTX encoder implementation");
-+ } PA_ONCE_END;
-+
-+ nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer;
-+
-+ for (;;) {
-+ ssize_t l;
-+
-+ l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type);
-+
-+ pa_assert(l != 0);
-+
-+ if (l < 0) {
-+
-+ if (errno == EINTR)
-+ /* Retry right away if we got interrupted */
-+ continue;
-+
-+ else if (errno == EAGAIN)
-+ /* Hmm, apparently the socket was not writable, give up for now */
-+ break;
-+
-+ pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
-+ ret = -1;
-+ break;
-+ }
-+
-+ pa_assert((size_t) l <= nbytes);
-+
-+ if ((size_t) l != nbytes) {
-+ pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
-+ (unsigned long long) l,
-+ (unsigned long long) nbytes);
-+ ret = -1;
-+ break;
-+ }
-+
-+ u->write_index += (uint64_t) u->write_memchunk.length;
-+ pa_memblock_unref(u->write_memchunk.memblock);
-+ pa_memchunk_reset(&u->write_memchunk);
-+
-+ ret = 1;
-+
-+ break;
-+ }
-+
-+ return ret;
-+}
-+#endif
-+
- static int a2dp_process_push(struct userdata *u) {
- int ret = 0;
- pa_memchunk memchunk;
-@@ -1104,8 +1264,13 @@ static void thread_func(void *userdata) {
- u->started_at = pa_rtclock_now();
-
- if (u->profile == PA_BLUEZ4_PROFILE_A2DP) {
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if ((n_written = a2dp_aptx_process_render(u)) < 0)
-+ goto io_fail;
-+#else
- if ((n_written = a2dp_process_render(u)) < 0)
- goto io_fail;
-+#endif
- } else {
- if ((n_written = hsp_process_render(u)) < 0)
- goto io_fail;
-@@ -1689,14 +1854,73 @@ static int add_source(struct userdata *u) {
- return 0;
- }
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+/* should be implemeted */
-+static int bt_transport_config_a2dp_for_aptx(struct userdata *u) {
-+ //const pa_bluetooth_transport *t;
-+ struct a2dp_info *a2dp = &u->a2dp;
-+ //a2dp_sbc_t *config;
-+
-+ //t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport);
-+ //pa_assert(t);
-+
-+ //config = (a2dp_sbc_t *) t->config;
-+
-+ u->sample_spec.format = PA_SAMPLE_S16LE;
-+
-+ if (!a2dp->aptx_initialized){
-+ #if __BYTE_ORDER==__LITTLE_ENDIAN
-+ a2dp->aptx = aptx_new(1);
-+ #elif __BYTE_ORDER==__BIG_ENDIAN
-+ a2dp->aptx = aptx_new(0);
-+ #else
-+ #error "Unknown byte order"
-+ #endif
-+ a2dp->aptx_initialized = TRUE;
-+ }
-+
-+ pa_log_debug("aptx Encoder is intialized !!");
-+
-+ u->write_block_size =(size_t)(u->write_link_mtu/(size_t)16) *16*4 ;
-+ u->read_block_size =(size_t)(u->read_link_mtu/(size_t)16) *16*4 ;
-+
-+ pa_log_info("APTX parameters write_block_size(%d),write_link_mtu(%d)",u->write_block_size,u->write_link_mtu);
-+ pa_log_info("APTX parameters read_block_size(%d),read_link_mtu(%d)",u->read_block_size,u->read_link_mtu);
-+
-+ return 0;
-+}
-+#endif
-+
- static void bt_transport_config_a2dp(struct userdata *u) {
- const pa_bluez4_transport *t;
- struct a2dp_info *a2dp = &u->a2dp;
- a2dp_sbc_t *config;
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ a2dp_aptx_t *aptx_config;
-+#endif
-
- t = u->transport;
- pa_assert(t);
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ if (t->codec == A2DP_CODEC_NON_A2DP) {
-+ aptx_config = (a2dp_aptx_t *) t->config;
-+ if (aptx_config->vendor_id[0] == APTX_VENDOR_ID0 &&
-+ aptx_config->vendor_id[1] == APTX_VENDOR_ID1 &&
-+ aptx_config->vendor_id[2] == APTX_VENDOR_ID2 &&
-+ aptx_config->vendor_id[3] == APTX_VENDOR_ID3 &&
-+ aptx_config->codec_id[0] == APTX_CODEC_ID0 &&
-+ aptx_config->codec_id[1] == APTX_CODEC_ID1 ){
-+ pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec");
-+
-+ return bt_transport_config_a2dp_for_aptx(u);
-+ } else {
-+ pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
-+ return -1;
-+ }
-+ }
-+#endif
-+
- config = (a2dp_sbc_t *) t->config;
-
- u->sample_spec.format = PA_SAMPLE_S16LE;
-@@ -2424,6 +2648,9 @@ int pa__init(pa_module *m) {
- struct userdata *u;
- const char *address, *path;
- pa_bluez4_device *device;
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ void *handle;
-+#endif
-
- pa_assert(m);
-
-@@ -2524,6 +2751,15 @@ int pa__init(pa_module *m) {
- u->msg->parent.process_msg = device_process_msg;
- u->msg->card = u->card;
-
-+#ifdef BLUETOOTH_APTX_SUPPORT
-+ handle = pa_aptx_get_handle();
-+
-+ if (handle) {
-+ pa_log_debug("Aptx Library loaded\n");
-+ pa_load_aptx_sym(handle);
-+ }
-+#endif
-+
- if (u->profile != PA_BLUEZ4_PROFILE_OFF)
- if (init_profile(u) < 0)
- goto off;
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 8 Aug 2013 11:23:38 +0300
-Subject: create pa_ready file - samsung
-
-Change-Id: I2146599f2e814be064864f8ca76879b761642f11
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/daemon/main.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/daemon/main.c b/src/daemon/main.c
-index e01371e..74cb45f 100644
---- a/src/daemon/main.c
-+++ b/src/daemon/main.c
-@@ -38,6 +38,8 @@
- #include <sys/types.h>
- #include <sys/stat.h>
-
-+#include <fcntl.h>
-+
- #ifdef HAVE_SYS_MMAN_H
- #include <sys/mman.h>
- #endif
-@@ -101,6 +103,7 @@
- #include "ltdl-bind-now.h"
- #include "server-lookup.h"
-
-+#define PA_READY "/tmp/.pa_ready"
- #ifdef HAVE_LIBWRAP
- /* Only one instance of these variables */
- int allow_severity = LOG_INFO;
-@@ -1145,6 +1148,8 @@ int main(int argc, char *argv[]) {
- #endif
-
- pa_log_info(_("Daemon startup complete."));
-+ /* broadcast if we're ready */
-+ creat(PA_READY, 0644);
-
- retval = 0;
- if (pa_mainloop_run(mainloop, &retval) < 0)
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 8 Aug 2013 11:24:25 +0300
-Subject: set alsa suspend timeout to zero - samsung
-
-Change-Id: Ie7c93c727d878226189f751efbd6e088ece7f36f
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/alsa/alsa-sink.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
-index 5a41cf6..03babb3 100644
---- a/src/modules/alsa/alsa-sink.c
-+++ b/src/modules/alsa/alsa-sink.c
-@@ -60,6 +60,7 @@
- #include "alsa-util.h"
- #include "alsa-sink.h"
-
-+#define ALSA_SUSPEND_ON_IDLE_TIMEOUT "0"
- /* #define DEBUG_TIMING */
-
- #define DEFAULT_DEVICE "default"
-@@ -2275,6 +2276,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
-
-+ /* Set Suspend timeout to ZERO to avoid noise */
-+ pa_log_info("Set suspend-on-idle timeout to ZERO to avoid noise");
-+ pa_proplist_sets(data.proplist, "module-suspend-on-idle.timeout", ALSA_SUSPEND_ON_IDLE_TIMEOUT);
- if (mapping) {
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 8 Aug 2013 11:27:44 +0300
-Subject: cope with possible infinite waiting in startup - samsung
-
-Change-Id: Ie7c74131e267f44324f031a953c15f81b0c31a07
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core-util.c | 19 ++++++++++++++++++-
- 1 file changed, 18 insertions(+), 1 deletion(-)
-
-diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
-index 0d9e354..1256a1e 100644
---- a/src/pulsecore/core-util.c
-+++ b/src/pulsecore/core-util.c
-@@ -1698,6 +1698,7 @@ static char* make_random_dir(mode_t m) {
- char *fn;
- size_t pathlen;
-
-+ srand (time(NULL));
- fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir());
- pathlen = strlen(fn);
-
-@@ -1763,6 +1764,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) {
- char *pa_get_runtime_dir(void) {
- char *d, *k = NULL, *p = NULL, *t = NULL, *mid;
- mode_t m;
-+ int retry_count = 100;
-
- /* The runtime directory shall contain dynamic data that needs NOT
- * to be kept across reboots and is usually private to the user,
-@@ -1823,6 +1825,21 @@ char *pa_get_runtime_dir(void) {
- for (;;) {
- /* OK, first let's check if the "runtime" symlink already exists */
-
-+ /* FIXME: This is recovery routine for infinite waiting issue such as below situation.
-+ * eg. 50f64052a5dbbe087c11dfac4effb63c-runtime -> /tmp/pulse-LDK8gTL6Dh9N
-+ 50f64052a5dbbe087c11dfac4effb63c-runtime.tmp -> /tmp/pulse-cDM1bQhObZ7O */
-+ if (retry_count-- == 0) {
-+ pa_log_error ("retry is over....do cleanup");
-+
-+ /* Remove original file */
-+ unlink (k);
-+
-+ /* Remove original.tmp file */
-+ t = pa_sprintf_malloc("%s.tmp", k);
-+ unlink (t);
-+ pa_xfree(t);
-+ t = NULL;
-+ }
- p = pa_readlink(k);
- if (!p) {
-
-@@ -3297,7 +3314,7 @@ const char *pa_get_temp_dir(void) {
- pa_is_path_absolute(t))
- return t;
-
-- return "/tmp";
-+ return "/tmp/pulseaudio";
- }
-
- int pa_open_cloexec(const char *fn, int flags, mode_t mode) {
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 8 Aug 2013 11:28:39 +0300
-Subject: use udev only for usb devices - samsung
-
-Change-Id: Ia8cd2f5eb5ebe5248af11906c67d572ede133b33
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 14 +++++++++++++-
- src/modules/alsa/module-alsa-card.c | 7 +++++++
- src/modules/module-udev-detect.c | 17 +++++++++++++++++
- 3 files changed, 37 insertions(+), 1 deletion(-)
-
-diff --git a/configure.ac b/configure.ac
-index ebff16c..13c8f6c 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1115,8 +1115,18 @@ AC_SUBST(HAVE_UDEV)
- AM_CONDITIONAL([HAVE_UDEV], [test "x$HAVE_UDEV" = x1])
- AS_IF([test "x$HAVE_UDEV" = "x1"], AC_DEFINE([HAVE_UDEV], 1, [Have UDEV.]))
-
--#### HAL compat support (optional, dependent on UDEV) ####
-+#### udev for usb _only_ support (optional, dependant on UDEV) ####
-+AC_ARG_ENABLE([udev_with_usb_only],
-+ AS_HELP_STRING([--enable-udev-with-usb-only],[Enable UDEV with only USB support]))
-+
-+AS_IF([test "x$enable_udev_with_usb_only" != "xyes"],
-+ [AS_IF([test "x$HAVE_UDEV" = "x1"], HAVE_UDEV_ONLY_USB=0, HAVE_UDEV_ONLY_USB=1)],
-+ HAVE_UDEV_ONLY_USB=1)
-
-+AM_CONDITIONAL([HAVE_UDEV_ONLY_USB], [test "x$HAVE_UDEV_ONLY_USB" = x1])
-+AS_IF([test "x$HAVE_UDEV_ONLY_USB" = "x1"], AC_DEFINE([HAVE_UDEV_ONLY_USB], 1, [Have usb only with udev.]))
-+
-+#### HAL compat support (optional, dependent on UDEV) ####
- AC_ARG_ENABLE([hal-compat],
- AS_HELP_STRING([--disable-hal-compat],[Disable optional HAL->udev transition compatibility support]))
-
-@@ -1511,6 +1521,7 @@ AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE
- AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no)
- AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no)
- AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no)
-+AS_IF([test "x$HAVE_UDEV_ONLY_USB" = "x1"], ENABLE_UDEV_ONLY_USB=yes, ENABLE_UDEV_ONLY_USB=no)
- AS_IF([test "x$HAVE_TCPWRAP" = "x1"], ENABLE_TCPWRAP=yes, ENABLE_TCPWRAP=no)
- AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], ENABLE_LIBSAMPLERATE=yes, ENABLE_LIBSAMPLERATE=no)
- AS_IF([test "x$HAVE_IPV6" = "x1"], ENABLE_IPV6=yes, ENABLE_IPV6=no)
-@@ -1566,6 +1577,7 @@ echo "
- headset backed: ${BLUETOOTH_HEADSET_BACKEND}
- Enable udev: ${ENABLE_UDEV}
- Enable HAL->udev compat: ${ENABLE_HAL_COMPAT}
-+ Enable udev usb only: ${ENABLE_UDEV_ONLY_USB}
- Enable systemd login: ${ENABLE_SYSTEMD}
- Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL}
- Enable TCP Wrappers: ${ENABLE_TCPWRAP}
-diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
-index cf54c0f..1e63230 100644
---- a/src/modules/alsa/module-alsa-card.c
-+++ b/src/modules/alsa/module-alsa-card.c
-@@ -678,6 +678,13 @@ int pa__init(pa_module *m) {
- u->use_ucm = false;
- #ifdef HAVE_UDEV
- fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
-+#ifdef ENABLE_UDEV_ONLY_USB
-+ pa_log("PULSE_PROFILE_SET = %s", fn);
-+ if (fn == NULL) {
-+ fn = strdup ("tizen_usb.conf");
-+ pa_log("(new) PULSE_PROFILE_SET = %s", fn);
-+ }
-+#endif
- #endif
-
- if (pa_modargs_get_value(u->modargs, "profile_set", NULL)) {
-diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
-index c28c867..1e8fb06 100644
---- a/src/modules/module-udev-detect.c
-+++ b/src/modules/module-udev-detect.c
-@@ -464,6 +464,23 @@ static void process_device(struct userdata *u, struct udev_device *dev) {
- return;
- }
-
-+ pa_log_debug ("devpath = %s", udev_device_get_devpath(dev));
-+ pa_log_debug ("subsystem = %s", udev_device_get_subsystem(dev));
-+ pa_log_debug ("devtype = %s", udev_device_get_devtype(dev));
-+ pa_log_debug ("syspath = %s", udev_device_get_syspath(dev));
-+ pa_log_debug ("sysname = %s", udev_device_get_sysname(dev));
-+ pa_log_debug ("sysnum = %s", udev_device_get_sysnum(dev));
-+ pa_log_debug ("devnode = %s", udev_device_get_devnode(dev));
-+ pa_log_debug ("parent subsystem = %s", udev_device_get_subsystem(udev_device_get_parent(dev)));
-+
-+#ifdef ENABLE_UDEV_ONLY_USB
-+ /* If parent's subsystem is not USB, return */
-+ if (!pa_streq(udev_device_get_subsystem(udev_device_get_parent(dev)), "usb")) {
-+ pa_log_debug("Ignoring %s, because it's parent subsystem is not a USB.", udev_device_get_devpath(dev));
-+ return;
-+ }
-+#endif
-+
- action = udev_device_get_action(dev);
-
- if (action && pa_streq(action, "remove"))
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Fri, 7 Mar 2014 17:49:42 +0200
-Subject: fixes and improvements to makefile and configure in - samsung
-
-Change-Id: Ic2338a8382fe45f9d509537950592c9c4aa83606
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 14 +++++++++++++-
- src/Makefile.am | 28 +++++++++-------------------
- 2 files changed, 22 insertions(+), 20 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 13c8f6c..870375f 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1539,6 +1539,11 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], EN
- AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no)
- AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no)
- AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no)
-+AS_IF([test "x$USE_SAMSUNG_POLICY" = "x1"], ENABLE_SAMSUNG_POLICY=yes, ENABLE_SAMSUNG_POLICY=no)
-+AS_IF([test "x$USE_DLOG" = "x1"], ENABLE_DLOG=yes, ENABLE_DLOG=no)
-+AS_IF([test "x$USE_PM_LOCK" = "x1"], ENABLE_PM_LOCK=yes, ENABLE_PM_LOCK=no)
-+AS_IF([test "x$USE_BT_PROFILE_SET" = "x1"], ENABLE_BT_PROFILE_SET=yes, ENABLE_BT_PROFILE_SET=no)
-+AS_IF([test "x$HAVE_BT_A2DP_APTX" = "x1"], HAVE_BT_A2DP_APTX=yes, HAVE_BT_A2DP_APTX=no)
-
- echo "
- ---{ $PACKAGE_NAME $VERSION }---
-@@ -1577,7 +1582,6 @@ echo "
- headset backed: ${BLUETOOTH_HEADSET_BACKEND}
- Enable udev: ${ENABLE_UDEV}
- Enable HAL->udev compat: ${ENABLE_HAL_COMPAT}
-- Enable udev usb only: ${ENABLE_UDEV_ONLY_USB}
- Enable systemd login: ${ENABLE_SYSTEMD}
- Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL}
- Enable TCP Wrappers: ${ENABLE_TCPWRAP}
-@@ -1604,6 +1608,14 @@ echo "
- Preopened modules: ${PREOPEN_MODS}
-
- Legacy Database Entry Support: ${ENABLE_LEGACY_DATABASE_ENTRY_FORMAT}
-+
-+ Tizen
-+ samsung policy: ${ENABLE_SAMSUNG_POLICY}
-+ dlog: ${ENABLE_DLOG}
-+ pmapi: ${ENABLE_PM_LOCK}
-+ bluetooth profile set: ${ENABLE_BT_PROFILE_SET}
-+ bluetooth aptx codec: ${HAVE_BT_A2DP_APTX}
-+ udev with usb only: ${ENABLE_UDEV_ONLY_USB}
- "
-
- if test "${ENABLE_SPEEX}" = "no" && test "${ENABLE_WEBRTC}" = "no" && test "${ENABLE_ADRIAN_EC}" = "no" ; then
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 3c062eb..a60b5bf 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -2078,10 +2078,9 @@ module_bluetooth_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
- module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
- module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD)
--if HAVE_BT_A2DP_APTX
--module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
--else
- module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS)
-+if HAVE_BT_A2DP_APTX
-+module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-
- # Bluetooth BlueZ 4 sink / source
-@@ -2096,22 +2095,17 @@ libbluez4_util_la_SOURCES = \
- modules/bluetooth/bluez4-util.h
- libbluez4_util_la_LDFLAGS = -avoid-version
- libbluez4_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
--
--if HAVE_BT_A2DP_APTX
--libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
--else
- libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-+if HAVE_BT_A2DP_APTX
-+libbluez4_util_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-
- module_bluez4_device_la_SOURCES = modules/bluetooth/module-bluez4-device.c modules/bluetooth/rtp.h
- module_bluez4_device_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_bluez4_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) libbluez4-util.la
--
--if HAVE_BT_A2DP_APTX
--module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) \
-- -DBLUETOOTH_APTX_SUPPORT
--else
- module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
-+if HAVE_BT_A2DP_APTX
-+module_bluez4_device_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-
- # Bluetooth BlueZ 5 sink / source
-@@ -2123,11 +2117,10 @@ libbluez5_util_la_SOURCES = \
- modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c
- libbluez5_util_la_LDFLAGS = -avoid-version
- libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
-+libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-
- if HAVE_BT_A2DP_APTX
--libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT
--else
--libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-+libbluez5_util_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-
- module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
-@@ -2141,10 +2134,7 @@ module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la
- module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS)
-
- if HAVE_BT_A2DP_APTX
--module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) \
-- -DBLUETOOTH_APTX_SUPPORT
--else
--module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS)
-+module_bluez5_device_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-
- # Apple Airtunes/RAOP
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Tue, 11 Mar 2014 12:47:52 +0200
-Subject: fix warning in gconf helper
-
-Change-Id: Id75cd24cfd1c0d62d4c227b6715dc0d9d5ea6b1f
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/gconf/gconf-helper.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
-index fbd8cfd..4681748 100644
---- a/src/modules/gconf/gconf-helper.c
-+++ b/src/modules/gconf/gconf-helper.c
-@@ -99,7 +99,9 @@ int main(int argc, char *argv[]) {
- GConfClient *client;
- GSList *modules, *m;
-
-+#if !defined(GLIB_VERSION_2_36)
- g_type_init();
-+#endif
-
- if (!(client = gconf_client_get_default()))
- goto fail;
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@helsinki.fi>
-Date: Wed, 8 Aug 2012 11:14:40 +0300
-Subject: volume ramp: add client api support for volume ramping
-
-Change-Id: I6afc7540af68400db54eec258bdf4a80c311bb69
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/map-file | 3 ++
- src/pulse/introspect.c | 74 +++++++++++++++++++++++++++++++++++++
- src/pulse/introspect.h | 9 +++++
- src/pulse/stream.c | 6 ++-
- src/pulsecore/native-common.h | 3 ++
- src/pulsecore/pdispatch.c | 2 +
- src/pulsecore/protocol-native.c | 81 ++++++++++++++++++++++++++++++++++++++++-
- src/pulsecore/tagstruct.c | 74 +++++++++++++++++++++++++++++++++++++
- src/pulsecore/tagstruct.h | 3 ++
- 9 files changed, 252 insertions(+), 3 deletions(-)
-
-diff --git a/src/map-file b/src/map-file
-index d51596c..20e577a 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -94,12 +94,15 @@ pa_context_set_event_callback;
- pa_context_set_name;
- pa_context_set_sink_input_mute;
- pa_context_set_sink_input_volume;
-+pa_context_set_sink_input_volume_ramp;
- pa_context_set_sink_mute_by_index;
- pa_context_set_sink_mute_by_name;
- pa_context_set_sink_port_by_index;
- pa_context_set_sink_port_by_name;
- pa_context_set_sink_volume_by_index;
- pa_context_set_sink_volume_by_name;
-+pa_context_set_sink_volume_ramp_by_index;
-+pa_context_set_sink_volume_ramp_by_name;
- pa_context_set_source_output_mute;
- pa_context_set_source_output_volume;
- pa_context_set_source_mute_by_index;
-diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
-index 2d54fdb..a72020a 100644
---- a/src/pulse/introspect.c
-+++ b/src/pulse/introspect.c
-@@ -1446,6 +1446,56 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name
- return o;
- }
-
-+pa_operation* pa_context_set_sink_volume_ramp_by_index(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) {
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+ uint32_t tag;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ pa_assert(ramp);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME_RAMP, &tag);
-+ pa_tagstruct_putu32(t, idx);
-+ pa_tagstruct_puts(t, NULL);
-+ pa_tagstruct_put_cvolume_ramp(t, ramp);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
-+pa_operation* pa_context_set_sink_volume_ramp_by_name(pa_context *c, const char *name, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) {
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+ uint32_t tag;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ pa_assert(name);
-+ pa_assert(ramp);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME_RAMP, &tag);
-+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-+ pa_tagstruct_puts(t, name);
-+ pa_tagstruct_put_cvolume_ramp(t, ramp);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
- pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
- pa_operation *o;
- pa_tagstruct *t;
-@@ -1543,6 +1593,30 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu
- return o;
- }
-
-+pa_operation* pa_context_set_sink_input_volume_ramp(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) {
-+ pa_operation *o;
-+ pa_tagstruct *t;
-+ uint32_t tag;
-+
-+ pa_assert(c);
-+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
-+ pa_assert(ramp);
-+
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-+
-+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-+
-+ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP, &tag);
-+ pa_tagstruct_putu32(t, idx);
-+ pa_tagstruct_puts(t, NULL);
-+ pa_tagstruct_put_cvolume_ramp(t, ramp);
-+ pa_pstream_send_tagstruct(c->pstream, t);
-+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-+
-+ return o;
-+}
-+
- pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
- pa_operation *o;
- pa_tagstruct *t;
-diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
-index 22fefed..127cade 100644
---- a/src/pulse/introspect.h
-+++ b/src/pulse/introspect.h
-@@ -287,6 +287,12 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int
- /** Set the mute switch of a sink device specified by its name */
- pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
-
-+/** Set the volume ramp of a sink device specified by its index */
-+pa_operation* pa_context_set_sink_volume_ramp_by_index(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata);
-+
-+/** Set the volume ramp of a sink device specified by its name */
-+pa_operation* pa_context_set_sink_volume_ramp_by_name(pa_context *c, const char *name, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata);
-+
- /** Suspend/Resume a sink. \since 0.9.7 */
- pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
-
-@@ -604,6 +610,9 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu
- /** Kill a sink input. */
- pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
-
-+/** Set the volume ramp of a sink input specified by its index */
-+pa_operation* pa_context_set_sink_input_volume_ramp(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata);
-+
- /** @} */
-
- /** @{ \name Source Outputs */
-diff --git a/src/pulse/stream.c b/src/pulse/stream.c
-index 8e35c29..501b5b6 100644
---- a/src/pulse/stream.c
-+++ b/src/pulse/stream.c
-@@ -1213,7 +1213,8 @@ static int create_stream(
- PA_STREAM_START_UNMUTED|
- PA_STREAM_FAIL_ON_SUSPEND|
- PA_STREAM_RELATIVE_VOLUME|
-- PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
-+ PA_STREAM_PASSTHROUGH|
-+ PA_STREAM_START_RAMP_MUTED)), PA_ERR_INVALID);
-
- PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
- PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
-@@ -1372,6 +1373,9 @@ static int create_stream(
- pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
- }
-
-+ if (s->context->version >= 22 && s->direction == PA_STREAM_PLAYBACK)
-+ pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_RAMP_MUTED));
-+
- pa_pstream_send_tagstruct(s->context->pstream, t);
- pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
-
-diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
-index dad82e0..301e2e7 100644
---- a/src/pulsecore/native-common.h
-+++ b/src/pulsecore/native-common.h
-@@ -176,6 +176,9 @@ enum {
- /* Supported since protocol v27 (3.0) */
- PA_COMMAND_SET_PORT_LATENCY_OFFSET,
-
-+ PA_COMMAND_SET_SINK_VOLUME_RAMP,
-+ PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP,
-+
- PA_COMMAND_MAX
- };
-
-diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
-index 4033240..8643378 100644
---- a/src/pulsecore/pdispatch.c
-+++ b/src/pulsecore/pdispatch.c
-@@ -190,6 +190,8 @@ static const char *command_names[PA_COMMAND_MAX] = {
- [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = "SET_SOURCE_OUTPUT_VOLUME",
- [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = "SET_SOURCE_OUTPUT_MUTE",
-
-+ [PA_COMMAND_SET_SINK_VOLUME_RAMP] = "SET_SINK_VOLUME_RAMP",
-+ [PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP] = "SET_SINK_INPUT_VOLUME_RAMP",
- };
-
- #endif
-diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
-index 41b4b50..4304cd4 100644
---- a/src/pulsecore/protocol-native.c
-+++ b/src/pulsecore/protocol-native.c
-@@ -294,6 +294,7 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
- static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
- static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
- static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-+static void command_set_volume_ramp(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,
-@@ -397,6 +398,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
-
- [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,
-
-+ [PA_COMMAND_SET_SINK_VOLUME_RAMP] = command_set_volume_ramp,
-+ [PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP] = command_set_volume_ramp,
-+
- [PA_COMMAND_EXTENSION] = command_extension
- };
-
-@@ -1994,7 +1998,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
- muted_set = false,
- fail_on_suspend = false,
- relative_volume = false,
-- passthrough = false;
-+ passthrough = false,
-+ ramp_muted = false;
-
- pa_sink_input_flags_t flags = 0;
- pa_proplist *p = NULL;
-@@ -2122,6 +2127,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
- }
- pa_idxset_put(formats, format, NULL);
- }
-+
- }
-
- if (n_formats == 0) {
-@@ -2134,6 +2140,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
- }
- }
-
-+ if (pa_tagstruct_get_boolean(t, &ramp_muted) < 0 ) {
-+ protocol_error(c);
-+ goto finish;
-+ }
-+
- if (!pa_tagstruct_eof(t)) {
- protocol_error(c);
- goto finish;
-@@ -2165,7 +2176,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
- (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
- (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
- (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
-- (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
-+ (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0) |
-+ (ramp_muted ? PA_SINK_INPUT_START_RAMP_MUTED : 0);
-
- /* Only since protocol version 15 there's a separate muted_set
- * flag. For older versions we synthesize it here */
-@@ -3797,6 +3809,71 @@ static void command_set_volume(
- pa_pstream_send_simple_ack(c->pstream, tag);
- }
-
-+static void command_set_volume_ramp(
-+ 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_cvolume_ramp ramp;
-+ pa_sink *sink = NULL;
-+ pa_sink_input *si = NULL;
-+ const char *name = NULL;
-+ const char *client_name;
-+
-+ pa_native_connection_assert_ref(c);
-+ pa_assert(t);
-+
-+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
-+ (command == PA_COMMAND_SET_SINK_VOLUME_RAMP && pa_tagstruct_gets(t, &name) < 0) ||
-+ (command == PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP && pa_tagstruct_gets(t, &name) < 0) ||
-+ pa_tagstruct_get_cvolume_ramp(t, &ramp) ||
-+ !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_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), 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);
-+
-+ switch (command) {
-+
-+ case PA_COMMAND_SET_SINK_VOLUME_RAMP:
-+ 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);
-+ break;
-+
-+ case PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP:
-+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
-+ break;
-+
-+ default:
-+ pa_assert_not_reached();
-+ }
-+
-+ CHECK_VALIDITY(c->pstream, sink || si, tag, PA_ERR_NOENTITY);
-+
-+ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
-+
-+ if (sink) {
-+ pa_log_debug("Client %s changes volume ramp of sink %s.", client_name, sink->name);
-+ pa_sink_set_volume_ramp(sink, &ramp, TRUE, TRUE);
-+ } else if (si) {
-+ pa_log_debug("Client %s changes volume ramp of sink input %s.", client_name, pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
-+ pa_sink_input_set_volume_ramp(si, &ramp, TRUE, TRUE);
-+ }
-+
-+ pa_pstream_send_simple_ack(c->pstream, tag);
-+}
-+
- static void command_set_mute(
- pa_pdispatch *pd,
- uint32_t command,
-diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
-index e51fcf2..aba0178 100644
---- a/src/pulsecore/tagstruct.c
-+++ b/src/pulsecore/tagstruct.c
-@@ -256,6 +256,35 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
- }
- }
-
-+void pa_tagstruct_put_cvolume_ramp(pa_tagstruct *t, const pa_cvolume_ramp *ramp) {
-+ unsigned i;
-+ pa_volume_ramp_type_t type;
-+ long length;
-+ pa_volume_t target;
-+
-+ pa_assert(t);
-+ pa_assert(ramp);
-+ extend(t, 2 + ramp->channels * (sizeof(pa_volume_ramp_type_t) + sizeof(long) + sizeof(pa_volume_t)));
-+
-+ t->data[t->length++] = PA_TAG_CVOLUME_RAMP;
-+ t->data[t->length++] = ramp->channels;
-+
-+ for (i = 0; i < ramp->channels; i++) {
-+ type = htonl(ramp->ramps[i].type);
-+ target = htonl(ramp->ramps[i].target);
-+ length = htonl(ramp->ramps[i].length);
-+
-+ memcpy(t->data + t->length, &type, sizeof(pa_volume_ramp_type_t));
-+ t->length += sizeof(pa_volume_ramp_type_t);
-+
-+ memcpy(t->data + t->length, &length, sizeof(long));
-+ t->length += sizeof(long);
-+
-+ memcpy(t->data + t->length, &target, sizeof(pa_volume_t));
-+ t->length += sizeof(pa_volume_t);
-+ }
-+}
-+
- void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) {
- uint32_t u;
- pa_assert(t);
-@@ -579,6 +608,51 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
- return 0;
- }
-
-+int pa_tagstruct_get_cvolume_ramp(pa_tagstruct *t, pa_cvolume_ramp *ramp) {
-+ unsigned i;
-+ pa_volume_ramp_type_t type;
-+ long length;
-+ pa_volume_t target;
-+ uint8_t *read_ptr;
-+
-+ pa_assert(t);
-+ pa_assert(ramp);
-+
-+ if (t->rindex+2 > t->length)
-+ return -1;
-+
-+ if (t->data[t->rindex] != PA_TAG_CVOLUME_RAMP)
-+ return -1;
-+
-+ if ((ramp->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
-+ return -1;
-+
-+ if (t->rindex+2+ramp->channels*(sizeof(pa_volume_ramp_type_t)+sizeof(long)+sizeof(pa_volume_t)) > t->length)
-+ return -1;
-+
-+ read_ptr = t->data + t->rindex + 2;
-+
-+ for (i = 0; i < ramp->channels; i++) {
-+ memcpy(&type, read_ptr, sizeof(pa_volume_ramp_type_t));
-+ ramp->ramps[i].type = (pa_volume_ramp_type_t) ntohl(type);
-+ read_ptr += sizeof(pa_volume_ramp_type_t);
-+
-+
-+ memcpy(&length, read_ptr, sizeof(long));
-+ ramp->ramps[i].length = (long) ntohl(length);
-+ read_ptr += sizeof(long);
-+
-+
-+ memcpy(&target, read_ptr, sizeof(pa_volume_t));
-+ ramp->ramps[i].target = (pa_volume_t) ntohl(target);
-+ read_ptr += sizeof(pa_volume_t);
-+ }
-+
-+ t->rindex = read_ptr - t->data;
-+
-+ return 0;
-+}
-+
- int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) {
- uint32_t u;
-
-diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
-index 9fef255..4634bd6 100644
---- a/src/pulsecore/tagstruct.h
-+++ b/src/pulsecore/tagstruct.h
-@@ -60,6 +60,7 @@ enum {
- PA_TAG_PROPLIST = 'P',
- PA_TAG_VOLUME = 'V',
- PA_TAG_FORMAT_INFO = 'f',
-+ PA_TAG_CVOLUME_RAMP = 'J'
- };
-
- pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
-@@ -86,6 +87,7 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
- void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
- void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume);
- void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f);
-+void pa_tagstruct_put_cvolume_ramp(pa_tagstruct *t, const pa_cvolume_ramp *ramp);
-
- int pa_tagstruct_get(pa_tagstruct *t, ...);
-
-@@ -104,5 +106,6 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
- int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
- int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v);
- int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f);
-+int pa_tagstruct_get_cvolume_ramp(pa_tagstruct *t, pa_cvolume_ramp *ramp);
-
- #endif
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Wed, 14 Aug 2013 15:00:52 +0300
-Subject: adjust default bluetooth profile to off
-
-Change-Id: I95ca525b0d20c1a864a0c66060767bb4c2160400
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/bluetooth/module-bluez4-device.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
-index eba92c6..6b775b7 100644
---- a/src/modules/bluetooth/module-bluez4-device.c
-+++ b/src/modules/bluetooth/module-bluez4-device.c
-@@ -2527,6 +2527,8 @@ static int add_card(struct userdata *u) {
- else
- pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile);
- }
-+ else
-+ pa_card_new_data_set_profile(&data, p->name);
-
- u->card = pa_card_new(u->core, &data);
- pa_card_new_data_done(&data);
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Wed, 17 Jul 2013 11:17:40 +0800
-Subject: Add bt_profile_set patch which fixed bt a2dp&hsp profile setting
- issues in mobile
-
-Change-Id: I9bc3649b02ab7ac56584211789a3ea18ff17fbb7
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- configure.ac | 13 +++++++++++++
- src/Makefile.am | 3 +++
- src/modules/bluetooth/module-bluez4-discover.c | 18 ++++++++++++++++++
- 3 files changed, 34 insertions(+)
-
-diff --git a/configure.ac b/configure.ac
-index 870375f..6a6bc83 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -679,6 +679,19 @@ fi
- AM_CONDITIONAL(USE_DLOG, test "x$USE_DLOG" = "xyes")
- dnl end --------------------------------------------------------------------
-
-+dnl use bt-profile-set --------------------------------------------------------------------------
-+AC_ARG_ENABLE(bt-profile-set, AC_HELP_STRING([--enable-bt-profile-set], [enable bt profile param]),
-+[
-+ case "${enableval}" in
-+ yes) USE_BT_PROFILE_SET=yes ;;
-+ no) USE_BT_PROFILE_SET=no ;;
-+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bt-profile-set) ;;
-+ esac
-+ ],[USE_BT_PROFILE_SET=no])
-+
-+AM_CONDITIONAL(USE_BT_PROFILE_SET, test "x$USE_BT_PROFILE_SET" = "xyes")
-+dnl end --------------------------------------------------------------------
-+
- #### atomic-ops ####
-
- AC_MSG_CHECKING([whether we need libatomic_ops])
-diff --git a/src/Makefile.am b/src/Makefile.am
-index a60b5bf..b09903f 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -2082,6 +2082,9 @@ module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS)
- if HAVE_BT_A2DP_APTX
- module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT
- endif
-+if USE_BT_PROFILE_SET
-+module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_PROFILE_SET
-+endif
-
- # Bluetooth BlueZ 4 sink / source
- module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c
-diff --git a/src/modules/bluetooth/module-bluez4-discover.c b/src/modules/bluetooth/module-bluez4-discover.c
-index 7673ba7..aaaa095 100644
---- a/src/modules/bluetooth/module-bluez4-discover.c
-+++ b/src/modules/bluetooth/module-bluez4-discover.c
-@@ -79,9 +79,27 @@ static pa_hook_result_t load_module_for_device(pa_bluez4_discovery *y, const pa_
- pa_module *m = NULL;
- char *args;
-
-+#ifdef BLUETOOTH_PROFILE_SET
-+ const char *profile = NULL;
-+
-+ if ((d->transports[PROFILE_A2DP] && d->transports[PROFILE_A2DP]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) {
-+ profile = "a2dp";
-+ } if ((d->transports[PROFILE_A2DP_SOURCE] && d->transports[PROFILE_A2DP_SOURCE]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) {
-+ profile = "a2dp_source";
-+ } else if ((d->transports[PROFILE_HFGW] && d->transports[PROFILE_HFGW]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) {
-+ profile = "hfgw";
-+ }
-+ if (!profile)
-+ return PA_HOOK_OK;
-+#endif
-+
- /* Oh, awesome, a new device has shown up and been connected! */
-
-+#ifdef BLUETOOTH_PROFILE_SET
-+ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" profile=\"%s\"", d->address, d->path, profile);
-+#else
- args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
-+#endif
-
- if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
- pa_modargs_get_value(u->modargs, "sco_source", NULL)) {
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Tue, 11 Jun 2013 17:03:27 +0300
-Subject: added pulseaudio.service file
-
-Change-Id: I8efef41060189f116be49b3455c588d67f045f82
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- pulseaudio.service | 10 ++++++++++
- 1 file changed, 10 insertions(+)
- create mode 100644 pulseaudio.service
-
-diff --git a/pulseaudio.service b/pulseaudio.service
-new file mode 100644
-index 0000000..f9d8592
---- /dev/null
-+++ b/pulseaudio.service
-@@ -0,0 +1,10 @@
-+[Unit]
-+Description=pulseaudio service
-+After=syslog.target dbus.service
-+
-+[Service]
-+Type=simple
-+ExecStart=/usr/bin/pulseaudio --system
-+
-+[Install]
-+WantedBy=multi-user.target
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 24 Feb 2014 12:38:31 +0200
-Subject: .gitignore: Add pulsecore-config.h
-
-Change-Id: I8409f1964e65e79669eaeb4930c6d536417a0b05
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- .gitignore | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/.gitignore b/.gitignore
-index f41ee72..b4a465e 100644
---- a/.gitignore
-+++ b/.gitignore
-@@ -34,3 +34,4 @@ missing
- mkinstalldirs
- stamp-*
- .dirstamp
-+pulsecore-config.h
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 16 Feb 2014 14:30:38 +0200
-Subject: pactl: Fix crash with older servers
-
-Servers older than 0.9.15 don't know anything about cards, and card
-operations will return a NULL pa_operation object when connected to
-that old server. We must check the pa_operation pointer before passing
-it to pa_operation_unref(), otherwise a NULL operation will result in
-a crash.
-
-Change-Id: Idc258479c077aafaff6254b76acb18034a158107
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/utils/pactl.c | 203 +++++++++++++++++++++++++++++++++++-------------------
- 1 file changed, 133 insertions(+), 70 deletions(-)
-
-diff --git a/src/utils/pactl.c b/src/utils/pactl.c
-index 1d8faa4..958d700 100644
---- a/src/utils/pactl.c
-+++ b/src/utils/pactl.c
-@@ -94,7 +94,7 @@ static pa_stream *sample_stream = NULL;
- static pa_sample_spec sample_spec;
- static pa_channel_map channel_map;
- static size_t sample_length = 0;
--static int actions = 1;
-+static int actions = 0;
-
- static bool nl = false;
-
-@@ -1046,6 +1046,7 @@ static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
- char *format = NULL;
- const char *state = NULL;
- int i = 0;
-+ pa_operation *o = NULL;
-
- while ((format = pa_split(str, ";", &state))) {
- pa_format_info *f = pa_format_info_from_string(pa_strip(format));
-@@ -1059,7 +1060,11 @@ static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
- pa_xfree(format);
- }
-
-- pa_operation_unref(pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL));
-+ o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-
- done:
- if (format)
-@@ -1185,7 +1190,10 @@ static void context_subscribe_callback(pa_context *c, pa_subscription_event_type
- }
-
- static void context_state_callback(pa_context *c, void *userdata) {
-+ pa_operation *o = NULL;
-+
- pa_assert(c);
-+
- switch (pa_context_get_state(c)) {
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
-@@ -1195,21 +1203,26 @@ static void context_state_callback(pa_context *c, void *userdata) {
- case PA_CONTEXT_READY:
- switch (action) {
- case STAT:
-- pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
-+ o = pa_context_stat(c, stat_callback, NULL);
- if (short_list_format)
- break;
-- actions++;
-+
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+ /* Fall through */
-
- case INFO:
-- pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
-+ o = pa_context_get_server_info(c, get_server_info_callback, NULL);
- break;
-
- case PLAY_SAMPLE:
-- pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
-+ o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
- break;
-
- case REMOVE_SAMPLE:
-- pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
-+ o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
- break;
-
- case UPLOAD_SAMPLE:
-@@ -1219,165 +1232,205 @@ static void context_state_callback(pa_context *c, void *userdata) {
- pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
- pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
- pa_stream_connect_upload(sample_stream, sample_length);
-+ actions++;
- break;
-
- case EXIT:
-- pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
-+ o = pa_context_exit_daemon(c, simple_callback, NULL);
- break;
-
- case LIST:
- if (list_type) {
- if (pa_streq(list_type, "modules"))
-- pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
-+ o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
- else if (pa_streq(list_type, "sinks"))
-- pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
-+ o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
- else if (pa_streq(list_type, "sources"))
-- pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
-+ o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
- else if (pa_streq(list_type, "sink-inputs"))
-- pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
-+ o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
- else if (pa_streq(list_type, "source-outputs"))
-- pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
-+ o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
- else if (pa_streq(list_type, "clients"))
-- pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
-+ o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
- else if (pa_streq(list_type, "samples"))
-- pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
-+ o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
- else if (pa_streq(list_type, "cards"))
-- pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
-+ o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
- else if (pa_streq(list_type, "nodes"))
-- pa_operation_unref(pa_ext_node_manager_read_nodes(c, node_list_callback, NULL));
-+ o = pa_ext_node_manager_read_nodes(c, node_list_callback, NULL);
- else
- pa_assert_not_reached();
- } else {
-- actions = 8;
-- pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
-- pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
-- pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
-- pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
-- pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
-- pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
-- pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
-- pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
-+ o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+ o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ o = NULL;
- }
- break;
-
- case MOVE_SINK_INPUT:
-- pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
-+ o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
- break;
-
- case MOVE_SOURCE_OUTPUT:
-- pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
-+ o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
- break;
-
- case LOAD_MODULE:
-- pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
-+ o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
- break;
-
- case UNLOAD_MODULE:
- if (module_name)
-- pa_operation_unref(pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL));
-+ o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
- else
-- pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
-+ o = pa_context_unload_module(c, module_index, simple_callback, NULL);
- break;
-
- case SUSPEND_SINK:
- if (sink_name)
-- pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
-+ o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
- else
-- pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
-+ o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
- break;
-
- case SUSPEND_SOURCE:
- if (source_name)
-- pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
-+ o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
- else
-- pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
-+ o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
- break;
-
- case SET_CARD_PROFILE:
-- pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
-+ o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
- break;
-
- case SET_SINK_PORT:
-- pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
-+ o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
- break;
-
- case SET_DEFAULT_SINK:
-- pa_operation_unref(pa_context_set_default_sink(c, sink_name, simple_callback, NULL));
-+ o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
- break;
-
- case SET_SOURCE_PORT:
-- pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
-+ o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
- break;
-
- case SET_DEFAULT_SOURCE:
-- pa_operation_unref(pa_context_set_default_source(c, source_name, simple_callback, NULL));
-+ o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
- break;
-
- case SET_SINK_MUTE:
- if (mute == TOGGLE_MUTE)
-- pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL));
-+ o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
- else
-- pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
-+ o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
- break;
-
- case SET_SOURCE_MUTE:
- if (mute == TOGGLE_MUTE)
-- pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL));
-+ o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
- else
-- pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
-+ o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
- break;
-
- case SET_SINK_INPUT_MUTE:
- if (mute == TOGGLE_MUTE)
-- pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL));
-+ o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
- else
-- pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
-+ o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
- break;
-
- case SET_SOURCE_OUTPUT_MUTE:
- if (mute == TOGGLE_MUTE)
-- pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL));
-+ o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
- else
-- pa_operation_unref(pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL));
-+ o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
- break;
-
- case SET_SINK_VOLUME:
- if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-- pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
-+ o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
- } else {
- pa_cvolume v;
- pa_cvolume_set(&v, 1, volume);
-- pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
-+ o = pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL);
- }
- break;
-
- case SET_SOURCE_VOLUME:
- if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-- pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
-+ o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
- } else {
- pa_cvolume v;
- pa_cvolume_set(&v, 1, volume);
-- pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
-+ o = pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL);
- }
- break;
-
- case SET_SINK_INPUT_VOLUME:
- if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-- pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
-+ o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
- } else {
- pa_cvolume v;
- pa_cvolume_set(&v, 1, volume);
-- pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
-+ o = pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL);
- }
- break;
-
- case SET_SOURCE_OUTPUT_VOLUME:
- if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-- pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
-+ o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
- } else {
- pa_cvolume v;
- pa_cvolume_set(&v, 1, volume);
-- pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL));
-+ o = pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL);
- }
- break;
-
-@@ -1386,25 +1439,24 @@ static void context_state_callback(pa_context *c, void *userdata) {
- break;
-
- case SET_PORT_LATENCY_OFFSET:
-- pa_operation_unref(pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL));
-+ o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
- break;
-
- case SUBSCRIBE:
- pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
-
-- pa_operation_unref(pa_context_subscribe(
-- c,
-- PA_SUBSCRIPTION_MASK_SINK|
-- PA_SUBSCRIPTION_MASK_SOURCE|
-- PA_SUBSCRIPTION_MASK_SINK_INPUT|
-- PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
-- PA_SUBSCRIPTION_MASK_MODULE|
-- PA_SUBSCRIPTION_MASK_CLIENT|
-- PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
-- PA_SUBSCRIPTION_MASK_SERVER|
-- PA_SUBSCRIPTION_MASK_CARD,
-- NULL,
-- NULL));
-+ o = pa_context_subscribe(c,
-+ PA_SUBSCRIPTION_MASK_SINK|
-+ PA_SUBSCRIPTION_MASK_SOURCE|
-+ PA_SUBSCRIPTION_MASK_SINK_INPUT|
-+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
-+ PA_SUBSCRIPTION_MASK_MODULE|
-+ PA_SUBSCRIPTION_MASK_CLIENT|
-+ PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
-+ PA_SUBSCRIPTION_MASK_SERVER|
-+ PA_SUBSCRIPTION_MASK_CARD,
-+ NULL,
-+ NULL);
- break;
- case NODE_CONNECT:
- pa_operation_unref(pa_ext_node_manager_connect_nodes(c,
-@@ -1422,6 +1474,17 @@ static void context_state_callback(pa_context *c, void *userdata) {
- default:
- pa_assert_not_reached();
- }
-+
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ }
-+
-+ if (actions == 0) {
-+ pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ }
-+
- break;
-
- case PA_CONTEXT_TERMINATED:
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 9 Mar 2014 10:50:23 +0200
-Subject: volume: Increase PA_SW_VOLUME_SNPRINT_DB_MAX
-
-10 bytes isn't enough for "-123.45 dB", including the terminating null
-byte.
-
-Change-Id: I865060befd01d3dde69556c1f90b7de55350501a
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulse/volume.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/pulse/volume.h b/src/pulse/volume.h
-index 3ffb573..cfdc85c 100644
---- a/src/pulse/volume.h
-+++ b/src/pulse/volume.h
-@@ -216,7 +216,7 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v);
- * any release without warning and without being considered API or ABI
- * breakage. You should not use this definition anywhere where it
- * might become part of an ABI. \since 0.9.15 */
--#define PA_SW_VOLUME_SNPRINT_DB_MAX 10
-+#define PA_SW_VOLUME_SNPRINT_DB_MAX 11
-
- /** Pretty print a volume but show dB values. \since 0.9.15 */
- char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 14:29:48 +0300
-Subject: sink-input, source-output: Fix mute saving
-
-Change-Id: I2298ab51a384c3ddfa33da3e941e03f5027b4d77
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 2 +-
- src/pulsecore/source-output.c | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index dff6324..d0509b3 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1470,7 +1470,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
- if (!i->muted == !mute) {
-- i->save_muted = i->save_muted || mute;
-+ i->save_muted |= save;
- return;
- }
-
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index 4e4b7e9..b12758a 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1062,7 +1062,7 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
- pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-
- if (!o->muted == !mute) {
-- o->save_muted = o->save_muted || mute;
-+ o->save_muted |= save;
- return;
- }
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 26 Mar 2014 10:25:17 +0200
-Subject: direction: Add a couple of direction helper functions
-
-Change-Id: I365acd7ce3e7abcbcb8a532c79016fca558238e8
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- doxygen/doxygen.conf.in | 1 +
- src/Makefile.am | 2 ++
- src/map-file | 2 ++
- src/pulse/direction.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- src/pulse/direction.h | 37 +++++++++++++++++++++++++++++++++++++
- src/pulse/pulseaudio.h | 5 +++--
- 6 files changed, 91 insertions(+), 2 deletions(-)
- create mode 100644 src/pulse/direction.c
- create mode 100644 src/pulse/direction.h
-
-diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in
-index a078e27..40cea8b 100644
---- a/doxygen/doxygen.conf.in
-+++ b/doxygen/doxygen.conf.in
-@@ -421,6 +421,7 @@ INPUT = \
- @srcdir@/../src/pulse/channelmap.h \
- @srcdir@/../src/pulse/context.h \
- @srcdir@/../src/pulse/def.h \
-+ @srcdir@/../src/pulse/direction.h \
- @srcdir@/../src/pulse/error.h \
- @srcdir@/../src/pulse/ext-stream-restore.h \
- @srcdir@/../src/pulse/ext-device-manager.h \
-diff --git a/src/Makefile.am b/src/Makefile.am
-index b09903f..fe6cc53 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -758,6 +758,7 @@ pulseinclude_HEADERS = \
- pulse/channelmap.h \
- pulse/context.h \
- pulse/def.h \
-+ pulse/direction.h \
- pulse/error.h \
- pulse/ext-device-manager.h \
- pulse/ext-device-restore.h \
-@@ -810,6 +811,7 @@ libpulse_la_SOURCES = \
- pulse/channelmap.c pulse/channelmap.h \
- pulse/context.c pulse/context.h \
- pulse/def.h \
-+ pulse/direction.c pulse/direction.h \
- pulse/error.c pulse/error.h \
- pulse/ext-device-manager.c pulse/ext-device-manager.h \
- pulse/ext-device-restore.c pulse/ext-device-restore.h \
-diff --git a/src/map-file b/src/map-file
-index 20e577a..fbf3f22 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -153,6 +153,8 @@ pa_cvolume_set_position;
- pa_cvolume_snprint;
- pa_cvolume_snprint_verbose;
- pa_cvolume_valid;
-+pa_direction_to_string;
-+pa_direction_valid;
- pa_encoding_to_string;
- pa_ext_device_manager_delete;
- pa_ext_device_manager_enable_role_device_priority_routing;
-diff --git a/src/pulse/direction.c b/src/pulse/direction.c
-new file mode 100644
-index 0000000..95f5e00
---- /dev/null
-+++ b/src/pulse/direction.c
-@@ -0,0 +1,46 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "direction.h"
-+
-+#include <pulsecore/i18n.h>
-+
-+int pa_direction_valid(pa_direction_t direction) {
-+ if (direction != PA_DIRECTION_INPUT
-+ && direction != PA_DIRECTION_OUTPUT
-+ && direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
-+ return 0;
-+
-+ return 1;
-+}
-+
-+const char *pa_direction_to_string(pa_direction_t direction) {
-+ pa_init_i18n();
-+
-+ if (direction == PA_DIRECTION_INPUT)
-+ return _("input");
-+ if (direction == PA_DIRECTION_OUTPUT)
-+ return _("output");
-+ if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
-+ return _("bidirectional");
-+
-+ return _("invalid");
-+}
-diff --git a/src/pulse/direction.h b/src/pulse/direction.h
-new file mode 100644
-index 0000000..127f07a
---- /dev/null
-+++ b/src/pulse/direction.h
-@@ -0,0 +1,37 @@
-+#ifndef foodirectionhfoo
-+#define foodirectionhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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/def.h>
-+
-+/** \file
-+ * Utility functions for \ref pa_direction_t. */
-+
-+/** Return non-zero if the given value is a valid direction (either input,
-+ * output or bidirectional). \since 6.0 */
-+int pa_direction_valid(pa_direction_t direction) PA_GCC_CONST;
-+
-+/** Return a textual representation of the direction. \since 6.0 */
-+const char *pa_direction_to_string(pa_direction_t direction);
-+
-+#endif
-diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
-index 21b7213..2e270dd 100644
---- a/src/pulse/pulseaudio.h
-+++ b/src/pulse/pulseaudio.h
-@@ -23,6 +23,7 @@
- USA.
- ***/
-
-+#include <pulse/direction.h>
- #include <pulse/mainloop-api.h>
- #include <pulse/sample.h>
- #include <pulse/format.h>
-@@ -49,8 +50,8 @@
-
- /** \file
- * Include all libpulse header files at once. The following files are
-- * included: \ref mainloop-api.h, \ref sample.h, \ref def.h, \ref
-- * context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref
-+ * included: \ref direction.h, \ref mainloop-api.h, \ref sample.h, \ref def.h,
-+ * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref
- * scache.h, \ref version.h, \ref error.h, \ref channelmap.h, \ref
- * operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
- * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h,
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 26 Mar 2014 13:15:12 +0200
-Subject: core-util: Add pa_join()
-
-Change-Id: I84ac0ee7a3097fce8ed9bad26b210fc97db9e9a7
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core-util.c | 18 ++++++++++++++++++
- src/pulsecore/core-util.h | 1 +
- 2 files changed, 19 insertions(+)
-
-diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
-index 1256a1e..508b3d3 100644
---- a/src/pulsecore/core-util.c
-+++ b/src/pulsecore/core-util.c
-@@ -1120,6 +1120,24 @@ char *pa_split_spaces(const char *c, const char **state) {
- return pa_xstrndup(current, l);
- }
-
-+char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter) {
-+ pa_strbuf *buf;
-+ unsigned i;
-+
-+ pa_assert(strings || n_strings == 0);
-+
-+ buf = pa_strbuf_new();
-+
-+ for (i = 0; i < n_strings; i++) {
-+ if (i > 0 && delimiter)
-+ pa_strbuf_puts(buf, delimiter);
-+
-+ pa_strbuf_puts(buf, strings[i]);
-+ }
-+
-+ return pa_strbuf_tostring_free(buf);
-+}
-+
- PA_STATIC_TLS_DECLARE(signame, pa_xfree);
-
- /* Return the name of an UNIX signal. Similar to Solaris sig2str() */
-diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
-index e6cb261..aba1863 100644
---- a/src/pulsecore/core-util.h
-+++ b/src/pulsecore/core-util.h
-@@ -108,6 +108,7 @@ static inline const char *pa_strna(const char *x) {
- char *pa_split(const char *c, const char*delimiters, const char **state);
- const char *pa_split_in_place(const char *c, const char*delimiters, int *n, const char **state);
- char *pa_split_spaces(const char *c, const char **state);
-+char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter);
-
- char *pa_strip_nl(char *s);
- char *pa_strip(char *s);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 26 Mar 2014 13:33:31 +0200
-Subject: dynarray: Add pa_dynarray_get_raw_array()
-
-Change-Id: I6e40c2a20586d13c99c9d98059e4dbb1d0e9e562
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/dynarray.c | 6 ++++++
- src/pulsecore/dynarray.h | 1 +
- 2 files changed, 7 insertions(+)
-
-diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
-index b207eca..b65fb62 100644
---- a/src/pulsecore/dynarray.c
-+++ b/src/pulsecore/dynarray.c
-@@ -93,3 +93,9 @@ unsigned pa_dynarray_size(pa_dynarray *array) {
-
- return array->n_entries;
- }
-+
-+void * const *pa_dynarray_get_raw_array(pa_dynarray *array) {
-+ pa_assert(array);
-+
-+ return array->data;
-+}
-diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
-index 04dd2d2..078acec 100644
---- a/src/pulsecore/dynarray.h
-+++ b/src/pulsecore/dynarray.h
-@@ -54,5 +54,6 @@ void *pa_dynarray_get(pa_dynarray *array, unsigned i);
- void *pa_dynarray_steal_last(pa_dynarray *array);
-
- unsigned pa_dynarray_size(pa_dynarray *array);
-+void * const *pa_dynarray_get_raw_array(pa_dynarray *array);
-
- #endif
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Thu, 19 Dec 2013 21:29:50 +0200
-Subject: dynarray: Add PA_DYNARRAY_FOREACH
-
-The PA_DYNARRAY_FOREACH macro requires that pa_dynarray_get() returns
-NULL if the index is out of bounds.
-
-Change-Id: If9db312516fbb079e8b67d94d35a44783ab3395a
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/dynarray.c | 4 +++-
- src/pulsecore/dynarray.h | 5 +++++
- src/pulsecore/tokenizer.c | 3 ---
- 3 files changed, 8 insertions(+), 4 deletions(-)
-
-diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
-index b65fb62..8dd8fab 100644
---- a/src/pulsecore/dynarray.c
-+++ b/src/pulsecore/dynarray.c
-@@ -74,7 +74,9 @@ void pa_dynarray_append(pa_dynarray *array, void *p) {
-
- void *pa_dynarray_get(pa_dynarray *array, unsigned i) {
- pa_assert(array);
-- pa_assert(i < array->n_entries);
-+
-+ if (i >= array->n_entries)
-+ return NULL;
-
- return array->data[i];
- }
-diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
-index 078acec..65030f2 100644
---- a/src/pulsecore/dynarray.h
-+++ b/src/pulsecore/dynarray.h
-@@ -48,6 +48,8 @@ pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb);
- void pa_dynarray_free(pa_dynarray *array);
-
- void pa_dynarray_append(pa_dynarray *array, void *p);
-+
-+/* Returns NULL if i is out of bounds. */
- void *pa_dynarray_get(pa_dynarray *array, unsigned i);
-
- /* Returns the removed item, or NULL if the array is empty. */
-@@ -56,4 +58,7 @@ void *pa_dynarray_steal_last(pa_dynarray *array);
- unsigned pa_dynarray_size(pa_dynarray *array);
- void * const *pa_dynarray_get_raw_array(pa_dynarray *array);
-
-+#define PA_DYNARRAY_FOREACH(elem, array, idx) \
-+ for ((idx) = 0; ((elem) = pa_dynarray_get(array, idx)); (idx)++)
-+
- #endif
-diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
-index 4c610e8..d71a7da 100644
---- a/src/pulsecore/tokenizer.c
-+++ b/src/pulsecore/tokenizer.c
-@@ -80,8 +80,5 @@ const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
-
- pa_assert(a);
-
-- if (i >= pa_dynarray_size(a))
-- return NULL;
--
- return pa_dynarray_get(a, i);
- }
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 21 May 2014 11:32:09 +0300
-Subject: dynarray: Add pa_dynarray_remove_last()
-
-Change-Id: I9098df96aac57a3ee2084061aa174f7ee02b1588
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/dynarray.c | 15 +++++++++++++++
- src/pulsecore/dynarray.h | 3 +++
- 2 files changed, 18 insertions(+)
-
-diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
-index 8dd8fab..c0b5fb0 100644
---- a/src/pulsecore/dynarray.c
-+++ b/src/pulsecore/dynarray.c
-@@ -81,6 +81,21 @@ void *pa_dynarray_get(pa_dynarray *array, unsigned i) {
- return array->data[i];
- }
-
-+int pa_dynarray_remove_last(pa_dynarray *array) {
-+ void *entry;
-+
-+ pa_assert(array);
-+
-+ entry = pa_dynarray_steal_last(array);
-+ if (!entry)
-+ return -PA_ERR_NOENTITY;
-+
-+ if (array->free_cb)
-+ array->free_cb(entry);
-+
-+ return 0;
-+}
-+
- void *pa_dynarray_steal_last(pa_dynarray *array) {
- pa_assert(array);
-
-diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
-index 65030f2..fd91433 100644
---- a/src/pulsecore/dynarray.h
-+++ b/src/pulsecore/dynarray.h
-@@ -52,6 +52,9 @@ void pa_dynarray_append(pa_dynarray *array, void *p);
- /* Returns NULL if i is out of bounds. */
- void *pa_dynarray_get(pa_dynarray *array, unsigned i);
-
-+/* Returns -PA_ERR_NOENTITY if the array is empty, and zero otherwise. */
-+int pa_dynarray_remove_last(pa_dynarray *array);
-+
- /* Returns the removed item, or NULL if the array is empty. */
- void *pa_dynarray_steal_last(pa_dynarray *array);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 21 May 2014 11:36:19 +0300
-Subject: dynarray: Add pa_dynarray_remove_all()
-
-Change-Id: I35079f8fe4b361221a1bdc1fececbe318bf3ee0e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/dynarray.c | 13 ++++++++-----
- src/pulsecore/dynarray.h | 2 ++
- 2 files changed, 10 insertions(+), 5 deletions(-)
-
-diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c
-index c0b5fb0..6dba743 100644
---- a/src/pulsecore/dynarray.c
-+++ b/src/pulsecore/dynarray.c
-@@ -47,13 +47,9 @@ pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb) {
- }
-
- void pa_dynarray_free(pa_dynarray *array) {
-- unsigned i;
- pa_assert(array);
-
-- if (array->free_cb)
-- for (i = 0; i < array->n_entries; i++)
-- array->free_cb(array->data[i]);
--
-+ pa_dynarray_remove_all(array);
- pa_xfree(array->data);
- pa_xfree(array);
- }
-@@ -105,6 +101,13 @@ void *pa_dynarray_steal_last(pa_dynarray *array) {
- return NULL;
- }
-
-+void pa_dynarray_remove_all(pa_dynarray *array) {
-+ pa_assert(array);
-+
-+ while (array->n_entries > 0)
-+ pa_dynarray_remove_last(array);
-+}
-+
- unsigned pa_dynarray_size(pa_dynarray *array) {
- pa_assert(array);
-
-diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h
-index fd91433..439f181 100644
---- a/src/pulsecore/dynarray.h
-+++ b/src/pulsecore/dynarray.h
-@@ -58,6 +58,8 @@ int pa_dynarray_remove_last(pa_dynarray *array);
- /* Returns the removed item, or NULL if the array is empty. */
- void *pa_dynarray_steal_last(pa_dynarray *array);
-
-+void pa_dynarray_remove_all(pa_dynarray *array);
-+
- unsigned pa_dynarray_size(pa_dynarray *array);
- void * const *pa_dynarray_get_raw_array(pa_dynarray *array);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 26 Mar 2014 13:58:40 +0200
-Subject: hashmap: Add pa_hashmap_remove_and_free()
-
-Change-Id: Ia3530c29cecf8964989aec4f0527fc982e80e34a
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/hashmap.c | 13 +++++++++++++
- src/pulsecore/hashmap.h | 7 +++++++
- 2 files changed, 20 insertions(+)
-
-diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
-index acac1e0..2cc03cb 100644
---- a/src/pulsecore/hashmap.c
-+++ b/src/pulsecore/hashmap.c
-@@ -207,6 +207,19 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
- return data;
- }
-
-+int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) {
-+ void *data;
-+
-+ pa_assert(h);
-+
-+ data = pa_hashmap_remove(h, key);
-+
-+ if (data && h->value_free_func)
-+ h->value_free_func(data);
-+
-+ return data ? 0 : -1;
-+}
-+
- void pa_hashmap_remove_all(pa_hashmap *h) {
- pa_assert(h);
-
-diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
-index e42732a..8042f7b 100644
---- a/src/pulsecore/hashmap.h
-+++ b/src/pulsecore/hashmap.h
-@@ -52,6 +52,13 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key);
- /* Returns the data of the entry while removing */
- void* pa_hashmap_remove(pa_hashmap *h, const void *key);
-
-+/* Removes the entry and frees the entry data. Returns a negative value if the
-+ * entry is not found. FIXME: This function shouldn't be needed.
-+ * pa_hashmap_remove() should free the entry data, and the current semantics of
-+ * pa_hashmap_remove() should be implemented by a function called
-+ * pa_hashmap_steal(). */
-+int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key);
-+
- /* Remove all entries but don't free the hashmap */
- void pa_hashmap_remove_all(pa_hashmap *h);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 26 Mar 2014 13:41:42 +0200
-Subject: device-port: Add pa_device_port.active
-
-In the Tizen volume API, I create and delete volume control objects
-for ports based on their state (only active ports have volume
-controls). Having the pa_device_port.active flag directly accessible
-is much nicer than figuring out the state by iterating through sinks
-and checking what their active port is. It's also safer to get a
-notification for a deactivated port before the port switch is
-executed, compared to using the SINK_PORT_CHANGED hook that is fired
-only after the port switch is complete.
-
-Change-Id: I3f7f8855721c8dc3a643708a72f6e35341ff7117
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core.h | 1 +
- src/pulsecore/device-port.c | 15 +++++++++++++++
- src/pulsecore/device-port.h | 4 ++++
- src/pulsecore/sink.c | 28 ++++++++++++++++++++++++----
- src/pulsecore/source.c | 29 +++++++++++++++++++++++++----
- 5 files changed, 69 insertions(+), 8 deletions(-)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index f268e42..e1cd18f 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -119,6 +119,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_CARD_PROFILE_ADDED,
- PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED,
- PA_CORE_HOOK_PORT_AVAILABLE_CHANGED,
-+ PA_CORE_HOOK_PORT_ACTIVE_CHANGED,
- PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED,
- PA_CORE_HOOK_MAX
- } pa_core_hook_t;
-diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
-index 0b65d5c..c183990 100644
---- a/src/pulsecore/device-port.c
-+++ b/src/pulsecore/device-port.c
-@@ -176,3 +176,18 @@ void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) {
- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
- pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p);
- }
-+
-+void pa_device_port_active_changed(pa_device_port *port, bool new_active) {
-+ bool old_active;
-+
-+ pa_assert(port);
-+
-+ old_active = port->active;
-+
-+ if (new_active == old_active)
-+ return;
-+
-+ port->active = new_active;
-+ pa_log_debug("Port %s %s.", port->name, new_active ? "activated" : "deactivated");
-+ pa_hook_fire(&port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED], port);
-+}
-diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
-index b10d554..2964900 100644
---- a/src/pulsecore/device-port.h
-+++ b/src/pulsecore/device-port.h
-@@ -48,6 +48,7 @@ struct pa_device_port {
-
- unsigned priority;
- pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */
-+ bool active;
-
- pa_proplist *proplist;
- pa_hashmap *profiles; /* Does not own the profiles */
-@@ -83,4 +84,7 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t available);
-
- void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset);
-
-+/* Called from sink.c and source.c only. */
-+void pa_device_port_active_changed(pa_device_port *port, bool new_active);
-+
- #endif
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 61656ab..11a6e77 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -669,6 +669,9 @@ void pa_sink_put(pa_sink* s) {
- else
- pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
-
-+ if (s->active_port)
-+ pa_device_port_active_changed(s->active_port, true);
-+
- pa_source_put(s->monitor_source);
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
-@@ -696,6 +699,9 @@ void pa_sink_unlink(pa_sink* s) {
- if (linked)
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
-
-+ if (s->active_port)
-+ pa_device_port_active_changed(s->active_port, false);
-+
- if (s->state != PA_SINK_UNLINKED)
- pa_namereg_unregister(s->core, s->name);
- pa_idxset_remove_by_data(s->core->sinks, s, NULL);
-@@ -3410,6 +3416,7 @@ size_t pa_sink_get_max_request(pa_sink *s) {
- /* Called from main context */
- int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
- pa_device_port *port;
-+ pa_device_port *old_port;
- int ret;
-
- pa_sink_assert_ref(s);
-@@ -3426,11 +3433,15 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
- if (!(port = pa_hashmap_get(s->ports, name)))
- return -PA_ERR_NOENTITY;
-
-- if (s->active_port == port) {
-+ old_port = s->active_port;
-+
-+ if (port == old_port) {
- s->save_port = s->save_port || save;
- return 0;
- }
-
-+ pa_device_port_active_changed(old_port, false);
-+
- if (s->flags & PA_SINK_DEFERRED_VOLUME) {
- struct sink_message_set_port msg = { .port = port, .ret = 0 };
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-@@ -3439,17 +3450,26 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
- else
- ret = s->set_port(s, port);
-
-- if (ret < 0)
-- return -PA_ERR_NOENTITY;
-+ if (ret < 0) {
-+ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name);
-+
-+ /* We don't know the real state of the device, but let's assume that
-+ * the old port is still active, because s->active_port is left to
-+ * point to the old port anyway. */
-+ pa_device_port_active_changed(old_port, true);
-+
-+ return ret;
-+ }
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-
-- pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
-+ pa_log_info("Changed port of sink %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name);
-
- s->active_port = port;
- s->save_port = save;
-
- pa_sink_set_latency_offset(s, s->active_port->latency_offset);
-+ pa_device_port_active_changed(port, true);
-
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
-
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index af4c6ec..d39193f 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -611,6 +611,9 @@ void pa_source_put(pa_source *s) {
- else
- pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
-
-+ if (s->active_port)
-+ pa_device_port_active_changed(s->active_port, true);
-+
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
- }
-@@ -631,6 +634,9 @@ void pa_source_unlink(pa_source *s) {
- if (linked)
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
-
-+ if (s->active_port)
-+ pa_device_port_active_changed(s->active_port, false);
-+
- if (s->state != PA_SOURCE_UNLINKED)
- pa_namereg_unregister(s->core, s->name);
- pa_idxset_remove_by_data(s->core->sources, s, NULL);
-@@ -2608,6 +2614,7 @@ size_t pa_source_get_max_rewind(pa_source *s) {
- /* Called from main context */
- int pa_source_set_port(pa_source *s, const char *name, bool save) {
- pa_device_port *port;
-+ pa_device_port *old_port;
- int ret;
-
- pa_source_assert_ref(s);
-@@ -2624,11 +2631,15 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
- if (!(port = pa_hashmap_get(s->ports, name)))
- return -PA_ERR_NOENTITY;
-
-- if (s->active_port == port) {
-+ old_port = s->active_port;
-+
-+ if (port == old_port) {
- s->save_port = s->save_port || save;
- return 0;
- }
-
-+ pa_device_port_active_changed(old_port, false);
-+
- if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
- struct source_message_set_port msg = { .port = port, .ret = 0 };
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-@@ -2637,16 +2648,26 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
- else
- ret = s->set_port(s, port);
-
-- if (ret < 0)
-- return -PA_ERR_NOENTITY;
-+ if (ret < 0) {
-+ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name);
-+
-+ /* We don't know the real state of the device, but let's assume that
-+ * the old port is still active, because s->active_port is left to
-+ * point to the old port anyway. */
-+ pa_device_port_active_changed(old_port, true);
-+
-+ return ret;
-+ }
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-
-- pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
-+ pa_log_info("Changed port of source %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name);
-
- s->active_port = port;
- s->save_port = save;
-
-+ pa_device_port_active_changed(port, true);
-+
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s);
-
- return 0;
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 7 Apr 2014 12:20:58 +0300
-Subject: sink, source: Assign to reference_volume from only one place
-
-Forcing all reference volume changes to go through
-set_reference_volume_direct() makes it easier to check where the
-reference volume is changed, and it also allows us to have only one
-place where notifications for changed reference volume are sent.
-
-Change-Id: I2e769b8a2b0d7031a02446dead8ca2e0c3402751
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 23 ++++++++++-------------
- src/pulsecore/sink.c | 30 ++++++++++++++++++++++++++----
- src/pulsecore/sink.h | 7 +++++++
- src/pulsecore/source-output.c | 23 ++++++++++-------------
- src/pulsecore/source.c | 30 ++++++++++++++++++++++++++----
- src/pulsecore/source.h | 7 +++++++
- 6 files changed, 86 insertions(+), 34 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index d0509b3..6596eeb 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1716,6 +1716,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
- * their volume - this function does all that by using recursion. */
- static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- pa_cvolume old_volume;
-+ pa_cvolume new_volume;
-
- pa_assert(i);
- pa_assert(dest);
-@@ -1787,25 +1788,21 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- * (sinks that use volume sharing should always have
- * soft_volume of 0 dB) */
-
-- old_volume = i->origin_sink->reference_volume;
--
-- i->origin_sink->reference_volume = root_sink->reference_volume;
-- pa_cvolume_remap(&i->origin_sink->reference_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
-+ new_volume = root_sink->reference_volume;
-+ pa_cvolume_remap(&new_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
-+ pa_sink_set_reference_volume_direct(i->origin_sink, &new_volume);
-
- i->origin_sink->real_volume = root_sink->real_volume;
- pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
-
- pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume));
-
-- /* Notify others about the changed sink volume. If you wonder whether
-- * i->origin_sink->set_volume() should be called somewhere, that's not
-- * the case, because sinks that use volume sharing shouldn't have any
-- * internal volume that set_volume() would update. If you wonder
-- * whether the thread_info variables should be synced, yes, they
-- * should, and it's done by the PA_SINK_MESSAGE_FINISH_MOVE message
-- * handler. */
-- if (!pa_cvolume_equal(&i->origin_sink->reference_volume, &old_volume))
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, i->origin_sink->index);
-+ /* If you wonder whether i->origin_sink->set_volume() should be called
-+ * somewhere, that's not the case, because sinks that use volume
-+ * sharing shouldn't have any internal volume that set_volume() would
-+ * update. If you wonder whether the thread_info variables should be
-+ * synced, yes, they should, and it's done by the
-+ * PA_SINK_MESSAGE_FINISH_MOVE message handler. */
-
- /* Recursively update origin sink inputs. */
- PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx)
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 11a6e77..e00dce5 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -2017,13 +2017,11 @@ static bool update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_ch
- pa_cvolume_remap(&volume, channel_map, &s->channel_map);
-
- reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
-- s->reference_volume = volume;
-+ pa_sink_set_reference_volume_direct(s, &volume);
-
- s->save_volume = (!reference_volume_changed && s->save_volume) || save;
-
-- if (reference_volume_changed)
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-- else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
-+ if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
- /* If the root sink's volume doesn't change, then there can't be any
- * changes in the other sinks in the sink tree either.
- *
-@@ -3905,3 +3903,27 @@ done:
-
- return out_formats;
- }
-+
-+/* Called from the main thread. */
-+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
-+ pa_cvolume old_volume;
-+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(s);
-+ pa_assert(volume);
-+
-+ old_volume = s->reference_volume;
-+
-+ if (pa_cvolume_equal(volume, &old_volume))
-+ return;
-+
-+ s->reference_volume = *volume;
-+ pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
-+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
-+ s->flags & PA_SINK_DECIBEL_VOLUME),
-+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
-+ s->flags & PA_SINK_DECIBEL_VOLUME));
-+
-+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+}
-diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
-index 576f34b..41a439b 100644
---- a/src/pulsecore/sink.h
-+++ b/src/pulsecore/sink.h
-@@ -512,6 +512,13 @@ void pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic);
-
- pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
-
-+/* Called from the main thread, from sink-input.c only. The normal way to set
-+ * the sink reference volume is to call pa_sink_set_volume(), but the flat
-+ * volume logic in sink-input.c needs also a function that doesn't do all the
-+ * extra stuff that pa_sink_set_volume() does. This function simply sets
-+ * s->reference_volume and fires change notifications. */
-+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
-+
- /* Verify that we called in IO context (aka 'thread context), or that
- * the sink is not yet set up, i.e. the thread not set up yet. See
- * pa_assert_io_context() in thread-mq.h for more information. */
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index b12758a..169d98d 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1263,6 +1263,7 @@ int pa_source_output_start_move(pa_source_output *o) {
- * their volume - this function does all that by using recursion. */
- static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- pa_cvolume old_volume;
-+ pa_cvolume new_volume;
-
- pa_assert(o);
- pa_assert(dest);
-@@ -1336,25 +1337,21 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- * (sources that use volume sharing should always have
- * soft_volume of 0 dB) */
-
-- old_volume = o->destination_source->reference_volume;
--
-- o->destination_source->reference_volume = root_source->reference_volume;
-- pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map);
-+ new_volume = root_source->reference_volume;
-+ pa_cvolume_remap(&new_volume, &root_source->channel_map, &o->destination_source->channel_map);
-+ pa_source_set_reference_volume_direct(o->destination_source, &new_volume);
-
- o->destination_source->real_volume = root_source->real_volume;
- pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
-
- pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
-
-- /* Notify others about the changed source volume. If you wonder whether
-- * o->destination_source->set_volume() should be called somewhere, that's not
-- * the case, because sources that use volume sharing shouldn't have any
-- * internal volume that set_volume() would update. If you wonder
-- * whether the thread_info variables should be synced, yes, they
-- * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message
-- * handler. */
-- if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume))
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index);
-+ /* If you wonder whether o->destination_source->set_volume() should be
-+ * called somewhere, that's not the case, because sources that use
-+ * volume sharing shouldn't have any internal volume that set_volume()
-+ * would update. If you wonder whether the thread_info variables should
-+ * be synced, yes, they should, and it's done by the
-+ * PA_SOURCE_MESSAGE_FINISH_MOVE message handler. */
-
- /* Recursively update origin source outputs. */
- PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index d39193f..f58bb44 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1562,13 +1562,11 @@ static bool update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_
- pa_cvolume_remap(&volume, channel_map, &s->channel_map);
-
- reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
-- s->reference_volume = volume;
-+ pa_source_set_reference_volume_direct(s, &volume);
-
- s->save_volume = (!reference_volume_changed && s->save_volume) || save;
-
-- if (reference_volume_changed)
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-- else if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
-+ if (!reference_volume_changed && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
- /* If the root source's volume doesn't change, then there can't be any
- * changes in the other source in the source tree either.
- *
-@@ -2899,3 +2897,27 @@ done:
-
- return out_formats;
- }
-+
-+/* Called from the main thread. */
-+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume) {
-+ pa_cvolume old_volume;
-+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(s);
-+ pa_assert(volume);
-+
-+ old_volume = s->reference_volume;
-+
-+ if (pa_cvolume_equal(volume, &old_volume))
-+ return;
-+
-+ s->reference_volume = *volume;
-+ pa_log_debug("The reference volume of source %s changed from %s to %s.", s->name,
-+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
-+ s->flags & PA_SOURCE_DECIBEL_VOLUME),
-+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
-+ s->flags & PA_SOURCE_DECIBEL_VOLUME));
-+
-+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+}
-diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
-index 5c74a51..6318595 100644
---- a/src/pulsecore/source.h
-+++ b/src/pulsecore/source.h
-@@ -426,6 +426,13 @@ bool pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next);
- void pa_source_invalidate_requested_latency(pa_source *s, bool dynamic);
- pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
-
-+/* Called from the main thread, from source-output.c only. The normal way to
-+ * set the source reference volume is to call pa_source_set_volume(), but the
-+ * flat volume logic in source-output.c needs also a function that doesn't do
-+ * all the extra stuff that pa_source_set_volume() does. This function simply
-+ * sets s->reference_volume and fires change notifications. */
-+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume);
-+
- #define pa_source_assert_io_context(s) \
- pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state))
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 7 Apr 2014 12:48:15 +0300
-Subject: sink-input, source-output: Assign to volume from only one place
-
-Forcing all volume changes to go through set_volume_direct() makes
-it easier to check where the stream volume is changed, and it also
-allows us to have only one place where notifications for changed
-volume are sent.
-
-Change-Id: Ie61bcc5747b419bb83c19a3ed78fd9f4d8a73cce
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 69 +++++++++++++++++++++----------------------
- src/pulsecore/sink-input.h | 7 +++++
- src/pulsecore/sink.c | 53 +++++++++------------------------
- src/pulsecore/source-output.c | 62 ++++++++++++++++++++------------------
- src/pulsecore/source-output.h | 7 +++++
- src/pulsecore/source.c | 54 +++++++++------------------------
- 6 files changed, 108 insertions(+), 144 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 6596eeb..83a0493 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1292,7 +1292,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
- return;
- }
-
-- i->volume = *volume;
-+ pa_sink_input_set_volume_direct(i, volume);
- i->save_volume = save;
-
- if (pa_sink_flat_volume_enabled(i->sink)) {
-@@ -1310,13 +1310,6 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
- /* Copy the new soft_volume to the thread_info struct */
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
- }
--
-- /* The volume changed, let's tell people so */
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- /* 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);
- }
-
- void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) {
-@@ -1715,7 +1708,6 @@ int pa_sink_input_start_move(pa_sink_input *i) {
- * then also the origin sink and all streams connected to it need to update
- * their volume - this function does all that by using recursion. */
- static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
-- pa_cvolume old_volume;
- pa_cvolume new_volume;
-
- pa_assert(i);
-@@ -1765,19 +1757,11 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- * always have volume_factor as soft_volume, so no change
- * should be needed) */
-
-- old_volume = i->volume;
-- pa_cvolume_reset(&i->volume, i->volume.channels);
-+ pa_cvolume_reset(&new_volume, i->volume.channels);
-+ pa_sink_input_set_volume_direct(i, &new_volume);
- pa_cvolume_reset(&i->reference_ratio, i->reference_ratio.channels);
- pa_assert(pa_cvolume_is_norm(&i->real_ratio));
- pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor));
--
-- /* Notify others about the changed sink input volume. */
-- if (!pa_cvolume_equal(&i->volume, &old_volume)) {
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-- }
- }
-
- /* Additionally, the origin sink volume needs updating:
-@@ -1809,8 +1793,6 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- update_volume_due_to_moving(origin_sink_input, dest);
-
- } else {
-- old_volume = i->volume;
--
- if (pa_sink_flat_volume_enabled(i->sink)) {
- /* Ok, so this is a regular stream, and flat volume is enabled. The
- * volume will have to be updated as follows:
-@@ -1822,9 +1804,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- * i->soft_volume := i->real_ratio * i->volume_factor
- * (handled later by pa_sink_set_volume) */
-
-- i->volume = i->sink->reference_volume;
-- pa_cvolume_remap(&i->volume, &i->sink->channel_map, &i->channel_map);
-- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
-+ new_volume = i->sink->reference_volume;
-+ pa_cvolume_remap(&new_volume, &i->sink->channel_map, &i->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
-+ pa_sink_input_set_volume_direct(i, &new_volume);
-
- } else {
- /* Ok, so this is a regular stream, and flat volume is disabled.
-@@ -1835,21 +1818,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
- * i->real_ratio := i->reference_ratio
- * i->soft_volume := i->real_ratio * i->volume_factor */
-
-- i->volume = i->reference_ratio;
-+ pa_sink_input_set_volume_direct(i, &i->reference_ratio);
- i->real_ratio = i->reference_ratio;
- pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
- }
--
-- /* Notify others about the changed sink input volume. */
-- if (!pa_cvolume_equal(&i->volume, &old_volume)) {
-- /* XXX: In case i->sink has flat volume enabled, then real_ratio
-- * and soft_volume are not updated yet. Let's hope that the
-- * callback implementation doesn't care about those variables... */
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-- }
- }
-
- /* If i->sink == dest, then recursion has finished, and we can finally call
-@@ -2333,3 +2305,28 @@ int pa_sink_input_update_rate(pa_sink_input *i) {
-
- return 0;
- }
-+
-+/* Called from the main thread. */
-+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) {
-+ pa_cvolume old_volume;
-+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(i);
-+ pa_assert(volume);
-+
-+ old_volume = i->volume;
-+
-+ if (pa_cvolume_equal(volume, &old_volume))
-+ return;
-+
-+ i->volume = *volume;
-+ pa_log_debug("The volume of sink input %u changed from %s to %s.", i->index,
-+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &i->channel_map, true),
-+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &i->channel_map, true));
-+
-+ if (i->volume_changed)
-+ i->volume_changed(i);
-+
-+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-+}
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index deea348..4e7b229 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -431,6 +431,13 @@ bool pa_sink_input_process_underrun(pa_sink_input *i);
-
- pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
-
-+/* Called from the main thread, from sink.c only. The normal way to set the
-+ * sink input volume is to call pa_sink_input_set_volume(), but the flat volume
-+ * logic in sink.c needs also a function that doesn't do all the extra stuff
-+ * that pa_sink_input_set_volume() does. This function simply sets i->volume
-+ * and fires change notifications. */
-+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume);
-+
- #define pa_sink_input_assert_io_context(s) \
- pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index e00dce5..94046b1 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -1898,20 +1898,13 @@ static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_chan
- PA_IDXSET_FOREACH(i, s->inputs, idx) {
- if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
- if (pa_sink_flat_volume_enabled(s)) {
-- pa_cvolume old_volume = i->volume;
-+ pa_cvolume new_input_volume;
-
- /* Follow the root sink's real volume. */
-- i->volume = *new_volume;
-- pa_cvolume_remap(&i->volume, channel_map, &i->channel_map);
-+ new_input_volume = *new_volume;
-+ pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map);
-+ pa_sink_input_set_volume_direct(i, &new_input_volume);
- compute_reference_ratio(i);
--
-- /* The volume changed, let's tell people so */
-- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-- }
- }
-
- update_real_volume(i->origin_sink, new_volume, channel_map);
-@@ -1966,7 +1959,7 @@ static void propagate_reference_volume(pa_sink *s) {
- * sink input volumes accordingly */
-
- PA_IDXSET_FOREACH(i, s->inputs, idx) {
-- pa_cvolume old_volume;
-+ pa_cvolume new_volume;
-
- if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
- propagate_reference_volume(i->origin_sink);
-@@ -1977,24 +1970,14 @@ static void propagate_reference_volume(pa_sink *s) {
- continue;
- }
-
-- old_volume = i->volume;
--
- /* This basically calculates:
- *
- * i->volume := s->reference_volume * i->reference_ratio */
-
-- i->volume = s->reference_volume;
-- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
-- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
--
-- /* The volume changed, let's tell people so */
-- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
--
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-- }
-+ new_volume = s->reference_volume;
-+ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
-+ pa_sink_input_set_volume_direct(i, &new_volume);
- }
- }
-
-@@ -2215,7 +2198,7 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
- if (pa_sink_flat_volume_enabled(s)) {
-
- PA_IDXSET_FOREACH(i, s->inputs, idx) {
-- pa_cvolume old_volume = i->volume;
-+ pa_cvolume new_volume;
-
- /* 2. Since the sink's reference and real volumes are equal
- * now our ratios should be too. */
-@@ -2229,18 +2212,10 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
- * i->volume = s->reference_volume * i->reference_ratio
- *
- * This is identical to propagate_reference_volume() */
-- i->volume = s->reference_volume;
-- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
-- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
--
-- /* Notify if something changed */
-- if (!pa_cvolume_equal(&old_volume, &i->volume)) {
--
-- if (i->volume_changed)
-- i->volume_changed(i);
--
-- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-- }
-+ new_volume = s->reference_volume;
-+ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
-+ pa_sink_input_set_volume_direct(i, &new_volume);
-
- if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
- propagate_real_volume(i->origin_sink, old_real_volume);
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index 169d98d..761323e 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -972,7 +972,7 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume,
- return;
- }
-
-- o->volume = *volume;
-+ pa_source_output_set_volume_direct(o, volume);
- o->save_volume = save;
-
- if (pa_source_flat_volume_enabled(o->source)) {
-@@ -1262,7 +1262,6 @@ int pa_source_output_start_move(pa_source_output *o) {
- * then also the origin source and all streams connected to it need to update
- * their volume - this function does all that by using recursion. */
- static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
-- pa_cvolume old_volume;
- pa_cvolume new_volume;
-
- pa_assert(o);
-@@ -1314,19 +1313,11 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- * always have volume_factor as soft_volume, so no change
- * should be needed) */
-
-- old_volume = o->volume;
-- pa_cvolume_reset(&o->volume, o->volume.channels);
-+ pa_cvolume_reset(&new_volume, o->volume.channels);
-+ pa_source_output_set_volume_direct(o, &new_volume);
- pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
- pa_assert(pa_cvolume_is_norm(&o->real_ratio));
- pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
--
-- /* Notify others about the changed source output volume. */
-- if (!pa_cvolume_equal(&o->volume, &old_volume)) {
-- if (o->volume_changed)
-- o->volume_changed(o);
--
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-- }
- }
-
- /* Additionally, the origin source volume needs updating:
-@@ -1358,8 +1349,6 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- update_volume_due_to_moving(destination_source_output, dest);
-
- } else {
-- old_volume = o->volume;
--
- if (pa_source_flat_volume_enabled(o->source)) {
- /* Ok, so this is a regular stream, and flat volume is enabled. The
- * volume will have to be updated as follows:
-@@ -1371,9 +1360,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- * o->soft_volume := o->real_ratio * o->volume_factor
- * (handled later by pa_source_set_volume) */
-
-- o->volume = o->source->reference_volume;
-- pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map);
-- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
-+ new_volume = o->source->reference_volume;
-+ pa_cvolume_remap(&new_volume, &o->source->channel_map, &o->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
-+ pa_source_output_set_volume_direct(o, &new_volume);
-
- } else {
- /* Ok, so this is a regular stream, and flat volume is disabled.
-@@ -1384,21 +1374,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- * o->real_ratio := o->reference_ratio
- * o->soft_volume := o->real_ratio * o->volume_factor */
-
-- o->volume = o->reference_ratio;
-+ pa_source_output_set_volume_direct(o, &o->reference_ratio);
- o->real_ratio = o->reference_ratio;
- pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
- }
--
-- /* Notify others about the changed source output volume. */
-- if (!pa_cvolume_equal(&o->volume, &old_volume)) {
-- /* XXX: In case o->source has flat volume enabled, then real_ratio
-- * and soft_volume are not updated yet. Let's hope that the
-- * callback implementation doesn't care about those variables... */
-- if (o->volume_changed)
-- o->volume_changed(o);
--
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-- }
- }
-
- /* If o->source == dest, then recursion has finished, and we can finally call
-@@ -1692,3 +1671,28 @@ int pa_source_output_update_rate(pa_source_output *o) {
-
- return 0;
- }
-+
-+/* Called from the main thread. */
-+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume) {
-+ pa_cvolume old_volume;
-+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(o);
-+ pa_assert(volume);
-+
-+ old_volume = o->volume;
-+
-+ if (pa_cvolume_equal(volume, &old_volume))
-+ return;
-+
-+ o->volume = *volume;
-+ pa_log_debug("The volume of source output %u changed from %s to %s.", o->index,
-+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &o->channel_map, true),
-+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &o->channel_map, true));
-+
-+ if (o->volume_changed)
-+ o->volume_changed(o);
-+
-+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-+}
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index 102fb8b..27d6fd4 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -353,6 +353,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
-
- pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
-
-+/* Called from the main thread, from source.c only. The normal way to set the
-+ * source output volume is to call pa_source_output_set_volume(), but the flat
-+ * volume logic in source.c needs also a function that doesn't do all the extra
-+ * stuff that pa_source_output_set_volume() does. This function simply sets
-+ * o->volume and fires change notifications. */
-+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume);
-+
- #define pa_source_output_assert_io_context(s) \
- pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state))
-
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index f58bb44..c8165e6 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1443,20 +1443,13 @@ static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_ch
- PA_IDXSET_FOREACH(o, s->outputs, idx) {
- if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
- if (pa_source_flat_volume_enabled(s)) {
-- pa_cvolume old_volume = o->volume;
-+ pa_cvolume new_output_volume;
-
- /* Follow the root source's real volume. */
-- o->volume = *new_volume;
-- pa_cvolume_remap(&o->volume, channel_map, &o->channel_map);
-+ new_output_volume = *new_volume;
-+ pa_cvolume_remap(&new_output_volume, channel_map, &o->channel_map);
-+ pa_source_output_set_volume_direct(o, &new_output_volume);
- compute_reference_ratio(o);
--
-- /* The volume changed, let's tell people so */
-- if (!pa_cvolume_equal(&old_volume, &o->volume)) {
-- if (o->volume_changed)
-- o->volume_changed(o);
--
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-- }
- }
-
- update_real_volume(o->destination_source, new_volume, channel_map);
-@@ -1511,7 +1504,7 @@ static void propagate_reference_volume(pa_source *s) {
- * source output volumes accordingly */
-
- PA_IDXSET_FOREACH(o, s->outputs, idx) {
-- pa_cvolume old_volume;
-+ pa_cvolume new_volume;
-
- if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
- propagate_reference_volume(o->destination_source);
-@@ -1522,24 +1515,14 @@ static void propagate_reference_volume(pa_source *s) {
- continue;
- }
-
-- old_volume = o->volume;
--
- /* This basically calculates:
- *
- * o->volume := o->reference_volume * o->reference_ratio */
-
-- o->volume = s->reference_volume;
-- pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
-- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
--
-- /* The volume changed, let's tell people so */
-- if (!pa_cvolume_equal(&old_volume, &o->volume)) {
--
-- if (o->volume_changed)
-- o->volume_changed(o);
--
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-- }
-+ new_volume = s->reference_volume;
-+ pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
-+ pa_source_output_set_volume_direct(o, &new_volume);
- }
- }
-
-@@ -1732,9 +1715,8 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum
- }
-
- if (pa_source_flat_volume_enabled(s)) {
--
- PA_IDXSET_FOREACH(o, s->outputs, idx) {
-- pa_cvolume old_volume = o->volume;
-+ pa_cvolume new_volume;
-
- /* 2. Since the source's reference and real volumes are equal
- * now our ratios should be too. */
-@@ -1748,18 +1730,10 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum
- * o->volume = s->reference_volume * o->reference_ratio
- *
- * This is identical to propagate_reference_volume() */
-- o->volume = s->reference_volume;
-- pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
-- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
--
-- /* Notify if something changed */
-- if (!pa_cvolume_equal(&old_volume, &o->volume)) {
--
-- if (o->volume_changed)
-- o->volume_changed(o);
--
-- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-- }
-+ new_volume = s->reference_volume;
-+ pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
-+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
-+ pa_source_output_set_volume_direct(o, &new_volume);
-
- if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
- propagate_real_volume(o->destination_source, old_real_volume);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 14:03:24 +0300
-Subject: sink, source: Return early from set_mute()
-
-This avoids redundant set_mute() callback calls.
-
-Some logging was added too.
-
-Change-Id: I10188c3b43d61fe751abe0f9940015af35c4a137
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink.c | 13 +++++++++----
- src/pulsecore/source.c | 13 +++++++++----
- 2 files changed, 18 insertions(+), 8 deletions(-)
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 94046b1..3eed550 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -2288,16 +2288,21 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
- pa_assert(PA_SINK_IS_LINKED(s->state));
-
- old_muted = s->muted;
-+
-+ if (mute == old_muted) {
-+ s->save_muted |= save;
-+ return;
-+ }
-+
- s->muted = mute;
-- s->save_muted = (old_muted == s->muted && s->save_muted) || save;
-+ s->save_muted = save;
-
- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
- s->set_mute(s);
-
-+ pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_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);
-+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- }
-
- /* Called from main thread */
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index c8165e6..4f4aea3 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1806,16 +1806,21 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
- pa_assert(PA_SOURCE_IS_LINKED(s->state));
-
- old_muted = s->muted;
-+
-+ if (mute == old_muted) {
-+ s->save_muted |= save;
-+ return;
-+ }
-+
- s->muted = mute;
-- s->save_muted = (old_muted == s->muted && s->save_muted) || save;
-+ s->save_muted = save;
-
- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
- s->set_mute(s);
-
-+ pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_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);
-+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- }
-
- /* Called from main thread */
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 14:13:56 +0300
-Subject: sink-input, source-output: Add logging to set_mute()
-
-Change-Id: Ie10aa76cae75c7b6a52ea4a9039b8e3e37a748b2
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink-input.c | 8 +++++++-
- src/pulsecore/source-output.c | 8 +++++++-
- 2 files changed, 14 insertions(+), 2 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 83a0493..f706acc 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1458,16 +1458,22 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool
-
- /* Called from main context */
- void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
-+ bool old_mute;
-+
- pa_sink_input_assert_ref(i);
- pa_assert_ctl_context();
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
-- if (!i->muted == !mute) {
-+ old_mute = i->muted;
-+
-+ if (mute == old_mute) {
- i->save_muted |= save;
- return;
- }
-
- i->muted = mute;
-+ pa_log_debug("The mute of sink input %u changed from %s to %s.", i->index, pa_yes_no(old_mute), pa_yes_no(mute));
-+
- i->save_muted = save;
-
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index 761323e..bb89384 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1057,16 +1057,22 @@ pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume,
-
- /* Called from main context */
- void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
-+ bool old_mute;
-+
- pa_source_output_assert_ref(o);
- pa_assert_ctl_context();
- pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-
-- if (!o->muted == !mute) {
-+ old_mute = o->muted;
-+
-+ if (mute == old_mute) {
- o->save_muted |= save;
- return;
- }
-
- o->muted = mute;
-+ pa_log_debug("The mute of source output %u changed from %s to %s.", o->index, pa_yes_no(old_mute), pa_yes_no(mute));
-+
- o->save_muted = save;
-
- pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 14:43:23 +0300
-Subject: sink, source: Allow calling set_mute() during initialization
-
-Currently the alsa sink and source write directly to s->muted during
-initialization, but I think it's better to avoid direct writes, and
-use the set_mute() function instead, because that makes it easier to
-figure out where s->muted is modified. This patch prevents the
-set_mute() call from crashing in the state assertion.
-
-Change-Id: I12220fb2668723931bebbe1484f115016f1edf25
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink.c | 4 +++-
- src/pulsecore/source.c | 4 +++-
- 2 files changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 3eed550..7eb4cb4 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -2285,7 +2285,6 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
-
- pa_sink_assert_ref(s);
- pa_assert_ctl_context();
-- pa_assert(PA_SINK_IS_LINKED(s->state));
-
- old_muted = s->muted;
-
-@@ -2300,6 +2299,9 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
- s->set_mute(s);
-
-+ if (!PA_SINK_IS_LINKED(s->state))
-+ return;
-+
- pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 4f4aea3..3b6ad44 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1803,7 +1803,6 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
-
- pa_source_assert_ref(s);
- pa_assert_ctl_context();
-- pa_assert(PA_SOURCE_IS_LINKED(s->state));
-
- old_muted = s->muted;
-
-@@ -1818,6 +1817,9 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
- s->set_mute(s);
-
-+ if (!PA_SOURCE_IS_LINKED(s->state))
-+ return;
-+
- pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 15:13:08 +0300
-Subject: echo-cancel: Remove redundant get_mute() callback
-
-The callback just called pa_source_output_get_mute(), which doesn't
-have any side effects, and the return value wasn't used either, so
-the callback was essentially a no-op.
-
-Change-Id: Ic16824b06393f59b55087842da64c7d09035b9e8
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/echo-cancel/module-echo-cancel.c | 15 ---------------
- 1 file changed, 15 deletions(-)
-
-diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
-index 29eed13..8f791ce 100644
---- a/src/modules/echo-cancel/module-echo-cancel.c
-+++ b/src/modules/echo-cancel/module-echo-cancel.c
-@@ -656,20 +656,6 @@ static void sink_set_mute_cb(pa_sink *s) {
- pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
- }
-
--/* Called from main context */
--static void source_get_mute_cb(pa_source *s) {
-- struct userdata *u;
--
-- pa_source_assert_ref(s);
-- pa_assert_se(u = s->userdata);
--
-- if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-- !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-- return;
--
-- pa_source_output_get_mute(u->source_output);
--}
--
- /* Called from source I/O thread context. */
- static void apply_diff_time(struct userdata *u, int64_t diff_time) {
- int64_t diff;
-@@ -1857,7 +1843,6 @@ int pa__init(pa_module*m) {
- u->source->parent.process_msg = source_process_msg_cb;
- u->source->set_state = source_set_state_cb;
- u->source->update_requested_latency = source_update_requested_latency_cb;
-- pa_source_set_get_mute_callback(u->source, source_get_mute_cb);
- pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
- if (!u->use_volume_sharing) {
- pa_source_set_get_volume_callback(u->source, source_get_volume_cb);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 15 Apr 2014 09:39:49 +0300
-Subject: sink, source: Call set_mute() from mute_changed()
-
-This refactoring reduces duplication, as mute_changed() used to do the
-same things as set_mute(). Other benefits are improved logging
-(set_mute() logs the mute change, mute_changed() used to not do that)
-and the soft mute state is kept up to date, because set_mute() sends
-the SET_MUTE message to the IO thread.
-
-The set_mute_in_progress flag is an extra precaution for preventing
-recursion in case a sink/source implementation's set_mute() callback
-causes mute_changed() to be called. Currently there are no such
-implementations, but I think that would be a valid thing to do, so
-some day there might be such implementation.
-
-Change-Id: I33c81f4034001f777c4533c2c63eada67548c683
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink.c | 19 ++++++++++++-------
- src/pulsecore/sink.h | 2 ++
- src/pulsecore/source.c | 19 ++++++++++++-------
- src/pulsecore/source.h | 2 ++
- 4 files changed, 28 insertions(+), 14 deletions(-)
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 7eb4cb4..4c348b5 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -2296,8 +2296,11 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
- s->muted = mute;
- s->save_muted = save;
-
-- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
-+ if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) {
-+ s->set_mute_in_progress = true;
- s->set_mute(s);
-+ s->set_mute_in_progress = false;
-+ }
-
- if (!PA_SINK_IS_LINKED(s->state))
- return;
-@@ -2341,15 +2344,17 @@ void pa_sink_mute_changed(pa_sink *s, bool new_muted) {
- pa_assert_ctl_context();
- pa_assert(PA_SINK_IS_LINKED(s->state));
-
-- /* The sink implementor may call this if the volume changed to make sure everyone is notified */
--
-- if (s->muted == new_muted)
-+ if (s->set_mute_in_progress)
- return;
-
-- s->muted = new_muted;
-- s->save_muted = true;
-+ /* pa_sink_set_mute() does this same check, so this may appear redundant,
-+ * but we must have this here also, because the save parameter of
-+ * pa_sink_set_mute() would otherwise have unintended side effects (saving
-+ * the mute state when it shouldn't be saved). */
-+ if (new_muted == s->muted)
-+ return;
-
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_sink_set_mute(s, new_muted, true);
- }
-
- /* Called from main thread */
-diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
-index 41a439b..72437a4 100644
---- a/src/pulsecore/sink.h
-+++ b/src/pulsecore/sink.h
-@@ -122,6 +122,8 @@ struct pa_sink {
-
- unsigned priority;
-
-+ bool set_mute_in_progress;
-+
- /* Called when the main loop requests a state change. Called from
- * main loop context. If returns -1 the state change will be
- * inhibited */
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 3b6ad44..2f6aaad 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1814,8 +1814,11 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
- s->muted = mute;
- s->save_muted = save;
-
-- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
-+ if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) {
-+ s->set_mute_in_progress = true;
- s->set_mute(s);
-+ s->set_mute_in_progress = false;
-+ }
-
- if (!PA_SOURCE_IS_LINKED(s->state))
- return;
-@@ -1859,15 +1862,17 @@ void pa_source_mute_changed(pa_source *s, bool new_muted) {
- pa_assert_ctl_context();
- pa_assert(PA_SOURCE_IS_LINKED(s->state));
-
-- /* The source implementor may call this if the mute state changed to make sure everyone is notified */
--
-- if (s->muted == new_muted)
-+ if (s->set_mute_in_progress)
- return;
-
-- s->muted = new_muted;
-- s->save_muted = true;
-+ /* pa_source_set_mute() does this same check, so this may appear redundant,
-+ * but we must have this here also, because the save parameter of
-+ * pa_source_set_mute() would otherwise have unintended side effects
-+ * (saving the mute state when it shouldn't be saved). */
-+ if (new_muted == s->muted)
-+ return;
-
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_source_set_mute(s, new_muted, true);
- }
-
- /* Called from main thread */
-diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
-index 6318595..ca2ed59 100644
---- a/src/pulsecore/source.h
-+++ b/src/pulsecore/source.h
-@@ -118,6 +118,8 @@ struct pa_source {
-
- unsigned priority;
-
-+ bool set_mute_in_progress;
-+
- /* Called when the main loop requests a state change. Called from
- * main loop context. If returns -1 the state change will be
- * inhibited */
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 14:52:16 +0300
-Subject: sink, source: Assign to s->muted from only one place
-
-Forcing all mute changes to go through set_mute() makes it easier to
-check where the muted field is changed, and it also allows us to have
-only one place where notifications for changed mute are sent.
-
-Change-Id: Idb1bd6ef923a165e249d42265ebedc30a6c8fca4
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/alsa/alsa-sink.c | 17 ++++++++++-------
- src/modules/alsa/alsa-source.c | 17 ++++++++++-------
- src/modules/module-solaris.c | 17 +++++++++++------
- src/pulsecore/sink.c | 26 ++++++++++----------------
- src/pulsecore/sink.h | 24 ++++++++++++++++++------
- src/pulsecore/source.c | 26 ++++++++++----------------
- src/pulsecore/source.h | 24 ++++++++++++++++++------
- 7 files changed, 87 insertions(+), 64 deletions(-)
-
-diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
-index 03babb3..2bc27ff 100644
---- a/src/modules/alsa/alsa-sink.c
-+++ b/src/modules/alsa/alsa-sink.c
-@@ -1401,18 +1401,17 @@ static void sink_write_volume_cb(pa_sink *s) {
- }
- }
-
--static void sink_get_mute_cb(pa_sink *s) {
-+static int sink_get_mute_cb(pa_sink *s, bool *mute) {
- struct userdata *u = s->userdata;
-- bool b;
-
- pa_assert(u);
- pa_assert(u->mixer_path);
- pa_assert(u->mixer_handle);
-
-- if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
-- return;
-+ if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
-+ return -1;
-
-- s->muted = b;
-+ return 0;
- }
-
- static void sink_set_mute_cb(pa_sink *s) {
-@@ -2399,8 +2398,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
- if (u->sink->set_mute)
- u->sink->set_mute(u->sink);
- } else {
-- if (u->sink->get_mute)
-- u->sink->get_mute(u->sink);
-+ if (u->sink->get_mute) {
-+ bool mute;
-+
-+ if (u->sink->get_mute(u->sink, &mute) >= 0)
-+ pa_sink_set_mute(u->sink, mute, false);
-+ }
- }
-
- if ((data.volume_is_set || data.muted_is_set) && u->sink->write_volume)
-diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
-index 2e93e0f..e181a30 100644
---- a/src/modules/alsa/alsa-source.c
-+++ b/src/modules/alsa/alsa-source.c
-@@ -1277,18 +1277,17 @@ static void source_write_volume_cb(pa_source *s) {
- }
- }
-
--static void source_get_mute_cb(pa_source *s) {
-+static int source_get_mute_cb(pa_source *s, bool *mute) {
- struct userdata *u = s->userdata;
-- bool b;
-
- pa_assert(u);
- pa_assert(u->mixer_path);
- pa_assert(u->mixer_handle);
-
-- if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
-- return;
-+ if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
-+ return -1;
-
-- s->muted = b;
-+ return 0;
- }
-
- static void source_set_mute_cb(pa_source *s) {
-@@ -2088,8 +2087,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
- if (u->source->set_mute)
- u->source->set_mute(u->source);
- } else {
-- if (u->source->get_mute)
-- u->source->get_mute(u->source);
-+ if (u->source->get_mute) {
-+ bool mute;
-+
-+ if (u->source->get_mute(u->source, &mute) >= 0)
-+ pa_source_set_mute(u->source, mute, false);
-+ }
- }
-
- if ((data.volume_is_set || data.muted_is_set) && u->source->write_volume)
-diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
-index b4fa734..71a98e9 100644
---- a/src/modules/module-solaris.c
-+++ b/src/modules/module-solaris.c
-@@ -571,18 +571,23 @@ static void sink_set_mute(pa_sink *s) {
- }
- }
-
--static void sink_get_mute(pa_sink *s) {
-+static int sink_get_mute(pa_sink *s, bool *mute) {
- struct userdata *u = s->userdata;
- audio_info_t info;
-
- pa_assert(u);
-
-- if (u->fd >= 0) {
-- if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
-- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
-- else
-- s->muted = !!info.output_muted;
-+ if (u->fd < 0)
-+ return -1;
-+
-+ if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) {
-+ pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno));
-+ return -1;
- }
-+
-+ *mute = info.output_muted;
-+
-+ return 0;
- }
-
- static void process_rewind(struct userdata *u) {
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 4c348b5..b64001b 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -519,7 +519,7 @@ void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- }
-
--void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
-+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb) {
- pa_assert(s);
-
- s->get_mute = cb;
-@@ -2317,21 +2317,15 @@ bool pa_sink_get_mute(pa_sink *s, bool force_refresh) {
- pa_assert_ctl_context();
- pa_assert(PA_SINK_IS_LINKED(s->state));
-
-- if (s->refresh_muted || force_refresh) {
-- bool old_muted = s->muted;
-+ if ((s->refresh_muted || force_refresh) && s->get_mute) {
-+ bool mute;
-
-- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_mute)
-- s->get_mute(s);
--
-- 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) {
-- s->save_muted = true;
--
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
--
-- /* Make sure the soft mute status stays in sync */
-- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
-+ if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
-+ pa_sink_mute_changed(s, mute);
-+ } else {
-+ if (s->get_mute(s, &mute) >= 0)
-+ pa_sink_mute_changed(s, mute);
- }
- }
-
-@@ -2848,7 +2842,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
- case PA_SINK_MESSAGE_GET_MUTE:
-
- if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
-- s->get_mute(s);
-+ return s->get_mute(s, userdata);
-
- return 0;
-
-diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
-index 72437a4..e069d02 100644
---- a/src/pulsecore/sink.h
-+++ b/src/pulsecore/sink.h
-@@ -59,6 +59,8 @@ static inline bool PA_SINK_IS_LINKED(pa_sink_state_t x) {
- /* A generic definition for void callback functions */
- typedef void(*pa_sink_cb_t)(pa_sink *s);
-
-+typedef int (*pa_sink_get_mute_cb_t)(pa_sink *s, bool *mute);
-+
- struct pa_sink {
- pa_msgobject parent;
-
-@@ -195,14 +197,24 @@ struct pa_sink {
- * set this callback. */
- pa_sink_cb_t write_volume; /* may be NULL */
-
-- /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE
-- * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME
-- * flag is set otherwise from main loop context. If refresh_mute is false
-- * neither this function is called nor a message is sent.
-+ /* If the sink mute can change "spontaneously" (i.e. initiated by the sink
-+ * implementation, not by someone else calling pa_sink_set_mute()), then
-+ * the sink implementation can notify about changed mute either by calling
-+ * pa_sink_mute_changed() or by calling pa_sink_get_mute() with
-+ * force_refresh=true. If the implementation chooses the latter approach,
-+ * it should implement the get_mute callback. Otherwise get_mute can be
-+ * NULL.
-+ *
-+ * This is called when pa_sink_get_mute() is called with
-+ * force_refresh=true. This is called from the IO thread if the
-+ * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the
-+ * main thread. On success, the implementation is expected to return 0 and
-+ * set the mute parameter that is passed as a reference. On failure, the
-+ * implementation is expected to return -1.
- *
- * You must use the function pa_sink_set_get_mute_callback() to
- * set this callback. */
-- pa_sink_cb_t get_mute; /* may be NULL */
-+ pa_sink_get_mute_cb_t get_mute;
-
- /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
- * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME
-@@ -386,7 +398,7 @@ pa_sink* pa_sink_new(
- void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb);
- void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb);
- void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb);
--void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb);
-+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb);
- void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb);
- void pa_sink_enable_decibel_volume(pa_sink *s, bool enable);
-
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 2f6aaad..24e8be9 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -467,7 +467,7 @@ void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) {
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
- }
-
--void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) {
-+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb) {
- pa_assert(s);
-
- s->get_mute = cb;
-@@ -1835,21 +1835,15 @@ bool pa_source_get_mute(pa_source *s, bool force_refresh) {
- pa_assert_ctl_context();
- pa_assert(PA_SOURCE_IS_LINKED(s->state));
-
-- if (s->refresh_muted || force_refresh) {
-- bool old_muted = s->muted;
-+ if ((s->refresh_muted || force_refresh) && s->get_mute) {
-+ bool mute;
-
-- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_mute)
-- s->get_mute(s);
--
-- 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) {
-- s->save_muted = true;
--
-- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
--
-- /* Make sure the soft mute status stays in sync */
-- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
-+ if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
-+ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
-+ pa_source_mute_changed(s, mute);
-+ } else {
-+ if (s->get_mute(s, &mute) >= 0)
-+ pa_source_mute_changed(s, mute);
- }
- }
-
-@@ -2136,7 +2130,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
- case PA_SOURCE_MESSAGE_GET_MUTE:
-
- if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->get_mute)
-- s->get_mute(s);
-+ return s->get_mute(s, userdata);
-
- return 0;
-
-diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
-index ca2ed59..83bc2dd 100644
---- a/src/pulsecore/source.h
-+++ b/src/pulsecore/source.h
-@@ -58,6 +58,8 @@ static inline bool PA_SOURCE_IS_LINKED(pa_source_state_t x) {
- /* A generic definition for void callback functions */
- typedef void(*pa_source_cb_t)(pa_source *s);
-
-+typedef int (*pa_source_get_mute_cb_t)(pa_source *s, bool *mute);
-+
- struct pa_source {
- pa_msgobject parent;
-
-@@ -158,14 +160,24 @@ struct pa_source {
- * set this callback. */
- pa_source_cb_t write_volume; /* may be NULL */
-
-- /* 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.
-+ /* If the source mute can change "spontaneously" (i.e. initiated by the
-+ * source implementation, not by someone else calling
-+ * pa_source_set_mute()), then the source implementation can notify about
-+ * changed mute either by calling pa_source_mute_changed() or by calling
-+ * pa_source_get_mute() with force_refresh=true. If the implementation
-+ * chooses the latter approach, it should implement the get_mute callback.
-+ * Otherwise get_mute can be NULL.
-+ *
-+ * This is called when pa_source_get_mute() is called with
-+ * force_refresh=true. This is called from the IO thread if the
-+ * PA_SOURCE_DEFERRED_VOLUME flag is set, otherwise this is called from the
-+ * main thread. On success, the implementation is expected to return 0 and
-+ * set the mute parameter that is passed as a reference. On failure, the
-+ * implementation is expected to return -1.
- *
- * You must use the function pa_source_set_get_mute_callback() to
- * set this callback. */
-- pa_source_cb_t get_mute; /* may be NULL */
-+ pa_source_get_mute_cb_t get_mute;
-
- /* Called when the mute setting shall be changed. Called from main
- * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
-@@ -316,7 +328,7 @@ pa_source* pa_source_new(
- void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb);
- void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb);
- void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb);
--void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb);
-+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb);
- void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb);
- void pa_source_enable_decibel_volume(pa_source *s, bool enable);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 15:24:31 +0300
-Subject: sink-input, source-output: Remove redundant get_mute() functions
-
-The functions just return the muted value. Callers can as well read
-the struct field directly, it's simpler that way.
-
-Change-Id: I368f7e09cdf522039a6573e5002f7544b4e840d3
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/dbus/iface-stream.c | 4 ++--
- src/modules/module-role-cork.c | 9 ++++-----
- src/modules/module-stream-restore.c | 4 ++--
- src/pulsecore/cli-text.c | 4 ++--
- src/pulsecore/protocol-native.c | 4 ++--
- src/pulsecore/sink-input.c | 9 ---------
- src/pulsecore/sink-input.h | 1 -
- src/pulsecore/source-output.c | 9 ---------
- src/pulsecore/source-output.h | 1 -
- 9 files changed, 12 insertions(+), 33 deletions(-)
-
-diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
-index 1cff95e..4cbcd74 100644
---- a/src/modules/dbus/iface-stream.c
-+++ b/src/modules/dbus/iface-stream.c
-@@ -765,7 +765,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
- }
- }
-
-- new_mute = pa_sink_input_get_mute(s->sink_input);
-+ new_mute = s->sink_input->muted;
-
- if (s->mute != new_mute) {
- s->mute = new_mute;
-@@ -861,7 +861,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p
- else
- pa_cvolume_init(&s->volume);
-
-- s->mute = pa_sink_input_get_mute(sink_input);
-+ s->mute = sink_input->muted;
- s->proplist = pa_proplist_copy(sink_input->proplist);
- s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
- s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
-diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c
-index 6573cd6..8ca2109 100644
---- a/src/modules/module-role-cork.c
-+++ b/src/modules/module-role-cork.c
-@@ -102,7 +102,7 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
- pa_sink_assert_ref(s);
-
- for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
-- bool corked, muted, corked_here;
-+ bool corked, corked_here;
- const char *role;
-
- if (j == ignore)
-@@ -119,10 +119,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
- continue;
-
- corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
-- muted = pa_sink_input_get_mute(j);
- corked_here = !!pa_hashmap_get(u->cork_state, j);
-
-- if (cork && !corked && !muted) {
-+ if (cork && !corked && !j->muted) {
- pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role);
- if (!corked_here)
- pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1));
-@@ -131,9 +130,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
- } else if (!cork) {
- pa_hashmap_remove(u->cork_state, j);
-
-- if (corked_here && (corked || muted)) {
-+ if (corked_here && (corked || j->muted)) {
- pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role);
-- if (muted)
-+ if (j->muted)
- pa_sink_input_set_mute(j, false, false);
- if (corked)
- pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
-diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
-index 68968c9..4fc5645 100644
---- a/src/modules/module-stream-restore.c
-+++ b/src/modules/module-stream-restore.c
-@@ -1303,7 +1303,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
- }
-
- if (sink_input->save_muted) {
-- entry->muted = pa_sink_input_get_mute(sink_input);
-+ entry->muted = sink_input->muted;
- entry->muted_valid = true;
-
- mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
-@@ -1353,7 +1353,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
- }
-
- if (source_output->save_muted) {
-- entry->muted = pa_source_output_get_mute(source_output);
-+ entry->muted = source_output->muted;
- entry->muted_valid = true;
-
- mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
-diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
-index c7db0a6..2992ae8 100644
---- a/src/pulsecore/cli-text.c
-+++ b/src/pulsecore/cli-text.c
-@@ -536,7 +536,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
- state_table[pa_source_output_get_state(o)],
- o->source->index, o->source->name,
- volume_str,
-- pa_yes_no(pa_source_output_get_mute(o)),
-+ pa_yes_no(o->muted),
- (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
- clt,
- pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
-@@ -634,7 +634,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
- state_table[pa_sink_input_get_state(i)],
- i->sink->index, i->sink->name,
- volume_str,
-- pa_yes_no(pa_sink_input_get_mute(i)),
-+ pa_yes_no(i->muted),
- (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
- clt,
- pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
-diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
-index 4304cd4..21e02fe 100644
---- a/src/pulsecore/protocol-native.c
-+++ b/src/pulsecore/protocol-native.c
-@@ -3378,7 +3378,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
- pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
- pa_tagstruct_puts(t, s->driver);
- if (c->version >= 11)
-- pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
-+ pa_tagstruct_put_boolean(t, s->muted);
- if (c->version >= 13)
- pa_tagstruct_put_proplist(t, s->proplist);
- if (c->version >= 19)
-@@ -3425,7 +3425,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
- pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED));
- if (c->version >= 22) {
- pa_tagstruct_put_cvolume(t, &v);
-- pa_tagstruct_put_boolean(t, pa_source_output_get_mute(s));
-+ pa_tagstruct_put_boolean(t, s->muted);
- pa_tagstruct_put_boolean(t, has_volume);
- pa_tagstruct_put_boolean(t, s->volume_writable);
- pa_tagstruct_put_format_info(t, s->format);
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index f706acc..a274620 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1485,15 +1485,6 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
-
--/* Called from main context */
--bool pa_sink_input_get_mute(pa_sink_input *i) {
-- pa_sink_input_assert_ref(i);
-- pa_assert_ctl_context();
-- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
--
-- return i->muted;
--}
--
- /* Called from main thread */
- void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
- pa_sink_input_assert_ref(i);
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index 4e7b229..1bd3eee 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -387,7 +387,6 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key);
- pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute);
-
- void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save);
--bool pa_sink_input_get_mute(pa_sink_input *i);
-
- void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg, bool save);
-
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index bb89384..d3888df 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1084,15 +1084,6 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- }
-
--/* Called from main context */
--bool pa_source_output_get_mute(pa_source_output *o) {
-- pa_source_output_assert_ref(o);
-- pa_assert_ctl_context();
-- pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
--
-- return o->muted;
--}
--
- /* Called from main thread */
- void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
- pa_source_output_assert_ref(o);
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index 27d6fd4..73170d3 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -318,7 +318,6 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume,
- pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute);
-
- void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save);
--bool pa_source_output_get_mute(pa_source_output *o);
-
- void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 14 Apr 2014 15:34:57 +0300
-Subject: solaris, tunnel: Remove some redundant boolean conversions
-
-Change-Id: Ibc922f8455a3ccb6c71289e70cd474464930643e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/modules/module-solaris.c | 2 +-
- src/modules/module-tunnel.c | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
-index 71a98e9..4f11000 100644
---- a/src/modules/module-solaris.c
-+++ b/src/modules/module-solaris.c
-@@ -564,7 +564,7 @@ static void sink_set_mute(pa_sink *s) {
- if (u->fd >= 0) {
- AUDIO_INITINFO(&info);
-
-- info.output_muted = !!s->muted;
-+ info.output_muted = s->muted;
-
- if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
-diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
-index 1ddfd25..0fc3d73 100644
---- a/src/modules/module-tunnel.c
-+++ b/src/modules/module-tunnel.c
-@@ -1906,7 +1906,7 @@ static void sink_set_mute(pa_sink *sink) {
- pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
- pa_tagstruct_putu32(t, u->ctag++);
- pa_tagstruct_putu32(t, u->device_index);
-- pa_tagstruct_put_boolean(t, !!sink->muted);
-+ pa_tagstruct_put_boolean(t, sink->muted);
- pa_pstream_send_tagstruct(u->pstream, t);
- }
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 9 Mar 2014 13:36:04 +0200
-Subject: sink, source: Add hooks for volume changes
-
-Change-Id: Id4389a38e601dee3f84d7fad0583a7dc87108e87
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core.h | 2 ++
- src/pulsecore/sink.c | 1 +
- src/pulsecore/source.c | 1 +
- 3 files changed, 4 insertions(+)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index e1cd18f..85f1b81 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -76,6 +76,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
- PA_CORE_HOOK_SINK_PORT_CHANGED,
- PA_CORE_HOOK_SINK_FLAGS_CHANGED,
-+ PA_CORE_HOOK_SINK_VOLUME_CHANGED,
- PA_CORE_HOOK_SOURCE_NEW,
- PA_CORE_HOOK_SOURCE_FIXATE,
- PA_CORE_HOOK_SOURCE_PUT,
-@@ -85,6 +86,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
- PA_CORE_HOOK_SOURCE_PORT_CHANGED,
- PA_CORE_HOOK_SOURCE_FLAGS_CHANGED,
-+ PA_CORE_HOOK_SOURCE_VOLUME_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_NEW,
- PA_CORE_HOOK_SINK_INPUT_FIXATE,
- PA_CORE_HOOK_SINK_INPUT_PUT,
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index b64001b..f8e5449 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -3907,4 +3907,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
- s->flags & PA_SINK_DECIBEL_VOLUME));
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
- }
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 24e8be9..8a43708 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -2900,4 +2900,5 @@ void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volum
- s->flags & PA_SOURCE_DECIBEL_VOLUME));
-
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], s);
- }
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 7 Apr 2014 14:22:43 +0300
-Subject: sink-input, source-output: Add hooks for volume changes
-
-Change-Id: I89c6f2934762caa2c49c70c0446c14d0de58a10e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core.h | 2 ++
- src/pulsecore/sink-input.c | 1 +
- src/pulsecore/source-output.c | 1 +
- 3 files changed, 4 insertions(+)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index 85f1b81..a839898 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -97,6 +97,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
- PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
-+ PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
- PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
- PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
-@@ -108,6 +109,7 @@ 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_VOLUME_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
- PA_CORE_HOOK_CLIENT_NEW,
- PA_CORE_HOOK_CLIENT_PUT,
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index a274620..3024539 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -2326,4 +2326,5 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume)
- i->volume_changed(i);
-
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], i);
- }
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index d3888df..18a1478 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1692,4 +1692,5 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v
- o->volume_changed(o);
-
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], o);
- }
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 15 Apr 2014 11:10:24 +0300
-Subject: sink, source: Add hooks for mute changes
-
-Change-Id: I1203c1199fea0e93f1a61391695b12ab8af66180
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core.h | 2 ++
- src/pulsecore/sink.c | 1 +
- src/pulsecore/source.c | 1 +
- 3 files changed, 4 insertions(+)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index a839898..af7fa50 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -77,6 +77,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SINK_PORT_CHANGED,
- PA_CORE_HOOK_SINK_FLAGS_CHANGED,
- PA_CORE_HOOK_SINK_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SINK_MUTE_CHANGED,
- PA_CORE_HOOK_SOURCE_NEW,
- PA_CORE_HOOK_SOURCE_FIXATE,
- PA_CORE_HOOK_SOURCE_PUT,
-@@ -87,6 +88,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SOURCE_PORT_CHANGED,
- PA_CORE_HOOK_SOURCE_FLAGS_CHANGED,
- PA_CORE_HOOK_SOURCE_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SOURCE_MUTE_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_NEW,
- PA_CORE_HOOK_SINK_INPUT_FIXATE,
- PA_CORE_HOOK_SINK_INPUT_PUT,
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index f8e5449..695e471 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -2308,6 +2308,7 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
- pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED], s);
- }
-
- /* Called from main thread */
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 8a43708..0fddfaa 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1826,6 +1826,7 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
- pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-+ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED], s);
- }
-
- /* Called from main thread */
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 15 Apr 2014 11:27:53 +0300
-Subject: sink-input, source-output: Add hooks for mute changes
-
-Change-Id: I256cfa27ffa6addb35640266b73f1fe07a483203
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/core.h | 2 ++
- src/pulsecore/sink-input.c | 1 +
- src/pulsecore/source-output.c | 1 +
- 3 files changed, 4 insertions(+)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index af7fa50..1f042b9 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -100,6 +100,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_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
- PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
- PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
-@@ -112,6 +113,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
- PA_CORE_HOOK_CLIENT_NEW,
- PA_CORE_HOOK_CLIENT_PUT,
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 3024539..9d13269 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1483,6 +1483,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
- i->mute_changed(i);
-
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i);
- }
-
- /* Called from main thread */
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index 18a1478..d3d15f1 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1082,6 +1082,7 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
- o->mute_changed(o);
-
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o);
- }
-
- /* Called from main thread */
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Fri, 9 May 2014 11:25:28 +0300
-Subject: sink: Link monitor source before activating port
-
-The port activation hook callbacks may want to use the monitor source.
-
-Change-Id: I5d5c51171a78162dacb3286983cb560001e79ba1
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/pulsecore/sink.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 695e471..191b560 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -669,11 +669,11 @@ void pa_sink_put(pa_sink* s) {
- else
- pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
-
-+ pa_source_put(s->monitor_source);
-+
- if (s->active_port)
- pa_device_port_active_changed(s->active_port, true);
-
-- pa_source_put(s->monitor_source);
--
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
- }
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 4 Mar 2014 15:03:05 +0200
-Subject: context, extension: Add the pa_extension class
-
-pa_extension is an abstraction layer that allows pa_context to manage
-the extensions without needing any extension-specific code. This patch
-only implements the pa_extension base class, the class isn't used yet
-by any actual extensions.
-
-Change-Id: I457b3d0b674b4cfd1d38452d8f8cb51cf6b7b533
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 1 +
- src/pulse/context.c | 48 +++++++++++++++++++++++-
- src/pulse/extension.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
- src/pulse/extension.h | 63 +++++++++++++++++++++++++++++++
- src/pulse/internal.h | 6 +++
- 5 files changed, 217 insertions(+), 1 deletion(-)
- create mode 100644 src/pulse/extension.c
- create mode 100644 src/pulse/extension.h
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index fe6cc53..22b9b81 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -813,6 +813,7 @@ libpulse_la_SOURCES = \
- pulse/def.h \
- pulse/direction.c pulse/direction.h \
- pulse/error.c pulse/error.h \
-+ pulse/extension.c pulse/extension.h \
- pulse/ext-device-manager.c pulse/ext-device-manager.h \
- pulse/ext-device-restore.c pulse/ext-device-restore.h \
- pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
-diff --git a/src/pulse/context.c b/src/pulse/context.c
-index b8688f2..9c9c3d9 100644
---- a/src/pulse/context.c
-+++ b/src/pulse/context.c
-@@ -186,14 +186,20 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
- }
- }
-
-+ c->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+
- return c;
- }
-
- static void context_unlink(pa_context *c) {
-+ pa_extension *extension;
- pa_stream *s;
-
- pa_assert(c);
-
-+ while ((extension = pa_hashmap_first(c->extensions)))
-+ pa_extension_kill(extension);
-+
- s = c->streams ? pa_stream_ref(c->streams) : NULL;
- while (s) {
- pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
-@@ -280,6 +286,9 @@ void pa_context_unref(pa_context *c) {
- }
-
- void pa_context_set_state(pa_context *c, pa_context_state_t st) {
-+ pa_extension *extension;
-+ void *state;
-+
- pa_assert(c);
- pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
-@@ -290,6 +299,12 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) {
-
- c->state = st;
-
-+ PA_HASHMAP_FOREACH(extension, c->extensions, state)
-+ pa_extension_context_state_changed(extension, 1);
-+
-+ PA_HASHMAP_FOREACH(extension, c->extensions, state)
-+ pa_extension_context_state_changed(extension, 2);
-+
- if (c->state_callback)
- c->state_callback(c, c->state_userdata);
-
-@@ -1338,6 +1353,7 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
- pa_context *c = userdata;
- uint32_t idx;
- const char *name;
-+ pa_extension *extension;
-
- pa_assert(pd);
- pa_assert(command == PA_COMMAND_EXTENSION);
-@@ -1366,7 +1382,16 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
- pa_ext_stream_restore_command(c, tag, t);
- else if (pa_streq(name, "module-node-manager"))
- pa_ext_node_manager_command(c, tag, t);
-- else
-+ else if ((extension = pa_context_get_extension(c, name))) {
-+ uint32_t subcommand;
-+
-+ if (pa_tagstruct_getu32(t, &subcommand) < 0) {
-+ pa_context_fail(c, PA_ERR_PROTOCOL);
-+ goto finish;
-+ }
-+
-+ pa_extension_process_command(extension, subcommand, tag, t);
-+ } else
- pa_log(_("Received message for unknown extension '%s'"), name);
-
- finish:
-@@ -1464,3 +1489,24 @@ int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path
-
- return pa_client_conf_load_cookie_from_file(c->conf, cookie_file_path);
- }
-+
-+pa_extension *pa_context_get_extension(pa_context *context, const char *name) {
-+ pa_assert(context);
-+ pa_assert(name);
-+
-+ return pa_hashmap_get(context->extensions, name);
-+}
-+
-+void pa_context_add_extension(pa_context *context, pa_extension *extension) {
-+ pa_assert(context);
-+ pa_assert(extension);
-+
-+ pa_assert_se(pa_hashmap_put(context->extensions, extension->name, extension) >= 0);
-+}
-+
-+int pa_context_remove_extension(pa_context *context, pa_extension *extension) {
-+ pa_assert(context);
-+ pa_assert(extension);
-+
-+ return pa_hashmap_remove(context->extensions, extension->name) ? 0 : -1;
-+}
-diff --git a/src/pulse/extension.c b/src/pulse/extension.c
-new file mode 100644
-index 0000000..17e7e6c
---- /dev/null
-+++ b/src/pulse/extension.c
-@@ -0,0 +1,100 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "extension.h"
-+
-+#include <pulsecore/macro.h>
-+
-+#include <pulse/internal.h>
-+#include <pulse/xmalloc.h>
-+
-+pa_extension *pa_extension_new(pa_context *context, const char *name) {
-+ pa_extension *extension = NULL;
-+
-+ pa_assert(context);
-+ pa_assert(name);
-+
-+ extension = pa_xnew0(pa_extension, 1);
-+ extension->context = context;
-+ extension->name = pa_xstrdup(name);
-+
-+ return extension;
-+}
-+
-+void pa_extension_put(pa_extension *extension) {
-+ pa_assert(extension);
-+ pa_assert(extension->kill);
-+
-+ pa_context_add_extension(extension->context, extension);
-+}
-+
-+static void extension_unlink(pa_extension *extension) {
-+ pa_assert(extension);
-+
-+ if (extension->unlinked)
-+ return;
-+
-+ extension->unlinked = true;
-+
-+ pa_context_remove_extension(extension->context, extension);
-+}
-+
-+void pa_extension_free(pa_extension *extension) {
-+ pa_assert(extension);
-+
-+ extension_unlink(extension);
-+
-+ pa_xfree(extension->name);
-+ pa_xfree(extension);
-+}
-+
-+void pa_extension_context_state_changed(pa_extension *extension, unsigned phase) {
-+ pa_assert(extension);
-+ pa_assert(phase == 1 || phase == 2);
-+
-+ if (extension->context_state_changed)
-+ extension->context_state_changed(extension, phase);
-+}
-+
-+void pa_extension_kill(pa_extension *extension) {
-+ pa_assert(extension);
-+
-+ if (extension->unlinked)
-+ return;
-+
-+ extension->kill(extension);
-+}
-+
-+void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct) {
-+ pa_assert(extension);
-+ pa_assert(tagstruct);
-+
-+ if (extension->process_command)
-+ extension->process_command(extension, command, tag, tagstruct);
-+ else {
-+ pa_log("Unexpected command for extension %s: %u", extension->name, command);
-+ pa_context_fail(extension->context, PA_ERR_PROTOCOL);
-+ }
-+}
-diff --git a/src/pulse/extension.h b/src/pulse/extension.h
-new file mode 100644
-index 0000000..cadc267
---- /dev/null
-+++ b/src/pulse/extension.h
-@@ -0,0 +1,63 @@
-+#ifndef fooextensionhfoo
-+#define fooextensionhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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/context.h>
-+
-+#include <pulsecore/tagstruct.h>
-+
-+#include <stdbool.h>
-+
-+typedef struct pa_extension pa_extension;
-+
-+struct pa_extension {
-+ pa_context *context;
-+ char *name;
-+ bool unlinked;
-+
-+ /* This is called when the context state changes. The callback is called
-+ * twice for each state change, first with phase = 1 and then with
-+ * phase = 2. In the first phase the extension should update its internal
-+ * state without calling any application callbacks. In the second phase it
-+ * should call the application callbacks (if any). May be NULL. */
-+ void (*context_state_changed)(pa_extension *extension, unsigned phase);
-+
-+ /* Called from pa_extension_kill(). May not be NULL. */
-+ void (*kill)(pa_extension *extension);
-+
-+ /* Called from pa_extension_process_command(). May be NULL, if the
-+ * extension doesn't expect any commands from the server. */
-+ void (*process_command)(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct);
-+
-+ void *userdata;
-+};
-+
-+pa_extension *pa_extension_new(pa_context *context, const char *name);
-+void pa_extension_put(pa_extension *extension);
-+void pa_extension_free(pa_extension *extension);
-+
-+void pa_extension_context_state_changed(pa_extension *extension, unsigned phase);
-+void pa_extension_kill(pa_extension *extension);
-+void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct);
-+
-+#endif
-diff --git a/src/pulse/internal.h b/src/pulse/internal.h
-index 61095d0..1428fb8 100644
---- a/src/pulse/internal.h
-+++ b/src/pulse/internal.h
-@@ -23,6 +23,7 @@
- USA.
- ***/
-
-+#include <pulse/extension.h>
- #include <pulse/mainloop-api.h>
- #include <pulse/context.h>
- #include <pulse/stream.h>
-@@ -103,6 +104,8 @@ struct pa_context {
-
- uint32_t client_index;
-
-+ pa_hashmap *extensions; /* extension name -> pa_extension */
-+
- /* Extension specific data */
- struct {
- pa_ext_device_manager_subscribe_cb_t callback;
-@@ -269,6 +272,9 @@ int pa_context_set_error(pa_context *c, int error);
- void pa_context_set_state(pa_context *c, pa_context_state_t st);
- int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail);
- pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
-+pa_extension *pa_context_get_extension(pa_context *context, const char *name);
-+void pa_context_add_extension(pa_context *context, pa_extension *extension);
-+int pa_context_remove_extension(pa_context *context, pa_extension *extension);
-
- void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 21 May 2014 14:05:47 +0300
-Subject: pactl: Add support for the new volume API
-
-Change-Id: I2bb6625c1cd575366388ec8dc3dd4fd2097c9a4a
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- man/pactl.1.xml.in | 50 +++-
- src/utils/pactl.c | 727 +++++++++++++++++++++++++++++++++++++++++++++++++++--
- 2 files changed, 759 insertions(+), 18 deletions(-)
-
-diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
-index 29071b3..cd54e4c 100644
---- a/man/pactl.1.xml.in
-+++ b/man/pactl.1.xml.in
-@@ -80,9 +80,12 @@ USA.
-
- <option>
- <p><opt>list</opt> [<arg>short</arg>] [<arg>TYPE</arg>]</p>
-- <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams, etc. <arg>TYPE</arg> must be one of:
-- modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards. If not specified, all info is listed. If
-- short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc>
-+ <optdesc><p>Dump all currently loaded modules, available sinks, sources,
-+ streams, etc. <arg>TYPE</arg> must be one of: modules, sinks, sources,
-+ sink-inputs, source-outputs, clients, samples, cards, volume-controls,
-+ mute-controls, devices, streams, audio-groups. If not specified, all info
-+ is listed. If short is given, output is in a tabular format, for easy
-+ parsing by scripts. </p></optdesc>
- </option>
-
- <option>
-@@ -244,7 +247,46 @@ USA.
- <arg>FORMATS</arg> is specified as a semi-colon (;) separated list of formats in the form
- 'encoding[, key1=value1, key2=value2, ...]' (for example, AC3 at 32000, 44100 and 48000 Hz would be specified as
- 'ac3-iec61937, format.rate = "[ 32000, 44100, 48000 ]"').
-- </p></optdesc> </option>
-+ </p></optdesc>
-+ </option>
-+
-+ <option>
-+ <p><opt>set-volume-control-volume</opt> <arg>CONTROL</arg>
-+ <arg>VOLUME</arg> <arg>[BALANCE ...]</arg>
-+ </p>
-+ <optdesc><p>Set the overall volume of the specified volume control
-+ (identified by its name or index). <arg>VOLUME</arg> can be specified
-+ as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a
-+ percentage (e.g. 10%, 100%) or a decibel value (e.g. 0dB, 20dB). If
-+ the volume specification start with a + or - the volume adjustment will
-+ be relative to the current source output volume. Optionally, you can
-+ also provide the channel balance (see also set-volume-control-balance).
-+ </p></optdesc>
-+ </option>
-+
-+ <option>
-+ <p><opt>set-volume-control-balance</opt> <arg>CONTROL</arg>
-+ <arg>BALANCE ...</arg>
-+ </p>
-+ <optdesc><p>Set the channel balance of the specified volume control
-+ (identified by its name or index). The balance is given as separate
-+ values for each channel. The balance values must be between 0.0 and
-+ 1.0. The number of values must match the volume control's channel map.
-+ </p></optdesc>
-+ </option>
-+
-+ <option>
-+ <p>
-+ <opt>set-mute-control-mute</opt> <arg>CONTROL</arg> <arg>1|0|toggle
-+ </arg>
-+ </p>
-+ <optdesc><p>
-+ Set the mute state of the specified mute control (identified by its
-+ name or index). If the mute value is "toggle", then the mute control
-+ will be muted if it was previously unmuted, and unmuted if it was
-+ previously muted.
-+ </p></optdesc>
-+ </option>
-
- <option>
- <p><opt>subscribe</opt></p>
-diff --git a/src/utils/pactl.c b/src/utils/pactl.c
-index 958d700..f947681 100644
---- a/src/utils/pactl.c
-+++ b/src/utils/pactl.c
-@@ -39,6 +39,7 @@
- #include <pulse/pulseaudio.h>
- #include <pulse/ext-device-restore.h>
- #include <pulse/ext-node-manager.h>
-+#include <pulse/ext-volume-api.h>
-
- #include <pulsecore/i18n.h>
- #include <pulsecore/macro.h>
-@@ -59,7 +60,9 @@ static char
- *card_name = NULL,
- *profile_name = NULL,
- *port_name = NULL,
-- *formats = NULL;
-+ *formats = NULL,
-+ *volume_control_name = NULL,
-+ *mute_control_name = NULL;
-
- static uint32_t
- sink_input_idx = PA_INVALID_INDEX,
-@@ -101,6 +104,14 @@ static bool nl = false;
- static uint32_t src_node_id;
- static uint32_t dst_node_id;
- static uint32_t conn_id;
-+bool volume_api_connected = false;
-+pa_ext_volume_api_bvolume bvolume;
-+bool volume_valid = false;
-+bool balance_valid = false;
-+uint32_t main_output_volume_control = PA_INVALID_INDEX;
-+uint32_t main_input_volume_control = PA_INVALID_INDEX;
-+uint32_t main_output_mute_control = PA_INVALID_INDEX;
-+uint32_t main_input_mute_control = PA_INVALID_INDEX;
-
- static enum {
- NONE,
-@@ -132,6 +143,8 @@ static enum {
- SET_SOURCE_OUTPUT_MUTE,
- SET_SINK_FORMATS,
- SET_PORT_LATENCY_OFFSET,
-+ SET_VOLUME_CONTROL_VOLUME,
-+ SET_MUTE_CONTROL_MUTE,
- SUBSCRIBE,
- NODE_CONNECT,
- NODE_DISCONNECT
-@@ -838,18 +851,18 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
- complete_action();
- }
-
--static void volume_relative_adjust(pa_cvolume *cv) {
-+static void volume_relative_adjust(pa_cvolume *cv, pa_volume_t adjustment) {
- pa_assert((volume_flags & VOL_RELATIVE) == VOL_RELATIVE);
-
- /* Relative volume change is additive in case of UINT or PERCENT
- * and multiplicative for LINEAR or DECIBEL */
- if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
- pa_volume_t v = pa_cvolume_avg(cv);
-- v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
-+ v = v + adjustment < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + adjustment - PA_VOLUME_NORM;
- pa_cvolume_set(cv, 1, v);
- }
- if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
-- pa_sw_cvolume_multiply_scalar(cv, cv, volume);
-+ pa_sw_cvolume_multiply_scalar(cv, cv, adjustment);
- }
- }
-
-@@ -893,7 +906,7 @@ static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int i
- pa_assert(i);
-
- cv = i->volume;
-- volume_relative_adjust(&cv);
-+ volume_relative_adjust(&cv, volume);
- pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
- }
-
-@@ -912,7 +925,7 @@ static void get_source_volume_callback(pa_context *c, const pa_source_info *i, i
- pa_assert(i);
-
- cv = i->volume;
-- volume_relative_adjust(&cv);
-+ volume_relative_adjust(&cv, volume);
- pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
- }
-
-@@ -931,7 +944,7 @@ static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_in
- pa_assert(i);
-
- cv = i->volume;
-- volume_relative_adjust(&cv);
-+ volume_relative_adjust(&cv, volume);
- pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
- }
-
-@@ -950,7 +963,7 @@ static void get_source_output_volume_callback(pa_context *c, const pa_source_out
- pa_assert(o);
-
- cv = o->volume;
-- volume_relative_adjust(&cv);
-+ volume_relative_adjust(&cv, volume);
- pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
- }
-
-@@ -1189,6 +1202,575 @@ static void context_subscribe_callback(pa_context *c, pa_subscription_event_type
- fflush(stdout);
- }
-
-+static void get_volume_control_info_callback(pa_context *c, const pa_ext_volume_api_volume_control_info *info,
-+ int is_last, void *userdata) {
-+ char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
-+ char balance_str[PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX];
-+ char *proplist_str;
-+
-+ pa_assert(c);
-+
-+ if (is_last < 0) {
-+ pa_log(_("Failed to get volume control information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ if (is_last) {
-+ complete_action();
-+ return;
-+ }
-+
-+ pa_assert(info);
-+
-+ if (action == INFO) {
-+ if (info->index == main_output_volume_control)
-+ printf(_("Main output volume control: %s\n"), info->name);
-+
-+ if (info->index == main_input_volume_control)
-+ printf(_("Main input volume control: %s\n"), info->name);
-+
-+ return;
-+ }
-+
-+ if (action == SET_VOLUME_CONTROL_VOLUME) {
-+ pa_ext_volume_api_bvolume bv;
-+
-+ if (balance_valid && bvolume.channel_map.channels != info->volume.channel_map.channels) {
-+ pa_log(_("Incompatible number of channels, expected %u channels."), info->volume.channel_map.channels);
-+ quit(1);
-+ }
-+
-+ bv = info->volume;
-+
-+ if (volume_valid) {
-+ if (volume_flags & VOL_RELATIVE) {
-+ pa_cvolume cv;
-+
-+ pa_cvolume_set(&cv, 1, info->volume.volume);
-+ volume_relative_adjust(&cv, bvolume.volume);
-+ bv.volume = cv.values[0];
-+ } else
-+ bv.volume = bvolume.volume;
-+ }
-+
-+ if (balance_valid)
-+ memcpy(bv.balance, bvolume.balance, sizeof(bv.balance));
-+
-+ pa_operation_unref(pa_ext_volume_api_set_volume_control_volume_by_name(c, volume_control_name, &bv,
-+ volume_valid, balance_valid,
-+ simple_callback, NULL));
-+ actions++;
-+
-+ return;
-+ }
-+
-+ pa_assert(action == LIST);
-+
-+ if (nl && !short_list_format)
-+ printf("\n");
-+ nl = true;
-+
-+ if (short_list_format) {
-+ printf("%u\t%s\t%u\n", info->index, info->name, info->volume.volume);
-+ return;
-+ }
-+
-+ pa_volume_snprint_verbose(volume_str, sizeof(volume_str), info->volume.volume, info->convertible_to_dB);
-+ pa_ext_volume_api_bvolume_snprint_balance(balance_str, sizeof(balance_str), &info->volume);
-+ proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
-+
-+ printf(_("Volume Control #%u\n"
-+ "\tName: %s\n"
-+ "\tDescription: %s\n"
-+ "\tVolume: %s\n"
-+ "\tBalance: %s\n"
-+ "\tProperties: %s%s\n"),
-+ info->index,
-+ info->name,
-+ info->description,
-+ volume_str,
-+ balance_str,
-+ *proplist_str ? "\n\t\t" : _("(none)"),
-+ proplist_str);
-+
-+ pa_xfree(proplist_str);
-+}
-+
-+static void get_mute_control_info_callback(pa_context *c, const pa_ext_volume_api_mute_control_info *info, int is_last,
-+ void *userdata) {
-+ char *proplist_str;
-+
-+ pa_assert(c);
-+
-+ if (is_last < 0) {
-+ pa_log(_("Failed to get mute control information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ if (is_last) {
-+ complete_action();
-+ return;
-+ }
-+
-+ pa_assert(info);
-+
-+ if (action == INFO) {
-+ if (info->index == main_output_mute_control)
-+ printf(_("Main output mute control: %s\n"), info->name);
-+
-+ if (info->index == main_input_mute_control)
-+ printf(_("Main input mute control: %s\n"), info->name);
-+
-+ return;
-+ }
-+
-+ if (action == SET_MUTE_CONTROL_MUTE) {
-+ pa_operation_unref(pa_ext_volume_api_set_mute_control_mute_by_index(c, info->index, info->mute ? false : true,
-+ simple_callback, NULL));
-+ actions++;
-+ return;
-+ }
-+
-+ pa_assert(action == LIST);
-+
-+ if (nl && !short_list_format)
-+ printf("\n");
-+ nl = true;
-+
-+ if (short_list_format) {
-+ printf("%u\t%s\t%s\n", info->index, info->name, pa_yes_no(info->mute));
-+ return;
-+ }
-+
-+ proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
-+
-+ printf(_("Mute Control #%u\n"
-+ "\tName: %s\n"
-+ "\tDescription: %s\n"
-+ "\tMute: %s\n"
-+ "\tProperties: %s%s\n"),
-+ info->index,
-+ info->name,
-+ info->description,
-+ pa_yes_no(info->mute),
-+ *proplist_str ? "\n\t\t" : _("(none)"),
-+ proplist_str);
-+
-+ pa_xfree(proplist_str);
-+}
-+
-+static void volume_api_get_server_info_callback(pa_context *c, const pa_ext_volume_api_server_info *info, void *userdata) {
-+ pa_assert(c);
-+
-+ if (!info) {
-+ pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ main_output_volume_control = info->main_output_volume_control;
-+ main_input_volume_control = info->main_input_volume_control;
-+ main_output_mute_control = info->main_output_mute_control;
-+ main_input_mute_control = info->main_input_mute_control;
-+
-+ if (main_output_volume_control == PA_INVALID_INDEX)
-+ printf(_("Main output volume control: (unset)\n"));
-+
-+ if (main_input_volume_control == PA_INVALID_INDEX)
-+ printf(_("Main input volume control: (unset)\n"));
-+
-+ if (main_output_mute_control == PA_INVALID_INDEX)
-+ printf(_("Main output mute control: (unset)\n"));
-+
-+ if (main_input_mute_control == PA_INVALID_INDEX)
-+ printf(_("Main input mute control: (unset)\n"));
-+
-+ if (main_output_volume_control != PA_INVALID_INDEX || main_input_volume_control != PA_INVALID_INDEX) {
-+ pa_operation_unref(pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL));
-+ actions++;
-+ }
-+
-+ if (main_output_mute_control != PA_INVALID_INDEX || main_input_mute_control != PA_INVALID_INDEX) {
-+ pa_operation_unref(pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL));
-+ actions++;
-+ }
-+
-+ complete_action();
-+}
-+
-+static void get_device_info_callback(pa_context *c, const pa_ext_volume_api_device_info *info, int is_last,
-+ void *userdata) {
-+ char *device_types_str = NULL;
-+ char *volume_control_str;
-+ char *mute_control_str;
-+ char *proplist_str;
-+
-+ pa_assert(c);
-+
-+ if (is_last < 0) {
-+ pa_log(_("Failed to get device information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ if (is_last) {
-+ complete_action();
-+ return;
-+ }
-+
-+ pa_assert(info);
-+
-+ if (nl && !short_list_format)
-+ printf("\n");
-+ nl = true;
-+
-+ if (info->n_device_types > 0)
-+ device_types_str = pa_join(info->device_types, info->n_device_types, ", ");
-+ else
-+ device_types_str = pa_xstrdup(_("(none)"));
-+
-+ if (info->volume_control != PA_INVALID_INDEX)
-+ volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
-+ else
-+ volume_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (info->mute_control != PA_INVALID_INDEX)
-+ mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
-+ else
-+ mute_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (short_list_format) {
-+ printf("%u\t%s\t%s\t%s\t%s\t%s\n", info->index, info->name, pa_direction_to_string(info->direction), device_types_str,
-+ volume_control_str, mute_control_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+ pa_xfree(device_types_str);
-+ return;
-+ }
-+
-+ proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
-+
-+ printf(_("Device #%u\n"
-+ "\tName: %s\n"
-+ "\tDescription: %s\n"
-+ "\tDirection: %s\n"
-+ "\tDevice Types: %s\n"
-+ "\tVolume Control: %s\n"
-+ "\tMute Control: %s\n"
-+ "\tProperties: %s%s\n"),
-+ info->index,
-+ info->name,
-+ info->description,
-+ pa_direction_to_string(info->direction),
-+ device_types_str,
-+ volume_control_str,
-+ mute_control_str,
-+ *proplist_str ? "\n\t\t" : _("(none)"),
-+ proplist_str);
-+
-+ pa_xfree(proplist_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+ pa_xfree(device_types_str);
-+}
-+
-+static void get_stream_info_callback(pa_context *c, const pa_ext_volume_api_stream_info *info, int is_last,
-+ void *userdata) {
-+ char *volume_control_str;
-+ char *mute_control_str;
-+ char *proplist_str;
-+
-+ pa_assert(c);
-+
-+ if (is_last < 0) {
-+ pa_log(_("Failed to get stream information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ if (is_last) {
-+ complete_action();
-+ return;
-+ }
-+
-+ pa_assert(info);
-+
-+ if (nl && !short_list_format)
-+ printf("\n");
-+ nl = true;
-+
-+ if (info->volume_control != PA_INVALID_INDEX)
-+ volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
-+ else
-+ volume_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (info->mute_control != PA_INVALID_INDEX)
-+ mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
-+ else
-+ mute_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (short_list_format) {
-+ printf("%u\t%s\t%s\t%s\t%s\n", info->index, info->name, pa_direction_to_string(info->direction), volume_control_str,
-+ mute_control_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+ return;
-+ }
-+
-+ proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
-+
-+ printf(_("Stream #%u\n"
-+ "\tName: %s\n"
-+ "\tDescription: %s\n"
-+ "\tDirection: %s\n"
-+ "\tVolume Control: %s\n"
-+ "\tMute Control: %s\n"
-+ "\tProperties: %s%s\n"),
-+ info->index,
-+ info->name,
-+ info->description,
-+ pa_direction_to_string(info->direction),
-+ volume_control_str,
-+ mute_control_str,
-+ *proplist_str ? "\n\t\t" : _("(none)"),
-+ proplist_str);
-+
-+ pa_xfree(proplist_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+}
-+
-+static void get_audio_group_info_callback(pa_context *c, const pa_ext_volume_api_audio_group_info *info, int is_last,
-+ void *userdata) {
-+ char *volume_control_str;
-+ char *mute_control_str;
-+ char *proplist_str;
-+
-+ pa_assert(c);
-+
-+ if (is_last < 0) {
-+ pa_log(_("Failed to get audio group information: %s"), pa_strerror(pa_context_errno(c)));
-+ quit(1);
-+ return;
-+ }
-+
-+ if (is_last) {
-+ complete_action();
-+ return;
-+ }
-+
-+ pa_assert(info);
-+
-+ if (nl && !short_list_format)
-+ printf("\n");
-+ nl = true;
-+
-+ if (info->volume_control != PA_INVALID_INDEX)
-+ volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
-+ else
-+ volume_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (info->mute_control != PA_INVALID_INDEX)
-+ mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
-+ else
-+ mute_control_str = pa_xstrdup(_("(unset)"));
-+
-+ if (short_list_format) {
-+ printf("%u\t%s\t%s\t%s\n", info->index, info->name, volume_control_str, mute_control_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+ return;
-+ }
-+
-+ proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
-+
-+ printf(_("Audio Group #%u\n"
-+ "\tName: %s\n"
-+ "\tDescription: %s\n"
-+ "\tVolume Control: %s\n"
-+ "\tMute Control: %s\n"
-+ "\tProperties: %s%s\n"),
-+ info->index,
-+ info->name,
-+ info->description,
-+ volume_control_str,
-+ mute_control_str,
-+ *proplist_str ? "\n\t\t" : _("(none)"),
-+ proplist_str);
-+
-+ pa_xfree(proplist_str);
-+ pa_xfree(mute_control_str);
-+ pa_xfree(volume_control_str);
-+}
-+
-+static const char *volume_api_subscription_event_facility_to_string(pa_ext_volume_api_subscription_event_type_t type) {
-+
-+ switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_SERVER:
-+ return _("server (volume API)");
-+
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_VOLUME_CONTROL:
-+ return _("volume-control");
-+
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_MUTE_CONTROL:
-+ return _("mute-control");
-+
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_DEVICE:
-+ return _("device");
-+
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_STREAM:
-+ return _("stream");
-+
-+ case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_AUDIO_GROUP:
-+ return _("audio-group");
-+ }
-+
-+ return _("unknown");
-+}
-+
-+static void volume_api_subscribe_cb(pa_context *c, pa_ext_volume_api_subscription_event_type_t event_type, uint32_t idx,
-+ void *userdata) {
-+ pa_assert(c);
-+
-+ printf(_("Event '%s' on %s #%u\n"),
-+ subscription_event_type_to_string(event_type),
-+ volume_api_subscription_event_facility_to_string(event_type),
-+ idx);
-+ fflush(stdout);
-+}
-+
-+static void volume_api_state_cb(pa_context *c, void *userdata) {
-+ pa_ext_volume_api_state_t state;
-+
-+ pa_assert(c);
-+
-+ state = pa_ext_volume_api_get_state(c);
-+
-+ switch (state) {
-+ case PA_EXT_VOLUME_API_STATE_READY: {
-+ pa_operation *o = NULL;
-+
-+ volume_api_connected = true;
-+
-+ switch (action) {
-+ case INFO:
-+ o = pa_ext_volume_api_get_server_info(c, volume_api_get_server_info_callback, NULL);
-+ actions++;
-+ break;
-+
-+ case LIST:
-+ if (!list_type) {
-+ o = pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL);
-+ pa_operation_unref(o);
-+ o = pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL);
-+ pa_operation_unref(o);
-+ o = pa_ext_volume_api_get_device_info_list(c, get_device_info_callback, NULL);
-+ pa_operation_unref(o);
-+ o = pa_ext_volume_api_get_stream_info_list(c, get_stream_info_callback, NULL);
-+ pa_operation_unref(o);
-+ o = pa_ext_volume_api_get_audio_group_info_list(c, get_audio_group_info_callback, NULL);
-+ pa_operation_unref(o);
-+ o = NULL;
-+ actions += 4;
-+ } else if (pa_streq(list_type, "volume-controls")) {
-+ o = pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL);
-+ actions++;
-+ } else if (pa_streq(list_type, "mute-controls")) {
-+ o = pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL);
-+ actions++;
-+ } else if (pa_streq(list_type, "devices")) {
-+ o = pa_ext_volume_api_get_device_info_list(c, get_device_info_callback, NULL);
-+ actions++;
-+ } else if (pa_streq(list_type, "streams")) {
-+ o = pa_ext_volume_api_get_stream_info_list(c, get_stream_info_callback, NULL);
-+ actions++;
-+ } else if (pa_streq(list_type, "audio-groups")) {
-+ o = pa_ext_volume_api_get_audio_group_info_list(c, get_audio_group_info_callback, NULL);
-+ actions++;
-+ }
-+ break;
-+
-+ case SET_VOLUME_CONTROL_VOLUME:
-+ if (!balance_valid && !(volume_flags & VOL_RELATIVE)) {
-+ pa_assert(volume_valid);
-+ o = pa_ext_volume_api_set_volume_control_volume_by_name(c, volume_control_name, &bvolume, true,
-+ false, simple_callback, NULL);
-+ } else
-+ o = pa_ext_volume_api_get_volume_control_info_by_name(c, volume_control_name,
-+ get_volume_control_info_callback, NULL);
-+
-+ actions++;
-+ break;
-+
-+ case SET_MUTE_CONTROL_MUTE:
-+ if (mute == TOGGLE_MUTE)
-+ o = pa_ext_volume_api_get_mute_control_info_by_name(c, mute_control_name,
-+ get_mute_control_info_callback, NULL);
-+ else
-+ o = pa_ext_volume_api_set_mute_control_mute_by_name(c, mute_control_name, mute, simple_callback,
-+ NULL);
-+
-+ actions++;
-+ break;
-+
-+ case SUBSCRIBE:
-+ pa_ext_volume_api_set_subscribe_callback(c, volume_api_subscribe_cb, NULL);
-+ o = pa_ext_volume_api_subscribe(c, PA_EXT_VOLUME_API_SUBSCRIPTION_MASK_ALL, NULL, NULL);
-+ break;
-+
-+ default:
-+ break;
-+ }
-+
-+ if (o)
-+ pa_operation_unref(o);
-+
-+ complete_action();
-+ break;
-+ }
-+
-+ case PA_EXT_VOLUME_API_STATE_FAILED:
-+ pa_log("Volume API context failed: %s", pa_strerror(pa_context_errno(c)));
-+
-+ /* If the main context failed too, let's not do anything, because
-+ * calling complete_action() would reset the context error code to
-+ * PA_ERR_BADSTATE, meaning that the original error code would be
-+ * lost. */
-+ if (pa_context_get_state(c) == PA_CONTEXT_FAILED)
-+ break;
-+
-+ if (action == INFO || (action == LIST && !list_type) || action == SUBSCRIBE) {
-+ /* In these cases we shouldn't exit with an error if the volume
-+ * API happens to be or become unavailable. If we haven't yet
-+ * connected to the volume API, then we need to complete the
-+ * "connect to volume API" action. */
-+
-+ if (!volume_api_connected)
-+ complete_action();
-+ } else
-+ quit(1);
-+
-+ break;
-+
-+ default:
-+ break;
-+ }
-+}
-+
-+static void connect_to_volume_api(void) {
-+ int r;
-+
-+ pa_assert(context);
-+
-+ pa_ext_volume_api_set_state_callback(context, volume_api_state_cb, NULL);
-+
-+ r = pa_ext_volume_api_connect(context);
-+ if (r >= 0)
-+ actions++;
-+}
-+
- static void context_state_callback(pa_context *c, void *userdata) {
- pa_operation *o = NULL;
-
-@@ -1215,6 +1797,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
-
- case INFO:
- o = pa_context_get_server_info(c, get_server_info_callback, NULL);
-+ connect_to_volume_api();
- break;
-
- case PLAY_SAMPLE:
-@@ -1259,7 +1842,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
- o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
- else if (pa_streq(list_type, "nodes"))
- o = pa_ext_node_manager_read_nodes(c, node_list_callback, NULL);
-- else
-+ else if (pa_streq(list_type, "volume-controls")
-+ || pa_streq(list_type, "mute-controls")
-+ || pa_streq(list_type, "devices")
-+ || pa_streq(list_type, "streams")
-+ || pa_streq(list_type, "audio-groups")) {
-+ connect_to_volume_api();
-+ o = NULL;
-+ } else
- pa_assert_not_reached();
- } else {
- o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
-@@ -1309,6 +1899,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
- actions++;
- }
-
-+ connect_to_volume_api();
- o = NULL;
- }
- break;
-@@ -1442,6 +2033,11 @@ static void context_state_callback(pa_context *c, void *userdata) {
- o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
- break;
-
-+ case SET_VOLUME_CONTROL_VOLUME:
-+ case SET_MUTE_CONTROL_MUTE:
-+ connect_to_volume_api();
-+ break;
-+
- case SUBSCRIBE:
- pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
-
-@@ -1457,6 +2053,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
- PA_SUBSCRIPTION_MASK_CARD,
- NULL,
- NULL);
-+
-+ if (o) {
-+ pa_operation_unref(o);
-+ actions++;
-+ o = NULL;
-+ }
-+
-+ connect_to_volume_api();
- break;
- case NODE_CONNECT:
- pa_operation_unref(pa_ext_node_manager_connect_nodes(c,
-@@ -1599,6 +2203,9 @@ static void help(const char *argv0) {
- printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
- printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
- printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
-+ printf("%s %s %s %s\n", argv0, _("[options]"), "set-volume-control-volume", _("NAME|#N VOLUME [BALANCE ...]"));
-+ printf("%s %s %s %s\n", argv0, _("[options]"), "set-volume-control-balance", _("NAME|#N BALANCE ..."));
-+ printf("%s %s %s %s\n", argv0, _("[options]"), "set-mute-control-mute", _("NAME|#N 1|0|toggle"));
- printf("%s %s %s\n", argv0, _("[options]"), "subscribe");
- printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
- "can be used to specify the default sink, source and monitor.\n"));
-@@ -1613,6 +2220,40 @@ static void help(const char *argv0) {
- " -n, --client-name=NAME How to call this client on the server\n"));
- }
-
-+static int parse_balance(char *argv[], unsigned first_arg, unsigned n_channels, pa_ext_volume_api_bvolume *bv) {
-+ pa_ext_volume_api_bvolume bv_local;
-+ unsigned i;
-+
-+ pa_assert(n_channels > 0);
-+ pa_assert(bv);
-+
-+ if (n_channels > PA_CHANNELS_MAX) {
-+ pa_log("Too many channels, the maximum is %u.", PA_CHANNELS_MAX);
-+ return -1;
-+ }
-+
-+ bv_local = *bv;
-+
-+ for (i = 0; i < n_channels; i++) {
-+ const char *balance_str;
-+ double balance;
-+
-+ balance_str = argv[first_arg + i];
-+
-+ if (pa_atod(balance_str, &balance) < 0 || !pa_ext_volume_api_balance_valid(balance)) {
-+ pa_log(_("Invalid balance value: %s"), balance_str);
-+ return -1;
-+ }
-+
-+ bv_local.balance[i] = balance;
-+ }
-+
-+ bv_local.channel_map.channels = n_channels;
-+ *bv = bv_local;
-+
-+ return 0;
-+}
-+
- enum {
- ARG_VERSION = 256
- };
-@@ -1698,15 +2339,19 @@ int main(int argc, char *argv[]) {
- action = LIST;
-
- for (int i = optind+1; i < argc; i++) {
-- if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
-- pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
-- pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
-- pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes")) {
-+ if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
-+ pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
-+ pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
-+ pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes") ||
-+ pa_streq(argv[i], "volume-controls") || pa_streq(argv[i], "mute-controls") ||
-+ pa_streq(argv[i], "devices") || pa_streq(argv[i], "streams") ||
-+ pa_streq(argv[i], "audio-groups")) {
- list_type = pa_xstrdup(argv[i]);
- } else if (pa_streq(argv[i], "short")) {
- short_list_format = true;
- } else {
-- pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
-+ pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, "
-+ "clients, samples, cards, volume-controls, mute-controls, devices, streams, audio-groups");
- goto quit;
- }
- }
-@@ -2092,6 +2737,58 @@ int main(int argc, char *argv[]) {
-
- conn_id = (uint32_t) atoi(argv[optind+1]);
-
-+ } else if (pa_streq(argv[optind], "set-volume-control-volume")) {
-+ action = SET_VOLUME_CONTROL_VOLUME;
-+
-+ if (argc < optind + 3) {
-+ pa_log(_("You have to specify a volume control name/index and a volume, and optionally balance parameters."));
-+ goto quit;
-+ }
-+
-+ volume_control_name = pa_xstrdup(argv[optind + 1]);
-+
-+ if (parse_volume(argv[optind + 2], &bvolume.volume, &volume_flags) < 0)
-+ goto quit;
-+
-+ volume_valid = true;
-+
-+ if (argc > optind + 3) {
-+ if (parse_balance(argv, optind + 3, argc - (optind + 3), &bvolume) < 0)
-+ goto quit;
-+
-+ balance_valid = true;
-+ }
-+
-+ } else if (pa_streq(argv[optind], "set-volume-control-balance")) {
-+ action = SET_VOLUME_CONTROL_VOLUME;
-+
-+ if (argc < optind + 3) {
-+ pa_log(_("You have to specify a volume control name/index and balance parameters."));
-+ goto quit;
-+ }
-+
-+ volume_control_name = pa_xstrdup(argv[optind + 1]);
-+
-+ if (parse_balance(argv, optind + 2, argc - (optind + 2), &bvolume) < 0)
-+ goto quit;
-+
-+ balance_valid = true;
-+
-+ } else if (pa_streq(argv[optind], "set-mute-control-mute")) {
-+ action = SET_MUTE_CONTROL_MUTE;
-+
-+ if (argc != optind + 3) {
-+ pa_log(_("You have to specify a mute control name/index and a mute value."));
-+ goto quit;
-+ }
-+
-+ mute_control_name = pa_xstrdup(argv[optind + 1]);
-+
-+ if ((mute = parse_mute(argv[optind + 2])) == INVALID_MUTE) {
-+ pa_log(_("Invalid mute specification"));
-+ goto quit;
-+ }
-+
- } else if (pa_streq(argv[optind], "help")) {
- help(bn);
- ret = 0;
-@@ -2154,6 +2851,8 @@ quit:
- pa_xfree(profile_name);
- pa_xfree(port_name);
- pa_xfree(formats);
-+ pa_xfree(mute_control_name);
-+ pa_xfree(volume_control_name);
-
- if (sndfile)
- sf_close(sndfile);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 21 May 2014 14:08:40 +0300
-Subject: Add module-audio-groups
-
-Change-Id: Iaa0284e0537785ed245caae6e49544477ca246b9
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 13 +-
- src/daemon/default.pa.in | 4 +
- src/modules/audio-groups/audio-groups.conf.example | 28 +
- src/modules/audio-groups/module-audio-groups.c | 1317 ++++++++++++++++++++
- 4 files changed, 1360 insertions(+), 2 deletions(-)
- create mode 100644 src/modules/audio-groups/audio-groups.conf.example
- create mode 100644 src/modules/audio-groups/module-audio-groups.c
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index e075c1d..a6bb319 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1082,6 +1082,12 @@ libvolume_api_la_SOURCES = \
- libvolume_api_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
- libvolume_api_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
-
-+# Audio Groups
-+module_audio_groups_la_SOURCES = modules/audio-groups/module-audio-groups.c
-+module_audio_groups_la_LDFLAGS = $(MODULE_LDFLAGS)
-+module_audio_groups_la_LIBADD = $(MODULE_LIBADD) libvolume-api.la
-+module_audio_groups_la_CFLAGS = $(AM_CFLAGS)
-+
- if HAVE_ESOUND
- libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
- libprotocol_esound_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
-@@ -1168,7 +1174,8 @@ modlibexec_LTLIBRARIES += \
- module-switch-on-port-available.la \
- module-filter-apply.la \
- module-filter-heuristics.la \
-- module-role-ducking.la
-+ module-role-ducking.la \
-+ module-audio-groups.la
-
- if HAVE_ESOUND
- modlibexec_LTLIBRARIES += \
-@@ -1505,7 +1512,9 @@ SYMDEF_FILES = \
- module-switch-on-connect-symdef.h \
- module-switch-on-port-available-symdef.h \
- module-filter-apply-symdef.h \
-- module-filter-heuristics-symdef.h
-+ module-filter-heuristics-symdef.h \
-+ module-audio-groups-symdef.h
-+
- if USE_SAMSUNG_POLICY
- SYMDEF_FILES += \
- module-policy-symdef.h
-diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
-index a21ae04..7cf52a4 100755
---- a/src/daemon/default.pa.in
-+++ b/src/daemon/default.pa.in
-@@ -193,6 +193,10 @@ ifelse(@HAVE_X11@, 1, [dnl
- load-module module-volume-api
- .endif
-
-+.ifexists module-audio-groups
-+load-module module-audio-groups
-+.endif
-+
- ### Make some devices default
- #set-default-sink output
- #set-default-source input
-diff --git a/src/modules/audio-groups/audio-groups.conf.example b/src/modules/audio-groups/audio-groups.conf.example
-new file mode 100644
-index 0000000..8acdb76
---- /dev/null
-+++ b/src/modules/audio-groups/audio-groups.conf.example
-@@ -0,0 +1,28 @@
-+[General]
-+audio-groups = x-example-call-downlink-audio-group x-example-default-output-audio-group x-example-music-output-audio-group
-+streams = phone-output music-output default-output
-+
-+[AudioGroup x-example-call-downlink-audio-group]
-+volume-control = create
-+mute-control = none
-+
-+[AudioGroup x-example-default-output-audio-group]
-+volume-control = create
-+mute-control = none
-+
-+[AudioGroup x-example-music-output-audio-group]
-+volume-control = bind:AudioGroup:x-example-default-output-audio-group
-+
-+[Stream phone-output]
-+match = (direction output AND property media.role=phone)
-+audio-group-for-volume = x-example-call-downlink-audio-group
-+audio-group-for-mute = x-example-call-downlink-audio-group
-+
-+[Stream music-output]
-+match = (direction output AND property media.role=music)
-+audio-group-for-volume = x-example-music-output-audio-group
-+audio-group-for-mute = x-example-music-output-audio-group
-+
-+[Stream default-output]
-+audio-group-for-volume = x-example-default-output-audio-group
-+audio-group-for-mute = x-example-default-output-audio-group
-diff --git a/src/modules/audio-groups/module-audio-groups.c b/src/modules/audio-groups/module-audio-groups.c
-new file mode 100644
-index 0000000..320847c
---- /dev/null
-+++ b/src/modules/audio-groups/module-audio-groups.c
-@@ -0,0 +1,1317 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 <pulsecore/core-util.h>
-+#include <pulsecore/dynarray.h>
-+#include <pulsecore/module.h>
-+#include <pulsecore/modargs.h>
-+#include <pulsecore/conf-parser.h>
-+#include <pulsecore/hashmap.h>
-+
-+#include <modules/volume-api/sstream.h>
-+#include <modules/volume-api/volume-control.h>
-+#include <modules/volume-api/audio-group.h>
-+
-+#include "module-audio-groups-symdef.h"
-+
-+PA_MODULE_AUTHOR("Ismo Puustinen");
-+PA_MODULE_DESCRIPTION("Create audio groups and classify streams to them");
-+PA_MODULE_VERSION(PACKAGE_VERSION);
-+PA_MODULE_LOAD_ONCE(true);
-+
-+#ifndef AUDIO_GROUP_CONFIG
-+#define AUDIO_GROUP_CONFIG "audio-groups.conf"
-+#endif
-+
-+enum match_direction {
-+ match_direction_unknown = 0,
-+ match_direction_input,
-+ match_direction_output,
-+};
-+
-+/* logical expressions */
-+
-+struct literal {
-+
-+ /* TODO: this might be parsed to some faster-to-check format? */
-+
-+ char *property_name;
-+ char *property_value;
-+ enum match_direction stream_direction;
-+
-+ bool negation;
-+ PA_LLIST_FIELDS(struct literal);
-+};
-+
-+struct conjunction {
-+ /* a conjunction of literals */
-+ PA_LLIST_HEAD(struct literal, literals);
-+ PA_LLIST_FIELDS(struct conjunction);
-+};
-+
-+struct expression {
-+ /* this is in disjunctive normal form, so a disjunction of conjunctions */
-+ PA_LLIST_HEAD(struct conjunction, conjunctions);
-+};
-+
-+/* data gathered from settings */
-+
-+enum control_action {
-+ CONTROL_ACTION_NONE,
-+ CONTROL_ACTION_CREATE,
-+ CONTROL_ACTION_BIND,
-+};
-+
-+struct audio_group {
-+ struct userdata *userdata;
-+ char *id;
-+ char *description;
-+ enum control_action volume_control_action;
-+ enum control_action mute_control_action;
-+ pa_binding_target_info *volume_control_target_info;
-+ pa_binding_target_info *mute_control_target_info;
-+
-+ /* official audio group */
-+ pa_audio_group *group;
-+
-+ struct audio_group_control *volume_control;
-+ struct audio_group_control *mute_control;
-+
-+ bool unlinked;
-+};
-+
-+struct stream {
-+ struct userdata *userdata;
-+ char *id;
-+ enum match_direction direction;
-+ char *audio_group_name_for_volume;
-+ char *audio_group_name_for_mute;
-+ pa_audio_group *audio_group_for_volume;
-+ pa_audio_group *audio_group_for_mute;
-+ pa_binding_target_info *volume_control_target_info;
-+ pa_binding_target_info *mute_control_target_info;
-+ struct expression *rule;
-+
-+ bool unlinked;
-+};
-+
-+struct userdata {
-+ pa_hashmap *audio_groups; /* name -> struct audio_group */
-+ pa_dynarray *streams; /* struct stream */
-+ pa_hook_slot *new_stream_volume;
-+ pa_hook_slot *new_stream_mute;
-+
-+ pa_volume_api *api;
-+
-+ /* The following fields are only used during initialization. */
-+ pa_hashmap *audio_group_names; /* name -> name (hashmap-as-a-set) */
-+ pa_hashmap *unused_audio_groups; /* name -> struct audio_group */
-+ pa_dynarray *stream_names;
-+ pa_hashmap *unused_streams; /* name -> struct stream */
-+};
-+
-+static const char* const valid_modargs[] = {
-+ "filename",
-+ NULL
-+};
-+
-+static void audio_group_unlink(struct audio_group *group);
-+
-+static void print_literal(struct literal *l);
-+static void print_conjunction(struct conjunction *c);
-+static void print_expression(struct expression *e);
-+static void delete_expression(struct expression *e);
-+
-+static struct audio_group *audio_group_new(struct userdata *u, const char *name) {
-+ struct audio_group *group;
-+
-+ pa_assert(u);
-+ pa_assert(name);
-+
-+ group = pa_xnew0(struct audio_group, 1);
-+ group->userdata = u;
-+ group->id = pa_xstrdup(name);
-+ group->description = pa_xstrdup(name);
-+ group->volume_control_action = CONTROL_ACTION_NONE;
-+ group->mute_control_action = CONTROL_ACTION_NONE;
-+
-+ return group;
-+}
-+
-+static int audio_group_put(struct audio_group *group) {
-+ int r;
-+
-+ pa_assert(group);
-+
-+ r = pa_audio_group_new(group->userdata->api, group->id, group->description, &group->group);
-+ if (r < 0)
-+ goto fail;
-+
-+ switch (group->volume_control_action) {
-+ case CONTROL_ACTION_NONE:
-+ break;
-+
-+ case CONTROL_ACTION_CREATE:
-+ pa_audio_group_set_have_own_volume_control(group->group, true);
-+ pa_audio_group_set_volume_control(group->group, group->group->own_volume_control);
-+ break;
-+
-+ case CONTROL_ACTION_BIND:
-+ pa_audio_group_bind_volume_control(group->group, group->volume_control_target_info);
-+ break;
-+ }
-+
-+ switch (group->mute_control_action) {
-+ case CONTROL_ACTION_NONE:
-+ break;
-+
-+ case CONTROL_ACTION_CREATE:
-+ pa_audio_group_set_have_own_mute_control(group->group, true);
-+ pa_audio_group_set_mute_control(group->group, group->group->own_mute_control);
-+ break;
-+
-+ case CONTROL_ACTION_BIND:
-+ pa_audio_group_bind_mute_control(group->group, group->mute_control_target_info);
-+ break;
-+ }
-+
-+ pa_audio_group_put(group->group);
-+
-+ return 0;
-+
-+fail:
-+ audio_group_unlink(group);
-+
-+ return r;
-+}
-+
-+static void audio_group_unlink(struct audio_group *group) {
-+ pa_assert(group);
-+
-+ if (group->unlinked)
-+ return;
-+
-+ group->unlinked = true;
-+
-+ if (group->group) {
-+ pa_audio_group_free(group->group);
-+ group->group = NULL;
-+ }
-+}
-+
-+static void audio_group_free(struct audio_group *group) {
-+ pa_assert(group);
-+
-+ if (!group->unlinked)
-+ audio_group_unlink(group);
-+
-+ if (group->mute_control_target_info)
-+ pa_binding_target_info_free(group->mute_control_target_info);
-+
-+ if (group->volume_control_target_info)
-+ pa_binding_target_info_free(group->volume_control_target_info);
-+
-+ pa_xfree(group->description);
-+ pa_xfree(group->id);
-+ pa_xfree(group);
-+}
-+
-+static void audio_group_set_description(struct audio_group *group, const char *description) {
-+ pa_assert(group);
-+ pa_assert(description);
-+
-+ pa_xfree(group->description);
-+ group->description = pa_xstrdup(description);
-+}
-+
-+static void audio_group_set_volume_control_action(struct audio_group *group, enum control_action action,
-+ pa_binding_target_info *target_info) {
-+ pa_assert(group);
-+ pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
-+
-+ group->volume_control_action = action;
-+
-+ if (group->volume_control_target_info)
-+ pa_binding_target_info_free(group->volume_control_target_info);
-+
-+ if (action == CONTROL_ACTION_BIND)
-+ group->volume_control_target_info = pa_binding_target_info_copy(target_info);
-+ else
-+ group->volume_control_target_info = NULL;
-+}
-+
-+static void audio_group_set_mute_control_action(struct audio_group *group, enum control_action action,
-+ pa_binding_target_info *target_info) {
-+ pa_assert(group);
-+ pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
-+
-+ group->mute_control_action = action;
-+
-+ if (group->mute_control_target_info)
-+ pa_binding_target_info_free(group->mute_control_target_info);
-+
-+ if (action == CONTROL_ACTION_BIND)
-+ group->mute_control_target_info = pa_binding_target_info_copy(target_info);
-+ else
-+ group->mute_control_target_info = NULL;
-+}
-+
-+static struct stream *stream_new(struct userdata *u, const char *name) {
-+ struct stream *stream;
-+
-+ pa_assert(u);
-+ pa_assert(name);
-+
-+ stream = pa_xnew0(struct stream, 1);
-+ stream->userdata = u;
-+ stream->id = pa_xstrdup(name);
-+ stream->direction = match_direction_unknown;
-+
-+ return stream;
-+}
-+
-+static void stream_put(struct stream *stream) {
-+ pa_assert(stream);
-+
-+ if (stream->audio_group_name_for_volume) {
-+ stream->audio_group_for_volume = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_volume);
-+ if (stream->audio_group_for_volume)
-+ stream->volume_control_target_info =
-+ pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_volume->name,
-+ PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL);
-+ else
-+ pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
-+ }
-+
-+ if (stream->audio_group_name_for_mute) {
-+ stream->audio_group_for_mute = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_mute);
-+ if (stream->audio_group_for_mute)
-+ stream->mute_control_target_info =
-+ pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_mute->name,
-+ PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL);
-+ else
-+ pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
-+ }
-+}
-+
-+static void stream_unlink(struct stream *stream) {
-+ pa_assert(stream);
-+
-+ if (stream->unlinked)
-+ return;
-+
-+ if (stream->mute_control_target_info) {
-+ pa_binding_target_info_free(stream->mute_control_target_info);
-+ stream->mute_control_target_info = NULL;
-+ }
-+
-+ if (stream->volume_control_target_info) {
-+ pa_binding_target_info_free(stream->volume_control_target_info);
-+ stream->volume_control_target_info = NULL;
-+ }
-+
-+ stream->unlinked = true;
-+}
-+
-+static void stream_free(struct stream *stream) {
-+ pa_assert(stream);
-+
-+ if (!stream->unlinked)
-+ stream_unlink(stream);
-+
-+ if (stream->rule)
-+ delete_expression(stream->rule);
-+
-+ pa_xfree(stream->audio_group_name_for_mute);
-+ pa_xfree(stream->audio_group_name_for_volume);
-+ pa_xfree(stream->id);
-+ pa_xfree(stream);
-+}
-+
-+static void stream_set_audio_group_name_for_volume(struct stream *stream, const char *name) {
-+ pa_assert(stream);
-+
-+ pa_xfree(stream->audio_group_name_for_volume);
-+ stream->audio_group_name_for_volume = pa_xstrdup(name);
-+}
-+
-+static void stream_set_audio_group_name_for_mute(struct stream *stream, const char *name) {
-+ pa_assert(stream);
-+
-+ pa_xfree(stream->audio_group_name_for_mute);
-+ stream->audio_group_name_for_mute = pa_xstrdup(name);
-+}
-+
-+/* stream classification */
-+
-+static bool match_predicate(struct literal *l, pas_stream *d) {
-+
-+ if (l->stream_direction != match_direction_unknown) {
-+ /* check the stream direction; _sink inputs_ are always _outputs_ */
-+
-+ if ((d->direction == PA_DIRECTION_OUTPUT && l->stream_direction == match_direction_output) ||
-+ ((d->direction == PA_DIRECTION_INPUT && l->stream_direction == match_direction_input))) {
-+ return true;
-+ }
-+ }
-+ else if (l->property_name && l->property_value) {
-+ /* check the property from the property list */
-+
-+ if (pa_proplist_contains(d->proplist, l->property_name) &&
-+ strcmp(pa_proplist_gets(d->proplist, l->property_name), l->property_value) == 0)
-+ return true;
-+ }
-+
-+ /* no match */
-+ return false;
-+}
-+
-+static bool match_rule(struct expression *e, pas_stream *d) {
-+
-+ struct conjunction *c;
-+
-+ PA_LLIST_FOREACH(c, e->conjunctions) {
-+ struct literal *l;
-+ bool and_success = true;
-+ PA_LLIST_FOREACH(l, c->literals) {
-+ if (!match_predicate(l, d)) {
-+ /* at least one fail for conjunction */
-+ and_success = false;
-+ break;
-+ }
-+ }
-+
-+ if (and_success) {
-+ /* at least one match for disjunction */
-+ return true;
-+ }
-+ }
-+
-+ /* no matches */
-+ return false;
-+}
-+
-+static void classify_stream(struct userdata *u, pas_stream *new_data, bool mute) {
-+ /* do the classification here */
-+
-+ struct stream *stream = NULL;
-+ unsigned idx;
-+
-+ /* go through the stream match definitions in given order */
-+
-+ PA_DYNARRAY_FOREACH(stream, u->streams, idx) {
-+ if (stream->rule && match_rule(stream->rule, new_data)) {
-+ pa_log_info("stream %s (%s) match with rule %s:", new_data->name, new_data->description, stream->id);
-+ print_expression(stream->rule);
-+
-+ if (mute) {
-+ if (new_data->use_default_mute_control && stream->audio_group_for_mute)
-+ pas_stream_bind_mute_control(new_data, stream->mute_control_target_info);
-+ } else {
-+ if (new_data->use_default_volume_control && stream->audio_group_for_volume)
-+ pas_stream_bind_volume_control(new_data, stream->volume_control_target_info);
-+ }
-+
-+ return;
-+ }
-+ }
-+
-+ /* no matches, don't touch the volumes */
-+}
-+
-+static pa_hook_result_t set_volume_control_cb(
-+ void *hook_data,
-+ pas_stream *new_data,
-+ struct userdata *u) {
-+
-+ pa_assert(new_data);
-+ pa_assert(u);
-+
-+ classify_stream(u, new_data, false);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t set_mute_control_cb(
-+ void *hook_data,
-+ pas_stream *new_data,
-+ struct userdata *u) {
-+
-+ pa_assert(new_data);
-+ pa_assert(u);
-+
-+ classify_stream(u, new_data, true);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+/* parser for configuration file */
-+
-+/*
-+ Parse the match expression. The syntax is this:
-+
-+ OPER := "AND" | "OR"
-+ OPEN_BRACE := "("
-+ CLOSE_BRACE := ")"
-+ EXPR := OPEN_BRACE EXPR OPER EXPR CLOSE_BRACE | VAR
-+ VAR := LIT | "NEG" LIT
-+ LIT := PREDICATE (defined by rule semantics)
-+
-+ In addition there is a requirement that the expressions need to be in
-+ disjunctive normal form. It means that if there is an expression that
-+ has AND operator, there may not be any OR operators in its subexpressions.
-+
-+ Example expressions:
-+
-+ (foo)
-+ (foo AND bar)
-+ (foo OR (bar AND xxx))
-+ (NEG foo OR (bar AND NEG xxx))
-+
-+ The predicate here is the single rule that is matched against the new sink
-+ input. The syntax is this:
-+
-+ PREDICATE := "direction" DIRECTION | "property" PROPERTY
-+ DIRECTION := "input" | "output"
-+ PROPERTY := PROPERTY_NAME "=" PROPERTY_VALUE
-+ PROPERTY_NAME := STRING
-+ PROPERTY_VALUE := STRING
-+
-+ The allowed characters for STRING are standard ascii characters. Not
-+ allowed substrings are the reserved words "AND", "OR", "(", ")", "NEG" and
-+ "=".
-+
-+ Complete examples:
-+
-+ (property application.process.binary=paplay)
-+
-+ (property media.role=music AND direction input)
-+
-+ (property application.process.binary=paplay OR (direction input OR direction output))
-+*/
-+
-+static void print_literal(struct literal *l) {
-+ if (l->stream_direction != match_direction_unknown) {
-+ pa_log_info(" %sstream direction %s",
-+ l->negation ? "NEG " : "",
-+ l->stream_direction == match_direction_input ? "input" : "output");
-+ }
-+ else {
-+ pa_log_info(" %sproperty %s == %s",
-+ l->negation ? "NEG " : "",
-+ l->property_name ? l->property_name : "NULL",
-+ l->property_value ? l->property_value : "NULL");
-+ }
-+}
-+
-+static void print_conjunction(struct conjunction *c) {
-+ struct literal *l;
-+ pa_log_info(" conjunction for literals:");
-+ PA_LLIST_FOREACH(l, c->literals) {
-+ print_literal(l);
-+ }
-+}
-+
-+static void print_expression(struct expression *e) {
-+ struct conjunction *c;
-+ pa_log_info("disjunction for conjunctions:");
-+ PA_LLIST_FOREACH(c, e->conjunctions) {
-+ print_conjunction(c);
-+ }
-+}
-+
-+static void delete_literal(struct literal *l) {
-+
-+ if (!l)
-+ return;
-+
-+ pa_xfree(l->property_name);
-+ pa_xfree(l->property_value);
-+ pa_xfree(l);
-+}
-+
-+static void delete_conjunction(struct conjunction *c) {
-+ struct literal *l;
-+
-+ if (!c)
-+ return;
-+
-+ PA_LLIST_FOREACH(l, c->literals) {
-+ delete_literal(l);
-+ }
-+
-+ pa_xfree(c);
-+}
-+
-+static void delete_expression(struct expression *e) {
-+ struct conjunction *c;
-+
-+ PA_LLIST_FOREACH(c, e->conjunctions) {
-+ delete_conjunction(c);
-+ }
-+
-+ pa_xfree(e);
-+}
-+
-+enum logic_operator {
-+ operator_not_set = 0,
-+ operator_and,
-+ operator_or,
-+};
-+
-+struct expression_token {
-+ struct expression_token *left;
-+ struct expression_token *right;
-+
-+ enum logic_operator oper;
-+
-+ struct literal_token *lit;
-+};
-+
-+struct literal_token {
-+ bool negation;
-+ char *var;
-+};
-+
-+static void delete_literal_token(struct literal_token *l) {
-+
-+ if (!l)
-+ return;
-+
-+ pa_xfree(l->var);
-+ pa_xfree(l);
-+}
-+
-+static void delete_expression_token(struct expression_token *e) {
-+
-+ if (!e)
-+ return;
-+
-+ delete_expression_token(e->left);
-+ delete_expression_token(e->right);
-+ delete_literal_token(e->lit);
-+
-+ e->left = NULL;
-+ e->right = NULL;
-+ e->lit = NULL;
-+
-+ pa_xfree(e);
-+}
-+
-+static struct expression_token *parse_rule_internal(const char *rule, bool disjunction_allowed) {
-+
-+ int len = strlen(rule);
-+ struct expression_token *et;
-+ char *p;
-+ int brace_count = 0;
-+ bool braces_present = false;
-+ char left_buf[len];
-+ char right_buf[len];
-+
-+#if 0
-+ /* check if the rule is still valid */
-+
-+ if (len < 2)
-+ return NULL;
-+
-+ if (rule[0] != '(' || rule[len-1] != ')')
-+ return NULL;
-+#endif
-+
-+ et = pa_xnew0(struct expression_token, 1);
-+
-+ if (!et)
-+ return NULL;
-+
-+ /* count the braces -- we want to find the case when there is only one brace open */
-+
-+ p = (char *) rule;
-+
-+ while (*p) {
-+ if (*p == '(') {
-+ braces_present = true;
-+ brace_count++;
-+ }
-+ else if (*p == ')') {
-+ brace_count--;
-+ }
-+
-+ if (brace_count == 1) {
-+
-+ /* the parser is recursive and just goes down the tree on the
-+ * topmost level (where the brace count is 1). If there are no
-+ * braces this is a literal */
-+
-+ /* find the operator AND or OR */
-+
-+ if (strncmp(p, "AND", 3) == 0) {
-+
-+ /* copy parts */
-+ char *begin_left = (char *) rule+1;
-+ char *begin_right = p+3;
-+
-+ int left_len = p - rule - 1; /* minus '(' */
-+ int right_len = len - 3 - left_len - 2; /* minus AND and '(' and ')'*/
-+
-+ memcpy(left_buf, begin_left, left_len);
-+ left_buf[left_len] = '\0';
-+ memcpy(right_buf, begin_right, right_len);
-+ right_buf[right_len] = '\0';
-+
-+ et->left = parse_rule_internal(left_buf, false);
-+ et->right = parse_rule_internal(right_buf, false);
-+ et->oper = operator_and;
-+
-+ if (!et->left || !et->right) {
-+ delete_expression_token(et);
-+ return NULL;
-+ }
-+
-+ return et;
-+ }
-+ else if (strncmp(p, "OR", 2) == 0) {
-+
-+ char *begin_left = (char *) rule+1;
-+ char *begin_right = p+2;
-+
-+ int left_len = p - rule - 1; /* minus '(' */
-+ int right_len = len - 2 - left_len - 2; /* minus OR and '(' and ')'*/
-+
-+ if (!disjunction_allowed) {
-+ pa_log_error("logic expression not in dnf");
-+ delete_expression_token(et);
-+ return NULL;
-+ }
-+
-+ memcpy(left_buf, begin_left, left_len);
-+ left_buf[left_len] = '\0';
-+ memcpy(right_buf, begin_right, right_len);
-+ right_buf[right_len] = '\0';
-+
-+ et->left = parse_rule_internal(left_buf, true);
-+ et->right = parse_rule_internal(right_buf, true);
-+ et->oper = operator_or;
-+
-+ if (!et->left || !et->right) {
-+ delete_expression_token(et);
-+ return NULL;
-+ }
-+
-+ return et;
-+ }
-+ /* else a literal which is inside braces */
-+ }
-+
-+ p++;
-+ }
-+
-+ if (brace_count != 0) {
-+ /* the input is not valid */
-+ pa_log_error("mismatched braces in logic expression");
-+ delete_expression_token(et);
-+ return NULL;
-+ }
-+ else {
-+ /* this is a literal */
-+ char *begin_lit;
-+ char buf[strlen(rule)+1];
-+
-+ struct literal_token *lit = pa_xnew0(struct literal_token, 1);
-+ if (!lit) {
-+ delete_expression_token(et);
-+ return NULL;
-+ }
-+
-+ if (braces_present) {
-+ /* remove all braces */
-+ char *k;
-+ char *l;
-+
-+ k = (char *) rule;
-+ l = buf;
-+
-+ while (*k) {
-+ if (*k == '(' || *k == ')') {
-+ k++;
-+ continue;
-+ }
-+
-+ *l = *k;
-+ l++;
-+ k++;
-+ }
-+ *l = '\0';
-+ }
-+ else {
-+ strncpy(buf, rule, sizeof(buf));
-+ }
-+
-+ if (strncmp(buf, "NEG", 3) == 0) {
-+ begin_lit = (char *) buf + 3;
-+ lit->negation = true;
-+ }
-+ else {
-+ begin_lit = (char *) buf;
-+ lit->negation = false;
-+ }
-+
-+ lit->var = pa_xstrdup(begin_lit);
-+ et->lit = lit;
-+ }
-+
-+ return et;
-+}
-+
-+static bool gather_literal(struct expression_token *et, struct literal *l) {
-+#define PROPERTY_KEYWORD "property"
-+#define DIRECTION_KEYWORD "direction"
-+#define DIRECTION_VALUE_INPUT "input"
-+#define DIRECTION_VALUE_OUTPUT "output"
-+
-+ char *p = et->lit->var;
-+ int len = strlen(et->lit->var);
-+
-+ l->negation = et->lit->negation;
-+
-+ if (strncmp(p, PROPERTY_KEYWORD, strlen(PROPERTY_KEYWORD)) == 0) {
-+ char name[len];
-+ char value[len];
-+ int i = 0;
-+
-+ p += strlen(PROPERTY_KEYWORD);
-+
-+ /* parse the property pair: name=value */
-+
-+ while (*p && *p != '=') {
-+ name[i++] = *p;
-+ p++;
-+ }
-+
-+ /* check if we really found '=' */
-+
-+ if (*p != '=') {
-+ pa_log_error("property syntax broken for '%s'", et->lit->var);
-+ goto error;
-+ }
-+
-+ name[i] = '\0';
-+
-+ p++;
-+ i = 0;
-+
-+ while (*p) {
-+ value[i++] = *p;
-+ p++;
-+ }
-+
-+ value[i] = '\0';
-+
-+ l->property_name = pa_xstrdup(name);
-+ l->property_value = pa_xstrdup(value);
-+ }
-+ else if (strncmp(p, DIRECTION_KEYWORD, strlen(DIRECTION_KEYWORD)) == 0) {
-+ p += strlen(DIRECTION_KEYWORD);
-+
-+ if (strncmp(p, DIRECTION_VALUE_INPUT, strlen(DIRECTION_VALUE_INPUT)) == 0) {
-+ l->stream_direction = match_direction_input;
-+ }
-+ else if (strncmp(p, DIRECTION_VALUE_OUTPUT, strlen(DIRECTION_VALUE_OUTPUT)) == 0) {
-+ l->stream_direction = match_direction_output;
-+ }
-+ else {
-+ pa_log_error("unknown direction(%s): %s", et->lit->var, p);
-+ goto error;
-+ }
-+ }
-+ else {
-+ pa_log_error("not able to parse the value: '%s'", et->lit->var);
-+ goto error;
-+ }
-+
-+ return true;
-+
-+error:
-+ return false;
-+
-+#undef DIRECTION_VALUE_OUTPUT
-+#undef DIRECTION_VALUE_INPUT
-+#undef DIRECTION_KEYWORD
-+#undef PROPERTY_KEYWORD
-+}
-+
-+static bool gather_conjunction(struct expression_token *et, struct conjunction *c) {
-+
-+ if (et->oper == operator_and) {
-+ if (!gather_conjunction(et->left, c) ||
-+ !gather_conjunction(et->right, c))
-+ return false;
-+ }
-+ else {
-+ /* literal */
-+ struct literal *l = pa_xnew0(struct literal, 1);
-+
-+ if (!l)
-+ return false;
-+
-+ gather_literal(et, l);
-+
-+ PA_LLIST_PREPEND(struct literal, c->literals, l);
-+ }
-+
-+ return true;
-+}
-+
-+static bool gather_expression(struct expression *e, struct expression_token *et) {
-+
-+ if (et->oper == operator_or) {
-+ if (!gather_expression(e, et->right) ||
-+ !gather_expression(e, et->left))
-+ return false;
-+ }
-+ else {
-+ /* conjunction or literal */
-+ struct conjunction *c = pa_xnew0(struct conjunction, 1);
-+ if (!gather_conjunction(et, c))
-+ return false;
-+
-+ PA_LLIST_PREPEND(struct conjunction, e->conjunctions, c);
-+ }
-+
-+ return true;
-+}
-+
-+static struct expression *parse_rule(const char *rule_string) {
-+ char *k, *l;
-+ struct expression *e = NULL;
-+ int len;
-+ char *buf = NULL;
-+ struct expression_token *et = NULL;
-+
-+ if (!rule_string)
-+ goto error;
-+
-+ len = strlen(rule_string);
-+
-+ buf = (char *) pa_xmalloc0(len);
-+
-+ if (!buf)
-+ goto error;
-+
-+ /* remove whitespace */
-+
-+ k = (char *) rule_string;
-+ l = buf;
-+
-+ while (*k) {
-+ if (*k == ' ') {
-+ k++;
-+ continue;
-+ }
-+
-+ *l = *k;
-+ l++;
-+ k++;
-+ }
-+
-+ /* et is the root of an expression tree */
-+ et = parse_rule_internal(buf, true);
-+
-+ if (!et)
-+ goto error;
-+
-+ e = pa_xnew0(struct expression, 1);
-+
-+ if (!e)
-+ goto error;
-+
-+ /* gather expressions to actual match format */
-+ gather_expression(e, et);
-+
-+#if 1
-+ print_expression(e);
-+#endif
-+
-+ /* free memory */
-+ delete_expression_token(et);
-+ pa_xfree(buf);
-+
-+ return e;
-+
-+error:
-+ delete_expression_token(et);
-+ pa_xfree(buf);
-+ pa_xfree(e);
-+ return NULL;
-+}
-+
-+static int parse_audio_groups(pa_config_parser_state *state) {
-+ struct userdata *u;
-+ char *name;
-+ const char *split_state = NULL;
-+
-+ pa_assert(state);
-+
-+ u = state->userdata;
-+
-+ pa_hashmap_remove_all(u->audio_group_names);
-+
-+ while ((name = pa_split_spaces(state->rvalue, &split_state)))
-+ pa_hashmap_put(u->audio_group_names, name, name);
-+
-+ return 0;
-+}
-+
-+static int parse_streams(pa_config_parser_state *state) {
-+ struct userdata *u;
-+ char *name;
-+ const char *split_state = NULL;
-+
-+ pa_assert(state);
-+
-+ u = state->userdata;
-+
-+ pa_dynarray_remove_all(u->stream_names);
-+
-+ while ((name = pa_split_spaces(state->rvalue, &split_state))) {
-+ const char *name2;
-+ unsigned idx;
-+ bool duplicate = false;
-+
-+ /* Avoid adding duplicates in u->stream_names. */
-+ PA_DYNARRAY_FOREACH(name2, u->stream_names, idx) {
-+ if (pa_streq(name, name2)) {
-+ duplicate = true;
-+ break;
-+ }
-+ }
-+
-+ if (duplicate) {
-+ pa_xfree(name);
-+ continue;
-+ }
-+
-+ pa_dynarray_append(u->stream_names, name);
-+ }
-+
-+ return 0;
-+}
-+
-+static int parse_common(pa_config_parser_state *state) {
-+#define AUDIOGROUP_START "AudioGroup "
-+#define STREAM_START "Stream "
-+#define BIND_KEYWORD "bind:"
-+#define NONE_KEYWORD "none"
-+
-+ char *section;
-+ struct userdata *u = (struct userdata *) state->userdata;
-+ int r;
-+ pa_binding_target_info *target_info;
-+
-+ pa_assert(state);
-+
-+ section = state->section;
-+ if (!section)
-+ goto error;
-+
-+ if (strncmp(section, AUDIOGROUP_START, strlen(AUDIOGROUP_START)) == 0) {
-+ char *ag_name = section + strlen(AUDIOGROUP_START);
-+ struct audio_group *ag = (struct audio_group *) pa_hashmap_get(u->unused_audio_groups, ag_name);
-+
-+ if (!ag) {
-+ /* first item for this audio group section, so create the struct */
-+ ag = audio_group_new(u, ag_name);
-+ pa_hashmap_put(u->unused_audio_groups, ag->id, ag);
-+ }
-+
-+ if (strcmp(state->lvalue, "description") == 0)
-+ audio_group_set_description(ag, state->rvalue);
-+
-+ else if (strcmp(state->lvalue, "volume-control") == 0) {
-+ if (pa_streq(state->rvalue, "create"))
-+ audio_group_set_volume_control_action(ag, CONTROL_ACTION_CREATE, NULL);
-+
-+ else if (pa_streq(state->rvalue, NONE_KEYWORD))
-+ audio_group_set_volume_control_action(ag, CONTROL_ACTION_NONE, NULL);
-+
-+ else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
-+ r = pa_binding_target_info_new_from_string(state->rvalue, "volume_control", &target_info);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-+ goto error;
-+ }
-+
-+ audio_group_set_volume_control_action(ag, CONTROL_ACTION_BIND, target_info);
-+ pa_binding_target_info_free(target_info);
-+
-+ } else {
-+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-+ goto error;
-+ }
-+ }
-+ else if (strcmp(state->lvalue, "mute-control") == 0) {
-+ if (pa_streq(state->rvalue, "create"))
-+ audio_group_set_mute_control_action(ag, CONTROL_ACTION_CREATE, NULL);
-+
-+ else if (pa_streq(state->rvalue, NONE_KEYWORD))
-+ audio_group_set_mute_control_action(ag, CONTROL_ACTION_NONE, NULL);
-+
-+ else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
-+ r = pa_binding_target_info_new_from_string(state->rvalue, "mute_control", &target_info);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-+ goto error;
-+ }
-+
-+ audio_group_set_mute_control_action(ag, CONTROL_ACTION_BIND, target_info);
-+ pa_binding_target_info_free(target_info);
-+
-+ } else {
-+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-+ goto error;
-+ }
-+ }
-+ }
-+ else if (strncmp(section, STREAM_START, strlen(STREAM_START)) == 0) {
-+ char *stream_name = section + strlen(STREAM_START);
-+
-+ struct stream *stream = (struct stream *) pa_hashmap_get(u->unused_streams, stream_name);
-+
-+ if (!stream) {
-+ /* first item for this stream section, so create the struct */
-+ stream = stream_new(u, stream_name);
-+ pa_hashmap_put(u->unused_streams, stream->id, stream);
-+ }
-+
-+ if (pa_streq(state->lvalue, "audio-group-for-volume"))
-+ stream_set_audio_group_name_for_volume(stream, *state->rvalue ? state->rvalue : NULL);
-+
-+ else if (pa_streq(state->lvalue, "audio-group-for-mute"))
-+ stream_set_audio_group_name_for_mute(stream, *state->rvalue ? state->rvalue : NULL);
-+
-+ else if (strcmp(state->lvalue, "match") == 0) {
-+ if (!state->rvalue)
-+ goto error;
-+
-+ stream->rule = parse_rule(state->rvalue);
-+
-+ if (!stream->rule) {
-+ goto error;
-+ }
-+ }
-+ }
-+
-+ return 0;
-+
-+error:
-+
-+ pa_log_error("failed parsing audio group definition file");
-+ return -1;
-+
-+#undef NONE_KEYWORD
-+#undef AUDIO_GROUP_KEYWORD
-+#undef BIND_KEYWORD
-+#undef STREAM_START
-+#undef AUDIOGROUP_START
-+}
-+
-+static void finalize_config(struct userdata *u) {
-+ const char *group_name;
-+ void *state;
-+ struct audio_group *group;
-+ const char *stream_name;
-+ unsigned idx;
-+ struct stream *stream;
-+
-+ pa_assert(u);
-+
-+ PA_HASHMAP_FOREACH(group_name, u->audio_group_names, state) {
-+ int r;
-+
-+ group = pa_hashmap_remove(u->unused_audio_groups, group_name);
-+ if (!group)
-+ group = audio_group_new(u, group_name);
-+
-+ r = audio_group_put(group);
-+ if (r < 0) {
-+ pa_log("Failed to create audio group %s.", group_name);
-+ audio_group_free(group);
-+ continue;
-+ }
-+
-+ pa_assert_se(pa_hashmap_put(u->audio_groups, group->id, group) >= 0);
-+ }
-+
-+ PA_HASHMAP_FOREACH(group, u->unused_audio_groups, state)
-+ pa_log_debug("Audio group %s is not used.", group->id);
-+
-+ pa_hashmap_free(u->unused_audio_groups);
-+ u->unused_audio_groups = NULL;
-+
-+ pa_hashmap_free(u->audio_group_names);
-+ u->audio_group_names = NULL;
-+
-+ PA_DYNARRAY_FOREACH(stream_name, u->stream_names, idx) {
-+ stream = pa_hashmap_remove(u->unused_streams, stream_name);
-+ if (!stream) {
-+ pa_log("Reference to undefined stream %s, ignoring.", stream_name);
-+ continue;
-+ }
-+
-+ stream_put(stream);
-+ pa_dynarray_append(u->streams, stream);
-+ }
-+
-+ PA_HASHMAP_FOREACH(stream, u->unused_streams, state)
-+ pa_log_debug("Stream %s is not used.", stream->id);
-+
-+ pa_hashmap_free(u->unused_streams);
-+ u->unused_streams = NULL;
-+
-+ pa_dynarray_free(u->stream_names);
-+ u->stream_names = NULL;
-+}
-+
-+static bool parse_configuration(struct userdata *u, const char *filename) {
-+ FILE *f;
-+ char *fn = NULL;
-+
-+ pa_config_item table[] = {
-+ { "audio-groups", parse_audio_groups, NULL, "General" },
-+ { "streams", parse_streams, NULL, "General" },
-+ { NULL, parse_common, NULL, NULL },
-+ { NULL, NULL, NULL, NULL },
-+ };
-+
-+ u->audio_group_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
-+ u->unused_audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) audio_group_free);
-+ u->stream_names = pa_dynarray_new(pa_xfree);
-+ u->unused_streams = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) stream_free);
-+
-+ if (pa_is_path_absolute(filename))
-+ f = pa_open_config_file(filename, NULL, NULL, &fn);
-+ else {
-+ char *sys_conf_file;
-+
-+ sys_conf_file = pa_sprintf_malloc(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "%s", filename);
-+ f = pa_open_config_file(sys_conf_file, filename, NULL, &fn);
-+ pa_xfree(sys_conf_file);
-+ }
-+
-+ if (f) {
-+ pa_config_parse(fn, f, table, NULL, u);
-+ pa_xfree(fn);
-+ fn = NULL;
-+ fclose(f);
-+ f = NULL;
-+ }
-+
-+ finalize_config(u);
-+
-+ return true;
-+}
-+
-+void pa__done(pa_module *m) {
-+ struct userdata* u;
-+
-+ pa_assert(m);
-+
-+ u = (struct userdata *) m->userdata;
-+
-+ if (!u)
-+ return;
-+
-+ if (u->new_stream_volume)
-+ pa_hook_slot_free(u->new_stream_volume);
-+
-+ if (u->new_stream_mute)
-+ pa_hook_slot_free(u->new_stream_mute);
-+
-+ if (u->streams)
-+ pa_dynarray_free(u->streams);
-+
-+ if (u->audio_groups)
-+ pa_hashmap_free(u->audio_groups);
-+
-+ if (u->api)
-+ pa_volume_api_unref(u->api);
-+
-+ pa_xfree(u);
-+}
-+
-+int pa__init(pa_module *m) {
-+ pa_modargs *ma = NULL;
-+ struct userdata *u;
-+ const char *filename;
-+
-+ pa_assert(m);
-+
-+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-+ pa_log("Failed to parse module arguments");
-+ goto error;
-+ }
-+
-+ u = m->userdata = pa_xnew0(struct userdata, 1);
-+
-+ if (!u)
-+ goto error;
-+
-+ u->api = pa_volume_api_get(m->core);
-+
-+ if (!u->api)
-+ goto error;
-+
-+ u->audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) audio_group_free);
-+ u->streams = pa_dynarray_new((pa_free_cb_t) stream_free);
-+
-+ filename = pa_modargs_get_value(ma, "filename", AUDIO_GROUP_CONFIG);
-+
-+ if (!parse_configuration(u, filename))
-+ goto error;
-+
-+ u->new_stream_volume = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_volume_control_cb, u);
-+ u->new_stream_mute = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_mute_control_cb, u);
-+
-+ if (!u->new_stream_volume || !u->new_stream_mute)
-+ goto error;
-+
-+ pa_modargs_free(ma);
-+
-+ return 0;
-+
-+error:
-+ pa__done(m);
-+
-+ if (ma)
-+ pa_modargs_free(ma);
-+
-+ return -1;
-+}
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Wed, 21 May 2014 14:13:41 +0300
-Subject: Add module-main-volume-policy
-
-Change-Id: I787141b43cafb652aa752c64ae28b6b7aa052d8e
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- Makefile.am | 3 +
- src/Makefile.am | 15 +
- src/daemon/default.pa.in | 4 +
- .../main-volume-policy/main-volume-context.c | 325 ++++++++++++
- .../main-volume-policy/main-volume-context.h | 75 +++
- .../main-volume-policy/main-volume-policy.c | 213 ++++++++
- .../main-volume-policy.conf.example | 20 +
- .../main-volume-policy/main-volume-policy.h | 72 +++
- .../main-volume-policy/module-main-volume-policy.c | 556 +++++++++++++++++++++
- 9 files changed, 1283 insertions(+)
- create mode 100644 src/modules/main-volume-policy/main-volume-context.c
- create mode 100644 src/modules/main-volume-policy/main-volume-context.h
- create mode 100644 src/modules/main-volume-policy/main-volume-policy.c
- create mode 100644 src/modules/main-volume-policy/main-volume-policy.conf.example
- create mode 100644 src/modules/main-volume-policy/main-volume-policy.h
- create mode 100644 src/modules/main-volume-policy/module-main-volume-policy.c
-
-diff --git a/Makefile.am b/Makefile.am
-index cf4a648..646b7fc 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -61,6 +61,9 @@ moduledevinternaldir = $(includedir)/pulsemodule/pulse
- moduledevvolumeapi_DATA = src/modules/volume-api/*.h
- moduledevvolumeapidir = $(includedir)/pulsemodule/modules/volume-api
-
-+moduledevmainvolumepolicy_DATA = src/modules/main-volume-policy/*.h
-+moduledevmainvolumepolicydir = $(includedir)/pulsemodule/modules/main-volume-policy
-+
- filterdir = /etc/pulse/filter
- filter_DATA = filter/filter_44100_48000.dat \
- filter/filter_44100_8000.dat \
-diff --git a/src/Makefile.am b/src/Makefile.am
-index a6bb319..8fa60ec 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1017,6 +1017,7 @@ libpulsecore_foreign_la_CFLAGS = $(AM_CFLAGS) $(FOREIGN_CFLAGS)
-
- modlibexec_LTLIBRARIES = \
- libcli.la \
-+ libmain-volume-policy.la \
- libprotocol-cli.la \
- libprotocol-simple.la \
- libprotocol-http.la \
-@@ -1051,6 +1052,12 @@ libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
- libcli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
- libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
-
-+libmain_volume_policy_la_SOURCES = \
-+ modules/main-volume-policy/main-volume-context.c modules/main-volume-policy/main-volume-context.h \
-+ modules/main-volume-policy/main-volume-policy.c modules/main-volume-policy/main-volume-policy.h
-+libmain_volume_policy_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
-+libmain_volume_policy_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libvolume-api.la
-+
- libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
- libprotocol_cli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
- libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libcli.la
-@@ -1133,6 +1140,7 @@ endif
- modlibexec_LTLIBRARIES += \
- module-cli.la \
- module-cli-protocol-tcp.la \
-+ module-main-volume-policy.la \
- module-simple-protocol-tcp.la \
- module-volume-api.la \
- module-null-sink.la \
-@@ -1426,6 +1434,7 @@ SYMDEF_FILES = \
- module-cli-symdef.h \
- module-cli-protocol-tcp-symdef.h \
- module-cli-protocol-unix-symdef.h \
-+ module-main-volume-policy-symdef.h \
- module-pipe-sink-symdef.h \
- module-pipe-source-symdef.h \
- module-simple-protocol-tcp-symdef.h \
-@@ -1575,6 +1584,12 @@ module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_
- module_cli_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
- module_cli_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-cli.la
-
-+# Main volume and mute policy
-+
-+module_main_volume_policy_la_SOURCES = modules/main-volume-policy/module-main-volume-policy.c
-+module_main_volume_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
-+module_main_volume_policy_la_LIBADD = $(MODULE_LIBADD) libmain-volume-policy.la libvolume-api.la
-+
- # HTTP protocol
-
- module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
-diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
-index 7cf52a4..f70804c 100755
---- a/src/daemon/default.pa.in
-+++ b/src/daemon/default.pa.in
-@@ -197,6 +197,10 @@ load-module module-volume-api
- load-module module-audio-groups
- .endif
-
-+.ifexists module-main-volume-policy
-+load-module module-main-volume-policy
-+.endif
-+
- ### Make some devices default
- #set-default-sink output
- #set-default-source input
-diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c
-new file mode 100644
-index 0000000..7ac35c6
---- /dev/null
-+++ b/src/modules/main-volume-policy/main-volume-context.c
-@@ -0,0 +1,325 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "main-volume-context.h"
-+
-+#include <modules/volume-api/mute-control.h>
-+#include <modules/volume-api/volume-control.h>
-+
-+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
-+ pa_main_volume_context **context) {
-+ pa_main_volume_context *context_local;
-+ int r;
-+
-+ pa_assert(policy);
-+ pa_assert(name);
-+ pa_assert(description);
-+ pa_assert(context);
-+
-+ context_local = pa_xnew0(struct pa_main_volume_context, 1);
-+ context_local->main_volume_policy = policy;
-+ context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
-+
-+ r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
-+ if (r < 0)
-+ goto fail;
-+
-+ context_local->description = pa_xstrdup(description);
-+
-+ *context = context_local;
-+
-+ return 0;
-+
-+fail:
-+ pa_main_volume_context_free(context_local);
-+
-+ return r;
-+}
-+
-+void pa_main_volume_context_put(pa_main_volume_context *context) {
-+ pa_assert(context);
-+
-+ pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
-+
-+ context->linked = true;
-+
-+ pa_log_debug("Created main volume context #%u.", context->index);
-+ pa_log_debug(" Name: %s", context->name);
-+ pa_log_debug(" Description: %s", context->description);
-+ pa_log_debug(" Main output volume control: %s",
-+ context->main_output_volume_control ? context->main_output_volume_control->name : "(unset)");
-+ pa_log_debug(" Main input volume control: %s",
-+ context->main_input_volume_control ? context->main_input_volume_control->name : "(unset)");
-+ pa_log_debug(" Main output mute control: %s",
-+ context->main_output_mute_control ? context->main_output_mute_control->name : "(unset)");
-+ pa_log_debug(" Main input mute control: %s",
-+ context->main_input_mute_control ? context->main_input_mute_control->name : "(unset)");
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT], context);
-+}
-+
-+void pa_main_volume_context_unlink(pa_main_volume_context *context) {
-+ pa_assert(context);
-+
-+ if (context->unlinked) {
-+ pa_log_debug("Unlinking main volume context %s (already unlinked, this is a no-op).", context->name);
-+ return;
-+ }
-+
-+ context->unlinked = true;
-+
-+ pa_log_debug("Unlinking main volume context %s.", context->name);
-+
-+ if (context->linked)
-+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
-+
-+ if (context->main_input_mute_control_binding) {
-+ pa_binding_free(context->main_input_mute_control_binding);
-+ context->main_input_mute_control_binding = NULL;
-+ }
-+
-+ if (context->main_output_mute_control_binding) {
-+ pa_binding_free(context->main_output_mute_control_binding);
-+ context->main_output_mute_control_binding = NULL;
-+ }
-+
-+ if (context->main_input_volume_control_binding) {
-+ pa_binding_free(context->main_input_volume_control_binding);
-+ context->main_input_volume_control_binding = NULL;
-+ }
-+
-+ if (context->main_output_volume_control_binding) {
-+ pa_binding_free(context->main_output_volume_control_binding);
-+ context->main_output_volume_control_binding = NULL;
-+ }
-+
-+ context->main_input_mute_control = NULL;
-+ context->main_output_mute_control = NULL;
-+ context->main_input_volume_control = NULL;
-+ context->main_output_volume_control = NULL;
-+
-+ pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
-+}
-+
-+void pa_main_volume_context_free(pa_main_volume_context *context) {
-+ pa_assert(context);
-+
-+ if (!context->unlinked)
-+ pa_main_volume_context_unlink(context);
-+
-+ pa_xfree(context->description);
-+
-+ if (context->name)
-+ pa_main_volume_policy_unregister_name(context->main_volume_policy, context->name);
-+
-+ pa_xfree(context);
-+}
-+
-+const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
-+ pa_assert(context);
-+
-+ return context->name;
-+}
-+
-+static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
-+ pa_volume_control *old_control;
-+
-+ pa_assert(context);
-+
-+ old_control = context->main_output_volume_control;
-+
-+ if (control == old_control)
-+ return;
-+
-+ context->main_output_volume_control = control;
-+
-+ if (!context->linked || context->unlinked)
-+ return;
-+
-+ pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
-+ context);
-+}
-+
-+void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info) {
-+ pa_binding_owner_info owner_info = {
-+ .userdata = context,
-+ .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
-+ };
-+
-+ pa_assert(context);
-+ pa_assert(target_info);
-+
-+ if (context->main_output_volume_control_binding)
-+ pa_binding_free(context->main_output_volume_control_binding);
-+
-+ context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-+ target_info);
-+}
-+
-+static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
-+ pa_volume_control *old_control;
-+
-+ pa_assert(context);
-+
-+ old_control = context->main_input_volume_control;
-+
-+ if (control == old_control)
-+ return;
-+
-+ context->main_input_volume_control = control;
-+
-+ if (!context->linked || context->unlinked)
-+ return;
-+
-+ pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
-+ context);
-+}
-+
-+void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info) {
-+ pa_binding_owner_info owner_info = {
-+ .userdata = context,
-+ .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
-+ };
-+
-+ pa_assert(context);
-+ pa_assert(target_info);
-+
-+ if (context->main_input_volume_control_binding)
-+ pa_binding_free(context->main_input_volume_control_binding);
-+
-+ context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-+ target_info);
-+}
-+
-+static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
-+ pa_mute_control *old_control;
-+
-+ pa_assert(context);
-+
-+ old_control = context->main_output_mute_control;
-+
-+ if (control == old_control)
-+ return;
-+
-+ context->main_output_mute_control = control;
-+
-+ if (!context->linked || context->unlinked)
-+ return;
-+
-+ pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
-+ context);
-+}
-+
-+void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info) {
-+ pa_binding_owner_info owner_info = {
-+ .userdata = context,
-+ .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
-+ };
-+
-+ pa_assert(context);
-+ pa_assert(target_info);
-+
-+ if (context->main_output_mute_control_binding)
-+ pa_binding_free(context->main_output_mute_control_binding);
-+
-+ context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-+ target_info);
-+}
-+
-+static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
-+ pa_mute_control *old_control;
-+
-+ pa_assert(context);
-+
-+ old_control = context->main_input_mute_control;
-+
-+ if (control == old_control)
-+ return;
-+
-+ context->main_input_mute_control = control;
-+
-+ if (!context->linked || context->unlinked)
-+ return;
-+
-+ pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
-+ context);
-+}
-+
-+void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info) {
-+ pa_binding_owner_info owner_info = {
-+ .userdata = context,
-+ .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
-+ };
-+
-+ pa_assert(context);
-+ pa_assert(target_info);
-+
-+ if (context->main_input_mute_control_binding)
-+ pa_binding_free(context->main_input_mute_control_binding);
-+
-+ context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-+ target_info);
-+}
-+
-+pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
-+ pa_binding_target_type *type;
-+
-+ pa_assert(policy);
-+
-+ type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts,
-+ &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT],
-+ &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK],
-+ (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name);
-+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL,
-+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control));
-+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL,
-+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control));
-+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL,
-+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control));
-+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL,
-+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control));
-+
-+ return type;
-+}
-diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
-new file mode 100644
-index 0000000..4a0a6f7
---- /dev/null
-+++ b/src/modules/main-volume-policy/main-volume-context.h
-@@ -0,0 +1,75 @@
-+#ifndef foomainvolumecontexthfoo
-+#define foomainvolumecontexthfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 <modules/main-volume-policy/main-volume-policy.h>
-+
-+#include <modules/volume-api/binding.h>
-+
-+typedef struct pa_main_volume_context pa_main_volume_context;
-+
-+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext"
-+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control"
-+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control"
-+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control"
-+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control"
-+
-+struct pa_main_volume_context {
-+ pa_main_volume_policy *main_volume_policy;
-+ uint32_t index;
-+ const char *name;
-+ char *description;
-+ pa_volume_control *main_output_volume_control;
-+ pa_volume_control *main_input_volume_control;
-+ pa_mute_control *main_output_mute_control;
-+ pa_mute_control *main_input_mute_control;
-+
-+ pa_binding *main_output_volume_control_binding;
-+ pa_binding *main_input_volume_control_binding;
-+ pa_binding *main_output_mute_control_binding;
-+ pa_binding *main_input_mute_control_binding;
-+
-+ bool linked;
-+ bool unlinked;
-+};
-+
-+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
-+ pa_main_volume_context **context);
-+void pa_main_volume_context_put(pa_main_volume_context *context);
-+void pa_main_volume_context_unlink(pa_main_volume_context *context);
-+void pa_main_volume_context_free(pa_main_volume_context *context);
-+
-+const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
-+
-+void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info);
-+void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info);
-+void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
-+ pa_binding_target_info *target_info);
-+void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info);
-+
-+/* Called from main-volume-policy.c only. */
-+pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy);
-+
-+#endif
-diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
-new file mode 100644
-index 0000000..b0b4ede
---- /dev/null
-+++ b/src/modules/main-volume-policy/main-volume-policy.c
-@@ -0,0 +1,213 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "main-volume-policy.h"
-+
-+#include <modules/main-volume-policy/main-volume-context.h>
-+
-+#include <pulsecore/core-util.h>
-+#include <pulsecore/shared.h>
-+
-+static pa_main_volume_policy *main_volume_policy_new(pa_core *core);
-+static void main_volume_policy_free(pa_main_volume_policy *policy);
-+
-+pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core) {
-+ pa_main_volume_policy *policy;
-+
-+ pa_assert(core);
-+
-+ policy = pa_shared_get(core, "main-volume-policy");
-+
-+ if (policy)
-+ pa_main_volume_policy_ref(policy);
-+ else {
-+ policy = main_volume_policy_new(core);
-+ pa_assert_se(pa_shared_set(core, "main-volume-policy", policy) >= 0);
-+ }
-+
-+ return policy;
-+}
-+
-+pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy) {
-+ pa_assert(policy);
-+
-+ policy->refcnt++;
-+
-+ return policy;
-+}
-+
-+void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
-+ pa_assert(policy);
-+ pa_assert(policy->refcnt > 0);
-+
-+ policy->refcnt--;
-+
-+ if (policy->refcnt == 0) {
-+ pa_assert_se(pa_shared_remove(policy->core, "main-volume-policy") >= 0);
-+ main_volume_policy_free(policy);
-+ }
-+}
-+
-+static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
-+ pa_main_volume_policy *policy;
-+ unsigned i;
-+
-+ pa_assert(core);
-+
-+ policy = pa_xnew0(pa_main_volume_policy, 1);
-+ policy->core = core;
-+ policy->refcnt = 1;
-+ policy->volume_api = pa_volume_api_get(core);
-+ policy->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
-+ policy->main_volume_contexts = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+
-+ for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
-+ pa_hook_init(&policy->hooks[i], policy);
-+
-+ policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy);
-+ pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
-+
-+ pa_log_debug("Created a pa_main_volume_policy object.");
-+
-+ return policy;
-+}
-+
-+static void main_volume_policy_free(pa_main_volume_policy *policy) {
-+ unsigned i;
-+
-+ pa_assert(policy);
-+ pa_assert(policy->refcnt == 0);
-+
-+ pa_log_debug("Freeing the pa_main_volume_policy object.");
-+
-+ if (policy->main_volume_context_binding_target_type) {
-+ pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
-+ pa_binding_target_type_free(policy->main_volume_context_binding_target_type);
-+ }
-+
-+ for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
-+ pa_hook_done(&policy->hooks[i]);
-+
-+ if (policy->main_volume_contexts) {
-+ pa_assert(pa_hashmap_isempty(policy->main_volume_contexts));
-+ pa_hashmap_free(policy->main_volume_contexts);
-+ }
-+
-+ if (policy->names) {
-+ pa_assert(pa_hashmap_isempty(policy->names));
-+ pa_hashmap_free(policy->names);
-+ }
-+
-+ if (policy->volume_api)
-+ pa_volume_api_unref(policy->volume_api);
-+
-+ pa_xfree(policy);
-+}
-+
-+int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name,
-+ bool fail_if_already_registered, const char **registered_name) {
-+ char *n;
-+
-+ pa_assert(policy);
-+ pa_assert(requested_name);
-+ pa_assert(registered_name);
-+
-+ n = pa_xstrdup(requested_name);
-+
-+ if (pa_hashmap_put(policy->names, n, n) < 0) {
-+ unsigned i = 1;
-+
-+ pa_xfree(n);
-+
-+ if (fail_if_already_registered) {
-+ pa_log("Name %s already registered.", requested_name);
-+ return -PA_ERR_EXIST;
-+ }
-+
-+ do {
-+ i++;
-+ n = pa_sprintf_malloc("%s.%u", requested_name, i);
-+ } while (pa_hashmap_put(policy->names, n, n) < 0);
-+ }
-+
-+ *registered_name = n;
-+
-+ return 0;
-+}
-+
-+void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name) {
-+ pa_assert(policy);
-+ pa_assert(name);
-+
-+ pa_assert_se(pa_hashmap_remove_and_free(policy->names, name) >= 0);
-+}
-+
-+uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy) {
-+ uint32_t idx;
-+
-+ pa_assert(policy);
-+
-+ idx = policy->next_main_volume_context_index++;
-+
-+ return idx;
-+}
-+
-+void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
-+ pa_assert(policy);
-+ pa_assert(context);
-+
-+ pa_assert_se(pa_hashmap_put(policy->main_volume_contexts, (void *) context->name, context) >= 0);
-+}
-+
-+int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
-+ pa_assert(policy);
-+ pa_assert(context);
-+
-+ if (!pa_hashmap_remove(policy->main_volume_contexts, context->name))
-+ return -1;
-+
-+ if (context == policy->active_main_volume_context)
-+ pa_main_volume_policy_set_active_main_volume_context(policy, NULL);
-+
-+ return 0;
-+}
-+
-+void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
-+ pa_main_volume_context *old_context;
-+
-+ pa_assert(policy);
-+
-+ old_context = policy->active_main_volume_context;
-+
-+ if (context == old_context)
-+ return;
-+
-+ policy->active_main_volume_context = context;
-+
-+ pa_log_debug("The active main volume context changed from %s to %s.", old_context ? old_context->name : "(unset)",
-+ context ? context->name : "(unset)");
-+
-+ pa_hook_fire(&policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED], NULL);
-+}
-diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example
-new file mode 100644
-index 0000000..a4a35d3
---- /dev/null
-+++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
-@@ -0,0 +1,20 @@
-+[General]
-+output-volume-model = by-active-main-volume-context
-+input-volume-model = by-active-main-volume-context
-+output-mute-model = none
-+input-mute-model = none
-+main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
-+
-+[MainVolumeContext x-example-call-main-volume-context]
-+description = Call main volume context
-+main-output-volume-control = bind:AudioGroup:x-example-call-downlink-audio-group
-+main-input-volume-control = bind:AudioGroup:x-example-call-uplink-audio-group
-+main-output-mute-control = none
-+main-input-mute-control = none
-+
-+[MainVolumeContext x-example-default-main-volume-context]
-+description = Default main volume context
-+main-output-volume-control = bind:AudioGroup:x-example-default-output-audio-group
-+main-input-volume-control = bind:AudioGroup:x-example-default-input-audio-group
-+main-output-mute-control = none
-+main-input-mute-control = none
-diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h
-new file mode 100644
-index 0000000..5cd669e
---- /dev/null
-+++ b/src/modules/main-volume-policy/main-volume-policy.h
-@@ -0,0 +1,72 @@
-+#ifndef foomainvolumepolicyhfoo
-+#define foomainvolumepolicyhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 <modules/volume-api/binding.h>
-+#include <modules/volume-api/volume-api.h>
-+
-+#include <pulsecore/core.h>
-+
-+typedef struct pa_main_volume_policy pa_main_volume_policy;
-+
-+/* Avoid circular dependencies... */
-+typedef struct pa_main_volume_context pa_main_volume_context;
-+
-+enum {
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED,
-+ PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAX,
-+};
-+
-+struct pa_main_volume_policy {
-+ pa_core *core;
-+ unsigned refcnt;
-+ pa_volume_api *volume_api;
-+ pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
-+ pa_hashmap *main_volume_contexts; /* name -> pa_main_volume_context */
-+ pa_main_volume_context *active_main_volume_context;
-+
-+ uint32_t next_main_volume_context_index;
-+ pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX];
-+ pa_binding_target_type *main_volume_context_binding_target_type;
-+};
-+
-+pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core);
-+pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy);
-+void pa_main_volume_policy_unref(pa_main_volume_policy *policy);
-+
-+int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name,
-+ bool fail_if_already_registered, const char **registered_name);
-+void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name);
-+
-+uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy);
-+void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
-+int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
-+void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
-+
-+#endif
-diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
-new file mode 100644
-index 0000000..a14699d
---- /dev/null
-+++ b/src/modules/main-volume-policy/module-main-volume-policy.c
-@@ -0,0 +1,556 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "module-main-volume-policy-symdef.h"
-+
-+#include <modules/main-volume-policy/main-volume-context.h>
-+
-+#include <modules/volume-api/binding.h>
-+#include <modules/volume-api/volume-api.h>
-+
-+#include <pulse/direction.h>
-+
-+#include <pulsecore/conf-parser.h>
-+#include <pulsecore/core-util.h>
-+#include <pulsecore/i18n.h>
-+
-+PA_MODULE_AUTHOR("Tanu Kaskinen");
-+PA_MODULE_DESCRIPTION(_("Main volume and mute policy"));
-+PA_MODULE_VERSION(PACKAGE_VERSION);
-+PA_MODULE_LOAD_ONCE(true);
-+
-+enum control_type {
-+ CONTROL_TYPE_VOLUME,
-+ CONTROL_TYPE_MUTE,
-+};
-+
-+enum model {
-+ MODEL_NONE,
-+ MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT,
-+};
-+
-+struct userdata {
-+ pa_main_volume_policy *main_volume_policy;
-+ enum model output_volume_model;
-+ enum model input_volume_model;
-+ enum model output_mute_model;
-+ enum model input_mute_model;
-+ pa_hashmap *contexts; /* name -> struct context */
-+
-+ pa_hook_slot *active_main_volume_context_changed_slot;
-+
-+ /* The following fields are only used during initialization. */
-+ pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */
-+ pa_hashmap *unused_contexts; /* name -> struct context */
-+};
-+
-+struct context {
-+ struct userdata *userdata;
-+ char *name;
-+ char *description;
-+ pa_binding_target_info *main_output_volume_control_target_info;
-+ pa_binding_target_info *main_input_volume_control_target_info;
-+ pa_binding_target_info *main_output_mute_control_target_info;
-+ pa_binding_target_info *main_input_mute_control_target_info;
-+ pa_main_volume_context *main_volume_context;
-+
-+ bool unlinked;
-+};
-+
-+static void context_unlink(struct context *context);
-+
-+static const char *model_to_string(enum model model) {
-+ switch (model) {
-+ case MODEL_NONE:
-+ return "none";
-+
-+ case MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT:
-+ return "by-active-main-volume-context";
-+ }
-+
-+ pa_assert_not_reached();
-+}
-+
-+static int model_from_string(const char *str, enum model *model) {
-+ pa_assert(str);
-+ pa_assert(model);
-+
-+ if (pa_streq(str, "none"))
-+ *model = MODEL_NONE;
-+ else if (pa_streq(str, "by-active-main-volume-context"))
-+ *model = MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT;
-+ else
-+ return -PA_ERR_INVALID;
-+
-+ return 0;
-+}
-+
-+static struct context *context_new(struct userdata *u, const char *name) {
-+ struct context *context;
-+
-+ pa_assert(u);
-+ pa_assert(name);
-+
-+ context = pa_xnew0(struct context, 1);
-+ context->userdata = u;
-+ context->name = pa_xstrdup(name);
-+ context->description = pa_xstrdup(name);
-+
-+ return context;
-+}
-+
-+static int context_put(struct context *context) {
-+ int r;
-+
-+ pa_assert(context);
-+
-+ r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
-+ &context->main_volume_context);
-+ if (r < 0)
-+ goto fail;
-+
-+ if (context->main_output_volume_control_target_info)
-+ pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context,
-+ context->main_output_volume_control_target_info);
-+
-+ if (context->main_input_volume_control_target_info)
-+ pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context,
-+ context->main_input_volume_control_target_info);
-+
-+ if (context->main_output_mute_control_target_info)
-+ pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context,
-+ context->main_output_mute_control_target_info);
-+
-+ if (context->main_input_mute_control_target_info)
-+ pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context,
-+ context->main_input_mute_control_target_info);
-+
-+ pa_main_volume_context_put(context->main_volume_context);
-+
-+ return 0;
-+
-+fail:
-+ context_unlink(context);
-+
-+ return r;
-+}
-+
-+static void context_unlink(struct context *context) {
-+ pa_assert(context);
-+
-+ if (context->unlinked)
-+ return;
-+
-+ context->unlinked = true;
-+
-+ if (context->main_volume_context) {
-+ pa_main_volume_context_free(context->main_volume_context);
-+ context->main_volume_context = NULL;
-+ }
-+}
-+
-+static void context_free(struct context *context) {
-+ pa_assert(context);
-+
-+ if (!context->unlinked)
-+ context_unlink(context);
-+
-+ if (context->main_input_mute_control_target_info)
-+ pa_binding_target_info_free(context->main_input_mute_control_target_info);
-+
-+ if (context->main_output_mute_control_target_info)
-+ pa_binding_target_info_free(context->main_output_mute_control_target_info);
-+
-+ if (context->main_input_volume_control_target_info)
-+ pa_binding_target_info_free(context->main_input_volume_control_target_info);
-+
-+ if (context->main_output_volume_control_target_info)
-+ pa_binding_target_info_free(context->main_output_volume_control_target_info);
-+
-+ pa_xfree(context->description);
-+ pa_xfree(context->name);
-+ pa_xfree(context);
-+}
-+
-+static void context_set_description(struct context *context, const char *description) {
-+ pa_assert(context);
-+ pa_assert(description);
-+
-+ pa_xfree(context->description);
-+ context->description = pa_xstrdup(description);
-+}
-+
-+static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction,
-+ pa_binding_target_info *info) {
-+ pa_assert(context);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (direction == PA_DIRECTION_OUTPUT) {
-+ if (context->main_output_volume_control_target_info)
-+ pa_binding_target_info_free(context->main_output_volume_control_target_info);
-+
-+ if (info)
-+ context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
-+ else
-+ context->main_output_volume_control_target_info = NULL;
-+ } else {
-+ if (context->main_input_volume_control_target_info)
-+ pa_binding_target_info_free(context->main_input_volume_control_target_info);
-+
-+ if (info)
-+ context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
-+ else
-+ context->main_input_volume_control_target_info = NULL;
-+ }
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (direction == PA_DIRECTION_OUTPUT) {
-+ if (context->main_output_mute_control_target_info)
-+ pa_binding_target_info_free(context->main_output_mute_control_target_info);
-+
-+ if (info)
-+ context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
-+ else
-+ context->main_output_mute_control_target_info = NULL;
-+ } else {
-+ if (context->main_input_mute_control_target_info)
-+ pa_binding_target_info_free(context->main_input_mute_control_target_info);
-+
-+ if (info)
-+ context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
-+ else
-+ context->main_input_mute_control_target_info = NULL;
-+ }
-+ break;
-+ }
-+}
-+
-+static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_main_volume_context *context;
-+ pa_volume_api *api;
-+ pa_binding_target_info *info = NULL;
-+
-+ pa_assert(u);
-+
-+ context = u->main_volume_policy->active_main_volume_context;
-+ api = u->main_volume_policy->volume_api;
-+
-+ if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-+ if (context) {
-+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
-+ pa_volume_api_bind_main_output_volume_control(api, info);
-+ } else
-+ pa_volume_api_set_main_output_volume_control(api, NULL);
-+ }
-+
-+ if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-+ if (context) {
-+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
-+ pa_volume_api_bind_main_input_volume_control(api, info);
-+ } else
-+ pa_volume_api_set_main_input_volume_control(api, NULL);
-+ }
-+
-+ if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-+ if (context) {
-+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
-+ pa_volume_api_bind_main_output_mute_control(api, info);
-+ } else
-+ pa_volume_api_set_main_output_mute_control(api, NULL);
-+ }
-+
-+ if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-+ if (context) {
-+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
-+ pa_volume_api_bind_main_input_mute_control(api, info);
-+ } else
-+ pa_volume_api_set_main_input_mute_control(api, NULL);
-+ }
-+
-+ if (info)
-+ pa_binding_target_info_free(info);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static int parse_model(pa_config_parser_state *state) {
-+ int r;
-+
-+ pa_assert(state);
-+
-+ r = model_from_string(state->rvalue, state->data);
-+ if (r < 0)
-+ pa_log("[%s:%u] Failed to parse model: %s", state->filename, state->lineno, state->rvalue);
-+
-+ return r;
-+}
-+
-+static int parse_main_volume_contexts(pa_config_parser_state *state) {
-+ struct userdata *u;
-+ char *name;
-+ const char *split_state = NULL;
-+
-+ pa_assert(state);
-+
-+ u = state->userdata;
-+
-+ while ((name = pa_split_spaces(state->rvalue, &split_state)))
-+ pa_hashmap_put(u->context_names, name, name);
-+
-+ return 0;
-+}
-+
-+static struct context *get_context(struct userdata *u, const char *section) {
-+ const char *name;
-+ struct context *context;
-+
-+ pa_assert(u);
-+
-+ if (!section)
-+ return NULL;
-+
-+ if (!pa_startswith(section, "MainVolumeContext "))
-+ return NULL;
-+
-+ name = section + 18;
-+
-+ context = pa_hashmap_get(u->unused_contexts, name);
-+ if (!context) {
-+ context = context_new(u, name);
-+ pa_hashmap_put(u->unused_contexts, context->name, context);
-+ }
-+
-+ return context;
-+}
-+
-+static int parse_description(pa_config_parser_state *state) {
-+ struct userdata *u;
-+ struct context *context;
-+
-+ pa_assert(state);
-+
-+ u = state->userdata;
-+
-+ context = get_context(u, state->section);
-+ if (!context) {
-+ pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
-+ pa_strnull(state->section));
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ context_set_description(context, state->rvalue);
-+
-+ return 0;
-+}
-+
-+static const char *get_target_field_name(enum control_type type) {
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ return "volume_control";
-+
-+ case CONTROL_TYPE_MUTE:
-+ return "mute_control";
-+ }
-+
-+ pa_assert_not_reached();
-+}
-+
-+static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
-+ struct userdata *u;
-+ struct context *context;
-+
-+ pa_assert(state);
-+
-+ u = state->userdata;
-+
-+ context = get_context(u, state->section);
-+ if (!context) {
-+ pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
-+ pa_strnull(state->section));
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ if (pa_streq(state->rvalue, "none"))
-+ context_set_main_control_target_info(context, type, direction, NULL);
-+ else if (pa_startswith(state->rvalue, "bind:")) {
-+ int r;
-+ pa_binding_target_info *info;
-+
-+ r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-+ return r;
-+ }
-+
-+ context_set_main_control_target_info(context, type, direction, info);
-+ pa_binding_target_info_free(info);
-+ } else {
-+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ return 0;
-+}
-+
-+static int parse_main_output_volume_control(pa_config_parser_state *state) {
-+ pa_assert(state);
-+
-+ return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
-+}
-+
-+static int parse_main_input_volume_control(pa_config_parser_state *state) {
-+ pa_assert(state);
-+
-+ return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
-+}
-+
-+static int parse_main_output_mute_control(pa_config_parser_state *state) {
-+ pa_assert(state);
-+
-+ return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
-+}
-+
-+static int parse_main_input_mute_control(pa_config_parser_state *state) {
-+ pa_assert(state);
-+
-+ return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
-+}
-+
-+static void finalize_config(struct userdata *u) {
-+ const char *context_name;
-+ void *state;
-+ struct context *context;
-+
-+ pa_assert(u);
-+
-+ PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
-+ int r;
-+
-+ context = pa_hashmap_remove(u->unused_contexts, context_name);
-+ if (!context)
-+ context = context_new(u, context_name);
-+
-+ r = context_put(context);
-+ if (r < 0) {
-+ pa_log_warn("Failed to create main volume context %s.", context_name);
-+ context_free(context);
-+ continue;
-+ }
-+
-+ pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
-+ }
-+
-+ PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
-+ pa_log_debug("Main volume context %s is not used.", context->name);
-+
-+ pa_hashmap_free(u->unused_contexts);
-+ u->unused_contexts = NULL;
-+
-+ pa_hashmap_free(u->context_names);
-+ u->context_names = NULL;
-+}
-+
-+int pa__init(pa_module *module) {
-+ struct userdata *u;
-+ FILE *f;
-+ char *fn = NULL;
-+
-+ pa_assert(module);
-+
-+ u = module->userdata = pa_xnew0(struct userdata, 1);
-+ u->main_volume_policy = pa_main_volume_policy_get(module->core);
-+ u->output_volume_model = MODEL_NONE;
-+ u->input_volume_model = MODEL_NONE;
-+ u->output_mute_model = MODEL_NONE;
-+ u->input_mute_model = MODEL_NONE;
-+ u->contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) context_free);
-+ u->active_main_volume_context_changed_slot =
-+ pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED],
-+ PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u);
-+ u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
-+ u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) context_free);
-+
-+ f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
-+ if (f) {
-+ pa_config_item config_items[] = {
-+ { "output-volume-model", parse_model, &u->output_volume_model, "General" },
-+ { "input-volume-model", parse_model, &u->input_volume_model, "General" },
-+ { "output-mute-model", parse_model, &u->output_mute_model, "General" },
-+ { "input-mute-model", parse_model, &u->input_mute_model, "General" },
-+ { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" },
-+ { "description", parse_description, NULL, NULL },
-+ { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL },
-+ { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL },
-+ { "main-output-mute-control", parse_main_output_mute_control, NULL, NULL },
-+ { "main-input-mute-control", parse_main_input_mute_control, NULL, NULL },
-+ { NULL },
-+ };
-+
-+ pa_config_parse(fn, f, config_items, NULL, u);
-+ pa_xfree(fn);
-+ fn = NULL;
-+ fclose(f);
-+ f = NULL;
-+ }
-+
-+ finalize_config(u);
-+
-+ pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model));
-+ pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model));
-+ pa_log_debug("Output mute model: %s", model_to_string(u->output_mute_model));
-+ pa_log_debug("Input mute model: %s", model_to_string(u->input_mute_model));
-+
-+ return 0;
-+}
-+
-+void pa__done(pa_module *module) {
-+ struct userdata *u;
-+
-+ pa_assert(module);
-+
-+ u = module->userdata;
-+ if (!u)
-+ return;
-+
-+ if (u->active_main_volume_context_changed_slot)
-+ pa_hook_slot_free(u->active_main_volume_context_changed_slot);
-+
-+ if (u->contexts)
-+ pa_hashmap_free(u->contexts);
-+
-+ if (u->main_volume_policy)
-+ pa_main_volume_policy_unref(u->main_volume_policy);
-+
-+ pa_xfree(u);
-+}
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Thu, 22 May 2014 14:43:33 +0300
-Subject: configuration: Add default IVI audio group and main volume
- configuration
-
-Change-Id: Idd348cc9f469e988405d574dbc2459c5822a33c2
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/Makefile.am | 21 ++++++++++++++++
- src/tizen-ivi-examples/audio-groups.conf | 33 ++++++++++++++++++++++++++
- src/tizen-ivi-examples/main-volume-policy.conf | 20 ++++++++++++++++
- 3 files changed, 74 insertions(+)
- create mode 100644 src/tizen-ivi-examples/audio-groups.conf
- create mode 100644 src/tizen-ivi-examples/main-volume-policy.conf
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 8fa60ec..d57c30b 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -89,6 +89,8 @@ MODULE_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_
- EXTRA_DIST = \
- pulse/client.conf.in \
- pulse/version.h.in \
-+ tizen-ivi-examples/audio-groups.conf \
-+ tizen-ivi-examples/main-volume-policy.conf \
- daemon/daemon.conf.in \
- daemon/default.pa.in \
- daemon/system.pa.in \
-@@ -111,6 +113,25 @@ pulseconf_DATA = \
- daemon.conf \
- client.conf
-
-+# Add some Tizen specific configuration files.
-+#
-+# FIXME: These configuration files should be installed only if explicitly
-+# requested, because they define policy which may not be the desired policy in
-+# every Tizen profile. Currently default.pa loads module-audio-groups and
-+# module-main-volume-policy only if module-murphy-ivi is installed in the
-+# system, which helps with this issue, because non-IVI profiles don't
-+# currently use module-murphy-ivi, so these configuration files won't have any
-+# effect outside the IVI profile, but this is pretty hacky solution. It would
-+# be better to load module-audio-groups and module-main-volume-policy
-+# unconditionally, since they're not really tied to the Murphy module in any
-+# way. We use this hack, because otherwise we'd need a configure switch for
-+# enabling the example IVI configuration, and a new configure switch would also
-+# require a new switch in the Tizen IVI image configuration. That's an extra
-+# hurdle that we decided to avoid for now.
-+pulseconf_DATA += \
-+ tizen-ivi-examples/audio-groups.conf \
-+ tizen-ivi-examples/main-volume-policy.conf
-+
- if HAVE_DBUS
- dbuspolicy_DATA = \
- daemon/pulseaudio-system.conf
-diff --git a/src/tizen-ivi-examples/audio-groups.conf b/src/tizen-ivi-examples/audio-groups.conf
-new file mode 100644
-index 0000000..54939c9
---- /dev/null
-+++ b/src/tizen-ivi-examples/audio-groups.conf
-@@ -0,0 +1,33 @@
-+[General]
-+audio-groups = x-example-call-downlink-audio-group x-example-navigator-output-audio-group x-example-default-output-audio-group
-+streams = call-downlink navigator-output default-output
-+
-+[AudioGroup x-example-call-downlink-audio-group]
-+description = Call downlink
-+volume-control = create
-+mute-control = create
-+
-+[AudioGroup x-example-navigator-output-audio-group]
-+description = Navigator
-+volume-control = create
-+mute-control = create
-+
-+[AudioGroup x-example-default-output-audio-group]
-+description = Default
-+volume-control = create
-+mute-control = create
-+
-+[Stream call-downlink]
-+match = (direction output AND property media.role=phone)
-+audio-group-for-volume = x-example-call-downlink-audio-group
-+audio-group-for-mute = x-example-call-downlink-audio-group
-+
-+[Stream navigator-output]
-+match = (direction output AND property media.role=navigator)
-+audio-group-for-volume = x-example-navigator-output-audio-group
-+audio-group-for-mute = x-example-navigator-output-audio-group
-+
-+[Stream default-output]
-+match = (direction output)
-+audio-group-for-volume = x-example-default-output-audio-group
-+audio-group-for-mute = x-example-default-output-audio-group
-diff --git a/src/tizen-ivi-examples/main-volume-policy.conf b/src/tizen-ivi-examples/main-volume-policy.conf
-new file mode 100644
-index 0000000..5a73308
---- /dev/null
-+++ b/src/tizen-ivi-examples/main-volume-policy.conf
-@@ -0,0 +1,20 @@
-+[General]
-+output-volume-model = by-active-main-volume-context
-+input-volume-model = none
-+output-mute-model = by-active-main-volume-context
-+input-mute-model = none
-+main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
-+
-+[MainVolumeContext x-example-call-main-volume-context]
-+description = Call main volume context
-+main-output-volume-control = bind:AudioGroup:x-example-call-downlink-audio-group
-+main-input-volume-control = none
-+main-output-mute-control = bind:AudioGroup:x-example-call-downlink-audio-group
-+main-input-mute-control = none
-+
-+[MainVolumeContext x-example-default-main-volume-context]
-+description = Default main volume context
-+main-output-volume-control = bind:AudioGroup:x-example-default-output-audio-group
-+main-input-volume-control = none
-+main-output-mute-control = bind:AudioGroup:x-example-default-output-audio-group
-+main-input-mute-control = none
+++ /dev/null
-From: David Henningsson <david.henningsson@canonical.com>
-Date: Fri, 21 Mar 2014 10:19:19 +0100
-Subject: sink/source: Initialize port before fixate hook (fixes volume/mute
- not saved)
-
-In case a port has not yet been saved, which is e g often the case
-if a sink/source has only one port, reading volume/mute will be done
-without port, whereas writing volume/mute will be done with port.
-
-Work around this by setting a default port before the fixate hook,
-so module-device-restore can read volume/mute for the correct port.
-
-Change-Id: Iea6a742f0667771712059cb39b8082785b2a6887
-BugLink: https://bugs.launchpad.net/bugs/1289515
-Signed-off-by: David Henningsson <david.henningsson@canonical.com>
----
- src/pulsecore/device-port.c | 27 +++++++++++++++++++++++++++
- src/pulsecore/device-port.h | 2 ++
- src/pulsecore/sink.c | 27 ++++++++++-----------------
- src/pulsecore/source.c | 28 ++++++++++------------------
- 4 files changed, 49 insertions(+), 35 deletions(-)
-
-diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
-index c183990..1520142 100644
---- a/src/pulsecore/device-port.c
-+++ b/src/pulsecore/device-port.c
-@@ -191,3 +191,30 @@ void pa_device_port_active_changed(pa_device_port *port, bool new_active) {
- pa_log_debug("Port %s %s.", port->name, new_active ? "activated" : "deactivated");
- pa_hook_fire(&port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED], port);
- }
-+
-+pa_device_port *pa_device_port_find_best(pa_hashmap *ports)
-+{
-+ void *state;
-+ pa_device_port *p, *best = NULL;
-+
-+ if (!ports)
-+ return NULL;
-+
-+ /* First run: skip unavailable ports */
-+ PA_HASHMAP_FOREACH(p, ports, state) {
-+ if (p->available == PA_AVAILABLE_NO)
-+ continue;
-+
-+ if (!best || p->priority > best->priority)
-+ best = p;
-+ }
-+
-+ /* Second run: if only unavailable ports exist, still suggest a port */
-+ if (!best) {
-+ PA_HASHMAP_FOREACH(p, ports, state)
-+ if (!best || p->priority > best->priority)
-+ best = p;
-+ }
-+
-+ return best;
-+}
-diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
-index 2964900..bf45ab9 100644
---- a/src/pulsecore/device-port.h
-+++ b/src/pulsecore/device-port.h
-@@ -87,4 +87,6 @@ void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset);
- /* Called from sink.c and source.c only. */
- void pa_device_port_active_changed(pa_device_port *port, bool new_active);
-
-+pa_device_port *pa_device_port_find_best(pa_hashmap *ports);
-+
- #endif
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 191b560..41cffcc 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -235,6 +235,12 @@ pa_sink* pa_sink_new(
- pa_device_init_icon(data->proplist, true);
- pa_device_init_intended_roles(data->proplist);
-
-+ if (!data->active_port) {
-+ pa_device_port *p = pa_device_port_find_best(data->ports);
-+ if (p)
-+ pa_sink_new_data_set_port(data, p->name);
-+ }
-+
- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
- pa_xfree(s);
- pa_namereg_unregister(core, name);
-@@ -300,23 +306,10 @@ pa_sink* pa_sink_new(
- if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
- s->save_port = data->save_port;
-
-- if (!s->active_port) {
-- void *state;
-- pa_device_port *p;
--
-- PA_HASHMAP_FOREACH(p, s->ports, state) {
-- if (p->available == PA_AVAILABLE_NO)
-- continue;
--
-- if (!s->active_port || p->priority > s->active_port->priority)
-- s->active_port = p;
-- }
-- if (!s->active_port) {
-- PA_HASHMAP_FOREACH(p, s->ports, state)
-- if (!s->active_port || p->priority > s->active_port->priority)
-- s->active_port = p;
-- }
-- }
-+ /* Hopefully the active port has already been assigned in the previous call
-+ to pa_device_port_find_best, but better safe than sorry */
-+ if (!s->active_port)
-+ s->active_port = pa_device_port_find_best(s->ports);
-
- if (s->active_port)
- s->latency_offset = s->active_port->latency_offset;
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index 0fddfaa..db7f667 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -222,6 +222,12 @@ pa_source* pa_source_new(
- pa_device_init_icon(data->proplist, false);
- pa_device_init_intended_roles(data->proplist);
-
-+ if (!data->active_port) {
-+ pa_device_port *p = pa_device_port_find_best(data->ports);
-+ if (p)
-+ pa_source_new_data_set_port(data, p->name);
-+ }
-+
- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
- pa_xfree(s);
- pa_namereg_unregister(core, name);
-@@ -288,24 +294,10 @@ pa_source* pa_source_new(
- if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
- s->save_port = data->save_port;
-
-- if (!s->active_port) {
-- void *state;
-- pa_device_port *p;
--
-- PA_HASHMAP_FOREACH(p, s->ports, state) {
-- if (p->available == PA_AVAILABLE_NO)
-- continue;
--
-- if (!s->active_port || p->priority > s->active_port->priority)
-- s->active_port = p;
-- }
--
-- if (!s->active_port) {
-- PA_HASHMAP_FOREACH(p, s->ports, state)
-- if (!s->active_port || p->priority > s->active_port->priority)
-- s->active_port = p;
-- }
-- }
-+ /* Hopefully the active port has already been assigned in the previous call
-+ to pa_device_port_find_best, but better safe than sorry */
-+ if (!s->active_port)
-+ s->active_port = pa_device_port_find_best(s->ports);
-
- if (s->active_port)
- s->latency_offset = s->active_port->latency_offset;
+++ /dev/null
-From: Jaska Uimonen <jaska.uimonen@intel.com>
-Date: Thu, 18 Jul 2013 18:43:14 +0800
-Subject: configuration: pulseaudio tizen configuration in default.pa
-
-Change-Id: Id9370a1858d5c1ba0ed3319d717bc7f3e9ed5d31
-Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
----
- src/daemon/default.pa.in | 60 ++++++++++++++++++++++++------------------------
- 1 file changed, 30 insertions(+), 30 deletions(-)
-
-diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
-index f70804c..bae1a77 100755
---- a/src/daemon/default.pa.in
-+++ b/src/daemon/default.pa.in
-@@ -35,18 +35,6 @@ load-sample-dir-lazy %WINDIR%\Media\*.wav
-
- .fail
-
--### Automatically restore the volume of streams and devices
--load-module module-device-restore
--load-module module-stream-restore
--load-module module-card-restore
--
--### Automatically augment property information from .desktop files
--### stored in /usr/share/application
--load-module module-augment-properties
--
--### Should be after module-*-restore but before module-*-detect
--load-module module-switch-on-port-available
--
- ### Load audio drivers statically
- ### (it's probably better to not load these drivers manually, but instead
- ### use module-udev-detect -- see below -- for doing this automatically)
-@@ -81,16 +69,12 @@ load-module module-detect
- ### Automatically connect sink and source if JACK server is present
- .ifexists module-jackdbus-detect@PA_SOEXT@
- .nofail
--load-module module-jackdbus-detect channels=2
-+load-module module-jackdbus-detect
- .fail
- .endif
-
- ifelse(@HAVE_BLUEZ@, 1, [dnl
- ### Automatically load driver modules for Bluetooth hardware
--.ifexists module-bluetooth-policy@PA_SOEXT@
--load-module module-bluetooth-policy
--.endif
--
- .ifexists module-bluetooth-discover@PA_SOEXT@
- load-module module-bluetooth-discover
- .endif
-@@ -142,9 +126,6 @@ load-module module-rescue-streams
- ### Make sure we always have a sink around, even if it is a null sink.
- load-module module-always-sink
-
--### Honour intended role device property
--load-module module-intended-roles
--
- ### Automatically suspend sinks/sources that become idle for too long
- load-module module-suspend-on-idle
-
-@@ -160,15 +141,16 @@ load-module module-systemd-login
- ### Enable positioned event sounds
- load-module module-position-event-sounds
-
--### Cork music/video streams when a phone stream is active
--load-module module-role-cork
--
- ### Modules to allow autoloading of filters (such as echo cancellation)
- ### on demand. module-filter-heuristics tries to determine what filters
- ### make sense, and module-filter-apply does the heavy-lifting of
- ### loading modules and rerouting streams.
-+.ifexists module-filter-heuristics@PA_SOEXT@
- load-module module-filter-heuristics
-+.endif
-+.ifexists module-filter-apply@PA_SOEXT@
- load-module module-filter-apply
-+.endif
- ])dnl
-
- ifelse(@HAVE_X11@, 1, [dnl
-@@ -189,16 +171,34 @@ ifelse(@HAVE_X11@, 1, [dnl
- #.endif
- ])dnl
-
--.ifexists module-volume-api
-+### Load the Murphy IVI module if it exists
-+.ifexists module-murphy-ivi@PA_SOEXT@
-+load-module module-murphy-ivi
-+load-module module-stream-restore restore_device=false on_hotplug=false on_rescue=false restore_volume=true preferred_stream_group=media.role.within.application.name
-+load-module module-native-protocol-tcp
- load-module module-volume-api
--.endif
--
--.ifexists module-audio-groups
- load-module module-audio-groups
--.endif
--
--.ifexists module-main-volume-policy
- load-module module-main-volume-policy
-+.else
-+### Automatically restore the volume of streams and devices
-+load-module module-device-restore
-+load-module module-stream-restore
-+load-module module-card-restore
-+
-+### Automatically augment property information from .desktop files
-+### stored in /usr/share/application
-+.nofail
-+load-module module-augment-properties
-+.fail
-+
-+### Honour intended role device property
-+load-module module-intended-roles
-+
-+### Cork music/video streams when a phone stream is active
-+load-module module-role-cork
-+
-+load-module module-switch-on-port-available
-+
- .endif
-
- ### Make some devices default
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 26 May 2014 16:26:14 +0300
-Subject: pactl: Fix crash in "pactl list"
-
-Fixes this assertion error:
-
-Assertion 'actions > 0' failed at utils/pactl.c:172, function complete_action(). Aborting.
-
-Change-Id: Icdcdf0817af431115444cb4fdef0a042fe5d7560
----
- src/utils/pactl.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/utils/pactl.c b/src/utils/pactl.c
-index f947681..d8921f1 100644
---- a/src/utils/pactl.c
-+++ b/src/utils/pactl.c
-@@ -1673,7 +1673,7 @@ static void volume_api_state_cb(pa_context *c, void *userdata) {
- o = pa_ext_volume_api_get_audio_group_info_list(c, get_audio_group_info_callback, NULL);
- pa_operation_unref(o);
- o = NULL;
-- actions += 4;
-+ actions += 5;
- } else if (pa_streq(list_type, "volume-controls")) {
- o = pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL);
- actions++;
+++ /dev/null
-From: Ismo Puustinen <ismo.puustinen@intel.com>
-Date: Mon, 26 May 2014 14:37:48 +0300
-Subject: configuration: x-example -> x-tizen-ivi in volume configuration.
-
-Change-Id: I1c11084d6891e431dd909c632e4bfb62968167df
----
- src/Makefile.am | 8 +++----
- src/tizen-ivi-examples/audio-groups.conf | 33 --------------------------
- src/tizen-ivi-examples/main-volume-policy.conf | 20 ----------------
- src/tizen-ivi/audio-groups.conf | 33 ++++++++++++++++++++++++++
- src/tizen-ivi/main-volume-policy.conf | 20 ++++++++++++++++
- 5 files changed, 57 insertions(+), 57 deletions(-)
- delete mode 100644 src/tizen-ivi-examples/audio-groups.conf
- delete mode 100644 src/tizen-ivi-examples/main-volume-policy.conf
- create mode 100644 src/tizen-ivi/audio-groups.conf
- create mode 100644 src/tizen-ivi/main-volume-policy.conf
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index d57c30b..9d17336 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -89,8 +89,8 @@ MODULE_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_
- EXTRA_DIST = \
- pulse/client.conf.in \
- pulse/version.h.in \
-- tizen-ivi-examples/audio-groups.conf \
-- tizen-ivi-examples/main-volume-policy.conf \
-+ tizen-ivi/audio-groups.conf \
-+ tizen-ivi/main-volume-policy.conf \
- daemon/daemon.conf.in \
- daemon/default.pa.in \
- daemon/system.pa.in \
-@@ -129,8 +129,8 @@ pulseconf_DATA = \
- # require a new switch in the Tizen IVI image configuration. That's an extra
- # hurdle that we decided to avoid for now.
- pulseconf_DATA += \
-- tizen-ivi-examples/audio-groups.conf \
-- tizen-ivi-examples/main-volume-policy.conf
-+ tizen-ivi/audio-groups.conf \
-+ tizen-ivi/main-volume-policy.conf
-
- if HAVE_DBUS
- dbuspolicy_DATA = \
-diff --git a/src/tizen-ivi-examples/audio-groups.conf b/src/tizen-ivi-examples/audio-groups.conf
-deleted file mode 100644
-index 54939c9..0000000
---- a/src/tizen-ivi-examples/audio-groups.conf
-+++ /dev/null
-@@ -1,33 +0,0 @@
--[General]
--audio-groups = x-example-call-downlink-audio-group x-example-navigator-output-audio-group x-example-default-output-audio-group
--streams = call-downlink navigator-output default-output
--
--[AudioGroup x-example-call-downlink-audio-group]
--description = Call downlink
--volume-control = create
--mute-control = create
--
--[AudioGroup x-example-navigator-output-audio-group]
--description = Navigator
--volume-control = create
--mute-control = create
--
--[AudioGroup x-example-default-output-audio-group]
--description = Default
--volume-control = create
--mute-control = create
--
--[Stream call-downlink]
--match = (direction output AND property media.role=phone)
--audio-group-for-volume = x-example-call-downlink-audio-group
--audio-group-for-mute = x-example-call-downlink-audio-group
--
--[Stream navigator-output]
--match = (direction output AND property media.role=navigator)
--audio-group-for-volume = x-example-navigator-output-audio-group
--audio-group-for-mute = x-example-navigator-output-audio-group
--
--[Stream default-output]
--match = (direction output)
--audio-group-for-volume = x-example-default-output-audio-group
--audio-group-for-mute = x-example-default-output-audio-group
-diff --git a/src/tizen-ivi-examples/main-volume-policy.conf b/src/tizen-ivi-examples/main-volume-policy.conf
-deleted file mode 100644
-index 5a73308..0000000
---- a/src/tizen-ivi-examples/main-volume-policy.conf
-+++ /dev/null
-@@ -1,20 +0,0 @@
--[General]
--output-volume-model = by-active-main-volume-context
--input-volume-model = none
--output-mute-model = by-active-main-volume-context
--input-mute-model = none
--main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
--
--[MainVolumeContext x-example-call-main-volume-context]
--description = Call main volume context
--main-output-volume-control = bind:AudioGroup:x-example-call-downlink-audio-group
--main-input-volume-control = none
--main-output-mute-control = bind:AudioGroup:x-example-call-downlink-audio-group
--main-input-mute-control = none
--
--[MainVolumeContext x-example-default-main-volume-context]
--description = Default main volume context
--main-output-volume-control = bind:AudioGroup:x-example-default-output-audio-group
--main-input-volume-control = none
--main-output-mute-control = bind:AudioGroup:x-example-default-output-audio-group
--main-input-mute-control = none
-diff --git a/src/tizen-ivi/audio-groups.conf b/src/tizen-ivi/audio-groups.conf
-new file mode 100644
-index 0000000..4839307
---- /dev/null
-+++ b/src/tizen-ivi/audio-groups.conf
-@@ -0,0 +1,33 @@
-+[General]
-+audio-groups = x-tizen-ivi-call-downlink-audio-group x-tizen-ivi-navigator-output-audio-group x-tizen-ivi-default-output-audio-group
-+streams = call-downlink navigator-output default-output
-+
-+[AudioGroup x-tizen-ivi-call-downlink-audio-group]
-+description = Call downlink
-+volume-control = create
-+mute-control = create
-+
-+[AudioGroup x-tizen-ivi-navigator-output-audio-group]
-+description = Navigator
-+volume-control = create
-+mute-control = create
-+
-+[AudioGroup x-tizen-ivi-default-output-audio-group]
-+description = Default
-+volume-control = create
-+mute-control = create
-+
-+[Stream call-downlink]
-+match = (direction output AND property media.role=phone)
-+audio-group-for-volume = x-tizen-ivi-call-downlink-audio-group
-+audio-group-for-mute = x-tizen-ivi-call-downlink-audio-group
-+
-+[Stream navigator-output]
-+match = (direction output AND property media.role=navigator)
-+audio-group-for-volume = x-tizen-ivi-navigator-output-audio-group
-+audio-group-for-mute = x-tizen-ivi-navigator-output-audio-group
-+
-+[Stream default-output]
-+match = (direction output)
-+audio-group-for-volume = x-tizen-ivi-default-output-audio-group
-+audio-group-for-mute = x-tizen-ivi-default-output-audio-group
-diff --git a/src/tizen-ivi/main-volume-policy.conf b/src/tizen-ivi/main-volume-policy.conf
-new file mode 100644
-index 0000000..0a83968
---- /dev/null
-+++ b/src/tizen-ivi/main-volume-policy.conf
-@@ -0,0 +1,20 @@
-+[General]
-+output-volume-model = by-active-main-volume-context
-+input-volume-model = none
-+output-mute-model = by-active-main-volume-context
-+input-mute-model = none
-+main-volume-contexts = x-tizen-ivi-call default
-+
-+[MainVolumeContext x-tizen-ivi-call]
-+description = Call main volume context
-+main-output-volume-control = bind:AudioGroup:x-tizen-ivi-call-downlink-audio-group
-+main-input-volume-control = none
-+main-output-mute-control = bind:AudioGroup:x-tizen-ivi-call-downlink-audio-group
-+main-input-mute-control = none
-+
-+[MainVolumeContext default]
-+description = Default main volume context
-+main-output-volume-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
-+main-input-volume-control = none
-+main-output-mute-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
-+main-input-mute-control = none
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 27 May 2014 10:45:04 +0300
-Subject: device-creator, stream-creator: Add a couple of assertions
-
-Klocwork complained that source (in device-creator) and output (in
-stream-creator) may be dereferenced if they're NULL. Let's make
-Klocwork happy, and the code a bit more obvious to human readers as
-well.
-
-Change-Id: I835dd7d9da44e2866a97bc0424001a42c29602a8
----
- src/modules/volume-api/device-creator.c | 4 +++-
- src/modules/volume-api/stream-creator.c | 4 +++-
- 2 files changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c
-index 1d912ba..f35fab0 100644
---- a/src/modules/volume-api/device-creator.c
-+++ b/src/modules/volume-api/device-creator.c
-@@ -513,8 +513,10 @@ static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *ca
-
- if (sink)
- mute = sink->muted;
-- else
-+ else if (source)
- mute = source->muted;
-+ else
-+ pa_assert_not_reached();
-
- pa_mute_control_mute_changed(control->mute_control, mute);
-
-diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c
-index 2bd0053..f6ca7b3 100644
---- a/src/modules/volume-api/stream-creator.c
-+++ b/src/modules/volume-api/stream-creator.c
-@@ -216,8 +216,10 @@ static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_d
-
- if (input)
- mute = input->muted;
-- else
-+ else if (output)
- mute = output->muted;
-+ else
-+ pa_assert_not_reached();
-
- pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 27 May 2014 11:06:10 +0300
-Subject: main-volume-policy: Fix a memory leak
-
-There can be multiple calls to pa_binding_target_info_new() in this
-function, but only the last allocated info object was freed.
-
-Change-Id: I9df43f0663b27b07ba7b8d01bc8ea9cc0a6c1b51
----
- src/modules/main-volume-policy/module-main-volume-policy.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
-diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
-index a14699d..0a89aa7 100644
---- a/src/modules/main-volume-policy/module-main-volume-policy.c
-+++ b/src/modules/main-volume-policy/module-main-volume-policy.c
-@@ -253,7 +253,7 @@ static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, v
- struct userdata *u = userdata;
- pa_main_volume_context *context;
- pa_volume_api *api;
-- pa_binding_target_info *info = NULL;
-+ pa_binding_target_info *info;
-
- pa_assert(u);
-
-@@ -265,6 +265,7 @@ static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, v
- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
- pa_volume_api_bind_main_output_volume_control(api, info);
-+ pa_binding_target_info_free(info);
- } else
- pa_volume_api_set_main_output_volume_control(api, NULL);
- }
-@@ -274,6 +275,7 @@ static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, v
- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
- pa_volume_api_bind_main_input_volume_control(api, info);
-+ pa_binding_target_info_free(info);
- } else
- pa_volume_api_set_main_input_volume_control(api, NULL);
- }
-@@ -283,6 +285,7 @@ static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, v
- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
- pa_volume_api_bind_main_output_mute_control(api, info);
-+ pa_binding_target_info_free(info);
- } else
- pa_volume_api_set_main_output_mute_control(api, NULL);
- }
-@@ -292,13 +295,11 @@ static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, v
- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
- pa_volume_api_bind_main_input_mute_control(api, info);
-+ pa_binding_target_info_free(info);
- } else
- pa_volume_api_set_main_input_mute_control(api, NULL);
- }
-
-- if (info)
-- pa_binding_target_info_free(info);
--
- return PA_HOOK_OK;
- }
-
+++ /dev/null
-From: Ismo Puustinen <ismo.puustinen@intel.com>
-Date: Tue, 27 May 2014 10:27:16 +0300
-Subject: audio-groups: fix issues found by static analysis.
-
-Change-Id: Ia2805a5977868b236bd6a33e7bc8fdcb944020ea
----
- src/modules/audio-groups/module-audio-groups.c | 63 ++++++++++++++++----------
- 1 file changed, 40 insertions(+), 23 deletions(-)
-
-diff --git a/src/modules/audio-groups/module-audio-groups.c b/src/modules/audio-groups/module-audio-groups.c
-index 320847c..2b3a570 100644
---- a/src/modules/audio-groups/module-audio-groups.c
-+++ b/src/modules/audio-groups/module-audio-groups.c
-@@ -383,9 +383,13 @@ static bool match_predicate(struct literal *l, pas_stream *d) {
- else if (l->property_name && l->property_value) {
- /* check the property from the property list */
-
-- if (pa_proplist_contains(d->proplist, l->property_name) &&
-- strcmp(pa_proplist_gets(d->proplist, l->property_name), l->property_value) == 0)
-- return true;
-+ if (pa_proplist_contains(d->proplist, l->property_name)) {
-+ const char *prop = pa_proplist_gets(d->proplist, l->property_name);
-+
-+ if (prop && strcmp(prop, l->property_value) == 0) {
-+ return true;
-+ }
-+ }
- }
-
- /* no match */
-@@ -631,18 +635,8 @@ static struct expression_token *parse_rule_internal(const char *rule, bool disju
- char *p;
- int brace_count = 0;
- bool braces_present = false;
-- char left_buf[len];
-- char right_buf[len];
--
--#if 0
-- /* check if the rule is still valid */
--
-- if (len < 2)
-- return NULL;
--
-- if (rule[0] != '(' || rule[len-1] != ')')
-- return NULL;
--#endif
-+ char left_buf[len+1];
-+ char right_buf[len+1];
-
- et = pa_xnew0(struct expression_token, 1);
-
-@@ -740,9 +734,9 @@ static struct expression_token *parse_rule_internal(const char *rule, bool disju
- else {
- /* this is a literal */
- char *begin_lit;
-- char buf[strlen(rule)+1];
--
-+ char buf[len+1];
- struct literal_token *lit = pa_xnew0(struct literal_token, 1);
-+
- if (!lit) {
- delete_expression_token(et);
- return NULL;
-@@ -769,7 +763,8 @@ static struct expression_token *parse_rule_internal(const char *rule, bool disju
- *l = '\0';
- }
- else {
-- strncpy(buf, rule, sizeof(buf));
-+ strncpy(buf, rule, len);
-+ buf[len] = '\0';
- }
-
- if (strncmp(buf, "NEG", 3) == 0) {
-@@ -782,6 +777,7 @@ static struct expression_token *parse_rule_internal(const char *rule, bool disju
- }
-
- lit->var = pa_xstrdup(begin_lit);
-+
- et->lit = lit;
- }
-
-@@ -869,8 +865,9 @@ static bool gather_conjunction(struct expression_token *et, struct conjunction *
-
- if (et->oper == operator_and) {
- if (!gather_conjunction(et->left, c) ||
-- !gather_conjunction(et->right, c))
-+ !gather_conjunction(et->right, c)) {
- return false;
-+ }
- }
- else {
- /* literal */
-@@ -879,8 +876,13 @@ static bool gather_conjunction(struct expression_token *et, struct conjunction *
- if (!l)
- return false;
-
-- gather_literal(et, l);
-+ if (!gather_literal(et, l)) {
-+ pa_log_error("audio groups config: literal parsing failed");
-+ delete_literal(l);
-+ return false;
-+ }
-
-+ PA_LLIST_INIT(struct literal, l);
- PA_LLIST_PREPEND(struct literal, c->literals, l);
- }
-
-@@ -897,9 +899,18 @@ static bool gather_expression(struct expression *e, struct expression_token *et)
- else {
- /* conjunction or literal */
- struct conjunction *c = pa_xnew0(struct conjunction, 1);
-- if (!gather_conjunction(et, c))
-+
-+ if (!c)
- return false;
-
-+ PA_LLIST_HEAD_INIT(struct literal, c->literals);
-+
-+ if (!gather_conjunction(et, c)) {
-+ delete_conjunction(c);
-+ return false;
-+ }
-+
-+ PA_LLIST_INIT(struct conjunction, c);
- PA_LLIST_PREPEND(struct conjunction, e->conjunctions, c);
- }
-
-@@ -950,10 +961,16 @@ static struct expression *parse_rule(const char *rule_string) {
- if (!e)
- goto error;
-
-+ PA_LLIST_HEAD_INIT(struct conjunction, e->conjunctions);
-+
- /* gather expressions to actual match format */
-- gather_expression(e, et);
-+ if (!gather_expression(e, et)) {
-+ /* gathering the expression from tokens went wrong */
-+ pa_log_error("failed to parse audio group stream classification data");
-+ goto error;
-+ }
-
--#if 1
-+#if 0
- print_expression(e);
- #endif
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 8 Jun 2014 16:32:57 +0300
-Subject: core-util: Add pa_append_to_home_dir()
-
-Change-Id: I746d2efb5f205820480b0cbd11c23cff11367656
----
- src/pulsecore/authkey.c | 7 ++-----
- src/pulsecore/core-util.c | 17 +++++++++++++++++
- src/pulsecore/core-util.h | 1 +
- 3 files changed, 20 insertions(+), 5 deletions(-)
-
-diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
-index 03c0c4b..be31e98 100644
---- a/src/pulsecore/authkey.c
-+++ b/src/pulsecore/authkey.c
-@@ -156,14 +156,11 @@ static char *normalize_path(const char *fn) {
- #else
- if (strlen(fn) < 3 || !IsCharAlpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
- #endif
-- char *homedir, *s;
-+ char *s;
-
-- if (!(homedir = pa_get_home_dir_malloc()))
-+ if (pa_append_to_home_dir(fn, &s) < 0)
- return NULL;
-
-- s = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", homedir, fn);
-- pa_xfree(homedir);
--
- return s;
- }
-
-diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
-index 508b3d3..99aa51f 100644
---- a/src/pulsecore/core-util.c
-+++ b/src/pulsecore/core-util.c
-@@ -1685,6 +1685,23 @@ char *pa_get_home_dir_malloc(void) {
- return homedir;
- }
-
-+int pa_append_to_home_dir(const char *path, char **_r) {
-+ char *home_dir;
-+
-+ pa_assert(path);
-+ pa_assert(_r);
-+
-+ home_dir = pa_get_home_dir_malloc();
-+ if (!home_dir) {
-+ pa_log("Failed to get home directory.");
-+ return -PA_ERR_NOENTITY;
-+ }
-+
-+ *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", home_dir, path);
-+ pa_xfree(home_dir);
-+ return 0;
-+}
-+
- char *pa_get_binary_name_malloc(void) {
- char *t;
- size_t allocated = 128;
-diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
-index aba1863..05d628e 100644
---- a/src/pulsecore/core-util.h
-+++ b/src/pulsecore/core-util.h
-@@ -137,6 +137,7 @@ char* pa_find_config_file(const char *global, const char *local, const char *env
- char *pa_get_runtime_dir(void);
- char *pa_get_state_dir(void);
- char *pa_get_home_dir_malloc(void);
-+int pa_append_to_home_dir(const char *path, char **_r);
- char *pa_get_binary_name_malloc(void);
- char *pa_runtime_path(const char *fn);
- char *pa_state_path(const char *fn, bool prepend_machine_id);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 8 Jun 2014 16:32:59 +0300
-Subject: core-util: Add pa_get_config_home_dir()
-
-Change-Id: I6aa3df386a7414563b03435683bad2596cf60b8b
----
- src/pulsecore/core-util.c | 58 ++++++++++++++++++++++++++++++++---------------
- src/pulsecore/core-util.h | 1 +
- 2 files changed, 41 insertions(+), 18 deletions(-)
-
-diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
-index 99aa51f..2569cfd 100644
---- a/src/pulsecore/core-util.c
-+++ b/src/pulsecore/core-util.c
-@@ -1581,16 +1581,6 @@ int pa_unlock_lockfile(const char *fn, int fd) {
- return r;
- }
-
--static char *get_config_home(char *home) {
-- char *t;
--
-- t = getenv("XDG_CONFIG_HOME");
-- if (t)
-- return pa_xstrdup(t);
--
-- return pa_sprintf_malloc("%s" PA_PATH_SEP ".config", home);
--}
--
- static int check_ours(const char *p) {
- struct stat st;
-
-@@ -1608,7 +1598,7 @@ static int check_ours(const char *p) {
- }
-
- static char *get_pulse_home(void) {
-- char *h, *ret, *config_home;
-+ char *h, *ret;
- int t;
-
- h = pa_get_home_dir_malloc();
-@@ -1626,17 +1616,14 @@ static char *get_pulse_home(void) {
-
- /* If the old directory exists, use it. */
- ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
-- if (access(ret, F_OK) >= 0) {
-- free(h);
-+ pa_xfree(h);
-+ if (access(ret, F_OK) >= 0)
- return ret;
-- }
- free(ret);
-
- /* Otherwise go for the XDG compliant directory. */
-- config_home = get_config_home(h);
-- free(h);
-- ret = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", config_home);
-- free(config_home);
-+ if (pa_get_config_home_dir(false, &ret) < 0)
-+ return NULL;
-
- return ret;
- }
-@@ -1702,6 +1689,41 @@ int pa_append_to_home_dir(const char *path, char **_r) {
- return 0;
- }
-
-+int pa_get_config_home_dir(bool use_machine_id, char **_r) {
-+ const char *e;
-+ char *base = NULL;
-+ int r = 0;
-+ char *machine_id = NULL;
-+
-+ pa_assert(_r);
-+
-+ e = getenv("XDG_CONFIG_HOME");
-+ if (e && *e)
-+ base = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse", e);
-+ else {
-+ r = pa_append_to_home_dir(".config" PA_PATH_SEP "pulse", &base);
-+ if (r < 0)
-+ goto finish;
-+ }
-+
-+ if (use_machine_id) {
-+ machine_id = pa_machine_id();
-+ if (!machine_id) {
-+ r = -PA_ERR_NOENTITY;
-+ goto finish;
-+ }
-+
-+ *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", base, machine_id);
-+ } else
-+ *_r = pa_xstrdup(base);
-+
-+finish:
-+ pa_xfree(machine_id);
-+ pa_xfree(base);
-+
-+ return r;
-+}
-+
- char *pa_get_binary_name_malloc(void) {
- char *t;
- size_t allocated = 128;
-diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
-index 05d628e..e222874 100644
---- a/src/pulsecore/core-util.h
-+++ b/src/pulsecore/core-util.h
-@@ -138,6 +138,7 @@ char *pa_get_runtime_dir(void);
- char *pa_get_state_dir(void);
- char *pa_get_home_dir_malloc(void);
- int pa_append_to_home_dir(const char *path, char **_r);
-+int pa_get_config_home_dir(bool use_machine_id, char **_r);
- char *pa_get_binary_name_malloc(void);
- char *pa_runtime_path(const char *fn);
- char *pa_state_path(const char *fn, bool prepend_machine_id);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Sun, 8 Jun 2014 16:33:00 +0300
-Subject: core-util: Add pa_append_to_config_home_dir()
-
-Change-Id: Ib4e6a096a740a61188220a983f26ecea434f6200
----
- src/pulsecore/core-util.c | 16 ++++++++++++++++
- src/pulsecore/core-util.h | 1 +
- 2 files changed, 17 insertions(+)
-
-diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
-index 2569cfd..dbe9ec1 100644
---- a/src/pulsecore/core-util.c
-+++ b/src/pulsecore/core-util.c
-@@ -1724,6 +1724,22 @@ finish:
- return r;
- }
-
-+int pa_append_to_config_home_dir(const char *path, bool use_machine_id, char **_r) {
-+ int r;
-+ char *config_home_dir;
-+
-+ pa_assert(path);
-+ pa_assert(_r);
-+
-+ r = pa_get_config_home_dir(use_machine_id, &config_home_dir);
-+ if (r < 0)
-+ return r;
-+
-+ *_r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", config_home_dir, path);
-+ pa_xfree(config_home_dir);
-+ return 0;
-+}
-+
- char *pa_get_binary_name_malloc(void) {
- char *t;
- size_t allocated = 128;
-diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
-index e222874..4968385 100644
---- a/src/pulsecore/core-util.h
-+++ b/src/pulsecore/core-util.h
-@@ -139,6 +139,7 @@ char *pa_get_state_dir(void);
- char *pa_get_home_dir_malloc(void);
- int pa_append_to_home_dir(const char *path, char **_r);
- int pa_get_config_home_dir(bool use_machine_id, char **_r);
-+int pa_append_to_config_home_dir(const char *path, bool use_machine_id, char **_r);
- char *pa_get_binary_name_malloc(void);
- char *pa_runtime_path(const char *fn);
- char *pa_state_path(const char *fn, bool prepend_machine_id);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 17 Jun 2014 19:35:21 +0300
-Subject: core: Create the config home directory on startup
-
-This avoids the need to check for the existence of the config home
-directory every time some file needs to be opened from that directory.
-
-Change-Id: I449c61aa46eaea3f8c7eb0aa040310db58421828
----
- src/pulsecore/core.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
-diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
-index e6f2dfc..4a66f80 100644
---- a/src/pulsecore/core.c
-+++ b/src/pulsecore/core.c
-@@ -24,6 +24,7 @@
- #include <config.h>
- #endif
-
-+#include <errno.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <signal.h>
-@@ -33,6 +34,7 @@
- #include <pulse/xmalloc.h>
-
- #include <pulsecore/module.h>
-+#include <pulsecore/core-error.h>
- #include <pulsecore/core-rtclock.h>
- #include <pulsecore/core-util.h>
- #include <pulsecore/core-scache.h>
-@@ -64,12 +66,22 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
- static void core_free(pa_object *o);
-
- pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
-+ int r;
-+ char *config_home_dir;
- pa_core* c;
- pa_mempool *pool;
- int j;
-
- pa_assert(m);
-
-+ r = pa_get_config_home_dir(true, &config_home_dir);
-+ if (r >= 0) {
-+ r = pa_make_secure_dir(config_home_dir, 0700, (uid_t) -1, (gid_t) -1, true);
-+ pa_xfree(config_home_dir);
-+ if (r < 0)
-+ pa_log("Failed to create config home directory (%s): %s", config_home_dir, pa_cstrerror(errno));
-+ }
-+
- if (shared) {
- if (!(pool = pa_mempool_new(shared, shm_size))) {
- pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool.");
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 15:33:01 +0300
-Subject: alsa: Handle unlinking of uninitialized streams gracefully
-
-There should be no assumptions about what has been initialized when
-the unlink hook is fired.
-
-Change-Id: I7502f0e7a3d244413dd806bc8657014999c9b9b3
----
- src/modules/alsa/module-alsa-card.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
-index 1e63230..7c408ee 100644
---- a/src/modules/alsa/module-alsa-card.c
-+++ b/src/modules/alsa/module-alsa-card.c
-@@ -579,7 +579,11 @@ static pa_hook_result_t sink_input_unlink_hook_callback(pa_core *c, pa_sink_inpu
- const char *role;
- pa_sink *sink = sink_input->sink;
-
-- pa_assert(sink);
-+ if (!sink)
-+ return PA_HOOK_OK;
-+
-+ if (!sink_input->proplist)
-+ return PA_HOOK_OK;
-
- role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
-
-@@ -594,7 +598,11 @@ static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source
- const char *role;
- pa_source *source = source_output->source;
-
-- pa_assert(source);
-+ if (!source)
-+ return PA_HOOK_OK;
-+
-+ if (!source_output->proplist)
-+ return PA_HOOK_OK;
-
- role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 15:59:06 +0300
-Subject: role-cork: Handle unlinking of uninitialized streams gracefully
-
-There should be no assumptions about what has been initialized when
-the unlink hook is fired.
-
-Change-Id: I4ea4372570e7a0a83c31caab6e2e6781a98cd3ad
----
- src/modules/module-role-cork.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c
-index 8ca2109..d72350c 100644
---- a/src/modules/module-role-cork.c
-+++ b/src/modules/module-role-cork.c
-@@ -162,6 +162,9 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool creat
- if (!create)
- pa_hashmap_remove(u->cork_state, i);
-
-+ if (!i->proplist)
-+ return PA_HOOK_OK;
-+
- if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
- return PA_HOOK_OK;
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 16:00:01 +0300
-Subject: role-ducking: Handle unlinking of uninitialized streams gracefully
-
-There should be no assumptions about what has been initialized when
-the unlink hook is fired.
-
-Change-Id: Id3c069093894bf508fbc0be75db046ff875ce965
----
- src/modules/module-role-ducking.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c
-index 9947871..d1ae357 100644
---- a/src/modules/module-role-ducking.c
-+++ b/src/modules/module-role-ducking.c
-@@ -159,6 +159,9 @@ static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck)
- pa_assert(u);
- pa_sink_input_assert_ref(i);
-
-+ if (!i->proplist)
-+ return PA_HOOK_OK;
-+
- if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
- return PA_HOOK_OK;
-
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 20:31:00 +0300
-Subject: core-util: Add pa_boolean_to_string()
-
-I need to save booleans in a plain text database, and the existing
-pa_yes_no() is not good, because it will do translation in the next
-PulseAudio release.
-
-Change-Id: I85f12da01aa0eb3d5c555350bd14ba337fbcc25b
----
- src/pulsecore/core-util.h | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
-index 4968385..c108968 100644
---- a/src/pulsecore/core-util.h
-+++ b/src/pulsecore/core-util.h
-@@ -89,6 +89,10 @@ int pa_parse_boolean(const char *s) PA_GCC_PURE;
-
- int pa_parse_volume(const char *s, pa_volume_t *volume);
-
-+static inline const char *pa_boolean_to_string(bool b) {
-+ return b ? "true" : "false";
-+}
-+
- static inline const char *pa_yes_no(bool b) {
- return b ? "yes" : "no";
- }
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 20:57:55 +0300
-Subject: sink-input,
- source-output: Assign to reference_ratio from a single place
-
-This makes it easy to log a message every time the reference ratio
-changes. I also need to add a hook for reference ratio changes, but
-that need will go away if the stream relative volume controls will be
-created by the core in the future.
-
-Change-Id: I2344ba7825f76cd72241599bd138b21e16555e01
----
- src/pulsecore/sink-input.c | 28 ++++++++++++++++++++++++++--
- src/pulsecore/sink-input.h | 6 ++++++
- src/pulsecore/sink.c | 11 +++++++----
- src/pulsecore/source-output.c | 26 +++++++++++++++++++++++++-
- src/pulsecore/source-output.h | 6 ++++++
- src/pulsecore/source.c | 11 +++++++----
- 6 files changed, 77 insertions(+), 11 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 9d13269..57c906d 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -1305,7 +1305,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
- /* OK, we are in normal volume mode. The volume only affects
- * ourselves */
- set_real_ratio(i, volume);
-- i->reference_ratio = i->volume;
-+ pa_sink_input_set_reference_ratio(i, &i->volume);
-
- /* Copy the new soft_volume to the thread_info struct */
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
-@@ -1757,7 +1757,7 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
-
- pa_cvolume_reset(&new_volume, i->volume.channels);
- pa_sink_input_set_volume_direct(i, &new_volume);
-- pa_cvolume_reset(&i->reference_ratio, i->reference_ratio.channels);
-+ pa_sink_input_set_reference_ratio(i, &new_volume);
- pa_assert(pa_cvolume_is_norm(&i->real_ratio));
- pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor));
- }
-@@ -2329,3 +2329,27 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume)
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], i);
- }
-+
-+/* Called from the main thread. */
-+void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio) {
-+ pa_cvolume old_ratio;
-+ char old_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(i);
-+ pa_assert(ratio);
-+
-+ old_ratio = i->reference_ratio;
-+
-+ if (pa_cvolume_equal(ratio, &old_ratio))
-+ return;
-+
-+ i->reference_ratio = *ratio;
-+
-+ if (!PA_SINK_INPUT_IS_LINKED(i->state))
-+ return;
-+
-+ pa_log_debug("Sink input %u reference ratio changed from %s to %s.", i->index,
-+ pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &i->channel_map, true),
-+ pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &i->channel_map, true));
-+}
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index 1bd3eee..e5b0ae8 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -437,6 +437,12 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
- * and fires change notifications. */
- void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume);
-
-+/* Called from the main thread, from sink.c only. This shouldn't be a public
-+ * function, but the flat volume logic in sink.c currently needs a way to
-+ * directly set the sink input reference ratio. This function simply sets
-+ * i->reference_ratio and logs a message if the value changes. */
-+void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio);
-+
- #define pa_sink_input_assert_io_context(s) \
- pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
-
-diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
-index 41cffcc..400d06d 100644
---- a/src/pulsecore/sink.c
-+++ b/src/pulsecore/sink.c
-@@ -1670,6 +1670,7 @@ void pa_sink_leave_passthrough(pa_sink *s) {
- static void compute_reference_ratio(pa_sink_input *i) {
- unsigned c = 0;
- pa_cvolume remapped;
-+ pa_cvolume ratio;
-
- pa_assert(i);
- pa_assert(pa_sink_flat_volume_enabled(i->sink));
-@@ -1684,7 +1685,7 @@ static void compute_reference_ratio(pa_sink_input *i) {
- remapped = i->sink->reference_volume;
- pa_cvolume_remap(&remapped, &i->sink->channel_map, &i->channel_map);
-
-- i->reference_ratio.channels = i->sample_spec.channels;
-+ ratio = i->reference_ratio;
-
- for (c = 0; c < i->sample_spec.channels; c++) {
-
-@@ -1694,14 +1695,16 @@ static void compute_reference_ratio(pa_sink_input *i) {
-
- /* Don't update the reference ratio unless necessary */
- if (pa_sw_volume_multiply(
-- i->reference_ratio.values[c],
-+ ratio.values[c],
- remapped.values[c]) == i->volume.values[c])
- continue;
-
-- i->reference_ratio.values[c] = pa_sw_volume_divide(
-+ ratio.values[c] = pa_sw_volume_divide(
- i->volume.values[c],
- remapped.values[c]);
- }
-+
-+ pa_sink_input_set_reference_ratio(i, &ratio);
- }
-
- /* Called from main context. Only called for the root sink in volume sharing
-@@ -2195,7 +2198,7 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
-
- /* 2. Since the sink's reference and real volumes are equal
- * now our ratios should be too. */
-- i->reference_ratio = i->real_ratio;
-+ pa_sink_input_set_reference_ratio(i, &i->real_ratio);
-
- /* 3. Recalculate the new stream reference volume based on the
- * reference ratio and the sink's reference volume.
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index d3d15f1..0012be3 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1313,7 +1313,7 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
-
- pa_cvolume_reset(&new_volume, o->volume.channels);
- pa_source_output_set_volume_direct(o, &new_volume);
-- pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
-+ pa_source_output_set_reference_ratio(o, &new_volume);
- pa_assert(pa_cvolume_is_norm(&o->real_ratio));
- pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
- }
-@@ -1695,3 +1695,27 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], o);
- }
-+
-+/* Called from the main thread. */
-+void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio) {
-+ pa_cvolume old_ratio;
-+ char old_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+ char new_ratio_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
-+
-+ pa_assert(o);
-+ pa_assert(ratio);
-+
-+ old_ratio = o->reference_ratio;
-+
-+ if (pa_cvolume_equal(ratio, &old_ratio))
-+ return;
-+
-+ o->reference_ratio = *ratio;
-+
-+ if (!PA_SOURCE_OUTPUT_IS_LINKED(o->state))
-+ return;
-+
-+ pa_log_debug("Source output %u reference ratio changed from %s to %s.", o->index,
-+ pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &o->channel_map, true),
-+ pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &o->channel_map, true));
-+}
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index 73170d3..3ed950b 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -359,6 +359,12 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
- * o->volume and fires change notifications. */
- void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume);
-
-+/* Called from the main thread, from source.c only. This shouldn't be a public
-+ * function, but the flat volume logic in source.c currently needs a way to
-+ * directly set the source output reference ratio. This function simply sets
-+ * o->reference_ratio and logs a message if the value changes. */
-+void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio);
-+
- #define pa_source_output_assert_io_context(s) \
- pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state))
-
-diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
-index db7f667..293ab28 100644
---- a/src/pulsecore/source.c
-+++ b/src/pulsecore/source.c
-@@ -1214,6 +1214,7 @@ void pa_source_leave_passthrough(pa_source *s) {
- static void compute_reference_ratio(pa_source_output *o) {
- unsigned c = 0;
- pa_cvolume remapped;
-+ pa_cvolume ratio;
-
- pa_assert(o);
- pa_assert(pa_source_flat_volume_enabled(o->source));
-@@ -1228,7 +1229,7 @@ static void compute_reference_ratio(pa_source_output *o) {
- remapped = o->source->reference_volume;
- pa_cvolume_remap(&remapped, &o->source->channel_map, &o->channel_map);
-
-- o->reference_ratio.channels = o->sample_spec.channels;
-+ ratio = o->reference_ratio;
-
- for (c = 0; c < o->sample_spec.channels; c++) {
-
-@@ -1238,14 +1239,16 @@ static void compute_reference_ratio(pa_source_output *o) {
-
- /* Don't update the reference ratio unless necessary */
- if (pa_sw_volume_multiply(
-- o->reference_ratio.values[c],
-+ ratio.values[c],
- remapped.values[c]) == o->volume.values[c])
- continue;
-
-- o->reference_ratio.values[c] = pa_sw_volume_divide(
-+ ratio.values[c] = pa_sw_volume_divide(
- o->volume.values[c],
- remapped.values[c]);
- }
-+
-+ pa_source_output_set_reference_ratio(o, &ratio);
- }
-
- /* Called from main context. Only called for the root source in volume sharing
-@@ -1712,7 +1715,7 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum
-
- /* 2. Since the source's reference and real volumes are equal
- * now our ratios should be too. */
-- o->reference_ratio = o->real_ratio;
-+ pa_source_output_set_reference_ratio(o, &o->real_ratio);
-
- /* 3. Recalculate the new stream reference volume based on the
- * reference ratio and the sink's reference volume.
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 21:26:17 +0300
-Subject: sink-input, source-output: Add hooks for reference ratio changes
-
-Needed for implementing relative volume controls for streams in
-module-volume-api. The plan is to create those controls in the core,
-though, and these hooks won't be needed at that point any more.
-
-Change-Id: Id30f38f4adfa9ede7bd0b12b484fe329ca1a3991
----
- src/pulsecore/core.h | 2 ++
- src/pulsecore/sink-input.c | 2 ++
- src/pulsecore/sink-input.h | 3 ++-
- src/pulsecore/source-output.c | 2 ++
- src/pulsecore/source-output.h | 3 ++-
- 5 files changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
-index 1f042b9..0e8f709 100644
---- a/src/pulsecore/core.h
-+++ b/src/pulsecore/core.h
-@@ -100,6 +100,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_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SINK_INPUT_REFERENCE_RATIO_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
- PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
-@@ -113,6 +114,7 @@ typedef enum pa_core_hook {
- PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
-+ PA_CORE_HOOK_SOURCE_OUTPUT_REFERENCE_RATIO_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED,
- PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
- PA_CORE_HOOK_CLIENT_NEW,
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 57c906d..d62be6f 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -2352,4 +2352,6 @@ void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio
- pa_log_debug("Sink input %u reference ratio changed from %s to %s.", i->index,
- pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &i->channel_map, true),
- pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &i->channel_map, true));
-+
-+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_REFERENCE_RATIO_CHANGED], i);
- }
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index e5b0ae8..c99ce1f 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -440,7 +440,8 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume)
- /* Called from the main thread, from sink.c only. This shouldn't be a public
- * function, but the flat volume logic in sink.c currently needs a way to
- * directly set the sink input reference ratio. This function simply sets
-- * i->reference_ratio and logs a message if the value changes. */
-+ * i->reference_ratio and logs a message and fires a hook if the value
-+ * changes. */
- void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio);
-
- #define pa_sink_input_assert_io_context(s) \
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index 0012be3..ae5a92c 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -1718,4 +1718,6 @@ void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume
- pa_log_debug("Source output %u reference ratio changed from %s to %s.", o->index,
- pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &o->channel_map, true),
- pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &o->channel_map, true));
-+
-+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_REFERENCE_RATIO_CHANGED], o);
- }
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index 3ed950b..60bbda8 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -362,7 +362,8 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v
- /* Called from the main thread, from source.c only. This shouldn't be a public
- * function, but the flat volume logic in source.c currently needs a way to
- * directly set the source output reference ratio. This function simply sets
-- * o->reference_ratio and logs a message if the value changes. */
-+ * o->reference_ratio and logs a message and fires a hook if the value
-+ * changes. */
- void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio);
-
- #define pa_source_output_assert_io_context(s) \
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Mon, 4 Aug 2014 21:42:45 +0300
-Subject: sink-input,
- source-output: Use new_data.volume only for absolute volume
-
-This simplifies life for modules that care about the initial volume of
-streams. new_data.volume will always be the absolute volume (assuming
-that flat volume is in effect) and new_data.reference_ratio will
-always be the relative volume.
-
-This will be especially useful when creating volume controls (absolute
-and relative) for new streams in module-volume-api.
-
-Change-Id: Ibca033c8441dde35a0b43d9276c41e383c675306
----
- src/modules/module-match.c | 2 +-
- src/modules/module-stream-restore.c | 8 ++---
- src/pulsecore/play-memblockq.c | 2 +-
- src/pulsecore/protocol-native.c | 6 ++--
- src/pulsecore/sink-input.c | 71 +++++++++++++++++++++++++------------
- src/pulsecore/sink-input.h | 5 +--
- src/pulsecore/sound-file-stream.c | 2 +-
- src/pulsecore/source-output.c | 71 +++++++++++++++++++++++++------------
- src/pulsecore/source-output.h | 5 +--
- 9 files changed, 111 insertions(+), 61 deletions(-)
-
-diff --git a/src/modules/module-match.c b/src/modules/module-match.c
-index 8ce3f00..7d73086 100644
---- a/src/modules/module-match.c
-+++ b/src/modules/module-match.c
-@@ -234,7 +234,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
- pa_cvolume cv;
- pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
- pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
-- pa_sink_input_new_data_set_volume(si, &cv);
-+ pa_sink_input_new_data_set_volume(si, &cv, true);
- } else
- pa_log_debug("the volume of sink input '%s' is not writable, can't change it", n);
- }
-diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
-index 4fc5645..1aa8a07 100644
---- a/src/modules/module-stream-restore.c
-+++ b/src/modules/module-stream-restore.c
-@@ -1482,9 +1482,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
-
- v = e->volume;
- pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
-- pa_sink_input_new_data_set_volume(new_data, &v);
--
-- new_data->volume_is_absolute = false;
-+ pa_sink_input_new_data_set_volume(new_data, &v, true);
- new_data->save_volume = true;
- }
- }
-@@ -1579,9 +1577,7 @@ static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source
-
- v = e->volume;
- pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
-- pa_source_output_new_data_set_volume(new_data, &v);
--
-- new_data->volume_is_absolute = false;
-+ pa_source_output_new_data_set_volume(new_data, &v, true);
- new_data->save_volume = true;
- }
- }
-diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
-index ff0c52b..055fd2a 100644
---- a/src/pulsecore/play-memblockq.c
-+++ b/src/pulsecore/play-memblockq.c
-@@ -203,7 +203,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_volume(&data, volume, false);
- pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
- data.flags |= flags;
-
-diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
-index 21e02fe..b209737 100644
---- a/src/pulsecore/protocol-native.c
-+++ b/src/pulsecore/protocol-native.c
-@@ -679,8 +679,7 @@ static record_stream* record_stream_new(
- pa_source_output_new_data_set_formats(&data, formats);
- data.direct_on_input = direct_on_input;
- if (volume) {
-- pa_source_output_new_data_set_volume(&data, volume);
-- data.volume_is_absolute = !relative_volume;
-+ pa_source_output_new_data_set_volume(&data, volume, relative_volume);
- data.save_volume = false;
- }
- if (muted_set) {
-@@ -1149,8 +1148,7 @@ static playback_stream* playback_stream_new(
- formats = NULL;
- }
- if (volume) {
-- pa_sink_input_new_data_set_volume(&data, volume);
-- data.volume_is_absolute = !relative_volume;
-+ pa_sink_input_new_data_set_volume(&data, volume, relative_volume);
- data.save_volume = false;
- }
- if (muted_set) {
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index d62be6f..796a567 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -145,12 +145,44 @@ bool pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data) {
- return false;
- }
-
--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_volume(pa_sink_input_new_data *data, const pa_cvolume *volume, bool relative) {
-+ pa_cvolume remapped_sink_volume;
-+
- pa_assert(data);
- pa_assert(data->volume_writable);
-
- if ((data->volume_is_set = !!volume))
- data->volume = *volume;
-+ else
-+ return;
-+
-+ data->volume_is_relative = relative;
-+
-+ if (data->sink) {
-+ remapped_sink_volume = data->sink->reference_volume;
-+ pa_cvolume_remap(&remapped_sink_volume, &data->sink->channel_map, &data->channel_map);
-+ }
-+
-+ if (relative) {
-+ data->reference_ratio = data->volume;
-+
-+ if (data->sink && pa_sink_flat_volume_enabled(data->sink)) {
-+ /* Let's keep data->volume as absolute, so that modules won't ever
-+ * have to specially handle the relative case. Modules inspecting
-+ * the volume should do so in the FIXATE hook, and at that point
-+ * data->sink is always set. data->volume is relative only during
-+ * the time before routing, and only if the sink input owner
-+ * requested relative volume. */
-+ pa_sw_cvolume_multiply(&data->volume, &data->volume, &remapped_sink_volume);
-+ data->volume_is_relative = false;
-+ }
-+ } else {
-+ if (data->sink)
-+ pa_sw_cvolume_divide(&data->reference_ratio, &data->volume, &remapped_sink_volume);
-+
-+ /* If data->sink is not set, we can't compute the reference ratio.
-+ * We'll compute it after routing. */
-+ }
- }
-
- void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor) {
-@@ -292,6 +324,7 @@ int pa_sink_input_new(
- int r;
- char *pt;
- char *memblockq_name;
-+ pa_cvolume v;
-
- pa_assert(_i);
- pa_assert(core);
-@@ -380,18 +413,17 @@ int pa_sink_input_new(
- if (r != PA_OK)
- return r;
-
-+ /* Now that the routing is done, we can finalize the volume if it has been
-+ * set. If the set volume is relative, we convert it to absolute, and if
-+ * it's absolute, we compute the reference ratio. */
-+ if (data->volume_is_set)
-+ pa_sink_input_new_data_set_volume(data, &data->volume, data->volume_is_relative);
-+
- /* Don't restore (or save) stream volume for passthrough streams and
- * prevent attenuation/gain */
- if (pa_sink_input_new_data_is_passthrough(data)) {
-- data->volume_is_set = true;
-- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-- data->volume_is_absolute = true;
-- data->save_volume = false;
-- }
--
-- if (!data->volume_is_set) {
-- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-- data->volume_is_absolute = false;
-+ pa_cvolume_reset(&v, data->sample_spec.channels);
-+ pa_sink_input_new_data_set_volume(data, &v, false);
- data->save_volume = false;
- }
-
-@@ -432,6 +464,12 @@ int pa_sink_input_new(
- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
- return r;
-
-+ if (!data->volume_is_set) {
-+ pa_cvolume_reset(&v, data->sample_spec.channels);
-+ pa_sink_input_new_data_set_volume(data, &v, true);
-+ data->save_volume = false;
-+ }
-+
- if ((data->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND) &&
- pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED) {
- pa_log_warn("Failed to create sink input: sink is suspended.");
-@@ -482,18 +520,7 @@ int pa_sink_input_new(
- i->sample_spec = data->sample_spec;
- i->channel_map = data->channel_map;
- i->format = pa_format_info_copy(data->format);
--
-- if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
-- pa_cvolume remapped;
--
-- /* When the 'absolute' bool is not set then we'll treat the volume
-- * as relative to the sink volume even in flat volume mode */
-- remapped = data->sink->reference_volume;
-- pa_cvolume_remap(&remapped, &data->sink->channel_map, &data->channel_map);
-- pa_sw_cvolume_multiply(&i->volume, &data->volume, &remapped);
-- } else
-- i->volume = data->volume;
--
-+ i->volume = data->volume;
- i->volume_factor_items = data->volume_factor_items;
- data->volume_factor_items = NULL;
- volume_factor_from_hashmap(&i->volume_factor, i->volume_factor_items, i->sample_spec.channels);
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index c99ce1f..b2d4967 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -311,6 +311,7 @@ typedef struct pa_sink_input_new_data {
- pa_idxset *nego_formats;
-
- pa_cvolume volume;
-+ pa_cvolume reference_ratio;
- bool muted:1;
- pa_hashmap *volume_factor_items, *volume_factor_sink_items;
-
-@@ -320,7 +321,7 @@ typedef struct pa_sink_input_new_data {
- bool volume_is_set:1;
- bool muted_is_set:1;
-
-- bool volume_is_absolute:1;
-+ bool volume_is_relative:1;
-
- bool volume_writable:1;
-
-@@ -331,7 +332,7 @@ 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);
- bool pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
--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_volume(pa_sink_input_new_data *data, const pa_cvolume *volume, bool relative);
- void pa_sink_input_new_data_add_volume_factor(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
- void pa_sink_input_new_data_add_volume_factor_sink(pa_sink_input_new_data *data, const char *key, const pa_cvolume *volume_factor);
- void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, bool mute);
-diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
-index 33f7337..6886025 100644
---- a/src/pulsecore/sound-file-stream.c
-+++ b/src/pulsecore/sound-file-stream.c
-@@ -302,7 +302,7 @@ int pa_play_file(
- data.driver = __FILE__;
- pa_sink_input_new_data_set_sample_spec(&data, &ss);
- pa_sink_input_new_data_set_channel_map(&data, &cm);
-- pa_sink_input_new_data_set_volume(&data, volume);
-+ pa_sink_input_new_data_set_volume(&data, volume, false);
- pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
- pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
- pa_sndfile_init_proplist(u->sndfile, data.proplist);
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index ae5a92c..c929999 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -86,12 +86,44 @@ bool pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
- return false;
- }
-
--void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume) {
-+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume, bool relative) {
-+ pa_cvolume remapped_source_volume;
-+
- pa_assert(data);
- pa_assert(data->volume_writable);
-
- if ((data->volume_is_set = !!volume))
- data->volume = *volume;
-+ else
-+ return;
-+
-+ data->volume_is_relative = relative;
-+
-+ if (data->source) {
-+ remapped_source_volume = data->source->reference_volume;
-+ pa_cvolume_remap(&remapped_source_volume, &data->source->channel_map, &data->channel_map);
-+ }
-+
-+ if (relative) {
-+ data->reference_ratio = data->volume;
-+
-+ if (data->source && pa_source_flat_volume_enabled(data->source)) {
-+ /* Let's keep data->volume as absolute, so that modules won't ever
-+ * have to specially handle the relative case. Modules inspecting
-+ * the volume should do so in the FIXATE hook, and at that point
-+ * data->source is always set. data->volume is relative only during
-+ * the time before routing, and only if the source output owner
-+ * requested relative volume. */
-+ pa_sw_cvolume_multiply(&data->volume, &data->volume, &remapped_source_volume);
-+ data->volume_is_relative = false;
-+ }
-+ } else {
-+ if (data->source)
-+ pa_sw_cvolume_divide(&data->reference_ratio, &data->volume, &remapped_source_volume);
-+
-+ /* If data->source is not set, we can't compute the reference ratio.
-+ * We'll compute it after routing. */
-+ }
- }
-
- void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
-@@ -226,6 +258,7 @@ int pa_source_output_new(
- pa_channel_map volume_map;
- int r;
- char *pt;
-+ pa_cvolume v;
-
- pa_assert(_o);
- pa_assert(core);
-@@ -316,18 +349,17 @@ int pa_source_output_new(
- if (r < 0)
- return r;
-
-+ /* Now that the routing is done, we can finalize the volume if it has been
-+ * set. If the set volume is relative, we convert it to absolute, and if
-+ * it's absolute, we compute the reference ratio. */
-+ if (data->volume_is_set)
-+ pa_source_output_new_data_set_volume(data, &data->volume, data->volume_is_relative);
-+
- /* Don't restore (or save) stream volume for passthrough streams and
- * prevent attenuation/gain */
- if (pa_source_output_new_data_is_passthrough(data)) {
-- data->volume_is_set = true;
-- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-- data->volume_is_absolute = true;
-- data->save_volume = false;
-- }
--
-- if (!data->volume_is_set) {
-- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-- data->volume_is_absolute = false;
-+ pa_cvolume_reset(&v, data->sample_spec.channels);
-+ pa_source_output_new_data_set_volume(data, &v, false);
- data->save_volume = false;
- }
-
-@@ -378,6 +410,12 @@ int pa_source_output_new(
- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
- return r;
-
-+ if (!data->volume_is_set) {
-+ pa_cvolume_reset(&v, data->sample_spec.channels);
-+ pa_source_output_new_data_set_volume(data, &v, true);
-+ data->save_volume = false;
-+ }
-+
- if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
- pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
- pa_log("Failed to create source output: source is suspended.");
-@@ -427,18 +465,7 @@ int pa_source_output_new(
- o->sample_spec = data->sample_spec;
- o->channel_map = data->channel_map;
- o->format = pa_format_info_copy(data->format);
--
-- if (!data->volume_is_absolute && pa_source_flat_volume_enabled(o->source)) {
-- pa_cvolume remapped;
--
-- /* When the 'absolute' bool is not set then we'll treat the volume
-- * as relative to the source volume even in flat volume mode */
-- remapped = data->source->reference_volume;
-- pa_cvolume_remap(&remapped, &data->source->channel_map, &data->channel_map);
-- pa_sw_cvolume_multiply(&o->volume, &data->volume, &remapped);
-- } else
-- o->volume = data->volume;
--
-+ o->volume = data->volume;
- o->volume_factor = data->volume_factor;
- o->volume_factor_source = data->volume_factor_source;
- o->real_ratio = o->reference_ratio = data->volume;
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index 60bbda8..dc82af9 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -257,6 +257,7 @@ typedef struct pa_source_output_new_data {
- pa_idxset *nego_formats;
-
- pa_cvolume volume, volume_factor, volume_factor_source;
-+ pa_cvolume reference_ratio;
- bool muted:1;
-
- bool sample_spec_is_set:1;
-@@ -265,7 +266,7 @@ typedef struct pa_source_output_new_data {
- bool volume_is_set:1, volume_factor_is_set:1, volume_factor_source_is_set:1;
- bool muted_is_set:1;
-
-- bool volume_is_absolute:1;
-+ bool volume_is_relative:1;
-
- bool volume_writable:1;
-
-@@ -276,7 +277,7 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
- void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
- void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
- bool pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data);
--void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
-+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume, bool relative);
- void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
- void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
- void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, bool mute);
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 5 Aug 2014 12:01:54 +0300
-Subject: sink-input, source-output: Add the real object pointer to new_data
-
-module-volume-api needs the pointer already in the FIXATE hook, where
-it creates the volume control objects. The pointer is needed, because
-otherwise there's no way to correlate the created controls with the
-sink inputs and source outputs.
-
-Since the object is created early, pa_sink_input_new() and
-pa_source_output_new() need to free it in case of failure, so a bunch
-of direct returns were replaced with "goto fails".
-
-Since the object may now be unlinked in a completely uninitialized
-state, I reviewed the unlinking code, and made sure that unlinking is
-performed only once (the "unlinked" flag was needed for this).
-
-Change-Id: I89bee3fb51c54d270ccf856750c5b577babc7905
----
- src/pulsecore/sink-input.c | 96 ++++++++++++++++++++++++++------------
- src/pulsecore/sink-input.h | 6 +++
- src/pulsecore/source-output.c | 105 ++++++++++++++++++++++++++++++------------
- src/pulsecore/source-output.h | 6 +++
- 4 files changed, 155 insertions(+), 58 deletions(-)
-
-diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
-index 796a567..d419884 100644
---- a/src/pulsecore/sink-input.c
-+++ b/src/pulsecore/sink-input.c
-@@ -331,6 +331,9 @@ int pa_sink_input_new(
- pa_assert(data);
- pa_assert_ctl_context();
-
-+ i = data->sink_input = pa_msgobject_new(pa_sink_input);
-+ i->state = PA_SINK_INPUT_INIT;
-+
- if (data->client)
- pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
-
-@@ -348,22 +351,31 @@ int pa_sink_input_new(
- !(data->flags & PA_SINK_INPUT_FIX_FORMAT),
- !(data->flags & PA_SINK_INPUT_FIX_RATE),
- !(data->flags & PA_SINK_INPUT_FIX_CHANNELS));
-- if (!f)
-- return -PA_ERR_INVALID;
-+ if (!f) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- formats = pa_idxset_new(NULL, NULL);
- pa_idxset_put(formats, f, NULL);
- pa_sink_input_new_data_set_formats(data, formats);
- }
-
-- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
-- return r;
-+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data);
-
-- pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
-+ if (data->driver && !pa_utf8_valid(data->driver)) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- if (!data->sink) {
- pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
-- pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
-+
-+ if (!sink) {
-+ r = -PA_ERR_NOENTITY;
-+ goto fail;
-+ }
-+
- pa_sink_input_new_data_set_sink(data, sink, false);
- }
-
-@@ -382,13 +394,20 @@ int pa_sink_input_new(
- PA_IDXSET_FOREACH(format, data->req_formats, idx)
- pa_log_info(" -- %s", pa_format_info_snprint(fmt, sizeof(fmt), format));
-
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
-+ }
-+
-+ if (!PA_SINK_IS_LINKED(pa_sink_get_state(data->sink))) {
-+ r = -PA_ERR_BADSTATE;
-+ goto fail;
- }
-
-- pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
-- pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink
-- && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED),
-- -PA_ERR_INVALID);
-+ if (data->sync_base
-+ && !(data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED)) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- /* Routing is done. We have a sink and a format. */
-
-@@ -399,7 +418,7 @@ int pa_sink_input_new(
- * modified in pa_format_info_to_sample_spec2(). */
- r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
- if (r < 0)
-- return r;
-+ goto fail;
- }
-
- /* Now populate the sample spec and channel map according to the final
-@@ -407,11 +426,11 @@ int pa_sink_input_new(
- r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->sink->sample_spec,
- &data->sink->channel_map);
- if (r < 0)
-- return r;
-+ goto fail;
-
- r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
- if (r != PA_OK)
-- return r;
-+ goto fail;
-
- /* Now that the routing is done, we can finalize the volume if it has been
- * set. If the set volume is relative, we convert it to absolute, and if
-@@ -453,16 +472,19 @@ int pa_sink_input_new(
- /* rate update failed, or other parts of sample spec didn't match */
-
- pa_log_debug("Could not update sink sample spec to match passthrough stream");
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
- }
-
- 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->resample_method >= PA_RESAMPLER_MAX) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
-- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
-- return r;
-+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data);
-
- if (!data->volume_is_set) {
- pa_cvolume_reset(&v, data->sample_spec.channels);
-@@ -473,12 +495,14 @@ int pa_sink_input_new(
- if ((data->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND) &&
- pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED) {
- pa_log_warn("Failed to create sink input: sink is suspended.");
-- return -PA_ERR_BADSTATE;
-+ r = -PA_ERR_BADSTATE;
-+ goto fail;
- }
-
- if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
- pa_log_warn("Failed to create sink input: too many inputs per sink.");
-- return -PA_ERR_TOOLARGE;
-+ r = -PA_ERR_TOOLARGE;
-+ goto fail;
- }
-
- if ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
-@@ -497,16 +521,15 @@ int pa_sink_input_new(
- (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
- (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
- pa_log_warn("Unsupported resampling operation.");
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
- }
- }
-
-- i = pa_msgobject_new(pa_sink_input);
- i->parent.parent.free = sink_input_free;
- i->parent.process_msg = pa_sink_input_process_msg;
-
- i->core = core;
-- i->state = PA_SINK_INPUT_INIT;
- i->flags = data->flags;
- i->proplist = pa_proplist_copy(data->proplist);
- i->driver = pa_xstrdup(pa_path_get_filename(data->driver));
-@@ -610,6 +633,16 @@ int pa_sink_input_new(
-
- *_i = i;
- return 0;
-+
-+fail:
-+ if (i) {
-+ pa_sink_input_unlink(i);
-+ pa_sink_input_unref(i);
-+ }
-+
-+ data->sink_input = NULL;
-+
-+ return r;
- }
-
- /* Called from main context */
-@@ -683,6 +716,11 @@ void pa_sink_input_unlink(pa_sink_input *i) {
- pa_assert(i);
- pa_assert_ctl_context();
-
-+ if (i->unlinked)
-+ return;
-+
-+ i->unlinked = true;
-+
- /* See pa_sink_unlink() for a couple of comments how this function
- * works */
-
-@@ -691,7 +729,9 @@ void pa_sink_input_unlink(pa_sink_input *i) {
- linked = PA_SINK_INPUT_IS_LINKED(i->state);
-
- if (linked)
-- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
-+ pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL);
-+
-+ 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;
-@@ -700,8 +740,6 @@ void pa_sink_input_unlink(pa_sink_input *i) {
-
- i->sync_prev = i->sync_next = NULL;
-
-- 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);
-@@ -732,10 +770,10 @@ void pa_sink_input_unlink(pa_sink_input *i) {
-
- reset_callbacks(i);
-
-- if (linked) {
-+ if (linked)
- 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_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
-
- if (i->sink) {
- if (PA_SINK_IS_LINKED(pa_sink_get_state(i->sink)))
-diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
-index b2d4967..a4c6519 100644
---- a/src/pulsecore/sink-input.h
-+++ b/src/pulsecore/sink-input.h
-@@ -76,6 +76,7 @@ struct pa_sink_input {
- * pa_sink_input_get_state(). That function will transparently
- * merge the thread_info.drained value in. */
- pa_sink_input_state_t state;
-+ bool unlinked;
- pa_sink_input_flags_t flags;
-
- char *driver; /* may be NULL */
-@@ -289,6 +290,11 @@ typedef struct pa_sink_input_send_event_hook_data {
- } pa_sink_input_send_event_hook_data;
-
- typedef struct pa_sink_input_new_data {
-+ /* The sink input object is not properly initialized, so don't access the
-+ * member variables! You can only rely on the state variable being
-+ * initialized to PA_SINK_INPUT_INIT. */
-+ pa_sink_input *sink_input;
-+
- pa_sink_input_flags_t flags;
-
- pa_proplist *proplist;
-diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
-index c929999..60f3391 100644
---- a/src/pulsecore/source-output.c
-+++ b/src/pulsecore/source-output.c
-@@ -265,6 +265,9 @@ int pa_source_output_new(
- pa_assert(data);
- pa_assert_ctl_context();
-
-+ o = data->source_output = pa_msgobject_new(pa_source_output);
-+ o->state = PA_SOURCE_OUTPUT_INIT;
-+
- if (data->client)
- pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
-
-@@ -282,28 +285,38 @@ int pa_source_output_new(
- !(data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT),
- !(data->flags & PA_SOURCE_OUTPUT_FIX_RATE),
- !(data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS));
-- if (!f)
-- return -PA_ERR_INVALID;
-+ if (!f) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- formats = pa_idxset_new(NULL, NULL);
- pa_idxset_put(formats, f, NULL);
- pa_source_output_new_data_set_formats(data, formats);
- }
-
-- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
-- return r;
-+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data);
-
-- pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
-+ if (data->driver && !pa_utf8_valid(data->driver)) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- if (!data->source) {
- pa_source *source;
-
- if (data->direct_on_input) {
- source = data->direct_on_input->sink->monitor_source;
-- pa_return_val_if_fail(source, -PA_ERR_INVALID);
-+ if (!source) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
- } else {
- source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
-- pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
-+ if (!source) {
-+ r = -PA_ERR_NOENTITY;
-+ goto fail;
-+ }
- }
-
- pa_source_output_new_data_set_source(data, source, false);
-@@ -324,11 +337,19 @@ int pa_source_output_new(
- PA_IDXSET_FOREACH(format, data->req_formats, idx)
- pa_log_info(" -- %s", pa_format_info_snprint(fmt, sizeof(fmt), format));
-
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
- }
-
-- pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
-- pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
-+ if (!PA_SOURCE_IS_LINKED(pa_source_get_state(data->source))) {
-+ r = -PA_ERR_BADSTATE;
-+ goto fail;
-+ }
-+
-+ if (data->direct_on_input && data->direct_on_input->sink != data->source->monitor_of) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- /* Routing is done. We have a source and a format. */
-
-@@ -339,7 +360,7 @@ int pa_source_output_new(
- * modified in pa_format_info_to_sample_spec2(). */
- r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
- if (r < 0)
-- return r;
-+ goto fail;
- }
-
- /* Now populate the sample spec and channel map according to the final
-@@ -347,7 +368,7 @@ int pa_source_output_new(
- r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->source->sample_spec,
- &data->source->channel_map);
- if (r < 0)
-- return r;
-+ goto fail;
-
- /* Now that the routing is done, we can finalize the volume if it has been
- * set. If the set volume is relative, we convert it to absolute, and if
-@@ -374,12 +395,18 @@ int pa_source_output_new(
- if (!data->volume_factor_is_set)
- pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
-
-- pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
-+ if (!pa_cvolume_compatible(&data->volume_factor, &data->sample_spec)) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- if (!data->volume_factor_source_is_set)
- pa_cvolume_reset(&data->volume_factor_source, data->source->sample_spec.channels);
-
-- pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec), -PA_ERR_INVALID);
-+ if (!pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec)) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
- if (!data->muted_is_set)
- data->muted = false;
-@@ -399,16 +426,19 @@ int pa_source_output_new(
- /* rate update failed, or other parts of sample spec didn't match */
-
- pa_log_debug("Could not update source sample spec to match passthrough stream");
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
- }
-
- 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->resample_method >= PA_RESAMPLER_MAX) {
-+ r = -PA_ERR_INVALID;
-+ goto fail;
-+ }
-
-- if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
-- return r;
-+ pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data);
-
- if (!data->volume_is_set) {
- pa_cvolume_reset(&v, data->sample_spec.channels);
-@@ -419,12 +449,14 @@ int pa_source_output_new(
- if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
- pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
- pa_log("Failed to create source output: source is suspended.");
-- return -PA_ERR_BADSTATE;
-+ r = -PA_ERR_BADSTATE;
-+ goto fail;
- }
-
- if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
- pa_log("Failed to create source output: too many outputs per source.");
-- return -PA_ERR_TOOLARGE;
-+ r = -PA_ERR_TOOLARGE;
-+ goto fail;
- }
-
- if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
-@@ -442,16 +474,15 @@ int pa_source_output_new(
- (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
- (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
- pa_log_warn("Unsupported resampling operation.");
-- return -PA_ERR_NOTSUPPORTED;
-+ r = -PA_ERR_NOTSUPPORTED;
-+ goto fail;
- }
- }
-
-- o = pa_msgobject_new(pa_source_output);
- o->parent.parent.free = source_output_free;
- o->parent.process_msg = pa_source_output_process_msg;
-
- o->core = core;
-- o->state = PA_SOURCE_OUTPUT_INIT;
- o->flags = data->flags;
- o->proplist = pa_proplist_copy(data->proplist);
- o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
-@@ -526,6 +557,16 @@ int pa_source_output_new(
-
- *_o = o;
- return 0;
-+
-+fail:
-+ if (o) {
-+ pa_source_output_unlink(o);
-+ pa_source_output_unref(o);
-+ }
-+
-+ data->source_output = NULL;
-+
-+ return r;
- }
-
- /* Called from main context */
-@@ -576,9 +617,15 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_
- /* Called from main context */
- void pa_source_output_unlink(pa_source_output*o) {
- bool linked;
-+
- pa_assert(o);
- pa_assert_ctl_context();
-
-+ if (o->unlinked)
-+ return;
-+
-+ o->unlinked = true;
-+
- /* See pa_sink_unlink() for a couple of comments how this function
- * works */
-
-@@ -587,13 +634,13 @@ void pa_source_output_unlink(pa_source_output*o) {
- linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
-
- if (linked)
-- pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
-+ pa_idxset_remove_by_data(o->core->source_outputs, o, NULL);
-+
-+ 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->core->source_outputs, o, NULL);
--
- if (o->source)
- if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
- pa_source_output_unref(o);
-@@ -618,10 +665,10 @@ void pa_source_output_unlink(pa_source_output*o) {
-
- reset_callbacks(o);
-
-- if (linked) {
-+ if (linked)
- 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_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
-
- if (o->source) {
- if (PA_SOURCE_IS_LINKED(pa_source_get_state(o->source)))
-diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
-index dc82af9..af6d347 100644
---- a/src/pulsecore/source-output.h
-+++ b/src/pulsecore/source-output.h
-@@ -69,6 +69,7 @@ struct pa_source_output {
- pa_core *core;
-
- pa_source_output_state_t state;
-+ bool unlinked;
- pa_source_output_flags_t flags;
-
- char *driver; /* may be NULL */
-@@ -236,6 +237,11 @@ typedef struct pa_source_output_send_event_hook_data {
- } pa_source_output_send_event_hook_data;
-
- typedef struct pa_source_output_new_data {
-+ /* The source output object is not properly initialized, so don't access
-+ * the member variables! You can only rely on the state variable being
-+ * initialized to PA_SOURCE_OUTPUT_INIT. */
-+ pa_source_output *source_output;
-+
- pa_source_output_flags_t flags;
-
- pa_proplist *proplist;
+++ /dev/null
-From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
-Date: Tue, 17 Jun 2014 19:45:45 +0300
-Subject: audio-groups, main-volume-policy, volume-api: Various fixes
-
-Sorry, this is a huge unreviewable commit. Contained improvements
-include at least:
-
- * Flat volumes are now handled properly. Previously, audio groups
- controlled the absolute volume of streams if flat volume was in
- effect, which made no sense.
- * Audio group volumes are now persistent.
- * Audio group volumes are applied to new streams before the streams
- start to play, instead of after, which could cause audible
- glitches.
- * When a stream volume is changed by the user, the volume is
- propagated to the stream's audio group.
- * Fixed the handling of the "NEG" keyword in the match syntax in
- module-audio-groups. Previously the "NEG" keyword was parsed, but
- it had no effect.
-
-Change-Id: I02bad3d23b3e562c71dbc6af6f3e308089893751
----
- src/Makefile.am | 2 +-
- src/map-file | 3 +
- src/modules/audio-groups/audio-groups.conf.example | 19 +-
- src/modules/audio-groups/module-audio-groups.c | 1710 ++++++++++++++------
- .../main-volume-policy/main-volume-context.c | 184 +--
- .../main-volume-policy/main-volume-context.h | 35 +-
- .../main-volume-policy/main-volume-policy.c | 65 +-
- .../main-volume-policy.conf.example | 1 -
- .../main-volume-policy/main-volume-policy.h | 6 +-
- .../main-volume-policy/module-main-volume-policy.c | 651 +++++---
- src/modules/volume-api/audio-group.c | 288 +---
- src/modules/volume-api/audio-group.h | 29 +-
- src/modules/volume-api/binding.c | 386 -----
- src/modules/volume-api/binding.h | 128 --
- src/modules/volume-api/bvolume.h | 3 +
- src/modules/volume-api/device-creator.c | 431 +++--
- src/modules/volume-api/device.c | 54 +-
- src/modules/volume-api/device.h | 4 +-
- src/modules/volume-api/inidb.c | 553 +++++++
- src/modules/volume-api/inidb.h | 54 +
- src/modules/volume-api/module-volume-api.c | 27 +
- src/modules/volume-api/mute-control.c | 241 +--
- src/modules/volume-api/mute-control.h | 75 +-
- src/modules/volume-api/sstream.c | 313 ++--
- src/modules/volume-api/sstream.h | 56 +-
- src/modules/volume-api/stream-creator.c | 691 ++++----
- src/modules/volume-api/volume-api.c | 430 +++--
- src/modules/volume-api/volume-api.h | 79 +-
- src/modules/volume-api/volume-control.c | 308 ++--
- src/modules/volume-api/volume-control.h | 93 +-
- src/pulse/ext-volume-api.c | 100 ++
- src/pulse/ext-volume-api.h | 3 +
- src/tizen-ivi/audio-groups.conf | 23 +-
- src/tizen-ivi/main-volume-policy.conf | 1 -
- 34 files changed, 4012 insertions(+), 3034 deletions(-)
- delete mode 100644 src/modules/volume-api/binding.c
- delete mode 100644 src/modules/volume-api/binding.h
- create mode 100644 src/modules/volume-api/inidb.c
- create mode 100644 src/modules/volume-api/inidb.h
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 9d17336..b5cf2a8 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -1098,10 +1098,10 @@ endif
-
- libvolume_api_la_SOURCES = \
- modules/volume-api/audio-group.c modules/volume-api/audio-group.h \
-- modules/volume-api/binding.c modules/volume-api/binding.h \
- modules/volume-api/bvolume.h \
- modules/volume-api/device.c modules/volume-api/device.h \
- modules/volume-api/device-creator.c modules/volume-api/device-creator.h \
-+ modules/volume-api/inidb.c modules/volume-api/inidb.h \
- modules/volume-api/mute-control.c modules/volume-api/mute-control.h \
- modules/volume-api/sstream.c modules/volume-api/sstream.h \
- modules/volume-api/stream-creator.c modules/volume-api/stream-creator.h \
-diff --git a/src/map-file b/src/map-file
-index 28ea54e..cb31833 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -190,13 +190,16 @@ pa_ext_node_manager_set_subscribe_cb;
- pa_ext_echo_cancel_set_volume;
- pa_ext_echo_cancel_set_device;
- pa_ext_volume_api_balance_valid;
-+pa_ext_volume_api_bvolume_balance_to_string;
- pa_ext_volume_api_bvolume_copy_balance;
- pa_ext_volume_api_bvolume_get_left_right_balance;
- pa_ext_volume_api_bvolume_get_rear_front_balance;
- pa_ext_volume_api_bvolume_equal;
- pa_ext_volume_api_bvolume_from_cvolume;
-+pa_ext_volume_api_bvolume_init;
- pa_ext_volume_api_bvolume_init_invalid;
- pa_ext_volume_api_bvolume_init_mono;
-+pa_ext_volume_api_bvolume_parse_balance;
- pa_ext_volume_api_bvolume_remap;
- pa_ext_volume_api_bvolume_reset_balance;
- pa_ext_volume_api_bvolume_set_left_right_balance;
-diff --git a/src/modules/audio-groups/audio-groups.conf.example b/src/modules/audio-groups/audio-groups.conf.example
-index 8acdb76..6aa6989 100644
---- a/src/modules/audio-groups/audio-groups.conf.example
-+++ b/src/modules/audio-groups/audio-groups.conf.example
-@@ -1,28 +1,29 @@
- [General]
--audio-groups = x-example-call-downlink-audio-group x-example-default-output-audio-group x-example-music-output-audio-group
--streams = phone-output music-output default-output
-+stream-rules = phone-output music-output default-output
-
- [AudioGroup x-example-call-downlink-audio-group]
--volume-control = create
--mute-control = none
-+volume-control = create:call-downlink-volume-control
-+mute-control = create:call-downlink-mute-control
-
- [AudioGroup x-example-default-output-audio-group]
--volume-control = create
--mute-control = none
-+volume-control = create:default-output-volume-control
-+mute-control = create:call-downlink-mute-control
-
- [AudioGroup x-example-music-output-audio-group]
- volume-control = bind:AudioGroup:x-example-default-output-audio-group
-+mute-control = bind:AudioGroup:x-example-default-output-audio-group
-
--[Stream phone-output]
-+[StreamRule phone-output]
- match = (direction output AND property media.role=phone)
- audio-group-for-volume = x-example-call-downlink-audio-group
- audio-group-for-mute = x-example-call-downlink-audio-group
-
--[Stream music-output]
-+[StreamRule music-output]
- match = (direction output AND property media.role=music)
- audio-group-for-volume = x-example-music-output-audio-group
- audio-group-for-mute = x-example-music-output-audio-group
-
--[Stream default-output]
-+[StreamRule default-output]
-+match = (direction output)
- audio-group-for-volume = x-example-default-output-audio-group
- audio-group-for-mute = x-example-default-output-audio-group
-diff --git a/src/modules/audio-groups/module-audio-groups.c b/src/modules/audio-groups/module-audio-groups.c
-index 2b3a570..e37a24e 100644
---- a/src/modules/audio-groups/module-audio-groups.c
-+++ b/src/modules/audio-groups/module-audio-groups.c
-@@ -40,15 +40,18 @@
-
- #include "module-audio-groups-symdef.h"
-
-+#define AUDIOGROUP_START "AudioGroup "
-+#define STREAM_RULE_START "StreamRule "
-+#define NONE_KEYWORD "none"
-+#define CREATE_PREFIX "create:"
-+#define BIND_PREFIX "bind:"
-+#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
-+
- PA_MODULE_AUTHOR("Ismo Puustinen");
- PA_MODULE_DESCRIPTION("Create audio groups and classify streams to them");
- PA_MODULE_VERSION(PACKAGE_VERSION);
- PA_MODULE_LOAD_ONCE(true);
-
--#ifndef AUDIO_GROUP_CONFIG
--#define AUDIO_GROUP_CONFIG "audio-groups.conf"
--#endif
--
- enum match_direction {
- match_direction_unknown = 0,
- match_direction_input,
-@@ -80,60 +83,103 @@ struct expression {
- PA_LLIST_HEAD(struct conjunction, conjunctions);
- };
-
--/* data gathered from settings */
-+struct group {
-+ struct userdata *userdata;
-+ pa_audio_group *audio_group;
-+ struct control *volume_control;
-+ struct control *mute_control;
-+ char *own_volume_control_name;
-+ char *own_mute_control_name;
-+ struct group *volume_master;
-+ struct group *mute_master;
-+ char *volume_master_name;
-+ char *mute_master_name;
-+
-+ pa_hashmap *volume_slaves; /* struct group -> struct group (hashmap-as-a-set) */
-+ pa_hashmap *mute_slaves; /* struct group -> struct group (hashmap-as-a-set) */
-+ pa_hashmap *volume_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
-+ pa_hashmap *mute_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
-+
-+ bool unlinked;
-+};
-
--enum control_action {
-- CONTROL_ACTION_NONE,
-- CONTROL_ACTION_CREATE,
-- CONTROL_ACTION_BIND,
-+enum control_type {
-+ CONTROL_TYPE_VOLUME,
-+ CONTROL_TYPE_MUTE,
- };
-
--struct audio_group {
-+struct control {
- struct userdata *userdata;
-- char *id;
-- char *description;
-- enum control_action volume_control_action;
-- enum control_action mute_control_action;
-- pa_binding_target_info *volume_control_target_info;
-- pa_binding_target_info *mute_control_target_info;
-+ enum control_type type;
-+
-+ union {
-+ pa_volume_control *volume_control;
-+ pa_mute_control *mute_control;
-+ };
-+
-+ /* Controls that are created for streams don't own their pa_volume_control
-+ * and pa_mute_control objects, because they're owned by the streams. */
-+ bool own_control;
-
-- /* official audio group */
-- pa_audio_group *group;
-+ /* If non-NULL, then this control mirrors the state of the master
-+ * control. If someone changes the master state, the state of this control
-+ * is also updated, and also if someone changes this control's state, the
-+ * change is applied also to the master. */
-+ struct control *master;
-
-- struct audio_group_control *volume_control;
-- struct audio_group_control *mute_control;
-+ /* struct control -> struct control (hashmap-as-a-set)
-+ * Contains the controls that have this control as their master. */
-+ pa_hashmap *slaves;
-
-+ /* Set to true when the master control's state has been copied to this
-+ * control. */
-+ bool synced_with_master;
-+
-+ bool acquired;
- bool unlinked;
- };
-
--struct stream {
-+struct stream_rule {
- struct userdata *userdata;
-- char *id;
-+ char *name;
- enum match_direction direction;
- char *audio_group_name_for_volume;
- char *audio_group_name_for_mute;
-- pa_audio_group *audio_group_for_volume;
-- pa_audio_group *audio_group_for_mute;
-- pa_binding_target_info *volume_control_target_info;
-- pa_binding_target_info *mute_control_target_info;
-- struct expression *rule;
--
-- bool unlinked;
-+ struct group *group_for_volume;
-+ struct group *group_for_mute;
-+ struct expression *match_expression;
- };
-
- struct userdata {
-- pa_hashmap *audio_groups; /* name -> struct audio_group */
-- pa_dynarray *streams; /* struct stream */
-- pa_hook_slot *new_stream_volume;
-- pa_hook_slot *new_stream_mute;
--
-- pa_volume_api *api;
--
-- /* The following fields are only used during initialization. */
-- pa_hashmap *audio_group_names; /* name -> name (hashmap-as-a-set) */
-- pa_hashmap *unused_audio_groups; /* name -> struct audio_group */
-- pa_dynarray *stream_names;
-- pa_hashmap *unused_streams; /* name -> struct stream */
-+ pa_volume_api *volume_api;
-+ pa_hashmap *groups; /* name -> struct group */
-+ pa_hashmap *stream_rules; /* name -> struct stream_rule */
-+ pa_dynarray *stream_rules_list; /* struct stream_rule */
-+
-+ /* pas_stream -> struct stream_rule
-+ * When a stream matches with a rule, it's added here. */
-+ pa_hashmap *rules_by_stream;
-+
-+ /* pas_stream -> struct control
-+ * Contains proxy controls for all relative volume controls of streams. */
-+ pa_hashmap *stream_volume_controls;
-+
-+ /* pas_stream -> struct control
-+ * Contains proxy controls for all mute controls of streams. */
-+ pa_hashmap *stream_mute_controls;
-+
-+ pa_hook_slot *stream_put_slot;
-+ pa_hook_slot *stream_unlink_slot;
-+ pa_hook_slot *volume_control_implementation_initialized_slot;
-+ pa_hook_slot *mute_control_implementation_initialized_slot;
-+ pa_hook_slot *volume_control_set_initial_volume_slot;
-+ pa_hook_slot *mute_control_set_initial_mute_slot;
-+ pa_hook_slot *volume_control_volume_changed_slot;
-+ pa_hook_slot *mute_control_mute_changed_slot;
-+ pa_hook_slot *volume_control_unlink_slot;
-+ pa_hook_slot *mute_control_unlink_slot;
-+
-+ pa_dynarray *stream_rule_names; /* Only used during initialization. */
- };
-
- static const char* const valid_modargs[] = {
-@@ -141,77 +187,408 @@ static const char* const valid_modargs[] = {
- NULL
- };
-
--static void audio_group_unlink(struct audio_group *group);
-+static void control_free(struct control *control);
-+static void control_set_master(struct control *control, struct control *master);
-+static void control_add_slave(struct control *control, struct control *slave);
-+static void control_remove_slave(struct control *control, struct control *slave);
-
--static void print_literal(struct literal *l);
--static void print_conjunction(struct conjunction *c);
--static void print_expression(struct expression *e);
--static void delete_expression(struct expression *e);
-+static void group_free(struct group *group);
-+static void group_set_master(struct group *group, enum control_type type, struct group *master);
-+static int group_set_master_name(struct group *group, enum control_type type, const char *name);
-+static void group_disable_control(struct group *group, enum control_type type);
-+static void group_add_slave(struct group *group, enum control_type type, struct group *slave);
-+static void group_remove_slave(struct group *group, enum control_type type, struct group *slave);
-
--static struct audio_group *audio_group_new(struct userdata *u, const char *name) {
-- struct audio_group *group;
-+static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group);
-+static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name);
-
-- pa_assert(u);
-- pa_assert(name);
-+static bool literal_match(struct literal *literal, pas_stream *stream);
-
-- group = pa_xnew0(struct audio_group, 1);
-- group->userdata = u;
-- group->id = pa_xstrdup(name);
-- group->description = pa_xstrdup(name);
-- group->volume_control_action = CONTROL_ACTION_NONE;
-- group->mute_control_action = CONTROL_ACTION_NONE;
-+static struct expression *expression_new(void);
-+static void expression_free(struct expression *expression);
-+
-+static int volume_control_set_volume_cb(pa_volume_control *volume_control, const pa_bvolume *original_volume,
-+ const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
-+ struct control *control;
-+ struct control *slave;
-+ void *state;
-+
-+ pa_assert(volume_control);
-+ pa_assert(original_volume);
-+ pa_assert(remapped_volume);
-+
-+ control = volume_control->userdata;
-+
-+ /* There are four cases that need to be considered:
-+ *
-+ * 1) The master control is propagating the volume to this control. We need
-+ * to propagate the volume downstream.
-+ *
-+ * 2) This control was just assigned a master control and the volume hasn't
-+ * yet been synchronized. In this case the volume that is now being set for
-+ * this control is the master control's volume. We need to propagate the
-+ * volume downstream.
-+ *
-+ * 3) Someone set the volume directly for this control, and this control
-+ * has a master control. We need to propagate the volume upstream, and wait
-+ * for another call that will fall under the case 1.
-+ *
-+ * 4) Someone set the volume directly for this control, and this control
-+ * doesn't have a master control. We need to propagate the volume
-+ * downstream.
-+ *
-+ * As we can see, the action is the same in cases 1, 2 and 4. */
-+
-+ /* Case 3. */
-+ if (control->synced_with_master && !control->master->volume_control->set_volume_in_progress) {
-+ pa_volume_control_set_volume(control->master->volume_control, original_volume, set_volume, set_balance);
-+ return 0;
-+ }
-+
-+ /* Cases 1, 2 and 4. */
-+ PA_HASHMAP_FOREACH(slave, control->slaves, state)
-+ pa_volume_control_set_volume(slave->volume_control, original_volume, set_volume, set_balance);
-
-- return group;
-+ return 0;
- }
-
--static int audio_group_put(struct audio_group *group) {
-- int r;
-+static int mute_control_set_mute_cb(pa_mute_control *mute_control, bool mute) {
-+ struct control *control;
-+ struct control *slave;
-+ void *state;
-+
-+ pa_assert(mute_control);
-+
-+ control = mute_control->userdata;
-+
-+ /* There are four cases that need to be considered:
-+ *
-+ * 1) The master control is propagating the mute to this control. We need
-+ * to propagate the mute downstream.
-+ *
-+ * 2) This control was just assigned a master control and the mute hasn't
-+ * yet been synchronized. In this case the mute that is now being set for
-+ * this control is the master control's mute. We need to propagate the mute
-+ * downstream.
-+ *
-+ * 3) Someone set the mute directly for this control, and this control has
-+ * a master control. We need to propagate the mute upstream, and wait for
-+ * another call that will fall under the case 1.
-+ *
-+ * 4) Someone set the mute directly for this control, and this control
-+ * doesn't have a master control. We need to propagate the mute downstream.
-+ *
-+ * As we can see, the action is the same in cases 1, 2 and 4. */
-+
-+ /* Case 3. */
-+ if (control->synced_with_master && !control->master->mute_control->set_mute_in_progress) {
-+ pa_mute_control_set_mute(control->master->mute_control, mute);
-+ return 0;
-+ }
-+
-+ /* Cases 1, 2 and 4. */
-+ PA_HASHMAP_FOREACH(slave, control->slaves, state)
-+ pa_mute_control_set_mute(slave->mute_control, mute);
-+
-+ return 0;
-+}
-+
-+static int control_new_for_group(struct group *group, enum control_type type, const char *name, bool persistent, struct control **_r) {
-+ struct control *control = NULL;
-+ int r = 0;
-
- pa_assert(group);
-+ pa_assert(name);
-+ pa_assert(_r);
-+
-+ control = pa_xnew0(struct control, 1);
-+ control->userdata = group->userdata;
-+ control->type = type;
-+ control->slaves = pa_hashmap_new(NULL, NULL);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (persistent)
-+ control->volume_control = pa_hashmap_get(control->userdata->volume_api->volume_controls, name);
-+
-+ if (!control->volume_control) {
-+ r = pa_volume_control_new(control->userdata->volume_api, name, persistent, &control->volume_control);
-+ if (r < 0)
-+ goto fail;
-+ }
-
-- r = pa_audio_group_new(group->userdata->api, group->id, group->description, &group->group);
-- if (r < 0)
-- goto fail;
-+ pa_volume_control_set_convertible_to_dB(control->volume_control, true);
-+
-+ if (persistent) {
-+ r = pa_volume_control_acquire_for_audio_group(control->volume_control, group->audio_group,
-+ volume_control_set_volume_cb, control);
-+ if (r < 0)
-+ goto fail;
-+
-+ control->acquired = true;
-+ } else {
-+ control->volume_control->set_volume = volume_control_set_volume_cb;
-+ control->volume_control->userdata = control;
-+ }
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (persistent)
-+ control->mute_control = pa_hashmap_get(control->userdata->volume_api->mute_controls, name);
-+
-+ if (!control->mute_control) {
-+ r = pa_mute_control_new(control->userdata->volume_api, name, persistent, &control->mute_control);
-+ if (r < 0)
-+ goto fail;
-+ }
-+
-+ if (persistent) {
-+ r = pa_mute_control_acquire_for_audio_group(control->mute_control, group->audio_group,
-+ mute_control_set_mute_cb, control);
-+ if (r < 0)
-+ goto fail;
-+
-+ control->acquired = true;
-+ } else {
-+ control->mute_control->set_mute = mute_control_set_mute_cb;
-+ control->mute_control->userdata = control;
-+ }
-+ break;
-+ }
-+
-+ control->own_control = true;
-+
-+ *_r = control;
-+ return 0;
-+
-+fail:
-+ if (control)
-+ control_free(control);
-+
-+ return r;
-+}
-+
-+static struct control *control_new_for_stream(struct userdata *u, enum control_type type, pas_stream *stream) {
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(stream);
-
-- switch (group->volume_control_action) {
-- case CONTROL_ACTION_NONE:
-+ control = pa_xnew0(struct control, 1);
-+ control->userdata = u;
-+ control->type = type;
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ control->volume_control = stream->relative_volume_control;
-+ pa_assert(control->volume_control);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ control->mute_control = stream->mute_control;
-+ pa_assert(control->mute_control);
- break;
-+ }
-
-- case CONTROL_ACTION_CREATE:
-- pa_audio_group_set_have_own_volume_control(group->group, true);
-- pa_audio_group_set_volume_control(group->group, group->group->own_volume_control);
-+ return control;
-+}
-+
-+static void control_put(struct control *control) {
-+ pa_assert(control);
-+
-+ switch (control->type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (control->own_control && !control->volume_control->linked)
-+ pa_volume_control_put(control->volume_control);
- break;
-
-- case CONTROL_ACTION_BIND:
-- pa_audio_group_bind_volume_control(group->group, group->volume_control_target_info);
-+ case CONTROL_TYPE_MUTE:
-+ if (control->own_control && !control->mute_control->linked)
-+ pa_mute_control_put(control->mute_control);
- break;
- }
-+}
-+
-+static void control_unlink(struct control *control) {
-+ pa_assert(control);
-+
-+ if (control->unlinked)
-+ return;
-+
-+ control->unlinked = true;
-+
-+ if (control->slaves) {
-+ struct control *slave;
-+
-+ while ((slave = pa_hashmap_first(control->slaves)))
-+ control_set_master(slave, NULL);
-+ }
-+
-+ control_set_master(control, NULL);
-
-- switch (group->mute_control_action) {
-- case CONTROL_ACTION_NONE:
-+ switch (control->type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (control->own_control && control->volume_control && !control->volume_control->persistent)
-+ pa_volume_control_unlink(control->volume_control);
- break;
-
-- case CONTROL_ACTION_CREATE:
-- pa_audio_group_set_have_own_mute_control(group->group, true);
-- pa_audio_group_set_mute_control(group->group, group->group->own_mute_control);
-+ case CONTROL_TYPE_MUTE:
-+ if (control->own_control && control->mute_control && !control->mute_control->persistent)
-+ pa_mute_control_unlink(control->mute_control);
- break;
-+ }
-+}
-+
-+static void control_free(struct control *control) {
-+ pa_assert(control);
-+
-+ if (!control->unlinked)
-+ control_unlink(control);
-+
-+ if (control->slaves) {
-+ pa_assert(pa_hashmap_isempty(control->slaves));
-+ pa_hashmap_free(control->slaves);
-+ }
-+
-+ switch (control->type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (control->acquired)
-+ pa_volume_control_release(control->volume_control);
-+
-+ if (control->own_control && control->volume_control && !control->volume_control->persistent)
-+ pa_volume_control_free(control->volume_control);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (control->acquired)
-+ pa_mute_control_release(control->mute_control);
-
-- case CONTROL_ACTION_BIND:
-- pa_audio_group_bind_mute_control(group->group, group->mute_control_target_info);
-+ if (control->own_control && control->mute_control && !control->mute_control->persistent)
-+ pa_mute_control_free(control->mute_control);
- break;
- }
-
-- pa_audio_group_put(group->group);
-+ pa_xfree(control);
-+}
-+
-+static void control_set_master(struct control *control, struct control *master) {
-+ struct control *old_master;
-+
-+ pa_assert(control);
-+ pa_assert(!master || master->type == control->type);
-+
-+ old_master = control->master;
-+
-+ if (master == old_master)
-+ return;
-+
-+ if (old_master) {
-+ control_remove_slave(old_master, control);
-+ control->synced_with_master = false;
-+ }
-+
-+ control->master = master;
-+
-+ if (master) {
-+ control_add_slave(master, control);
-+
-+ switch (control->type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_volume_control_set_volume(control->volume_control, &master->volume_control->volume, true, true);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ pa_mute_control_set_mute(control->mute_control, master->mute_control->mute);
-+ break;
-+ }
-+
-+ control->synced_with_master = true;
-+ }
-+}
-+
-+static void control_add_slave(struct control *control, struct control *slave) {
-+ pa_assert(control);
-+ pa_assert(slave);
-+
-+ pa_assert_se(pa_hashmap_put(control->slaves, slave, slave) >= 0);
-+}
-+
-+static void control_remove_slave(struct control *control, struct control *slave) {
-+ pa_assert(control);
-+ pa_assert(slave);
-+
-+ pa_assert_se(pa_hashmap_remove(control->slaves, slave));
-+}
-+
-+static int group_new(struct userdata *u, const char *name, struct group **_r) {
-+ struct group *group = NULL;
-+ int r;
-+ struct group *slave;
-+ struct stream_rule *rule;
-+ void *state;
-+
-+ pa_assert(u);
-+ pa_assert(name);
-+ pa_assert(_r);
-+
-+ group = pa_xnew0(struct group, 1);
-+ group->userdata = u;
-+
-+ r = pa_audio_group_new(u->volume_api, name, &group->audio_group);
-+ if (r < 0)
-+ goto fail;
-+
-+ group->volume_slaves = pa_hashmap_new(NULL, NULL);
-+ group->mute_slaves = pa_hashmap_new(NULL, NULL);
-+ group->volume_stream_rules = pa_hashmap_new(NULL, NULL);
-+ group->mute_stream_rules = pa_hashmap_new(NULL, NULL);
-+
-+ PA_HASHMAP_FOREACH(slave, u->groups, state) {
-+ if (slave == group)
-+ continue;
-+
-+ if (pa_safe_streq(slave->volume_master_name, group->audio_group->name))
-+ group_set_master(slave, CONTROL_TYPE_VOLUME, group);
-+
-+ if (pa_safe_streq(slave->mute_master_name, group->audio_group->name))
-+ group_set_master(slave, CONTROL_TYPE_MUTE, group);
-+ }
-+
-+ PA_HASHMAP_FOREACH(rule, u->stream_rules, state) {
-+ if (pa_safe_streq(rule->audio_group_name_for_volume, group->audio_group->name))
-+ stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, group);
-
-+ if (pa_safe_streq(rule->audio_group_name_for_mute, group->audio_group->name))
-+ stream_rule_set_group(rule, CONTROL_TYPE_MUTE, group);
-+ }
-+
-+ *_r = group;
- return 0;
-
- fail:
-- audio_group_unlink(group);
-+ if (group)
-+ group_free(group);
-
- return r;
- }
-
--static void audio_group_unlink(struct audio_group *group) {
-+static void group_put(struct group *group) {
-+ pa_assert(group);
-+
-+ pa_audio_group_put(group->audio_group);
-+
-+ if (group->volume_control)
-+ control_put(group->volume_control);
-+
-+ if (group->mute_control)
-+ control_put(group->mute_control);
-+}
-+
-+static void group_unlink(struct group *group) {
-+ struct stream_rule *rule;
-+ struct group *slave;
-+ void *state;
-+
- pa_assert(group);
-
- if (group->unlinked)
-@@ -219,192 +596,406 @@ static void audio_group_unlink(struct audio_group *group) {
-
- group->unlinked = true;
-
-- if (group->group) {
-- pa_audio_group_free(group->group);
-- group->group = NULL;
-- }
-+ PA_HASHMAP_FOREACH(rule, group->volume_stream_rules, state)
-+ stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, NULL);
-+
-+ PA_HASHMAP_FOREACH(rule, group->mute_stream_rules, state)
-+ stream_rule_set_group(rule, CONTROL_TYPE_MUTE, NULL);
-+
-+ PA_HASHMAP_FOREACH(slave, group->volume_slaves, state)
-+ group_set_master(slave, CONTROL_TYPE_VOLUME, NULL);
-+
-+ PA_HASHMAP_FOREACH(slave, group->mute_slaves, state)
-+ group_set_master(slave, CONTROL_TYPE_MUTE, NULL);
-+
-+ group_disable_control(group, CONTROL_TYPE_MUTE);
-+ group_disable_control(group, CONTROL_TYPE_VOLUME);
-+
-+ if (group->audio_group)
-+ pa_audio_group_unlink(group->audio_group);
- }
-
--static void audio_group_free(struct audio_group *group) {
-+static void group_free(struct group *group) {
- pa_assert(group);
-
-- if (!group->unlinked)
-- audio_group_unlink(group);
-+ group_unlink(group);
-+
-+ if (group->mute_stream_rules) {
-+ pa_assert(pa_hashmap_isempty(group->mute_stream_rules));
-+ pa_hashmap_free(group->mute_stream_rules);
-+ }
-+
-+ if (group->volume_stream_rules) {
-+ pa_assert(pa_hashmap_isempty(group->volume_stream_rules));
-+ pa_hashmap_free(group->volume_stream_rules);
-+ }
-+
-+ if (group->mute_slaves) {
-+ pa_assert(pa_hashmap_isempty(group->mute_slaves));
-+ pa_hashmap_free(group->mute_slaves);
-+ }
-+
-+ if (group->volume_slaves) {
-+ pa_assert(pa_hashmap_isempty(group->volume_slaves));
-+ pa_hashmap_free(group->volume_slaves);
-+ }
-
-- if (group->mute_control_target_info)
-- pa_binding_target_info_free(group->mute_control_target_info);
-+ pa_assert(!group->mute_master_name);
-+ pa_assert(!group->volume_master_name);
-+ pa_assert(!group->mute_master);
-+ pa_assert(!group->volume_master);
-+ pa_assert(!group->mute_control);
-+ pa_assert(!group->volume_control);
-
-- if (group->volume_control_target_info)
-- pa_binding_target_info_free(group->volume_control_target_info);
-+ if (group->audio_group)
-+ pa_audio_group_free(group->audio_group);
-
-- pa_xfree(group->description);
-- pa_xfree(group->id);
- pa_xfree(group);
- }
-
--static void audio_group_set_description(struct audio_group *group, const char *description) {
-+static void group_set_own_control_name(struct group *group, enum control_type type, const char *name) {
-+ struct group *slave;
-+ void *state;
-+
- pa_assert(group);
-- pa_assert(description);
-
-- pa_xfree(group->description);
-- group->description = pa_xstrdup(description);
-+ if (name)
-+ group_set_master_name(group, type, NULL);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (pa_safe_streq(name, group->own_volume_control_name))
-+ return;
-+
-+ if (group->volume_control) {
-+ control_free(group->volume_control);
-+ group->volume_control = NULL;
-+ }
-+
-+ pa_xfree(group->own_volume_control_name);
-+ group->own_volume_control_name = pa_xstrdup(name);
-+
-+ if (name) {
-+ control_new_for_group(group, CONTROL_TYPE_VOLUME, name, true, &group->volume_control);
-+
-+ PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
-+ if (slave->volume_control)
-+ control_set_master(slave->volume_control, group->volume_control);
-+ }
-+ }
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (pa_safe_streq(name, group->own_mute_control_name))
-+ return;
-+
-+ if (group->mute_control) {
-+ control_free(group->mute_control);
-+ group->mute_control = NULL;
-+ }
-+
-+ pa_xfree(group->own_mute_control_name);
-+ group->own_mute_control_name = pa_xstrdup(name);
-+
-+ if (name) {
-+ control_new_for_group(group, CONTROL_TYPE_MUTE, name, true, &group->mute_control);
-+
-+ PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
-+ if (slave->mute_control)
-+ control_set_master(slave->mute_control, group->mute_control);
-+ }
-+ }
-+ break;
-+ }
- }
-
--static void audio_group_set_volume_control_action(struct audio_group *group, enum control_action action,
-- pa_binding_target_info *target_info) {
-+static void group_set_master(struct group *group, enum control_type type, struct group *master) {
-+ struct group *old_master;
-+ struct control *master_control = NULL;
-+
- pa_assert(group);
-- pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
-+ pa_assert(master != group);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ old_master = group->volume_master;
-+
-+ if (master == old_master)
-+ return;
-+
-+ if (old_master)
-+ group_remove_slave(old_master, CONTROL_TYPE_VOLUME, group);
-+
-+ group->volume_master = master;
-+
-+ if (master)
-+ group_add_slave(master, CONTROL_TYPE_VOLUME, group);
-+
-+ if (group->volume_control) {
-+ if (master)
-+ master_control = master->volume_control;
-
-- group->volume_control_action = action;
-+ control_set_master(group->volume_control, master_control);
-+ }
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ old_master = group->mute_master;
-+
-+ if (master == old_master)
-+ return;
-
-- if (group->volume_control_target_info)
-- pa_binding_target_info_free(group->volume_control_target_info);
-+ if (old_master)
-+ group_remove_slave(old_master, CONTROL_TYPE_MUTE, group);
-
-- if (action == CONTROL_ACTION_BIND)
-- group->volume_control_target_info = pa_binding_target_info_copy(target_info);
-- else
-- group->volume_control_target_info = NULL;
-+ group->mute_master = master;
-+
-+ if (master)
-+ group_add_slave(master, CONTROL_TYPE_MUTE, group);
-+
-+ if (group->mute_control) {
-+ if (master)
-+ master_control = master->volume_control;
-+
-+ control_set_master(group->volume_control, master_control);
-+ }
-+ break;
-+ }
- }
-
--static void audio_group_set_mute_control_action(struct audio_group *group, enum control_action action,
-- pa_binding_target_info *target_info) {
-+static int group_set_master_name(struct group *group, enum control_type type, const char *name) {
-+ struct group *slave;
-+ void *state;
-+ struct group *master = NULL;
-+
- pa_assert(group);
-- pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
-
-- group->mute_control_action = action;
-+ if (pa_safe_streq(name, group->audio_group->name)) {
-+ pa_log("Can't bind audio group control to itself.");
-+ return -PA_ERR_INVALID;
-+ }
-
-- if (group->mute_control_target_info)
-- pa_binding_target_info_free(group->mute_control_target_info);
-+ if (name)
-+ group_set_own_control_name(group, type, NULL);
-
-- if (action == CONTROL_ACTION_BIND)
-- group->mute_control_target_info = pa_binding_target_info_copy(target_info);
-- else
-- group->mute_control_target_info = NULL;
--}
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (pa_safe_streq(name, group->volume_master_name))
-+ return 0;
-
--static struct stream *stream_new(struct userdata *u, const char *name) {
-- struct stream *stream;
-+ pa_xfree(group->volume_master_name);
-+ group->volume_master_name = pa_xstrdup(name);
-
-- pa_assert(u);
-- pa_assert(name);
-+ if (name && !group->volume_control) {
-+ control_new_for_group(group, CONTROL_TYPE_VOLUME, "audio-group-volume-control", false, &group->volume_control);
-
-- stream = pa_xnew0(struct stream, 1);
-- stream->userdata = u;
-- stream->id = pa_xstrdup(name);
-- stream->direction = match_direction_unknown;
-+ PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
-+ if (slave->volume_control)
-+ control_set_master(slave->volume_control, group->volume_control);
-+ }
-
-- return stream;
--}
-+ } else if (!name && group->volume_control) {
-+ control_free(group->volume_control);
-+ group->volume_control = NULL;
-+ }
-+ break;
-
--static void stream_put(struct stream *stream) {
-- pa_assert(stream);
-+ case CONTROL_TYPE_MUTE:
-+ if (pa_safe_streq(name, group->mute_master_name))
-+ return 0;
-
-- if (stream->audio_group_name_for_volume) {
-- stream->audio_group_for_volume = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_volume);
-- if (stream->audio_group_for_volume)
-- stream->volume_control_target_info =
-- pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_volume->name,
-- PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL);
-- else
-- pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
-+ pa_xfree(group->mute_master_name);
-+ group->mute_master_name = pa_xstrdup(name);
-+
-+ if (name && !group->mute_control) {
-+ control_new_for_group(group, CONTROL_TYPE_MUTE, "audio-group-mute-control", false, &group->mute_control);
-+
-+ PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
-+ if (slave->mute_control)
-+ control_set_master(slave->mute_control, group->mute_control);
-+ }
-+
-+ } else if (!name && group->mute_control) {
-+ control_free(group->mute_control);
-+ group->mute_control = NULL;
-+ }
-+ break;
- }
-
-- if (stream->audio_group_name_for_mute) {
-- stream->audio_group_for_mute = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_mute);
-- if (stream->audio_group_for_mute)
-- stream->mute_control_target_info =
-- pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_mute->name,
-- PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL);
-- else
-- pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
-+ if (name)
-+ master = pa_hashmap_get(group->userdata->groups, name);
-+
-+ group_set_master(group, type, master);
-+
-+ return 0;
-+}
-+
-+static void group_disable_control(struct group *group, enum control_type type) {
-+ pa_assert(group);
-+
-+ group_set_own_control_name(group, type, NULL);
-+ group_set_master_name(group, type, NULL);
-+}
-+
-+static void group_add_slave(struct group *group, enum control_type type, struct group *slave) {
-+ pa_assert(group);
-+ pa_assert(slave);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_assert_se(pa_hashmap_put(group->volume_slaves, slave, slave) >= 0);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ pa_assert_se(pa_hashmap_put(group->mute_slaves, slave, slave) >= 0);
-+ break;
- }
- }
-
--static void stream_unlink(struct stream *stream) {
-- pa_assert(stream);
-+static void group_remove_slave(struct group *group, enum control_type type, struct group *slave) {
-+ pa_assert(group);
-+ pa_assert(slave);
-
-- if (stream->unlinked)
-- return;
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_assert_se(pa_hashmap_remove(group->volume_slaves, slave));
-+ break;
-
-- if (stream->mute_control_target_info) {
-- pa_binding_target_info_free(stream->mute_control_target_info);
-- stream->mute_control_target_info = NULL;
-+ case CONTROL_TYPE_MUTE:
-+ pa_assert_se(pa_hashmap_remove(group->mute_slaves, slave));
- }
-+}
-
-- if (stream->volume_control_target_info) {
-- pa_binding_target_info_free(stream->volume_control_target_info);
-- stream->volume_control_target_info = NULL;
-+static void group_add_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
-+ pa_assert(group);
-+ pa_assert(rule);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_assert_se(pa_hashmap_put(group->volume_stream_rules, rule, rule) >= 0);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ pa_assert_se(pa_hashmap_put(group->mute_stream_rules, rule, rule) >= 0);
-+ break;
- }
-+}
-+
-+static void group_remove_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
-+ pa_assert(group);
-+ pa_assert(rule);
-
-- stream->unlinked = true;
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_assert_se(pa_hashmap_remove(group->volume_stream_rules, rule));
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ pa_assert_se(pa_hashmap_remove(group->mute_stream_rules, rule));
-+ break;
-+ }
- }
-
--static void stream_free(struct stream *stream) {
-- pa_assert(stream);
-+static struct stream_rule *stream_rule_new(struct userdata *u, const char *name) {
-+ struct stream_rule *rule;
-
-- if (!stream->unlinked)
-- stream_unlink(stream);
-+ pa_assert(u);
-+ pa_assert(name);
-
-- if (stream->rule)
-- delete_expression(stream->rule);
-+ rule = pa_xnew0(struct stream_rule, 1);
-+ rule->userdata = u;
-+ rule->name = pa_xstrdup(name);
-+ rule->direction = match_direction_unknown;
-+ rule->match_expression = expression_new();
-
-- pa_xfree(stream->audio_group_name_for_mute);
-- pa_xfree(stream->audio_group_name_for_volume);
-- pa_xfree(stream->id);
-- pa_xfree(stream);
-+ return rule;
- }
-
--static void stream_set_audio_group_name_for_volume(struct stream *stream, const char *name) {
-- pa_assert(stream);
-+static void stream_rule_free(struct stream_rule *rule) {
-+ pa_assert(rule);
-
-- pa_xfree(stream->audio_group_name_for_volume);
-- stream->audio_group_name_for_volume = pa_xstrdup(name);
-+ if (rule->match_expression)
-+ expression_free(rule->match_expression);
-+
-+ stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, NULL);
-+ stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, NULL);
-+ pa_xfree(rule->name);
-+ pa_xfree(rule);
- }
-
--static void stream_set_audio_group_name_for_mute(struct stream *stream, const char *name) {
-- pa_assert(stream);
-+static void stream_rule_set_match_expression(struct stream_rule *rule, struct expression *expression) {
-+ pa_assert(rule);
-+ pa_assert(expression);
-
-- pa_xfree(stream->audio_group_name_for_mute);
-- stream->audio_group_name_for_mute = pa_xstrdup(name);
-+ if (rule->match_expression)
-+ expression_free(rule->match_expression);
-+
-+ rule->match_expression = expression;
- }
-
--/* stream classification */
-+static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group) {
-+ pa_assert(rule);
-
--static bool match_predicate(struct literal *l, pas_stream *d) {
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (group == rule->group_for_volume)
-+ return;
-
-- if (l->stream_direction != match_direction_unknown) {
-- /* check the stream direction; _sink inputs_ are always _outputs_ */
-+ if (rule->group_for_volume)
-+ group_remove_stream_rule(rule->group_for_volume, CONTROL_TYPE_VOLUME, rule);
-
-- if ((d->direction == PA_DIRECTION_OUTPUT && l->stream_direction == match_direction_output) ||
-- ((d->direction == PA_DIRECTION_INPUT && l->stream_direction == match_direction_input))) {
-- return true;
-- }
-+ rule->group_for_volume = group;
-+
-+ if (group)
-+ group_add_stream_rule(group, CONTROL_TYPE_VOLUME, rule);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (group == rule->group_for_mute)
-+ return;
-+
-+ if (rule->group_for_mute)
-+ group_remove_stream_rule(rule->group_for_mute, CONTROL_TYPE_MUTE, rule);
-+
-+ rule->group_for_mute = group;
-+
-+ if (group)
-+ group_add_stream_rule(group, CONTROL_TYPE_MUTE, rule);
-+ break;
- }
-- else if (l->property_name && l->property_value) {
-- /* check the property from the property list */
-+}
-
-- if (pa_proplist_contains(d->proplist, l->property_name)) {
-- const char *prop = pa_proplist_gets(d->proplist, l->property_name);
-+static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name) {
-+ struct group *group = NULL;
-
-- if (prop && strcmp(prop, l->property_value) == 0) {
-- return true;
-- }
-- }
-+ pa_assert(rule);
-+
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ pa_xfree(rule->audio_group_name_for_volume);
-+ rule->audio_group_name_for_volume = pa_xstrdup(name);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ pa_xfree(rule->audio_group_name_for_mute);
-+ rule->audio_group_name_for_mute = pa_xstrdup(name);
-+ break;
- }
-
-- /* no match */
-- return false;
--}
-+ if (name)
-+ group = pa_hashmap_get(rule->userdata->groups, name);
-
--static bool match_rule(struct expression *e, pas_stream *d) {
-+ stream_rule_set_group(rule, type, group);
-+}
-
-+static bool stream_rule_match(struct stream_rule *rule, pas_stream *stream) {
- struct conjunction *c;
-
-- PA_LLIST_FOREACH(c, e->conjunctions) {
-+ PA_LLIST_FOREACH(c, rule->match_expression->conjunctions) {
- struct literal *l;
- bool and_success = true;
- PA_LLIST_FOREACH(l, c->literals) {
-- if (!match_predicate(l, d)) {
-+ if (!literal_match(l, stream)) {
- /* at least one fail for conjunction */
- and_success = false;
- break;
-@@ -421,56 +1012,246 @@ static bool match_rule(struct expression *e, pas_stream *d) {
- return false;
- }
-
--static void classify_stream(struct userdata *u, pas_stream *new_data, bool mute) {
-- /* do the classification here */
-+/* stream classification */
-
-- struct stream *stream = NULL;
-- unsigned idx;
-+static bool literal_match(struct literal *literal, pas_stream *stream) {
-+
-+ if (literal->stream_direction != match_direction_unknown) {
-+ /* check the stream direction; _sink inputs_ are always _outputs_ */
-
-- /* go through the stream match definitions in given order */
-+ if ((stream->direction == PA_DIRECTION_OUTPUT && literal->stream_direction == match_direction_output) ||
-+ (stream->direction == PA_DIRECTION_INPUT && literal->stream_direction == match_direction_input)) {
-+ return literal->negation ? false : true;
-+ }
-+ }
-+ else if (literal->property_name && literal->property_value) {
-+ /* check the property from the property list */
-
-- PA_DYNARRAY_FOREACH(stream, u->streams, idx) {
-- if (stream->rule && match_rule(stream->rule, new_data)) {
-- pa_log_info("stream %s (%s) match with rule %s:", new_data->name, new_data->description, stream->id);
-- print_expression(stream->rule);
-+ if (pa_proplist_contains(stream->proplist, literal->property_name)) {
-+ const char *prop = pa_proplist_gets(stream->proplist, literal->property_name);
-
-- if (mute) {
-- if (new_data->use_default_mute_control && stream->audio_group_for_mute)
-- pas_stream_bind_mute_control(new_data, stream->mute_control_target_info);
-- } else {
-- if (new_data->use_default_volume_control && stream->audio_group_for_volume)
-- pas_stream_bind_volume_control(new_data, stream->volume_control_target_info);
-- }
-+ if (prop && strcmp(prop, literal->property_value) == 0)
-+ return literal->negation ? false : true;
-+ }
-+ }
-+
-+ /* no match */
-+ return literal->negation ? true : false;
-+}
-+
-+static pa_hook_result_t stream_put_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pas_stream *stream = call_data;
-+ struct stream_rule *rule;
-+ unsigned idx;
-
-- return;
-+ pa_assert(u);
-+ pa_assert(stream);
-+
-+ PA_DYNARRAY_FOREACH(rule, u->stream_rules_list, idx) {
-+ if (stream_rule_match(rule, stream)) {
-+ pa_hashmap_put(u->rules_by_stream, stream, rule);
-+ break;
- }
- }
-
-- /* no matches, don't touch the volumes */
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t stream_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pas_stream *stream = call_data;
-+
-+ pa_assert(u);
-+ pa_assert(stream);
-+
-+ pa_hashmap_remove(u->rules_by_stream, stream);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t volume_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_volume_control *volume_control = call_data;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(volume_control);
-+
-+ if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
-+ return PA_HOOK_OK;
-+
-+ control = control_new_for_stream(u, CONTROL_TYPE_VOLUME, volume_control->owner_stream);
-+ control_put(control);
-+ pa_assert_se(pa_hashmap_put(u->stream_volume_controls, volume_control->owner_stream, control) >= 0);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t mute_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_mute_control *mute_control = call_data;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(mute_control);
-+
-+ if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
-+ return PA_HOOK_OK;
-+
-+ control = control_new_for_stream(u, CONTROL_TYPE_MUTE, mute_control->owner_stream);
-+ control_put(control);
-+ pa_assert_se(pa_hashmap_put(u->stream_mute_controls, mute_control->owner_stream, control) >= 0);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t volume_control_set_initial_volume_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_volume_control *volume_control = call_data;
-+ struct stream_rule *rule;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(volume_control);
-+
-+ if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
-+ return PA_HOOK_OK;
-+
-+ rule = pa_hashmap_get(u->rules_by_stream, volume_control->owner_stream);
-+ if (!rule)
-+ return PA_HOOK_OK;
-+
-+ if (!rule->group_for_volume)
-+ return PA_HOOK_OK;
-+
-+ if (!rule->group_for_volume->volume_control)
-+ return PA_HOOK_OK;
-+
-+ control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
-+ pa_assert(control);
-+ pa_assert(control->volume_control == volume_control);
-+
-+ /* This will set the volume for volume_control. */
-+ control_set_master(control, rule->group_for_volume->volume_control);
-+
-+ return PA_HOOK_STOP;
-+}
-+
-+static pa_hook_result_t mute_control_set_initial_mute_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_mute_control *mute_control = call_data;
-+ struct stream_rule *rule;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(mute_control);
-+
-+ if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
-+ return PA_HOOK_OK;
-+
-+ rule = pa_hashmap_get(u->rules_by_stream, mute_control->owner_stream);
-+ if (!rule)
-+ return PA_HOOK_OK;
-+
-+ if (!rule->group_for_mute)
-+ return PA_HOOK_OK;
-+
-+ if (!rule->group_for_mute->mute_control)
-+ return PA_HOOK_OK;
-+
-+ control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
-+ pa_assert(control);
-+ pa_assert(control->mute_control == mute_control);
-+
-+ /* This will set the mute for mute_control. */
-+ control_set_master(control, rule->group_for_mute->mute_control);
-+
-+ return PA_HOOK_STOP;
-+}
-+
-+static pa_hook_result_t volume_control_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_volume_control *volume_control = call_data;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(volume_control);
-+
-+ if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
-+ return PA_HOOK_OK;
-+
-+ control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
-+ if (!control)
-+ return PA_HOOK_OK;
-+
-+ if (!control->master)
-+ return PA_HOOK_OK;
-+
-+ pa_volume_control_set_volume(control->master->volume_control, &volume_control->volume, true, true);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t mute_control_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_mute_control *mute_control = call_data;
-+ struct control *control;
-+
-+ pa_assert(u);
-+ pa_assert(mute_control);
-+
-+ if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
-+ return PA_HOOK_OK;
-+
-+ control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
-+ if (!control)
-+ return PA_HOOK_OK;
-+
-+ if (!control->master)
-+ return PA_HOOK_OK;
-+
-+ pa_mute_control_set_mute(control->master->mute_control, mute_control->mute);
-+
-+ return PA_HOOK_OK;
- }
-
--static pa_hook_result_t set_volume_control_cb(
-- void *hook_data,
-- pas_stream *new_data,
-- struct userdata *u) {
-+static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_volume_control *volume_control = call_data;
-+ struct control *control;
-
-- pa_assert(new_data);
- pa_assert(u);
-+ pa_assert(volume_control);
-+
-+ if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
-+ return PA_HOOK_OK;
-
-- classify_stream(u, new_data, false);
-+ control = pa_hashmap_remove(u->stream_volume_controls, volume_control->owner_stream);
-+ if (!control)
-+ return PA_HOOK_OK;
-+
-+ control_free(control);
-
- return PA_HOOK_OK;
- }
-
--static pa_hook_result_t set_mute_control_cb(
-- void *hook_data,
-- pas_stream *new_data,
-- struct userdata *u) {
-+static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_mute_control *mute_control = call_data;
-+ struct control *control;
-
-- pa_assert(new_data);
- pa_assert(u);
-+ pa_assert(mute_control);
-+
-+ if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
-+ return PA_HOOK_OK;
-
-- classify_stream(u, new_data, true);
-+ control = pa_hashmap_remove(u->stream_mute_controls, mute_control->owner_stream);
-+ if (!control)
-+ return PA_HOOK_OK;
-+
-+ control_free(control);
-
- return PA_HOOK_OK;
- }
-@@ -520,6 +1301,7 @@ static pa_hook_result_t set_mute_control_cb(
- (property application.process.binary=paplay OR (direction input OR direction output))
- */
-
-+#if 0
- static void print_literal(struct literal *l) {
- if (l->stream_direction != match_direction_unknown) {
- pa_log_info(" %sstream direction %s",
-@@ -549,6 +1331,7 @@ static void print_expression(struct expression *e) {
- print_conjunction(c);
- }
- }
-+#endif
-
- static void delete_literal(struct literal *l) {
-
-@@ -573,14 +1356,23 @@ static void delete_conjunction(struct conjunction *c) {
- pa_xfree(c);
- }
-
--static void delete_expression(struct expression *e) {
-+static struct expression *expression_new(void) {
-+ struct expression *expression;
-+
-+ expression = pa_xnew0(struct expression, 1);
-+
-+ return expression;
-+}
-+
-+static void expression_free(struct expression *expression) {
- struct conjunction *c;
-
-- PA_LLIST_FOREACH(c, e->conjunctions) {
-+ pa_assert(expression);
-+
-+ PA_LLIST_FOREACH(c, expression->conjunctions)
- delete_conjunction(c);
-- }
-
-- pa_xfree(e);
-+ pa_xfree(expression);
- }
-
- enum logic_operator {
-@@ -917,26 +1709,21 @@ static bool gather_expression(struct expression *e, struct expression_token *et)
- return true;
- }
-
--static struct expression *parse_rule(const char *rule_string) {
-- char *k, *l;
-+static int expression_from_string(const char *str, struct expression **_r) {
-+ const char *k;
-+ char *l;
- struct expression *e = NULL;
-- int len;
- char *buf = NULL;
- struct expression_token *et = NULL;
-
-- if (!rule_string)
-- goto error;
--
-- len = strlen(rule_string);
--
-- buf = (char *) pa_xmalloc0(len);
-+ pa_assert(str);
-+ pa_assert(_r);
-
-- if (!buf)
-- goto error;
-+ buf = pa_xmalloc0(strlen(str) + 1);
-
- /* remove whitespace */
-
-- k = (char *) rule_string;
-+ k = str;
- l = buf;
-
- while (*k) {
-@@ -958,9 +1745,6 @@ static struct expression *parse_rule(const char *rule_string) {
-
- e = pa_xnew0(struct expression, 1);
-
-- if (!e)
-- goto error;
--
- PA_LLIST_HEAD_INIT(struct conjunction, e->conjunctions);
-
- /* gather expressions to actual match format */
-@@ -978,30 +1762,15 @@ static struct expression *parse_rule(const char *rule_string) {
- delete_expression_token(et);
- pa_xfree(buf);
-
-- return e;
-+ *_r = e;
-+ return 0;
-
- error:
- delete_expression_token(et);
- pa_xfree(buf);
-- pa_xfree(e);
-- return NULL;
--}
--
--static int parse_audio_groups(pa_config_parser_state *state) {
-- struct userdata *u;
-- char *name;
-- const char *split_state = NULL;
--
-- pa_assert(state);
--
-- u = state->userdata;
--
-- pa_hashmap_remove_all(u->audio_group_names);
-+ expression_free(e);
-
-- while ((name = pa_split_spaces(state->rvalue, &split_state)))
-- pa_hashmap_put(u->audio_group_names, name, name);
--
-- return 0;
-+ return -PA_ERR_INVALID;
- }
-
- static int parse_streams(pa_config_parser_state *state) {
-@@ -1013,15 +1782,13 @@ static int parse_streams(pa_config_parser_state *state) {
-
- u = state->userdata;
-
-- pa_dynarray_remove_all(u->stream_names);
--
- while ((name = pa_split_spaces(state->rvalue, &split_state))) {
- const char *name2;
- unsigned idx;
- bool duplicate = false;
-
-- /* Avoid adding duplicates in u->stream_names. */
-- PA_DYNARRAY_FOREACH(name2, u->stream_names, idx) {
-+ /* Avoid adding duplicates in u->stream_rule_names. */
-+ PA_DYNARRAY_FOREACH(name2, u->stream_rule_names, idx) {
- if (pa_streq(name, name2)) {
- duplicate = true;
- break;
-@@ -1033,230 +1800,221 @@ static int parse_streams(pa_config_parser_state *state) {
- continue;
- }
-
-- pa_dynarray_append(u->stream_names, name);
-+ pa_dynarray_append(u->stream_rule_names, name);
- }
-
- return 0;
- }
-
--static int parse_common(pa_config_parser_state *state) {
--#define AUDIOGROUP_START "AudioGroup "
--#define STREAM_START "Stream "
--#define BIND_KEYWORD "bind:"
--#define NONE_KEYWORD "none"
--
-- char *section;
-- struct userdata *u = (struct userdata *) state->userdata;
-- int r;
-- pa_binding_target_info *target_info;
--
-+static int parse_group_control(pa_config_parser_state *state, struct group *group, enum control_type type) {
- pa_assert(state);
-+ pa_assert(group);
-
-- section = state->section;
-- if (!section)
-- goto error;
-+ if (pa_streq(state->rvalue, NONE_KEYWORD))
-+ group_disable_control(group, type);
-+
-+ else if (pa_startswith(state->rvalue, CREATE_PREFIX))
-+ group_set_own_control_name(group, type, state->rvalue + strlen(CREATE_PREFIX));
-
-- if (strncmp(section, AUDIOGROUP_START, strlen(AUDIOGROUP_START)) == 0) {
-- char *ag_name = section + strlen(AUDIOGROUP_START);
-- struct audio_group *ag = (struct audio_group *) pa_hashmap_get(u->unused_audio_groups, ag_name);
-+ else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
-+ if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX)) {
-+ int r;
-
-- if (!ag) {
-- /* first item for this audio group section, so create the struct */
-- ag = audio_group_new(u, ag_name);
-- pa_hashmap_put(u->unused_audio_groups, ag->id, ag);
-+ r = group_set_master_name(group, type, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to set binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
-+ return r;
-+ }
-+ } else {
-+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
-+ return -PA_ERR_INVALID;
- }
-+ } else {
-+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-+ return -PA_ERR_INVALID;
-+ }
-
-- if (strcmp(state->lvalue, "description") == 0)
-- audio_group_set_description(ag, state->rvalue);
-+ return 0;
-+}
-+
-+static int parse_common(pa_config_parser_state *state) {
-+ char *section;
-+ struct userdata *u = state->userdata;
-+ const char *name;
-+ int r;
-
-- else if (strcmp(state->lvalue, "volume-control") == 0) {
-- if (pa_streq(state->rvalue, "create"))
-- audio_group_set_volume_control_action(ag, CONTROL_ACTION_CREATE, NULL);
-+ pa_assert(state);
-
-- else if (pa_streq(state->rvalue, NONE_KEYWORD))
-- audio_group_set_volume_control_action(ag, CONTROL_ACTION_NONE, NULL);
-+ section = state->section;
-+ if (!section) {
-+ pa_log("[%s:%u] Lvalue \"%s\" not expected in the General section.", state->filename, state->lineno, state->lvalue);
-+ return -PA_ERR_INVALID;
-+ }
-
-- else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
-- r = pa_binding_target_info_new_from_string(state->rvalue, "volume_control", &target_info);
-- if (r < 0) {
-- pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-- goto error;
-- }
-+ if (pa_startswith(section, AUDIOGROUP_START)) {
-+ struct group *group;
-
-- audio_group_set_volume_control_action(ag, CONTROL_ACTION_BIND, target_info);
-- pa_binding_target_info_free(target_info);
-+ name = section + strlen(AUDIOGROUP_START);
-
-- } else {
-- pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-- goto error;
-+ group = pa_hashmap_get(u->groups, name);
-+ if (!group) {
-+ r = group_new(u, name, &group);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to create an audio group with name \"%s\".", state->filename, state->lineno, name);
-+ return r;
- }
-+
-+ pa_hashmap_put(u->groups, (void *) group->audio_group->name, group);
- }
-- else if (strcmp(state->lvalue, "mute-control") == 0) {
-- if (pa_streq(state->rvalue, "create"))
-- audio_group_set_mute_control_action(ag, CONTROL_ACTION_CREATE, NULL);
--
-- else if (pa_streq(state->rvalue, NONE_KEYWORD))
-- audio_group_set_mute_control_action(ag, CONTROL_ACTION_NONE, NULL);
--
-- else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
-- r = pa_binding_target_info_new_from_string(state->rvalue, "mute_control", &target_info);
-- if (r < 0) {
-- pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-- goto error;
-- }
-
-- audio_group_set_mute_control_action(ag, CONTROL_ACTION_BIND, target_info);
-- pa_binding_target_info_free(target_info);
-+ if (pa_streq(state->lvalue, "description"))
-+ pa_audio_group_set_description(group->audio_group, state->rvalue);
-
-- } else {
-- pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-- goto error;
-- }
-+ else if (pa_streq(state->lvalue, "volume-control"))
-+ return parse_group_control(state, group, CONTROL_TYPE_VOLUME);
-+
-+ else if (pa_streq(state->lvalue, "mute-control"))
-+ return parse_group_control(state, group, CONTROL_TYPE_MUTE);
-+
-+ else {
-+ pa_log("[%s:%u] Lvalue \"%s\" not expected in the AudioGroup section.", state->filename, state->lineno, state->lvalue);
-+ return -PA_ERR_INVALID;
- }
- }
-- else if (strncmp(section, STREAM_START, strlen(STREAM_START)) == 0) {
-- char *stream_name = section + strlen(STREAM_START);
-+ else if (pa_startswith(section, STREAM_RULE_START)) {
-+ struct stream_rule *rule;
-
-- struct stream *stream = (struct stream *) pa_hashmap_get(u->unused_streams, stream_name);
-+ name = section + strlen(STREAM_RULE_START);
-
-- if (!stream) {
-- /* first item for this stream section, so create the struct */
-- stream = stream_new(u, stream_name);
-- pa_hashmap_put(u->unused_streams, stream->id, stream);
-+ rule = pa_hashmap_get(u->stream_rules, name);
-+ if (!rule) {
-+ rule = stream_rule_new(u, name);
-+ pa_hashmap_put(u->stream_rules, rule->name, rule);
- }
-
- if (pa_streq(state->lvalue, "audio-group-for-volume"))
-- stream_set_audio_group_name_for_volume(stream, *state->rvalue ? state->rvalue : NULL);
-+ stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, state->rvalue);
-
- else if (pa_streq(state->lvalue, "audio-group-for-mute"))
-- stream_set_audio_group_name_for_mute(stream, *state->rvalue ? state->rvalue : NULL);
-+ stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, state->rvalue);
-
-- else if (strcmp(state->lvalue, "match") == 0) {
-- if (!state->rvalue)
-- goto error;
-+ else if (pa_streq(state->lvalue, "match")) {
-+ struct expression *expression;
-
-- stream->rule = parse_rule(state->rvalue);
--
-- if (!stream->rule) {
-- goto error;
-+ r = expression_from_string(state->rvalue, &expression);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
-+ return r;
- }
-+
-+ stream_rule_set_match_expression(rule, expression);
- }
- }
-
- return 0;
--
--error:
--
-- pa_log_error("failed parsing audio group definition file");
-- return -1;
--
--#undef NONE_KEYWORD
--#undef AUDIO_GROUP_KEYWORD
--#undef BIND_KEYWORD
--#undef STREAM_START
--#undef AUDIOGROUP_START
- }
-
--static void finalize_config(struct userdata *u) {
-- const char *group_name;
-+int pa__init(pa_module *module) {
-+ pa_modargs *ma = NULL;
-+ struct userdata *u;
-+ FILE *f;
-+ char *fn = NULL;
-+ struct group *group;
- void *state;
-- struct audio_group *group;
-- const char *stream_name;
-+ const char *name;
- unsigned idx;
-- struct stream *stream;
-
-- pa_assert(u);
--
-- PA_HASHMAP_FOREACH(group_name, u->audio_group_names, state) {
-- int r;
-+ pa_assert(module);
-
-- group = pa_hashmap_remove(u->unused_audio_groups, group_name);
-- if (!group)
-- group = audio_group_new(u, group_name);
--
-- r = audio_group_put(group);
-- if (r < 0) {
-- pa_log("Failed to create audio group %s.", group_name);
-- audio_group_free(group);
-- continue;
-- }
--
-- pa_assert_se(pa_hashmap_put(u->audio_groups, group->id, group) >= 0);
-+ if (!(ma = pa_modargs_new(module->argument, valid_modargs))) {
-+ pa_log("Failed to parse module arguments");
-+ goto fail;
- }
-
-- PA_HASHMAP_FOREACH(group, u->unused_audio_groups, state)
-- pa_log_debug("Audio group %s is not used.", group->id);
--
-- pa_hashmap_free(u->unused_audio_groups);
-- u->unused_audio_groups = NULL;
--
-- pa_hashmap_free(u->audio_group_names);
-- u->audio_group_names = NULL;
--
-- PA_DYNARRAY_FOREACH(stream_name, u->stream_names, idx) {
-- stream = pa_hashmap_remove(u->unused_streams, stream_name);
-- if (!stream) {
-- pa_log("Reference to undefined stream %s, ignoring.", stream_name);
-- continue;
-- }
-+ u = module->userdata = pa_xnew0(struct userdata, 1);
-+ u->volume_api = pa_volume_api_get(module->core);
-+ u->groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) group_free);
-+ u->stream_rules = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) stream_rule_free);
-+ u->stream_rules_list = pa_dynarray_new(NULL);
-+ u->rules_by_stream = pa_hashmap_new(NULL, NULL);
-+ u->stream_volume_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
-+ u->stream_mute_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
-+ u->stream_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], PA_HOOK_NORMAL, stream_put_cb,
-+ u);
-+ u->stream_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], PA_HOOK_NORMAL,
-+ stream_unlink_cb, u);
-+ u->volume_control_implementation_initialized_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED],
-+ PA_HOOK_NORMAL, volume_control_implementation_initialized_cb, u);
-+ u->mute_control_implementation_initialized_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED],
-+ PA_HOOK_NORMAL, mute_control_implementation_initialized_cb, u);
-+ u->volume_control_set_initial_volume_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], PA_HOOK_NORMAL,
-+ volume_control_set_initial_volume_cb, u);
-+ u->mute_control_set_initial_mute_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], PA_HOOK_NORMAL,
-+ mute_control_set_initial_mute_cb, u);
-+ u->volume_control_volume_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], PA_HOOK_NORMAL,
-+ volume_control_volume_changed_cb, u);
-+ u->mute_control_mute_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], PA_HOOK_NORMAL,
-+ mute_control_mute_changed_cb, u);
-+ u->volume_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
-+ PA_HOOK_NORMAL, volume_control_unlink_cb, u);
-+ u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
-+ PA_HOOK_NORMAL, mute_control_unlink_cb, u);
-+ u->stream_rule_names = pa_dynarray_new(pa_xfree);
-+
-+ f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "audio-groups.conf", "audio-groups.conf", NULL, &fn);
-+ if (f) {
-+ pa_config_item config_items[] = {
-+ { "stream-rules", parse_streams, NULL, "General" },
-+ { NULL, parse_common, NULL, NULL },
-+ { NULL, NULL, NULL, NULL },
-+ };
-
-- stream_put(stream);
-- pa_dynarray_append(u->streams, stream);
-+ pa_config_parse(fn, f, config_items, NULL, u);
-+ pa_xfree(fn);
-+ fn = NULL;
-+ fclose(f);
-+ f = NULL;
- }
-
-- PA_HASHMAP_FOREACH(stream, u->unused_streams, state)
-- pa_log_debug("Stream %s is not used.", stream->id);
--
-- pa_hashmap_free(u->unused_streams);
-- u->unused_streams = NULL;
--
-- pa_dynarray_free(u->stream_names);
-- u->stream_names = NULL;
--}
-+ PA_HASHMAP_FOREACH(group, u->groups, state)
-+ group_put(group);
-
--static bool parse_configuration(struct userdata *u, const char *filename) {
-- FILE *f;
-- char *fn = NULL;
-+ PA_DYNARRAY_FOREACH(name, u->stream_rule_names, idx) {
-+ struct stream_rule *rule;
-
-- pa_config_item table[] = {
-- { "audio-groups", parse_audio_groups, NULL, "General" },
-- { "streams", parse_streams, NULL, "General" },
-- { NULL, parse_common, NULL, NULL },
-- { NULL, NULL, NULL, NULL },
-- };
-+ rule = pa_hashmap_get(u->stream_rules, name);
-+ if (rule)
-+ pa_dynarray_append(u->stream_rules_list, rule);
-+ else
-+ pa_log("Non-existent stream rule \"%s\" referenced, ignoring.", name);
-+ }
-
-- u->audio_group_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
-- u->unused_audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-- (pa_free_cb_t) audio_group_free);
-- u->stream_names = pa_dynarray_new(pa_xfree);
-- u->unused_streams = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-- (pa_free_cb_t) stream_free);
-+ pa_dynarray_free(u->stream_rule_names);
-+ u->stream_rule_names = NULL;
-
-- if (pa_is_path_absolute(filename))
-- f = pa_open_config_file(filename, NULL, NULL, &fn);
-- else {
-- char *sys_conf_file;
-+ pa_modargs_free(ma);
-
-- sys_conf_file = pa_sprintf_malloc(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "%s", filename);
-- f = pa_open_config_file(sys_conf_file, filename, NULL, &fn);
-- pa_xfree(sys_conf_file);
-- }
-+ return 0;
-
-- if (f) {
-- pa_config_parse(fn, f, table, NULL, u);
-- pa_xfree(fn);
-- fn = NULL;
-- fclose(f);
-- f = NULL;
-- }
-+fail:
-+ pa__done(module);
-
-- finalize_config(u);
-+ if (ma)
-+ pa_modargs_free(ma);
-
-- return true;
-+ return -1;
- }
-
- void pa__done(pa_module *m) {
-- struct userdata* u;
-+ struct userdata *u;
-
- pa_assert(m);
-
-@@ -1265,70 +2023,56 @@ void pa__done(pa_module *m) {
- if (!u)
- return;
-
-- if (u->new_stream_volume)
-- pa_hook_slot_free(u->new_stream_volume);
-+ if (u->mute_control_unlink_slot)
-+ pa_hook_slot_free(u->mute_control_unlink_slot);
-
-- if (u->new_stream_mute)
-- pa_hook_slot_free(u->new_stream_mute);
-+ if (u->volume_control_unlink_slot)
-+ pa_hook_slot_free(u->volume_control_unlink_slot);
-
-- if (u->streams)
-- pa_dynarray_free(u->streams);
-+ if (u->mute_control_mute_changed_slot)
-+ pa_hook_slot_free(u->mute_control_mute_changed_slot);
-
-- if (u->audio_groups)
-- pa_hashmap_free(u->audio_groups);
-+ if (u->volume_control_volume_changed_slot)
-+ pa_hook_slot_free(u->volume_control_volume_changed_slot);
-
-- if (u->api)
-- pa_volume_api_unref(u->api);
-+ if (u->mute_control_set_initial_mute_slot)
-+ pa_hook_slot_free(u->mute_control_set_initial_mute_slot);
-
-- pa_xfree(u);
--}
-+ if (u->volume_control_set_initial_volume_slot)
-+ pa_hook_slot_free(u->volume_control_set_initial_volume_slot);
-
--int pa__init(pa_module *m) {
-- pa_modargs *ma = NULL;
-- struct userdata *u;
-- const char *filename;
-+ if (u->mute_control_implementation_initialized_slot)
-+ pa_hook_slot_free(u->mute_control_implementation_initialized_slot);
-
-- pa_assert(m);
-+ if (u->volume_control_implementation_initialized_slot)
-+ pa_hook_slot_free(u->volume_control_implementation_initialized_slot);
-
-- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-- pa_log("Failed to parse module arguments");
-- goto error;
-- }
-+ if (u->stream_unlink_slot)
-+ pa_hook_slot_free(u->stream_unlink_slot);
-
-- u = m->userdata = pa_xnew0(struct userdata, 1);
-+ if (u->stream_put_slot)
-+ pa_hook_slot_free(u->stream_put_slot);
-
-- if (!u)
-- goto error;
-+ if (u->stream_mute_controls)
-+ pa_hashmap_free(u->stream_mute_controls);
-
-- u->api = pa_volume_api_get(m->core);
-+ if (u->stream_volume_controls)
-+ pa_hashmap_free(u->stream_volume_controls);
-
-- if (!u->api)
-- goto error;
--
-- u->audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-- (pa_free_cb_t) audio_group_free);
-- u->streams = pa_dynarray_new((pa_free_cb_t) stream_free);
--
-- filename = pa_modargs_get_value(ma, "filename", AUDIO_GROUP_CONFIG);
--
-- if (!parse_configuration(u, filename))
-- goto error;
-+ if (u->rules_by_stream)
-+ pa_hashmap_free(u->rules_by_stream);
-
-- u->new_stream_volume = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_volume_control_cb, u);
-- u->new_stream_mute = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_mute_control_cb, u);
-+ if (u->stream_rules_list)
-+ pa_dynarray_free(u->stream_rules_list);
-
-- if (!u->new_stream_volume || !u->new_stream_mute)
-- goto error;
-+ if (u->stream_rules)
-+ pa_hashmap_free(u->stream_rules);
-
-- pa_modargs_free(ma);
-+ if (u->groups)
-+ pa_hashmap_free(u->groups);
-
-- return 0;
-+ if (u->volume_api)
-+ pa_volume_api_unref(u->volume_api);
-
--error:
-- pa__done(m);
--
-- if (ma)
-- pa_modargs_free(ma);
--
-- return -1;
-+ pa_xfree(u);
- }
-diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c
-index 7ac35c6..9b9f9fd 100644
---- a/src/modules/main-volume-policy/main-volume-context.c
-+++ b/src/modules/main-volume-policy/main-volume-context.c
-@@ -28,32 +28,33 @@
- #include <modules/volume-api/mute-control.h>
- #include <modules/volume-api/volume-control.h>
-
--int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
-- pa_main_volume_context **context) {
-- pa_main_volume_context *context_local;
-+#include <pulsecore/core-util.h>
-+
-+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r) {
-+ pa_main_volume_context *context;
- int r;
-
- pa_assert(policy);
- pa_assert(name);
-- pa_assert(description);
-- pa_assert(context);
-+ pa_assert(_r);
-
-- context_local = pa_xnew0(struct pa_main_volume_context, 1);
-- context_local->main_volume_policy = policy;
-- context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
-+ context = pa_xnew0(struct pa_main_volume_context, 1);
-+ context->main_volume_policy = policy;
-+ context->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
-
-- r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
-+ r = pa_main_volume_policy_register_name(policy, name, true, &context->name);
- if (r < 0)
- goto fail;
-
-- context_local->description = pa_xstrdup(description);
--
-- *context = context_local;
-+ context->description = pa_xstrdup(context->name);
-+ context->userdata = userdata;
-
-+ *_r = context;
- return 0;
-
- fail:
-- pa_main_volume_context_free(context_local);
-+ if (context)
-+ pa_main_volume_context_free(context);
-
- return r;
- }
-@@ -62,7 +63,6 @@ void pa_main_volume_context_put(pa_main_volume_context *context) {
- pa_assert(context);
-
- pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
--
- context->linked = true;
-
- pa_log_debug("Created main volume context #%u.", context->index);
-@@ -93,40 +93,21 @@ void pa_main_volume_context_unlink(pa_main_volume_context *context) {
- pa_log_debug("Unlinking main volume context %s.", context->name);
-
- if (context->linked)
-- pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
--
-- if (context->main_input_mute_control_binding) {
-- pa_binding_free(context->main_input_mute_control_binding);
-- context->main_input_mute_control_binding = NULL;
-- }
-+ pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
-
-- if (context->main_output_mute_control_binding) {
-- pa_binding_free(context->main_output_mute_control_binding);
-- context->main_output_mute_control_binding = NULL;
-- }
--
-- if (context->main_input_volume_control_binding) {
-- pa_binding_free(context->main_input_volume_control_binding);
-- context->main_input_volume_control_binding = NULL;
-- }
--
-- if (context->main_output_volume_control_binding) {
-- pa_binding_free(context->main_output_volume_control_binding);
-- context->main_output_volume_control_binding = NULL;
-- }
-+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
-
- context->main_input_mute_control = NULL;
- context->main_output_mute_control = NULL;
- context->main_input_volume_control = NULL;
- context->main_output_volume_control = NULL;
--
-- pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
- }
-
- void pa_main_volume_context_free(pa_main_volume_context *context) {
- pa_assert(context);
-
-- if (!context->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!context->unlinked && context->name)
- pa_main_volume_context_unlink(context);
-
- pa_xfree(context->description);
-@@ -137,13 +118,33 @@ void pa_main_volume_context_free(pa_main_volume_context *context) {
- pa_xfree(context);
- }
-
--const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
-+void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description) {
-+ char *old_description;
-+
- pa_assert(context);
-+ pa_assert(description);
-+
-+ old_description = context->description;
-+
-+ if (pa_streq(description, old_description))
-+ return;
-
-- return context->name;
-+ context->description = pa_xstrdup(description);
-+
-+ if (!context->linked || context->unlinked) {
-+ pa_xfree(old_description);
-+ return;
-+ }
-+
-+ pa_log_debug("Main volume context %s description changed from \"%s\" to \"%s\".", context->name, old_description,
-+ description);
-+ pa_xfree(old_description);
-+
-+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED],
-+ context);
- }
-
--static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
-+void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
- pa_volume_control *old_control;
-
- pa_assert(context);
-@@ -158,7 +159,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
- if (!context->linked || context->unlinked)
- return;
-
-- pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name,
-+ pa_log_debug("Main volume context %s main output volume control changed from %s to %s.", context->name,
- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
- pa_hook_fire(&context->main_volume_policy->hooks
-@@ -166,24 +167,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
- context);
- }
-
--void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = context,
-- .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
-- };
--
-- pa_assert(context);
-- pa_assert(target_info);
--
-- if (context->main_output_volume_control_binding)
-- pa_binding_free(context->main_output_volume_control_binding);
--
-- context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-- target_info);
--}
--
--static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
-+void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
- pa_volume_control *old_control;
-
- pa_assert(context);
-@@ -198,7 +182,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
- if (!context->linked || context->unlinked)
- return;
-
-- pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name,
-+ pa_log_debug("Main volume context %s main input volume control changed from %s to %s.", context->name,
- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
- pa_hook_fire(&context->main_volume_policy->hooks
-@@ -206,24 +190,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
- context);
- }
-
--void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = context,
-- .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
-- };
--
-- pa_assert(context);
-- pa_assert(target_info);
--
-- if (context->main_input_volume_control_binding)
-- pa_binding_free(context->main_input_volume_control_binding);
--
-- context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-- target_info);
--}
--
--static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
-+void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
- pa_mute_control *old_control;
-
- pa_assert(context);
-@@ -238,7 +205,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
- if (!context->linked || context->unlinked)
- return;
-
-- pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name,
-+ pa_log_debug("Main volume context %s main output mute control changed from %s to %s.", context->name,
- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
- pa_hook_fire(&context->main_volume_policy->hooks
-@@ -246,24 +213,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
- context);
- }
-
--void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = context,
-- .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
-- };
--
-- pa_assert(context);
-- pa_assert(target_info);
--
-- if (context->main_output_mute_control_binding)
-- pa_binding_free(context->main_output_mute_control_binding);
--
-- context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-- target_info);
--}
--
--static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
-+void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
- pa_mute_control *old_control;
-
- pa_assert(context);
-@@ -278,48 +228,10 @@ static void set_main_input_mute_control_internal(pa_main_volume_context *context
- if (!context->linked || context->unlinked)
- return;
-
-- pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name,
-+ pa_log_debug("Main volume context %s main input mute control changed from %s to %s.", context->name,
- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
- pa_hook_fire(&context->main_volume_policy->hooks
- [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
- context);
- }
--
--void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = context,
-- .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
-- };
--
-- pa_assert(context);
-- pa_assert(target_info);
--
-- if (context->main_input_mute_control_binding)
-- pa_binding_free(context->main_input_mute_control_binding);
--
-- context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
-- target_info);
--}
--
--pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
-- pa_binding_target_type *type;
--
-- pa_assert(policy);
--
-- type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts,
-- &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT],
-- &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK],
-- (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name);
-- pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control));
-- pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control));
-- pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control));
-- pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control));
--
-- return type;
--}
-diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
-index 4a0a6f7..3770168 100644
---- a/src/modules/main-volume-policy/main-volume-context.h
-+++ b/src/modules/main-volume-policy/main-volume-context.h
-@@ -24,16 +24,8 @@
-
- #include <modules/main-volume-policy/main-volume-policy.h>
-
--#include <modules/volume-api/binding.h>
--
- typedef struct pa_main_volume_context pa_main_volume_context;
-
--#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext"
--#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control"
--#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control"
--#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control"
--#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control"
--
- struct pa_main_volume_context {
- pa_main_volume_policy *main_volume_policy;
- uint32_t index;
-@@ -44,32 +36,21 @@ struct pa_main_volume_context {
- pa_mute_control *main_output_mute_control;
- pa_mute_control *main_input_mute_control;
-
-- pa_binding *main_output_volume_control_binding;
-- pa_binding *main_input_volume_control_binding;
-- pa_binding *main_output_mute_control_binding;
-- pa_binding *main_input_mute_control_binding;
--
- bool linked;
- bool unlinked;
-+
-+ void *userdata;
- };
-
--int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
-- pa_main_volume_context **context);
-+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r);
- void pa_main_volume_context_put(pa_main_volume_context *context);
- void pa_main_volume_context_unlink(pa_main_volume_context *context);
- void pa_main_volume_context_free(pa_main_volume_context *context);
-
--const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
--
--void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info);
--void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info);
--void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
-- pa_binding_target_info *target_info);
--void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info);
--
--/* Called from main-volume-policy.c only. */
--pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy);
-+void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description);
-+void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control);
-+void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control);
-+void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control);
-+void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control);
-
- #endif
-diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
-index b0b4ede..3c0fccf 100644
---- a/src/modules/main-volume-policy/main-volume-policy.c
-+++ b/src/modules/main-volume-policy/main-volume-policy.c
-@@ -28,6 +28,7 @@
- #include <modules/main-volume-policy/main-volume-context.h>
-
- #include <pulsecore/core-util.h>
-+#include <pulsecore/namereg.h>
- #include <pulsecore/shared.h>
-
- static pa_main_volume_policy *main_volume_policy_new(pa_core *core);
-@@ -70,6 +71,46 @@ void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
- }
- }
-
-+static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ pa_main_volume_policy *policy = userdata;
-+ pa_volume_control *control = call_data;
-+ pa_main_volume_context *context;
-+ void *state;
-+
-+ pa_assert(policy);
-+ pa_assert(control);
-+
-+ PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
-+ if (context->main_output_volume_control == control)
-+ pa_main_volume_context_set_main_output_volume_control(context, NULL);
-+
-+ if (context->main_input_volume_control == control)
-+ pa_main_volume_context_set_main_input_volume_control(context, NULL);
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ pa_main_volume_policy *policy = userdata;
-+ pa_mute_control *control = call_data;
-+ pa_main_volume_context *context;
-+ void *state;
-+
-+ pa_assert(policy);
-+ pa_assert(control);
-+
-+ PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
-+ if (context->main_output_mute_control == control)
-+ pa_main_volume_context_set_main_output_mute_control(context, NULL);
-+
-+ if (context->main_input_mute_control == control)
-+ pa_main_volume_context_set_main_input_mute_control(context, NULL);
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
- static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
- pa_main_volume_policy *policy;
- unsigned i;
-@@ -86,8 +127,10 @@ static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
- for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
- pa_hook_init(&policy->hooks[i], policy);
-
-- policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy);
-- pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
-+ policy->volume_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
-+ PA_HOOK_NORMAL, volume_control_unlink_cb, policy);
-+ policy->mute_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
-+ PA_HOOK_NORMAL, mute_control_unlink_cb, policy);
-
- pa_log_debug("Created a pa_main_volume_policy object.");
-
-@@ -102,10 +145,11 @@ static void main_volume_policy_free(pa_main_volume_policy *policy) {
-
- pa_log_debug("Freeing the pa_main_volume_policy object.");
-
-- if (policy->main_volume_context_binding_target_type) {
-- pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
-- pa_binding_target_type_free(policy->main_volume_context_binding_target_type);
-- }
-+ if (policy->mute_control_unlink_slot)
-+ pa_hook_slot_free(policy->mute_control_unlink_slot);
-+
-+ if (policy->volume_control_unlink_slot)
-+ pa_hook_slot_free(policy->volume_control_unlink_slot);
-
- for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
- pa_hook_done(&policy->hooks[i]);
-@@ -134,19 +178,24 @@ int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const cha
- pa_assert(requested_name);
- pa_assert(registered_name);
-
-+ if (!pa_namereg_is_valid_name(requested_name)) {
-+ pa_log("Invalid name: \"%s\"", requested_name);
-+ return -PA_ERR_INVALID;
-+ }
-+
- n = pa_xstrdup(requested_name);
-
- if (pa_hashmap_put(policy->names, n, n) < 0) {
- unsigned i = 1;
-
-- pa_xfree(n);
--
- if (fail_if_already_registered) {
-+ pa_xfree(n);
- pa_log("Name %s already registered.", requested_name);
- return -PA_ERR_EXIST;
- }
-
- do {
-+ pa_xfree(n);
- i++;
- n = pa_sprintf_malloc("%s.%u", requested_name, i);
- } while (pa_hashmap_put(policy->names, n, n) < 0);
-diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example
-index a4a35d3..3fcd267 100644
---- a/src/modules/main-volume-policy/main-volume-policy.conf.example
-+++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
-@@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
- input-volume-model = by-active-main-volume-context
- output-mute-model = none
- input-mute-model = none
--main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
-
- [MainVolumeContext x-example-call-main-volume-context]
- description = Call main volume context
-diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h
-index 5cd669e..d5f6e02 100644
---- a/src/modules/main-volume-policy/main-volume-policy.h
-+++ b/src/modules/main-volume-policy/main-volume-policy.h
-@@ -22,7 +22,6 @@
- USA.
- ***/
-
--#include <modules/volume-api/binding.h>
- #include <modules/volume-api/volume-api.h>
-
- #include <pulsecore/core.h>
-@@ -35,6 +34,7 @@ typedef struct pa_main_volume_context pa_main_volume_context;
- enum {
- PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT,
- PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK,
-+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED,
- PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
- PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
- PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
-@@ -53,7 +53,9 @@ struct pa_main_volume_policy {
-
- uint32_t next_main_volume_context_index;
- pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX];
-- pa_binding_target_type *main_volume_context_binding_target_type;
-+
-+ pa_hook_slot *volume_control_unlink_slot;
-+ pa_hook_slot *mute_control_unlink_slot;
- };
-
- pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core);
-diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
-index 0a89aa7..1b7693e 100644
---- a/src/modules/main-volume-policy/module-main-volume-policy.c
-+++ b/src/modules/main-volume-policy/module-main-volume-policy.c
-@@ -27,7 +27,7 @@
-
- #include <modules/main-volume-policy/main-volume-context.h>
-
--#include <modules/volume-api/binding.h>
-+#include <modules/volume-api/audio-group.h>
- #include <modules/volume-api/volume-api.h>
-
- #include <pulse/direction.h>
-@@ -36,6 +36,9 @@
- #include <pulsecore/core-util.h>
- #include <pulsecore/i18n.h>
-
-+#define BIND_PREFIX "bind:"
-+#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
-+
- PA_MODULE_AUTHOR("Tanu Kaskinen");
- PA_MODULE_DESCRIPTION(_("Main volume and mute policy"));
- PA_MODULE_VERSION(PACKAGE_VERSION);
-@@ -52,6 +55,7 @@ enum model {
- };
-
- struct userdata {
-+ pa_volume_api *volume_api;
- pa_main_volume_policy *main_volume_policy;
- enum model output_volume_model;
- enum model input_volume_model;
-@@ -60,26 +64,67 @@ struct userdata {
- pa_hashmap *contexts; /* name -> struct context */
-
- pa_hook_slot *active_main_volume_context_changed_slot;
-+ pa_hook_slot *main_volume_context_main_output_volume_control_changed_slot;
-+ pa_hook_slot *main_volume_context_main_input_volume_control_changed_slot;
-+ pa_hook_slot *main_volume_context_main_output_mute_control_changed_slot;
-+ pa_hook_slot *main_volume_context_main_input_mute_control_changed_slot;
-+ pa_hook_slot *audio_group_put_slot;
-+ pa_hook_slot *audio_group_unlink_slot;
-+ pa_hook_slot *audio_group_volume_control_changed_slot;
-+ pa_hook_slot *audio_group_mute_control_changed_slot;
-+};
-
-- /* The following fields are only used during initialization. */
-- pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */
-- pa_hashmap *unused_contexts; /* name -> struct context */
-+struct control_info {
-+ /* As appropriate for this control, points to one of
-+ * - pa_main_volume_context.main_output_volume_control
-+ * - pa_main_volume_context.main_input_volume_control
-+ * - pa_main_volume_context.main_output_mute_control
-+ * - pa_main_volume_context.main_input_mute_control */
-+ void **control;
-+
-+ /* As appropriate for this control, points to one of
-+ * - userdata.output_volume_model
-+ * - userdata.input_volume_model
-+ * - userdata.output_mute_model
-+ * - userdata.input_mute_model */
-+ enum model *model;
-+
-+ /* Name of the audio group to which the context volume or mute control is
-+ * bound. If the context control is not bound to anything, this is NULL. */
-+ char *binding_target_name;
-+
-+ /* Points to the audio group to which the context volume or mute control is
-+ * bound. If the context control is not bound to anything, or it's bound
-+ * but the target doesn't currently exist, this is NULL. */
-+ pa_audio_group *binding_target;
-+
-+ /* As appropriate for this control, points to one of
-+ * - pa_main_volume_context_set_main_output_volume_control()
-+ * - pa_main_volume_context_set_main_input_volume_control()
-+ * - pa_main_volume_context_set_main_output_mute_control()
-+ * - pa_main_volume_context_set_main_input_mute_control() */
-+ void (*set_control)(pa_main_volume_context *context, void *control);
-+
-+ /* As appropriate for this control, points to one of
-+ * - pa_volume_api_set_main_output_volume_control()
-+ * - pa_volume_api_set_main_input_volume_control()
-+ * - pa_volume_api_set_main_output_mute_control()
-+ * - pa_volume_api_set_main_input_mute_control() */
-+ void (*set_volume_api_control)(pa_volume_api *api, void *control);
- };
-
- struct context {
- struct userdata *userdata;
-- char *name;
-- char *description;
-- pa_binding_target_info *main_output_volume_control_target_info;
-- pa_binding_target_info *main_input_volume_control_target_info;
-- pa_binding_target_info *main_output_mute_control_target_info;
-- pa_binding_target_info *main_input_mute_control_target_info;
- pa_main_volume_context *main_volume_context;
-+ struct control_info output_volume_info;
-+ struct control_info input_volume_info;
-+ struct control_info output_mute_info;
-+ struct control_info input_mute_info;
-
- bool unlinked;
- };
-
--static void context_unlink(struct context *context);
-+static void context_free(struct context *context);
-
- static const char *model_to_string(enum model model) {
- switch (model) {
-@@ -107,56 +152,57 @@ static int model_from_string(const char *str, enum model *model) {
- return 0;
- }
-
--static struct context *context_new(struct userdata *u, const char *name) {
-- struct context *context;
-+static int context_new(struct userdata *u, const char *name, struct context **_r) {
-+ struct context *context = NULL;
-+ int r;
-
- pa_assert(u);
- pa_assert(name);
-+ pa_assert(_r);
-
- context = pa_xnew0(struct context, 1);
- context->userdata = u;
-- context->name = pa_xstrdup(name);
-- context->description = pa_xstrdup(name);
-
-- return context;
--}
--
--static int context_put(struct context *context) {
-- int r;
--
-- pa_assert(context);
--
-- r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
-- &context->main_volume_context);
-+ r = pa_main_volume_context_new(u->main_volume_policy, name, u, &context->main_volume_context);
- if (r < 0)
- goto fail;
-
-- if (context->main_output_volume_control_target_info)
-- pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context,
-- context->main_output_volume_control_target_info);
-+ context->output_volume_info.control = (void **) &context->main_volume_context->main_output_volume_control;
-+ context->input_volume_info.control = (void **) &context->main_volume_context->main_input_volume_control;
-+ context->output_mute_info.control = (void **) &context->main_volume_context->main_output_mute_control;
-+ context->input_mute_info.control = (void **) &context->main_volume_context->main_input_mute_control;
-
-- if (context->main_input_volume_control_target_info)
-- pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context,
-- context->main_input_volume_control_target_info);
-+ context->output_volume_info.model = &u->output_volume_model;
-+ context->input_volume_info.model = &u->input_volume_model;
-+ context->output_mute_info.model = &u->output_mute_model;
-+ context->input_mute_info.model = &u->input_mute_model;
-
-- if (context->main_output_mute_control_target_info)
-- pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context,
-- context->main_output_mute_control_target_info);
-+ context->output_volume_info.set_control = (void *) pa_main_volume_context_set_main_output_volume_control;
-+ context->input_volume_info.set_control = (void *) pa_main_volume_context_set_main_input_volume_control;
-+ context->output_mute_info.set_control = (void *) pa_main_volume_context_set_main_output_mute_control;
-+ context->input_mute_info.set_control = (void *) pa_main_volume_context_set_main_input_mute_control;
-
-- if (context->main_input_mute_control_target_info)
-- pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context,
-- context->main_input_mute_control_target_info);
--
-- pa_main_volume_context_put(context->main_volume_context);
-+ context->output_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_volume_control;
-+ context->input_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_volume_control;
-+ context->output_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_mute_control;
-+ context->input_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_mute_control;
-
-+ *_r = context;
- return 0;
-
- fail:
-- context_unlink(context);
-+ if (context)
-+ context_free(context);
-
- return r;
- }
-
-+static void context_put(struct context *context) {
-+ pa_assert(context);
-+
-+ pa_main_volume_context_put(context->main_volume_context);
-+}
-+
- static void context_unlink(struct context *context) {
- pa_assert(context);
-
-@@ -165,10 +211,8 @@ static void context_unlink(struct context *context) {
-
- context->unlinked = true;
-
-- if (context->main_volume_context) {
-- pa_main_volume_context_free(context->main_volume_context);
-- context->main_volume_context = NULL;
-- }
-+ if (context->main_volume_context)
-+ pa_main_volume_context_unlink(context->main_volume_context);
- }
-
- static void context_free(struct context *context) {
-@@ -177,132 +221,290 @@ static void context_free(struct context *context) {
- if (!context->unlinked)
- context_unlink(context);
-
-- if (context->main_input_mute_control_target_info)
-- pa_binding_target_info_free(context->main_input_mute_control_target_info);
--
-- if (context->main_output_mute_control_target_info)
-- pa_binding_target_info_free(context->main_output_mute_control_target_info);
--
-- if (context->main_input_volume_control_target_info)
-- pa_binding_target_info_free(context->main_input_volume_control_target_info);
--
-- if (context->main_output_volume_control_target_info)
-- pa_binding_target_info_free(context->main_output_volume_control_target_info);
-+ if (context->main_volume_context)
-+ pa_main_volume_context_free(context->main_volume_context);
-
-- pa_xfree(context->description);
-- pa_xfree(context->name);
- pa_xfree(context);
- }
-
--static void context_set_description(struct context *context, const char *description) {
-- pa_assert(context);
-- pa_assert(description);
--
-- pa_xfree(context->description);
-- context->description = pa_xstrdup(description);
--}
--
--static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction,
-- pa_binding_target_info *info) {
-+static struct control_info *context_get_control_info(struct context *context, enum control_type type,
-+ pa_direction_t direction) {
- pa_assert(context);
-
- switch (type) {
- case CONTROL_TYPE_VOLUME:
-- if (direction == PA_DIRECTION_OUTPUT) {
-- if (context->main_output_volume_control_target_info)
-- pa_binding_target_info_free(context->main_output_volume_control_target_info);
--
-- if (info)
-- context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
-- else
-- context->main_output_volume_control_target_info = NULL;
-- } else {
-- if (context->main_input_volume_control_target_info)
-- pa_binding_target_info_free(context->main_input_volume_control_target_info);
--
-- if (info)
-- context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
-- else
-- context->main_input_volume_control_target_info = NULL;
-+ switch (direction) {
-+ case PA_DIRECTION_OUTPUT:
-+ return &context->output_volume_info;
-+
-+ case PA_DIRECTION_INPUT:
-+ return &context->input_volume_info;
- }
- break;
-
- case CONTROL_TYPE_MUTE:
-- if (direction == PA_DIRECTION_OUTPUT) {
-- if (context->main_output_mute_control_target_info)
-- pa_binding_target_info_free(context->main_output_mute_control_target_info);
--
-- if (info)
-- context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
-- else
-- context->main_output_mute_control_target_info = NULL;
-- } else {
-- if (context->main_input_mute_control_target_info)
-- pa_binding_target_info_free(context->main_input_mute_control_target_info);
--
-- if (info)
-- context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
-- else
-- context->main_input_mute_control_target_info = NULL;
-+ switch (direction) {
-+ case PA_DIRECTION_OUTPUT:
-+ return &context->output_mute_info;
-+
-+ case PA_DIRECTION_INPUT:
-+ return &context->input_mute_info;
- }
- break;
- }
-+
-+ pa_assert_not_reached();
-+}
-+
-+static void context_set_binding_target(struct context *context, enum control_type type, pa_direction_t direction,
-+ pa_audio_group *group) {
-+ struct control_info *info;
-+ void *control = NULL;
-+
-+ pa_assert(context);
-+
-+ info = context_get_control_info(context, type, direction);
-+ info->binding_target = group;
-+
-+ if (group) {
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ control = group->volume_control;
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ control = group->mute_control;
-+ break;
-+ }
-+ }
-+
-+ info->set_control(context->main_volume_context, control);
-+}
-+
-+static void context_set_binding_target_name(struct context *context, enum control_type type, pa_direction_t direction,
-+ const char *name) {
-+ struct control_info *info;
-+ pa_audio_group *group = NULL;
-+
-+ pa_assert(context);
-+
-+ info = context_get_control_info(context, type, direction);
-+
-+ if (pa_safe_streq(name, info->binding_target_name))
-+ return;
-+
-+ pa_xfree(info->binding_target_name);
-+ info->binding_target_name = pa_xstrdup(name);
-+
-+ if (name)
-+ group = pa_hashmap_get(context->userdata->volume_api->audio_groups, name);
-+
-+ context_set_binding_target(context, type, direction, group);
- }
-
- static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) {
- struct userdata *u = userdata;
- pa_main_volume_context *context;
-- pa_volume_api *api;
-- pa_binding_target_info *info;
-
- pa_assert(u);
-
- context = u->main_volume_policy->active_main_volume_context;
-- api = u->main_volume_policy->volume_api;
-
- if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-- if (context) {
-- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
-- pa_volume_api_bind_main_output_volume_control(api, info);
-- pa_binding_target_info_free(info);
-- } else
-- pa_volume_api_set_main_output_volume_control(api, NULL);
-+ if (context)
-+ pa_volume_api_set_main_output_volume_control(u->volume_api, context->main_output_volume_control);
-+ else
-+ pa_volume_api_set_main_output_volume_control(u->volume_api, NULL);
- }
-
- if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-- if (context) {
-- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
-- pa_volume_api_bind_main_input_volume_control(api, info);
-- pa_binding_target_info_free(info);
-- } else
-- pa_volume_api_set_main_input_volume_control(api, NULL);
-+ if (context)
-+ pa_volume_api_set_main_input_volume_control(u->volume_api, context->main_input_volume_control);
-+ else
-+ pa_volume_api_set_main_input_volume_control(u->volume_api, NULL);
- }
-
- if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-- if (context) {
-- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
-- pa_volume_api_bind_main_output_mute_control(api, info);
-- pa_binding_target_info_free(info);
-- } else
-- pa_volume_api_set_main_output_mute_control(api, NULL);
-+ if (context)
-+ pa_volume_api_set_main_output_mute_control(u->volume_api, context->main_output_mute_control);
-+ else
-+ pa_volume_api_set_main_output_mute_control(u->volume_api, NULL);
- }
-
- if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
-- if (context) {
-- info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
-- PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
-- pa_volume_api_bind_main_input_mute_control(api, info);
-- pa_binding_target_info_free(info);
-- } else
-- pa_volume_api_set_main_input_mute_control(api, NULL);
-+ if (context)
-+ pa_volume_api_set_main_input_mute_control(u->volume_api, context->main_input_mute_control);
-+ else
-+ pa_volume_api_set_main_input_mute_control(u->volume_api, NULL);
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static void handle_context_control_change(struct context *context, enum control_type type, pa_direction_t direction) {
-+ struct control_info *info;
-+
-+ pa_assert(context);
-+
-+ info = context_get_control_info(context, type, direction);
-+
-+ if (*info->model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT
-+ && context->userdata->main_volume_policy->active_main_volume_context == context->main_volume_context)
-+ info->set_volume_api_control(context->userdata->volume_api, *info->control);
-+}
-+
-+static pa_hook_result_t main_volume_context_main_output_volume_control_changed_cb(void *hook_data, void *call_data,
-+ void *userdata) {
-+ pa_main_volume_context *context = call_data;
-+
-+ pa_assert(context);
-+
-+ handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t main_volume_context_main_input_volume_control_changed_cb(void *hook_data, void *call_data,
-+ void *userdata) {
-+ pa_main_volume_context *context = call_data;
-+
-+ pa_assert(context);
-+
-+ handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t main_volume_context_main_output_mute_control_changed_cb(void *hook_data, void *call_data,
-+ void *userdata) {
-+ pa_main_volume_context *context = call_data;
-+
-+ pa_assert(context);
-+
-+ handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t main_volume_context_main_input_mute_control_changed_cb(void *hook_data, void *call_data,
-+ void *userdata) {
-+ pa_main_volume_context *context = call_data;
-+
-+ pa_assert(context);
-+
-+ handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t audio_group_put_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_audio_group *group = call_data;
-+ struct context *context;
-+ void *state;
-+
-+ pa_assert(u);
-+ pa_assert(group);
-+
-+ PA_HASHMAP_FOREACH(context, u->contexts, state) {
-+ if (context->output_volume_info.binding_target_name
-+ && pa_streq(context->output_volume_info.binding_target_name, group->name))
-+ context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, group);
-+
-+ if (context->input_volume_info.binding_target_name
-+ && pa_streq(context->input_volume_info.binding_target_name, group->name))
-+ context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, group);
-+
-+ if (context->output_mute_info.binding_target_name
-+ && pa_streq(context->output_mute_info.binding_target_name, group->name))
-+ context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, group);
-+
-+ if (context->input_mute_info.binding_target_name
-+ && pa_streq(context->input_mute_info.binding_target_name, group->name))
-+ context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, group);
-+ }
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t audio_group_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_audio_group *group = call_data;
-+ struct context *context;
-+ void *state;
-+
-+ pa_assert(u);
-+ pa_assert(group);
-+
-+ PA_HASHMAP_FOREACH(context, u->contexts, state) {
-+ if (context->output_volume_info.binding_target == group)
-+ context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, NULL);
-+
-+ if (context->input_volume_info.binding_target == group)
-+ context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, NULL);
-+
-+ if (context->output_mute_info.binding_target == group)
-+ context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, NULL);
-+
-+ if (context->input_mute_info.binding_target == group)
-+ context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, NULL);
- }
-
- return PA_HOOK_OK;
- }
-
-+static void handle_audio_group_control_change(struct userdata *u, pa_audio_group *group, enum control_type type) {
-+ struct context *context;
-+ void *state;
-+
-+ pa_assert(u);
-+ pa_assert(group);
-+
-+ PA_HASHMAP_FOREACH(context, u->contexts, state) {
-+ switch (type) {
-+ case CONTROL_TYPE_VOLUME:
-+ if (context->output_volume_info.binding_target == group)
-+ pa_main_volume_context_set_main_output_volume_control(context->main_volume_context, group->volume_control);
-+
-+ if (context->input_volume_info.binding_target == group)
-+ pa_main_volume_context_set_main_input_volume_control(context->main_volume_context, group->volume_control);
-+ break;
-+
-+ case CONTROL_TYPE_MUTE:
-+ if (context->output_mute_info.binding_target == group)
-+ pa_main_volume_context_set_main_output_mute_control(context->main_volume_context, group->mute_control);
-+
-+ if (context->input_mute_info.binding_target == group)
-+ pa_main_volume_context_set_main_input_mute_control(context->main_volume_context, group->mute_control);
-+ break;
-+ }
-+ }
-+}
-+
-+static pa_hook_result_t audio_group_volume_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_audio_group *group = call_data;
-+
-+ pa_assert(u);
-+ pa_assert(group);
-+
-+ handle_audio_group_control_change(u, group, CONTROL_TYPE_VOLUME);
-+
-+ return PA_HOOK_OK;
-+}
-+
-+static pa_hook_result_t audio_group_mute_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct userdata *u = userdata;
-+ pa_audio_group *group = call_data;
-+
-+ pa_assert(u);
-+ pa_assert(group);
-+
-+ handle_audio_group_control_change(u, group, CONTROL_TYPE_MUTE);
-+
-+ return PA_HOOK_OK;
-+}
-+
- static int parse_model(pa_config_parser_state *state) {
- int r;
-
-@@ -315,105 +517,81 @@ static int parse_model(pa_config_parser_state *state) {
- return r;
- }
-
--static int parse_main_volume_contexts(pa_config_parser_state *state) {
-- struct userdata *u;
-- char *name;
-- const char *split_state = NULL;
--
-- pa_assert(state);
--
-- u = state->userdata;
--
-- while ((name = pa_split_spaces(state->rvalue, &split_state)))
-- pa_hashmap_put(u->context_names, name, name);
--
-- return 0;
--}
--
--static struct context *get_context(struct userdata *u, const char *section) {
-+static int get_context(struct userdata *u, const char *section, struct context **_r) {
- const char *name;
- struct context *context;
-
- pa_assert(u);
-
- if (!section)
-- return NULL;
-+ return -PA_ERR_INVALID;
-
- if (!pa_startswith(section, "MainVolumeContext "))
-- return NULL;
-+ return -PA_ERR_INVALID;
-
- name = section + 18;
-
-- context = pa_hashmap_get(u->unused_contexts, name);
-+ context = pa_hashmap_get(u->contexts, name);
- if (!context) {
-- context = context_new(u, name);
-- pa_hashmap_put(u->unused_contexts, context->name, context);
-+ int r;
-+
-+ r = context_new(u, name, &context);
-+ if (r < 0)
-+ return r;
-+
-+ pa_hashmap_put(u->contexts, (void *) context->main_volume_context->name, context);
- }
-
-- return context;
-+ *_r = context;
-+ return 0;
- }
-
- static int parse_description(pa_config_parser_state *state) {
- struct userdata *u;
-+ int r;
- struct context *context;
-
- pa_assert(state);
-
- u = state->userdata;
-
-- context = get_context(u, state->section);
-- if (!context) {
-- pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
-+ r = get_context(u, state->section, &context);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
- pa_strnull(state->section));
- return -PA_ERR_INVALID;
- }
-
-- context_set_description(context, state->rvalue);
-+ pa_main_volume_context_set_description(context->main_volume_context, state->rvalue);
-
- return 0;
- }
-
--static const char *get_target_field_name(enum control_type type) {
-- switch (type) {
-- case CONTROL_TYPE_VOLUME:
-- return "volume_control";
--
-- case CONTROL_TYPE_MUTE:
-- return "mute_control";
-- }
--
-- pa_assert_not_reached();
--}
--
--static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
-+static int parse_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
- struct userdata *u;
-+ int r;
- struct context *context;
-
- pa_assert(state);
-
- u = state->userdata;
-
-- context = get_context(u, state->section);
-- if (!context) {
-- pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
-+ r = get_context(u, state->section, &context);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
- pa_strnull(state->section));
- return -PA_ERR_INVALID;
- }
-
- if (pa_streq(state->rvalue, "none"))
-- context_set_main_control_target_info(context, type, direction, NULL);
-- else if (pa_startswith(state->rvalue, "bind:")) {
-- int r;
-- pa_binding_target_info *info;
--
-- r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
-- if (r < 0) {
-- pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
-- return r;
-+ context_set_binding_target_name(context, type, direction, NULL);
-+ else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
-+ if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX))
-+ context_set_binding_target_name(context, type, direction, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
-+ else {
-+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
-+ return -PA_ERR_INVALID;
- }
--
-- context_set_main_control_target_info(context, type, direction, info);
-- pa_binding_target_info_free(info);
- } else {
- pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
- return -PA_ERR_INVALID;
-@@ -425,69 +603,38 @@ static int parse_main_control(pa_config_parser_state *state, enum control_type t
- static int parse_main_output_volume_control(pa_config_parser_state *state) {
- pa_assert(state);
-
-- return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
-+ return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
- }
-
- static int parse_main_input_volume_control(pa_config_parser_state *state) {
- pa_assert(state);
-
-- return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
-+ return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
- }
-
- static int parse_main_output_mute_control(pa_config_parser_state *state) {
- pa_assert(state);
-
-- return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
-+ return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
- }
-
- static int parse_main_input_mute_control(pa_config_parser_state *state) {
- pa_assert(state);
-
-- return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
--}
--
--static void finalize_config(struct userdata *u) {
-- const char *context_name;
-- void *state;
-- struct context *context;
--
-- pa_assert(u);
--
-- PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
-- int r;
--
-- context = pa_hashmap_remove(u->unused_contexts, context_name);
-- if (!context)
-- context = context_new(u, context_name);
--
-- r = context_put(context);
-- if (r < 0) {
-- pa_log_warn("Failed to create main volume context %s.", context_name);
-- context_free(context);
-- continue;
-- }
--
-- pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
-- }
--
-- PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
-- pa_log_debug("Main volume context %s is not used.", context->name);
--
-- pa_hashmap_free(u->unused_contexts);
-- u->unused_contexts = NULL;
--
-- pa_hashmap_free(u->context_names);
-- u->context_names = NULL;
-+ return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
- }
-
- int pa__init(pa_module *module) {
- struct userdata *u;
- FILE *f;
- char *fn = NULL;
-+ struct context *context;
-+ void *state;
-
- pa_assert(module);
-
- u = module->userdata = pa_xnew0(struct userdata, 1);
-+ u->volume_api = pa_volume_api_get(module->core);
- u->main_volume_policy = pa_main_volume_policy_get(module->core);
- u->output_volume_model = MODEL_NONE;
- u->input_volume_model = MODEL_NONE;
-@@ -498,9 +645,32 @@ int pa__init(pa_module *module) {
- u->active_main_volume_context_changed_slot =
- pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED],
- PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u);
-- u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
-- u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-- (pa_free_cb_t) context_free);
-+ u->main_volume_context_main_output_volume_control_changed_slot =
-+ pa_hook_connect(&u->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
-+ PA_HOOK_NORMAL, main_volume_context_main_output_volume_control_changed_cb, u);
-+ u->main_volume_context_main_input_volume_control_changed_slot =
-+ pa_hook_connect(&u->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
-+ PA_HOOK_NORMAL, main_volume_context_main_input_volume_control_changed_cb, u);
-+ u->main_volume_context_main_output_mute_control_changed_slot =
-+ pa_hook_connect(&u->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
-+ PA_HOOK_NORMAL, main_volume_context_main_output_mute_control_changed_cb, u);
-+ u->main_volume_context_main_input_mute_control_changed_slot =
-+ pa_hook_connect(&u->main_volume_policy->hooks
-+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
-+ PA_HOOK_NORMAL, main_volume_context_main_input_mute_control_changed_cb, u);
-+ u->audio_group_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], PA_HOOK_NORMAL,
-+ audio_group_put_cb, u);
-+ u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], PA_HOOK_NORMAL,
-+ audio_group_unlink_cb, u);
-+ u->audio_group_volume_control_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], PA_HOOK_NORMAL,
-+ audio_group_volume_control_changed_cb, u);
-+ u->audio_group_mute_control_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
-+ audio_group_mute_control_changed_cb, u);
-
- f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
- if (f) {
-@@ -509,7 +679,6 @@ int pa__init(pa_module *module) {
- { "input-volume-model", parse_model, &u->input_volume_model, "General" },
- { "output-mute-model", parse_model, &u->output_mute_model, "General" },
- { "input-mute-model", parse_model, &u->input_mute_model, "General" },
-- { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" },
- { "description", parse_description, NULL, NULL },
- { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL },
- { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL },
-@@ -525,7 +694,8 @@ int pa__init(pa_module *module) {
- f = NULL;
- }
-
-- finalize_config(u);
-+ PA_HASHMAP_FOREACH(context, u->contexts, state)
-+ context_put(context);
-
- pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model));
- pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model));
-@@ -544,6 +714,30 @@ void pa__done(pa_module *module) {
- if (!u)
- return;
-
-+ if (u->audio_group_mute_control_changed_slot)
-+ pa_hook_slot_free(u->audio_group_mute_control_changed_slot);
-+
-+ if (u->audio_group_volume_control_changed_slot)
-+ pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
-+
-+ if (u->audio_group_unlink_slot)
-+ pa_hook_slot_free(u->audio_group_unlink_slot);
-+
-+ if (u->audio_group_put_slot)
-+ pa_hook_slot_free(u->audio_group_put_slot);
-+
-+ if (u->main_volume_context_main_input_mute_control_changed_slot)
-+ pa_hook_slot_free(u->main_volume_context_main_input_mute_control_changed_slot);
-+
-+ if (u->main_volume_context_main_output_mute_control_changed_slot)
-+ pa_hook_slot_free(u->main_volume_context_main_output_mute_control_changed_slot);
-+
-+ if (u->main_volume_context_main_input_volume_control_changed_slot)
-+ pa_hook_slot_free(u->main_volume_context_main_input_volume_control_changed_slot);
-+
-+ if (u->main_volume_context_main_output_volume_control_changed_slot)
-+ pa_hook_slot_free(u->main_volume_context_main_output_volume_control_changed_slot);
-+
- if (u->active_main_volume_context_changed_slot)
- pa_hook_slot_free(u->active_main_volume_context_changed_slot);
-
-@@ -553,5 +747,8 @@ void pa__done(pa_module *module) {
- if (u->main_volume_policy)
- pa_main_volume_policy_unref(u->main_volume_policy);
-
-+ if (u->volume_api)
-+ pa_volume_api_unref(u->volume_api);
-+
- pa_xfree(u);
- }
-diff --git a/src/modules/volume-api/audio-group.c b/src/modules/volume-api/audio-group.c
-index 76bfa69..66e0f8a 100644
---- a/src/modules/volume-api/audio-group.c
-+++ b/src/modules/volume-api/audio-group.c
-@@ -29,34 +29,33 @@
-
- #include <pulsecore/core-util.h>
-
--int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group) {
-- pa_audio_group *group_local;
-+int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r) {
-+ pa_audio_group *group = NULL;
- int r;
-
- pa_assert(api);
- pa_assert(name);
-- pa_assert(description);
-- pa_assert(group);
-+ pa_assert(_r);
-
-- group_local = pa_xnew0(pa_audio_group, 1);
-- group_local->volume_api = api;
-- group_local->index = pa_volume_api_allocate_audio_group_index(api);
-+ group = pa_xnew0(pa_audio_group, 1);
-+ group->volume_api = api;
-+ group->index = pa_volume_api_allocate_audio_group_index(api);
-
-- r = pa_volume_api_register_name(api, name, true, &group_local->name);
-+ r = pa_volume_api_register_name(api, name, true, &group->name);
- if (r < 0)
- goto fail;
-
-- group_local->description = pa_xstrdup(description);
-- group_local->proplist = pa_proplist_new();
-- group_local->volume_streams = pa_hashmap_new(NULL, NULL);
-- group_local->mute_streams = pa_hashmap_new(NULL, NULL);
--
-- *group = group_local;
-+ group->description = pa_xstrdup(group->name);
-+ group->proplist = pa_proplist_new();
-+ group->volume_streams = pa_hashmap_new(NULL, NULL);
-+ group->mute_streams = pa_hashmap_new(NULL, NULL);
-
-+ *_r = group;
- return 0;
-
- fail:
-- pa_audio_group_free(group_local);
-+ if (group)
-+ pa_audio_group_free(group);
-
- return r;
- }
-@@ -68,7 +67,6 @@ void pa_audio_group_put(pa_audio_group *group) {
- pa_assert(group);
-
- pa_volume_api_add_audio_group(group->volume_api, group);
--
- group->linked = true;
-
- pa_log_debug("Created audio group #%u.", group->index);
-@@ -99,9 +97,9 @@ void pa_audio_group_unlink(pa_audio_group *group) {
- pa_log_debug("Unlinking audio group %s.", group->name);
-
- if (group->linked)
-- pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
-+ pa_volume_api_remove_audio_group(group->volume_api, group);
-
-- pa_volume_api_remove_audio_group(group->volume_api, group);
-+ pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
-
- while ((stream = pa_hashmap_first(group->mute_streams)))
- pas_stream_set_audio_group_for_mute(stream, NULL);
-@@ -109,34 +107,15 @@ void pa_audio_group_unlink(pa_audio_group *group) {
- while ((stream = pa_hashmap_first(group->volume_streams)))
- pas_stream_set_audio_group_for_volume(stream, NULL);
-
-- if (group->mute_control_binding) {
-- pa_binding_free(group->mute_control_binding);
-- group->mute_control_binding = NULL;
-- }
--
-- if (group->volume_control_binding) {
-- pa_binding_free(group->volume_control_binding);
-- group->volume_control_binding = NULL;
-- }
--
-- pa_audio_group_set_have_own_mute_control(group, false);
-- pa_audio_group_set_have_own_volume_control(group, false);
--
-- if (group->mute_control) {
-- pa_mute_control_remove_audio_group(group->mute_control, group);
-- group->mute_control = NULL;
-- }
--
-- if (group->volume_control) {
-- pa_volume_control_remove_audio_group(group->volume_control, group);
-- group->volume_control = NULL;
-- }
-+ pa_audio_group_set_mute_control(group, NULL);
-+ pa_audio_group_set_volume_control(group, NULL);
- }
-
- void pa_audio_group_free(pa_audio_group *group) {
- pa_assert(group);
-
-- if (!group->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!group->unlinked && group->name)
- pa_audio_group_unlink(group);
-
- if (group->mute_streams)
-@@ -156,133 +135,33 @@ void pa_audio_group_free(pa_audio_group *group) {
- pa_xfree(group);
- }
-
--const char *pa_audio_group_get_name(pa_audio_group *group) {
-- pa_assert(group);
-+void pa_audio_group_set_description(pa_audio_group *group, const char *description) {
-+ char *old_description;
-
-- return group->name;
--}
--
--static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume,
-- bool set_balance) {
-- pa_audio_group *group;
-- pas_stream *stream;
-- void *state;
--
-- pa_assert(control);
-- pa_assert(volume);
--
-- group = control->userdata;
--
-- PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
-- if (stream->own_volume_control)
-- pa_volume_control_set_volume(stream->own_volume_control, volume, set_volume, set_balance);
-- }
--
-- return 0;
--}
--
--static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
-- pa_audio_group *group;
-- pas_stream *stream;
-- void *state;
--
-- pa_assert(control);
--
-- group = control->userdata;
--
-- PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
-- if (stream->own_volume_control)
-- pa_volume_control_set_volume(stream->own_volume_control, &control->volume, true, true);
-- }
--}
--
--void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have) {
- pa_assert(group);
-+ pa_assert(description);
-
-- if (have == group->have_own_volume_control)
-- return;
--
-- if (have) {
-- pa_bvolume initial_volume;
--
-- if (group->volume_api->core->flat_volumes)
-- /* Usually the initial volume should get overridden by some module
-- * that manages audio group volume levels, but if there's no such
-- * module, let's try to avoid too high volume in flat volume
-- * mode. */
-- pa_bvolume_init_mono(&initial_volume, 0.3 * PA_VOLUME_NORM);
-- else
-- pa_bvolume_init_mono(&initial_volume, PA_VOLUME_NORM);
--
-- pa_assert(!group->own_volume_control);
-- group->own_volume_control = pa_volume_control_new(group->volume_api, "audio-group-volume-control",
-- group->description, false, false);
-- pa_volume_control_set_owner_audio_group(group->own_volume_control, group);
-- group->own_volume_control->set_volume = volume_control_set_volume_cb;
-- group->own_volume_control->userdata = group;
-- pa_volume_control_put(group->own_volume_control, &initial_volume, volume_control_set_initial_volume_cb);
-- } else {
-- pa_volume_control_free(group->own_volume_control);
-- group->own_volume_control = NULL;
-- }
--
-- group->have_own_volume_control = have;
--}
--
--static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
-- pa_audio_group *group;
-- pas_stream *stream;
-- void *state;
--
-- pa_assert(control);
--
-- group = control->userdata;
--
-- PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
-- if (stream->own_mute_control)
-- pa_mute_control_set_mute(stream->own_mute_control, mute);
-- }
--
-- return 0;
--}
--
--static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
-- pa_audio_group *group;
-- pas_stream *stream;
-- void *state;
-+ old_description = group->description;
-
-- pa_assert(control);
-+ if (pa_streq(description, old_description))
-+ return;
-
-- group = control->userdata;
-+ group->description = pa_xstrdup(description);
-
-- PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
-- if (stream->own_mute_control)
-- pa_mute_control_set_mute(stream->own_mute_control, control->mute);
-+ if (!group->linked || group->unlinked) {
-+ pa_xfree(old_description);
-+ return;
- }
--}
-
--void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) {
-- pa_assert(group);
-+ pa_log_debug("The description of audio group %s changed from \"%s\" to \"%s\".", group->name, old_description,
-+ description);
-+ pa_xfree(old_description);
-
-- if (have == group->have_own_mute_control)
-- return;
-+ pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], group);
-
-- group->have_own_mute_control = have;
--
-- if (have) {
-- pa_assert(!group->own_mute_control);
-- group->own_mute_control = pa_mute_control_new(group->volume_api, "audio-group-mute-control", group->description);
-- pa_mute_control_set_owner_audio_group(group->own_mute_control, group);
-- group->own_mute_control->set_mute = mute_control_set_mute_cb;
-- group->own_mute_control->userdata = group;
-- pa_mute_control_put(group->own_mute_control, false, true, mute_control_set_initial_mute_cb);
-- } else {
-- pa_mute_control_free(group->own_mute_control);
-- group->own_mute_control = NULL;
-- }
- }
-
--static void set_volume_control_internal(pa_audio_group *group, pa_volume_control *control) {
-+void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
- pa_volume_control *old_control;
-
- pa_assert(group);
-@@ -292,14 +171,8 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
- if (control == old_control)
- return;
-
-- if (old_control)
-- pa_volume_control_remove_audio_group(old_control, group);
--
- group->volume_control = control;
-
-- if (control)
-- pa_volume_control_add_audio_group(control, group);
--
- if (!group->linked || group->unlinked)
- return;
-
-@@ -309,18 +182,7 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
- pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], group);
- }
-
--void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
-- pa_assert(group);
--
-- if (group->volume_control_binding) {
-- pa_binding_free(group->volume_control_binding);
-- group->volume_control_binding = NULL;
-- }
--
-- set_volume_control_internal(group, control);
--}
--
--static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *control) {
-+void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
- pa_mute_control *old_control;
-
- pa_assert(group);
-@@ -330,14 +192,8 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
- if (control == old_control)
- return;
-
-- if (old_control)
-- pa_mute_control_remove_audio_group(old_control, group);
--
- group->mute_control = control;
-
-- if (control)
-- pa_mute_control_add_audio_group(control, group);
--
- if (!group->linked || group->unlinked)
- return;
-
-@@ -347,57 +203,11 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
- pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], group);
- }
-
--void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
-- pa_assert(group);
--
-- if (group->mute_control_binding) {
-- pa_binding_free(group->mute_control_binding);
-- group->mute_control_binding = NULL;
-- }
--
-- set_mute_control_internal(group, control);
--}
--
--void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = group,
-- .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
-- };
--
-- pa_assert(group);
-- pa_assert(target_info);
--
-- if (group->volume_control_binding)
-- pa_binding_free(group->volume_control_binding);
--
-- group->volume_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
--}
--
--void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = group,
-- .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
-- };
--
-- pa_assert(group);
-- pa_assert(target_info);
--
-- if (group->mute_control_binding)
-- pa_binding_free(group->mute_control_binding);
--
-- group->mute_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
--}
--
- void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream) {
- pa_assert(group);
- pa_assert(stream);
-
- pa_assert_se(pa_hashmap_put(group->volume_streams, stream, stream) >= 0);
--
-- if (stream->own_volume_control && group->own_volume_control)
-- pa_volume_control_set_volume(stream->own_volume_control, &group->own_volume_control->volume, true, true);
--
-- pa_log_debug("Stream %s added to audio group %s (volume).", stream->name, group->name);
- }
-
- void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream) {
-@@ -405,8 +215,6 @@ void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stre
- pa_assert(stream);
-
- pa_assert_se(pa_hashmap_remove(group->volume_streams, stream));
--
-- pa_log_debug("Stream %s removed from audio group %s (volume).", stream->name, group->name);
- }
-
- void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
-@@ -414,11 +222,6 @@ void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
- pa_assert(stream);
-
- pa_assert_se(pa_hashmap_put(group->mute_streams, stream, stream) >= 0);
--
-- if (stream->own_mute_control && group->own_mute_control)
-- pa_mute_control_set_mute(stream->own_mute_control, group->own_mute_control->mute);
--
-- pa_log_debug("Stream %s added to audio group %s (mute).", stream->name, group->name);
- }
-
- void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream) {
-@@ -426,23 +229,4 @@ void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream
- pa_assert(stream);
-
- pa_assert_se(pa_hashmap_remove(group->mute_streams, stream));
--
-- pa_log_debug("Stream %s removed from audio group %s (mute).", stream->name, group->name);
--}
--
--pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api) {
-- pa_binding_target_type *type;
--
-- pa_assert(api);
--
-- type = pa_binding_target_type_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, api->audio_groups,
-- &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT],
-- &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
-- (pa_binding_target_type_get_name_cb_t) pa_audio_group_get_name);
-- pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, volume_control));
-- pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL,
-- PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, mute_control));
--
-- return type;
- }
-diff --git a/src/modules/volume-api/audio-group.h b/src/modules/volume-api/audio-group.h
-index 41591ba..02db3eb 100644
---- a/src/modules/volume-api/audio-group.h
-+++ b/src/modules/volume-api/audio-group.h
-@@ -22,7 +22,6 @@
- USA.
- ***/
-
--#include <modules/volume-api/binding.h>
- #include <modules/volume-api/mute-control.h>
- #include <modules/volume-api/volume-control.h>
-
-@@ -32,10 +31,6 @@
-
- typedef struct pa_audio_group pa_audio_group;
-
--#define PA_AUDIO_GROUP_BINDING_TARGET_TYPE "AudioGroup"
--#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL "volume_control"
--#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL "mute_control"
--
- struct pa_audio_group {
- pa_volume_api *volume_api;
- uint32_t index;
-@@ -44,13 +39,7 @@ struct pa_audio_group {
- pa_proplist *proplist;
- pa_volume_control *volume_control;
- pa_mute_control *mute_control;
-- bool have_own_volume_control;
-- bool have_own_mute_control;
-- pa_volume_control *own_volume_control;
-- pa_mute_control *own_mute_control;
-
-- pa_binding *volume_control_binding;
-- pa_binding *mute_control_binding;
- pa_hashmap *volume_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
- pa_hashmap *mute_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
-
-@@ -58,28 +47,22 @@ struct pa_audio_group {
- bool unlinked;
- };
-
--int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group);
-+int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r);
- void pa_audio_group_put(pa_audio_group *group);
- void pa_audio_group_unlink(pa_audio_group *group);
- void pa_audio_group_free(pa_audio_group *group);
-
--const char *pa_audio_group_get_name(pa_audio_group *group);
--
--/* Called by policy modules. */
--void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have);
--void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have);
-+/* Called by the audio group implementation. */
-+void pa_audio_group_set_description(pa_audio_group *group, const char *description);
- void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control);
- void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control);
--void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info);
--void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info);
-
--/* Called from sstream.c only. */
-+/* Called by sstream.c only. If you want to assign a stream to an audio group, use
-+ * pas_stream_set_audio_group_for_volume() and
-+ * pas_stream_set_audio_group_for_mute(). */
- void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream);
- void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream);
- void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream);
- void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream);
-
--/* Called from volume-api.c only. */
--pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api);
--
- #endif
-diff --git a/src/modules/volume-api/binding.c b/src/modules/volume-api/binding.c
-deleted file mode 100644
-index 6e73119..0000000
---- a/src/modules/volume-api/binding.c
-+++ /dev/null
-@@ -1,386 +0,0 @@
--/***
-- This file is part of PulseAudio.
--
-- Copyright 2014 Intel Corporation
--
-- 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.1 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 "binding.h"
--
--#include <pulse/def.h>
--#include <pulse/xmalloc.h>
--
--#include <pulsecore/core-util.h>
--#include <pulsecore/macro.h>
--
--struct field_entry {
-- char *name;
-- size_t offset;
--};
--
--static void set_target_type(pa_binding *binding, pa_binding_target_type *type);
--static void set_target_object(pa_binding *binding, void *object);
--
--pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata) {
-- pa_binding_owner_info *info;
--
-- pa_assert(set_value);
--
-- info = pa_xnew0(pa_binding_owner_info, 1);
-- info->set_value = set_value;
-- info->userdata = userdata;
--
-- return info;
--}
--
--pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info) {
-- pa_assert(info);
--
-- return pa_binding_owner_info_new(info->set_value, info->userdata);
--}
--
--void pa_binding_owner_info_free(pa_binding_owner_info *info) {
-- pa_assert(info);
--
-- pa_xfree(info);
--}
--
--pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field) {
-- pa_binding_target_info *info;
--
-- pa_assert(type);
-- pa_assert(name);
-- pa_assert(field);
--
-- info = pa_xnew0(pa_binding_target_info, 1);
-- info->type = pa_xstrdup(type);
-- info->name = pa_xstrdup(name);
-- info->field = pa_xstrdup(field);
--
-- return info;
--}
--
--int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info) {
-- const char *colon;
-- char *type = NULL;
-- char *name = NULL;
--
-- pa_assert(str);
-- pa_assert(field);
-- pa_assert(info);
--
-- if (!pa_startswith(str, "bind:"))
-- goto fail;
--
-- colon = strchr(str + 5, ':');
-- if (!colon)
-- goto fail;
--
-- type = pa_xstrndup(str + 5, colon - (str + 5));
--
-- if (!*type)
-- goto fail;
--
-- name = pa_xstrdup(colon + 1);
--
-- if (!*name)
-- goto fail;
--
-- *info = pa_binding_target_info_new(type, name, field);
-- pa_xfree(name);
-- pa_xfree(type);
--
-- return 0;
--
--fail:
-- pa_log("Invalid binding target: %s", str);
-- pa_xfree(name);
-- pa_xfree(type);
--
-- return -PA_ERR_INVALID;
--}
--
--pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info) {
-- pa_assert(info);
--
-- return pa_binding_target_info_new(info->type, info->name, info->field);
--}
--
--void pa_binding_target_info_free(pa_binding_target_info *info) {
-- pa_assert(info);
--
-- pa_xfree(info->field);
-- pa_xfree(info->name);
-- pa_xfree(info->type);
-- pa_xfree(info);
--}
--
--static void field_entry_free(struct field_entry *entry) {
-- pa_assert(entry);
--
-- pa_xfree(entry->name);
-- pa_xfree(entry);
--}
--
--pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
-- pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name) {
-- pa_binding_target_type *type;
--
-- pa_assert(name);
-- pa_assert(objects);
-- pa_assert(put_hook);
-- pa_assert(unlink_hook);
-- pa_assert(get_name);
--
-- type = pa_xnew0(pa_binding_target_type, 1);
-- type->name = pa_xstrdup(name);
-- type->objects = objects;
-- type->put_hook = put_hook;
-- type->unlink_hook = unlink_hook;
-- type->get_name = get_name;
-- type->fields = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) field_entry_free);
--
-- return type;
--}
--
--void pa_binding_target_type_free(pa_binding_target_type *type) {
-- pa_assert(type);
--
-- if (type->fields)
-- pa_hashmap_free(type->fields);
--
-- pa_xfree(type->name);
-- pa_xfree(type);
--}
--
--void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset) {
-- struct field_entry *entry;
--
-- pa_assert(type);
-- pa_assert(name);
--
-- entry = pa_xnew0(struct field_entry, 1);
-- entry->name = pa_xstrdup(name);
-- entry->offset = offset;
--
-- pa_assert_se(pa_hashmap_put(type->fields, entry->name, entry) >= 0);
--}
--
--int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset) {
-- struct field_entry *entry;
--
-- pa_assert(type);
-- pa_assert(field);
-- pa_assert(offset);
--
-- entry = pa_hashmap_get(type->fields, field);
-- if (!entry)
-- return -PA_ERR_NOENTITY;
--
-- *offset = entry->offset;
--
-- return 0;
--}
--
--static pa_hook_result_t target_type_added_cb(void *hook_data, void *call_data, void *userdata) {
-- pa_binding_target_type *type = call_data;
-- pa_binding *binding = userdata;
--
-- pa_assert(type);
-- pa_assert(binding);
--
-- if (!pa_streq(type->name, binding->target_info->type))
-- return PA_HOOK_OK;
--
-- set_target_type(binding, type);
--
-- return PA_HOOK_OK;
--}
--
--static pa_hook_result_t target_type_removed_cb(void *hook_data, void *call_data, void *userdata) {
-- pa_binding_target_type *type = call_data;
-- pa_binding *binding = userdata;
--
-- pa_assert(type);
-- pa_assert(binding);
--
-- if (type != binding->target_type)
-- return PA_HOOK_OK;
--
-- set_target_type(binding, NULL);
--
-- return PA_HOOK_OK;
--}
--
--static pa_hook_result_t target_put_cb(void *hook_data, void *call_data, void *userdata) {
-- pa_binding *binding = userdata;
--
-- pa_assert(call_data);
-- pa_assert(binding);
--
-- if (!pa_streq(binding->target_type->get_name(call_data), binding->target_info->name))
-- return PA_HOOK_OK;
--
-- set_target_object(binding, call_data);
--
-- return PA_HOOK_OK;
--}
--
--static pa_hook_result_t target_unlink_cb(void *hook_data, void *call_data, void *userdata) {
-- pa_binding *binding = userdata;
--
-- pa_assert(call_data);
-- pa_assert(binding);
--
-- if (call_data != binding->target_object)
-- return PA_HOOK_OK;
--
-- set_target_object(binding, NULL);
--
-- return PA_HOOK_OK;
--}
--
--static void set_target_object(pa_binding *binding, void *object) {
-- pa_assert(binding);
--
-- binding->target_object = object;
--
-- if (object) {
-- if (binding->target_put_slot) {
-- pa_hook_slot_free(binding->target_put_slot);
-- binding->target_put_slot = NULL;
-- }
--
-- if (!binding->target_unlink_slot)
-- binding->target_unlink_slot = pa_hook_connect(binding->target_type->unlink_hook, PA_HOOK_NORMAL, target_unlink_cb,
-- binding);
--
-- if (binding->target_field_offset_valid)
-- binding->owner_info->set_value(binding->owner_info->userdata,
-- *((void **) (((uint8_t *) object) + binding->target_field_offset)));
-- else
-- binding->owner_info->set_value(binding->owner_info->userdata, NULL);
-- } else {
-- if (binding->target_unlink_slot) {
-- pa_hook_slot_free(binding->target_unlink_slot);
-- binding->target_unlink_slot = NULL;
-- }
--
-- if (binding->target_type) {
-- if (!binding->target_put_slot)
-- binding->target_put_slot = pa_hook_connect(binding->target_type->put_hook, PA_HOOK_NORMAL, target_put_cb, binding);
-- } else {
-- if (binding->target_put_slot) {
-- pa_hook_slot_free(binding->target_put_slot);
-- binding->target_put_slot = NULL;
-- }
-- }
--
-- binding->owner_info->set_value(binding->owner_info->userdata, NULL);
-- }
--}
--
--static void set_target_type(pa_binding *binding, pa_binding_target_type *type) {
-- pa_assert(binding);
--
-- binding->target_type = type;
--
-- if (type) {
-- int r;
--
-- if (binding->target_type_added_slot) {
-- pa_hook_slot_free(binding->target_type_added_slot);
-- binding->target_type_added_slot = NULL;
-- }
--
-- if (!binding->target_type_removed_slot)
-- binding->target_type_removed_slot =
-- pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED],
-- PA_HOOK_NORMAL, target_type_removed_cb, binding);
--
-- r = pa_binding_target_type_get_field_offset(type, binding->target_info->field, &binding->target_field_offset);
-- if (r >= 0)
-- binding->target_field_offset_valid = true;
-- else {
-- pa_log_warn("Reference to non-existing field \"%s\" in binding target type \"%s\".", binding->target_info->field,
-- type->name);
-- binding->target_field_offset_valid = false;
-- }
--
-- set_target_object(binding, pa_hashmap_get(type->objects, binding->target_info->name));
-- } else {
-- if (binding->target_type_removed_slot) {
-- pa_hook_slot_free(binding->target_type_removed_slot);
-- binding->target_type_removed_slot = NULL;
-- }
--
-- if (!binding->target_type_added_slot)
-- binding->target_type_added_slot =
-- pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED],
-- PA_HOOK_NORMAL, target_type_added_cb, binding);
--
-- binding->target_field_offset_valid = false;
--
-- set_target_object(binding, NULL);
-- }
--}
--
--pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
-- const pa_binding_target_info *target_info) {
-- pa_binding *binding;
--
-- pa_assert(api);
-- pa_assert(owner_info);
-- pa_assert(target_info);
--
-- binding = pa_xnew0(pa_binding, 1);
-- binding->volume_api = api;
-- binding->owner_info = pa_binding_owner_info_copy(owner_info);
-- binding->target_info = pa_binding_target_info_copy(target_info);
--
-- set_target_type(binding, pa_hashmap_get(api->binding_target_types, target_info->type));
--
-- return binding;
--}
--
--void pa_binding_free(pa_binding *binding) {
-- pa_assert(binding);
--
-- if (binding->target_unlink_slot)
-- pa_hook_slot_free(binding->target_unlink_slot);
--
-- if (binding->target_put_slot)
-- pa_hook_slot_free(binding->target_put_slot);
--
-- if (binding->target_type_removed_slot)
-- pa_hook_slot_free(binding->target_type_removed_slot);
--
-- if (binding->target_type_added_slot)
-- pa_hook_slot_free(binding->target_type_added_slot);
--
-- if (binding->target_info)
-- pa_binding_target_info_free(binding->target_info);
--
-- if (binding->owner_info)
-- pa_binding_owner_info_free(binding->owner_info);
--
-- pa_xfree(binding);
--}
-diff --git a/src/modules/volume-api/binding.h b/src/modules/volume-api/binding.h
-deleted file mode 100644
-index ba4dea8..0000000
---- a/src/modules/volume-api/binding.h
-+++ /dev/null
-@@ -1,128 +0,0 @@
--#ifndef foobindinghfoo
--#define foobindinghfoo
--
--/***
-- This file is part of PulseAudio.
--
-- Copyright 2014 Intel Corporation
--
-- 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.1 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 <modules/volume-api/volume-api.h>
--
--typedef struct pa_binding pa_binding;
--typedef struct pa_binding_owner_info pa_binding_owner_info;
--typedef struct pa_binding_target_info pa_binding_target_info;
--typedef struct pa_binding_target_type pa_binding_target_type;
--
--typedef void (*pa_binding_set_value_cb_t)(void *userdata, void *value);
--
--struct pa_binding_owner_info {
-- /* This is the object that has the variable that the binding is created
-- * for. */
-- void *userdata;
--
-- /* Called when the owner object's value needs to be updated. The userdata
-- * parameter of the callback is the same as the userdata field in this
-- * struct, and the value parameter is the new value for whatever variable
-- * the binding was created for. */
-- pa_binding_set_value_cb_t set_value;
--};
--
--pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata);
--pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info);
--void pa_binding_owner_info_free(pa_binding_owner_info *info);
--
--struct pa_binding_target_info {
-- /* The target type name as registered with
-- * pa_binding_target_type_register(). */
-- char *type;
--
-- /* The target object name as returned by the get_name callback of
-- * pa_binding_target_type. */
-- char *name;
--
-- /* The target field of the target object. */
-- char *field;
--};
--
--pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field);
--
--/* The string format is "bind:TYPE:NAME". */
--int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info);
--
--pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info);
--void pa_binding_target_info_free(pa_binding_target_info *info);
--
--typedef const char *(*pa_binding_target_type_get_name_cb_t)(void *object);
--
--struct pa_binding_target_type {
-- /* Identifier for this target type. */
-- char *name;
--
-- /* name -> object. Points directly to some "master" object hashmap, so the
-- * hashmap is not owned by pa_binding_target_type. */
-- pa_hashmap *objects;
--
-- /* The hook that notifies of new objects if this target type. The call data
-- * of the hook must be a pointer to the new object (this should be true for
-- * all PUT hooks, so don't worry too much). */
-- pa_hook *put_hook;
--
-- /* The hook that notifies of unlinked objects of this target type. The call
-- * data of the hook must be a pointer to the removed object (this should be
-- * true for all UNLINK hooks, so don't worry too much). */
-- pa_hook *unlink_hook;
--
-- /* Function for getting the name of an object of this target type. */
-- pa_binding_target_type_get_name_cb_t get_name;
--
-- pa_hashmap *fields;
--};
--
--pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
-- pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name);
--void pa_binding_target_type_free(pa_binding_target_type *type);
--
--/* Useful when calling pa_binding_target_type_add_field(). */
--#define PA_BINDING_CALCULATE_FIELD_OFFSET(type, field) ((size_t) &(((type *) 0)->field))
--
--/* Called during the type initialization (right after
-- * pa_binding_target_type_new()). */
--void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset);
--
--int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset);
--
--struct pa_binding {
-- pa_volume_api *volume_api;
-- pa_binding_owner_info *owner_info;
-- pa_binding_target_info *target_info;
-- pa_binding_target_type *target_type;
-- void *target_object;
-- size_t target_field_offset;
-- bool target_field_offset_valid;
-- pa_hook_slot *target_type_added_slot;
-- pa_hook_slot *target_type_removed_slot;
-- pa_hook_slot *target_put_slot;
-- pa_hook_slot *target_unlink_slot;
--};
--
--pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
-- const pa_binding_target_info *target_info);
--void pa_binding_free(pa_binding *binding);
--
--#endif
-diff --git a/src/modules/volume-api/bvolume.h b/src/modules/volume-api/bvolume.h
-index 0317fb6..75545dd 100644
---- a/src/modules/volume-api/bvolume.h
-+++ b/src/modules/volume-api/bvolume.h
-@@ -29,13 +29,16 @@ typedef pa_ext_volume_api_bvolume pa_bvolume;
- #define pa_balance_valid pa_ext_volume_api_balance_valid
- #define pa_bvolume_valid pa_ext_volume_api_bvolume_valid
- #define pa_bvolume_init_invalid pa_ext_volume_api_bvolume_init_invalid
-+#define pa_bvolume_init pa_ext_volume_api_bvolume_init
- #define pa_bvolume_init_mono pa_ext_volume_api_bvolume_init_mono
-+#define pa_bvolume_parse_balance pa_ext_volume_api_bvolume_parse_balance
- #define pa_bvolume_equal pa_ext_volume_api_bvolume_equal
- #define pa_bvolume_from_cvolume pa_ext_volume_api_bvolume_from_cvolume
- #define pa_bvolume_to_cvolume pa_ext_volume_api_bvolume_to_cvolume
- #define pa_bvolume_copy_balance pa_ext_volume_api_bvolume_copy_balance
- #define pa_bvolume_reset_balance pa_ext_volume_api_bvolume_reset_balance
- #define pa_bvolume_remap pa_ext_volume_api_bvolume_remap
-+#define pa_bvolume_balance_to_string pa_ext_volume_api_bvolume_balance_to_string
-
- #define PA_BVOLUME_SNPRINT_BALANCE_MAX PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX
- #define pa_bvolume_snprint_balance pa_ext_volume_api_bvolume_snprint_balance
-diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c
-index f35fab0..fc486f8 100644
---- a/src/modules/volume-api/device-creator.c
-+++ b/src/modules/volume-api/device-creator.c
-@@ -59,6 +59,8 @@ struct device_volume_control {
- pa_hook_slot *volume_changed_slot;
- };
-
-+static void device_volume_control_free(struct device_volume_control *control);
-+
- struct device_mute_control {
- struct device *device;
- pa_mute_control *mute_control;
-@@ -68,6 +70,8 @@ struct device_mute_control {
- pa_hook_slot *mute_changed_slot;
- };
-
-+static void device_mute_control_free(struct device_mute_control *control);
-+
- struct device {
- pa_device_creator *creator;
- enum device_type type;
-@@ -85,6 +89,8 @@ struct device {
- struct device *monitor;
- };
-
-+static void device_free(struct device *device);
-+
- static const char *device_type_from_icon_name(const char *icon_name) {
- if (!icon_name)
- return NULL;
-@@ -168,112 +174,6 @@ static const char *get_source_description(pa_source *source) {
- return source->name;
- }
-
--static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *volume, bool set_volume, bool set_balance) {
-- struct device_volume_control *control;
-- struct device *device;
-- pa_bvolume bvolume;
-- pa_cvolume cvolume;
--
-- pa_assert(c);
-- pa_assert(volume);
--
-- control = c->userdata;
-- device = control->device;
--
-- switch (device->type) {
-- case DEVICE_TYPE_PORT:
-- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
-- else
-- pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
-- break;
--
-- case DEVICE_TYPE_SINK:
-- pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
-- break;
--
-- case DEVICE_TYPE_PORT_MONITOR:
-- case DEVICE_TYPE_SOURCE:
-- pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
-- break;
-- }
--
-- if (set_volume)
-- bvolume.volume = volume->volume;
--
-- if (set_balance)
-- pa_bvolume_copy_balance(&bvolume, volume);
--
-- pa_bvolume_to_cvolume(&bvolume, &cvolume);
--
-- switch (device->type) {
-- case DEVICE_TYPE_PORT:
-- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- pa_sink_set_volume(device->sink, &cvolume, true, true);
-- else
-- pa_source_set_volume(device->source, &cvolume, true, true);
-- break;
--
-- case DEVICE_TYPE_PORT_MONITOR:
-- case DEVICE_TYPE_SOURCE:
-- pa_source_set_volume(device->source, &cvolume, true, true);
-- break;
--
-- case DEVICE_TYPE_SINK:
-- pa_sink_set_volume(device->sink, &cvolume, true, true);
-- break;
-- }
--
-- return 0;
--}
--
--static struct device_volume_control *device_volume_control_new(struct device *device) {
-- struct device_volume_control *control;
-- const char *name = NULL;
-- bool convertible_to_dB = false;
-- bool channel_map_is_writable;
--
-- pa_assert(device);
--
-- control = pa_xnew0(struct device_volume_control, 1);
-- control->device = device;
--
-- switch (device->type) {
-- case DEVICE_TYPE_PORT:
-- name = "port-volume-control";
--
-- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
-- else
-- convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
--
-- break;
--
-- case DEVICE_TYPE_PORT_MONITOR:
-- name = "port-monitor-volume-control";
-- convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
-- break;
--
-- case DEVICE_TYPE_SINK:
-- name = "sink-volume-control";
-- convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
-- break;
--
-- case DEVICE_TYPE_SOURCE:
-- name = "source-volume-control";
-- convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
-- break;
-- }
--
-- channel_map_is_writable = false;
-- control->volume_control = pa_volume_control_new(device->creator->volume_api, name, device->device->description,
-- convertible_to_dB, channel_map_is_writable);
-- control->volume_control->set_volume = volume_control_set_volume_cb;
-- control->volume_control->userdata = control;
--
-- return control;
--}
--
- static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
- struct device_volume_control *control = userdata;
- struct device *device;
-@@ -309,24 +209,55 @@ static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *
-
- if (sink)
- pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map);
-- else
-+ else if (source)
- pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map);
-+ else
-+ pa_assert_not_reached();
-
-- pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true);
-+ pa_volume_control_set_volume(control->volume_control, &bvolume, true, true);
-
- return PA_HOOK_OK;
- }
-
--static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
-+static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *original_volume,
-+ const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
- struct device_volume_control *control;
- struct device *device;
-+ pa_bvolume bvolume;
- pa_cvolume cvolume;
-
- pa_assert(c);
-+ pa_assert(original_volume);
-+ pa_assert(remapped_volume);
-
- control = c->userdata;
- device = control->device;
-- pa_bvolume_to_cvolume(&control->volume_control->volume, &cvolume);
-+
-+ switch (device->type) {
-+ case DEVICE_TYPE_PORT:
-+ if (device->port->direction == PA_DIRECTION_OUTPUT)
-+ pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
-+ else
-+ pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
-+ break;
-+
-+ case DEVICE_TYPE_SINK:
-+ pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
-+ break;
-+
-+ case DEVICE_TYPE_PORT_MONITOR:
-+ case DEVICE_TYPE_SOURCE:
-+ pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
-+ break;
-+ }
-+
-+ if (set_volume)
-+ bvolume.volume = remapped_volume->volume;
-+
-+ if (set_balance)
-+ pa_bvolume_copy_balance(&bvolume, remapped_volume);
-+
-+ pa_bvolume_to_cvolume(&bvolume, &cvolume);
-
- switch (device->type) {
- case DEVICE_TYPE_PORT:
-@@ -345,44 +276,91 @@ static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
- pa_sink_set_volume(device->sink, &cvolume, true, true);
- break;
- }
-+
-+ return 0;
- }
-
--static void device_volume_control_put(struct device_volume_control *control) {
-- struct device *device;
-+static int device_volume_control_new(struct device *device, struct device_volume_control **_r) {
-+ struct device_volume_control *control = NULL;
-+ const char *name = NULL;
- pa_bvolume volume;
-+ bool convertible_to_dB = false;
-+ int r;
-
-- pa_assert(control);
-+ pa_assert(device);
-+ pa_assert(_r);
-
-- device = control->device;
-+ control = pa_xnew0(struct device_volume_control, 1);
-+ control->device = device;
-
- switch (device->type) {
- case DEVICE_TYPE_PORT:
-+ name = "port-volume-control";
-+
- if (device->port->direction == PA_DIRECTION_OUTPUT) {
- control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
- pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
-+ convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
- } else {
- control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
- pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
-+ convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
- }
-+
- break;
-
- case DEVICE_TYPE_PORT_MONITOR:
-- case DEVICE_TYPE_SOURCE:
-+ name = "port-monitor-volume-control";
- control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
- pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
-+ convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
- break;
-
- case DEVICE_TYPE_SINK:
-+ name = "sink-volume-control";
- control->volume_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
- pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
-+ convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
-+ break;
-+
-+ case DEVICE_TYPE_SOURCE:
-+ name = "source-volume-control";
-+ control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
-+ PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
-+ pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
-+ convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
- break;
- }
-
-- pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb);
-+ r = pa_volume_control_new(device->creator->volume_api, name, false, &control->volume_control);
-+ if (r < 0)
-+ goto fail;
-+
-+ pa_volume_control_set_description(control->volume_control, device->device->description);
-+ pa_volume_control_set_channel_map(control->volume_control, &volume.channel_map);
-+ pa_volume_control_set_volume(control->volume_control, &volume, true, true);
-+ pa_volume_control_set_convertible_to_dB(control->volume_control, convertible_to_dB);
-+ control->volume_control->set_volume = volume_control_set_volume_cb;
-+ control->volume_control->userdata = control;
-+
-+ *_r = control;
-+ return 0;
-+
-+fail:
-+ if (control)
-+ device_volume_control_free(control);
-+
-+ return r;
-+}
-+
-+static void device_volume_control_put(struct device_volume_control *control) {
-+ pa_assert(control);
-+
-+ pa_volume_control_put(control->volume_control);
- }
-
- static void device_volume_control_unlink(struct device_volume_control *control) {
-@@ -395,11 +373,6 @@ static void device_volume_control_unlink(struct device_volume_control *control)
-
- if (control->volume_control)
- pa_volume_control_unlink(control->volume_control);
--
-- if (control->volume_changed_slot) {
-- pa_hook_slot_free(control->volume_changed_slot);
-- control->volume_changed_slot = NULL;
-- }
- }
-
- static void device_volume_control_free(struct device_volume_control *control) {
-@@ -411,71 +384,10 @@ static void device_volume_control_free(struct device_volume_control *control) {
- if (control->volume_control)
- pa_volume_control_free(control->volume_control);
-
-- pa_xfree(control);
--}
--
--static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
-- struct device_mute_control *control;
-- struct device *device;
--
-- pa_assert(c);
--
-- control = c->userdata;
-- device = control->device;
--
-- switch (device->type) {
-- case DEVICE_TYPE_PORT:
-- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- pa_sink_set_mute(device->sink, mute, true);
-- else
-- pa_source_set_mute(device->source, mute, true);
-- break;
--
-- case DEVICE_TYPE_PORT_MONITOR:
-- case DEVICE_TYPE_SOURCE:
-- pa_source_set_mute(device->source, mute, true);
-- break;
--
-- case DEVICE_TYPE_SINK:
-- pa_sink_set_mute(device->sink, mute, true);
-- break;
-- }
--
-- return 0;
--}
--
--static struct device_mute_control *device_mute_control_new(struct device *device) {
-- struct device_mute_control *control;
-- const char *name = NULL;
--
-- pa_assert(device);
--
-- control = pa_xnew0(struct device_mute_control, 1);
-- control->device = device;
--
-- switch (device->type) {
-- case DEVICE_TYPE_PORT:
-- name = "port-mute-control";
-- break;
--
-- case DEVICE_TYPE_PORT_MONITOR:
-- name = "port-monitor-mute-control";
-- break;
--
-- case DEVICE_TYPE_SINK:
-- name = "sink-mute-control";
-- break;
--
-- case DEVICE_TYPE_SOURCE:
-- name = "source-mute-control";
-- break;
-- }
--
-- control->mute_control = pa_mute_control_new(device->creator->volume_api, name, device->device->description);
-- control->mute_control->set_mute = mute_control_set_mute_cb;
-- control->mute_control->userdata = control;
-+ if (control->volume_changed_slot)
-+ pa_hook_slot_free(control->volume_changed_slot);
-
-- return control;
-+ pa_xfree(control);
- }
-
- static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
-@@ -518,13 +430,13 @@ static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *ca
- else
- pa_assert_not_reached();
-
-- pa_mute_control_mute_changed(control->mute_control, mute);
-+ pa_mute_control_set_mute(control->mute_control, mute);
-
- return PA_HOOK_OK;
- }
-
--static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
-- struct device_volume_control *control;
-+static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
-+ struct device_mute_control *control;
- struct device *device;
-
- pa_assert(c);
-@@ -535,32 +447,40 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
- switch (device->type) {
- case DEVICE_TYPE_PORT:
- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- pa_sink_set_mute(device->sink, c->mute, true);
-+ pa_sink_set_mute(device->sink, mute, true);
- else
-- pa_source_set_mute(device->source, c->mute, true);
-+ pa_source_set_mute(device->source, mute, true);
- break;
-
- case DEVICE_TYPE_PORT_MONITOR:
- case DEVICE_TYPE_SOURCE:
-- pa_source_set_mute(device->source, c->mute, true);
-+ pa_source_set_mute(device->source, mute, true);
- break;
-
- case DEVICE_TYPE_SINK:
-- pa_sink_set_mute(device->sink, c->mute, true);
-+ pa_sink_set_mute(device->sink, mute, true);
- break;
- }
-+
-+ return 0;
- }
-
--static void device_mute_control_put(struct device_mute_control *control) {
-- struct device *device;
-+static int device_mute_control_new(struct device *device, struct device_mute_control **_r) {
-+ struct device_mute_control *control = NULL;
-+ const char *name = NULL;
- bool mute = false;
-+ int r;
-
-- pa_assert(control);
-+ pa_assert(device);
-+ pa_assert(_r);
-
-- device = control->device;
-+ control = pa_xnew0(struct device_mute_control, 1);
-+ control->device = device;
-
- switch (device->type) {
- case DEVICE_TYPE_PORT:
-+ name = "port-mute-control";
-+
- if (device->port->direction == PA_DIRECTION_OUTPUT) {
- control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
-@@ -573,20 +493,50 @@ static void device_mute_control_put(struct device_mute_control *control) {
- break;
-
- case DEVICE_TYPE_PORT_MONITOR:
-- case DEVICE_TYPE_SOURCE:
-- control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
-+ name = "port-monitor-mute-control";
-+ control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
- mute = device->source->muted;
- break;
-
- case DEVICE_TYPE_SINK:
-+ name = "sink-mute-control";
- control->mute_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
- PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
- mute = device->sink->muted;
- break;
-+
-+ case DEVICE_TYPE_SOURCE:
-+ name = "source-mute-control";
-+ control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
-+ PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
-+ mute = device->source->muted;
-+ break;
- }
-
-- pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb);
-+ r = pa_mute_control_new(device->creator->volume_api, name, false, &control->mute_control);
-+ if (r < 0)
-+ goto fail;
-+
-+ pa_mute_control_set_description(control->mute_control, device->device->description);
-+ pa_mute_control_set_mute(control->mute_control, mute);
-+ control->mute_control->set_mute = mute_control_set_mute_cb;
-+ control->mute_control->userdata = control;
-+
-+ *_r = control;
-+ return 0;
-+
-+fail:
-+ if (control)
-+ device_mute_control_free(control);
-+
-+ return r;
-+}
-+
-+static void device_mute_control_put(struct device_mute_control *control) {
-+ pa_assert(control);
-+
-+ pa_mute_control_put(control->mute_control);
- }
-
- static void device_mute_control_unlink(struct device_mute_control *control) {
-@@ -599,11 +549,6 @@ static void device_mute_control_unlink(struct device_mute_control *control) {
-
- if (control->mute_control)
- pa_mute_control_unlink(control->mute_control);
--
-- if (control->mute_changed_slot) {
-- pa_hook_slot_free(control->mute_changed_slot);
-- control->mute_changed_slot = NULL;
-- }
- }
-
- static void device_mute_control_free(struct device_mute_control *control) {
-@@ -615,6 +560,9 @@ static void device_mute_control_free(struct device_mute_control *control) {
- if (control->mute_control)
- pa_mute_control_free(control->mute_control);
-
-+ if (control->mute_changed_slot)
-+ pa_hook_slot_free(control->mute_changed_slot);
-+
- pa_xfree(control);
- }
-
-@@ -707,22 +655,24 @@ static pa_hook_result_t sink_or_source_proplist_changed_cb(void *hook_data, void
- }
-
- pa_device_description_changed(device->device, description);
-- pa_volume_control_description_changed(device->volume_control->volume_control, description);
-- pa_mute_control_description_changed(device->mute_control->mute_control, description);
-+ pa_volume_control_set_description(device->volume_control->volume_control, description);
-+ pa_mute_control_set_description(device->mute_control->mute_control, description);
-
- return PA_HOOK_OK;
- }
-
--static struct device *device_new(pa_device_creator *creator, enum device_type type, void *core_device) {
-+static int device_new(pa_device_creator *creator, enum device_type type, void *core_device, struct device **_r) {
- struct device *device = NULL;
- const char *name = NULL;
- char *description = NULL;
- pa_direction_t direction = PA_DIRECTION_OUTPUT;
- const char *device_type = NULL;
- bool create_volume_and_mute_controls = true;
-+ int r;
-
- pa_assert(creator);
- pa_assert(core_device);
-+ pa_assert(_r);
-
- device = pa_xnew0(struct device, 1);
- device->creator = creator;
-@@ -767,18 +717,20 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
- break;
- }
-
-- device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0);
-+ r = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0, &device->device);
- pa_xfree(description);
-+ if (r < 0)
-+ goto fail;
-
- if (create_volume_and_mute_controls) {
-- device->volume_control = device_volume_control_new(device);
-- device->mute_control = device_mute_control_new(device);
-+ device_volume_control_new(device, &device->volume_control);
-+ device_mute_control_new(device, &device->mute_control);
- }
-
- switch (type) {
- case DEVICE_TYPE_PORT:
- if (device->port->direction == PA_DIRECTION_OUTPUT)
-- device->monitor = device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port);
-+ device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port, &device->monitor);
- break;
-
- case DEVICE_TYPE_PORT_MONITOR:
-@@ -795,7 +747,14 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
- break;
- }
-
-- return device;
-+ *_r = device;
-+ return 0;
-+
-+fail:
-+ if (device)
-+ device_free(device);
-+
-+ return r;
- }
-
- static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data, void *userdata) {
-@@ -825,25 +784,36 @@ static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data,
- pa_assert_not_reached();
- }
-
-- if (should_have_volume_and_mute_controls && !device->volume_control) {
-- pa_assert(!device->mute_control);
-+ if (should_have_volume_and_mute_controls) {
-+ int r;
-
-- device->volume_control = device_volume_control_new(device);
-- device_volume_control_put(device->volume_control);
-- pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
-+ if (!device->volume_control) {
-+ r = device_volume_control_new(device, &device->volume_control);
-+ if (r >= 0) {
-+ device_volume_control_put(device->volume_control);
-+ pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
-+ }
-+ }
-
-- device->mute_control = device_mute_control_new(device);
-- device_mute_control_put(device->mute_control);
-- pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
-+ if (!device->mute_control) {
-+ r = device_mute_control_new(device, &device->mute_control);
-+ if (r >= 0) {
-+ device_mute_control_put(device->mute_control);
-+ pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
-+ }
-+ }
- }
-
-- if (!should_have_volume_and_mute_controls && device->volume_control) {
-- pa_assert(device->mute_control);
-+ if (!should_have_volume_and_mute_controls) {
-+ if (device->mute_control) {
-+ device_mute_control_free(device->mute_control);
-+ device->mute_control = NULL;
-+ }
-
-- device_mute_control_free(device->mute_control);
-- device->mute_control = NULL;
-- device_volume_control_free(device->volume_control);
-- device->volume_control = NULL;
-+ if (device->volume_control) {
-+ device_volume_control_free(device->volume_control);
-+ device->volume_control = NULL;
-+ }
- }
-
- return PA_HOOK_OK;
-@@ -928,6 +898,7 @@ static void device_free(struct device *device) {
-
- static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) {
- struct device *device;
-+ int r;
-
- pa_assert(creator);
- pa_assert(core_device);
-@@ -956,9 +927,11 @@ static void create_device(pa_device_creator *creator, enum device_type type, voi
- }
- }
-
-- device = device_new(creator, type, core_device);
-- pa_hashmap_put(creator->devices, core_device, device);
-- device_put(device);
-+ r = device_new(creator, type, core_device, &device);
-+ if (r >= 0) {
-+ pa_hashmap_put(creator->devices, core_device, device);
-+ device_put(device);
-+ }
- }
-
- static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *userdata) {
-diff --git a/src/modules/volume-api/device.c b/src/modules/volume-api/device.c
-index ea496ba..c1a580c 100644
---- a/src/modules/volume-api/device.c
-+++ b/src/modules/volume-api/device.c
-@@ -32,20 +32,26 @@
-
- #include <pulsecore/core-util.h>
-
--pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
-- const char * const *device_types, unsigned n_device_types) {
-- pa_device *device;
-+int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
-+ const char * const *device_types, unsigned n_device_types, pa_device **_r) {
-+ pa_device *device = NULL;
-+ int r;
- unsigned i;
-
- pa_assert(api);
- pa_assert(name);
- pa_assert(description);
- pa_assert(device_types || n_device_types == 0);
-+ pa_assert(_r);
-
- device = pa_xnew0(pa_device, 1);
- device->volume_api = api;
- device->index = pa_volume_api_allocate_device_index(api);
-- pa_assert_se(pa_volume_api_register_name(api, name, false, &device->name) >= 0);
-+
-+ r = pa_volume_api_register_name(api, name, false, &device->name);
-+ if (r < 0)
-+ goto fail;
-+
- device->description = pa_xstrdup(description);
- device->direction = direction;
- device->device_types = pa_dynarray_new(pa_xfree);
-@@ -57,7 +63,14 @@ pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *descr
- device->use_default_volume_control = true;
- device->use_default_mute_control = true;
-
-- return device;
-+ *_r = device;
-+ return 0;
-+
-+fail:
-+ if (device)
-+ pa_device_free(device);
-+
-+ return r;
- }
-
- void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control) {
-@@ -84,7 +97,6 @@ void pa_device_put(pa_device *device, pa_volume_control *default_volume_control,
- }
-
- pa_volume_api_add_device(device->volume_api, device);
--
- device->linked = true;
-
- device_types_str = pa_join((const char * const *) pa_dynarray_get_raw_array(device->device_types),
-@@ -120,35 +132,21 @@ void pa_device_unlink(pa_device *device) {
- pa_log_debug("Unlinking device %s.", device->name);
-
- if (device->linked)
-- pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
--
-- pa_volume_api_remove_device(device->volume_api, device);
-+ pa_volume_api_remove_device(device->volume_api, device);
-
-- if (device->mute_control) {
-- pa_mute_control_remove_device(device->mute_control, device);
-- device->mute_control = NULL;
-- }
--
-- if (device->default_mute_control) {
-- pa_mute_control_remove_default_for_device(device->default_mute_control, device);
-- device->default_mute_control = NULL;
-- }
-+ pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
-
-- if (device->volume_control) {
-- pa_volume_control_remove_device(device->volume_control, device);
-- device->volume_control = NULL;
-- }
--
-- if (device->default_volume_control) {
-- pa_volume_control_remove_default_for_device(device->default_volume_control, device);
-- device->default_volume_control = NULL;
-- }
-+ pa_device_set_mute_control(device, NULL);
-+ pa_device_set_default_mute_control(device, NULL);
-+ pa_device_set_volume_control(device, NULL);
-+ pa_device_set_default_volume_control(device, NULL);
- }
-
- void pa_device_free(pa_device *device) {
- pa_assert(device);
-
-- if (!device->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!device->unlinked && device->name)
- pa_device_unlink(device);
-
- if (device->proplist)
-diff --git a/src/modules/volume-api/device.h b/src/modules/volume-api/device.h
-index 9eac7e9..8bd5158 100644
---- a/src/modules/volume-api/device.h
-+++ b/src/modules/volume-api/device.h
-@@ -51,8 +51,8 @@ struct pa_device {
- bool unlinked;
- };
-
--pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
-- const char * const *device_types, unsigned n_device_types);
-+int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
-+ const char * const *device_types, unsigned n_device_types, pa_device **_r);
- void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control);
- void pa_device_unlink(pa_device *device);
- void pa_device_free(pa_device *device);
-diff --git a/src/modules/volume-api/inidb.c b/src/modules/volume-api/inidb.c
-new file mode 100644
-index 0000000..8116e72
---- /dev/null
-+++ b/src/modules/volume-api/inidb.c
-@@ -0,0 +1,553 @@
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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 "inidb.h"
-+
-+#include <pulse/mainloop-api.h>
-+#include <pulse/rtclock.h>
-+#include <pulse/timeval.h>
-+#include <pulse/xmalloc.h>
-+
-+#include <pulsecore/conf-parser.h>
-+#include <pulsecore/core-error.h>
-+#include <pulsecore/core-rtclock.h>
-+#include <pulsecore/core-util.h>
-+#include <pulsecore/hashmap.h>
-+#include <pulsecore/macro.h>
-+#include <pulsecore/namereg.h>
-+
-+#include <errno.h>
-+
-+#define SAVE_INTERVAL_USEC (10 * PA_USEC_PER_SEC)
-+
-+struct pa_inidb {
-+ pa_core *core;
-+ char *name;
-+ char *file_path;
-+ char *tmp_file_path;
-+ pa_hashmap *tables; /* table name -> pa_inidb_table */
-+ pa_time_event *time_event;
-+ bool failed;
-+ void *userdata;
-+};
-+
-+struct pa_inidb_table {
-+ pa_inidb *db;
-+ char *name;
-+ pa_hashmap *columns; /* column name -> column */
-+ pa_hashmap *rows; /* row id -> pa_inidb_row */
-+ pa_inidb_get_object_cb_t get_object;
-+};
-+
-+struct column {
-+ char *name;
-+ pa_inidb_parse_cb_t parse;
-+};
-+
-+struct pa_inidb_row {
-+ char *id;
-+ char *header;
-+ pa_hashmap *cells; /* column name -> cell */
-+};
-+
-+struct pa_inidb_cell {
-+ pa_inidb *db;
-+ struct column *column;
-+ char *value;
-+ char *assignment;
-+};
-+
-+static void save(pa_inidb *db);
-+
-+static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
-+static void table_free(pa_inidb_table *table);
-+static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id);
-+
-+static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb);
-+static void column_free(struct column *column);
-+
-+static pa_inidb_row *row_new(pa_inidb_table *table, const char *id);
-+static void row_free(pa_inidb_row *row);
-+
-+static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column);
-+static void cell_free(pa_inidb_cell *cell);
-+static void cell_set_value_internal(pa_inidb_cell *cell, const char *value);
-+
-+static int parse_assignment(pa_config_parser_state *state) {
-+ pa_inidb *db;
-+ const char *end_of_table_name;
-+ size_t table_name_len;
-+ char *table_name;
-+ pa_inidb_table *table;
-+ const char *row_id;
-+ pa_inidb_row *row;
-+ int r;
-+ void *object;
-+ struct column *column;
-+ pa_inidb_cell *cell;
-+
-+ pa_assert(state);
-+
-+ db = state->userdata;
-+
-+ /* FIXME: pa_config_parser should be improved so that it could parse the
-+ * table name and row id for us in the section header. */
-+ end_of_table_name = strchr(state->section, ' ');
-+ if (!end_of_table_name) {
-+ pa_log("[%s:%u] Failed to parse table name and row id in section \"%s\"", state->filename, state->lineno,
-+ state->section);
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ table_name_len = end_of_table_name - state->section;
-+ table_name = pa_xstrndup(state->section, table_name_len);
-+ table = pa_hashmap_get(db->tables, table_name);
-+ if (!table)
-+ pa_log("[%s:%u] Unknown table name: \"%s\"", state->filename, state->lineno, table_name);
-+ pa_xfree(table_name);
-+ if (!table)
-+ return -PA_ERR_INVALID;
-+
-+ row_id = end_of_table_name + 1;
-+ if (!pa_namereg_is_valid_name(row_id)) {
-+ pa_log("[%s:%u] Invalid row id: \"%s\"", state->filename, state->lineno, row_id);
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ /* This is not strictly necessary, but we do this to avoid saving the
-+ * database when there is no actual change. Without this, the get_object()
-+ * callback would cause redundant saving whenever creating new objects. */
-+ if (!(row = pa_hashmap_get(table->rows, row_id)))
-+ row = table_add_row_internal(table, row_id);
-+
-+ r = table->get_object(db, row_id, &object);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to create object %s.", state->filename, state->lineno, row_id);
-+ return r;
-+ }
-+
-+ column = pa_hashmap_get(table->columns, state->lvalue);
-+ if (!column) {
-+ pa_log("[%s:%u] Unknown column name: \"%s\"", state->filename, state->lineno, state->lvalue);
-+ return -PA_ERR_INVALID;
-+ }
-+
-+ /* This is not strictly necessary, but we do this to avoid saving the
-+ * database when there is no actual change. Without this, the parse()
-+ * callback would cause redundant saving whenever setting the cell value
-+ * for the first time. */
-+ cell = pa_hashmap_get(row->cells, column->name);
-+ cell_set_value_internal(cell, state->rvalue);
-+
-+ r = column->parse(db, state->rvalue, object);
-+ if (r < 0) {
-+ pa_log("[%s:%u] Failed to parse %s value \"%s\".", state->filename, state->lineno, column->name, state->rvalue);
-+ return r;
-+ }
-+
-+ return 0;
-+}
-+
-+pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata) {
-+ pa_inidb *db;
-+ int r;
-+
-+ pa_assert(core);
-+ pa_assert(name);
-+
-+ db = pa_xnew0(pa_inidb, 1);
-+ db->core = core;
-+ db->name = pa_xstrdup(name);
-+
-+ r = pa_append_to_config_home_dir(name, true, &db->file_path);
-+ if (r < 0) {
-+ pa_log("Failed to find the file location for database \"%s\". The database will start empty, and updates will not be "
-+ "saved on disk.", name);
-+ db->failed = true;
-+ }
-+
-+ if (db->file_path)
-+ db->tmp_file_path = pa_sprintf_malloc("%s.tmp", db->file_path);
-+
-+ db->tables = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) table_free);
-+ db->userdata = userdata;
-+
-+ return db;
-+}
-+
-+void pa_inidb_free(pa_inidb *db) {
-+ pa_assert(db);
-+
-+ if (db->time_event) {
-+ db->core->mainloop->time_free(db->time_event);
-+ save(db);
-+ }
-+
-+ if (db->tables)
-+ pa_hashmap_free(db->tables);
-+
-+ pa_xfree(db->tmp_file_path);
-+ pa_xfree(db->file_path);
-+ pa_xfree(db->name);
-+ pa_xfree(db);
-+}
-+
-+void *pa_inidb_get_userdata(pa_inidb *db) {
-+ pa_assert(db);
-+
-+ return db->userdata;
-+}
-+
-+pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
-+ pa_inidb_table *table;
-+
-+ pa_assert(db);
-+ pa_assert(name);
-+ pa_assert(get_object_cb);
-+
-+ table = table_new(db, name, get_object_cb);
-+ pa_assert_se(pa_hashmap_put(db->tables, table->name, table) >= 0);
-+
-+ return table;
-+}
-+
-+void pa_inidb_load(pa_inidb *db) {
-+ unsigned n_config_items;
-+ pa_inidb_table *table;
-+ void *state;
-+ pa_config_item *config_items;
-+ unsigned i;
-+
-+ pa_assert(db);
-+
-+ if (db->failed)
-+ return;
-+
-+ n_config_items = 0;
-+ PA_HASHMAP_FOREACH(table, db->tables, state)
-+ n_config_items += pa_hashmap_size(table->columns);
-+
-+ config_items = pa_xnew0(pa_config_item, n_config_items + 1);
-+
-+ i = 0;
-+ PA_HASHMAP_FOREACH(table, db->tables, state) {
-+ struct column *column;
-+ void *state2;
-+
-+ PA_HASHMAP_FOREACH(column, table->columns, state2) {
-+ config_items[i].lvalue = column->name;
-+ config_items[i].parse = parse_assignment;
-+ i++;
-+ }
-+ }
-+
-+ pa_config_parse(db->file_path, NULL, config_items, NULL, db);
-+ pa_xfree(config_items);
-+}
-+
-+static void save(pa_inidb *db) {
-+ FILE *f;
-+ pa_inidb_table *table;
-+ void *state;
-+ int r;
-+
-+ pa_assert(db);
-+
-+ if (db->failed)
-+ return;
-+
-+ f = pa_fopen_cloexec(db->tmp_file_path, "w");
-+ if (!f) {
-+ pa_log("pa_fopen_cloexec() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+
-+ PA_HASHMAP_FOREACH(table, db->tables, state) {
-+ pa_inidb_row *row;
-+ void *state2;
-+
-+ PA_HASHMAP_FOREACH(row, table->rows, state2) {
-+ size_t len;
-+ size_t items_written;
-+ pa_inidb_cell *cell;
-+ void *state3;
-+
-+ len = strlen(row->header);
-+ items_written = fwrite(row->header, len, 1, f);
-+
-+ if (items_written != 1) {
-+ pa_log("fwrite() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+
-+ PA_HASHMAP_FOREACH(cell, row->cells, state3) {
-+ if (!cell->assignment)
-+ continue;
-+
-+ len = strlen(cell->assignment);
-+ items_written = fwrite(cell->assignment, len, 1, f);
-+
-+ if (items_written != 1) {
-+ pa_log("fwrite() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+ }
-+
-+ items_written = fwrite("\n", 1, 1, f);
-+
-+ if (items_written != 1) {
-+ pa_log("fwrite() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+ }
-+ }
-+
-+ r = fclose(f);
-+ if (r < 0) {
-+ pa_log("fclose() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+
-+ r = rename(db->tmp_file_path, db->file_path);
-+ if (r < 0) {
-+ pa_log("rename() failed: %s", pa_cstrerror(errno));
-+ goto fail;
-+ }
-+
-+ pa_log_debug("Database \"%s\" saved.", db->name);
-+
-+ return;
-+
-+fail:
-+ if (f)
-+ fclose(f);
-+
-+ db->failed = true;
-+ pa_log("Saving database \"%s\" failed, current and future database changes will not be written to the disk.", db->name);
-+}
-+
-+static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
-+ pa_inidb_table *table;
-+
-+ pa_assert(db);
-+ pa_assert(name);
-+ pa_assert(get_object_cb);
-+
-+ table = pa_xnew0(pa_inidb_table, 1);
-+ table->db = db;
-+ table->name = pa_xstrdup(name);
-+ table->columns = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) column_free);
-+ table->rows = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) row_free);
-+ table->get_object = get_object_cb;
-+
-+ return table;
-+}
-+
-+static void table_free(pa_inidb_table *table) {
-+ pa_assert(table);
-+
-+ if (table->rows)
-+ pa_hashmap_free(table->rows);
-+
-+ if (table->columns)
-+ pa_hashmap_free(table->columns);
-+
-+ pa_xfree(table->name);
-+ pa_xfree(table);
-+}
-+
-+void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb) {
-+ struct column *column;
-+
-+ pa_assert(table);
-+ pa_assert(name);
-+ pa_assert(parse_cb);
-+
-+ column = column_new(name, parse_cb);
-+ pa_assert_se(pa_hashmap_put(table->columns, column->name, column) >= 0);
-+}
-+
-+static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id) {
-+ pa_inidb_row *row;
-+
-+ pa_assert(table);
-+ pa_assert(row_id);
-+
-+ row = row_new(table, row_id);
-+ pa_assert_se(pa_hashmap_put(table->rows, row->id, row) >= 0);
-+
-+ return row;
-+}
-+
-+static void time_cb(pa_mainloop_api *api, pa_time_event *event, const struct timeval *tv, void *userdata) {
-+ pa_inidb *db = userdata;
-+
-+ pa_assert(api);
-+ pa_assert(db);
-+
-+ api->time_free(event);
-+ db->time_event = NULL;
-+
-+ save(db);
-+}
-+
-+static void trigger_save(pa_inidb *db) {
-+ struct timeval tv;
-+
-+ pa_assert(db);
-+
-+ if (db->time_event)
-+ return;
-+
-+ pa_timeval_rtstore(&tv, pa_rtclock_now() + SAVE_INTERVAL_USEC, true);
-+ db->time_event = db->core->mainloop->time_new(db->core->mainloop, &tv, time_cb, db);
-+}
-+
-+pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id) {
-+ pa_inidb_row *row;
-+
-+ pa_assert(table);
-+ pa_assert(row_id);
-+
-+ row = pa_hashmap_get(table->rows, row_id);
-+ if (row)
-+ return row;
-+
-+ row = table_add_row_internal(table, row_id);
-+ trigger_save(table->db);
-+
-+ return row;
-+}
-+
-+static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb) {
-+ struct column *column;
-+
-+ pa_assert(name);
-+ pa_assert(parse_cb);
-+
-+ column = pa_xnew(struct column, 1);
-+ column->name = pa_xstrdup(name);
-+ column->parse = parse_cb;
-+
-+ return column;
-+}
-+
-+static void column_free(struct column *column) {
-+ pa_assert(column);
-+
-+ pa_xfree(column->name);
-+ pa_xfree(column);
-+}
-+
-+static pa_inidb_row *row_new(pa_inidb_table *table, const char *id) {
-+ pa_inidb_row *row;
-+ struct column *column;
-+ void *state;
-+
-+ pa_assert(table);
-+ pa_assert(id);
-+
-+ row = pa_xnew0(pa_inidb_row, 1);
-+ row->id = pa_xstrdup(id);
-+ row->header = pa_sprintf_malloc("[%s %s]\n", table->name, id);
-+ row->cells = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
-+ (pa_free_cb_t) cell_free);
-+
-+ PA_HASHMAP_FOREACH(column, table->columns, state) {
-+ pa_inidb_cell *cell;
-+
-+ cell = cell_new(table->db, column);
-+ pa_hashmap_put(row->cells, cell->column->name, cell);
-+ }
-+
-+ return row;
-+}
-+
-+static void row_free(pa_inidb_row *row) {
-+ pa_assert(row);
-+
-+ pa_xfree(row->header);
-+ pa_xfree(row->id);
-+ pa_xfree(row);
-+}
-+
-+pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name) {
-+ pa_inidb_cell *cell;
-+
-+ pa_assert(row);
-+ pa_assert(column_name);
-+
-+ pa_assert_se(cell = pa_hashmap_get(row->cells, column_name));
-+
-+ return cell;
-+}
-+
-+static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column) {
-+ pa_inidb_cell *cell;
-+
-+ pa_assert(db);
-+ pa_assert(column);
-+
-+ cell = pa_xnew0(pa_inidb_cell, 1);
-+ cell->db = db;
-+ cell->column = column;
-+
-+ return cell;
-+}
-+
-+static void cell_free(pa_inidb_cell *cell) {
-+ pa_assert(cell);
-+
-+ pa_xfree(cell->assignment);
-+ pa_xfree(cell->value);
-+ pa_xfree(cell);
-+}
-+
-+static void cell_set_value_internal(pa_inidb_cell *cell, const char *value) {
-+ pa_assert(cell);
-+ pa_assert(value);
-+
-+ pa_xfree(cell->value);
-+ cell->value = pa_xstrdup(value);
-+
-+ pa_xfree(cell->assignment);
-+ if (value)
-+ cell->assignment = pa_sprintf_malloc("%s = %s\n", cell->column->name, value);
-+ else
-+ cell->assignment = NULL;
-+}
-+
-+void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value) {
-+ pa_assert(cell);
-+
-+ if (pa_safe_streq(value, cell->value))
-+ return;
-+
-+ cell_set_value_internal(cell, value);
-+ trigger_save(cell->db);
-+}
-diff --git a/src/modules/volume-api/inidb.h b/src/modules/volume-api/inidb.h
-new file mode 100644
-index 0000000..ded73ba
---- /dev/null
-+++ b/src/modules/volume-api/inidb.h
-@@ -0,0 +1,54 @@
-+#ifndef fooinidbhfoo
-+#define fooinidbhfoo
-+
-+/***
-+ This file is part of PulseAudio.
-+
-+ Copyright 2014 Intel Corporation
-+
-+ 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.1 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/core.h>
-+
-+typedef struct pa_inidb pa_inidb;
-+typedef struct pa_inidb_cell pa_inidb_cell;
-+typedef struct pa_inidb_row pa_inidb_row;
-+typedef struct pa_inidb_table pa_inidb_table;
-+
-+/* If there's no object with the given name, the implementation is expected to
-+ * create a new object (or at least try to). */
-+typedef int (*pa_inidb_get_object_cb_t)(pa_inidb *db, const char *name, void **_r);
-+
-+/* The implementation is expected to parse the value, and set the parsed value
-+ * on the object. */
-+typedef int (*pa_inidb_parse_cb_t)(pa_inidb *db, const char *value, void *object);
-+
-+pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata);
-+void pa_inidb_free(pa_inidb *db);
-+
-+void *pa_inidb_get_userdata(pa_inidb *db);
-+pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
-+void pa_inidb_load(pa_inidb *db);
-+
-+void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb);
-+pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id);
-+
-+pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name);
-+
-+void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value);
-+
-+#endif
-diff --git a/src/modules/volume-api/module-volume-api.c b/src/modules/volume-api/module-volume-api.c
-index 845ac09..7b112f6 100644
---- a/src/modules/volume-api/module-volume-api.c
-+++ b/src/modules/volume-api/module-volume-api.c
-@@ -51,6 +51,7 @@ struct userdata {
- pa_hook_slot *volume_control_unlink_slot;
- pa_hook_slot *volume_control_description_changed_slot;
- pa_hook_slot *volume_control_volume_changed_slot;
-+ pa_hook_slot *volume_control_convertible_to_db_changed_slot;
- pa_hook_slot *mute_control_put_slot;
- pa_hook_slot *mute_control_unlink_slot;
- pa_hook_slot *mute_control_description_changed_slot;
-@@ -63,10 +64,13 @@ struct userdata {
- pa_hook_slot *stream_put_slot;
- pa_hook_slot *stream_unlink_slot;
- pa_hook_slot *stream_description_changed_slot;
-+ pa_hook_slot *stream_proplist_changed_slot;
- pa_hook_slot *stream_volume_control_changed_slot;
-+ pa_hook_slot *stream_relative_volume_control_changed_slot;
- pa_hook_slot *stream_mute_control_changed_slot;
- pa_hook_slot *audio_group_put_slot;
- pa_hook_slot *audio_group_unlink_slot;
-+ pa_hook_slot *audio_group_description_changed_slot;
- pa_hook_slot *audio_group_volume_control_changed_slot;
- pa_hook_slot *audio_group_mute_control_changed_slot;
- pa_hook_slot *main_output_volume_control_changed_slot;
-@@ -1247,6 +1251,9 @@ int pa__init(pa_module *module) {
- u->volume_control_volume_changed_slot =
- pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED],
- PA_HOOK_NORMAL, volume_control_event_cb, u);
-+ u->volume_control_convertible_to_db_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], PA_HOOK_NORMAL,
-+ volume_control_event_cb, u);
- u->mute_control_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT],
- PA_HOOK_NORMAL, mute_control_put_cb, u);
- u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
-@@ -1277,9 +1284,14 @@ int pa__init(pa_module *module) {
- u->stream_description_changed_slot =
- pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
- stream_event_cb, u);
-+ u->stream_proplist_changed_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED],
-+ PA_HOOK_NORMAL, stream_event_cb, u);
- u->stream_volume_control_changed_slot =
- pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED],
- PA_HOOK_NORMAL, stream_event_cb, u);
-+ u->stream_relative_volume_control_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED],
-+ PA_HOOK_NORMAL, stream_event_cb, u);
- u->stream_mute_control_changed_slot =
- pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
- stream_event_cb, u);
-@@ -1287,6 +1299,9 @@ int pa__init(pa_module *module) {
- PA_HOOK_NORMAL, audio_group_put_cb, u);
- u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
- PA_HOOK_NORMAL, audio_group_unlink_cb, u);
-+ u->audio_group_description_changed_slot =
-+ pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
-+ audio_group_event_cb, u);
- u->audio_group_volume_control_changed_slot =
- pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED],
- PA_HOOK_NORMAL, audio_group_event_cb, u);
-@@ -1352,6 +1367,9 @@ void pa__done(pa_module *module) {
- if (u->audio_group_volume_control_changed_slot)
- pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
-
-+ if (u->audio_group_description_changed_slot)
-+ pa_hook_slot_free(u->audio_group_description_changed_slot);
-+
- if (u->audio_group_unlink_slot)
- pa_hook_slot_free(u->audio_group_unlink_slot);
-
-@@ -1361,9 +1379,15 @@ void pa__done(pa_module *module) {
- if (u->stream_mute_control_changed_slot)
- pa_hook_slot_free(u->stream_mute_control_changed_slot);
-
-+ if (u->stream_relative_volume_control_changed_slot)
-+ pa_hook_slot_free(u->stream_relative_volume_control_changed_slot);
-+
- if (u->stream_volume_control_changed_slot)
- pa_hook_slot_free(u->stream_volume_control_changed_slot);
-
-+ if (u->stream_proplist_changed_slot)
-+ pa_hook_slot_free(u->stream_proplist_changed_slot);
-+
- if (u->stream_description_changed_slot)
- pa_hook_slot_free(u->stream_description_changed_slot);
-
-@@ -1400,6 +1424,9 @@ void pa__done(pa_module *module) {
- if (u->mute_control_put_slot)
- pa_hook_slot_free(u->mute_control_put_slot);
-
-+ if (u->volume_control_convertible_to_db_changed_slot)
-+ pa_hook_slot_free(u->volume_control_convertible_to_db_changed_slot);
-+
- if (u->volume_control_volume_changed_slot)
- pa_hook_slot_free(u->volume_control_volume_changed_slot);
-
-diff --git a/src/modules/volume-api/mute-control.c b/src/modules/volume-api/mute-control.c
-index adc008e..1b2f276 100644
---- a/src/modules/volume-api/mute-control.c
-+++ b/src/modules/volume-api/mute-control.c
-@@ -31,52 +31,73 @@
-
- #include <pulsecore/core-util.h>
-
--pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) {
-- pa_mute_control *control;
-+int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r) {
-+ pa_mute_control *control = NULL;
-+ int r;
-
- pa_assert(api);
- pa_assert(name);
-- pa_assert(description);
-+ pa_assert(_r);
-
- control = pa_xnew0(pa_mute_control, 1);
- control->volume_api = api;
- control->index = pa_volume_api_allocate_mute_control_index(api);
-- pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
-- control->description = pa_xstrdup(description);
-+
-+ r = pa_volume_api_register_name(api, name, false, &control->name);
-+ if (r < 0)
-+ goto fail;
-+
-+ control->description = pa_xstrdup(control->name);
- control->proplist = pa_proplist_new();
-+ control->present = !persistent;
-+ control->persistent = persistent;
-+ control->purpose = PA_MUTE_CONTROL_PURPOSE_OTHER;
- control->devices = pa_hashmap_new(NULL, NULL);
- control->default_for_devices = pa_hashmap_new(NULL, NULL);
-- control->streams = pa_hashmap_new(NULL, NULL);
-- control->audio_groups = pa_hashmap_new(NULL, NULL);
-
-- return control;
-+ if (persistent) {
-+ pa_inidb_row *row;
-+
-+ row = pa_inidb_table_add_row(api->control_db.mute_controls, control->name);
-+ control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
-+ control->db_cells.mute = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE);
-+ }
-+
-+ *_r = control;
-+ return 0;
-+
-+fail:
-+ if (control)
-+ pa_mute_control_free(control);
-+
-+ return r;
- }
-
--void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
-- pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb) {
-+void pa_mute_control_put(pa_mute_control *control) {
- const char *prop_key;
- void *state = NULL;
-
- pa_assert(control);
-- pa_assert(initial_mute_is_set || control->set_mute);
-- pa_assert(set_initial_mute_cb || !control->set_mute);
-+ pa_assert(control->set_mute || !control->present);
-
-- if (initial_mute_is_set)
-- control->mute = initial_mute;
-- else
-- control->mute = false;
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED], control);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], control);
-
-- if (set_initial_mute_cb)
-- set_initial_mute_cb(control);
-+ if (control->set_mute) {
-+ control->set_mute_in_progress = true;
-+ control->set_mute(control, control->mute);
-+ control->set_mute_in_progress = false;
-+ }
-
- pa_volume_api_add_mute_control(control->volume_api, control);
--
- control->linked = true;
-
- pa_log_debug("Created mute control #%u.", control->index);
- pa_log_debug(" Name: %s", control->name);
- pa_log_debug(" Description: %s", control->description);
- pa_log_debug(" Mute: %s", pa_yes_no(control->mute));
-+ pa_log_debug(" Present: %s", pa_yes_no(control->present));
-+ pa_log_debug(" Persistent: %s", pa_yes_no(control->persistent));
- pa_log_debug(" Properties:");
-
- while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
-@@ -86,9 +107,7 @@ void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initi
- }
-
- void pa_mute_control_unlink(pa_mute_control *control) {
-- pa_audio_group *group;
- pa_device *device;
-- pas_stream *stream;
-
- pa_assert(control);
-
-@@ -102,15 +121,9 @@ void pa_mute_control_unlink(pa_mute_control *control) {
- pa_log_debug("Unlinking mute control %s.", control->name);
-
- if (control->linked)
-- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
-+ pa_volume_api_remove_mute_control(control->volume_api, control);
-
-- pa_volume_api_remove_mute_control(control->volume_api, control);
--
-- while ((group = pa_hashmap_first(control->audio_groups)))
-- pa_audio_group_set_mute_control(group, NULL);
--
-- while ((stream = pa_hashmap_first(control->streams)))
-- pas_stream_set_mute_control(stream, NULL);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
-
- while ((device = pa_hashmap_first(control->default_for_devices)))
- pa_device_set_default_mute_control(device, NULL);
-@@ -133,19 +146,10 @@ void pa_mute_control_unlink(pa_mute_control *control) {
- void pa_mute_control_free(pa_mute_control *control) {
- pa_assert(control);
-
-- if (!control->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!control->unlinked && control->name)
- pa_mute_control_unlink(control);
-
-- if (control->audio_groups) {
-- pa_assert(pa_hashmap_isempty(control->audio_groups));
-- pa_hashmap_free(control->audio_groups);
-- }
--
-- if (control->streams) {
-- pa_assert(pa_hashmap_isempty(control->streams));
-- pa_hashmap_free(control->streams);
-- }
--
- if (control->default_for_devices) {
- pa_assert(pa_hashmap_isempty(control->default_for_devices));
- pa_hashmap_free(control->default_for_devices);
-@@ -167,86 +171,137 @@ void pa_mute_control_free(pa_mute_control *control) {
- pa_xfree(control);
- }
-
--void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) {
-+void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner) {
- pa_assert(control);
-- pa_assert(group);
-+ pa_assert(!control->linked);
-
-- control->owner_audio_group = group;
-+ control->purpose = purpose;
-+ control->owner = owner;
- }
-
--static void set_mute_internal(pa_mute_control *control, bool mute) {
-- bool old_mute;
--
-+int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
-+ pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata) {
- pa_assert(control);
-+ pa_assert(group);
-+ pa_assert(set_mute_cb);
-
-- old_mute = control->mute;
-+ if (control->present) {
-+ pa_log("Can't acquire mute control %s, it's already present.", control->name);
-+ return -PA_ERR_BUSY;
-+ }
-
-- if (mute == old_mute)
-- return;
-+ control->owner_audio_group = group;
-+ control->set_mute = set_mute_cb;
-+ control->userdata = userdata;
-
-- control->mute = mute;
-+ control->set_mute_in_progress = true;
-+ control->set_mute(control, control->mute);
-+ control->set_mute_in_progress = false;
-+
-+ control->present = true;
-
- if (!control->linked || control->unlinked)
-- return;
-+ return 0;
-
-- pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_yes_no(old_mute),
-- pa_yes_no(control->mute));
-+ pa_log_debug("Mute control %s became present.", control->name);
-
-- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
-+ return 0;
- }
-
--int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
-- int r;
--
-+void pa_mute_control_release(pa_mute_control *control) {
- pa_assert(control);
-
-- if (!control->set_mute) {
-- pa_log_info("Tried to set the mute of mute control %s, but the mute control doesn't support the operation.",
-- control->name);
-- return -PA_ERR_NOTSUPPORTED;
-- }
-+ if (!control->present)
-+ return;
-
-- if (mute == control->mute)
-- return 0;
-+ control->present = false;
-
-- control->set_mute_in_progress = true;
-- r = control->set_mute(control, mute);
-- control->set_mute_in_progress = false;
-+ control->userdata = NULL;
-+ control->set_mute = NULL;
-+ control->owner_audio_group = NULL;
-
-- if (r >= 0)
-- set_mute_internal(control, mute);
-+ if (!control->linked || control->unlinked)
-+ return;
-
-- return r;
-+ pa_log_debug("Mute control %s became not present.", control->name);
- }
-
--void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) {
-+void pa_mute_control_set_description(pa_mute_control *control, const char *description) {
- char *old_description;
-
- pa_assert(control);
-- pa_assert(new_description);
-+ pa_assert(description);
-
- old_description = control->description;
-
-- if (pa_streq(new_description, old_description))
-+ if (pa_streq(description, old_description))
-+ return;
-+
-+ control->description = pa_xstrdup(description);
-+
-+ if (control->persistent)
-+ pa_inidb_cell_set_value(control->db_cells.description, description);
-+
-+ if (!control->linked || control->unlinked) {
-+ pa_xfree(old_description);
- return;
-+ }
-
-- control->description = pa_xstrdup(new_description);
- pa_log_debug("The description of mute control %s changed from \"%s\" to \"%s\".", control->name, old_description,
-- new_description);
-+ description);
- pa_xfree(old_description);
- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED], control);
- }
-
--void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute) {
-+static void set_mute_internal(pa_mute_control *control, bool mute) {
-+ bool old_mute;
-+
- pa_assert(control);
-
-- if (!control->linked)
-+ old_mute = control->mute;
-+
-+ if (mute == old_mute)
- return;
-
-- if (control->set_mute_in_progress)
-+ control->mute = mute;
-+
-+ if (control->persistent)
-+ pa_inidb_cell_set_value(control->db_cells.mute, pa_boolean_to_string(mute));
-+
-+ if (!control->linked || control->unlinked)
- return;
-
-- set_mute_internal(control, new_mute);
-+ pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_boolean_to_string(old_mute),
-+ pa_boolean_to_string(control->mute));
-+
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
-+}
-+
-+int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
-+ int r;
-+
-+ pa_assert(control);
-+
-+ if (control->set_mute_in_progress)
-+ return 0;
-+
-+ if (mute == control->mute)
-+ return 0;
-+
-+ if (control->linked && control->present) {
-+ control->set_mute_in_progress = true;
-+ r = control->set_mute(control, mute);
-+ control->set_mute_in_progress = false;
-+
-+ if (r < 0) {
-+ pa_log("Setting the mute of mute control %s failed.", control->name);
-+ return r;
-+ }
-+ }
-+
-+ set_mute_internal(control, mute);
-+
-+ return 0;
- }
-
- void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) {
-@@ -276,31 +331,3 @@ void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_devi
-
- pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
- }
--
--void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream) {
-- pa_assert(control);
-- pa_assert(stream);
--
-- pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
--}
--
--void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream) {
-- pa_assert(control);
-- pa_assert(stream);
--
-- pa_assert_se(pa_hashmap_remove(control->streams, stream));
--}
--
--void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group) {
-- pa_assert(control);
-- pa_assert(group);
--
-- pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
--}
--
--void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group) {
-- pa_assert(control);
-- pa_assert(group);
--
-- pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
--}
-diff --git a/src/modules/volume-api/mute-control.h b/src/modules/volume-api/mute-control.h
-index 1f70a43..40f8a9c 100644
---- a/src/modules/volume-api/mute-control.h
-+++ b/src/modules/volume-api/mute-control.h
-@@ -22,10 +22,18 @@
- USA.
- ***/
-
-+#include <modules/volume-api/inidb.h>
- #include <modules/volume-api/volume-api.h>
-
- typedef struct pa_mute_control pa_mute_control;
-
-+typedef enum {
-+ PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE,
-+ PA_MUTE_CONTROL_PURPOSE_OTHER,
-+} pa_mute_control_purpose_t;
-+
-+typedef int (*pa_mute_control_set_mute_cb_t)(pa_mute_control *control, bool mute);
-+
- struct pa_mute_control {
- pa_volume_api *volume_api;
- uint32_t index;
-@@ -33,6 +41,14 @@ struct pa_mute_control {
- char *description;
- pa_proplist *proplist;
- bool mute;
-+ bool present;
-+ bool persistent;
-+
-+ pa_mute_control_purpose_t purpose;
-+ union {
-+ pas_stream *owner_stream;
-+ void *owner;
-+ };
-
- /* If this mute control is the "own mute control" of an audio group, this
- * is set to point to that group, otherwise this is NULL. */
-@@ -40,50 +56,43 @@ struct pa_mute_control {
-
- pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
- pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
-- pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
-- pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
-+
-+ struct {
-+ pa_inidb_cell *description;
-+ pa_inidb_cell *mute;
-+ } db_cells;
-
- bool linked;
- bool unlinked;
- bool set_mute_in_progress;
-
- /* Called from pa_mute_control_set_mute(). The implementation is expected
-- * to return a negative error code on failure. May be NULL, if the mute
-- * control is read-only. */
-- int (*set_mute)(pa_mute_control *control, bool mute);
-+ * to return a negative error code on failure. */
-+ pa_mute_control_set_mute_cb_t set_mute;
-
- void *userdata;
- };
-
--pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description);
--
--typedef void (*pa_mute_control_set_initial_mute_cb_t)(pa_mute_control *control);
--
--/* initial_mute is the preferred initial mute of the mute control
-- * implementation. It may be unset, if the implementation doesn't care about
-- * the initial state of the mute control. Read-only mute controls, however,
-- * must always set initial_mute.
-- *
-- * The implementation's initial mute preference may be overridden by policy, if
-- * the mute control isn't read-only. When the final initial mute is known, the
-- * the implementation is notified via set_initial_mute_cb (the mute can be read
-- * from control->mute). set_initial_mute_cb may be NULL, if the mute control is
-- * read-only. */
--void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
-- pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb);
--
-+int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r);
-+void pa_mute_control_put(pa_mute_control *control);
- void pa_mute_control_unlink(pa_mute_control *control);
- void pa_mute_control_free(pa_mute_control *control);
-
--/* Called by audio-group.c only. */
--void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group);
--
--/* Called by clients and policy modules. */
--int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
-+/* Called by the mute control implementation, before pa_mute_control_put(). */
-+void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner);
-
- /* Called by the mute control implementation. */
--void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description);
--void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute);
-+int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
-+ pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata);
-+
-+/* Called by the mute control implementation. This must only be called for
-+ * persistent controls; use pa_mute_control_free() for non-persistent
-+ * controls. */
-+void pa_mute_control_release(pa_mute_control *control);
-+
-+/* Called by anyone. */
-+void pa_mute_control_set_description(pa_mute_control *control, const char *description);
-+int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
-
- /* Called from device.c only. */
- void pa_mute_control_add_device(pa_mute_control *control, pa_device *device);
-@@ -91,12 +100,4 @@ void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device);
- void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device);
- void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device);
-
--/* Called from sstream.c only. */
--void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream);
--void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream);
--
--/* Called from audio-group.c only. */
--void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group);
--void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group);
--
- #endif
-diff --git a/src/modules/volume-api/sstream.c b/src/modules/volume-api/sstream.c
-index e3531a8..1738d15 100644
---- a/src/modules/volume-api/sstream.c
-+++ b/src/modules/volume-api/sstream.c
-@@ -26,7 +26,6 @@
- #include "sstream.h"
-
- #include <modules/volume-api/audio-group.h>
--#include <modules/volume-api/binding.h>
- #include <modules/volume-api/mute-control.h>
- #include <modules/volume-api/volume-control.h>
-
-@@ -34,121 +33,43 @@
-
- #include <pulsecore/core-util.h>
-
--pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) {
-- pas_stream *stream;
-+int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r) {
-+ pas_stream *stream = NULL;
-+ int r;
-
- pa_assert(api);
- pa_assert(name);
-- pa_assert(description);
-+ pa_assert(_r);
-
- stream = pa_xnew0(pas_stream, 1);
- stream->volume_api = api;
- stream->index = pa_volume_api_allocate_stream_index(api);
-- pa_assert_se(pa_volume_api_register_name(api, name, false, &stream->name) >= 0);
-- stream->description = pa_xstrdup(description);
-- stream->direction = direction;
-- stream->proplist = pa_proplist_new();
-- stream->use_default_volume_control = true;
-- stream->use_default_mute_control = true;
--
-- return stream;
--}
--
--static void set_volume_control_internal(pas_stream *stream, pa_volume_control *control) {
-- pa_volume_control *old_control;
--
-- pa_assert(stream);
--
-- old_control = stream->volume_control;
--
-- if (control == old_control)
-- return;
--
-- if (old_control) {
-- /* If the old control pointed to the own volume control of an audio
-- * group, then the stream's audio group for volume needs to be
-- * updated. We set it to NULL here, and if it should be non-NULL, that
-- * will be fixed very soon (a few lines down). */
-- pas_stream_set_audio_group_for_volume(stream, NULL);
--
-- pa_volume_control_remove_stream(old_control, stream);
-- }
--
-- stream->volume_control = control;
--
-- if (control) {
-- pa_volume_control_add_stream(control, stream);
-- pas_stream_set_audio_group_for_volume(stream, control->owner_audio_group);
-- }
--
-- if (!stream->linked || stream->unlinked)
-- return;
--
-- pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
-- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
--
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
--}
--
--static void set_mute_control_internal(pas_stream *stream, pa_mute_control *control) {
-- pa_mute_control *old_control;
--
-- pa_assert(stream);
--
-- old_control = stream->mute_control;
-
-- if (control == old_control)
-- return;
-+ r = pa_volume_api_register_name(api, name, false, &stream->name);
-+ if (r < 0)
-+ goto fail;
-
-- if (old_control) {
-- /* If the old control pointed to the own mute control of an audio
-- * group, then the stream's audio group for mute needs to be updated.
-- * We set it to NULL here, and if it should be non-NULL, that will be
-- * fixed very soon (a few lines down). */
-- pas_stream_set_audio_group_for_mute(stream, NULL);
--
-- pa_mute_control_remove_stream(old_control, stream);
-- }
--
-- stream->mute_control = control;
--
-- if (control) {
-- pa_mute_control_add_stream(control, stream);
-- pas_stream_set_audio_group_for_mute(stream, control->owner_audio_group);
-- }
-+ stream->description = pa_xstrdup(stream->name);
-+ stream->direction = PA_DIRECTION_OUTPUT;
-+ stream->proplist = pa_proplist_new();
-
-- if (!stream->linked || stream->unlinked)
-- return;
-+ *_r = stream;
-+ return 0;
-
-- pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
-- old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+fail:
-+ if (stream)
-+ pas_stream_free(stream);
-
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
-+ return r;
- }
-
--void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
-+void pas_stream_put(pas_stream *stream) {
- const char *prop_key;
- void *state = NULL;
-
- pa_assert(stream);
-- pa_assert(!stream->create_own_volume_control || stream->delete_own_volume_control);
-- pa_assert(!stream->create_own_mute_control || stream->delete_own_mute_control);
--
-- if (initial_properties)
-- pa_proplist_update(stream->proplist, PA_UPDATE_REPLACE, initial_properties);
--
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], stream);
--
-- if (stream->use_default_volume_control)
-- set_volume_control_internal(stream, stream->own_volume_control);
--
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], stream);
--
-- if (stream->use_default_mute_control)
-- set_mute_control_internal(stream, stream->own_mute_control);
-
- pa_volume_api_add_stream(stream->volume_api, stream);
--
- stream->linked = true;
-
- pa_log_debug("Created stream #%u.", stream->index);
-@@ -157,6 +78,10 @@ void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
- pa_log_debug(" Direction: %s", pa_direction_to_string(stream->direction));
- pa_log_debug(" Volume control: %s", stream->volume_control ? stream->volume_control->name : "(unset)");
- pa_log_debug(" Mute control: %s", stream->mute_control ? stream->mute_control->name : "(unset)");
-+ pa_log_debug(" Audio group for volume: %s",
-+ stream->audio_group_for_volume ? stream->audio_group_for_volume->name : "(unset)");
-+ pa_log_debug(" Audio group for mute: %s",
-+ stream->audio_group_for_mute ? stream->audio_group_for_mute->name : "(unset)");
- pa_log_debug(" Properties:");
-
- while ((prop_key = pa_proplist_iterate(stream->proplist, &state)))
-@@ -178,22 +103,22 @@ void pas_stream_unlink(pas_stream *stream) {
- pa_log_debug("Unlinking stream %s.", stream->name);
-
- if (stream->linked)
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
-+ pa_volume_api_remove_stream(stream->volume_api, stream);
-
-- pa_volume_api_remove_stream(stream->volume_api, stream);
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
-
- pas_stream_set_audio_group_for_mute(stream, NULL);
- pas_stream_set_audio_group_for_volume(stream, NULL);
- pas_stream_set_mute_control(stream, NULL);
-+ pas_stream_set_relative_volume_control(stream, NULL);
- pas_stream_set_volume_control(stream, NULL);
-- pas_stream_set_have_own_mute_control(stream, false);
-- pas_stream_set_have_own_volume_control(stream, false);
- }
-
- void pas_stream_free(pas_stream *stream) {
- pa_assert(stream);
-
-- if (!stream->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!stream->unlinked && stream->name)
- pas_stream_unlink(stream);
-
- if (stream->proplist)
-@@ -207,160 +132,172 @@ void pas_stream_free(pas_stream *stream) {
- pa_xfree(stream);
- }
-
--int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) {
-+void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction) {
- pa_assert(stream);
-+ pa_assert(!stream->linked);
-
-- if (have == stream->have_own_volume_control)
-- return 0;
-+ stream->direction = direction;
-+}
-
-- if (have) {
-- pa_assert(!stream->own_volume_control);
-+void pas_stream_set_description(pas_stream *stream, const char *description) {
-+ char *old_description;
-+
-+ pa_assert(stream);
-+ pa_assert(description);
-
-- if (!stream->create_own_volume_control) {
-- pa_log_debug("Stream %s doesn't support own volume control.", stream->name);
-- return -PA_ERR_NOTSUPPORTED;
-- }
-+ old_description = stream->description;
-+
-+ if (pa_streq(description, old_description))
-+ return;
-
-- stream->own_volume_control = stream->create_own_volume_control(stream);
-- } else {
-- stream->delete_own_volume_control(stream);
-- stream->own_volume_control = NULL;
-+ stream->description = pa_xstrdup(description);
-+
-+ if (!stream->linked || stream->unlinked) {
-+ pa_xfree(old_description);
-+ return;
- }
-
-- stream->have_own_volume_control = have;
-+ pa_log_debug("Stream %s description changed from \"%s\" to \"%s\".", stream->name, old_description,
-+ description);
-+ pa_xfree(old_description);
-
-- return 0;
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
- }
-
--int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) {
-+void pas_stream_set_property(pas_stream *stream, const char *key, const char *value) {
-+ const char *old_value;
-+
- pa_assert(stream);
-+ pa_assert(key);
-
-- if (have == stream->have_own_mute_control)
-- return 0;
-+ old_value = pa_proplist_gets(stream->proplist, key);
-
-- if (have) {
-- pa_assert(!stream->own_mute_control);
-+ if (pa_safe_streq(value, old_value))
-+ return;
-
-- if (!stream->create_own_mute_control) {
-- pa_log_debug("Stream %s doesn't support own mute control.", stream->name);
-- return -PA_ERR_NOTSUPPORTED;
-- }
-+ if (value)
-+ pa_proplist_sets(stream->proplist, key, value);
-+ else
-+ pa_proplist_unset(stream->proplist, key);
-
-- stream->own_mute_control = stream->create_own_mute_control(stream);
-- } else {
-- stream->delete_own_mute_control(stream);
-- stream->own_mute_control = NULL;
-- }
-+ if (!stream->linked || stream->unlinked)
-+ return;
-
-- stream->have_own_mute_control = have;
-+ pa_log_debug("Stream %s property \"%s\" changed from \"%s\" to \"%s\".", stream->name, key,
-+ old_value ? old_value : "(unset)", value ? value : "(unset)");
-
-- return 0;
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED], stream);
- }
-
- void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) {
-- pa_assert(stream);
-+ pa_volume_control *old_control;
-
-- stream->use_default_volume_control = false;
-+ pa_assert(stream);
-
-- if (stream->volume_control_binding) {
-- pa_binding_free(stream->volume_control_binding);
-- stream->volume_control_binding = NULL;
-- }
-+ old_control = stream->volume_control;
-
-- set_volume_control_internal(stream, control);
--}
-+ if (control == old_control)
-+ return;
-
--void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
-- pa_assert(stream);
-+ stream->volume_control = control;
-
-- stream->use_default_mute_control = false;
-+ if (!stream->linked || stream->unlinked)
-+ return;
-
-- if (stream->mute_control_binding) {
-- pa_binding_free(stream->mute_control_binding);
-- stream->mute_control_binding = NULL;
-- }
-+ pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
-- set_mute_control_internal(stream, control);
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
- }
-
--void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = stream,
-- .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
-- };
-+void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control) {
-+ pa_volume_control *old_control;
-
- pa_assert(stream);
-- pa_assert(target_info);
--
-- stream->use_default_volume_control = false;
-
-- if (stream->volume_control_binding)
-- pa_binding_free(stream->volume_control_binding);
-+ old_control = stream->relative_volume_control;
-
-- stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
--}
-+ if (control == old_control)
-+ return;
-
--void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = stream,
-- .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
-- };
-+ stream->relative_volume_control = control;
-
-- pa_assert(stream);
-- pa_assert(target_info);
--
-- stream->use_default_mute_control = false;
-+ if (!stream->linked || stream->unlinked)
-+ return;
-
-- if (stream->mute_control_binding)
-- pa_binding_free(stream->mute_control_binding);
-+ pa_log_debug("The relative volume control of stream %s changed from %s to %s.", stream->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-
-- stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED], stream);
- }
-
--void pas_stream_description_changed(pas_stream *stream, const char *new_description) {
-- char *old_description;
-+void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
-+ pa_mute_control *old_control;
-
- pa_assert(stream);
-- pa_assert(new_description);
-
-- old_description = stream->description;
-+ old_control = stream->mute_control;
-
-- if (pa_streq(new_description, old_description))
-+ if (control == old_control)
- return;
-
-- stream->description = pa_xstrdup(new_description);
-- pa_log_debug("The description of stream %s changed from \"%s\" to \"%s\".", stream->name, old_description,
-- new_description);
-- pa_xfree(old_description);
-- pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
-+ stream->mute_control = control;
-+
-+ if (!stream->linked || stream->unlinked)
-+ return;
-+
-+ pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
-+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
-+
-+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
- }
-
- void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) {
-+ pa_audio_group *old_group;
-+
- pa_assert(stream);
-
-- if (group == stream->audio_group_for_volume)
-+ old_group = stream->audio_group_for_volume;
-+
-+ if (group == old_group)
- return;
-
-- if (stream->audio_group_for_volume)
-- pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream);
-+ if (old_group)
-+ pa_audio_group_remove_volume_stream(old_group, stream);
-
- stream->audio_group_for_volume = group;
-
- if (group)
- pa_audio_group_add_volume_stream(group, stream);
-+
-+ if (!stream->linked || stream->unlinked)
-+ return;
-+
-+ pa_log_debug("Stream %s audio group for volume changed from %s to %s.", stream->name,
-+ old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
- }
-
- void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) {
-+ pa_audio_group *old_group;
-+
- pa_assert(stream);
-
-- if (group == stream->audio_group_for_mute)
-+ old_group = stream->audio_group_for_mute;
-+
-+ if (group == old_group)
- return;
-
-- if (stream->audio_group_for_mute)
-- pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream);
-+ if (old_group)
-+ pa_audio_group_remove_mute_stream(old_group, stream);
-
- stream->audio_group_for_mute = group;
-
- if (group)
- pa_audio_group_add_mute_stream(group, stream);
-+
-+ if (!stream->linked || stream->unlinked)
-+ return;
-+
-+ pa_log_debug("Stream %s audio group for mute changed from %s to %s.", stream->name,
-+ old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
- }
-diff --git a/src/modules/volume-api/sstream.h b/src/modules/volume-api/sstream.h
-index a65b34c..715bf2c 100644
---- a/src/modules/volume-api/sstream.h
-+++ b/src/modules/volume-api/sstream.h
-@@ -39,69 +39,33 @@ struct pas_stream {
- pa_direction_t direction;
- pa_proplist *proplist;
- pa_volume_control *volume_control;
-+ pa_volume_control *relative_volume_control;
- pa_mute_control *mute_control;
-- bool use_default_volume_control;
-- bool use_default_mute_control;
-- bool have_own_volume_control;
-- bool have_own_mute_control;
-- pa_volume_control *own_volume_control;
-- pa_mute_control *own_mute_control;
--
-- pa_binding *volume_control_binding;
-- pa_binding *mute_control_binding;
- pa_audio_group *audio_group_for_volume;
- pa_audio_group *audio_group_for_mute;
-
- bool linked;
- bool unlinked;
-
-- /* Called when the own volume control is enabled. The callback
-- * implementation should return a new linked volume control object. The
-- * callback may be NULL, in which case the own volume control can't be
-- * enabled. */
-- pa_volume_control *(*create_own_volume_control)(pas_stream *stream);
--
-- /* Called when the own volume control is disabled. The implementation
-- * should free stream->own_volume_control. The callback may be NULL only if
-- * create_own_volume_control is NULL also. */
-- void (*delete_own_volume_control)(pas_stream *stream);
--
-- /* Called when the own mute control is enabled. The callback implementation
-- * should return a new linked mute control object. The callback may be
-- * NULL, in which case the own mute control can't be enabled. */
-- pa_mute_control *(*create_own_mute_control)(pas_stream *stream);
--
-- /* Called when the own mute control is disabled. The implementation should
-- * free stream->own_mute_control. The callback may be NULL only if
-- * create_own_mute_control is NULL also. */
-- void (*delete_own_mute_control)(pas_stream *stream);
--
- void *userdata;
- };
-
--pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction);
--void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties);
-+int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r);
-+void pas_stream_put(pas_stream *stream);
- void pas_stream_unlink(pas_stream *stream);
- void pas_stream_free(pas_stream *stream);
-
--/* Called by the stream implementation and possibly by policy modules.
-- * Enabling own controls may fail (the stream may not support own controls),
-- * disabling will never fail. */
--int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have);
--int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have);
-+/* Called by the stream implementation, only during initialization. */
-+void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction);
-
--/* Called by policy modules. */
-+/* Called by the stream implementation. */
-+void pas_stream_set_description(pas_stream *stream, const char *description);
-+void pas_stream_set_property(pas_stream *stream, const char *key, const char *value);
- void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control);
-+void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control);
- void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control);
--void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info);
--void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info);
--
--/* Called by the stream implementation. */
--void pas_stream_description_changed(pas_stream *stream, const char *new_description);
-
--/* Called by audio-group.c only. Adding a stream to an audio group happens
-- * implicitly when the volume or mute control of a stream is set to point to
-- * the own control of an audio group. */
-+/* Called by anyone. */
- void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group);
- void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group);
-
-diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c
-index f6ca7b3..0d9ea24 100644
---- a/src/modules/volume-api/stream-creator.c
-+++ b/src/modules/volume-api/stream-creator.c
-@@ -35,9 +35,9 @@
- struct pa_stream_creator {
- pa_volume_api *volume_api;
- pa_hashmap *streams; /* pa_sink_input/pa_source_output -> struct stream */
-- pa_hook_slot *sink_input_put_slot;
-+ pa_hook_slot *sink_input_fixate_slot;
- pa_hook_slot *sink_input_unlink_slot;
-- pa_hook_slot *source_output_put_slot;
-+ pa_hook_slot *source_output_fixate_slot;
- pa_hook_slot *source_output_unlink_slot;
- };
-
-@@ -47,73 +47,61 @@ enum stream_type {
- };
-
- struct stream {
-+ pa_core *core;
- pa_stream_creator *creator;
- enum stream_type type;
-+ pa_sink_input_new_data *sink_input_new_data;
- pa_sink_input *sink_input;
-+ pa_source_output_new_data *source_output_new_data;
- pa_source_output *source_output;
- pa_client *client;
-+ pa_volume_control *volume_control;
-+ pa_volume_control *relative_volume_control;
-+ pa_mute_control *mute_control;
- pas_stream *stream;
-
-- bool unlinked;
--
- pa_hook_slot *proplist_changed_slot;
-- pa_hook_slot *client_proplist_changed_slot;
- pa_hook_slot *volume_changed_slot;
-+ pa_hook_slot *reference_ratio_changed_slot;
- pa_hook_slot *mute_changed_slot;
- };
-
--static char *get_stream_volume_and_mute_control_description_malloc(struct stream *stream) {
-- const char *application_name = NULL;
-- char *description;
--
-- pa_assert(stream);
--
-- if (stream->client)
-- application_name = pa_proplist_gets(stream->client->proplist, PA_PROP_APPLICATION_NAME);
--
-- if (application_name)
-- description = pa_sprintf_malloc("%s: %s", application_name, stream->stream->description);
-- else
-- description = pa_xstrdup(stream->stream->description);
--
-- return description;
--}
-+static void stream_free(struct stream *stream);
-
--static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
-+static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
-+ const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
- struct stream *stream;
- pa_bvolume bvolume;
- pa_cvolume cvolume;
-
- pa_assert(control);
-- pa_assert(volume);
-+ pa_assert(original_volume);
-+ pa_assert(remapped_volume);
-
- stream = control->userdata;
--
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- pa_bvolume_from_cvolume(&bvolume, &stream->sink_input->volume, &stream->sink_input->channel_map);
-- break;
--
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- pa_bvolume_from_cvolume(&bvolume, &stream->source_output->volume, &stream->source_output->channel_map);
-- break;
-- }
-+ bvolume = control->volume;
-
- if (set_volume)
-- bvolume.volume = volume->volume;
-+ bvolume.volume = remapped_volume->volume;
-
- if (set_balance)
-- pa_bvolume_copy_balance(&bvolume, volume);
-+ pa_bvolume_copy_balance(&bvolume, remapped_volume);
-
- pa_bvolume_to_cvolume(&bvolume, &cvolume);
-
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
-- pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
-+ if (stream->sink_input->state == PA_SINK_INPUT_INIT)
-+ pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, false);
-+ else
-+ pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-- pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
-+ if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
-+ pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, false);
-+ else
-+ pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
- break;
- }
-
-@@ -129,6 +117,9 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
- pa_assert(stream);
- pa_assert(call_data);
-
-+ if (!stream->volume_control)
-+ return PA_HOOK_OK;
-+
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
- input = call_data;
-@@ -144,63 +135,95 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
-
- if (input)
- pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map);
-- else
-+ else if (output)
- pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map);
-+ else
-+ pa_assert_not_reached();
-
-- pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true);
-+ pa_volume_control_set_volume(stream->volume_control, &bvolume, true, true);
-
- return PA_HOOK_OK;
- }
-
--static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
-+static int relative_volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
-+ const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
- struct stream *stream;
-+ pa_bvolume bvolume;
- pa_cvolume cvolume;
-
- pa_assert(control);
-+ pa_assert(original_volume);
-+ pa_assert(remapped_volume);
-
- stream = control->userdata;
-- pa_bvolume_to_cvolume(&control->volume, &cvolume);
--
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
-- break;
-+ bvolume = control->volume;
-
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
-- break;
-- }
--}
--
--static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
-- struct stream *stream;
-+ if (set_volume)
-+ bvolume.volume = remapped_volume->volume;
-
-- pa_assert(control);
-+ if (set_balance)
-+ pa_bvolume_copy_balance(&bvolume, remapped_volume);
-
-- stream = control->userdata;
-+ pa_bvolume_to_cvolume(&bvolume, &cvolume);
-
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
-- pa_sink_input_set_mute(stream->sink_input, mute, true);
-+ if (stream->sink_input->state == PA_SINK_INPUT_INIT) {
-+ pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
-+
-+ /* XXX: This is a bit ugly. This is needed, because when we
-+ * call pa_sink_input_new_data_set_volume(), there's no
-+ * automatic notification to the primary volume control object
-+ * about the changed volume. This problem should go away once
-+ * stream volume controls are moved into the core. */
-+ if (stream->volume_control) {
-+ pa_bvolume absolute_volume;
-+
-+ pa_bvolume_from_cvolume(&absolute_volume, &stream->sink_input_new_data->volume,
-+ &stream->sink_input_new_data->channel_map);
-+ pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
-+ }
-+ } else
-+ pa_sink_input_set_volume(stream->sink_input, &cvolume, true, false);
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-- pa_source_output_set_mute(stream->source_output, mute, true);
-+ if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT) {
-+ pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
-+
-+ /* XXX: This is a bit ugly. This is needed, because when we
-+ * call pa_source_output_new_data_set_volume(), there's no
-+ * automatic notification to the primary volume control object
-+ * about the changed volume. This problem should go away once
-+ * stream volume controls are moved into the core. */
-+ if (stream->volume_control) {
-+ pa_bvolume absolute_volume;
-+
-+ pa_bvolume_from_cvolume(&absolute_volume, &stream->source_output_new_data->volume,
-+ &stream->source_output_new_data->channel_map);
-+ pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
-+ }
-+ } else
-+ pa_source_output_set_volume(stream->source_output, &cvolume, true, false);
- break;
- }
-
- return 0;
- }
-
--static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+static pa_hook_result_t sink_input_or_source_output_reference_ratio_changed_cb(void *hook_data, void *call_data,
-+ void *userdata) {
- struct stream *stream = userdata;
- pa_sink_input *input = NULL;
- pa_source_output *output = NULL;
-- bool mute;
-+ pa_bvolume bvolume;
-
- pa_assert(stream);
- pa_assert(call_data);
-
-+ if (!stream->relative_volume_control)
-+ return PA_HOOK_OK;
-+
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
- input = call_data;
-@@ -215,18 +238,18 @@ static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_d
- return PA_HOOK_OK;
-
- if (input)
-- mute = input->muted;
-+ pa_bvolume_from_cvolume(&bvolume, &input->reference_ratio, &input->channel_map);
- else if (output)
-- mute = output->muted;
-+ pa_bvolume_from_cvolume(&bvolume, &output->reference_ratio, &output->channel_map);
- else
- pa_assert_not_reached();
-
-- pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
-+ pa_volume_control_set_volume(stream->relative_volume_control, &bvolume, true, true);
-
- return PA_HOOK_OK;
- }
-
--static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
-+static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
- struct stream *stream;
-
- pa_assert(control);
-@@ -235,167 +258,66 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
-
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
-- pa_sink_input_set_mute(stream->sink_input, control->mute, true);
-- break;
--
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- pa_source_output_set_mute(stream->source_output, control->mute, true);
-- break;
-- }
--}
--
--static const char *get_sink_input_description(pa_sink_input *input) {
-- const char *description;
--
-- pa_assert(input);
--
-- description = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_NAME);
-- if (description)
-- return description;
--
-- return NULL;
--}
--
--static const char *get_source_output_description(pa_source_output *output) {
-- const char *description;
--
-- pa_assert(output);
--
-- description = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_NAME);
-- if (description)
-- return description;
--
-- return NULL;
--}
--
--static pa_volume_control *stream_create_own_volume_control_cb(pas_stream *s) {
-- struct stream *stream;
-- const char *name = NULL;
-- char *description;
-- pa_volume_control *control;
-- pa_bvolume volume;
--
-- pa_assert(s);
--
-- stream = s->userdata;
--
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- name = "sink-input-volume-control";
-- break;
--
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- name = "source-output-volume-control";
-- break;
-- }
--
-- description = get_stream_volume_and_mute_control_description_malloc(stream);
-- control = pa_volume_control_new(stream->creator->volume_api, name, description, true, false);
-- pa_xfree(description);
-- control->set_volume = volume_control_set_volume_cb;
-- control->userdata = stream;
--
-- pa_assert(!stream->volume_changed_slot);
--
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- stream->volume_changed_slot =
-- pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
-- sink_input_or_source_output_volume_changed_cb, stream);
-- pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, &stream->sink_input->channel_map);
-+ if (stream->sink_input->state == PA_SINK_INPUT_INIT)
-+ pa_sink_input_new_data_set_muted(stream->sink_input_new_data, mute);
-+ else
-+ pa_sink_input_set_mute(stream->sink_input, mute, true);
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-- stream->volume_changed_slot =
-- pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
-- PA_HOOK_NORMAL, sink_input_or_source_output_volume_changed_cb, stream);
-- pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, &stream->source_output->channel_map);
-+ if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
-+ pa_source_output_new_data_set_muted(stream->source_output_new_data, mute);
-+ else
-+ pa_source_output_set_mute(stream->source_output, mute, true);
- break;
- }
-
-- pa_volume_control_put(control, &volume, volume_control_set_initial_volume_cb);
--
-- return control;
--}
--
--static void stream_delete_own_volume_control_cb(pas_stream *s) {
-- struct stream *stream;
--
-- pa_assert(s);
--
-- stream = s->userdata;
-- pa_hook_slot_free(stream->volume_changed_slot);
-- stream->volume_changed_slot = NULL;
-- pa_volume_control_free(s->own_volume_control);
-+ return 0;
- }
-
--static pa_mute_control *stream_create_own_mute_control_cb(pas_stream *s) {
-- struct stream *stream;
-- const char *name = NULL;
-- char *description;
-- pa_mute_control *control;
-- bool mute = false;
--
-- pa_assert(s);
--
-- stream = s->userdata;
--
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- name = "sink-input-mute-control";
-- break;
--
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- name = "source-output-mute-control";
-- break;
-- }
-+static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
-+ struct stream *stream = userdata;
-+ pa_sink_input *input = NULL;
-+ pa_source_output *output = NULL;
-+ bool mute;
-
-- description = get_stream_volume_and_mute_control_description_malloc(stream);
-- control = pa_mute_control_new(stream->creator->volume_api, name, description);
-- pa_xfree(description);
-- control->set_mute = mute_control_set_mute_cb;
-- control->userdata = stream;
-+ pa_assert(stream);
-+ pa_assert(call_data);
-
-- pa_assert(!stream->mute_changed_slot);
-+ if (!stream->mute_control)
-+ return PA_HOOK_OK;
-
- switch (stream->type) {
- case STREAM_TYPE_SINK_INPUT:
-- stream->mute_changed_slot =
-- pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
-- sink_input_or_source_output_mute_changed_cb, stream);
-- mute = stream->sink_input->muted;
-+ input = call_data;
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-- stream->mute_changed_slot =
-- pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
-- PA_HOOK_NORMAL, sink_input_or_source_output_mute_changed_cb, stream);
-- mute = stream->source_output->muted;
-+ output = call_data;
- break;
- }
-
-- pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb);
--
-- return control;
--}
-+ if ((input && input != stream->sink_input) || (output && output != stream->source_output))
-+ return PA_HOOK_OK;
-
--static void stream_delete_own_mute_control_cb(pas_stream *s) {
-- struct stream *stream;
-+ if (input)
-+ mute = input->muted;
-+ else if (output)
-+ mute = output->muted;
-+ else
-+ pa_assert_not_reached();
-
-- pa_assert(s);
-+ pa_mute_control_set_mute(stream->mute_control, mute);
-
-- stream = s->userdata;
-- pa_hook_slot_free(stream->mute_changed_slot);
-- stream->mute_changed_slot = NULL;
-- pa_mute_control_free(s->own_mute_control);
-+ return PA_HOOK_OK;
- }
-
- static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
- struct stream *stream = userdata;
- pa_sink_input *input = NULL;
- pa_source_output *output = NULL;
-- const char *new_stream_description = NULL;
-- char *new_control_description;
-+ pa_proplist *proplist = NULL;
-+ const char *description = NULL;
-
- pa_assert(stream);
- pa_assert(call_data);
-@@ -407,9 +329,7 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
- if (input != stream->sink_input)
- return PA_HOOK_OK;
-
-- new_stream_description = get_sink_input_description(input);
-- if (!new_stream_description)
-- new_stream_description = stream->stream->name;
-+ proplist = stream->sink_input->proplist;
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-@@ -418,187 +338,290 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
- if (output != stream->source_output)
- return PA_HOOK_OK;
-
-- new_stream_description = get_source_output_description(output);
-- if (!new_stream_description)
-- new_stream_description = stream->stream->name;
-+ proplist = stream->source_output->proplist;
- break;
- }
-
-- pas_stream_description_changed(stream->stream, new_stream_description);
--
-- new_control_description = get_stream_volume_and_mute_control_description_malloc(stream);
-+ description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
-+ if (!description)
-+ description = stream->stream->name;
-
-- if (stream->stream->own_volume_control)
-- pa_volume_control_description_changed(stream->stream->own_volume_control, new_control_description);
--
-- if (stream->stream->own_mute_control)
-- pa_mute_control_description_changed(stream->stream->own_mute_control, new_control_description);
--
-- pa_xfree(new_control_description);
-+ pas_stream_set_description(stream->stream, description);
-
- return PA_HOOK_OK;
- }
-
--static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
-- struct stream *stream = userdata;
-- pa_client *client = call_data;
-- char *description;
--
-- pa_assert(stream);
-- pa_assert(client);
--
-- if (client != stream->client)
-- return PA_HOOK_OK;
--
-- description = get_stream_volume_and_mute_control_description_malloc(stream);
--
-- if (stream->stream->own_volume_control)
-- pa_volume_control_description_changed(stream->stream->own_volume_control, description);
--
-- if (stream->stream->own_mute_control)
-- pa_mute_control_description_changed(stream->stream->own_mute_control, description);
--
-- pa_xfree(description);
--
-- return PA_HOOK_OK;
--}
--
--static struct stream *stream_new(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
-- struct stream *stream;
-- const char *name = NULL;
-+static int stream_new(pa_stream_creator *creator, enum stream_type type, void *new_data, void *core_stream,
-+ struct stream **_r) {
-+ struct stream *stream = NULL;
-+ pa_proplist *proplist = NULL;
-+ pa_channel_map *channel_map = NULL;
-+ bool volume_available = false;
-+ pa_bvolume volume;
-+ pa_bvolume relative_volume;
-+ bool mute = false;
-+ const char *stream_name = NULL;
- const char *description = NULL;
-+ const char *volume_control_name = NULL;
-+ const char *relative_volume_control_name = NULL;
-+ const char *mute_control_name = NULL;
- pa_direction_t direction = PA_DIRECTION_OUTPUT;
-+ int r;
-+ const char *prop_key;
-+ void *state = NULL;
-
- pa_assert(creator);
- pa_assert(core_stream);
-+ pa_assert(_r);
-+
-+ pa_bvolume_init_invalid(&volume);
-+ pa_bvolume_init_invalid(&relative_volume);
-
- stream = pa_xnew0(struct stream, 1);
-+ stream->core = creator->volume_api->core;
- stream->creator = creator;
- stream->type = type;
-
- switch (type) {
- case STREAM_TYPE_SINK_INPUT:
-+ stream->sink_input_new_data = new_data;
- stream->sink_input = core_stream;
-- stream->client = stream->sink_input->client;
-- name = "sink-input-stream";
-
-- description = get_sink_input_description(stream->sink_input);
-- if (!description)
-- description = name;
-+ if (new_data) {
-+ stream->client = stream->sink_input_new_data->client;
-+ proplist = stream->sink_input_new_data->proplist;
-+ channel_map = &stream->sink_input_new_data->channel_map;
-+ volume_available = stream->sink_input_new_data->volume_writable;
-+
-+ if (volume_available) {
-+ if (!stream->sink_input_new_data->volume_is_set) {
-+ pa_cvolume cvolume;
-+
-+ pa_cvolume_reset(&cvolume, channel_map->channels);
-+ pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
-+ }
-+
-+ pa_bvolume_from_cvolume(&volume, &stream->sink_input_new_data->volume, channel_map);
-+ pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input_new_data->reference_ratio, channel_map);
-+ }
-+
-+ if (!stream->sink_input_new_data->muted_is_set)
-+ pa_sink_input_new_data_set_muted(stream->sink_input_new_data, false);
-+
-+ mute = stream->sink_input_new_data->muted;
-+ } else {
-+ stream->client = stream->sink_input->client;
-+ proplist = stream->sink_input->proplist;
-+ channel_map = &stream->sink_input->channel_map;
-+ pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, channel_map);
-+ pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input->reference_ratio, channel_map);
-+ mute = stream->sink_input->muted;
-+ }
-+
-+ stream_name = "sink-input-stream";
-+ volume_control_name = "sink-input-volume-control";
-+ relative_volume_control_name = "sink-input-relative-volume-control";
-+ mute_control_name = "sink-input-mute-control";
-
- direction = PA_DIRECTION_OUTPUT;
-+
-+ stream->proplist_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_proplist_changed_cb, stream);
-+ stream->volume_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_volume_changed_cb, stream);
-+ stream->reference_ratio_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_REFERENCE_RATIO_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_reference_ratio_changed_cb, stream);
-+ stream->mute_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_mute_changed_cb, stream);
- break;
-
- case STREAM_TYPE_SOURCE_OUTPUT:
-+ stream->source_output_new_data = new_data;
- stream->source_output = core_stream;
-- stream->client = stream->source_output->client;
-- name = "source-output-stream";
-
-- description = get_source_output_description(stream->source_output);
-- if (!description)
-- description = name;
-+ if (new_data) {
-+ stream->client = stream->source_output_new_data->client;
-+ proplist = stream->source_output_new_data->proplist;
-+ channel_map = &stream->source_output_new_data->channel_map;
-+ volume_available = stream->source_output_new_data->volume_writable;
-+
-+ if (volume_available) {
-+ if (!stream->source_output_new_data->volume_is_set) {
-+ pa_cvolume cvolume;
-+
-+ pa_cvolume_reset(&cvolume, channel_map->channels);
-+ pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
-+ }
-+
-+ pa_bvolume_from_cvolume(&volume, &stream->source_output_new_data->volume, channel_map);
-+ pa_bvolume_from_cvolume(&relative_volume, &stream->source_output_new_data->reference_ratio, channel_map);
-+ }
-+
-+ if (!stream->source_output_new_data->muted_is_set)
-+ pa_source_output_new_data_set_muted(stream->source_output_new_data, false);
-+
-+ mute = stream->source_output_new_data->muted;
-+ } else {
-+ stream->client = stream->source_output->client;
-+ proplist = stream->source_output->proplist;
-+ channel_map = &stream->source_output->channel_map;
-+ pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, channel_map);
-+ pa_bvolume_from_cvolume(&relative_volume, &stream->source_output->reference_ratio, channel_map);
-+ mute = stream->source_output->muted;
-+ }
-+
-+ stream_name = "source-output-stream";
-+ volume_control_name = "source-output-volume-control";
-+ relative_volume_control_name = "source-output-relative-volume-control";
-+ mute_control_name = "source-output-mute-control";
-
- direction = PA_DIRECTION_INPUT;
-- break;
-- }
--
-- stream->stream = pas_stream_new(creator->volume_api, name, description, direction);
-- stream->stream->create_own_volume_control = stream_create_own_volume_control_cb;
-- stream->stream->delete_own_volume_control = stream_delete_own_volume_control_cb;
-- stream->stream->create_own_mute_control = stream_create_own_mute_control_cb;
-- stream->stream->delete_own_mute_control = stream_delete_own_mute_control_cb;
-- stream->stream->userdata = stream;
-- pas_stream_set_have_own_volume_control(stream->stream, true);
-- pas_stream_set_have_own_mute_control(stream->stream, true);
-
-- switch (type) {
-- case STREAM_TYPE_SINK_INPUT:
- stream->proplist_changed_slot =
-- pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
- sink_input_or_source_output_proplist_changed_cb, stream);
-- break;
-
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- stream->proplist_changed_slot =
-- pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
-- PA_HOOK_NORMAL, sink_input_or_source_output_proplist_changed_cb, stream);
-+ if (volume_available) {
-+ stream->volume_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_volume_changed_cb, stream);
-+ stream->reference_ratio_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_REFERENCE_RATIO_CHANGED],
-+ PA_HOOK_NORMAL, sink_input_or_source_output_reference_ratio_changed_cb, stream);
-+ }
-+
-+ stream->mute_changed_slot =
-+ pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
-+ sink_input_or_source_output_mute_changed_cb, stream);
- break;
- }
-
-- stream->client_proplist_changed_slot =
-- pa_hook_connect(&stream->creator->volume_api->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
-- PA_HOOK_NORMAL, client_proplist_changed_cb, stream);
-+ r = pas_stream_new(creator->volume_api, stream_name, &stream->stream);
-+ if (r < 0)
-+ goto fail;
-
-- return stream;
--}
-+ description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
-+ if (!description)
-+ description = stream->stream->name;
-
--static void stream_put(struct stream *stream) {
-- pa_proplist *proplist = NULL;
-+ pas_stream_set_description(stream->stream, description);
-
-- pa_assert(stream);
-+ while ((prop_key = pa_proplist_iterate(proplist, &state)))
-+ pas_stream_set_property(stream->stream, prop_key, pa_proplist_gets(proplist, prop_key));
-
-- switch (stream->type) {
-- case STREAM_TYPE_SINK_INPUT:
-- proplist = stream->sink_input->proplist;
-- break;
-+ pas_stream_set_direction(stream->stream, direction);
-+ stream->stream->userdata = stream;
-
-- case STREAM_TYPE_SOURCE_OUTPUT:
-- proplist = stream->source_output->proplist;
-- break;
-+ if (volume_available) {
-+ r = pa_volume_control_new(stream->creator->volume_api, volume_control_name, false,
-+ &stream->volume_control);
-+ if (r >= 0) {
-+ pa_volume_control_set_description(stream->volume_control, _("Volume"));
-+ pa_volume_control_set_channel_map(stream->volume_control, channel_map);
-+ pa_volume_control_set_volume(stream->volume_control, &volume, true, true);
-+ pa_volume_control_set_convertible_to_dB(stream->volume_control, true);
-+ stream->volume_control->set_volume = volume_control_set_volume_cb;
-+ stream->volume_control->userdata = stream;
-+
-+ pas_stream_set_volume_control(stream->stream, stream->volume_control);
-+ }
-+
-+ r = pa_volume_control_new(stream->creator->volume_api, relative_volume_control_name, false,
-+ &stream->relative_volume_control);
-+ if (r >= 0) {
-+ pa_volume_control_set_description(stream->relative_volume_control, _("Relative volume"));
-+ pa_volume_control_set_channel_map(stream->relative_volume_control, channel_map);
-+ pa_volume_control_set_volume(stream->relative_volume_control, &relative_volume, true, true);
-+ pa_volume_control_set_convertible_to_dB(stream->relative_volume_control, true);
-+ pa_volume_control_set_purpose(stream->relative_volume_control, PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
-+ stream->stream);
-+ stream->relative_volume_control->set_volume = relative_volume_control_set_volume_cb;
-+ stream->relative_volume_control->userdata = stream;
-+
-+ pas_stream_set_relative_volume_control(stream->stream, stream->relative_volume_control);
-+ }
- }
-
-- pas_stream_put(stream->stream, proplist);
--}
-+ r = pa_mute_control_new(stream->creator->volume_api, mute_control_name, false, &stream->mute_control);
-+ if (r >= 0) {
-+ pa_mute_control_set_description(stream->mute_control, _("Mute"));
-+ pa_mute_control_set_mute(stream->mute_control, mute);
-+ pa_mute_control_set_purpose(stream->mute_control, PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE, stream->stream);
-+ stream->mute_control->set_mute = mute_control_set_mute_cb;
-+ stream->mute_control->userdata = stream;
-
--static void stream_unlink(struct stream *stream) {
-- pa_assert(stream);
-+ pas_stream_set_mute_control(stream->stream, stream->mute_control);
-+ }
-
-- if (stream->unlinked)
-- return;
-+ pas_stream_put(stream->stream);
-
-- stream->unlinked = true;
-+ if (stream->volume_control)
-+ pa_volume_control_put(stream->volume_control);
-
-- if (stream->stream)
-- pas_stream_unlink(stream->stream);
-+ if (stream->relative_volume_control)
-+ pa_volume_control_put(stream->relative_volume_control);
-+
-+ if (stream->mute_control)
-+ pa_mute_control_put(stream->mute_control);
-+
-+ *_r = stream;
-+ return 0;
-+
-+fail:
-+ if (stream)
-+ stream_free(stream);
-+
-+ return r;
- }
-
- static void stream_free(struct stream *stream) {
- pa_assert(stream);
-
-- if (!stream->unlinked)
-- stream_unlink(stream);
-+ if (stream->mute_changed_slot)
-+ pa_hook_slot_free(stream->mute_changed_slot);
-+
-+ if (stream->reference_ratio_changed_slot)
-+ pa_hook_slot_free(stream->reference_ratio_changed_slot);
-
-- if (stream->client_proplist_changed_slot)
-- pa_hook_slot_free(stream->client_proplist_changed_slot);
-+ if (stream->volume_changed_slot)
-+ pa_hook_slot_free(stream->volume_changed_slot);
-
- if (stream->proplist_changed_slot)
- pa_hook_slot_free(stream->proplist_changed_slot);
-
-+ if (stream->mute_control)
-+ pa_mute_control_free(stream->mute_control);
-+
-+ if (stream->relative_volume_control)
-+ pa_volume_control_free(stream->relative_volume_control);
-+
-+ if (stream->volume_control)
-+ pa_volume_control_free(stream->volume_control);
-+
- if (stream->stream)
- pas_stream_free(stream->stream);
-
- pa_xfree(stream);
- }
-
--static void create_stream(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
-+static pa_hook_result_t sink_input_fixate_cb(void *hook_data, void *call_data, void *userdata) {
-+ pa_stream_creator *creator = userdata;
-+ pa_sink_input_new_data *data = call_data;
-+ int r;
- struct stream *stream;
-
- pa_assert(creator);
-- pa_assert(core_stream);
-+ pa_assert(data);
-
-- stream = stream_new(creator, type, core_stream);
-- pa_hashmap_put(creator->streams, core_stream, stream);
-- stream_put(stream);
--}
--
--static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *userdata) {
-- pa_stream_creator *creator = userdata;
-- pa_sink_input *input = call_data;
--
-- pa_assert(creator);
-- pa_assert(input);
-+ r = stream_new(creator, STREAM_TYPE_SINK_INPUT, data, data->sink_input, &stream);
-+ if (r < 0)
-+ return PA_HOOK_OK;
-
-- create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
-+ pa_hashmap_put(creator->streams, stream->sink_input, stream);
-
- return PA_HOOK_OK;
- }
-@@ -615,14 +638,20 @@ static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, v
- return PA_HOOK_OK;
- }
-
--static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *userdata) {
-+static pa_hook_result_t source_output_fixate_cb(void *hook_data, void *call_data, void *userdata) {
- pa_stream_creator *creator = userdata;
-- pa_source_output *output = call_data;
-+ pa_source_output_new_data *data = call_data;
-+ int r;
-+ struct stream *stream;
-
- pa_assert(creator);
-- pa_assert(output);
-+ pa_assert(data);
-+
-+ r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, data, data->source_output, &stream);
-+ if (r < 0)
-+ return PA_HOOK_OK;
-
-- create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
-+ pa_hashmap_put(creator->streams, stream->source_output, stream);
-
- return PA_HOOK_OK;
- }
-@@ -644,26 +673,34 @@ pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) {
- uint32_t idx;
- pa_sink_input *input;
- pa_source_output *output;
-+ int r;
-+ struct stream *stream;
-
- pa_assert(api);
-
- creator = pa_xnew0(pa_stream_creator, 1);
- creator->volume_api = api;
- creator->streams = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) stream_free);
-- creator->sink_input_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_NORMAL,
-- sink_input_put_cb, creator);
-+ creator->sink_input_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL,
-+ sink_input_fixate_cb, creator);
- creator->sink_input_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL,
- sink_input_unlink_cb, creator);
-- creator->source_output_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
-- source_output_put_cb, creator);
-+ creator->source_output_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL,
-+ source_output_fixate_cb, creator);
- creator->source_output_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL,
- source_output_unlink_cb, creator);
-
-- PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx)
-- create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
-+ PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx) {
-+ r = stream_new(creator, STREAM_TYPE_SINK_INPUT, NULL, input, &stream);
-+ if (r >= 0)
-+ pa_hashmap_put(creator->streams, stream->sink_input, stream);
-+ }
-
-- PA_IDXSET_FOREACH(output, api->core->source_outputs, idx)
-- create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
-+ PA_IDXSET_FOREACH(output, api->core->source_outputs, idx) {
-+ r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, NULL, output, &stream);
-+ if (r >= 0)
-+ pa_hashmap_put(creator->streams, stream->source_output, stream);
-+ }
-
- return creator;
- }
-@@ -677,14 +714,14 @@ void pa_stream_creator_free(pa_stream_creator *creator) {
- if (creator->source_output_unlink_slot)
- pa_hook_slot_free(creator->source_output_unlink_slot);
-
-- if (creator->source_output_put_slot)
-- pa_hook_slot_free(creator->source_output_put_slot);
-+ if (creator->source_output_fixate_slot)
-+ pa_hook_slot_free(creator->source_output_fixate_slot);
-
- if (creator->sink_input_unlink_slot)
- pa_hook_slot_free(creator->sink_input_unlink_slot);
-
-- if (creator->sink_input_put_slot)
-- pa_hook_slot_free(creator->sink_input_put_slot);
-+ if (creator->sink_input_fixate_slot)
-+ pa_hook_slot_free(creator->sink_input_fixate_slot);
-
- if (creator->streams)
- pa_hashmap_free(creator->streams);
-diff --git a/src/modules/volume-api/volume-api.c b/src/modules/volume-api/volume-api.c
-index 9abea7e..4a8a2e6 100644
---- a/src/modules/volume-api/volume-api.c
-+++ b/src/modules/volume-api/volume-api.c
-@@ -26,16 +26,20 @@
- #include "volume-api.h"
-
- #include <modules/volume-api/audio-group.h>
--#include <modules/volume-api/binding.h>
- #include <modules/volume-api/device.h>
- #include <modules/volume-api/device-creator.h>
-+#include <modules/volume-api/inidb.h>
- #include <modules/volume-api/sstream.h>
- #include <modules/volume-api/stream-creator.h>
- #include <modules/volume-api/volume-control.h>
-
- #include <pulsecore/core-util.h>
-+#include <pulsecore/namereg.h>
- #include <pulsecore/shared.h>
-
-+#define CONTROL_DB_TABLE_NAME_VOLUME_CONTROL "VolumeControl"
-+#define CONTROL_DB_TABLE_NAME_MUTE_CONTROL "MuteControl"
-+
- static pa_volume_api *volume_api_new(pa_core *core);
- static void volume_api_free(pa_volume_api *api);
-
-@@ -76,44 +80,209 @@ void pa_volume_api_unref(pa_volume_api *api) {
- }
- }
-
--void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
-- pa_assert(api);
-- pa_assert(type);
-+static int control_db_get_volume_control_cb(pa_inidb *db, const char *name, void **_r) {
-+ pa_volume_api *api;
-+ pa_volume_control *control;
-+
-+ pa_assert(db);
-+ pa_assert(name);
-+ pa_assert(_r);
-+
-+ api = pa_inidb_get_userdata(db);
-
-- pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0);
-+ control = pa_hashmap_get(api->volume_controls_from_db, name);
-+ if (!control) {
-+ int r;
-
-- pa_log_debug("Added binding target type %s.", type->name);
-+ r = pa_volume_control_new(api, name, true, &control);
-+ if (r < 0)
-+ return r;
-
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type);
-+ pa_hashmap_put(api->volume_controls_from_db, (void *) control->name, control);
-+ }
-+
-+ *_r = control;
-+ return 0;
- }
-
--void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
-- pa_assert(api);
-- pa_assert(type);
-+static int control_db_parse_volume_control_description_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_volume_control *control = object;
-+
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-
-- pa_log_debug("Removing binding target type %s.", type->name);
-+ pa_volume_control_set_description(control, value);
-
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type);
-+ return 0;
-+}
-+
-+static int control_db_parse_volume_control_volume_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_volume_control *control = object;
-+ int r;
-+ pa_bvolume bvolume;
-+
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-+
-+ r = pa_atou(value, &bvolume.volume);
-+ if (r < 0)
-+ return -PA_ERR_INVALID;
-+
-+ if (!PA_VOLUME_IS_VALID(bvolume.volume))
-+ return -PA_ERR_INVALID;
-
-- pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name));
-+ pa_volume_control_set_volume(control, &bvolume, true, false);
-+
-+ return 0;
- }
-
--static void create_builtin_binding_target_types(pa_volume_api *api) {
-- pa_binding_target_type *type;
-+static int control_db_parse_volume_control_balance_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_volume_control *control = object;
-+ int r;
-+ pa_bvolume bvolume;
-
-- pa_assert(api);
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-+
-+ r = pa_bvolume_parse_balance(value, &bvolume);
-+ if (r < 0)
-+ return -PA_ERR_INVALID;
-+
-+ pa_volume_control_set_channel_map(control, &bvolume.channel_map);
-+ pa_volume_control_set_volume(control, &bvolume, false, true);
-+
-+ return 0;
-+}
-+
-+static int control_db_parse_volume_control_convertible_to_dB_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_volume_control *control = object;
-+ int r;
-+
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-+
-+ r = pa_parse_boolean(value);
-+ if (r < 0)
-+ return -PA_ERR_INVALID;
-+
-+ pa_volume_control_set_convertible_to_dB(control, r);
-+
-+ return 0;
-+}
-+
-+static int control_db_get_mute_control_cb(pa_inidb *db, const char *name, void **_r) {
-+ pa_volume_api *api;
-+ pa_mute_control *control;
-+
-+ pa_assert(db);
-+ pa_assert(name);
-+ pa_assert(_r);
-+
-+ api = pa_inidb_get_userdata(db);
-+
-+ control = pa_hashmap_get(api->mute_controls_from_db, name);
-+ if (!control) {
-+ int r;
-+
-+ r = pa_mute_control_new(api, name, true, &control);
-+ if (r < 0)
-+ return r;
-+
-+ pa_hashmap_put(api->mute_controls_from_db, (void *) control->name, control);
-+ }
-+
-+ *_r = control;
-+ return 0;
-+}
-+
-+static int control_db_parse_mute_control_description_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_mute_control *control = object;
-
-- type = pa_audio_group_create_binding_target_type(api);
-- pa_volume_api_add_binding_target_type(api, type);
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-+
-+ pa_mute_control_set_description(control, value);
-+
-+ return 0;
- }
-
--static void delete_builtin_binding_target_types(pa_volume_api *api) {
-- pa_binding_target_type *type;
-+static int control_db_parse_mute_control_mute_cb(pa_inidb *db, const char *value, void *object) {
-+ pa_mute_control *control = object;
-+ int mute;
-+
-+ pa_assert(db);
-+ pa_assert(value);
-+ pa_assert(control);
-+
-+ mute = pa_parse_boolean(value);
-+ if (mute < 0)
-+ return -PA_ERR_INVALID;
-+
-+ pa_mute_control_set_mute(control, mute);
-+
-+ return 0;
-+}
-+
-+static void create_control_db(pa_volume_api *api) {
-+ pa_volume_control *volume_control;
-+ pa_mute_control *mute_control;
-+
-+ pa_assert(api);
-+ pa_assert(!api->control_db.db);
-+
-+ api->control_db.db = pa_inidb_new(api->core, "controls", api);
-+
-+ api->control_db.volume_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_VOLUME_CONTROL,
-+ control_db_get_volume_control_cb);
-+ pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
-+ control_db_parse_volume_control_description_cb);
-+ pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME,
-+ control_db_parse_volume_control_volume_cb);
-+ pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE,
-+ control_db_parse_volume_control_balance_cb);
-+ pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB,
-+ control_db_parse_volume_control_convertible_to_dB_cb);
-+
-+ api->control_db.mute_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_MUTE_CONTROL,
-+ control_db_get_mute_control_cb);
-+ pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
-+ control_db_parse_mute_control_description_cb);
-+ pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE,
-+ control_db_parse_mute_control_mute_cb);
-+
-+ api->volume_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+ api->mute_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-+
-+ pa_inidb_load(api->control_db.db);
-+
-+ while ((volume_control = pa_hashmap_steal_first(api->volume_controls_from_db)))
-+ pa_volume_control_put(volume_control);
-
-+ pa_hashmap_free(api->volume_controls_from_db);
-+ api->volume_controls_from_db = NULL;
-+
-+ while ((mute_control = pa_hashmap_steal_first(api->mute_controls_from_db)))
-+ pa_mute_control_put(mute_control);
-+
-+ pa_hashmap_free(api->mute_controls_from_db);
-+ api->mute_controls_from_db = NULL;
-+}
-+
-+static void delete_control_db(pa_volume_api *api) {
- pa_assert(api);
-
-- type = pa_hashmap_get(api->binding_target_types, PA_AUDIO_GROUP_BINDING_TARGET_TYPE);
-- pa_volume_api_remove_binding_target_type(api, type);
-+ if (!api->control_db.db)
-+ return;
-+
-+ pa_inidb_free(api->control_db.db);
-+ api->control_db.mute_controls = NULL;
-+ api->control_db.volume_controls = NULL;
-+ api->control_db.db = NULL;
- }
-
- static void create_objects_defer_event_cb(pa_mainloop_api *mainloop_api, pa_defer_event *event, void *userdata) {
-@@ -138,7 +307,6 @@ static pa_volume_api *volume_api_new(pa_core *core) {
- api = pa_xnew0(pa_volume_api, 1);
- api->core = core;
- api->refcnt = 1;
-- api->binding_target_types = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- api->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
- api->volume_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- api->mute_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-@@ -149,7 +317,7 @@ static pa_volume_api *volume_api_new(pa_core *core) {
- for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
- pa_hook_init(&api->hooks[i], api);
-
-- create_builtin_binding_target_types(api);
-+ create_control_db(api);
-
- /* We delay the object creation to ensure that policy modules have a chance
- * to affect the initialization of the objects. If we created the objects
-@@ -170,6 +338,9 @@ static void volume_api_free(pa_volume_api *api) {
-
- pa_log_debug("Freeing the pa_volume_api object.");
-
-+ pa_assert(!api->mute_controls_from_db);
-+ pa_assert(!api->volume_controls_from_db);
-+
- if (api->stream_creator)
- pa_stream_creator_free(api->stream_creator);
-
-@@ -179,8 +350,7 @@ static void volume_api_free(pa_volume_api *api) {
- if (api->create_objects_defer_event)
- api->core->mainloop->defer_free(api->create_objects_defer_event);
-
-- if (api->binding_target_types)
-- delete_builtin_binding_target_types(api);
-+ delete_control_db(api);
-
- for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
- pa_hook_done(&api->hooks[i]);
-@@ -201,12 +371,24 @@ static void volume_api_free(pa_volume_api *api) {
- }
-
- if (api->mute_controls) {
-- pa_assert(pa_hashmap_isempty(api->mute_controls));
-+ pa_mute_control *control;
-+
-+ while ((control = pa_hashmap_first(api->mute_controls))) {
-+ pa_assert(!control->present);
-+ pa_mute_control_free(control);
-+ }
-+
- pa_hashmap_free(api->mute_controls);
- }
-
- if (api->volume_controls) {
-- pa_assert(pa_hashmap_isempty(api->volume_controls));
-+ pa_volume_control *control;
-+
-+ while ((control = pa_hashmap_first(api->volume_controls))) {
-+ pa_assert(!control->present);
-+ pa_volume_control_free(control);
-+ }
-+
- pa_hashmap_free(api->volume_controls);
- }
-
-@@ -215,11 +397,6 @@ static void volume_api_free(pa_volume_api *api) {
- pa_hashmap_free(api->names);
- }
-
-- if (api->binding_target_types) {
-- pa_assert(pa_hashmap_isempty(api->binding_target_types));
-- pa_hashmap_free(api->binding_target_types);
-- }
--
- pa_xfree(api);
- }
-
-@@ -231,19 +408,24 @@ int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name,
- pa_assert(requested_name);
- pa_assert(registered_name);
-
-+ if (!pa_namereg_is_valid_name(requested_name)) {
-+ pa_log("Invalid name: \"%s\"", requested_name);
-+ return -PA_ERR_INVALID;
-+ }
-+
- n = pa_xstrdup(requested_name);
-
- if (pa_hashmap_put(api->names, n, n) < 0) {
- unsigned i = 1;
-
-- pa_xfree(n);
--
- if (fail_if_already_registered) {
-+ pa_xfree(n);
- pa_log("Name %s already registered.", requested_name);
- return -PA_ERR_EXIST;
- }
-
- do {
-+ pa_xfree(n);
- i++;
- n = pa_sprintf_malloc("%s.%u", requested_name, i);
- } while (pa_hashmap_put(api->names, n, n) < 0);
-@@ -271,38 +453,6 @@ uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) {
- return idx;
- }
-
--static void set_main_output_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
-- pa_volume_control *old_control;
--
-- pa_assert(api);
--
-- old_control = api->main_output_volume_control;
--
-- if (control == old_control)
-- return;
--
-- api->main_output_volume_control = control;
-- pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-- control ? control->name : "(unset)");
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
--}
--
--static void set_main_input_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
-- pa_volume_control *old_control;
--
-- pa_assert(api);
--
-- old_control = api->main_input_volume_control;
--
-- if (control == old_control)
-- return;
--
-- api->main_input_volume_control = control;
-- pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-- control ? control->name : "(unset)");
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
--}
--
- void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control) {
- pa_assert(api);
- pa_assert(control);
-@@ -318,10 +468,10 @@ int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *c
- return -1;
-
- if (control == api->main_output_volume_control)
-- set_main_output_volume_control_internal(api, NULL);
-+ pa_volume_api_set_main_output_volume_control(api, NULL);
-
- if (control == api->main_input_volume_control)
-- set_main_input_volume_control_internal(api, NULL);
-+ pa_volume_api_set_main_input_volume_control(api, NULL);
-
- return 0;
- }
-@@ -350,38 +500,6 @@ uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) {
- return idx;
- }
-
--static void set_main_output_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
-- pa_mute_control *old_control;
--
-- pa_assert(api);
--
-- old_control = api->main_output_mute_control;
--
-- if (control == old_control)
-- return;
--
-- api->main_output_mute_control = control;
-- pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-- control ? control->name : "(unset)");
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
--}
--
--static void set_main_input_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
-- pa_mute_control *old_control;
--
-- pa_assert(api);
--
-- old_control = api->main_input_mute_control;
--
-- if (control == old_control)
-- return;
--
-- api->main_input_mute_control = control;
-- pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-- control ? control->name : "(unset)");
-- pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
--}
--
- void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control) {
- pa_assert(api);
- pa_assert(control);
-@@ -397,10 +515,10 @@ int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *contr
- return -1;
-
- if (control == api->main_output_mute_control)
-- set_main_output_mute_control_internal(api, NULL);
-+ pa_volume_api_set_main_output_mute_control(api, NULL);
-
- if (control == api->main_input_mute_control)
-- set_main_input_mute_control_internal(api, NULL);
-+ pa_volume_api_set_main_input_mute_control(api, NULL);
-
- return 0;
- }
-@@ -543,105 +661,73 @@ pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint3
- }
-
- void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control) {
-+ pa_volume_control *old_control;
-+
- pa_assert(api);
-
-- if (api->main_output_volume_control_binding) {
-- pa_binding_free(api->main_output_volume_control_binding);
-- api->main_output_volume_control_binding = NULL;
-- }
-+ old_control = api->main_output_volume_control;
-
-- set_main_output_volume_control_internal(api, control);
--}
-+ if (control == old_control)
-+ return;
-
--void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
-- pa_assert(api);
-+ api->main_output_volume_control = control;
-
-- if (api->main_input_volume_control_binding) {
-- pa_binding_free(api->main_input_volume_control_binding);
-- api->main_input_volume_control_binding = NULL;
-- }
-+ pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-+ control ? control->name : "(unset)");
-
-- set_main_input_volume_control_internal(api, control);
-+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
- }
-
--void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
-+void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
-+ pa_volume_control *old_control;
-+
- pa_assert(api);
-
-- if (api->main_output_mute_control_binding) {
-- pa_binding_free(api->main_output_mute_control_binding);
-- api->main_output_mute_control_binding = NULL;
-- }
-+ old_control = api->main_input_volume_control;
-
-- set_main_output_mute_control_internal(api, control);
--}
-+ if (control == old_control)
-+ return;
-
--void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
-- pa_assert(api);
-+ api->main_input_volume_control = control;
-
-- if (api->main_input_mute_control_binding) {
-- pa_binding_free(api->main_input_mute_control_binding);
-- api->main_input_mute_control_binding = NULL;
-- }
-+ pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-+ control ? control->name : "(unset)");
-
-- set_main_input_mute_control_internal(api, control);
-+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
- }
-
--void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = api,
-- .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
-- };
-+void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
-+ pa_mute_control *old_control;
-
- pa_assert(api);
-- pa_assert(target_info);
--
-- if (api->main_output_volume_control_binding)
-- pa_binding_free(api->main_output_volume_control_binding);
-
-- api->main_output_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
--}
-+ old_control = api->main_output_mute_control;
-
--void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = api,
-- .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
-- };
-+ if (control == old_control)
-+ return;
-
-- pa_assert(api);
-- pa_assert(target_info);
-+ api->main_output_mute_control = control;
-
-- if (api->main_input_volume_control_binding)
-- pa_binding_free(api->main_input_volume_control_binding);
-+ pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-+ control ? control->name : "(unset)");
-
-- api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
-+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
- }
-
--void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = api,
-- .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
-- };
-+void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
-+ pa_mute_control *old_control;
-
- pa_assert(api);
-- pa_assert(target_info);
--
-- if (api->main_output_mute_control_binding)
-- pa_binding_free(api->main_output_mute_control_binding);
-
-- api->main_output_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
--}
-+ old_control = api->main_input_mute_control;
-
--void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
-- pa_binding_owner_info owner_info = {
-- .userdata = api,
-- .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
-- };
-+ if (control == old_control)
-+ return;
-
-- pa_assert(api);
-- pa_assert(target_info);
-+ api->main_input_mute_control = control;
-
-- if (api->main_input_mute_control_binding)
-- pa_binding_free(api->main_input_mute_control_binding);
-+ pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
-+ control ? control->name : "(unset)");
-
-- api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
-+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
- }
-diff --git a/src/modules/volume-api/volume-api.h b/src/modules/volume-api/volume-api.h
-index 73a1410..f99182a 100644
---- a/src/modules/volume-api/volume-api.h
-+++ b/src/modules/volume-api/volume-api.h
-@@ -24,27 +24,56 @@
-
- #include <pulsecore/core.h>
-
-+#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION "description"
-+#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME "volume"
-+#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE "balance"
-+#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB "convertible-to-dB"
-+#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE "mute"
-+
- typedef struct pa_volume_api pa_volume_api;
-
- /* Avoid circular dependencies... */
- typedef struct pa_audio_group pa_audio_group;
--typedef struct pa_binding pa_binding;
--typedef struct pa_binding_target_info pa_binding_target_info;
--typedef struct pa_binding_target_type pa_binding_target_type;
- typedef struct pa_device pa_device;
- typedef struct pa_device_creator pa_device_creator;
-+typedef struct pa_inidb pa_inidb;
-+typedef struct pa_inidb_table pa_inidb_table;
- typedef struct pa_mute_control pa_mute_control;
- typedef struct pas_stream pas_stream;
- typedef struct pa_stream_creator pa_stream_creator;
- typedef struct pa_volume_control pa_volume_control;
-
- enum {
-- PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED,
-- PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED,
-+ /* This is fired after the volume control implementation has done its part
-+ * of the volume control initialization, but before policy modules have
-+ * done their part of the initialization. Hook users are expected to not
-+ * modify the volume control state in this hook. */
-+ PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED,
-+
-+ /* Policy modules can use this hook to initialize the volume control
-+ * volume. This is fired before PUT. If a policy module sets the volume, it
-+ * should return PA_HOOK_STOP to prevent lower-priority policy modules from
-+ * modifying the volume. */
-+ PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME,
-+
- PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT,
- PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK,
- PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED,
- PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED,
-+ PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED,
-+
-+ /* This is fired after the mute control implementation has done its part of
-+ * the mute control initialization, but before policy modules have done
-+ * their part of the initialization. Hook users are expected to not modify
-+ * the mute control state in this hook. */
-+ PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED,
-+
-+ /* Policy modules can use this hook to initialize the mute control mute.
-+ * This is fired before PUT. If a policy module sets the mute, it should
-+ * return PA_HOOK_STOP to prevent lower-priority policy modules from
-+ * modifying the mute. */
-+ PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE,
-+
- PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT,
- PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK,
- PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED,
-@@ -54,26 +83,16 @@ enum {
- PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED,
- PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED,
- PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED,
--
-- /* Policy modules can use this to set the initial volume control for a
-- * stream. The hook callback should use pas_stream_set_volume_control() to
-- * set the volume control. The hook callback should not do anything if
-- * stream->volume_control is already non-NULL. */
-- PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL,
--
-- /* Policy modules can use this to set the initial mute control for a
-- * stream. The hook callback should use pas_stream_set_mute_control() to
-- * set the mute control. The hook callback should not do anything if
-- * stream->mute_control is already non-NULL. */
-- PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL,
--
- PA_VOLUME_API_HOOK_STREAM_PUT,
- PA_VOLUME_API_HOOK_STREAM_UNLINK,
- PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED,
-+ PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED,
- PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED,
-+ PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED,
- PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED,
- PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT,
- PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK,
-+ PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED,
- PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED,
- PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED,
- PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
-@@ -86,7 +105,6 @@ enum {
- struct pa_volume_api {
- pa_core *core;
- unsigned refcnt;
-- pa_hashmap *binding_target_types; /* name -> pa_binding_target_type */
- pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
- pa_hashmap *volume_controls; /* name -> pa_volume_control */
- pa_hashmap *mute_controls; /* name -> pa_mute_control */
-@@ -103,27 +121,28 @@ struct pa_volume_api {
- uint32_t next_device_index;
- uint32_t next_stream_index;
- uint32_t next_audio_group_index;
-- pa_binding *main_output_volume_control_binding;
-- pa_binding *main_input_volume_control_binding;
-- pa_binding *main_output_mute_control_binding;
-- pa_binding *main_input_mute_control_binding;
- pa_hook hooks[PA_VOLUME_API_HOOK_MAX];
-+
-+ struct {
-+ pa_inidb *db;
-+ pa_inidb_table *volume_controls;
-+ pa_inidb_table *mute_controls;
-+ } control_db;
-+
- pa_defer_event *create_objects_defer_event;
- pa_device_creator *device_creator;
- pa_stream_creator *stream_creator;
-+
-+ pa_hashmap *volume_controls_from_db; /* control name -> pa_volume_control, only used during initialization. */
-+ pa_hashmap *mute_controls_from_db; /* control name -> pa_mute_control, only used during initialization. */
- };
-
- pa_volume_api *pa_volume_api_get(pa_core *core);
- pa_volume_api *pa_volume_api_ref(pa_volume_api *api);
- void pa_volume_api_unref(pa_volume_api *api);
-
--void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
--void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
--
--/* If fail_if_already_registered is false, this function never fails. */
- int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered,
- const char **registered_name);
--
- void pa_volume_api_unregister_name(pa_volume_api *api, const char *name);
-
- uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api);
-@@ -155,9 +174,5 @@ void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_
- void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control);
- void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control);
- void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control);
--void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
--void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
--void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
--void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
-
- #endif
-diff --git a/src/modules/volume-api/volume-control.c b/src/modules/volume-api/volume-control.c
-index c7f5dbb..bf4db71 100644
---- a/src/modules/volume-api/volume-control.c
-+++ b/src/modules/volume-api/volume-control.c
-@@ -27,88 +27,96 @@
-
- #include <modules/volume-api/audio-group.h>
- #include <modules/volume-api/device.h>
-+#include <modules/volume-api/inidb.h>
- #include <modules/volume-api/sstream.h>
-
- #include <pulsecore/core-util.h>
-
--pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
-- bool channel_map_is_writable) {
-- pa_volume_control *control;
-+int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r) {
-+ pa_volume_control *control = NULL;
-+ int r;
-
- pa_assert(api);
- pa_assert(name);
-- pa_assert(description);
-+ pa_assert(_r);
-
- control = pa_xnew0(pa_volume_control, 1);
- control->volume_api = api;
- control->index = pa_volume_api_allocate_volume_control_index(api);
-- pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
-- control->description = pa_xstrdup(description);
-+
-+ r = pa_volume_api_register_name(api, name, persistent, &control->name);
-+ if (r < 0)
-+ goto fail;
-+
-+ control->description = pa_xstrdup(control->name);
- control->proplist = pa_proplist_new();
-- pa_bvolume_init_invalid(&control->volume);
-- control->convertible_to_dB = convertible_to_dB;
-- control->channel_map_is_writable = channel_map_is_writable;
-+ pa_bvolume_init_mono(&control->volume, PA_VOLUME_NORM);
-+ control->present = !persistent;
-+ control->persistent = persistent;
-+ control->purpose = PA_VOLUME_CONTROL_PURPOSE_OTHER;
- control->devices = pa_hashmap_new(NULL, NULL);
- control->default_for_devices = pa_hashmap_new(NULL, NULL);
-- control->streams = pa_hashmap_new(NULL, NULL);
-- control->audio_groups = pa_hashmap_new(NULL, NULL);
-
-- return control;
-+ if (persistent) {
-+ pa_inidb_row *row;
-+
-+ row = pa_inidb_table_add_row(api->control_db.volume_controls, control->name);
-+ control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
-+ control->db_cells.volume = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME);
-+ control->db_cells.balance = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE);
-+ control->db_cells.convertible_to_dB = pa_inidb_row_get_cell(row,
-+ PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB);
-+ }
-+
-+ *_r = control;
-+ return 0;
-+
-+fail:
-+ if (control)
-+ pa_volume_control_free(control);
-+
-+ return r;
- }
-
--void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
-- pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb) {
-+void pa_volume_control_put(pa_volume_control *control) {
- const char *prop_key;
- void *state = NULL;
- char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
- char balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
-
- pa_assert(control);
-- pa_assert((initial_volume && pa_bvolume_valid(initial_volume, true, true)) || control->set_volume);
-- pa_assert((initial_volume && pa_channel_map_valid(&initial_volume->channel_map)) || control->channel_map_is_writable);
-- pa_assert(set_initial_volume_cb || !control->set_volume);
--
-- if (initial_volume && pa_bvolume_valid(initial_volume, true, false))
-- control->volume.volume = initial_volume->volume;
-- else
-- control->volume.volume = PA_VOLUME_NORM / 3;
--
-- if (initial_volume && pa_bvolume_valid(initial_volume, false, true))
-- pa_bvolume_copy_balance(&control->volume, initial_volume);
-- else if (initial_volume && pa_channel_map_valid(&initial_volume->channel_map))
-- pa_bvolume_reset_balance(&control->volume, &initial_volume->channel_map);
-- else {
-- pa_channel_map_init_mono(&control->volume.channel_map);
-- pa_bvolume_reset_balance(&control->volume, &control->volume.channel_map);
-- }
-+ pa_assert(control->set_volume || !control->present);
-
-- if (set_initial_volume_cb)
-- set_initial_volume_cb(control);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED], control);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], control);
-
-- pa_volume_api_add_volume_control(control->volume_api, control);
-+ if (control->set_volume) {
-+ control->set_volume_in_progress = true;
-+ control->set_volume(control, &control->volume, &control->volume, true, true);
-+ control->set_volume_in_progress = false;
-+ }
-
-+ pa_volume_api_add_volume_control(control->volume_api, control);
- control->linked = true;
-
- pa_log_debug("Created volume control #%u.", control->index);
- pa_log_debug(" Name: %s", control->name);
- pa_log_debug(" Description: %s", control->description);
-+ pa_log_debug(" Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
-+ control->convertible_to_dB));
-+ pa_log_debug(" Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
-+ pa_log_debug(" Present: %s", pa_yes_no(control->present));
-+ pa_log_debug(" Persistent: %s", pa_yes_no(control->persistent));
- pa_log_debug(" Properties:");
-
- while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
- pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(control->proplist, prop_key)));
-
-- pa_log_debug(" Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
-- control->convertible_to_dB));
-- pa_log_debug(" Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
-- pa_log_debug(" Channel map is writable: %s", pa_yes_no(control->channel_map_is_writable));
--
- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT], control);
- }
-
- void pa_volume_control_unlink(pa_volume_control *control) {
-- pa_audio_group *group;
- pa_device *device;
-- pas_stream *stream;
-
- pa_assert(control);
-
-@@ -122,15 +130,9 @@ void pa_volume_control_unlink(pa_volume_control *control) {
- pa_log_debug("Unlinking volume control %s.", control->name);
-
- if (control->linked)
-- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
--
-- pa_volume_api_remove_volume_control(control->volume_api, control);
--
-- while ((group = pa_hashmap_first(control->audio_groups)))
-- pa_audio_group_set_volume_control(group, NULL);
-+ pa_volume_api_remove_volume_control(control->volume_api, control);
-
-- while ((stream = pa_hashmap_first(control->streams)))
-- pas_stream_set_volume_control(stream, NULL);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
-
- while ((device = pa_hashmap_first(control->default_for_devices)))
- pa_device_set_default_volume_control(device, NULL);
-@@ -153,19 +155,10 @@ void pa_volume_control_unlink(pa_volume_control *control) {
- void pa_volume_control_free(pa_volume_control *control) {
- pa_assert(control);
-
-- if (!control->unlinked)
-+ /* unlink() expects name to be set. */
-+ if (!control->unlinked && control->name)
- pa_volume_control_unlink(control);
-
-- if (control->audio_groups) {
-- pa_assert(pa_hashmap_isempty(control->audio_groups));
-- pa_hashmap_free(control->audio_groups);
-- }
--
-- if (control->streams) {
-- pa_assert(pa_hashmap_isempty(control->streams));
-- pa_hashmap_free(control->streams);
-- }
--
- if (control->default_for_devices) {
- pa_assert(pa_hashmap_isempty(control->default_for_devices));
- pa_hashmap_free(control->default_for_devices);
-@@ -187,17 +180,91 @@ void pa_volume_control_free(pa_volume_control *control) {
- pa_xfree(control);
- }
-
--void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) {
-+void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner) {
-+ pa_assert(control);
-+ pa_assert(!control->linked);
-+
-+ control->purpose = purpose;
-+ control->owner = owner;
-+}
-+
-+int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
-+ pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata) {
- pa_assert(control);
- pa_assert(group);
-+ pa_assert(set_volume_cb);
-+
-+ if (control->present) {
-+ pa_log("Can't acquire volume control %s, it's already present.", control->name);
-+ return -PA_ERR_BUSY;
-+ }
-+
-+ control->set_volume = set_volume_cb;
-+ control->userdata = userdata;
-+
-+ control->set_volume_in_progress = true;
-+ control->set_volume(control, &control->volume, &control->volume, true, true);
-+ control->set_volume_in_progress = false;
-+
-+ control->present = true;
-+
-+ if (!control->linked || control->unlinked)
-+ return 0;
-+
-+ pa_log_debug("Volume control %s became present.", control->name);
-+
-+ return 0;
-+}
-+
-+void pa_volume_control_release(pa_volume_control *control) {
-+ pa_assert(control);
-+
-+ if (!control->present)
-+ return;
-+
-+ control->present = false;
-+
-+ control->userdata = NULL;
-+ control->set_volume = NULL;
-+
-+ if (!control->linked || control->unlinked)
-+ return;
-+
-+ pa_log_debug("Volume control %s became not present.", control->name);
-+}
-+
-+void pa_volume_control_set_description(pa_volume_control *control, const char *description) {
-+ char *old_description;
-+
-+ pa_assert(control);
-+ pa_assert(description);
-
-- control->owner_audio_group = group;
-+ old_description = control->description;
-+
-+ if (pa_streq(description, old_description))
-+ return;
-+
-+ control->description = pa_xstrdup(description);
-+
-+ if (control->persistent)
-+ pa_inidb_cell_set_value(control->db_cells.description, description);
-+
-+ if (!control->linked || control->unlinked) {
-+ pa_xfree(old_description);
-+ return;
-+ }
-+
-+ pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
-+ description);
-+ pa_xfree(old_description);
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
- }
-
- static void set_volume_internal(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
- pa_bvolume old_volume;
- bool volume_changed;
- bool balance_changed;
-+ char *str;
-
- pa_assert(control);
- pa_assert(volume);
-@@ -209,12 +276,26 @@ static void set_volume_internal(pa_volume_control *control, const pa_bvolume *vo
- if (!volume_changed && !balance_changed)
- return;
-
-- if (volume_changed)
-+ if (volume_changed) {
- control->volume.volume = volume->volume;
-
-- if (balance_changed)
-+ if (control->persistent) {
-+ str = pa_sprintf_malloc("%u", control->volume.volume);
-+ pa_inidb_cell_set_value(control->db_cells.volume, str);
-+ pa_xfree(str);
-+ }
-+ }
-+
-+ if (balance_changed) {
- pa_bvolume_copy_balance(&control->volume, volume);
-
-+ if (control->persistent) {
-+ pa_assert_se(pa_bvolume_balance_to_string(&control->volume, &str) >= 0);
-+ pa_inidb_cell_set_value(control->db_cells.balance, str);
-+ pa_xfree(str);
-+ }
-+ }
-+
- if (!control->linked || control->unlinked)
- return;
-
-@@ -248,62 +329,69 @@ int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *v
- pa_assert(control);
- pa_assert(volume);
-
-- volume_local = *volume;
-+ if (control->set_volume_in_progress)
-+ return 0;
-
-- if (!control->set_volume) {
-- pa_log_info("Tried to set the volume of volume control %s, but the volume control doesn't support the operation.",
-- control->name);
-- return -PA_ERR_NOTSUPPORTED;
-- }
-+ volume_local = *volume;
-
-- if (set_balance
-- && !control->channel_map_is_writable
-- && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
-+ if (set_balance && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
- pa_bvolume_remap(&volume_local, &control->volume.channel_map);
-
- if (pa_bvolume_equal(&volume_local, &control->volume, set_volume, set_balance))
- return 0;
-
-- control->set_volume_in_progress = true;
-- r = control->set_volume(control, &volume_local, set_volume, set_balance);
-- control->set_volume_in_progress = false;
-+ if (control->linked && control->present) {
-+ control->set_volume_in_progress = true;
-+ r = control->set_volume(control, volume, &volume_local, set_volume, set_balance);
-+ control->set_volume_in_progress = false;
-
-- if (r >= 0)
-- set_volume_internal(control, &volume_local, set_volume, set_balance);
-+ if (r < 0) {
-+ pa_log("Setting the volume of volume control %s failed.", control->name);
-+ return r;
-+ }
-+ }
-
-- return r;
-+ set_volume_internal(control, &volume_local, set_volume, set_balance);
-+
-+ return 0;
- }
-
--void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) {
-- char *old_description;
-+void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map) {
-+ pa_bvolume bvolume;
-
- pa_assert(control);
-- pa_assert(new_description);
-+ pa_assert(map);
-
-- old_description = control->description;
--
-- if (pa_streq(new_description, old_description))
-+ if (pa_channel_map_equal(map, &control->volume.channel_map))
- return;
-
-- control->description = pa_xstrdup(new_description);
-- pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
-- new_description);
-- pa_xfree(old_description);
-- pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
-+ pa_bvolume_copy_balance(&bvolume, &control->volume);
-+ pa_bvolume_remap(&bvolume, map);
-+
-+ set_volume_internal(control, &bvolume, false, true);
- }
-
--void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
-- bool balance_changed) {
-+void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible) {
-+ bool old_convertible;
-+
- pa_assert(control);
-- pa_assert(new_volume);
-
-- if (!control->linked)
-+ old_convertible = control->convertible_to_dB;
-+
-+ if (convertible == old_convertible)
- return;
-
-- if (control->set_volume_in_progress)
-+ control->convertible_to_dB = convertible;
-+
-+ if (control->persistent)
-+ pa_inidb_cell_set_value(control->db_cells.convertible_to_dB, pa_boolean_to_string(convertible));
-+
-+ if (!control->linked || control->unlinked)
- return;
-
-- set_volume_internal(control, new_volume, volume_changed, balance_changed);
-+ pa_log_debug("The volume of volume control %s became %sconvertible to dB.", control->name, convertible ? "" : "not ");
-+
-+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], control);
- }
-
- void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) {
-@@ -333,31 +421,3 @@ void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_
-
- pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
- }
--
--void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream) {
-- pa_assert(control);
-- pa_assert(stream);
--
-- pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
--}
--
--void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream) {
-- pa_assert(control);
-- pa_assert(stream);
--
-- pa_assert_se(pa_hashmap_remove(control->streams, stream));
--}
--
--void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group) {
-- pa_assert(control);
-- pa_assert(group);
--
-- pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
--}
--
--void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group) {
-- pa_assert(control);
-- pa_assert(group);
--
-- pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
--}
-diff --git a/src/modules/volume-api/volume-control.h b/src/modules/volume-api/volume-control.h
-index aaba758..a47ab20 100644
---- a/src/modules/volume-api/volume-control.h
-+++ b/src/modules/volume-api/volume-control.h
-@@ -23,10 +23,23 @@
- ***/
-
- #include <modules/volume-api/bvolume.h>
-+#include <modules/volume-api/inidb.h>
- #include <modules/volume-api/volume-api.h>
-
- typedef struct pa_volume_control pa_volume_control;
-
-+typedef enum {
-+ PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
-+ PA_VOLUME_CONTROL_PURPOSE_OTHER,
-+} pa_volume_control_purpose_t;
-+
-+/* Usually remapped_volume is the volume to use, because it has a matching
-+ * channel map with the control, but in case the volume needs to be propagated
-+ * to another control, original_volume can be used to avoid loss of precision
-+ * that can result from remapping. */
-+typedef int (*pa_volume_control_set_volume_cb_t)(pa_volume_control *control, const pa_bvolume *original_volume,
-+ const pa_bvolume *remapped_volume, bool set_volume, bool set_balance);
-+
- struct pa_volume_control {
- pa_volume_api *volume_api;
- uint32_t index;
-@@ -35,65 +48,61 @@ struct pa_volume_control {
- pa_proplist *proplist;
- pa_bvolume volume;
- bool convertible_to_dB;
-- bool channel_map_is_writable;
-+ bool present;
-+ bool persistent;
-
-- /* If this volume control is the "own volume control" of an audio group,
-- * this is set to point to that group, otherwise this is NULL. */
-- pa_audio_group *owner_audio_group;
-+ pa_volume_control_purpose_t purpose;
-+ union {
-+ pas_stream *owner_stream;
-+ void *owner;
-+ };
-
- pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
- pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
-- pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
-- pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
-+
-+ struct {
-+ pa_inidb_cell *description;
-+ pa_inidb_cell *volume;
-+ pa_inidb_cell *balance;
-+ pa_inidb_cell *convertible_to_dB;
-+ } db_cells;
-
- bool linked;
- bool unlinked;
- bool set_volume_in_progress;
-
- /* Called from pa_volume_control_set_volume(). The implementation is
-- * expected to return a negative error code on failure. May be NULL, if the
-- * volume control is read-only. */
-- int (*set_volume)(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
-+ * expected to return a negative error code on failure. */
-+ pa_volume_control_set_volume_cb_t set_volume;
-
- void *userdata;
- };
-
--pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
-- bool channel_map_is_writable);
--
--typedef void (*pa_volume_control_set_initial_volume_cb_t)(pa_volume_control *control);
--
--/* initial_volume is the preferred initial volume of the volume control
-- * implementation. It may be NULL or partially invalid, if the implementation
-- * doesn't care about the initial state of the volume control, as long as these
-- * two rules are followed:
-- *
-- * 1) Read-only volume controls must always specify fully valid initial
-- * volume.
-- * 2) Volume controls with read-only channel map must always specify a valid
-- * channel map in initial_volume.
-- *
-- * The implementation's initial volume preference may be overridden by policy,
-- * if the volume control isn't read-only. When the final initial volume is
-- * known, the implementation is notified via set_initial_volume_cb (the volume
-- * can be read from control->volume). set_initial_volume_cb may be NULL, if the
-- * volume control is read-only. */
--void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
-- pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb);
--
-+int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r);
-+void pa_volume_control_put(pa_volume_control *control);
- void pa_volume_control_unlink(pa_volume_control *control);
- void pa_volume_control_free(pa_volume_control *control);
-
--/* Called by audio-group.c only. */
--void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group);
-+/* Called by the volume control implementation, before
-+ * pa_volume_control_put(). */
-+void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner);
-
--/* Called by clients and policy modules. */
-+/* Called by the volume control implementation. */
-+int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
-+ pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata);
-+
-+/* Called by the volume control implementation. This must only be called for
-+ * persistent controls; use pa_volume_control_free() for non-persistent
-+ * controls. */
-+void pa_volume_control_release(pa_volume_control *control);
-+
-+/* Called by anyone. */
-+void pa_volume_control_set_description(pa_volume_control *control, const char *description);
- int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
-
- /* Called by the volume control implementation. */
--void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description);
--void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
-- bool balance_changed);
-+void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map);
-+void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible);
-
- /* Called from device.c only. */
- void pa_volume_control_add_device(pa_volume_control *control, pa_device *device);
-@@ -101,12 +110,4 @@ void pa_volume_control_remove_device(pa_volume_control *control, pa_device *devi
- void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device);
- void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device);
-
--/* Called from sstream.c only. */
--void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream);
--void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream);
--
--/* Called from audio-group.c only. */
--void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group);
--void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group);
--
- #endif
-diff --git a/src/pulse/ext-volume-api.c b/src/pulse/ext-volume-api.c
-index 032e108..b81909a 100644
---- a/src/pulse/ext-volume-api.c
-+++ b/src/pulse/ext-volume-api.c
-@@ -36,6 +36,7 @@
- #include <pulsecore/i18n.h>
- #include <pulsecore/macro.h>
- #include <pulsecore/pstream-util.h>
-+#include <pulsecore/strbuf.h>
-
- #include <math.h>
-
-@@ -94,6 +95,21 @@ void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) {
- pa_channel_map_init(&volume->channel_map);
- }
-
-+void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map) {
-+ unsigned i;
-+
-+ pa_assert(bvolume);
-+ pa_assert(PA_VOLUME_IS_VALID(volume));
-+ pa_assert(map);
-+ pa_assert(pa_channel_map_valid(map));
-+
-+ bvolume->volume = volume;
-+ bvolume->channel_map = *map;
-+
-+ for (i = 0; i < map->channels; i++)
-+ bvolume->balance[i] = 1.0;
-+}
-+
- void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume) {
- pa_assert(bvolume);
- pa_assert(PA_VOLUME_IS_VALID(volume));
-@@ -103,6 +119,67 @@ void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_
- pa_channel_map_init_mono(&bvolume->channel_map);
- }
-
-+int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *_r) {
-+ pa_ext_volume_api_bvolume bvolume;
-+
-+ pa_assert(str);
-+ pa_assert(_r);
-+
-+ bvolume.channel_map.channels = 0;
-+
-+ for (;;) {
-+ const char *colon;
-+ size_t channel_name_len;
-+ char *channel_name;
-+ pa_channel_position_t position;
-+ const char *space;
-+ size_t balance_str_len;
-+ char *balance_str;
-+ int r;
-+ double balance;
-+
-+ colon = strchr(str, ':');
-+ if (!colon)
-+ return -PA_ERR_INVALID;
-+
-+ channel_name_len = colon - str;
-+ channel_name = pa_xstrndup(str, channel_name_len);
-+
-+ position = pa_channel_position_from_string(channel_name);
-+ pa_xfree(channel_name);
-+ if (position == PA_CHANNEL_POSITION_INVALID)
-+ return -PA_ERR_INVALID;
-+
-+ bvolume.channel_map.map[bvolume.channel_map.channels] = position;
-+ str = colon + 1;
-+
-+ space = strchr(str, ' ');
-+ if (space)
-+ balance_str_len = space - str;
-+ else
-+ balance_str_len = strlen(str);
-+
-+ balance_str = pa_xstrndup(str, balance_str_len);
-+
-+ r = pa_atod(balance_str, &balance);
-+ if (r < 0)
-+ return -PA_ERR_INVALID;
-+
-+ if (!pa_ext_volume_api_balance_valid(balance))
-+ return -PA_ERR_INVALID;
-+
-+ bvolume.balance[bvolume.channel_map.channels++] = balance;
-+
-+ if (space)
-+ str = space + 1;
-+ else
-+ break;
-+ }
-+
-+ pa_ext_volume_api_bvolume_copy_balance(_r, &bvolume);
-+ return 0;
-+}
-+
- int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
- int check_volume, int check_balance) {
- unsigned i;
-@@ -266,6 +343,29 @@ void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume
- volume->volume = old_volume;
- }
-
-+int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r) {
-+ pa_strbuf *buf;
-+ unsigned i;
-+
-+ pa_assert(volume);
-+ pa_assert(_r);
-+
-+ if (!pa_ext_volume_api_bvolume_valid(volume, false, true))
-+ return -PA_ERR_INVALID;
-+
-+ buf = pa_strbuf_new();
-+
-+ for (i = 0; i < volume->channel_map.channels; i++) {
-+ if (i != 0)
-+ pa_strbuf_putc(buf, ' ');
-+
-+ pa_strbuf_printf(buf, "%s:%.2f", pa_channel_position_to_string(volume->channel_map.map[i]), volume->balance[i]);
-+ }
-+
-+ *_r = pa_strbuf_tostring_free(buf);
-+ return 0;
-+}
-+
- char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len,
- const pa_ext_volume_api_bvolume *volume) {
- char *e;
-diff --git a/src/pulse/ext-volume-api.h b/src/pulse/ext-volume-api.h
-index 720ff39..6402f4b 100644
---- a/src/pulse/ext-volume-api.h
-+++ b/src/pulse/ext-volume-api.h
-@@ -50,7 +50,9 @@ int pa_ext_volume_api_balance_valid(double balance) PA_GCC_CONST;
- int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance)
- PA_GCC_PURE;
- void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume);
-+void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map);
- void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume);
-+int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *bvolume);
- int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
- int check_volume, int check_balance) PA_GCC_PURE;
- void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume,
-@@ -64,6 +66,7 @@ double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_
- void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance);
- double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE;
- void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance);
-+int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r);
-
- #define PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX 500
- char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_size,
-diff --git a/src/tizen-ivi/audio-groups.conf b/src/tizen-ivi/audio-groups.conf
-index 4839307..182df0d 100644
---- a/src/tizen-ivi/audio-groups.conf
-+++ b/src/tizen-ivi/audio-groups.conf
-@@ -1,33 +1,32 @@
- [General]
--audio-groups = x-tizen-ivi-call-downlink-audio-group x-tizen-ivi-navigator-output-audio-group x-tizen-ivi-default-output-audio-group
--streams = call-downlink navigator-output default-output
-+stream-rules = call-downlink navigator-output default-output
-
- [AudioGroup x-tizen-ivi-call-downlink-audio-group]
- description = Call downlink
--volume-control = create
--mute-control = create
-+volume-control = create:call-downlink-volume-control
-+mute-control = create:call-downlink-mute-control
-
- [AudioGroup x-tizen-ivi-navigator-output-audio-group]
- description = Navigator
--volume-control = create
--mute-control = create
-+volume-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
-+mute-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
-
- [AudioGroup x-tizen-ivi-default-output-audio-group]
- description = Default
--volume-control = create
--mute-control = create
-+volume-control = create:default-output-volume-control
-+mute-control = create:default-output-mute-control
-
--[Stream call-downlink]
-+[StreamRule call-downlink]
- match = (direction output AND property media.role=phone)
- audio-group-for-volume = x-tizen-ivi-call-downlink-audio-group
- audio-group-for-mute = x-tizen-ivi-call-downlink-audio-group
-
--[Stream navigator-output]
-+[StreamRule navigator-output]
- match = (direction output AND property media.role=navigator)
- audio-group-for-volume = x-tizen-ivi-navigator-output-audio-group
- audio-group-for-mute = x-tizen-ivi-navigator-output-audio-group
-
--[Stream default-output]
--match = (direction output)
-+[StreamRule default-output]
-+match = (direction output AND NEG property media.role=filter)
- audio-group-for-volume = x-tizen-ivi-default-output-audio-group
- audio-group-for-mute = x-tizen-ivi-default-output-audio-group
-diff --git a/src/tizen-ivi/main-volume-policy.conf b/src/tizen-ivi/main-volume-policy.conf
-index 0a83968..f2b3513 100644
---- a/src/tizen-ivi/main-volume-policy.conf
-+++ b/src/tizen-ivi/main-volume-policy.conf
-@@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
- input-volume-model = none
- output-mute-model = by-active-main-volume-context
- input-mute-model = none
--main-volume-contexts = x-tizen-ivi-call default
-
- [MainVolumeContext x-tizen-ivi-call]
- description = Call main volume context
+++ /dev/null
---- a/configure.ac 2014-06-06 10:00:12.384361791 +0200
-+++ b/configure.ac 2014-06-06 09:59:26.573362703 +0200
-@@ -1158,7 +1158,7 @@
- AS_HELP_STRING([--disable-systemd],[Disable optional systemd support]))
-
- AS_IF([test "x$enable_systemd" != "xno"],
-- [PKG_CHECK_MODULES(SYSTEMD, [ libsystemd ], HAVE_SYSTEMD=1, HAVE_SYSTEMD=0)],
-+ [PKG_CHECK_MODULES(SYSTEMD, [ libsystemd-login ], HAVE_SYSTEMD=1, HAVE_SYSTEMD=0)],
- HAVE_SYSTEMD=0)
-
- AS_IF([test "x$enable_systemd" = "xyes" && test "x$HAVE_SYSTEMD" = "x0"],
+++ /dev/null
-From: "vivian,zhang" <vivian.zhang@intel.com>
-Date: Tue, 18 Jun 2013 16:10:15 +0800
-Subject: changes to pa simple api - samsung
-
-Change-Id: I997c02217a8dc14524480164aa0baeea901c7b4e
----
- src/Makefile.am | 4 +-
- src/map-file | 6 +
- src/pulse/simple.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/pulse/simple.h | 28 +++++
- 4 files changed, 314 insertions(+), 2 deletions(-)
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 6340dbc..ebbed80 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -815,7 +815,7 @@ libpulse_la_SOURCES = \
- pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h
-
--libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS)
-+libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS)
- libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la
- libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
-
-@@ -825,7 +825,7 @@ libpulse_la_LIBADD += $(DBUS_LIBS)
- endif
-
- libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
--libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
-+libpulse_simple_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
- libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
- libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO)
-
-diff --git a/src/map-file b/src/map-file
-index 3dee7ee..a074a49 100644
---- a/src/map-file
-+++ b/src/map-file
-@@ -274,11 +274,17 @@ pa_signal_new;
- pa_signal_set_destroy;
- pa_simple_drain;
- pa_simple_flush;
-+pa_simple_mute;
- pa_simple_free;
- pa_simple_get_latency;
- pa_simple_new;
-+pa_simple_new_proplist;
- pa_simple_read;
- pa_simple_write;
-+pa_simple_set_volume;
-+pa_simple_get_stream_index;
-+pa_simple_cork;
-+pa_simple_is_corked;
- pa_stream_begin_write;
- pa_stream_cancel_write;
- pa_stream_connect_playback;
-diff --git a/src/pulse/simple.c b/src/pulse/simple.c
-index 860cd18..fa11b30 100644
---- a/src/pulse/simple.c
-+++ b/src/pulse/simple.c
-@@ -32,10 +32,12 @@
- #include <pulse/thread-mainloop.h>
- #include <pulse/xmalloc.h>
-
-+#include <pulsecore/native-common.h>
- #include <pulsecore/log.h>
- #include <pulsecore/macro.h>
-
- #include "simple.h"
-+#include "internal.h"
-
- struct pa_simple {
- pa_threaded_mainloop *mainloop;
-@@ -102,6 +104,14 @@ static void context_state_cb(pa_context *c, void *userdata) {
- }
- }
-
-+static void success_context_cb(pa_context *c, int success, void *userdata) {
-+ pa_simple *p = userdata;
-+ pa_assert(c);
-+ pa_assert(p);
-+
-+ p->operation_success = success;
-+ pa_threaded_mainloop_signal(p->mainloop, 0);
-+}
- static void stream_state_cb(pa_stream *s, void * userdata) {
- pa_simple *p = userdata;
- pa_assert(s);
-@@ -251,6 +261,122 @@ fail:
- return NULL;
- }
-
-+pa_simple* pa_simple_new_proplist(
-+ const char *server,
-+ const char *name,
-+ pa_stream_direction_t dir,
-+ const char *dev,
-+ const char *stream_name,
-+ const pa_sample_spec *ss,
-+ const pa_channel_map *map,
-+ const pa_buffer_attr *attr,
-+ pa_proplist *proplist,
-+ int *rerror) {
-+
-+ pa_simple *p;
-+ int error = PA_ERR_INTERNAL, r;
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
-+
-+ p = pa_xnew0(pa_simple, 1);
-+ p->direction = dir;
-+
-+ if (!(p->mainloop = pa_threaded_mainloop_new()))
-+ goto fail;
-+
-+ if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
-+ goto fail;
-+
-+ pa_context_set_state_callback(p->context, context_state_cb, p);
-+
-+ if (pa_context_connect(p->context, server, 0, NULL) < 0) {
-+ error = pa_context_errno(p->context);
-+ goto fail;
-+ }
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ if (pa_threaded_mainloop_start(p->mainloop) < 0)
-+ goto unlock_and_fail;
-+
-+ for (;;) {
-+ pa_context_state_t state;
-+
-+ state = pa_context_get_state(p->context);
-+
-+ if (state == PA_CONTEXT_READY)
-+ break;
-+
-+ if (!PA_CONTEXT_IS_GOOD(state)) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ /* Wait until the context is ready */
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ }
-+
-+ if (!(p->stream = pa_stream_new_with_proplist(p->context, stream_name, ss, map, proplist))) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ pa_stream_set_state_callback(p->stream, stream_state_cb, p);
-+ pa_stream_set_read_callback(p->stream, stream_request_cb, p);
-+ pa_stream_set_write_callback(p->stream, stream_request_cb, p);
-+ pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
-+
-+ if (dir == PA_STREAM_PLAYBACK)
-+ r = pa_stream_connect_playback(p->stream, dev, attr,
-+ PA_STREAM_INTERPOLATE_TIMING
-+ |PA_STREAM_ADJUST_LATENCY
-+ |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
-+ else
-+ r = pa_stream_connect_record(p->stream, dev, attr,
-+ PA_STREAM_INTERPOLATE_TIMING
-+ |PA_STREAM_ADJUST_LATENCY
-+ |PA_STREAM_AUTO_TIMING_UPDATE
-+ |PA_STREAM_START_CORKED);
-+
-+ if (r < 0) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ for (;;) {
-+ pa_stream_state_t state;
-+
-+ state = pa_stream_get_state(p->stream);
-+
-+ if (state == PA_STREAM_READY)
-+ break;
-+
-+ if (!PA_STREAM_IS_GOOD(state)) {
-+ error = pa_context_errno(p->context);
-+ goto unlock_and_fail;
-+ }
-+
-+ /* Wait until the stream is ready */
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return p;
-+
-+unlock_and_fail:
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+fail:
-+ if (rerror)
-+ *rerror = error;
-+ pa_simple_free(p);
-+ return NULL;
-+}
- void pa_simple_free(pa_simple *s) {
- pa_assert(s);
-
-@@ -454,6 +580,111 @@ unlock_and_fail:
- return -1;
- }
-
-+int pa_simple_mute(pa_simple *p, int mute, int *rerror) {
-+ pa_operation *o = NULL;
-+ uint32_t idx;
-+
-+ pa_assert(p);
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail);
-+
-+
-+ o = pa_context_set_sink_input_mute (p->context, idx, mute, success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror) {
-+ pa_assert(p);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, idx != NULL, PA_ERR_INVALID, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ *idx = pa_stream_get_index(p->stream);
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return 0;
-+
-+unlock_and_fail:
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_set_volume(pa_simple *p, int volume, int *rerror) {
-+ pa_operation *o = NULL;
-+ pa_stream *s = NULL;
-+ uint32_t idx;
-+ pa_cvolume cv;
-+ pa_volume_t v;
-+
-+ pa_assert(p);
-+
-+ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, volume >= 0, PA_ERR_INVALID, -1);
-+ CHECK_VALIDITY_RETURN_ANY(rerror, volume <= 65535, PA_ERR_INVALID, -1);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail);
-+
-+ s = p->stream;
-+ pa_assert(s);
-+ pa_cvolume_set(&cv, s->sample_spec.channels, volume);
-+
-+ o = pa_context_set_sink_input_volume (p->context, idx, &cv, success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
- pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
- pa_usec_t t;
- int negative;
-@@ -483,3 +714,50 @@ unlock_and_fail:
- pa_threaded_mainloop_unlock(p->mainloop);
- return (pa_usec_t) -1;
- }
-+
-+int pa_simple_cork(pa_simple *p, int cork, int *rerror) {
-+ pa_operation *o = NULL;
-+
-+ pa_assert(p);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+
-+ o = pa_stream_cork(p->stream, cork, success_context_cb, p);
-+ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
-+
-+ p->operation_success = 0;
-+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-+ pa_threaded_mainloop_wait(p->mainloop);
-+ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
-+ }
-+ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
-+
-+ pa_operation_unref(o);
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return 0;
-+
-+unlock_and_fail:
-+
-+ if (o) {
-+ pa_operation_cancel(o);
-+ pa_operation_unref(o);
-+ }
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+ return -1;
-+}
-+
-+int pa_simple_is_corked(pa_simple *p) {
-+ int is_cork;
-+ pa_assert(p);
-+
-+ pa_threaded_mainloop_lock(p->mainloop);
-+
-+ is_cork = pa_stream_is_corked(p->stream);
-+
-+ pa_threaded_mainloop_unlock(p->mainloop);
-+
-+ return is_cork;
-+}
-diff --git a/src/pulse/simple.h b/src/pulse/simple.h
-index 0fab8ee..bff9dbb 100644
---- a/src/pulse/simple.h
-+++ b/src/pulse/simple.h
-@@ -31,6 +31,7 @@
- #include <pulse/cdecl.h>
- #include <pulse/version.h>
-
-+#include <pulse/proplist.h>
- /** \page simple Simple API
- *
- * \section overv_sec Overview
-@@ -128,6 +129,19 @@ pa_simple* pa_simple_new(
- int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
- );
-
-+/** Create a new connection to the server with proplist */
-+pa_simple* pa_simple_new_proplist(
-+ const char *server, /**< Server name, or NULL for default */
-+ const char *name, /**< A descriptive name for this client (application name, ...) */
-+ pa_stream_direction_t dir, /**< Open this stream for recording or playback? */
-+ const char *dev, /**< Sink (resp. source) name, or NULL for default */
-+ const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */
-+ const pa_sample_spec *ss, /**< The sample type to use */
-+ const pa_channel_map *map, /**< The channel map to use, or NULL for default */
-+ const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */
-+ pa_proplist *proplist, /**< Properties, or NULL for default */
-+ int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
-+ );
- /** Close and free the connection to the server. The connection object becomes invalid when this is called. */
- void pa_simple_free(pa_simple *s);
-
-@@ -145,6 +159,20 @@ pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
-
- /** Flush the playback buffer. This discards any audio in the buffer. */
- int pa_simple_flush(pa_simple *s, int *error);
-+/** Mute the playback stream */
-+int pa_simple_mute(pa_simple *p, int mute, int *rerror);
-+
-+/** Volume control the playback stream */
-+int pa_simple_set_volume(pa_simple *p, int volume, int *rerror);
-+
-+/** Get stream index */
-+int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror);
-+
-+/** Cork on=1/off=0 stream */
-+int pa_simple_cork(pa_simple *p, int cork, int *rerror);
-+
-+/** Check whether stream is corked or not */
-+int pa_simple_is_corked(pa_simple *p);
-
- PA_C_DECL_END
+++ /dev/null
-require pulseaudio.inc
-
-PRIORITY = "10"
-
-S = "${WORKDIR}/git"
-
-LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
-
-SRC_URI = "git://review.tizen.org/platform/upstream/pulseaudio;tag=99714e130755179217cd948661eaca8d31e041c7;nobranch=1"
-
-#SRC_URI += "file://0001-configure.ac-Check-only-for-libsystemd-not-libsystem.patch"
-SRC_URI += "file://volatiles.04_pulse"
-
-
-BBCLASSEXTEND += " native "
-
-do_compile_prepend() {
- ${S}/bootstrap.sh
-}
-
-do_compile_prepend() {
- mkdir -p ${S}/libltdl
- cp ${STAGING_LIBDIR}/libltdl* ${S}/libltdl
-}