X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=meta-agl-profile-core%2Frecipes-multimedia%2Fpulseaudio%2Fpulseaudio%2F0030-volume-api-Add-libvolume-api.patch;fp=meta-agl-profile-core%2Frecipes-multimedia%2Fpulseaudio%2Fpulseaudio%2F0030-volume-api-Add-libvolume-api.patch;h=0000000000000000000000000000000000000000;hb=4464b68290669f133c17e7135dc86be9c4019326;hp=b904ac315f19a56bebe608357f63dbe12d378809;hpb=0037291bf3011ae006c8ecd42764aebc4a32c826;p=AGL%2Fmeta-agl.git diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0030-volume-api-Add-libvolume-api.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0030-volume-api-Add-libvolume-api.patch deleted file mode 100644 index b904ac315..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0030-volume-api-Add-libvolume-api.patch +++ /dev/null @@ -1,6118 +0,0 @@ -From 0223ac3e092249c792bedcab2bf5dbaec443f960 Mon Sep 17 00:00:00 2001 -From: Tanu Kaskinen -Date: Wed, 21 May 2014 11:51:27 +0300 -Subject: [PATCH 1/1] volume-api: Add libvolume-api.so - -This library implements the "core" of the new volume system, which -will be used by several modules. - -Change-Id: Ib25ada1392e83237a3908e6064ee0ad6dff7afaf -Signed-off-by: Jaska Uimonen ---- - Makefile.am | 3 + - src/Makefile.am | 19 +- - src/map-file | 15 + - src/modules/volume-api/audio-group.c | 448 +++++++++++++ - src/modules/volume-api/audio-group.h | 85 +++ - src/modules/volume-api/binding.c | 386 +++++++++++ - src/modules/volume-api/binding.h | 128 ++++ - src/modules/volume-api/bvolume.h | 43 ++ - src/modules/volume-api/device-creator.c | 1108 +++++++++++++++++++++++++++++++ - src/modules/volume-api/device-creator.h | 32 + - src/modules/volume-api/device.c | 293 ++++++++ - src/modules/volume-api/device.h | 76 +++ - src/modules/volume-api/mute-control.c | 306 +++++++++ - src/modules/volume-api/mute-control.h | 102 +++ - src/modules/volume-api/sstream.c | 366 ++++++++++ - src/modules/volume-api/sstream.h | 108 +++ - src/modules/volume-api/stream-creator.c | 691 +++++++++++++++++++ - src/modules/volume-api/stream-creator.h | 32 + - src/modules/volume-api/volume-api.c | 647 ++++++++++++++++++ - src/modules/volume-api/volume-api.h | 163 +++++ - src/modules/volume-api/volume-control.c | 363 ++++++++++ - src/modules/volume-api/volume-control.h | 112 ++++ - src/pulse/ext-volume-api.c | 275 ++++++++ - src/pulse/ext-volume-api.h | 68 ++ - 24 files changed, 5868 insertions(+), 1 deletion(-) - create mode 100644 src/modules/volume-api/audio-group.c - create mode 100644 src/modules/volume-api/audio-group.h - create mode 100644 src/modules/volume-api/binding.c - create mode 100644 src/modules/volume-api/binding.h - create mode 100644 src/modules/volume-api/bvolume.h - create mode 100644 src/modules/volume-api/device-creator.c - create mode 100644 src/modules/volume-api/device-creator.h - create mode 100644 src/modules/volume-api/device.c - create mode 100644 src/modules/volume-api/device.h - create mode 100644 src/modules/volume-api/mute-control.c - create mode 100644 src/modules/volume-api/mute-control.h - create mode 100644 src/modules/volume-api/sstream.c - create mode 100644 src/modules/volume-api/sstream.h - create mode 100644 src/modules/volume-api/stream-creator.c - create mode 100644 src/modules/volume-api/stream-creator.h - create mode 100644 src/modules/volume-api/volume-api.c - create mode 100644 src/modules/volume-api/volume-api.h - create mode 100644 src/modules/volume-api/volume-control.c - create mode 100644 src/modules/volume-api/volume-control.h - create mode 100644 src/pulse/ext-volume-api.c - create mode 100644 src/pulse/ext-volume-api.h - -diff --git a/Makefile.am b/Makefile.am -index 9431d4a..cf4a648 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -57,6 +57,9 @@ - moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h - moduledevinternaldir = $(includedir)/pulsemodule/pulse - -+moduledevvolumeapi_DATA = $(top_srcdir)/src/modules/volume-api/*.h -+moduledevvolumeapidir = $(includedir)/pulsemodule/modules/volume-api -+ - if HAVE_GLIB20 - pkgconfig_DATA += \ - libpulse-mainloop-glib.pc -diff --git a/src/Makefile.am b/src/Makefile.am -index 22b9b81..bc41dca 100644 ---- a/src/Makefile.am 2016-04-13 15:51:34.439019531 +0200 -+++ b/src/Makefile.am 2016-04-13 15:53:03.721019382 +0200 -@@ -792,6 +792,7 @@ - pulse/ext-device-manager.h \ - pulse/ext-device-restore.h \ - pulse/ext-stream-restore.h \ -+ pulse/ext-volume-api.h \ - pulse/format.h \ - pulse/gccmacro.h \ - pulse/introspect.h \ -@@ -838,6 +839,7 @@ - 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-volume-api.c pulse/ext-volume-api.h \ - pulse/format.c pulse/format.h \ - pulse/gccmacro.h \ - pulse/internal.h \ -@@ -1018,7 +1020,8 @@ modlibexec_LTLIBRARIES = \ - libprotocol-cli.la \ - libprotocol-simple.la \ - libprotocol-http.la \ -- libprotocol-native.la -+ libprotocol-native.la \ -+ libvolume-api.la - - if HAVE_WEBRTC - modlibexec_LTLIBRARIES += libwebrtc-util.la -@@ -1065,6 +1068,20 @@ libprotocol_native_la_CFLAGS += $(DBUS_CFLAGS) - libprotocol_native_la_LIBADD += $(DBUS_LIBS) - 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/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 \ -+ modules/volume-api/volume-api.c modules/volume-api/volume-api.h \ -+ modules/volume-api/volume-control.c modules/volume-api/volume-control.h -+libvolume_api_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -+libvolume_api_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la -+ - 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 -diff --git a/src/map-file b/src/map-file -index fbf3f22..1f64a2f 100644 ---- a/src/map-file -+++ b/src/map-file -@@ -172,6 +172,21 @@ - pa_ext_stream_restore_subscribe; - pa_ext_stream_restore_test; - pa_ext_stream_restore_write; -+pa_ext_volume_api_balance_valid; -+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_invalid; -+pa_ext_volume_api_bvolume_init_mono; -+pa_ext_volume_api_bvolume_remap; -+pa_ext_volume_api_bvolume_reset_balance; -+pa_ext_volume_api_bvolume_set_left_right_balance; -+pa_ext_volume_api_bvolume_set_rear_front_balance; -+pa_ext_volume_api_bvolume_snprint_balance; -+pa_ext_volume_api_bvolume_to_cvolume; -+pa_ext_volume_api_bvolume_valid; - pa_format_info_copy; - pa_format_info_free; - pa_format_info_from_string; -diff --git a/src/modules/volume-api/audio-group.c b/src/modules/volume-api/audio-group.c -new file mode 100644 -index 0000000..76bfa69 ---- /dev/null -+++ b/src/modules/volume-api/audio-group.c -@@ -0,0 +1,448 @@ -+/*** -+ 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 -+#endif -+ -+#include "audio-group.h" -+ -+#include -+ -+#include -+ -+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 r; -+ -+ pa_assert(api); -+ pa_assert(name); -+ pa_assert(description); -+ pa_assert(group); -+ -+ group_local = pa_xnew0(pa_audio_group, 1); -+ group_local->volume_api = api; -+ group_local->index = pa_volume_api_allocate_audio_group_index(api); -+ -+ r = pa_volume_api_register_name(api, name, true, &group_local->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; -+ -+ return 0; -+ -+fail: -+ pa_audio_group_free(group_local); -+ -+ return r; -+} -+ -+void pa_audio_group_put(pa_audio_group *group) { -+ const char *prop_key; -+ void *state = NULL; -+ -+ 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); -+ pa_log_debug(" Name: %s", group->name); -+ pa_log_debug(" Description: %s", group->description); -+ pa_log_debug(" Volume control: %s", group->volume_control ? group->volume_control->name : "(unset)"); -+ pa_log_debug(" Mute control: %s", group->mute_control ? group->mute_control->name : "(unset)"); -+ pa_log_debug(" Properties:"); -+ -+ while ((prop_key = pa_proplist_iterate(group->proplist, &state))) -+ pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(group->proplist, prop_key))); -+ -+ pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], group); -+} -+ -+void pa_audio_group_unlink(pa_audio_group *group) { -+ pas_stream *stream; -+ -+ pa_assert(group); -+ -+ if (group->unlinked) { -+ pa_log_debug("Unlinking audio group %s (already unlinked, this is a no-op).", group->name); -+ return; -+ } -+ -+ group->unlinked = true; -+ -+ 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); -+ -+ while ((stream = pa_hashmap_first(group->mute_streams))) -+ pas_stream_set_audio_group_for_mute(stream, NULL); -+ -+ 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; -+ } -+} -+ -+void pa_audio_group_free(pa_audio_group *group) { -+ pa_assert(group); -+ -+ if (!group->unlinked) -+ pa_audio_group_unlink(group); -+ -+ if (group->mute_streams) -+ pa_hashmap_free(group->mute_streams); -+ -+ if (group->volume_streams) -+ pa_hashmap_free(group->volume_streams); -+ -+ if (group->proplist) -+ pa_proplist_free(group->proplist); -+ -+ pa_xfree(group->description); -+ -+ if (group->name) -+ pa_volume_api_unregister_name(group->volume_api, group->name); -+ -+ pa_xfree(group); -+} -+ -+const char *pa_audio_group_get_name(pa_audio_group *group) { -+ pa_assert(group); -+ -+ 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); -+ -+ 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; -+ -+ 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, control->mute); -+ } -+} -+ -+void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) { -+ pa_assert(group); -+ -+ if (have == group->have_own_mute_control) -+ return; -+ -+ 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) { -+ pa_volume_control *old_control; -+ -+ pa_assert(group); -+ -+ old_control = group->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; -+ -+ pa_log_debug("The volume control of audio group %s changed from %s to %s.", group->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ 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) { -+ pa_mute_control *old_control; -+ -+ pa_assert(group); -+ -+ old_control = group->mute_control; -+ -+ 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; -+ -+ pa_log_debug("The mute control of audio group %s changed from %s to %s.", group->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ 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) { -+ pa_assert(group); -+ 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) { -+ pa_assert(group); -+ 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) { -+ pa_assert(group); -+ 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 -new file mode 100644 -index 0000000..41591ba ---- /dev/null -+++ b/src/modules/volume-api/audio-group.h -@@ -0,0 +1,85 @@ -+#ifndef fooaudiogrouphfoo -+#define fooaudiogrouphfoo -+ -+/*** -+ 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 -+#include -+#include -+ -+#include -+ -+#include -+ -+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; -+ const char *name; -+ char *description; -+ 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) */ -+ -+ bool linked; -+ bool unlinked; -+}; -+ -+int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group); -+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); -+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. */ -+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 -new file mode 100644 -index 0000000..6e73119 ---- /dev/null -+++ b/src/modules/volume-api/binding.c -@@ -0,0 +1,386 @@ -+/*** -+ 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 -+#endif -+ -+#include "binding.h" -+ -+#include -+#include -+ -+#include -+#include -+ -+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 -new file mode 100644 -index 0000000..ba4dea8 ---- /dev/null -+++ b/src/modules/volume-api/binding.h -@@ -0,0 +1,128 @@ -+#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 -+ -+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 -new file mode 100644 -index 0000000..0317fb6 ---- /dev/null -+++ b/src/modules/volume-api/bvolume.h -@@ -0,0 +1,43 @@ -+#ifndef foobvolumehfoo -+#define foobvolumehfoo -+ -+/*** -+ 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 -+ -+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_mono pa_ext_volume_api_bvolume_init_mono -+#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_SNPRINT_BALANCE_MAX PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX -+#define pa_bvolume_snprint_balance pa_ext_volume_api_bvolume_snprint_balance -+ -+#endif -diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c -new file mode 100644 -index 0000000..1d912ba ---- /dev/null -+++ b/src/modules/volume-api/device-creator.c -@@ -0,0 +1,1108 @@ -+/*** -+ 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 -+#endif -+ -+#include "device-creator.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+ -+struct pa_device_creator { -+ pa_volume_api *volume_api; -+ pa_hashmap *devices; /* pa_device_port/pa_sink/pa_source -> struct device */ -+ pa_hook_slot *card_put_slot; -+ pa_hook_slot *card_unlink_slot; -+ pa_hook_slot *sink_put_slot; -+ pa_hook_slot *sink_unlink_slot; -+ pa_hook_slot *source_put_slot; -+ pa_hook_slot *source_unlink_slot; -+}; -+ -+enum device_type { -+ DEVICE_TYPE_PORT, -+ DEVICE_TYPE_PORT_MONITOR, -+ DEVICE_TYPE_SINK, -+ DEVICE_TYPE_SOURCE, -+}; -+ -+struct device_volume_control { -+ struct device *device; -+ pa_volume_control *volume_control; -+ -+ bool unlinked; -+ -+ pa_hook_slot *volume_changed_slot; -+}; -+ -+struct device_mute_control { -+ struct device *device; -+ pa_mute_control *mute_control; -+ -+ bool unlinked; -+ -+ pa_hook_slot *mute_changed_slot; -+}; -+ -+struct device { -+ pa_device_creator *creator; -+ enum device_type type; -+ pa_device_port *port; -+ pa_sink *sink; -+ pa_source *source; -+ pa_device *device; -+ struct device_volume_control *volume_control; -+ struct device_mute_control *mute_control; -+ -+ bool unlinked; -+ -+ pa_hook_slot *proplist_changed_slot; -+ pa_hook_slot *port_active_changed_slot; -+ struct device *monitor; -+}; -+ -+static const char *device_type_from_icon_name(const char *icon_name) { -+ if (!icon_name) -+ return NULL; -+ -+ if (pa_streq(icon_name, "audio-input-microphone")) -+ return "microphone"; -+ -+ if (pa_streq(icon_name, "audio-speakers")) -+ return "speakers"; -+ -+ if (pa_streq(icon_name, "audio-headphones")) -+ return "headphones"; -+ -+ return NULL; -+} -+ -+static const char *device_type_from_port_name(pa_device_port *port) { -+ pa_assert(port); -+ -+ if (strstr(port->name, "analog")) { -+ if (port->direction == PA_DIRECTION_INPUT) -+ return "analog-input"; -+ else -+ return "analog-output"; -+ } -+ -+ if (strstr(port->name, "hdmi")) { -+ if (port->direction == PA_DIRECTION_INPUT) -+ return "hdmi-input"; -+ else -+ return "hdmi-output"; -+ } -+ -+ if (strstr(port->name, "iec958")) { -+ if (port->direction == PA_DIRECTION_INPUT) -+ return "spdif-input"; -+ else -+ return "spdif-output"; -+ } -+ -+ return NULL; -+} -+ -+static const char *device_type_from_port(pa_device_port *port) { -+ const char *device_type; -+ -+ pa_assert(port); -+ -+ device_type = device_type_from_icon_name(pa_proplist_gets(port->proplist, PA_PROP_DEVICE_ICON_NAME)); -+ if (device_type) -+ return device_type; -+ -+ device_type = device_type_from_port_name(port); -+ if (device_type) -+ return device_type; -+ -+ return NULL; -+} -+ -+static const char *get_sink_description(pa_sink *sink) { -+ const char *description; -+ -+ pa_assert(sink); -+ -+ description = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION); -+ if (description) -+ return description; -+ -+ return sink->name; -+} -+ -+static const char *get_source_description(pa_source *source) { -+ const char *description; -+ -+ pa_assert(source); -+ -+ description = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION); -+ if (description) -+ return description; -+ -+ 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; -+ pa_sink *sink = NULL; -+ pa_source *source = NULL; -+ pa_bvolume bvolume; -+ -+ pa_assert(control); -+ pa_assert(call_data); -+ -+ device = control->device; -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ if (device->port->direction == PA_DIRECTION_OUTPUT) -+ sink = call_data; -+ else -+ source = call_data; -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ case DEVICE_TYPE_SOURCE: -+ source = call_data; -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ sink = call_data; -+ break; -+ } -+ -+ if ((sink && sink != device->sink) || (source && source != device->source)) -+ return PA_HOOK_OK; -+ -+ if (sink) -+ pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map); -+ else -+ pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map); -+ -+ pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true); -+ -+ return PA_HOOK_OK; -+} -+ -+static void volume_control_set_initial_volume_cb(pa_volume_control *c) { -+ struct device_volume_control *control; -+ struct device *device; -+ pa_cvolume cvolume; -+ -+ pa_assert(c); -+ -+ 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_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; -+ } -+} -+ -+static void device_volume_control_put(struct device_volume_control *control) { -+ struct device *device; -+ pa_bvolume volume; -+ -+ pa_assert(control); -+ -+ device = control->device; -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ 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); -+ } 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); -+ } -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ case DEVICE_TYPE_SOURCE: -+ 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); -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ 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); -+ break; -+ } -+ -+ pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb); -+} -+ -+static void device_volume_control_unlink(struct device_volume_control *control) { -+ pa_assert(control); -+ -+ if (control->unlinked) -+ return; -+ -+ control->unlinked = true; -+ -+ 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) { -+ pa_assert(control); -+ -+ if (!control->unlinked) -+ device_volume_control_unlink(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; -+ -+ return control; -+} -+ -+static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *call_data, void *userdata) { -+ struct device_mute_control *control = userdata; -+ struct device *device; -+ pa_sink *sink = NULL; -+ pa_source *source = NULL; -+ bool mute; -+ -+ pa_assert(control); -+ pa_assert(call_data); -+ -+ device = control->device; -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ if (device->port->direction == PA_DIRECTION_OUTPUT) -+ sink = call_data; -+ else -+ source = call_data; -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ case DEVICE_TYPE_SOURCE: -+ source = call_data; -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ sink = call_data; -+ break; -+ } -+ -+ if ((sink && sink != device->sink) || (source && source != device->source)) -+ return PA_HOOK_OK; -+ -+ if (sink) -+ mute = sink->muted; -+ else -+ mute = source->muted; -+ -+ pa_mute_control_mute_changed(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; -+ 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, c->mute, true); -+ else -+ pa_source_set_mute(device->source, c->mute, true); -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ case DEVICE_TYPE_SOURCE: -+ pa_source_set_mute(device->source, c->mute, true); -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ pa_sink_set_mute(device->sink, c->mute, true); -+ break; -+ } -+} -+ -+static void device_mute_control_put(struct device_mute_control *control) { -+ struct device *device; -+ bool mute = false; -+ -+ pa_assert(control); -+ -+ device = control->device; -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ 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); -+ mute = device->sink->muted; -+ } else { -+ 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_PORT_MONITOR: -+ case DEVICE_TYPE_SOURCE: -+ 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; -+ -+ case DEVICE_TYPE_SINK: -+ 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; -+ } -+ -+ pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb); -+} -+ -+static void device_mute_control_unlink(struct device_mute_control *control) { -+ pa_assert(control); -+ -+ if (control->unlinked) -+ return; -+ -+ control->unlinked = true; -+ -+ 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) { -+ pa_assert(control); -+ -+ if (!control->unlinked) -+ device_mute_control_unlink(control); -+ -+ if (control->mute_control) -+ pa_mute_control_free(control->mute_control); -+ -+ pa_xfree(control); -+} -+ -+static void device_set_sink_and_source_from_port(struct device *device) { -+ pa_sink *sink; -+ pa_source *source; -+ uint32_t idx; -+ -+ pa_assert(device); -+ -+ device->sink = NULL; -+ device->source = NULL; -+ -+ if (!device->port->active) -+ return; -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ if (device->port->direction == PA_DIRECTION_OUTPUT) { -+ PA_IDXSET_FOREACH(sink, device->port->card->sinks, idx) { -+ if (sink->active_port == device->port) { -+ device->sink = sink; -+ break; -+ } -+ } -+ -+ pa_assert(device->sink); -+ } else { -+ PA_IDXSET_FOREACH(source, device->port->card->sources, idx) { -+ if (source->active_port == device->port) { -+ device->source = source; -+ break; -+ } -+ } -+ -+ pa_assert(device->source); -+ } -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: { -+ PA_IDXSET_FOREACH(sink, device->port->card->sinks, idx) { -+ if (sink->active_port == device->port) { -+ device->sink = sink; -+ device->source = sink->monitor_source; -+ break; -+ } -+ } -+ -+ pa_assert(device->sink); -+ break; -+ } -+ -+ case DEVICE_TYPE_SINK: -+ case DEVICE_TYPE_SOURCE: -+ pa_assert_not_reached(); -+ } -+} -+ -+static pa_hook_result_t sink_or_source_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) { -+ struct device *device = userdata; -+ pa_sink *sink = NULL; -+ pa_source *source = NULL; -+ const char *description = NULL; -+ -+ pa_assert(device); -+ pa_assert(call_data); -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ case DEVICE_TYPE_PORT_MONITOR: -+ pa_assert_not_reached(); -+ -+ case DEVICE_TYPE_SINK: -+ sink = call_data; -+ -+ if (sink != device->sink) -+ return PA_HOOK_OK; -+ -+ description = get_sink_description(sink); -+ break; -+ -+ case DEVICE_TYPE_SOURCE: -+ source = call_data; -+ -+ if (source != device->source) -+ return PA_HOOK_OK; -+ -+ description = get_source_description(source); -+ break; -+ } -+ -+ 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); -+ -+ return PA_HOOK_OK; -+} -+ -+static struct device *device_new(pa_device_creator *creator, enum device_type type, void *core_device) { -+ 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; -+ -+ pa_assert(creator); -+ pa_assert(core_device); -+ -+ device = pa_xnew0(struct device, 1); -+ device->creator = creator; -+ device->type = type; -+ -+ switch (type) { -+ case DEVICE_TYPE_PORT: -+ device->port = core_device; -+ device_set_sink_and_source_from_port(device); -+ name = "port-device"; -+ description = pa_xstrdup(device->port->description); -+ direction = device->port->direction; -+ device_type = device_type_from_port(device->port); -+ -+ if (!device->sink && !device->source) -+ create_volume_and_mute_controls = false; -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ device->port = core_device; -+ device_set_sink_and_source_from_port(device); -+ name = "port-monitor-device"; -+ description = pa_sprintf_malloc(_("Monitor of %s"), device->port->description); -+ direction = PA_DIRECTION_INPUT; -+ -+ if (!device->source) -+ create_volume_and_mute_controls = false; -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ device->sink = core_device; -+ name = "sink-device"; -+ description = pa_xstrdup(get_sink_description(device->sink)); -+ direction = PA_DIRECTION_OUTPUT; -+ break; -+ -+ case DEVICE_TYPE_SOURCE: -+ device->source = core_device; -+ name = "source-device"; -+ description = pa_xstrdup(get_source_description(device->source)); -+ direction = PA_DIRECTION_INPUT; -+ break; -+ } -+ -+ device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0); -+ pa_xfree(description); -+ -+ if (create_volume_and_mute_controls) { -+ device->volume_control = device_volume_control_new(device); -+ device->mute_control = device_mute_control_new(device); -+ } -+ -+ switch (type) { -+ case DEVICE_TYPE_PORT: -+ if (device->port->direction == PA_DIRECTION_OUTPUT) -+ device->monitor = device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port); -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ device->proplist_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], -+ PA_HOOK_NORMAL, sink_or_source_proplist_changed_cb, device); -+ break; -+ -+ case DEVICE_TYPE_SOURCE: -+ device->proplist_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], -+ PA_HOOK_NORMAL, sink_or_source_proplist_changed_cb, device); -+ break; -+ } -+ -+ return device; -+} -+ -+static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data, void *userdata) { -+ struct device *device = userdata; -+ pa_device_port *port = call_data; -+ bool should_have_volume_and_mute_controls = false; -+ -+ pa_assert(device); -+ pa_assert(port); -+ -+ if (port != device->port) -+ return PA_HOOK_OK; -+ -+ device_set_sink_and_source_from_port(device); -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ should_have_volume_and_mute_controls = device->sink || device->source; -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ should_have_volume_and_mute_controls = !!device->source; -+ break; -+ -+ case DEVICE_TYPE_SINK: -+ case DEVICE_TYPE_SOURCE: -+ pa_assert_not_reached(); -+ } -+ -+ if (should_have_volume_and_mute_controls && !device->volume_control) { -+ pa_assert(!device->mute_control); -+ -+ 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); -+ -+ 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 (!should_have_volume_and_mute_controls && device->volume_control) { -+ pa_assert(device->mute_control); -+ -+ device_mute_control_free(device->mute_control); -+ device->mute_control = NULL; -+ device_volume_control_free(device->volume_control); -+ device->volume_control = NULL; -+ } -+ -+ return PA_HOOK_OK; -+} -+ -+static void device_put(struct device *device) { -+ pa_assert(device); -+ -+ switch (device->type) { -+ case DEVICE_TYPE_PORT: -+ case DEVICE_TYPE_PORT_MONITOR: -+ device->port_active_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED], -+ PA_HOOK_NORMAL, port_active_changed_cb, device); -+ -+ case DEVICE_TYPE_SINK: -+ case DEVICE_TYPE_SOURCE: -+ break; -+ } -+ -+ if (device->volume_control) -+ device_volume_control_put(device->volume_control); -+ -+ if (device->mute_control) -+ device_mute_control_put(device->mute_control); -+ -+ pa_device_put(device->device, device->volume_control ? device->volume_control->volume_control : NULL, -+ device->mute_control ? device->mute_control->mute_control : NULL); -+ -+ if (device->monitor) -+ device_put(device->monitor); -+} -+ -+static void device_unlink(struct device *device) { -+ pa_assert(device); -+ -+ if (device->unlinked) -+ return; -+ -+ device->unlinked = true; -+ -+ if (device->monitor) -+ device_unlink(device->monitor); -+ -+ if (device->device) -+ pa_device_unlink(device->device); -+ -+ if (device->mute_control) -+ device_mute_control_unlink(device->mute_control); -+ -+ if (device->volume_control) -+ device_volume_control_unlink(device->volume_control); -+ -+ if (device->port_active_changed_slot) { -+ pa_hook_slot_free(device->port_active_changed_slot); -+ device->port_active_changed_slot = NULL; -+ } -+} -+ -+static void device_free(struct device *device) { -+ pa_assert(device); -+ -+ if (!device->unlinked) -+ device_unlink(device); -+ -+ if (device->monitor) -+ device_free(device->monitor); -+ -+ if (device->proplist_changed_slot) -+ pa_hook_slot_free(device->proplist_changed_slot); -+ -+ if (device->mute_control) -+ device_mute_control_free(device->mute_control); -+ -+ if (device->volume_control) -+ device_volume_control_free(device->volume_control); -+ -+ if (device->device) -+ pa_device_free(device->device); -+ -+ pa_xfree(device); -+} -+ -+static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) { -+ struct device *device; -+ -+ pa_assert(creator); -+ pa_assert(core_device); -+ -+ switch (type) { -+ case DEVICE_TYPE_PORT: -+ break; -+ -+ case DEVICE_TYPE_PORT_MONITOR: -+ pa_assert_not_reached(); -+ -+ case DEVICE_TYPE_SINK: -+ if (!pa_hashmap_isempty(((pa_sink *) core_device)->ports)) -+ return; -+ break; -+ -+ case DEVICE_TYPE_SOURCE: { -+ pa_source *source = core_device; -+ -+ if (source->monitor_of && !pa_hashmap_isempty(source->monitor_of->ports)) -+ return; -+ -+ if (!pa_hashmap_isempty(((pa_source *) core_device)->ports)) -+ return; -+ break; -+ } -+ } -+ -+ device = device_new(creator, type, core_device); -+ 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) { -+ pa_device_creator *creator = userdata; -+ pa_card *card = call_data; -+ pa_device_port *port; -+ void *state; -+ -+ pa_assert(creator); -+ pa_assert(card); -+ -+ PA_HASHMAP_FOREACH(port, card->ports, state) -+ create_device(creator, DEVICE_TYPE_PORT, port); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t card_unlink_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_device_creator *creator = userdata; -+ pa_card *card = call_data; -+ pa_device_port *port; -+ void *state; -+ -+ pa_assert(creator); -+ pa_assert(card); -+ -+ PA_HASHMAP_FOREACH(port, card->ports, state) -+ pa_hashmap_remove_and_free(creator->devices, port); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t sink_put_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_device_creator *creator = userdata; -+ pa_sink *sink = call_data; -+ -+ pa_assert(creator); -+ pa_assert(sink); -+ -+ create_device(creator, DEVICE_TYPE_SINK, sink); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t sink_unlink_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_device_creator *creator = userdata; -+ pa_sink *sink = call_data; -+ -+ pa_assert(creator); -+ pa_assert(sink); -+ -+ pa_hashmap_remove_and_free(creator->devices, sink); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t source_put_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_device_creator *creator = userdata; -+ pa_source *source = call_data; -+ -+ pa_assert(creator); -+ pa_assert(source); -+ -+ create_device(creator, DEVICE_TYPE_SOURCE, source); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t source_unlink_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_device_creator *creator = userdata; -+ pa_source *source = call_data; -+ -+ pa_assert(creator); -+ pa_assert(source); -+ -+ pa_hashmap_remove_and_free(creator->devices, source); -+ -+ return PA_HOOK_OK; -+} -+ -+pa_device_creator *pa_device_creator_new(pa_volume_api *api) { -+ pa_device_creator *creator; -+ pa_card *card; -+ uint32_t idx; -+ pa_sink *sink; -+ pa_source *source; -+ -+ pa_assert(api); -+ -+ creator = pa_xnew0(pa_device_creator, 1); -+ creator->volume_api = api; -+ creator->devices = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) device_free); -+ creator->card_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, card_put_cb, creator); -+ creator->card_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL, card_unlink_cb, -+ creator); -+ creator->sink_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, sink_put_cb, creator); -+ creator->sink_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL, sink_unlink_cb, -+ creator); -+ creator->source_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, source_put_cb, -+ creator); -+ creator->source_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_NORMAL, -+ source_unlink_cb, creator); -+ -+ PA_IDXSET_FOREACH(card, api->core->cards, idx) { -+ pa_device_port *port; -+ void *state; -+ -+ PA_HASHMAP_FOREACH(port, card->ports, state) -+ create_device(creator, DEVICE_TYPE_PORT, port); -+ } -+ -+ PA_IDXSET_FOREACH(sink, api->core->sinks, idx) -+ create_device(creator, DEVICE_TYPE_SINK, sink); -+ -+ PA_IDXSET_FOREACH(source, api->core->sources, idx) -+ create_device(creator, DEVICE_TYPE_SOURCE, source); -+ -+ return creator; -+} -+ -+void pa_device_creator_free(pa_device_creator *creator) { -+ pa_assert(creator); -+ -+ if (creator->devices) -+ pa_hashmap_remove_all(creator->devices); -+ -+ if (creator->source_unlink_slot) -+ pa_hook_slot_free(creator->source_unlink_slot); -+ -+ if (creator->source_put_slot) -+ pa_hook_slot_free(creator->source_put_slot); -+ -+ if (creator->sink_unlink_slot) -+ pa_hook_slot_free(creator->sink_unlink_slot); -+ -+ if (creator->sink_put_slot) -+ pa_hook_slot_free(creator->sink_put_slot); -+ -+ if (creator->card_unlink_slot) -+ pa_hook_slot_free(creator->card_unlink_slot); -+ -+ if (creator->card_put_slot) -+ pa_hook_slot_free(creator->card_put_slot); -+ -+ if (creator->devices) -+ pa_hashmap_free(creator->devices); -+ -+ pa_xfree(creator); -+} -diff --git a/src/modules/volume-api/device-creator.h b/src/modules/volume-api/device-creator.h -new file mode 100644 -index 0000000..bcec8d9 ---- /dev/null -+++ b/src/modules/volume-api/device-creator.h -@@ -0,0 +1,32 @@ -+#ifndef foodevicecreatorhfoo -+#define foodevicecreatorhfoo -+ -+/*** -+ 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 -+ -+typedef struct pa_device_creator pa_device_creator; -+ -+pa_device_creator *pa_device_creator_new(pa_volume_api *api); -+void pa_device_creator_free(pa_device_creator *creator); -+ -+#endif -diff --git a/src/modules/volume-api/device.c b/src/modules/volume-api/device.c -new file mode 100644 -index 0000000..ea496ba ---- /dev/null -+++ b/src/modules/volume-api/device.c -@@ -0,0 +1,293 @@ -+/*** -+ 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 -+#endif -+ -+#include "device.h" -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+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; -+ unsigned i; -+ -+ pa_assert(api); -+ pa_assert(name); -+ pa_assert(description); -+ pa_assert(device_types || n_device_types == 0); -+ -+ 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); -+ device->description = pa_xstrdup(description); -+ device->direction = direction; -+ device->device_types = pa_dynarray_new(pa_xfree); -+ -+ for (i = 0; i < n_device_types; i++) -+ pa_dynarray_append(device->device_types, pa_xstrdup(device_types[i])); -+ -+ device->proplist = pa_proplist_new(); -+ device->use_default_volume_control = true; -+ device->use_default_mute_control = true; -+ -+ return device; -+} -+ -+void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control) { -+ char *device_types_str; -+ const char *prop_key; -+ void *state = NULL; -+ -+ pa_assert(device); -+ -+ if (default_volume_control) { -+ device->default_volume_control = default_volume_control; -+ pa_volume_control_add_default_for_device(default_volume_control, device); -+ -+ device->volume_control = default_volume_control; -+ pa_volume_control_add_device(default_volume_control, device); -+ } -+ -+ if (default_mute_control) { -+ device->default_mute_control = default_mute_control; -+ pa_mute_control_add_default_for_device(default_mute_control, device); -+ -+ device->mute_control = default_mute_control; -+ pa_mute_control_add_device(default_mute_control, device); -+ } -+ -+ 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), -+ pa_dynarray_size(device->device_types), ", "); -+ -+ pa_log_debug("Created device #%u.", device->index); -+ pa_log_debug(" Name: %s", device->name); -+ pa_log_debug(" Description: %s", device->description); -+ pa_log_debug(" Direction: %s", pa_direction_to_string(device->direction)); -+ pa_log_debug(" Device Types: %s", *device_types_str ? device_types_str : "(none)"); -+ pa_log_debug(" Volume control: %s", device->volume_control ? device->volume_control->name : "(unset)"); -+ pa_log_debug(" Mute control: %s", device->mute_control ? device->mute_control->name : "(unset)"); -+ pa_log_debug(" Properties:"); -+ -+ while ((prop_key = pa_proplist_iterate(device->proplist, &state))) -+ pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(device->proplist, prop_key))); -+ -+ pa_xfree(device_types_str); -+ -+ pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_PUT], device); -+} -+ -+void pa_device_unlink(pa_device *device) { -+ pa_assert(device); -+ -+ if (device->unlinked) { -+ pa_log_debug("Unlinking device %s (already unlinked, this is a no-op).", device->name); -+ return; -+ } -+ -+ device->unlinked = true; -+ -+ 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); -+ -+ 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; -+ } -+ -+ 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; -+ } -+} -+ -+void pa_device_free(pa_device *device) { -+ pa_assert(device); -+ -+ if (!device->unlinked) -+ pa_device_unlink(device); -+ -+ if (device->proplist) -+ pa_proplist_free(device->proplist); -+ -+ if (device->device_types) -+ pa_dynarray_free(device->device_types); -+ -+ pa_xfree(device->description); -+ -+ if (device->name) -+ pa_volume_api_unregister_name(device->volume_api, device->name); -+ -+ pa_xfree(device); -+} -+ -+static void set_volume_control_internal(pa_device *device, pa_volume_control *control) { -+ pa_volume_control *old_control; -+ -+ pa_assert(device); -+ -+ old_control = device->volume_control; -+ -+ if (control == old_control) -+ return; -+ -+ if (old_control) -+ pa_volume_control_remove_device(old_control, device); -+ -+ device->volume_control = control; -+ -+ if (control) -+ pa_volume_control_add_device(control, device); -+ -+ if (!device->linked || device->unlinked) -+ return; -+ -+ pa_log_debug("The volume control of device %s changed from %s to %s.", device->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED], device); -+} -+ -+void pa_device_set_volume_control(pa_device *device, pa_volume_control *control) { -+ pa_assert(device); -+ -+ device->use_default_volume_control = false; -+ set_volume_control_internal(device, control); -+} -+ -+static void set_mute_control_internal(pa_device *device, pa_mute_control *control) { -+ pa_mute_control *old_control; -+ -+ pa_assert(device); -+ -+ old_control = device->mute_control; -+ -+ if (control == old_control) -+ return; -+ -+ if (old_control) -+ pa_mute_control_remove_device(old_control, device); -+ -+ device->mute_control = control; -+ -+ if (control) -+ pa_mute_control_add_device(control, device); -+ -+ pa_log_debug("The mute control of device %s changed from %s to %s.", device->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED], device); -+} -+ -+void pa_device_set_mute_control(pa_device *device, pa_mute_control *control) { -+ pa_assert(device); -+ -+ device->use_default_mute_control = false; -+ set_mute_control_internal(device, control); -+} -+ -+void pa_device_description_changed(pa_device *device, const char *new_description) { -+ char *old_description; -+ -+ pa_assert(device); -+ pa_assert(new_description); -+ -+ old_description = device->description; -+ -+ if (pa_streq(new_description, old_description)) -+ return; -+ -+ device->description = pa_xstrdup(new_description); -+ pa_log_debug("The description of device %s changed from \"%s\" to \"%s\".", device->name, old_description, -+ new_description); -+ pa_xfree(old_description); -+ pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED], device); -+} -+ -+void pa_device_set_default_volume_control(pa_device *device, pa_volume_control *control) { -+ pa_volume_control *old_control; -+ -+ pa_assert(device); -+ -+ old_control = device->default_volume_control; -+ -+ if (control == old_control) -+ return; -+ -+ if (old_control) -+ pa_volume_control_remove_default_for_device(old_control, device); -+ -+ device->default_volume_control = control; -+ -+ if (control) -+ pa_volume_control_add_default_for_device(control, device); -+ -+ if (device->use_default_volume_control) -+ set_volume_control_internal(device, control); -+} -+ -+void pa_device_set_default_mute_control(pa_device *device, pa_mute_control *control) { -+ pa_mute_control *old_control; -+ -+ pa_assert(device); -+ -+ old_control = device->default_mute_control; -+ -+ if (control == old_control) -+ return; -+ -+ if (old_control) -+ pa_mute_control_remove_default_for_device(old_control, device); -+ -+ device->default_mute_control = control; -+ -+ if (control) -+ pa_mute_control_add_default_for_device(control, device); -+ -+ if (device->use_default_mute_control) -+ set_mute_control_internal(device, control); -+} -diff --git a/src/modules/volume-api/device.h b/src/modules/volume-api/device.h -new file mode 100644 -index 0000000..9eac7e9 ---- /dev/null -+++ b/src/modules/volume-api/device.h -@@ -0,0 +1,76 @@ -+#ifndef foodevicehfoo -+#define foodevicehfoo -+ -+/*** -+ 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 -+ -+#include -+ -+typedef struct pa_device pa_device; -+ -+struct pa_device { -+ pa_volume_api *volume_api; -+ uint32_t index; -+ const char *name; -+ char *description; -+ pa_direction_t direction; -+ pa_dynarray *device_types; -+ pa_proplist *proplist; -+ pa_volume_control *volume_control; -+ pa_mute_control *mute_control; -+ -+ /* The device implementation can provide default volume and mute controls, -+ * which are used in case there's no policy module that wants to override -+ * the defaults. */ -+ pa_volume_control *default_volume_control; -+ bool use_default_volume_control; -+ pa_mute_control *default_mute_control; -+ bool use_default_mute_control; -+ -+ bool linked; -+ 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); -+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); -+ -+/* Called by policy modules. */ -+void pa_device_set_volume_control(pa_device *device, pa_volume_control *control); -+void pa_device_set_mute_control(pa_device *device, pa_mute_control *control); -+ -+/* Called by policy modules. Note that pa_device_set_volume_control() and -+ * pa_device_set_mute_control() automatically disable the corresponding -+ * use_default flags, so these functions are mainly useful for re-enabling the -+ * flags. */ -+void pa_device_set_use_default_volume_control(pa_device *device, bool use); -+void pa_device_set_use_default_mute_control(pa_device *device, bool use); -+ -+/* Called by the device implementation. */ -+void pa_device_description_changed(pa_device *device, const char *new_description); -+void pa_device_set_default_volume_control(pa_device *device, pa_volume_control *control); -+void pa_device_set_default_mute_control(pa_device *device, pa_mute_control *control); -+ -+#endif -diff --git a/src/modules/volume-api/mute-control.c b/src/modules/volume-api/mute-control.c -new file mode 100644 -index 0000000..adc008e ---- /dev/null -+++ b/src/modules/volume-api/mute-control.c -@@ -0,0 +1,306 @@ -+/*** -+ 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 -+#endif -+ -+#include "mute-control.h" -+ -+#include -+#include -+#include -+ -+#include -+ -+pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) { -+ pa_mute_control *control; -+ -+ pa_assert(api); -+ pa_assert(name); -+ pa_assert(description); -+ -+ 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); -+ control->proplist = pa_proplist_new(); -+ 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; -+} -+ -+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) { -+ 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); -+ -+ if (initial_mute_is_set) -+ control->mute = initial_mute; -+ else -+ control->mute = false; -+ -+ if (set_initial_mute_cb) -+ set_initial_mute_cb(control); -+ -+ 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(" 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_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT], control); -+} -+ -+void pa_mute_control_unlink(pa_mute_control *control) { -+ pa_audio_group *group; -+ pa_device *device; -+ pas_stream *stream; -+ -+ pa_assert(control); -+ -+ if (control->unlinked) { -+ pa_log_debug("Unlinking mute control %s (already unlinked, this is a no-op).", control->name); -+ return; -+ } -+ -+ control->unlinked = true; -+ -+ 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); -+ -+ 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); -+ -+ while ((device = pa_hashmap_first(control->default_for_devices))) -+ pa_device_set_default_mute_control(device, NULL); -+ -+ while ((device = pa_hashmap_first(control->devices))) { -+ /* Why do we have this assertion here? The concern is that if we call -+ * pa_device_set_mute_control() for some device that has the -+ * use_default_mute_control flag set, then that flag will be unset as -+ * a side effect, and we don't want that side effect. This assertion -+ * should be safe, because we just called -+ * pa_device_set_default_mute_control(NULL) for each device that this -+ * control was the default for, and that should ensure that we don't -+ * any more hold any references to devices that used to use this -+ * control as the default. */ -+ pa_assert(!device->use_default_mute_control); -+ pa_device_set_mute_control(device, NULL); -+ } -+} -+ -+void pa_mute_control_free(pa_mute_control *control) { -+ pa_assert(control); -+ -+ if (!control->unlinked) -+ 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); -+ } -+ -+ if (control->devices) { -+ pa_assert(pa_hashmap_isempty(control->devices)); -+ pa_hashmap_free(control->devices); -+ } -+ -+ if (control->proplist) -+ pa_proplist_free(control->proplist); -+ -+ pa_xfree(control->description); -+ -+ if (control->name) -+ pa_volume_api_unregister_name(control->volume_api, control->name); -+ -+ pa_xfree(control); -+} -+ -+void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) { -+ pa_assert(control); -+ pa_assert(group); -+ -+ control->owner_audio_group = group; -+} -+ -+static void set_mute_internal(pa_mute_control *control, bool mute) { -+ bool old_mute; -+ -+ pa_assert(control); -+ -+ old_mute = control->mute; -+ -+ if (mute == old_mute) -+ return; -+ -+ control->mute = mute; -+ -+ if (!control->linked || control->unlinked) -+ return; -+ -+ 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_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) { -+ 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 (mute == control->mute) -+ return 0; -+ -+ control->set_mute_in_progress = true; -+ r = control->set_mute(control, mute); -+ control->set_mute_in_progress = false; -+ -+ if (r >= 0) -+ set_mute_internal(control, mute); -+ -+ return r; -+} -+ -+void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) { -+ char *old_description; -+ -+ pa_assert(control); -+ pa_assert(new_description); -+ -+ old_description = control->description; -+ -+ if (pa_streq(new_description, 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); -+ 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) { -+ pa_assert(control); -+ -+ if (!control->linked) -+ return; -+ -+ if (control->set_mute_in_progress) -+ return; -+ -+ set_mute_internal(control, new_mute); -+} -+ -+void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0); -+} -+ -+void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_remove(control->devices, device)); -+} -+ -+void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0); -+} -+ -+void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ 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 -new file mode 100644 -index 0000000..1f70a43 ---- /dev/null -+++ b/src/modules/volume-api/mute-control.h -@@ -0,0 +1,102 @@ -+#ifndef foomutecontrolhfoo -+#define foomutecontrolhfoo -+ -+/*** -+ 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 -+ -+typedef struct pa_mute_control pa_mute_control; -+ -+struct pa_mute_control { -+ pa_volume_api *volume_api; -+ uint32_t index; -+ const char *name; -+ char *description; -+ pa_proplist *proplist; -+ bool mute; -+ -+ /* 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. */ -+ pa_audio_group *owner_audio_group; -+ -+ 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) */ -+ -+ 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); -+ -+ 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); -+ -+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. */ -+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); -+ -+/* Called from device.c only. */ -+void pa_mute_control_add_device(pa_mute_control *control, pa_device *device); -+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 -new file mode 100644 -index 0000000..e3531a8 ---- /dev/null -+++ b/src/modules/volume-api/sstream.c -@@ -0,0 +1,366 @@ -+/*** -+ 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 -+#endif -+ -+#include "sstream.h" -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) { -+ pas_stream *stream; -+ -+ pa_assert(api); -+ pa_assert(name); -+ pa_assert(description); -+ -+ 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; -+ -+ 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); -+ } -+ -+ 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_put(pas_stream *stream, pa_proplist *initial_properties) { -+ 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); -+ pa_log_debug(" Name: %s", stream->name); -+ pa_log_debug(" Description: %s", stream->description); -+ 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(" Properties:"); -+ -+ while ((prop_key = pa_proplist_iterate(stream->proplist, &state))) -+ pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(stream->proplist, prop_key))); -+ -+ pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], stream); -+} -+ -+void pas_stream_unlink(pas_stream *stream) { -+ pa_assert(stream); -+ -+ if (stream->unlinked) { -+ pa_log_debug("Unlinking stream %s (already unlinked, this is a no-op).", stream->name); -+ return; -+ } -+ -+ stream->unlinked = true; -+ -+ 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); -+ -+ 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_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) -+ pas_stream_unlink(stream); -+ -+ if (stream->proplist) -+ pa_proplist_free(stream->proplist); -+ -+ pa_xfree(stream->description); -+ -+ if (stream->name) -+ pa_volume_api_unregister_name(stream->volume_api, stream->name); -+ -+ pa_xfree(stream); -+} -+ -+int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) { -+ pa_assert(stream); -+ -+ if (have == stream->have_own_volume_control) -+ return 0; -+ -+ if (have) { -+ pa_assert(!stream->own_volume_control); -+ -+ if (!stream->create_own_volume_control) { -+ pa_log_debug("Stream %s doesn't support own volume control.", stream->name); -+ return -PA_ERR_NOTSUPPORTED; -+ } -+ -+ stream->own_volume_control = stream->create_own_volume_control(stream); -+ } else { -+ stream->delete_own_volume_control(stream); -+ stream->own_volume_control = NULL; -+ } -+ -+ stream->have_own_volume_control = have; -+ -+ return 0; -+} -+ -+int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) { -+ pa_assert(stream); -+ -+ if (have == stream->have_own_mute_control) -+ return 0; -+ -+ if (have) { -+ pa_assert(!stream->own_mute_control); -+ -+ if (!stream->create_own_mute_control) { -+ pa_log_debug("Stream %s doesn't support own mute control.", stream->name); -+ return -PA_ERR_NOTSUPPORTED; -+ } -+ -+ stream->own_mute_control = stream->create_own_mute_control(stream); -+ } else { -+ stream->delete_own_mute_control(stream); -+ stream->own_mute_control = NULL; -+ } -+ -+ stream->have_own_mute_control = have; -+ -+ return 0; -+} -+ -+void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) { -+ pa_assert(stream); -+ -+ stream->use_default_volume_control = false; -+ -+ if (stream->volume_control_binding) { -+ pa_binding_free(stream->volume_control_binding); -+ stream->volume_control_binding = NULL; -+ } -+ -+ set_volume_control_internal(stream, control); -+} -+ -+void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) { -+ pa_assert(stream); -+ -+ stream->use_default_mute_control = false; -+ -+ if (stream->mute_control_binding) { -+ pa_binding_free(stream->mute_control_binding); -+ stream->mute_control_binding = NULL; -+ } -+ -+ set_mute_control_internal(stream, control); -+} -+ -+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, -+ }; -+ -+ 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); -+ -+ stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info); -+} -+ -+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, -+ }; -+ -+ pa_assert(stream); -+ pa_assert(target_info); -+ -+ stream->use_default_mute_control = false; -+ -+ if (stream->mute_control_binding) -+ pa_binding_free(stream->mute_control_binding); -+ -+ stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info); -+} -+ -+void pas_stream_description_changed(pas_stream *stream, const char *new_description) { -+ char *old_description; -+ -+ pa_assert(stream); -+ pa_assert(new_description); -+ -+ old_description = stream->description; -+ -+ if (pa_streq(new_description, old_description)) -+ 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); -+} -+ -+void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) { -+ pa_assert(stream); -+ -+ if (group == stream->audio_group_for_volume) -+ return; -+ -+ if (stream->audio_group_for_volume) -+ pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream); -+ -+ stream->audio_group_for_volume = group; -+ -+ if (group) -+ pa_audio_group_add_volume_stream(group, stream); -+} -+ -+void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) { -+ pa_assert(stream); -+ -+ if (group == stream->audio_group_for_mute) -+ return; -+ -+ if (stream->audio_group_for_mute) -+ pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream); -+ -+ stream->audio_group_for_mute = group; -+ -+ if (group) -+ pa_audio_group_add_mute_stream(group, stream); -+} -diff --git a/src/modules/volume-api/sstream.h b/src/modules/volume-api/sstream.h -new file mode 100644 -index 0000000..a65b34c ---- /dev/null -+++ b/src/modules/volume-api/sstream.h -@@ -0,0 +1,108 @@ -+#ifndef foosstreamhfoo -+#define foosstreamhfoo -+ -+/*** -+ 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 -+ -+/* We use the "pas_" prefix in pas_stream, because there's already pa_stream in -+ * the client API, and there's no good alternative term for streams. The 's' in -+ * "pas" means "server", i.e. the point is that this stuff is for servers, -+ * while pa_stream is for clients. */ -+ -+typedef struct pas_stream pas_stream; -+ -+struct pas_stream { -+ pa_volume_api *volume_api; -+ uint32_t index; -+ const char *name; -+ char *description; -+ pa_direction_t direction; -+ pa_proplist *proplist; -+ pa_volume_control *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); -+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 policy modules. */ -+void pas_stream_set_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. */ -+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); -+ -+#endif -diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c -new file mode 100644 -index 0000000..2bd0053 ---- /dev/null -+++ b/src/modules/volume-api/stream-creator.c -@@ -0,0 +1,691 @@ -+/*** -+ 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 -+#endif -+ -+#include "stream-creator.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+ -+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_unlink_slot; -+ pa_hook_slot *source_output_put_slot; -+ pa_hook_slot *source_output_unlink_slot; -+}; -+ -+enum stream_type { -+ STREAM_TYPE_SINK_INPUT, -+ STREAM_TYPE_SOURCE_OUTPUT, -+}; -+ -+struct stream { -+ pa_stream_creator *creator; -+ enum stream_type type; -+ pa_sink_input *sink_input; -+ pa_source_output *source_output; -+ pa_client *client; -+ 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 *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 int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) { -+ struct stream *stream; -+ pa_bvolume bvolume; -+ pa_cvolume cvolume; -+ -+ pa_assert(control); -+ pa_assert(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; -+ } -+ -+ if (set_volume) -+ bvolume.volume = volume->volume; -+ -+ if (set_balance) -+ pa_bvolume_copy_balance(&bvolume, 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); -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ pa_source_output_set_volume(stream->source_output, &cvolume, true, true); -+ break; -+ } -+ -+ return 0; -+} -+ -+static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook_data, void *call_data, void *userdata) { -+ struct stream *stream = userdata; -+ pa_sink_input *input = NULL; -+ pa_source_output *output = NULL; -+ pa_bvolume bvolume; -+ -+ pa_assert(stream); -+ pa_assert(call_data); -+ -+ switch (stream->type) { -+ case STREAM_TYPE_SINK_INPUT: -+ input = call_data; -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ output = call_data; -+ break; -+ } -+ -+ if ((input && input != stream->sink_input) || (output && output != stream->source_output)) -+ return PA_HOOK_OK; -+ -+ if (input) -+ pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map); -+ else -+ pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map); -+ -+ pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true); -+ -+ return PA_HOOK_OK; -+} -+ -+static void volume_control_set_initial_volume_cb(pa_volume_control *control) { -+ struct stream *stream; -+ pa_cvolume cvolume; -+ -+ pa_assert(control); -+ -+ 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; -+ -+ 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; -+ -+ pa_assert(control); -+ -+ stream = control->userdata; -+ -+ switch (stream->type) { -+ case STREAM_TYPE_SINK_INPUT: -+ pa_sink_input_set_mute(stream->sink_input, mute, true); -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ pa_source_output_set_mute(stream->source_output, mute, true); -+ break; -+ } -+ -+ return 0; -+} -+ -+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; -+ -+ pa_assert(stream); -+ pa_assert(call_data); -+ -+ switch (stream->type) { -+ case STREAM_TYPE_SINK_INPUT: -+ input = call_data; -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ output = call_data; -+ break; -+ } -+ -+ if ((input && input != stream->sink_input) || (output && output != stream->source_output)) -+ return PA_HOOK_OK; -+ -+ if (input) -+ mute = input->muted; -+ else -+ mute = output->muted; -+ -+ pa_mute_control_mute_changed(stream->stream->own_mute_control, mute); -+ -+ return PA_HOOK_OK; -+} -+ -+static void mute_control_set_initial_mute_cb(pa_mute_control *control) { -+ struct stream *stream; -+ -+ pa_assert(control); -+ -+ stream = control->userdata; -+ -+ 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); -+ 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); -+ 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); -+} -+ -+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; -+ } -+ -+ 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->mute_changed_slot); -+ -+ 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; -+ 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; -+ break; -+ } -+ -+ pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb); -+ -+ return control; -+} -+ -+static void stream_delete_own_mute_control_cb(pas_stream *s) { -+ struct stream *stream; -+ -+ pa_assert(s); -+ -+ stream = s->userdata; -+ pa_hook_slot_free(stream->mute_changed_slot); -+ stream->mute_changed_slot = NULL; -+ pa_mute_control_free(s->own_mute_control); -+} -+ -+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_assert(stream); -+ pa_assert(call_data); -+ -+ switch (stream->type) { -+ case STREAM_TYPE_SINK_INPUT: -+ input = call_data; -+ -+ 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; -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ output = call_data; -+ -+ 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; -+ break; -+ } -+ -+ pas_stream_description_changed(stream->stream, new_stream_description); -+ -+ new_control_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, 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); -+ -+ 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; -+ const char *description = NULL; -+ pa_direction_t direction = PA_DIRECTION_OUTPUT; -+ -+ pa_assert(creator); -+ pa_assert(core_stream); -+ -+ stream = pa_xnew0(struct stream, 1); -+ stream->creator = creator; -+ stream->type = type; -+ -+ switch (type) { -+ case STREAM_TYPE_SINK_INPUT: -+ 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; -+ -+ direction = PA_DIRECTION_OUTPUT; -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ 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; -+ -+ 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, -+ 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); -+ 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); -+ -+ return stream; -+} -+ -+static void stream_put(struct stream *stream) { -+ pa_proplist *proplist = NULL; -+ -+ pa_assert(stream); -+ -+ switch (stream->type) { -+ case STREAM_TYPE_SINK_INPUT: -+ proplist = stream->sink_input->proplist; -+ break; -+ -+ case STREAM_TYPE_SOURCE_OUTPUT: -+ proplist = stream->source_output->proplist; -+ break; -+ } -+ -+ pas_stream_put(stream->stream, proplist); -+} -+ -+static void stream_unlink(struct stream *stream) { -+ pa_assert(stream); -+ -+ if (stream->unlinked) -+ return; -+ -+ stream->unlinked = true; -+ -+ if (stream->stream) -+ pas_stream_unlink(stream->stream); -+} -+ -+static void stream_free(struct stream *stream) { -+ pa_assert(stream); -+ -+ if (!stream->unlinked) -+ stream_unlink(stream); -+ -+ if (stream->client_proplist_changed_slot) -+ pa_hook_slot_free(stream->client_proplist_changed_slot); -+ -+ if (stream->proplist_changed_slot) -+ pa_hook_slot_free(stream->proplist_changed_slot); -+ -+ 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) { -+ struct stream *stream; -+ -+ pa_assert(creator); -+ pa_assert(core_stream); -+ -+ 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); -+ -+ create_stream(creator, STREAM_TYPE_SINK_INPUT, input); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t sink_input_unlink_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); -+ -+ pa_hashmap_remove_and_free(creator->streams, input); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_stream_creator *creator = userdata; -+ pa_source_output *output = call_data; -+ -+ pa_assert(creator); -+ pa_assert(output); -+ -+ create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t source_output_unlink_cb(void *hook_data, void *call_data, void *userdata) { -+ pa_stream_creator *creator = userdata; -+ pa_source_output *output = call_data; -+ -+ pa_assert(creator); -+ pa_assert(output); -+ -+ pa_hashmap_remove_and_free(creator->streams, output); -+ -+ return PA_HOOK_OK; -+} -+ -+pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) { -+ pa_stream_creator *creator; -+ uint32_t idx; -+ pa_sink_input *input; -+ pa_source_output *output; -+ -+ 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_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_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(output, api->core->source_outputs, idx) -+ create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output); -+ -+ return creator; -+} -+ -+void pa_stream_creator_free(pa_stream_creator *creator) { -+ pa_assert(creator); -+ -+ if (creator->streams) -+ pa_hashmap_remove_all(creator->streams); -+ -+ 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->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->streams) -+ pa_hashmap_free(creator->streams); -+ -+ pa_xfree(creator); -+} -diff --git a/src/modules/volume-api/stream-creator.h b/src/modules/volume-api/stream-creator.h -new file mode 100644 -index 0000000..97a03a4 ---- /dev/null -+++ b/src/modules/volume-api/stream-creator.h -@@ -0,0 +1,32 @@ -+#ifndef foostreamcreatorhfoo -+#define foostreamcreatorhfoo -+ -+/*** -+ 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 -+ -+typedef struct pa_stream_creator pa_stream_creator; -+ -+pa_stream_creator *pa_stream_creator_new(pa_volume_api *api); -+void pa_stream_creator_free(pa_stream_creator *creator); -+ -+#endif -diff --git a/src/modules/volume-api/volume-api.c b/src/modules/volume-api/volume-api.c -new file mode 100644 -index 0000000..9abea7e ---- /dev/null -+++ b/src/modules/volume-api/volume-api.c -@@ -0,0 +1,647 @@ -+/*** -+ 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 -+#endif -+ -+#include "volume-api.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+static pa_volume_api *volume_api_new(pa_core *core); -+static void volume_api_free(pa_volume_api *api); -+ -+pa_volume_api *pa_volume_api_get(pa_core *core) { -+ pa_volume_api *api; -+ -+ pa_assert(core); -+ -+ api = pa_shared_get(core, "volume-api"); -+ -+ if (api) -+ pa_volume_api_ref(api); -+ else { -+ api = volume_api_new(core); -+ pa_assert_se(pa_shared_set(core, "volume-api", api) >= 0); -+ } -+ -+ return api; -+} -+ -+pa_volume_api *pa_volume_api_ref(pa_volume_api *api) { -+ pa_assert(api); -+ -+ api->refcnt++; -+ -+ return api; -+} -+ -+void pa_volume_api_unref(pa_volume_api *api) { -+ pa_assert(api); -+ pa_assert(api->refcnt > 0); -+ -+ api->refcnt--; -+ -+ if (api->refcnt == 0) { -+ pa_assert_se(pa_shared_remove(api->core, "volume-api") >= 0); -+ volume_api_free(api); -+ } -+} -+ -+void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) { -+ pa_assert(api); -+ pa_assert(type); -+ -+ pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0); -+ -+ pa_log_debug("Added binding target type %s.", type->name); -+ -+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type); -+} -+ -+void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) { -+ pa_assert(api); -+ pa_assert(type); -+ -+ pa_log_debug("Removing binding target type %s.", type->name); -+ -+ pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type); -+ -+ pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name)); -+} -+ -+static void create_builtin_binding_target_types(pa_volume_api *api) { -+ pa_binding_target_type *type; -+ -+ pa_assert(api); -+ -+ type = pa_audio_group_create_binding_target_type(api); -+ pa_volume_api_add_binding_target_type(api, type); -+} -+ -+static void delete_builtin_binding_target_types(pa_volume_api *api) { -+ pa_binding_target_type *type; -+ -+ 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); -+} -+ -+static void create_objects_defer_event_cb(pa_mainloop_api *mainloop_api, pa_defer_event *event, void *userdata) { -+ pa_volume_api *volume_api = userdata; -+ -+ pa_assert(volume_api); -+ pa_assert(event == volume_api->create_objects_defer_event); -+ -+ mainloop_api->defer_free(event); -+ volume_api->create_objects_defer_event = NULL; -+ -+ volume_api->device_creator = pa_device_creator_new(volume_api); -+ volume_api->stream_creator = pa_stream_creator_new(volume_api); -+} -+ -+static pa_volume_api *volume_api_new(pa_core *core) { -+ pa_volume_api *api; -+ unsigned i; -+ -+ pa_assert(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); -+ api->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -+ api->streams = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -+ api->audio_groups = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -+ -+ for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++) -+ pa_hook_init(&api->hooks[i], api); -+ -+ create_builtin_binding_target_types(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 -+ * immediately, policy modules wouldn't have a chance of connecting to the -+ * object creation hooks before the objects are created. */ -+ api->create_objects_defer_event = core->mainloop->defer_new(core->mainloop, create_objects_defer_event_cb, api); -+ -+ pa_log_debug("Created a pa_volume_api object."); -+ -+ return api; -+} -+ -+static void volume_api_free(pa_volume_api *api) { -+ unsigned i; -+ -+ pa_assert(api); -+ pa_assert(api->refcnt == 0); -+ -+ pa_log_debug("Freeing the pa_volume_api object."); -+ -+ if (api->stream_creator) -+ pa_stream_creator_free(api->stream_creator); -+ -+ if (api->device_creator) -+ pa_device_creator_free(api->device_creator); -+ -+ 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); -+ -+ for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++) -+ pa_hook_done(&api->hooks[i]); -+ -+ if (api->audio_groups) { -+ pa_assert(pa_hashmap_isempty(api->audio_groups)); -+ pa_hashmap_free(api->audio_groups); -+ } -+ -+ if (api->streams) { -+ pa_assert(pa_hashmap_isempty(api->streams)); -+ pa_hashmap_free(api->streams); -+ } -+ -+ if (api->devices) { -+ pa_assert(pa_hashmap_isempty(api->devices)); -+ pa_hashmap_free(api->devices); -+ } -+ -+ if (api->mute_controls) { -+ pa_assert(pa_hashmap_isempty(api->mute_controls)); -+ pa_hashmap_free(api->mute_controls); -+ } -+ -+ if (api->volume_controls) { -+ pa_assert(pa_hashmap_isempty(api->volume_controls)); -+ pa_hashmap_free(api->volume_controls); -+ } -+ -+ if (api->names) { -+ pa_assert(pa_hashmap_isempty(api->names)); -+ 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); -+} -+ -+int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered, -+ const char **registered_name) { -+ char *n; -+ -+ pa_assert(api); -+ pa_assert(requested_name); -+ pa_assert(registered_name); -+ -+ 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_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(api->names, n, n) < 0); -+ } -+ -+ *registered_name = n; -+ -+ return 0; -+} -+ -+void pa_volume_api_unregister_name(pa_volume_api *api, const char *name) { -+ pa_assert(api); -+ pa_assert(name); -+ -+ pa_assert_se(pa_hashmap_remove_and_free(api->names, name) >= 0); -+} -+ -+uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) { -+ uint32_t idx; -+ -+ pa_assert(api); -+ -+ idx = api->next_volume_control_index++; -+ -+ 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); -+ -+ pa_assert_se(pa_hashmap_put(api->volume_controls, (void *) control->name, control) >= 0); -+} -+ -+int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *control) { -+ pa_assert(api); -+ pa_assert(control); -+ -+ if (!pa_hashmap_remove(api->volume_controls, control->name)) -+ return -1; -+ -+ if (control == api->main_output_volume_control) -+ set_main_output_volume_control_internal(api, NULL); -+ -+ if (control == api->main_input_volume_control) -+ set_main_input_volume_control_internal(api, NULL); -+ -+ return 0; -+} -+ -+pa_volume_control *pa_volume_api_get_volume_control_by_index(pa_volume_api *api, uint32_t idx) { -+ pa_volume_control *control; -+ void *state; -+ -+ pa_assert(api); -+ -+ PA_HASHMAP_FOREACH(control, api->volume_controls, state) { -+ if (control->index == idx) -+ return control; -+ } -+ -+ return NULL; -+} -+ -+uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) { -+ uint32_t idx; -+ -+ pa_assert(api); -+ -+ idx = api->next_mute_control_index++; -+ -+ 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); -+ -+ pa_assert_se(pa_hashmap_put(api->mute_controls, (void *) control->name, control) >= 0); -+} -+ -+int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *control) { -+ pa_assert(api); -+ pa_assert(control); -+ -+ if (!pa_hashmap_remove(api->mute_controls, control->name)) -+ return -1; -+ -+ if (control == api->main_output_mute_control) -+ set_main_output_mute_control_internal(api, NULL); -+ -+ if (control == api->main_input_mute_control) -+ set_main_input_mute_control_internal(api, NULL); -+ -+ return 0; -+} -+ -+pa_mute_control *pa_volume_api_get_mute_control_by_index(pa_volume_api *api, uint32_t idx) { -+ pa_mute_control *control; -+ void *state; -+ -+ pa_assert(api); -+ -+ PA_HASHMAP_FOREACH(control, api->mute_controls, state) { -+ if (control->index == idx) -+ return control; -+ } -+ -+ return NULL; -+} -+ -+uint32_t pa_volume_api_allocate_device_index(pa_volume_api *api) { -+ uint32_t idx; -+ -+ pa_assert(api); -+ -+ idx = api->next_device_index++; -+ -+ return idx; -+} -+ -+void pa_volume_api_add_device(pa_volume_api *api, pa_device *device) { -+ pa_assert(api); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_put(api->devices, (void *) device->name, device) >= 0); -+} -+ -+int pa_volume_api_remove_device(pa_volume_api *api, pa_device *device) { -+ pa_assert(api); -+ pa_assert(device); -+ -+ if (!pa_hashmap_remove(api->devices, device->name)) -+ return -1; -+ -+ return 0; -+} -+ -+pa_device *pa_volume_api_get_device_by_index(pa_volume_api *api, uint32_t idx) { -+ pa_device *device; -+ void *state; -+ -+ pa_assert(api); -+ -+ PA_HASHMAP_FOREACH(device, api->devices, state) { -+ if (device->index == idx) -+ return device; -+ } -+ -+ return NULL; -+} -+ -+uint32_t pa_volume_api_allocate_stream_index(pa_volume_api *api) { -+ uint32_t idx; -+ -+ pa_assert(api); -+ -+ idx = api->next_stream_index++; -+ -+ return idx; -+} -+ -+void pa_volume_api_add_stream(pa_volume_api *api, pas_stream *stream) { -+ pa_assert(api); -+ pa_assert(stream); -+ -+ pa_assert_se(pa_hashmap_put(api->streams, (void *) stream->name, stream) >= 0); -+} -+ -+int pa_volume_api_remove_stream(pa_volume_api *api, pas_stream *stream) { -+ pa_assert(api); -+ pa_assert(stream); -+ -+ if (!pa_hashmap_remove(api->streams, stream->name)) -+ return -1; -+ -+ return 0; -+} -+ -+pas_stream *pa_volume_api_get_stream_by_index(pa_volume_api *api, uint32_t idx) { -+ pas_stream *stream; -+ void *state; -+ -+ pa_assert(api); -+ -+ PA_HASHMAP_FOREACH(stream, api->streams, state) { -+ if (stream->index == idx) -+ return stream; -+ } -+ -+ return NULL; -+} -+ -+uint32_t pa_volume_api_allocate_audio_group_index(pa_volume_api *api) { -+ uint32_t idx; -+ -+ pa_assert(api); -+ -+ idx = api->next_audio_group_index++; -+ -+ return idx; -+} -+ -+void pa_volume_api_add_audio_group(pa_volume_api *api, pa_audio_group *group) { -+ pa_assert(api); -+ pa_assert(group); -+ -+ pa_assert_se(pa_hashmap_put(api->audio_groups, (void *) group->name, group) >= 0); -+} -+ -+int pa_volume_api_remove_audio_group(pa_volume_api *api, pa_audio_group *group) { -+ pa_assert(api); -+ pa_assert(group); -+ -+ if (!pa_hashmap_remove(api->audio_groups, group->name)) -+ return -1; -+ -+ return 0; -+} -+ -+pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint32_t idx) { -+ pa_audio_group *group; -+ void *state; -+ -+ pa_assert(api); -+ -+ PA_HASHMAP_FOREACH(group, api->audio_groups, state) { -+ if (group->index == idx) -+ return group; -+ } -+ -+ return NULL; -+} -+ -+void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *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; -+ } -+ -+ set_main_output_volume_control_internal(api, control); -+} -+ -+void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) { -+ pa_assert(api); -+ -+ if (api->main_input_volume_control_binding) { -+ pa_binding_free(api->main_input_volume_control_binding); -+ api->main_input_volume_control_binding = NULL; -+ } -+ -+ set_main_input_volume_control_internal(api, control); -+} -+ -+void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *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; -+ } -+ -+ set_main_output_mute_control_internal(api, control); -+} -+ -+void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) { -+ pa_assert(api); -+ -+ if (api->main_input_mute_control_binding) { -+ pa_binding_free(api->main_input_mute_control_binding); -+ api->main_input_mute_control_binding = NULL; -+ } -+ -+ set_main_input_mute_control_internal(api, control); -+} -+ -+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, -+ }; -+ -+ 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); -+} -+ -+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, -+ }; -+ -+ pa_assert(api); -+ pa_assert(target_info); -+ -+ if (api->main_input_volume_control_binding) -+ pa_binding_free(api->main_input_volume_control_binding); -+ -+ api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info); -+} -+ -+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, -+ }; -+ -+ 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); -+} -+ -+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, -+ }; -+ -+ pa_assert(api); -+ pa_assert(target_info); -+ -+ if (api->main_input_mute_control_binding) -+ pa_binding_free(api->main_input_mute_control_binding); -+ -+ api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info); -+} -diff --git a/src/modules/volume-api/volume-api.h b/src/modules/volume-api/volume-api.h -new file mode 100644 -index 0000000..73a1410 ---- /dev/null -+++ b/src/modules/volume-api/volume-api.h -@@ -0,0 +1,163 @@ -+#ifndef foovolumeapihfoo -+#define foovolumeapihfoo -+ -+/*** -+ 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 -+ -+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_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, -+ 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_MUTE_CONTROL_PUT, -+ PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK, -+ PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED, -+ PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED, -+ PA_VOLUME_API_HOOK_DEVICE_PUT, -+ PA_VOLUME_API_HOOK_DEVICE_UNLINK, -+ 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_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_VOLUME_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED, -+ PA_VOLUME_API_HOOK_MAX -+}; -+ -+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 */ -+ pa_hashmap *devices; /* name -> pa_device */ -+ pa_hashmap *streams; /* name -> pas_stream */ -+ pa_hashmap *audio_groups; /* name -> pa_audio_group */ -+ 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; -+ -+ uint32_t next_volume_control_index; -+ uint32_t next_mute_control_index; -+ 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]; -+ pa_defer_event *create_objects_defer_event; -+ pa_device_creator *device_creator; -+ pa_stream_creator *stream_creator; -+}; -+ -+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); -+void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control); -+int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *control); -+pa_volume_control *pa_volume_api_get_volume_control_by_index(pa_volume_api *api, uint32_t idx); -+ -+uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api); -+void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control); -+int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *control); -+pa_mute_control *pa_volume_api_get_mute_control_by_index(pa_volume_api *api, uint32_t idx); -+ -+uint32_t pa_volume_api_allocate_device_index(pa_volume_api *api); -+void pa_volume_api_add_device(pa_volume_api *api, pa_device *device); -+int pa_volume_api_remove_device(pa_volume_api *api, pa_device *device); -+pa_device *pa_volume_api_get_device_by_index(pa_volume_api *api, uint32_t idx); -+ -+uint32_t pa_volume_api_allocate_stream_index(pa_volume_api *api); -+void pa_volume_api_add_stream(pa_volume_api *api, pas_stream *stream); -+int pa_volume_api_remove_stream(pa_volume_api *api, pas_stream *stream); -+pas_stream *pa_volume_api_get_stream_by_index(pa_volume_api *api, uint32_t idx); -+ -+uint32_t pa_volume_api_allocate_audio_group_index(pa_volume_api *api); -+void pa_volume_api_add_audio_group(pa_volume_api *api, pa_audio_group *group); -+int pa_volume_api_remove_audio_group(pa_volume_api *api, pa_audio_group *group); -+pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint32_t idx); -+ -+void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control); -+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 -new file mode 100644 -index 0000000..c7f5dbb ---- /dev/null -+++ b/src/modules/volume-api/volume-control.c -@@ -0,0 +1,363 @@ -+/*** -+ 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 -+#endif -+ -+#include "volume-control.h" -+ -+#include -+#include -+#include -+ -+#include -+ -+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; -+ -+ pa_assert(api); -+ pa_assert(name); -+ pa_assert(description); -+ -+ 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); -+ 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; -+ 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; -+} -+ -+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) { -+ 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); -+ } -+ -+ if (set_initial_volume_cb) -+ set_initial_volume_cb(control); -+ -+ 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(" 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); -+ -+ if (control->unlinked) { -+ pa_log_debug("Unlinking volume control %s (already unlinked, this is a no-op).", control->name); -+ return; -+ } -+ -+ control->unlinked = true; -+ -+ 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); -+ -+ while ((stream = pa_hashmap_first(control->streams))) -+ pas_stream_set_volume_control(stream, NULL); -+ -+ while ((device = pa_hashmap_first(control->default_for_devices))) -+ pa_device_set_default_volume_control(device, NULL); -+ -+ while ((device = pa_hashmap_first(control->devices))) { -+ /* Why do we have this assertion here? The concern is that if we call -+ * pa_device_set_volume_control() for some device that has the -+ * use_default_volume_control flag set, then that flag will be unset as -+ * a side effect, and we don't want that side effect. This assertion -+ * should be safe, because we just called -+ * pa_device_set_default_volume_control(NULL) for each device that this -+ * control was the default for, and that should ensure that we don't -+ * any more hold any references to devices that used to use this -+ * control as the default. */ -+ pa_assert(!device->use_default_volume_control); -+ pa_device_set_volume_control(device, NULL); -+ } -+} -+ -+void pa_volume_control_free(pa_volume_control *control) { -+ pa_assert(control); -+ -+ if (!control->unlinked) -+ 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); -+ } -+ -+ if (control->devices) { -+ pa_assert(pa_hashmap_isempty(control->devices)); -+ pa_hashmap_free(control->devices); -+ } -+ -+ if (control->proplist) -+ pa_proplist_free(control->proplist); -+ -+ pa_xfree(control->description); -+ -+ if (control->name) -+ pa_volume_api_unregister_name(control->volume_api, control->name); -+ -+ pa_xfree(control); -+} -+ -+void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) { -+ pa_assert(control); -+ pa_assert(group); -+ -+ control->owner_audio_group = group; -+} -+ -+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; -+ -+ pa_assert(control); -+ pa_assert(volume); -+ -+ old_volume = control->volume; -+ volume_changed = !pa_bvolume_equal(volume, &old_volume, set_volume, false); -+ balance_changed = !pa_bvolume_equal(volume, &old_volume, false, set_balance); -+ -+ if (!volume_changed && !balance_changed) -+ return; -+ -+ if (volume_changed) -+ control->volume.volume = volume->volume; -+ -+ if (balance_changed) -+ pa_bvolume_copy_balance(&control->volume, volume); -+ -+ if (!control->linked || control->unlinked) -+ return; -+ -+ if (volume_changed) { -+ char old_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX]; -+ char new_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX]; -+ -+ pa_log_debug("The volume of volume control %s changed from %s to %s.", control->name, -+ pa_volume_snprint_verbose(old_volume_str, sizeof(old_volume_str), old_volume.volume, -+ control->convertible_to_dB), -+ pa_volume_snprint_verbose(new_volume_str, sizeof(new_volume_str), control->volume.volume, -+ control->convertible_to_dB)); -+ } -+ -+ if (balance_changed) { -+ char old_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX]; -+ char new_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX]; -+ -+ pa_log_debug("The balance of volume control %s changed from %s to %s.", control->name, -+ pa_bvolume_snprint_balance(old_balance_str, sizeof(old_balance_str), &control->volume), -+ pa_bvolume_snprint_balance(new_balance_str, sizeof(new_balance_str), &control->volume)); -+ } -+ -+ pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], control); -+} -+ -+int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) { -+ pa_bvolume volume_local; -+ int r; -+ -+ pa_assert(control); -+ pa_assert(volume); -+ -+ volume_local = *volume; -+ -+ 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; -+ } -+ -+ if (set_balance -+ && !control->channel_map_is_writable -+ && !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 (r >= 0) -+ set_volume_internal(control, &volume_local, set_volume, set_balance); -+ -+ return r; -+} -+ -+void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) { -+ char *old_description; -+ -+ pa_assert(control); -+ pa_assert(new_description); -+ -+ old_description = control->description; -+ -+ if (pa_streq(new_description, old_description)) -+ 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); -+} -+ -+void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed, -+ bool balance_changed) { -+ pa_assert(control); -+ pa_assert(new_volume); -+ -+ if (!control->linked) -+ return; -+ -+ if (control->set_volume_in_progress) -+ return; -+ -+ set_volume_internal(control, new_volume, volume_changed, balance_changed); -+} -+ -+void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0); -+} -+ -+void pa_volume_control_remove_device(pa_volume_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_remove(control->devices, device)); -+} -+ -+void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0); -+} -+ -+void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device) { -+ pa_assert(control); -+ pa_assert(device); -+ -+ 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 -new file mode 100644 -index 0000000..aaba758 ---- /dev/null -+++ b/src/modules/volume-api/volume-control.h -@@ -0,0 +1,112 @@ -+#ifndef foovolumecontrolhfoo -+#define foovolumecontrolhfoo -+ -+/*** -+ 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 -+#include -+ -+typedef struct pa_volume_control pa_volume_control; -+ -+struct pa_volume_control { -+ pa_volume_api *volume_api; -+ uint32_t index; -+ const char *name; -+ char *description; -+ pa_proplist *proplist; -+ pa_bvolume volume; -+ bool convertible_to_dB; -+ bool channel_map_is_writable; -+ -+ /* 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_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) */ -+ -+ 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); -+ -+ 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); -+ -+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 clients and policy modules. */ -+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); -+ -+/* Called from device.c only. */ -+void pa_volume_control_add_device(pa_volume_control *control, pa_device *device); -+void pa_volume_control_remove_device(pa_volume_control *control, pa_device *device); -+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 -new file mode 100644 -index 0000000..8e93bce ---- /dev/null -+++ b/src/pulse/ext-volume-api.c -@@ -0,0 +1,275 @@ -+/*** -+ 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 -+#endif -+ -+#include "ext-volume-api.h" -+ -+#include -+#include -+#include -+ -+#include -+ -+int pa_ext_volume_api_balance_valid(double balance) { -+ return balance >= 0.0 && balance <= 1.0; -+} -+ -+int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance) { -+ unsigned channel; -+ -+ pa_assert(volume); -+ -+ if (check_volume && !PA_VOLUME_IS_VALID(volume->volume)) -+ return 0; -+ -+ if (!check_balance) -+ return 1; -+ -+ if (!pa_channel_map_valid(&volume->channel_map)) -+ return 0; -+ -+ for (channel = 0; channel < volume->channel_map.channels; channel++) { -+ if (!pa_ext_volume_api_balance_valid(volume->balance[channel])) -+ return 0; -+ } -+ -+ return 1; -+} -+ -+void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) { -+ unsigned i; -+ -+ pa_assert(volume); -+ -+ volume->volume = PA_VOLUME_INVALID; -+ -+ for (i = 0; i < PA_CHANNELS_MAX; i++) -+ volume->balance[i] = -1.0; -+ -+ pa_channel_map_init(&volume->channel_map); -+} -+ -+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)); -+ -+ bvolume->volume = volume; -+ bvolume->balance[0] = 1.0; -+ pa_channel_map_init_mono(&bvolume->channel_map); -+} -+ -+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; -+ -+ pa_assert(a); -+ pa_assert(b); -+ -+ if (check_volume && a->volume != b->volume) -+ return 0; -+ -+ if (!check_balance) -+ return 1; -+ -+ if (!pa_channel_map_equal(&a->channel_map, &b->channel_map)) -+ return 0; -+ -+ for (i = 0; i < a->channel_map.channels; i++) { -+ if (fabs(a->balance[i] - b->balance[i]) > 0.00001) -+ return 0; -+ } -+ -+ return 1; -+} -+ -+void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume, -+ const pa_channel_map *map) { -+ unsigned i; -+ -+ pa_assert(bvolume); -+ pa_assert(cvolume); -+ pa_assert(map); -+ pa_assert(cvolume->channels == map->channels); -+ -+ bvolume->volume = pa_cvolume_max(cvolume); -+ bvolume->channel_map = *map; -+ -+ for (i = 0; i < map->channels; i++) { -+ if (bvolume->volume != PA_VOLUME_MUTED) -+ bvolume->balance[i] = ((double) cvolume->values[i]) / ((double) bvolume->volume); -+ else -+ bvolume->balance[i] = 1.0; -+ } -+} -+ -+void pa_ext_volume_api_bvolume_to_cvolume(const pa_ext_volume_api_bvolume *bvolume, pa_cvolume *cvolume) { -+ unsigned i; -+ -+ pa_assert(bvolume); -+ pa_assert(cvolume); -+ pa_assert(pa_ext_volume_api_bvolume_valid(bvolume, true, true)); -+ -+ cvolume->channels = bvolume->channel_map.channels; -+ -+ for (i = 0; i < bvolume->channel_map.channels; i++) -+ cvolume->values[i] = bvolume->volume * bvolume->balance[i]; -+} -+ -+void pa_ext_volume_api_bvolume_copy_balance(pa_ext_volume_api_bvolume *to, -+ const pa_ext_volume_api_bvolume *from) { -+ pa_assert(to); -+ pa_assert(from); -+ -+ memcpy(to->balance, from->balance, sizeof(from->balance)); -+ to->channel_map = from->channel_map; -+} -+ -+void pa_ext_volume_api_bvolume_reset_balance(pa_ext_volume_api_bvolume *volume, const pa_channel_map *map) { -+ unsigned i; -+ -+ pa_assert(volume); -+ pa_assert(map); -+ pa_assert(pa_channel_map_valid(map)); -+ -+ for (i = 0; i < map->channels; i++) -+ volume->balance[i] = 1.0; -+ -+ volume->channel_map = *map; -+} -+ -+void pa_ext_volume_api_bvolume_remap(pa_ext_volume_api_bvolume *volume, const pa_channel_map *to) { -+ unsigned i; -+ pa_cvolume cvolume; -+ -+ pa_assert(volume); -+ pa_assert(to); -+ pa_assert(pa_ext_volume_api_bvolume_valid(volume, false, true)); -+ pa_assert(pa_channel_map_valid(to)); -+ -+ cvolume.channels = volume->channel_map.channels; -+ -+ for (i = 0; i < cvolume.channels; i++) -+ cvolume.values[i] = volume->balance[i] * (double) PA_VOLUME_NORM; -+ -+ pa_cvolume_remap(&cvolume, &volume->channel_map, to); -+ -+ for (i = 0; i < to->channels; i++) -+ volume->balance[i] = (double) cvolume.values[i] / (double) PA_VOLUME_NORM; -+ -+ volume->channel_map = *to; -+} -+ -+double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_bvolume *volume) { -+ pa_ext_volume_api_bvolume bvolume; -+ pa_cvolume cvolume; -+ double ret; -+ -+ pa_assert(volume); -+ -+ bvolume.volume = PA_VOLUME_NORM; -+ pa_ext_volume_api_bvolume_copy_balance(&bvolume, volume); -+ pa_ext_volume_api_bvolume_to_cvolume(&bvolume, &cvolume); -+ ret = pa_cvolume_get_balance(&cvolume, &volume->channel_map); -+ -+ return ret; -+} -+ -+void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance) { -+ pa_cvolume cvolume; -+ pa_volume_t old_volume; -+ -+ pa_assert(volume); -+ -+ if (!pa_channel_map_can_balance(&volume->channel_map)) -+ return; -+ -+ pa_cvolume_reset(&cvolume, volume->channel_map.channels); -+ pa_cvolume_set_balance(&cvolume, &volume->channel_map, balance); -+ old_volume = volume->volume; -+ pa_ext_volume_api_bvolume_from_cvolume(volume, &cvolume, &volume->channel_map); -+ volume->volume = old_volume; -+} -+ -+double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) { -+ pa_ext_volume_api_bvolume bvolume; -+ pa_cvolume cvolume; -+ double ret; -+ -+ pa_assert(volume); -+ -+ bvolume.volume = PA_VOLUME_NORM; -+ pa_ext_volume_api_bvolume_copy_balance(&bvolume, volume); -+ pa_ext_volume_api_bvolume_to_cvolume(&bvolume, &cvolume); -+ ret = pa_cvolume_get_fade(&cvolume, &volume->channel_map); -+ -+ return ret; -+} -+ -+void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance) { -+ pa_cvolume cvolume; -+ pa_volume_t old_volume; -+ -+ pa_assert(volume); -+ -+ if (!pa_channel_map_can_fade(&volume->channel_map)) -+ return; -+ -+ pa_cvolume_reset(&cvolume, volume->channel_map.channels); -+ pa_cvolume_set_fade(&cvolume, &volume->channel_map, balance); -+ old_volume = volume->volume; -+ pa_ext_volume_api_bvolume_from_cvolume(volume, &cvolume, &volume->channel_map); -+ volume->volume = old_volume; -+} -+ -+char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len, -+ const pa_ext_volume_api_bvolume *volume) { -+ char *e; -+ unsigned channel; -+ bool first = true; -+ -+ pa_assert(buf); -+ pa_assert(buf_len > 0); -+ pa_assert(volume); -+ -+ pa_init_i18n(); -+ -+ if (!pa_ext_volume_api_bvolume_valid(volume, true, true)) { -+ pa_snprintf(buf, buf_len, _("(invalid)")); -+ return buf; -+ } -+ -+ *(e = buf) = 0; -+ -+ for (channel = 0; channel < volume->channel_map.channels && buf_len > 1; channel++) { -+ buf_len -= pa_snprintf(e, buf_len, "%s%s: %u%%", -+ first ? "" : ", ", -+ pa_channel_position_to_string(volume->channel_map.map[channel]), -+ (unsigned) (volume->balance[channel] * 100 + 0.5)); -+ -+ e = strchr(e, 0); -+ first = false; -+ } -+ -+ return buf; -+} -diff --git a/src/pulse/ext-volume-api.h b/src/pulse/ext-volume-api.h -new file mode 100644 -index 0000000..36b7748 ---- /dev/null -+++ b/src/pulse/ext-volume-api.h -@@ -0,0 +1,68 @@ -+#ifndef fooextvolumeapihfoo -+#define fooextvolumeapihfoo -+ -+/*** -+ 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 -+#include -+#include -+ -+/* This API is temporary, and has no stability guarantees whatsoever. Think -+ * twice before making anything that relies on this API. This is undocumented -+ * for a reason. */ -+ -+PA_C_DECL_BEGIN -+ -+typedef struct pa_ext_volume_api_bvolume pa_ext_volume_api_bvolume; -+ -+struct pa_ext_volume_api_bvolume { -+ pa_volume_t volume; -+ double balance[PA_CHANNELS_MAX]; -+ pa_channel_map channel_map; -+}; -+ -+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_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume); -+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, -+ const pa_channel_map *map); -+void pa_ext_volume_api_bvolume_to_cvolume(const pa_ext_volume_api_bvolume *bvolume, pa_cvolume *cvolume); -+void pa_ext_volume_api_bvolume_copy_balance(pa_ext_volume_api_bvolume *to, -+ const pa_ext_volume_api_bvolume *from); -+void pa_ext_volume_api_bvolume_reset_balance(pa_ext_volume_api_bvolume *volume, const pa_channel_map *map); -+void pa_ext_volume_api_bvolume_remap(pa_ext_volume_api_bvolume *volume, const pa_channel_map *to); -+double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE; -+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); -+ -+#define PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX 500 -+char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_size, -+ const pa_ext_volume_api_bvolume *volume); -+ -+PA_C_DECL_END -+ -+#endif --- -2.1.4 - ---- a/po/POTFILES.in 2016-04-13 17:40:00.818008672 +0200 -+++ b/po/POTFILES.in 2016-04-13 17:40:30.885008622 +0200 -@@ -198,3 +198,5 @@ - src/utils/padsp.c - src/utils/pasuspender.c - src/utils/pax11publish.c -+src/modules/volume-api/device-creator.c -+src/pulse/ext-volume-api.c