From 4464b68290669f133c17e7135dc86be9c4019326 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Fri, 20 Jul 2018 14:32:51 +0300 Subject: pulseaudio: remove unused .patch files from the repository No recipe uses those. Change-Id: Iec368fcf6e6340866c6d346a438e21c8cb7960ef Signed-off-by: George Kiagiadakis --- ...08-install-files-for-a-module-development.patch | 53 - ...ume-ramp-additions-to-the-low-level-infra.patch | 503 -- ...-ramp-adding-volume-ramping-to-sink-input.patch | 159 - ...12-volume-ramp-add-volume-ramping-to-sink.patch | 159 - ...add-internal-corking-state-for-sink-input.patch | 72 - .../pulseaudio/0020-core-util-Add-pa_join.patch | 37 - ...21-dynarray-Add-pa_dynarray_get_raw_array.patch | 21 - ...022-device-port-Add-pa_device_port.active.patch | 198 - .../0030-volume-api-Add-libvolume-api.patch | 6118 -------------------- .../0031-Add-module-main-volume-policy.patch | 1418 ----- .../0039-main-volume-policy-adapt-to-pa6rev.patch | 11 - 11 files changed, 8749 deletions(-) delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0008-install-files-for-a-module-development.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0010-volume-ramp-additions-to-the-low-level-infra.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0011-volume-ramp-adding-volume-ramping-to-sink-input.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0012-volume-ramp-add-volume-ramping-to-sink.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0013-add-internal-corking-state-for-sink-input.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0020-core-util-Add-pa_join.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0021-dynarray-Add-pa_dynarray_get_raw_array.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0022-device-port-Add-pa_device_port.active.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0030-volume-api-Add-libvolume-api.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0031-Add-module-main-volume-policy.patch delete mode 100644 meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0039-main-volume-policy-adapt-to-pa6rev.patch diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0008-install-files-for-a-module-development.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0008-install-files-for-a-module-development.patch deleted file mode 100644 index ac164d643..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0008-install-files-for-a-module-development.patch +++ /dev/null @@ -1,53 +0,0 @@ ---- a/Makefile.am 2016-04-12 11:28:10.832997230 +0200 -+++ b/Makefile.am 2016-04-12 11:32:41.868996777 +0200 -@@ -49,7 +49,13 @@ - vala/libpulse-simple.deps vala/libpulse-simple.vapi - - pkgconfigdir = $(libdir)/pkgconfig --pkgconfig_DATA = libpulse.pc libpulse-simple.pc -+pkgconfig_DATA = libpulse.pc libpulse-simple.pc pulseaudio-module-devel.pc -+ -+moduledev_DATA = pulsecore-config.h $(top_srcdir)/src/pulsecore/*.h -+moduledevdir = $(includedir)/pulsemodule/pulsecore -+ -+moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h -+moduledevinternaldir = $(includedir)/pulsemodule/pulse - - if HAVE_GLIB20 - pkgconfig_DATA += \ -@@ -103,6 +109,9 @@ - check-daemon: - $(MAKE) -C src check-daemon - -+pulsecore-config.h: config.h -+ cp $< $@ -+ - .PHONY: homepage distcleancheck doxygen - - # see git-version-gen ---- a/configure.ac 2016-04-12 11:34:10.605996629 +0200 -+++ b/configure.ac 2016-04-12 11:37:43.502996274 +0200 -@@ -1464,6 +1464,7 @@ - man/default.pa.5.xml - man/pulse-cli-syntax.5.xml - man/start-pulseaudio-x11.1.xml -+pulseaudio-module-devel.pc - ]) - - AC_CONFIG_FILES([src/esdcompat:src/daemon/esdcompat.in], [chmod +x src/esdcompat]) ---- /dev/null 2016-03-15 16:08:23.302999643 +0100 -+++ b/pulseaudio-module-devel.pc.in 2016-03-17 16:16:58.427977484 +0100 -@@ -0,0 +1,12 @@ -+prefix=@prefix@ -+exec_prefix=@exec_prefix@ -+libdir=@libdir@ -+includedir=@includedir@ -+modlibexecdir=@modlibexecdir@ -+ -+Name: pulseaudio-module-devel -+Description: PulseAudio Module Development Interface -+Version: @PACKAGE_VERSION@ -+Libs: -L${libdir} -L${libdir}/pulseaudio -L${modlibexecdir} -lpulsecommon-@PA_MAJORMINOR@ -lpulsecore-@PA_MAJORMINOR@ -lprotocol-native -+Libs.private: -+Cflags: -I${includedir}/pulsemodule -D_REENTRANT - diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0010-volume-ramp-additions-to-the-low-level-infra.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0010-volume-ramp-additions-to-the-low-level-infra.patch deleted file mode 100644 index e249bc438..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0010-volume-ramp-additions-to-the-low-level-infra.patch +++ /dev/null @@ -1,503 +0,0 @@ ---- a/src/map-file 2016-04-12 15:03:17.009975690 +0200 -+++ b/src/map-file 2016-04-12 15:03:52.389975631 +0200 -@@ -136,6 +136,9 @@ - pa_cvolume_merge; - pa_cvolume_min; - pa_cvolume_min_mask; -+pa_cvolume_ramp_init; -+pa_cvolume_ramp_set; -+pa_cvolume_ramp_channel_ramp_set; - pa_cvolume_remap; - pa_cvolume_scale; - pa_cvolume_scale_mask; ---- a/src/pulse/def.h 2016-04-12 15:05:58.245975421 +0200 -+++ b/src/pulse/def.h 2016-04-12 15:13:19.424974685 +0200 -@@ -347,11 +347,15 @@ - * consider absolute when the sink is in flat volume mode, - * relative otherwise. \since 0.9.20 */ - -- PA_STREAM_PASSTHROUGH = 0x80000U -+ PA_STREAM_PASSTHROUGH = 0x80000U, - /**< Used to tag content that will be rendered by passthrough sinks. - * The data will be left as is and not reformatted, resampled. - * \since 1.0 */ - -+ PA_STREAM_START_RAMP_MUTED = 0x100000U -+ /**< Used to tag content that the stream will be started ramp volume -+ * muted so that you can nicely fade it in */ -+ - } pa_stream_flags_t; - - /** \cond fulldocs */ -@@ -380,6 +384,7 @@ - #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND - #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME - #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH -+#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED - - /** \endcond */ - -@@ -1047,6 +1052,13 @@ - /** \endcond */ - #endif - -+/** \cond fulldocs */ -+#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR -+#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC -+#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC -+ -+/** \endcond */ -+ - PA_C_DECL_END - - #endif ---- a/src/pulse/volume.c 2016-04-12 15:13:38.598974653 +0200 -+++ b/src/pulse/volume.c 2016-04-12 15:27:57.729973219 +0200 -@@ -445,7 +445,10 @@ - unsigned c; - pa_assert(a); - -- pa_return_val_if_fail(pa_cvolume_valid(a), 0); -+ if (pa_cvolume_valid(a) == 0) -+ abort(); -+ -+ /* pa_return_val_if_fail(pa_cvolume_valid(a), 0); */ - pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0); - - for (c = 0; c < a->channels; c++) -@@ -986,3 +989,51 @@ - - return pa_cvolume_scale(v, m); - } -+ -+pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp) { -+ unsigned c; -+ -+ pa_assert(ramp); -+ -+ ramp->channels = 0; -+ -+ for (c = 0; c < PA_CHANNELS_MAX; c++) { -+ ramp->ramps[c].type = PA_VOLUME_RAMP_TYPE_LINEAR; -+ ramp->ramps[c].length = 0; -+ ramp->ramps[c].target = PA_VOLUME_INVALID; -+ } -+ -+ return ramp; -+} -+ -+pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channels, pa_volume_ramp_type_t type, long time, pa_volume_t vol) { -+ int i; -+ -+ pa_assert(ramp); -+ pa_assert(channels > 0); -+ pa_assert(time >= 0); -+ pa_assert(channels <= PA_CHANNELS_MAX); -+ -+ ramp->channels = (uint8_t) channels; -+ -+ for (i = 0; i < ramp->channels; i++) { -+ ramp->ramps[i].type = type; -+ ramp->ramps[i].length = time; -+ ramp->ramps[i].target = PA_CLAMP_VOLUME(vol); -+ } -+ -+ return ramp; -+} -+ -+pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol) { -+ -+ pa_assert(ramp); -+ pa_assert(channel <= ramp->channels); -+ pa_assert(time >= 0); -+ -+ ramp->ramps[channel].type = type; -+ ramp->ramps[channel].length = time; -+ ramp->ramps[channel].target = PA_CLAMP_VOLUME(vol); -+ -+ return ramp; -+} ---- a/src/pulse/volume.h 2016-04-12 15:40:34.989971955 +0200 -+++ b/src/pulse/volume.h 2016-04-12 15:38:50.708972129 +0200 -@@ -413,6 +413,36 @@ - * the channels are kept. \since 0.9.16 */ - pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec); - -+/** Volume ramp type -+*/ -+typedef enum pa_volume_ramp_type { -+ PA_VOLUME_RAMP_TYPE_LINEAR = 0, /**< linear */ -+ PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1, /**< logarithmic */ -+ PA_VOLUME_RAMP_TYPE_CUBIC = 2, -+} pa_volume_ramp_type_t; -+ -+/** A structure encapsulating a volume ramp */ -+typedef struct pa_volume_ramp_t { -+ pa_volume_ramp_type_t type; -+ long length; -+ pa_volume_t target; -+} pa_volume_ramp_t; -+ -+/** A structure encapsulating a multichannel volume ramp */ -+typedef struct pam_cvolume_ramp { -+ uint8_t channels; -+ pa_volume_ramp_t ramps[PA_CHANNELS_MAX]; -+} pa_cvolume_ramp; -+ -+/** Init volume ramp struct */ -+pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp); -+ -+/** Set first n channels of ramp struct to certain value */ -+pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); -+ -+/** Set individual channel in the channel struct */ -+pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); -+ - PA_C_DECL_END - - #endif ---- a/src/pulsecore/sample-util.c 2016-04-12 15:41:51.812971827 +0200 -+++ b/src/pulsecore/sample-util.c 2016-04-12 16:31:56.795966812 +0200 -@@ -41,6 +41,13 @@ - - #define PA_SILENCE_MAX (PA_PAGE_SIZE*16) - -+#define VOLUME_PADDING 32 -+ -+typedef union { -+ float f; -+ uint32_t i; -+} volume_val; -+ - pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { - void *data; - -@@ -403,3 +410,292 @@ - usec = pa_bytes_to_usec_round_up(size, from); - return pa_usec_to_bytes_round_up(usec, to); - } -+ -+static void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) { -+ unsigned channel, padding; -+ -+ pa_assert(linear); -+ pa_assert(volume); -+ -+ for (channel = 0; channel < nchannels; channel++) -+ linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U); -+ -+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) -+ linear[channel] = linear[padding]; -+} -+ -+static void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) { -+ unsigned channel, padding; -+ -+ pa_assert(linear); -+ pa_assert(volume); -+ -+ for (channel = 0; channel < nchannels; channel++) -+ linear[channel] = volume[channel]; -+ -+ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) -+ linear[channel] = linear[padding]; -+} -+ -+typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels); -+ -+static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = { -+ [PA_SAMPLE_U8] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_ALAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_ULAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S16LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S16BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping, -+ [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping, -+ [PA_SAMPLE_S32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S24LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S24BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S24_32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, -+ [PA_SAMPLE_S24_32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping -+}; -+ -+static const unsigned format_sample_size_table[] = { -+ [PA_SAMPLE_U8] = 1, -+ [PA_SAMPLE_ALAW] = 1, -+ [PA_SAMPLE_ULAW] = 1, -+ [PA_SAMPLE_S16LE] = 2, -+ [PA_SAMPLE_S16BE] = 2, -+ [PA_SAMPLE_FLOAT32LE] = 4, -+ [PA_SAMPLE_FLOAT32BE] = 4, -+ [PA_SAMPLE_S32LE] = 4, -+ [PA_SAMPLE_S32BE] = 4, -+ [PA_SAMPLE_S24LE] = 3, -+ [PA_SAMPLE_S24BE] = 3, -+ [PA_SAMPLE_S24_32LE] = 4, -+ [PA_SAMPLE_S24_32BE] = 4 -+}; -+ -+static float calc_volume_ramp_linear(pa_volume_ramp_int_t *ramp) { -+ pa_assert(ramp); -+ pa_assert(ramp->length > 0); -+ -+ /* basic linear interpolation */ -+ return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / (float) ramp->length; -+} -+ -+static float calc_volume_ramp_logarithmic(pa_volume_ramp_int_t *ramp) { -+ float x_val, s, e; -+ long temp; -+ -+ pa_assert(ramp); -+ pa_assert(ramp->length > 0); -+ -+ if (ramp->end > ramp->start) { -+ temp = ramp->left; -+ s = ramp->end; -+ e = ramp->start; -+ } else { -+ temp = ramp->length - ramp->left; -+ s = ramp->start; -+ e = ramp->end; -+ } -+ -+ x_val = temp == 0 ? 0.0 : powf(temp, 10); -+ -+ /* base 10 logarithmic interpolation */ -+ return s + x_val * (e - s) / powf(ramp->length, 10); -+} -+ -+static float calc_volume_ramp_cubic(pa_volume_ramp_int_t *ramp) { -+ float x_val, s, e; -+ long temp; -+ -+ pa_assert(ramp); -+ pa_assert(ramp->length > 0); -+ -+ if (ramp->end > ramp->start) { -+ temp = ramp->left; -+ s = ramp->end; -+ e = ramp->start; -+ } else { -+ temp = ramp->length - ramp->left; -+ s = ramp->start; -+ e = ramp->end; -+ } -+ -+ x_val = temp == 0 ? 0.0 : cbrtf(temp); -+ -+ /* cubic interpolation */ -+ return s + x_val * (e - s) / cbrtf(ramp->length); -+} -+ -+typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp_int_t *); -+ -+static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = { -+ [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear, -+ [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic, -+ [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic -+}; -+ -+static void calc_volume_ramps(pa_cvolume_ramp_int *ram, float *vol) -+{ -+ int i; -+ -+ for (i = 0; i < ram->channels; i++) { -+ if (ram->ramps[i].left <= 0) { -+ if (ram->ramps[i].target == PA_VOLUME_NORM) { -+ vol[i] = 1.0; -+ } -+ } else { -+ vol[i] = ram->ramps[i].curr = calc_volume_ramp_table[ram->ramps[i].type] (&ram->ramps[i]); -+ ram->ramps[i].left--; -+ } -+ } -+} -+ -+void pa_volume_ramp_memchunk( -+ pa_memchunk *c, -+ const pa_sample_spec *spec, -+ pa_cvolume_ramp_int *ramp) { -+ -+ void *ptr; -+ volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING]; -+ float vol[PA_CHANNELS_MAX + VOLUME_PADDING]; -+ pa_do_volume_func_t do_volume; -+ long length_in_frames; -+ int i; -+ -+ pa_assert(c); -+ pa_assert(spec); -+ pa_assert(pa_frame_aligned(c->length, spec)); -+ pa_assert(ramp); -+ -+ length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels; -+ -+ if (pa_memblock_is_silence(c->memblock)) { -+ for (i = 0; i < ramp->channels; i++) { -+ if (ramp->ramps[i].length > 0) -+ ramp->ramps[i].length -= length_in_frames; -+ } -+ return; -+ } -+ -+ if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) { -+ pa_log_warn("Unable to change volume of format"); -+ return; -+ } -+ -+ do_volume = pa_get_volume_func(spec->format); -+ pa_assert(do_volume); -+ -+ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index; -+ -+ for (i = 0; i < length_in_frames; i++) { -+ calc_volume_ramps(ramp, vol); -+ calc_volume_table_no_mapping[spec->format] ((void *)linear, vol, spec->channels); -+ -+ /* we only process one frame per iteration */ -+ do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels); -+ -+ /* pa_log_debug("1: %d 2: %d", linear[0], linear[1]); */ -+ -+ ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels; -+ } -+ -+ pa_memblock_release(c->memblock); -+} -+ -+pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate) { -+ int i; -+ float temp; -+ -+ for (i = 0; i < dst->channels; i++) { -+ dst->ramps[i].type = src->ramps[i].type; -+ /* ms to samples */ -+ dst->ramps[i].length = src->ramps[i].length * sample_rate / 1000; -+ dst->ramps[i].left = dst->ramps[i].length; -+ dst->ramps[i].start = dst->ramps[i].end; -+ dst->ramps[i].target = src->ramps[i].target; -+ /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */ -+ temp = src->ramps[i].target / (float)0x10000U; -+ dst->ramps[i].end = temp * temp * temp; -+ } -+ -+ return dst; -+} -+ -+bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp) { -+ int i; -+ -+ for (i = 0; i < ramp->channels; i++) { -+ if (ramp->ramps[i].left > 0) -+ return true; -+ } -+ -+ return false; -+} -+ -+bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp) { -+ int i; -+ -+ for (i = 0; i < ramp->channels; i++) { -+ if (ramp->ramps[i].target != PA_VOLUME_NORM) -+ return true; -+ } -+ -+ return false; -+} -+ -+pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume) { -+ int i = 0; -+ -+ volume->channels = ramp->channels; -+ -+ for (i = 0; i < ramp->channels; i++) -+ volume->values[i] = ramp->ramps[i].target; -+ -+ return volume; -+} -+ -+pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst) { -+ int i; -+ -+ for (i = 0; i < src->channels; i++) { -+ /* if new vols are invalid, copy old ramp i.e. no effect */ -+ if (dst->ramps[i].target == PA_VOLUME_INVALID) -+ dst->ramps[i] = src->ramps[i]; -+ /* if there's some old ramp still left */ -+ else if (src->ramps[i].left > 0) -+ dst->ramps[i].start = src->ramps[i].curr; -+ } -+ -+ return dst; -+} -+ -+pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels) { -+ int i; -+ float temp; -+ -+ src->channels = channels; -+ -+ for (i = 0; i < channels; i++) { -+ src->ramps[i].type = PA_VOLUME_RAMP_TYPE_LINEAR; -+ src->ramps[i].length = 0; -+ src->ramps[i].left = 0; -+ if (vol == PA_VOLUME_NORM) { -+ src->ramps[i].start = 1.0; -+ src->ramps[i].end = 1.0; -+ src->ramps[i].curr = 1.0; -+ } -+ else if (vol == PA_VOLUME_MUTED) { -+ src->ramps[i].start = 0.0; -+ src->ramps[i].end = 0.0; -+ src->ramps[i].curr = 0.0; -+ } -+ else { -+ temp = vol / (float)0x10000U; -+ src->ramps[i].start = temp * temp * temp; -+ src->ramps[i].end = src->ramps[i].start; -+ src->ramps[i].curr = src->ramps[i].start; -+ } -+ src->ramps[i].target = vol; -+ } -+ -+ return src; -+} ---- a/src/pulsecore/sample-util.h 2016-04-12 15:53:06.327970701 +0200 -+++ b/src/pulsecore/sample-util.h 2016-04-12 16:24:16.356967580 +0200 -@@ -45,6 +45,33 @@ - - pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length); - -+typedef struct pa_volume_ramp_int_t { -+ pa_volume_ramp_type_t type; -+ long length; -+ long left; -+ float start; -+ float end; -+ float curr; -+ pa_volume_t target; -+} pa_volume_ramp_int_t; -+ -+typedef struct pa_cvolume_ramp_int { -+ uint8_t channels; -+ pa_volume_ramp_int_t ramps[PA_CHANNELS_MAX]; -+} pa_cvolume_ramp_int; -+ -+pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate); -+bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp); -+bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp); -+pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst); -+pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels); -+pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume); -+ -+void pa_volume_ramp_memchunk( -+ pa_memchunk *c, -+ const pa_sample_spec *spec, -+ pa_cvolume_ramp_int *ramp); -+ - size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; - - bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0011-volume-ramp-adding-volume-ramping-to-sink-input.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0011-volume-ramp-adding-volume-ramping-to-sink-input.patch deleted file mode 100644 index 08fb79248..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0011-volume-ramp-adding-volume-ramping-to-sink-input.patch +++ /dev/null @@ -1,159 +0,0 @@ ---- a/src/pulsecore/sink-input.c 2016-04-12 16:50:41.311964935 +0200 -+++ b/src/pulsecore/sink-input.c 2016-04-12 17:22:40.420961732 +0200 -@@ -525,6 +525,11 @@ - reset_callbacks(i); - i->userdata = NULL; - -+ if (data->flags & PA_SINK_INPUT_START_RAMP_MUTED) -+ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_MUTED, data->sample_spec.channels); -+ else -+ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_NORM, data->sample_spec.channels); -+ - i->thread_info.state = i->state; - i->thread_info.attached = false; - pa_atomic_store(&i->thread_info.drained, 1); -@@ -541,6 +546,8 @@ - i->thread_info.playing_for = 0; - i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - -+ i->thread_info.ramp = i->ramp; -+ - pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0); - pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0); - -@@ -922,6 +929,8 @@ - while (tchunk.length > 0) { - pa_memchunk wchunk; - bool nvfs = need_volume_factor_sink; -+ pa_cvolume target; -+ bool tmp; - - wchunk = tchunk; - pa_memblock_ref(wchunk.memblock); -@@ -958,6 +967,16 @@ - pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &i->volume_factor_sink); - } - -+ /* check for possible volume ramp */ -+ if (pa_cvolume_ramp_active(&i->thread_info.ramp)) { -+ pa_memchunk_make_writable(&wchunk, 0); -+ pa_volume_ramp_memchunk(&wchunk, &i->sink->sample_spec, &(i->thread_info.ramp)); -+ } else if ((tmp = pa_cvolume_ramp_target_active(&(i->thread_info.ramp)))) { -+ pa_memchunk_make_writable(&wchunk, 0); -+ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target); -+ pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &target); -+ } -+ - pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk); - } else { - pa_memchunk rchunk; -@@ -974,6 +993,16 @@ - pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink); - } - -+ /* check for possible volume ramp */ -+ if (pa_cvolume_ramp_active(&i->thread_info.ramp)) { -+ pa_memchunk_make_writable(&wchunk, 0); -+ pa_volume_ramp_memchunk(&wchunk, &i->sink->sample_spec, &(i->thread_info.ramp)); -+ } else if ((tmp = pa_cvolume_ramp_target_active(&(i->thread_info.ramp)))) { -+ pa_memchunk_make_writable(&wchunk, 0); -+ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target); -+ pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &target); -+ } -+ - pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); - pa_memblock_unref(rchunk.memblock); - } -@@ -1338,6 +1367,31 @@ - return 0; - } - -+/* Called from main thread */ -+void pa_sink_input_set_volume_ramp( -+ pa_sink_input *i, -+ const pa_cvolume_ramp *ramp, -+ bool send_msg, -+ bool save) { -+ -+ pa_sink_input_assert_ref(i); -+ pa_assert_ctl_context(); -+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -+ pa_assert(ramp); -+ -+ pa_cvolume_ramp_convert(ramp, &i->ramp, i->sample_spec.rate); -+ -+ pa_log_debug("setting volume ramp with target vol:%d and length:%ld", -+ i->ramp.ramps[0].target, -+ i->ramp.ramps[0].length); -+ -+ -+ /* This tells the sink that volume ramp changed */ -+ if (send_msg) -+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, -+ NULL, 0, NULL) == 0); -+} -+ - /* Called from main context */ - static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { - pa_sink_input_assert_ref(i); -@@ -1929,6 +1983,12 @@ - } - return 0; - -+ case PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP: -+ /* we have ongoing ramp where we take current start values */ -+ pa_cvolume_ramp_start_from(&i->thread_info.ramp, &i->ramp); -+ i->thread_info.ramp = i->ramp; -+ return 0; -+ - case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: - if (i->thread_info.muted != i->muted) { - i->thread_info.muted = i->muted; ---- a/src/pulsecore/sink-input.h 2016-04-12 16:50:46.712964926 +0200 -+++ b/src/pulsecore/sink-input.h 2016-04-12 17:30:24.289960958 +0200 -@@ -59,7 +59,8 @@ - PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256, - PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512, - PA_SINK_INPUT_KILL_ON_SUSPEND = 1024, -- PA_SINK_INPUT_PASSTHROUGH = 2048 -+ PA_SINK_INPUT_PASSTHROUGH = 2048, -+ PA_SINK_INPUT_START_RAMP_MUTED = 4096, - } pa_sink_input_flags_t; - - struct pa_sink_input { -@@ -122,6 +123,9 @@ - * this.*/ - bool save_sink:1, save_volume:1, save_muted:1; - -+ /* for volume ramps */ -+ pa_cvolume_ramp_int ramp; -+ - pa_resample_method_t requested_resample_method, actual_resample_method; - - /* Returns the chunk of audio data and drops it from the -@@ -250,6 +254,8 @@ - pa_usec_t requested_sink_latency; - - pa_hashmap *direct_outputs; -+ -+ pa_cvolume_ramp_int ramp; - } thread_info; - - void *userdata; -@@ -266,6 +272,7 @@ - PA_SINK_INPUT_MESSAGE_SET_STATE, - PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, - PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, -+ PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, - PA_SINK_INPUT_MESSAGE_MAX - }; - -@@ -374,6 +381,8 @@ - - void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save); - -+void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg, bool save); -+ - void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p); - - pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0012-volume-ramp-add-volume-ramping-to-sink.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0012-volume-ramp-add-volume-ramping-to-sink.patch deleted file mode 100644 index 43e8283ef..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0012-volume-ramp-add-volume-ramping-to-sink.patch +++ /dev/null @@ -1,159 +0,0 @@ ---- a/src/pulsecore/sink.c 2016-04-12 18:01:23.403957855 +0200 -+++ b/src/pulsecore/sink.c 2016-04-12 18:44:49.677953506 +0200 -@@ -324,6 +324,8 @@ - &s->sample_spec, - 0); - -+ pa_cvolume_ramp_int_init(&s->ramp, PA_VOLUME_NORM, data->sample_spec.channels); -+ - s->thread_info.rtpoll = NULL; - s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, - (pa_free_cb_t) pa_sink_input_unref); -@@ -347,6 +349,8 @@ - s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec; - s->thread_info.latency_offset = s->latency_offset; - -+ s->thread_info.ramp = s->ramp; -+ - /* FIXME: This should probably be moved to pa_sink_put() */ - pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); - -@@ -1182,6 +1186,7 @@ - - } else if (n == 1) { - pa_cvolume volume; -+ pa_cvolume target; - - *result = info[0].chunk; - pa_memblock_ref(result->memblock); -@@ -1198,9 +1203,20 @@ - result, - &s->sample_spec, - result->length); -- } else if (!pa_cvolume_is_norm(&volume)) { -+ } else if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { - pa_memchunk_make_writable(result, 0); -- pa_volume_memchunk(result, &s->sample_spec, &volume); -+ if (!pa_cvolume_ramp_active(&s->thread_info.ramp)) { -+ if (!pa_cvolume_is_norm(&volume)) -+ pa_volume_memchunk(result, &s->sample_spec, &volume); -+ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp)); -+ } -+ else { -+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) { -+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target); -+ pa_sw_cvolume_multiply(&volume, &volume, &target); -+ } -+ pa_volume_memchunk(result, &s->sample_spec, &volume); -+ } - } - } else { - void *ptr; -@@ -1290,6 +1306,7 @@ - - } else { - void *ptr; -+ pa_cvolume target_vol; - - ptr = pa_memblock_acquire(target->memblock); - -@@ -1299,6 +1316,15 @@ - &s->thread_info.soft_volume, - s->thread_info.soft_muted); - -+ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { -+ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) -+ pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp)); -+ else { -+ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol); -+ pa_volume_memchunk(target, &s->sample_spec, &target_vol); -+ } -+ } -+ - pa_memblock_release(target->memblock); - } - -@@ -2058,6 +2084,32 @@ - pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0); - } - -+/* Called from main thread */ -+void pa_sink_set_volume_ramp( -+ pa_sink *s, -+ const pa_cvolume_ramp *ramp, -+ bool send_msg, -+ bool save) { -+ -+ pa_sink_assert_ref(s); -+ pa_assert_ctl_context(); -+ pa_assert(PA_SINK_IS_LINKED(s->state)); -+ pa_assert(ramp); -+ -+ /* make sure we don't change the volume when a PASSTHROUGH input is connected ... -+ * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */ -+ if (pa_sink_is_passthrough(s)) { -+ pa_log_warn("Cannot do volume ramp, Sink is connected to PASSTHROUGH input"); -+ return; -+ } -+ -+ pa_cvolume_ramp_convert(ramp, &s->ramp, s->sample_spec.rate); -+ -+ /* This tells the sink that volume ramp changed */ -+ if (send_msg) -+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0); -+} -+ - /* Called from the io thread if sync volume is used, otherwise from the main thread. - * Only to be called by sink implementor */ - void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { -@@ -2713,6 +2765,12 @@ - sync_input_volumes_within_thread(s); - return 0; - -+ case PA_SINK_MESSAGE_SET_VOLUME_RAMP: -+ /* if we have ongoing ramp where we take current start values */ -+ pa_cvolume_ramp_start_from(&s->thread_info.ramp, &s->ramp); -+ s->thread_info.ramp = s->ramp; -+ return 0; -+ - case PA_SINK_MESSAGE_GET_VOLUME: - - if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) { ---- a/src/pulsecore/sink.h 2016-04-12 18:01:42.117957824 +0200 -+++ b/src/pulsecore/sink.h 2016-04-12 18:23:29.394955642 +0200 -@@ -105,6 +105,9 @@ - pa_cvolume saved_volume; - bool saved_save_volume:1; - -+ /* for volume ramps */ -+ pa_cvolume_ramp_int ramp; -+ - pa_asyncmsgq *asyncmsgq; - - pa_memchunk silence; -@@ -300,6 +303,8 @@ - uint32_t volume_change_safety_margin; - /* Usec delay added to all volume change events, may be negative. */ - int32_t volume_change_extra_delay; -+ -+ pa_cvolume_ramp_int ramp; - } thread_info; - - void *userdata; -@@ -333,6 +338,7 @@ - PA_SINK_MESSAGE_SET_MAX_REQUEST, - PA_SINK_MESSAGE_SET_PORT, - PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, -+ PA_SINK_MESSAGE_SET_VOLUME_RAMP, - PA_SINK_MESSAGE_SET_LATENCY_OFFSET, - PA_SINK_MESSAGE_MAX - } pa_sink_message_t; -@@ -453,6 +459,8 @@ - void pa_sink_set_mute(pa_sink *sink, bool mute, bool save); - bool pa_sink_get_mute(pa_sink *sink, bool force_refresh); - -+void pa_sink_set_volume_ramp(pa_sink *s, const pa_cvolume_ramp *ramp, bool send_msg, bool save); -+ - bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); - - int pa_sink_set_port(pa_sink *s, const char *name, bool save); diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0013-add-internal-corking-state-for-sink-input.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0013-add-internal-corking-state-for-sink-input.patch deleted file mode 100644 index cb68e26af..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0013-add-internal-corking-state-for-sink-input.patch +++ /dev/null @@ -1,72 +0,0 @@ ---- a/src/pulsecore/sink-input.c 2016-04-13 11:44:04.389999429 +0200 -+++ b/src/pulsecore/sink-input.c 2016-04-13 11:52:45.167998560 +0200 -@@ -786,6 +786,9 @@ - update_n_corked(i, state); - i->state = state; - -+ i->corked = false; -+ i->corked_internal = false; -+ - /* We might need to update the sink's volume if we are in flat volume mode. */ - if (pa_sink_flat_volume_enabled(i->sink)) - pa_sink_set_volume(i->sink, NULL, false, i->save_volume); -@@ -1493,13 +1496,38 @@ - } - } - -+static void pa_sink_input_cork_really(pa_sink_input *i, bool b) { -+ pa_sink_input_assert_ref(i); -+ pa_assert_ctl_context(); -+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -+ -+ if (i->corked_internal == false && i->corked == false) -+ b = false; -+ else -+ b = true; -+ -+ sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); -+} -+ - /* Called from main context */ - void pa_sink_input_cork(pa_sink_input *i, bool b) { - pa_sink_input_assert_ref(i); - pa_assert_ctl_context(); - pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - -- sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); -+ i->corked = b; -+ -+ pa_sink_input_cork_really(i, b); -+} -+ -+void pa_sink_input_cork_internal(pa_sink_input *i, bool b) { -+ pa_sink_input_assert_ref(i); -+ pa_assert_ctl_context(); -+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -+ -+ i->corked_internal = b; -+ -+ pa_sink_input_cork_really(i, b); - } - - /* Called from main context */ ---- a/src/pulsecore/sink-input.h 2016-04-13 11:44:28.035999390 +0200 -+++ b/src/pulsecore/sink-input.h 2016-04-13 11:53:56.325998441 +0200 -@@ -126,6 +126,9 @@ - /* for volume ramps */ - pa_cvolume_ramp_int ramp; - -+ bool corked; -+ bool corked_internal; -+ - pa_resample_method_t requested_resample_method, actual_resample_method; - - /* Returns the chunk of audio data and drops it from the -@@ -357,6 +360,7 @@ - void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, bool rewrite, bool flush, bool dont_rewind_render); - - void pa_sink_input_cork(pa_sink_input *i, bool b); -+void pa_sink_input_cork_internal(pa_sink_input *i, bool b); - - int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); - int pa_sink_input_update_rate(pa_sink_input *i); diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0020-core-util-Add-pa_join.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0020-core-util-Add-pa_join.patch deleted file mode 100644 index 891a5b83a..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0020-core-util-Add-pa_join.patch +++ /dev/null @@ -1,37 +0,0 @@ ---- a/src/pulsecore/core-util.c 2016-04-13 16:17:33.314016929 +0200 -+++ b/src/pulsecore/core-util.c 2016-04-13 16:18:31.186016833 +0200 -@@ -1104,6 +1104,24 @@ - return pa_xstrndup(current, l); - } - -+char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter) { -+ pa_strbuf *buf; -+ unsigned i; -+ -+ pa_assert(strings || n_strings == 0); -+ -+ buf = pa_strbuf_new(); -+ -+ for (i = 0; i < n_strings; i++) { -+ if (i > 0 && delimiter) -+ pa_strbuf_puts(buf, delimiter); -+ -+ pa_strbuf_puts(buf, strings[i]); -+ } -+ -+ return pa_strbuf_tostring_free(buf); -+} -+ - PA_STATIC_TLS_DECLARE(signame, pa_xfree); - - /* Return the name of an UNIX signal. Similar to Solaris sig2str() */ ---- a/src/pulsecore/core-util.h 2016-04-13 16:17:39.177016919 +0200 -+++ b/src/pulsecore/core-util.h 2016-04-13 16:19:09.141016769 +0200 -@@ -108,6 +108,7 @@ - char *pa_split(const char *c, const char*delimiters, const char **state); - const char *pa_split_in_place(const char *c, const char*delimiters, int *n, const char **state); - char *pa_split_spaces(const char *c, const char **state); -+char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter); - - char *pa_strip_nl(char *s); - char *pa_strip(char *s); diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0021-dynarray-Add-pa_dynarray_get_raw_array.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0021-dynarray-Add-pa_dynarray_get_raw_array.patch deleted file mode 100644 index 040913c8f..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0021-dynarray-Add-pa_dynarray_get_raw_array.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- a/src/pulsecore/dynarray.c 2016-04-13 16:25:38.628016119 +0200 -+++ b/src/pulsecore/dynarray.c 2016-04-13 16:26:54.694015992 +0200 -@@ -91,3 +91,9 @@ - - return array->n_entries; - } -+ -+void * const *pa_dynarray_get_raw_array(pa_dynarray *array) { -+ pa_assert(array); -+ -+ return array->data; -+} ---- a/src/pulsecore/dynarray.h 2016-04-13 16:25:43.225016112 +0200 -+++ b/src/pulsecore/dynarray.h 2016-04-13 16:27:47.414015904 +0200 -@@ -52,5 +52,5 @@ - void *pa_dynarray_steal_last(pa_dynarray *array); - - unsigned pa_dynarray_size(pa_dynarray *array); -- -+void * const *pa_dynarray_get_raw_array(pa_dynarray *array); - #endif diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0022-device-port-Add-pa_device_port.active.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0022-device-port-Add-pa_device_port.active.patch deleted file mode 100644 index 4a8143f61..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0022-device-port-Add-pa_device_port.active.patch +++ /dev/null @@ -1,198 +0,0 @@ ---- a/src/pulsecore/core.h 2016-04-13 16:38:56.392014788 +0200 -+++ b/src/pulsecore/core.h 2016-04-13 16:41:50.331014498 +0200 -@@ -125,6 +125,7 @@ - PA_CORE_HOOK_CARD_PROFILE_ADDED, - PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED, - PA_CORE_HOOK_PORT_AVAILABLE_CHANGED, -+ PA_CORE_HOOK_PORT_ACTIVE_CHANGED, - PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED, - PA_CORE_HOOK_MAX - } pa_core_hook_t; ---- a/src/pulsecore/device-port.c 2016-04-13 16:39:37.411014719 +0200 -+++ b/src/pulsecore/device-port.c 2016-04-13 17:06:47.393011999 +0200 -@@ -175,6 +175,21 @@ - pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p); - } - -+void pa_device_port_active_changed(pa_device_port *port, bool new_active) { -+ bool old_active; -+ -+ pa_assert(port); -+ -+ old_active = port->active; -+ -+ if (new_active == old_active) -+ return; -+ -+ port->active = new_active; -+ pa_log_debug("Port %s %s.", port->name, new_active ? "activated" : "deactivated"); -+ pa_hook_fire(&port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED], port); -+} -+ - pa_device_port *pa_device_port_find_best(pa_hashmap *ports) - { - void *state; ---- a/src/pulsecore/device-port.h 2016-04-13 16:39:51.603014696 +0200 -+++ b/src/pulsecore/device-port.h 2016-04-13 17:07:12.649011957 +0200 -@@ -46,6 +46,7 @@ - - unsigned priority; - pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */ -+ bool active; - - pa_proplist *proplist; - pa_hashmap *profiles; /* Does not own the profiles */ ---- a/src/pulsecore/sink.c 2016-04-13 16:40:11.131014663 +0200 -+++ b/src/pulsecore/sink.c 2016-04-13 17:14:30.963011225 +0200 -@@ -658,6 +658,9 @@ - else - pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); - -+ if (s->active_port) -+ pa_device_port_active_changed(s->active_port, true); -+ - pa_source_put(s->monitor_source); - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); -@@ -685,6 +688,9 @@ - if (linked) - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); - -+ if (s->active_port) -+ pa_device_port_active_changed(s->active_port, false); -+ - if (s->state != PA_SINK_UNLINKED) - pa_namereg_unregister(s->core, s->name); - pa_idxset_remove_by_data(s->core->sinks, s, NULL); -@@ -3297,6 +3303,7 @@ - /* Called from main context */ - int pa_sink_set_port(pa_sink *s, const char *name, bool save) { - pa_device_port *port; -+ pa_device_port *old_port; - int ret; - - pa_sink_assert_ref(s); -@@ -3313,11 +3320,15 @@ - if (!(port = pa_hashmap_get(s->ports, name))) - return -PA_ERR_NOENTITY; - -- if (s->active_port == port) { -+ old_port = s->active_port; -+ -+ if (port == old_port) { - s->save_port = s->save_port || save; - return 0; - } - -+ pa_device_port_active_changed(old_port, false); -+ - if (s->flags & PA_SINK_DEFERRED_VOLUME) { - struct sink_message_set_port msg = { .port = port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); -@@ -3326,17 +3337,26 @@ - else - ret = s->set_port(s, port); - -- if (ret < 0) -- return -PA_ERR_NOENTITY; -+ if (ret < 0) { -+ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name); -+ -+ /* We don't know the real state of the device, but let's assume that -+ * the old port is still active, because s->active_port is left to -+ * point to the old port anyway. */ -+ pa_device_port_active_changed(old_port, true); -+ -+ return ret; -+ } - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - -- pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name); -+ pa_log_info("Changed port of sink %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name); - - s->active_port = port; - s->save_port = save; - - pa_sink_set_latency_offset(s, s->active_port->latency_offset); -+ pa_device_port_active_changed(port, true); - - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s); - ---- a/src/pulsecore/source.c 2016-04-13 16:40:25.290014640 +0200 -+++ b/src/pulsecore/source.c 2016-04-13 17:21:26.051010533 +0200 -@@ -603,6 +603,9 @@ - else - pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); - -+ if (s->active_port) -+ pa_device_port_active_changed(s->active_port, true); -+ - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); - } -@@ -623,6 +626,9 @@ - if (linked) - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); - -+ if (s->active_port) -+ pa_device_port_active_changed(s->active_port, false); -+ - if (s->state != PA_SOURCE_UNLINKED) - pa_namereg_unregister(s->core, s->name); - pa_idxset_remove_by_data(s->core->sources, s, NULL); -@@ -2576,6 +2582,7 @@ - /* Called from main context */ - int pa_source_set_port(pa_source *s, const char *name, bool save) { - pa_device_port *port; -+ pa_device_port *old_port; - int ret; - - pa_source_assert_ref(s); -@@ -2592,11 +2599,15 @@ - if (!(port = pa_hashmap_get(s->ports, name))) - return -PA_ERR_NOENTITY; - -- if (s->active_port == port) { -+ old_port = s->active_port; -+ -+ if (port == old_port) { - s->save_port = s->save_port || save; - return 0; - } - -+ pa_device_port_active_changed(old_port, false); -+ - if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { - struct source_message_set_port msg = { .port = port, .ret = 0 }; - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); -@@ -2605,16 +2616,26 @@ - else - ret = s->set_port(s, port); - -- if (ret < 0) -- return -PA_ERR_NOENTITY; -+ if (ret < 0) { -+ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name); -+ -+ /* We don't know the real state of the device, but let's assume that -+ * the old port is still active, because s->active_port is left to -+ * point to the old port anyway. */ -+ pa_device_port_active_changed(old_port, true); -+ -+ return ret; -+ } - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - -- pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name); -+ pa_log_info("Changed port of source %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name); - - s->active_port = port; - s->save_port = save; - -+ pa_device_port_active_changed(port, true); -+ - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s); - - return 0; 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 diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0031-Add-module-main-volume-policy.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0031-Add-module-main-volume-policy.patch deleted file mode 100644 index 216ed21e9..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0031-Add-module-main-volume-policy.patch +++ /dev/null @@ -1,1418 +0,0 @@ -From cfb39f18569679f59c9b6283c47e8d90ddd9763d Mon Sep 17 00:00:00 2001 -From: Tanu Kaskinen -Date: Wed, 21 May 2014 14:13:41 +0300 -Subject: [PATCH] Add module-main-volume-policy - -Change-Id: I787141b43cafb652aa752c64ae28b6b7aa052d8e -Signed-off-by: Jaska Uimonen ---- - Makefile.am | 3 + - src/Makefile.am | 15 + - src/daemon/default.pa.in | 4 + - .../main-volume-policy/main-volume-context.c | 325 ++++++++++++ - .../main-volume-policy/main-volume-context.h | 75 +++ - .../main-volume-policy/main-volume-policy.c | 213 ++++++++ - .../main-volume-policy.conf.example | 20 + - .../main-volume-policy/main-volume-policy.h | 72 +++ - .../main-volume-policy/module-main-volume-policy.c | 556 +++++++++++++++++++++ - 9 files changed, 1283 insertions(+) - create mode 100644 src/modules/main-volume-policy/main-volume-context.c - create mode 100644 src/modules/main-volume-policy/main-volume-context.h - create mode 100644 src/modules/main-volume-policy/main-volume-policy.c - create mode 100644 src/modules/main-volume-policy/main-volume-policy.conf.example - create mode 100644 src/modules/main-volume-policy/main-volume-policy.h - create mode 100644 src/modules/main-volume-policy/module-main-volume-policy.c - -diff --git a/Makefile.am b/Makefile.am -index cf4a648..646b7fc 100644 ---- a/Makefile.am 2016-04-13 15:14:28.942023245 +0200 -+++ b/Makefile.am 2016-04-13 15:16:32.691023039 +0200 -@@ -60,6 +60,9 @@ - moduledevvolumeapi_DATA = src/modules/volume-api/*.h - moduledevvolumeapidir = $(includedir)/pulsemodule/modules/volume-api - -+moduledevmainvolumepolicy_DATA = $(top_srcdir)/src/modules/main-volume-policy/*.h -+moduledevmainvolumepolicydir = $(includedir)/pulsemodule/modules/main-volume-policy -+ - if HAVE_GLIB20 - pkgconfig_DATA += \ - libpulse-mainloop-glib.pc - libpulse-mainloop-glib.pc -diff --git a/src/Makefile.am b/src/Makefile.am -index a6bb319..8fa60ec 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -1050,7 +1050,8 @@ - libprotocol-simple.la \ - libprotocol-http.la \ - libprotocol-native.la \ -- libvolume-api.la -+ libvolume-api.la \ -+ libmain-volume-policy.la - - if HAVE_WEBRTC - modlibexec_LTLIBRARIES += libwebrtc-util.la -@@ -1051,6 +1052,12 @@ libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h - libcli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version - libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la - -+libmain_volume_policy_la_SOURCES = \ -+ modules/main-volume-policy/main-volume-context.c modules/main-volume-policy/main-volume-context.h \ -+ modules/main-volume-policy/main-volume-policy.c modules/main-volume-policy/main-volume-policy.h -+libmain_volume_policy_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -+libmain_volume_policy_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libvolume-api.la -+ - libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h - libprotocol_cli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version - libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libcli.la -@@ -1136,6 +1136,7 @@ endif - modlibexec_LTLIBRARIES += \ - module-cli.la \ - module-cli-protocol-tcp.la \ -+ module-main-volume-policy.la \ - module-simple-protocol-tcp.la \ - module-null-sink.la \ - module-null-source.la \ -@@ -1426,6 +1434,7 @@ SYMDEF_FILES = \ - module-cli-symdef.h \ - module-cli-protocol-tcp-symdef.h \ - module-cli-protocol-unix-symdef.h \ -+ module-main-volume-policy-symdef.h \ - module-pipe-sink-symdef.h \ - module-pipe-source-symdef.h \ - module-simple-protocol-tcp-symdef.h \ -@@ -1575,6 +1584,12 @@ module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_ - module_cli_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS) - module_cli_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-cli.la - -+# Main volume and mute policy -+ -+module_main_volume_policy_la_SOURCES = modules/main-volume-policy/module-main-volume-policy.c -+module_main_volume_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_main_volume_policy_la_LIBADD = $(MODULE_LIBADD) libmain-volume-policy.la libvolume-api.la -+ - # HTTP protocol - - module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c -diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in -index 7cf52a4..f70804c 100755 ---- a/src/daemon/default.pa.in -+++ b/src/daemon/default.pa.in -@@ -188,6 +188,10 @@ - #.endif - ])dnl - -+.ifexists module-main-volume-policy -+load-module module-main-volume-policy -+.endif -+ - ### Make some devices default - #set-default-sink output - #set-default-source input -diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c -new file mode 100644 -index 0000000..7ac35c6 ---- /dev/null -+++ b/src/modules/main-volume-policy/main-volume-context.c -@@ -0,0 +1,325 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2014 Intel Corporation -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include "main-volume-context.h" -+ -+#include -+#include -+ -+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description, -+ pa_main_volume_context **context) { -+ pa_main_volume_context *context_local; -+ int r; -+ -+ pa_assert(policy); -+ pa_assert(name); -+ pa_assert(description); -+ pa_assert(context); -+ -+ context_local = pa_xnew0(struct pa_main_volume_context, 1); -+ context_local->main_volume_policy = policy; -+ context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy); -+ -+ r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name); -+ if (r < 0) -+ goto fail; -+ -+ context_local->description = pa_xstrdup(description); -+ -+ *context = context_local; -+ -+ return 0; -+ -+fail: -+ pa_main_volume_context_free(context_local); -+ -+ return r; -+} -+ -+void pa_main_volume_context_put(pa_main_volume_context *context) { -+ pa_assert(context); -+ -+ pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context); -+ -+ context->linked = true; -+ -+ pa_log_debug("Created main volume context #%u.", context->index); -+ pa_log_debug(" Name: %s", context->name); -+ pa_log_debug(" Description: %s", context->description); -+ pa_log_debug(" Main output volume control: %s", -+ context->main_output_volume_control ? context->main_output_volume_control->name : "(unset)"); -+ pa_log_debug(" Main input volume control: %s", -+ context->main_input_volume_control ? context->main_input_volume_control->name : "(unset)"); -+ pa_log_debug(" Main output mute control: %s", -+ context->main_output_mute_control ? context->main_output_mute_control->name : "(unset)"); -+ pa_log_debug(" Main input mute control: %s", -+ context->main_input_mute_control ? context->main_input_mute_control->name : "(unset)"); -+ -+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT], context); -+} -+ -+void pa_main_volume_context_unlink(pa_main_volume_context *context) { -+ pa_assert(context); -+ -+ if (context->unlinked) { -+ pa_log_debug("Unlinking main volume context %s (already unlinked, this is a no-op).", context->name); -+ return; -+ } -+ -+ context->unlinked = true; -+ -+ pa_log_debug("Unlinking main volume context %s.", context->name); -+ -+ if (context->linked) -+ pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context); -+ -+ if (context->main_input_mute_control_binding) { -+ pa_binding_free(context->main_input_mute_control_binding); -+ context->main_input_mute_control_binding = NULL; -+ } -+ -+ if (context->main_output_mute_control_binding) { -+ pa_binding_free(context->main_output_mute_control_binding); -+ context->main_output_mute_control_binding = NULL; -+ } -+ -+ if (context->main_input_volume_control_binding) { -+ pa_binding_free(context->main_input_volume_control_binding); -+ context->main_input_volume_control_binding = NULL; -+ } -+ -+ if (context->main_output_volume_control_binding) { -+ pa_binding_free(context->main_output_volume_control_binding); -+ context->main_output_volume_control_binding = NULL; -+ } -+ -+ context->main_input_mute_control = NULL; -+ context->main_output_mute_control = NULL; -+ context->main_input_volume_control = NULL; -+ context->main_output_volume_control = NULL; -+ -+ pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context); -+} -+ -+void pa_main_volume_context_free(pa_main_volume_context *context) { -+ pa_assert(context); -+ -+ if (!context->unlinked) -+ pa_main_volume_context_unlink(context); -+ -+ pa_xfree(context->description); -+ -+ if (context->name) -+ pa_main_volume_policy_unregister_name(context->main_volume_policy, context->name); -+ -+ pa_xfree(context); -+} -+ -+const char *pa_main_volume_context_get_name(pa_main_volume_context *context) { -+ pa_assert(context); -+ -+ return context->name; -+} -+ -+static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) { -+ pa_volume_control *old_control; -+ -+ pa_assert(context); -+ -+ old_control = context->main_output_volume_control; -+ -+ if (control == old_control) -+ return; -+ -+ context->main_output_volume_control = control; -+ -+ if (!context->linked || context->unlinked) -+ return; -+ -+ pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&context->main_volume_policy->hooks -+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], -+ context); -+} -+ -+void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info) { -+ pa_binding_owner_info owner_info = { -+ .userdata = context, -+ .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal, -+ }; -+ -+ pa_assert(context); -+ pa_assert(target_info); -+ -+ if (context->main_output_volume_control_binding) -+ pa_binding_free(context->main_output_volume_control_binding); -+ -+ context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info, -+ target_info); -+} -+ -+static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) { -+ pa_volume_control *old_control; -+ -+ pa_assert(context); -+ -+ old_control = context->main_input_volume_control; -+ -+ if (control == old_control) -+ return; -+ -+ context->main_input_volume_control = control; -+ -+ if (!context->linked || context->unlinked) -+ return; -+ -+ pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&context->main_volume_policy->hooks -+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED], -+ context); -+} -+ -+void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info) { -+ pa_binding_owner_info owner_info = { -+ .userdata = context, -+ .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal, -+ }; -+ -+ pa_assert(context); -+ pa_assert(target_info); -+ -+ if (context->main_input_volume_control_binding) -+ pa_binding_free(context->main_input_volume_control_binding); -+ -+ context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info, -+ target_info); -+} -+ -+static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) { -+ pa_mute_control *old_control; -+ -+ pa_assert(context); -+ -+ old_control = context->main_output_mute_control; -+ -+ if (control == old_control) -+ return; -+ -+ context->main_output_mute_control = control; -+ -+ if (!context->linked || context->unlinked) -+ return; -+ -+ pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&context->main_volume_policy->hooks -+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], -+ context); -+} -+ -+void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info) { -+ pa_binding_owner_info owner_info = { -+ .userdata = context, -+ .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal, -+ }; -+ -+ pa_assert(context); -+ pa_assert(target_info); -+ -+ if (context->main_output_mute_control_binding) -+ pa_binding_free(context->main_output_mute_control_binding); -+ -+ context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info, -+ target_info); -+} -+ -+static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) { -+ pa_mute_control *old_control; -+ -+ pa_assert(context); -+ -+ old_control = context->main_input_mute_control; -+ -+ if (control == old_control) -+ return; -+ -+ context->main_input_mute_control = control; -+ -+ if (!context->linked || context->unlinked) -+ return; -+ -+ pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name, -+ old_control ? old_control->name : "(unset)", control ? control->name : "(unset)"); -+ -+ pa_hook_fire(&context->main_volume_policy->hooks -+ [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED], -+ context); -+} -+ -+void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info) { -+ pa_binding_owner_info owner_info = { -+ .userdata = context, -+ .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal, -+ }; -+ -+ pa_assert(context); -+ pa_assert(target_info); -+ -+ if (context->main_input_mute_control_binding) -+ pa_binding_free(context->main_input_mute_control_binding); -+ -+ context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info, -+ target_info); -+} -+ -+pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) { -+ pa_binding_target_type *type; -+ -+ pa_assert(policy); -+ -+ type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts, -+ &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT], -+ &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], -+ (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name); -+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL, -+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control)); -+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL, -+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control)); -+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL, -+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control)); -+ pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL, -+ PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control)); -+ -+ return type; -+} -diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h -new file mode 100644 -index 0000000..4a0a6f7 ---- /dev/null -+++ b/src/modules/main-volume-policy/main-volume-context.h -@@ -0,0 +1,75 @@ -+#ifndef foomainvolumecontexthfoo -+#define foomainvolumecontexthfoo -+ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2014 Intel Corporation -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#include -+ -+#include -+ -+typedef struct pa_main_volume_context pa_main_volume_context; -+ -+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext" -+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control" -+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control" -+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control" -+#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control" -+ -+struct pa_main_volume_context { -+ pa_main_volume_policy *main_volume_policy; -+ uint32_t index; -+ const char *name; -+ char *description; -+ pa_volume_control *main_output_volume_control; -+ pa_volume_control *main_input_volume_control; -+ pa_mute_control *main_output_mute_control; -+ pa_mute_control *main_input_mute_control; -+ -+ pa_binding *main_output_volume_control_binding; -+ pa_binding *main_input_volume_control_binding; -+ pa_binding *main_output_mute_control_binding; -+ pa_binding *main_input_mute_control_binding; -+ -+ bool linked; -+ bool unlinked; -+}; -+ -+int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description, -+ pa_main_volume_context **context); -+void pa_main_volume_context_put(pa_main_volume_context *context); -+void pa_main_volume_context_unlink(pa_main_volume_context *context); -+void pa_main_volume_context_free(pa_main_volume_context *context); -+ -+const char *pa_main_volume_context_get_name(pa_main_volume_context *context); -+ -+void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info); -+void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info); -+void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context, -+ pa_binding_target_info *target_info); -+void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info); -+ -+/* Called from main-volume-policy.c only. */ -+pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy); -+ -+#endif -diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c -new file mode 100644 -index 0000000..b0b4ede ---- /dev/null -+++ b/src/modules/main-volume-policy/main-volume-policy.c -@@ -0,0 +1,213 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2014 Intel Corporation -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include "main-volume-policy.h" -+ -+#include -+ -+#include -+#include -+ -+static pa_main_volume_policy *main_volume_policy_new(pa_core *core); -+static void main_volume_policy_free(pa_main_volume_policy *policy); -+ -+pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core) { -+ pa_main_volume_policy *policy; -+ -+ pa_assert(core); -+ -+ policy = pa_shared_get(core, "main-volume-policy"); -+ -+ if (policy) -+ pa_main_volume_policy_ref(policy); -+ else { -+ policy = main_volume_policy_new(core); -+ pa_assert_se(pa_shared_set(core, "main-volume-policy", policy) >= 0); -+ } -+ -+ return policy; -+} -+ -+pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy) { -+ pa_assert(policy); -+ -+ policy->refcnt++; -+ -+ return policy; -+} -+ -+void pa_main_volume_policy_unref(pa_main_volume_policy *policy) { -+ pa_assert(policy); -+ pa_assert(policy->refcnt > 0); -+ -+ policy->refcnt--; -+ -+ if (policy->refcnt == 0) { -+ pa_assert_se(pa_shared_remove(policy->core, "main-volume-policy") >= 0); -+ main_volume_policy_free(policy); -+ } -+} -+ -+static pa_main_volume_policy *main_volume_policy_new(pa_core *core) { -+ pa_main_volume_policy *policy; -+ unsigned i; -+ -+ pa_assert(core); -+ -+ policy = pa_xnew0(pa_main_volume_policy, 1); -+ policy->core = core; -+ policy->refcnt = 1; -+ policy->volume_api = pa_volume_api_get(core); -+ policy->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree); -+ policy->main_volume_contexts = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -+ -+ for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++) -+ pa_hook_init(&policy->hooks[i], policy); -+ -+ policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy); -+ pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type); -+ -+ pa_log_debug("Created a pa_main_volume_policy object."); -+ -+ return policy; -+} -+ -+static void main_volume_policy_free(pa_main_volume_policy *policy) { -+ unsigned i; -+ -+ pa_assert(policy); -+ pa_assert(policy->refcnt == 0); -+ -+ pa_log_debug("Freeing the pa_main_volume_policy object."); -+ -+ if (policy->main_volume_context_binding_target_type) { -+ pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type); -+ pa_binding_target_type_free(policy->main_volume_context_binding_target_type); -+ } -+ -+ for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++) -+ pa_hook_done(&policy->hooks[i]); -+ -+ if (policy->main_volume_contexts) { -+ pa_assert(pa_hashmap_isempty(policy->main_volume_contexts)); -+ pa_hashmap_free(policy->main_volume_contexts); -+ } -+ -+ if (policy->names) { -+ pa_assert(pa_hashmap_isempty(policy->names)); -+ pa_hashmap_free(policy->names); -+ } -+ -+ if (policy->volume_api) -+ pa_volume_api_unref(policy->volume_api); -+ -+ pa_xfree(policy); -+} -+ -+int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name, -+ bool fail_if_already_registered, const char **registered_name) { -+ char *n; -+ -+ pa_assert(policy); -+ pa_assert(requested_name); -+ pa_assert(registered_name); -+ -+ n = pa_xstrdup(requested_name); -+ -+ if (pa_hashmap_put(policy->names, n, n) < 0) { -+ unsigned i = 1; -+ -+ pa_xfree(n); -+ -+ if (fail_if_already_registered) { -+ pa_log("Name %s already registered.", requested_name); -+ return -PA_ERR_EXIST; -+ } -+ -+ do { -+ i++; -+ n = pa_sprintf_malloc("%s.%u", requested_name, i); -+ } while (pa_hashmap_put(policy->names, n, n) < 0); -+ } -+ -+ *registered_name = n; -+ -+ return 0; -+} -+ -+void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name) { -+ pa_assert(policy); -+ pa_assert(name); -+ -+ pa_assert_se(pa_hashmap_remove_and_free(policy->names, name) >= 0); -+} -+ -+uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy) { -+ uint32_t idx; -+ -+ pa_assert(policy); -+ -+ idx = policy->next_main_volume_context_index++; -+ -+ return idx; -+} -+ -+void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) { -+ pa_assert(policy); -+ pa_assert(context); -+ -+ pa_assert_se(pa_hashmap_put(policy->main_volume_contexts, (void *) context->name, context) >= 0); -+} -+ -+int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) { -+ pa_assert(policy); -+ pa_assert(context); -+ -+ if (!pa_hashmap_remove(policy->main_volume_contexts, context->name)) -+ return -1; -+ -+ if (context == policy->active_main_volume_context) -+ pa_main_volume_policy_set_active_main_volume_context(policy, NULL); -+ -+ return 0; -+} -+ -+void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) { -+ pa_main_volume_context *old_context; -+ -+ pa_assert(policy); -+ -+ old_context = policy->active_main_volume_context; -+ -+ if (context == old_context) -+ return; -+ -+ policy->active_main_volume_context = context; -+ -+ pa_log_debug("The active main volume context changed from %s to %s.", old_context ? old_context->name : "(unset)", -+ context ? context->name : "(unset)"); -+ -+ pa_hook_fire(&policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED], NULL); -+} -diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example -new file mode 100644 -index 0000000..a4a35d3 ---- /dev/null -+++ b/src/modules/main-volume-policy/main-volume-policy.conf.example -@@ -0,0 +1,20 @@ -+[General] -+output-volume-model = by-active-main-volume-context -+input-volume-model = by-active-main-volume-context -+output-mute-model = none -+input-mute-model = none -+main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context -+ -+[MainVolumeContext x-example-call-main-volume-context] -+description = Call main volume context -+main-output-volume-control = bind:AudioGroup:x-example-call-downlink-audio-group -+main-input-volume-control = bind:AudioGroup:x-example-call-uplink-audio-group -+main-output-mute-control = none -+main-input-mute-control = none -+ -+[MainVolumeContext x-example-default-main-volume-context] -+description = Default main volume context -+main-output-volume-control = bind:AudioGroup:x-example-default-output-audio-group -+main-input-volume-control = bind:AudioGroup:x-example-default-input-audio-group -+main-output-mute-control = none -+main-input-mute-control = none -diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h -new file mode 100644 -index 0000000..5cd669e ---- /dev/null -+++ b/src/modules/main-volume-policy/main-volume-policy.h -@@ -0,0 +1,72 @@ -+#ifndef foomainvolumepolicyhfoo -+#define foomainvolumepolicyhfoo -+ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2014 Intel Corporation -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#include -+#include -+ -+#include -+ -+typedef struct pa_main_volume_policy pa_main_volume_policy; -+ -+/* Avoid circular dependencies... */ -+typedef struct pa_main_volume_context pa_main_volume_context; -+ -+enum { -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED, -+ PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED, -+ PA_MAIN_VOLUME_POLICY_HOOK_MAX, -+}; -+ -+struct pa_main_volume_policy { -+ pa_core *core; -+ unsigned refcnt; -+ pa_volume_api *volume_api; -+ pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */ -+ pa_hashmap *main_volume_contexts; /* name -> pa_main_volume_context */ -+ pa_main_volume_context *active_main_volume_context; -+ -+ uint32_t next_main_volume_context_index; -+ pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX]; -+ pa_binding_target_type *main_volume_context_binding_target_type; -+}; -+ -+pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core); -+pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy); -+void pa_main_volume_policy_unref(pa_main_volume_policy *policy); -+ -+int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name, -+ bool fail_if_already_registered, const char **registered_name); -+void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name); -+ -+uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy); -+void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context); -+int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context); -+void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context); -+ -+#endif -diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c -new file mode 100644 -index 0000000..a14699d ---- /dev/null -+++ b/src/modules/main-volume-policy/module-main-volume-policy.c -@@ -0,0 +1,556 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2014 Intel Corporation -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include "module-main-volume-policy-symdef.h" -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+PA_MODULE_AUTHOR("Tanu Kaskinen"); -+PA_MODULE_DESCRIPTION(_("Main volume and mute policy")); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+PA_MODULE_LOAD_ONCE(true); -+ -+enum control_type { -+ CONTROL_TYPE_VOLUME, -+ CONTROL_TYPE_MUTE, -+}; -+ -+enum model { -+ MODEL_NONE, -+ MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT, -+}; -+ -+struct userdata { -+ pa_main_volume_policy *main_volume_policy; -+ enum model output_volume_model; -+ enum model input_volume_model; -+ enum model output_mute_model; -+ enum model input_mute_model; -+ pa_hashmap *contexts; /* name -> struct context */ -+ -+ pa_hook_slot *active_main_volume_context_changed_slot; -+ -+ /* The following fields are only used during initialization. */ -+ pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */ -+ pa_hashmap *unused_contexts; /* name -> struct context */ -+}; -+ -+struct context { -+ struct userdata *userdata; -+ char *name; -+ char *description; -+ pa_binding_target_info *main_output_volume_control_target_info; -+ pa_binding_target_info *main_input_volume_control_target_info; -+ pa_binding_target_info *main_output_mute_control_target_info; -+ pa_binding_target_info *main_input_mute_control_target_info; -+ pa_main_volume_context *main_volume_context; -+ -+ bool unlinked; -+}; -+ -+static void context_unlink(struct context *context); -+ -+static const char *model_to_string(enum model model) { -+ switch (model) { -+ case MODEL_NONE: -+ return "none"; -+ -+ case MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT: -+ return "by-active-main-volume-context"; -+ } -+ -+ pa_assert_not_reached(); -+} -+ -+static int model_from_string(const char *str, enum model *model) { -+ pa_assert(str); -+ pa_assert(model); -+ -+ if (pa_streq(str, "none")) -+ *model = MODEL_NONE; -+ else if (pa_streq(str, "by-active-main-volume-context")) -+ *model = MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT; -+ else -+ return -PA_ERR_INVALID; -+ -+ return 0; -+} -+ -+static struct context *context_new(struct userdata *u, const char *name) { -+ struct context *context; -+ -+ pa_assert(u); -+ pa_assert(name); -+ -+ context = pa_xnew0(struct context, 1); -+ context->userdata = u; -+ context->name = pa_xstrdup(name); -+ context->description = pa_xstrdup(name); -+ -+ return context; -+} -+ -+static int context_put(struct context *context) { -+ int r; -+ -+ pa_assert(context); -+ -+ r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description, -+ &context->main_volume_context); -+ if (r < 0) -+ goto fail; -+ -+ if (context->main_output_volume_control_target_info) -+ pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context, -+ context->main_output_volume_control_target_info); -+ -+ if (context->main_input_volume_control_target_info) -+ pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context, -+ context->main_input_volume_control_target_info); -+ -+ if (context->main_output_mute_control_target_info) -+ pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context, -+ context->main_output_mute_control_target_info); -+ -+ if (context->main_input_mute_control_target_info) -+ pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context, -+ context->main_input_mute_control_target_info); -+ -+ pa_main_volume_context_put(context->main_volume_context); -+ -+ return 0; -+ -+fail: -+ context_unlink(context); -+ -+ return r; -+} -+ -+static void context_unlink(struct context *context) { -+ pa_assert(context); -+ -+ if (context->unlinked) -+ return; -+ -+ context->unlinked = true; -+ -+ if (context->main_volume_context) { -+ pa_main_volume_context_free(context->main_volume_context); -+ context->main_volume_context = NULL; -+ } -+} -+ -+static void context_free(struct context *context) { -+ pa_assert(context); -+ -+ if (!context->unlinked) -+ context_unlink(context); -+ -+ if (context->main_input_mute_control_target_info) -+ pa_binding_target_info_free(context->main_input_mute_control_target_info); -+ -+ if (context->main_output_mute_control_target_info) -+ pa_binding_target_info_free(context->main_output_mute_control_target_info); -+ -+ if (context->main_input_volume_control_target_info) -+ pa_binding_target_info_free(context->main_input_volume_control_target_info); -+ -+ if (context->main_output_volume_control_target_info) -+ pa_binding_target_info_free(context->main_output_volume_control_target_info); -+ -+ pa_xfree(context->description); -+ pa_xfree(context->name); -+ pa_xfree(context); -+} -+ -+static void context_set_description(struct context *context, const char *description) { -+ pa_assert(context); -+ pa_assert(description); -+ -+ pa_xfree(context->description); -+ context->description = pa_xstrdup(description); -+} -+ -+static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction, -+ pa_binding_target_info *info) { -+ pa_assert(context); -+ -+ switch (type) { -+ case CONTROL_TYPE_VOLUME: -+ if (direction == PA_DIRECTION_OUTPUT) { -+ if (context->main_output_volume_control_target_info) -+ pa_binding_target_info_free(context->main_output_volume_control_target_info); -+ -+ if (info) -+ context->main_output_volume_control_target_info = pa_binding_target_info_copy(info); -+ else -+ context->main_output_volume_control_target_info = NULL; -+ } else { -+ if (context->main_input_volume_control_target_info) -+ pa_binding_target_info_free(context->main_input_volume_control_target_info); -+ -+ if (info) -+ context->main_input_volume_control_target_info = pa_binding_target_info_copy(info); -+ else -+ context->main_input_volume_control_target_info = NULL; -+ } -+ break; -+ -+ case CONTROL_TYPE_MUTE: -+ if (direction == PA_DIRECTION_OUTPUT) { -+ if (context->main_output_mute_control_target_info) -+ pa_binding_target_info_free(context->main_output_mute_control_target_info); -+ -+ if (info) -+ context->main_output_mute_control_target_info = pa_binding_target_info_copy(info); -+ else -+ context->main_output_mute_control_target_info = NULL; -+ } else { -+ if (context->main_input_mute_control_target_info) -+ pa_binding_target_info_free(context->main_input_mute_control_target_info); -+ -+ if (info) -+ context->main_input_mute_control_target_info = pa_binding_target_info_copy(info); -+ else -+ context->main_input_mute_control_target_info = NULL; -+ } -+ break; -+ } -+} -+ -+static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) { -+ struct userdata *u = userdata; -+ pa_main_volume_context *context; -+ pa_volume_api *api; -+ pa_binding_target_info *info = NULL; -+ -+ pa_assert(u); -+ -+ context = u->main_volume_policy->active_main_volume_context; -+ api = u->main_volume_policy->volume_api; -+ -+ if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) { -+ if (context) { -+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name, -+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL); -+ pa_volume_api_bind_main_output_volume_control(api, info); -+ } else -+ pa_volume_api_set_main_output_volume_control(api, NULL); -+ } -+ -+ if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) { -+ if (context) { -+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name, -+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL); -+ pa_volume_api_bind_main_input_volume_control(api, info); -+ } else -+ pa_volume_api_set_main_input_volume_control(api, NULL); -+ } -+ -+ if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) { -+ if (context) { -+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name, -+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL); -+ pa_volume_api_bind_main_output_mute_control(api, info); -+ } else -+ pa_volume_api_set_main_output_mute_control(api, NULL); -+ } -+ -+ if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) { -+ if (context) { -+ info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name, -+ PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL); -+ pa_volume_api_bind_main_input_mute_control(api, info); -+ } else -+ pa_volume_api_set_main_input_mute_control(api, NULL); -+ } -+ -+ if (info) -+ pa_binding_target_info_free(info); -+ -+ return PA_HOOK_OK; -+} -+ -+static int parse_model(pa_config_parser_state *state) { -+ int r; -+ -+ pa_assert(state); -+ -+ r = model_from_string(state->rvalue, state->data); -+ if (r < 0) -+ pa_log("[%s:%u] Failed to parse model: %s", state->filename, state->lineno, state->rvalue); -+ -+ return r; -+} -+ -+static int parse_main_volume_contexts(pa_config_parser_state *state) { -+ struct userdata *u; -+ char *name; -+ const char *split_state = NULL; -+ -+ pa_assert(state); -+ -+ u = state->userdata; -+ -+ while ((name = pa_split_spaces(state->rvalue, &split_state))) -+ pa_hashmap_put(u->context_names, name, name); -+ -+ return 0; -+} -+ -+static struct context *get_context(struct userdata *u, const char *section) { -+ const char *name; -+ struct context *context; -+ -+ pa_assert(u); -+ -+ if (!section) -+ return NULL; -+ -+ if (!pa_startswith(section, "MainVolumeContext ")) -+ return NULL; -+ -+ name = section + 18; -+ -+ context = pa_hashmap_get(u->unused_contexts, name); -+ if (!context) { -+ context = context_new(u, name); -+ pa_hashmap_put(u->unused_contexts, context->name, context); -+ } -+ -+ return context; -+} -+ -+static int parse_description(pa_config_parser_state *state) { -+ struct userdata *u; -+ struct context *context; -+ -+ pa_assert(state); -+ -+ u = state->userdata; -+ -+ context = get_context(u, state->section); -+ if (!context) { -+ pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue, -+ pa_strnull(state->section)); -+ return -PA_ERR_INVALID; -+ } -+ -+ context_set_description(context, state->rvalue); -+ -+ return 0; -+} -+ -+static const char *get_target_field_name(enum control_type type) { -+ switch (type) { -+ case CONTROL_TYPE_VOLUME: -+ return "volume_control"; -+ -+ case CONTROL_TYPE_MUTE: -+ return "mute_control"; -+ } -+ -+ pa_assert_not_reached(); -+} -+ -+static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) { -+ struct userdata *u; -+ struct context *context; -+ -+ pa_assert(state); -+ -+ u = state->userdata; -+ -+ context = get_context(u, state->section); -+ if (!context) { -+ pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue, -+ pa_strnull(state->section)); -+ return -PA_ERR_INVALID; -+ } -+ -+ if (pa_streq(state->rvalue, "none")) -+ context_set_main_control_target_info(context, type, direction, NULL); -+ else if (pa_startswith(state->rvalue, "bind:")) { -+ int r; -+ pa_binding_target_info *info; -+ -+ r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info); -+ if (r < 0) { -+ pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue); -+ return r; -+ } -+ -+ context_set_main_control_target_info(context, type, direction, info); -+ pa_binding_target_info_free(info); -+ } else { -+ pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue); -+ return -PA_ERR_INVALID; -+ } -+ -+ return 0; -+} -+ -+static int parse_main_output_volume_control(pa_config_parser_state *state) { -+ pa_assert(state); -+ -+ return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT); -+} -+ -+static int parse_main_input_volume_control(pa_config_parser_state *state) { -+ pa_assert(state); -+ -+ return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT); -+} -+ -+static int parse_main_output_mute_control(pa_config_parser_state *state) { -+ pa_assert(state); -+ -+ return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT); -+} -+ -+static int parse_main_input_mute_control(pa_config_parser_state *state) { -+ pa_assert(state); -+ -+ return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT); -+} -+ -+static void finalize_config(struct userdata *u) { -+ const char *context_name; -+ void *state; -+ struct context *context; -+ -+ pa_assert(u); -+ -+ PA_HASHMAP_FOREACH(context_name, u->context_names, state) { -+ int r; -+ -+ context = pa_hashmap_remove(u->unused_contexts, context_name); -+ if (!context) -+ context = context_new(u, context_name); -+ -+ r = context_put(context); -+ if (r < 0) { -+ pa_log_warn("Failed to create main volume context %s.", context_name); -+ context_free(context); -+ continue; -+ } -+ -+ pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0); -+ } -+ -+ PA_HASHMAP_FOREACH(context, u->unused_contexts, state) -+ pa_log_debug("Main volume context %s is not used.", context->name); -+ -+ pa_hashmap_free(u->unused_contexts); -+ u->unused_contexts = NULL; -+ -+ pa_hashmap_free(u->context_names); -+ u->context_names = NULL; -+} -+ -+int pa__init(pa_module *module) { -+ struct userdata *u; -+ FILE *f; -+ char *fn = NULL; -+ -+ pa_assert(module); -+ -+ u = module->userdata = pa_xnew0(struct userdata, 1); -+ u->main_volume_policy = pa_main_volume_policy_get(module->core); -+ u->output_volume_model = MODEL_NONE; -+ u->input_volume_model = MODEL_NONE; -+ u->output_mute_model = MODEL_NONE; -+ u->input_mute_model = MODEL_NONE; -+ u->contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, -+ (pa_free_cb_t) context_free); -+ u->active_main_volume_context_changed_slot = -+ pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED], -+ PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u); -+ u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree); -+ u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, -+ (pa_free_cb_t) context_free); -+ -+ f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn); -+ if (f) { -+ pa_config_item config_items[] = { -+ { "output-volume-model", parse_model, &u->output_volume_model, "General" }, -+ { "input-volume-model", parse_model, &u->input_volume_model, "General" }, -+ { "output-mute-model", parse_model, &u->output_mute_model, "General" }, -+ { "input-mute-model", parse_model, &u->input_mute_model, "General" }, -+ { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" }, -+ { "description", parse_description, NULL, NULL }, -+ { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL }, -+ { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL }, -+ { "main-output-mute-control", parse_main_output_mute_control, NULL, NULL }, -+ { "main-input-mute-control", parse_main_input_mute_control, NULL, NULL }, -+ { NULL }, -+ }; -+ -+ pa_config_parse(fn, f, config_items, NULL, u); -+ pa_xfree(fn); -+ fn = NULL; -+ fclose(f); -+ f = NULL; -+ } -+ -+ finalize_config(u); -+ -+ pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model)); -+ pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model)); -+ pa_log_debug("Output mute model: %s", model_to_string(u->output_mute_model)); -+ pa_log_debug("Input mute model: %s", model_to_string(u->input_mute_model)); -+ -+ return 0; -+} -+ -+void pa__done(pa_module *module) { -+ struct userdata *u; -+ -+ pa_assert(module); -+ -+ u = module->userdata; -+ if (!u) -+ return; -+ -+ if (u->active_main_volume_context_changed_slot) -+ pa_hook_slot_free(u->active_main_volume_context_changed_slot); -+ -+ if (u->contexts) -+ pa_hashmap_free(u->contexts); -+ -+ if (u->main_volume_policy) -+ pa_main_volume_policy_unref(u->main_volume_policy); -+ -+ pa_xfree(u); -+} --- -2.1.4 - ---- a/po/POTFILES.in 2016-04-14 13:03:50.715006116 +0200 -+++ b/po/POTFILES.in 2016-04-14 13:04:23.097006062 +0200 -@@ -200,3 +200,4 @@ - src/utils/pax11publish.c - src/modules/volume-api/device-creator.c - src/pulse/ext-volume-api.c -+src/modules/main-volume-policy/module-main-volume-policy.c diff --git a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0039-main-volume-policy-adapt-to-pa6rev.patch b/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0039-main-volume-policy-adapt-to-pa6rev.patch deleted file mode 100644 index 4f2777112..000000000 --- a/meta-agl-profile-core/recipes-multimedia/pulseaudio/pulseaudio/0039-main-volume-policy-adapt-to-pa6rev.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/src/modules/main-volume-policy/module-main-volume-policy.c 2016-04-18 10:59:46.124086205 +0200 -+++ b/src/modules/main-volume-policy/module-main-volume-policy.c 2016-04-18 11:01:31.497089360 +0200 -@@ -517,7 +517,7 @@ - { NULL }, - }; - -- pa_config_parse(fn, f, config_items, NULL, u); -+ pa_config_parse(fn, f, config_items, NULL, false, u); - pa_xfree(fn); - fn = NULL; - fclose(f); -- cgit 1.2.3-korg