1 From 0223ac3e092249c792bedcab2bf5dbaec443f960 Mon Sep 17 00:00:00 2001
2 From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
3 Date: Wed, 21 May 2014 11:51:27 +0300
4 Subject: [PATCH 1/1] volume-api: Add libvolume-api.so
6 This library implements the "core" of the new volume system, which
7 will be used by several modules.
9 Change-Id: Ib25ada1392e83237a3908e6064ee0ad6dff7afaf
10 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
13 src/Makefile.am | 19 +-
15 src/modules/volume-api/audio-group.c | 448 +++++++++++++
16 src/modules/volume-api/audio-group.h | 85 +++
17 src/modules/volume-api/binding.c | 386 +++++++++++
18 src/modules/volume-api/binding.h | 128 ++++
19 src/modules/volume-api/bvolume.h | 43 ++
20 src/modules/volume-api/device-creator.c | 1108 +++++++++++++++++++++++++++++++
21 src/modules/volume-api/device-creator.h | 32 +
22 src/modules/volume-api/device.c | 293 ++++++++
23 src/modules/volume-api/device.h | 76 +++
24 src/modules/volume-api/mute-control.c | 306 +++++++++
25 src/modules/volume-api/mute-control.h | 102 +++
26 src/modules/volume-api/sstream.c | 366 ++++++++++
27 src/modules/volume-api/sstream.h | 108 +++
28 src/modules/volume-api/stream-creator.c | 691 +++++++++++++++++++
29 src/modules/volume-api/stream-creator.h | 32 +
30 src/modules/volume-api/volume-api.c | 647 ++++++++++++++++++
31 src/modules/volume-api/volume-api.h | 163 +++++
32 src/modules/volume-api/volume-control.c | 363 ++++++++++
33 src/modules/volume-api/volume-control.h | 112 ++++
34 src/pulse/ext-volume-api.c | 275 ++++++++
35 src/pulse/ext-volume-api.h | 68 ++
36 24 files changed, 5868 insertions(+), 1 deletion(-)
37 create mode 100644 src/modules/volume-api/audio-group.c
38 create mode 100644 src/modules/volume-api/audio-group.h
39 create mode 100644 src/modules/volume-api/binding.c
40 create mode 100644 src/modules/volume-api/binding.h
41 create mode 100644 src/modules/volume-api/bvolume.h
42 create mode 100644 src/modules/volume-api/device-creator.c
43 create mode 100644 src/modules/volume-api/device-creator.h
44 create mode 100644 src/modules/volume-api/device.c
45 create mode 100644 src/modules/volume-api/device.h
46 create mode 100644 src/modules/volume-api/mute-control.c
47 create mode 100644 src/modules/volume-api/mute-control.h
48 create mode 100644 src/modules/volume-api/sstream.c
49 create mode 100644 src/modules/volume-api/sstream.h
50 create mode 100644 src/modules/volume-api/stream-creator.c
51 create mode 100644 src/modules/volume-api/stream-creator.h
52 create mode 100644 src/modules/volume-api/volume-api.c
53 create mode 100644 src/modules/volume-api/volume-api.h
54 create mode 100644 src/modules/volume-api/volume-control.c
55 create mode 100644 src/modules/volume-api/volume-control.h
56 create mode 100644 src/pulse/ext-volume-api.c
57 create mode 100644 src/pulse/ext-volume-api.h
59 diff --git a/Makefile.am b/Makefile.am
60 index 9431d4a..cf4a648 100644
64 moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h
65 moduledevinternaldir = $(includedir)/pulsemodule/pulse
67 +moduledevvolumeapi_DATA = $(top_srcdir)/src/modules/volume-api/*.h
68 +moduledevvolumeapidir = $(includedir)/pulsemodule/modules/volume-api
72 libpulse-mainloop-glib.pc
73 diff --git a/src/Makefile.am b/src/Makefile.am
74 index 22b9b81..bc41dca 100644
75 --- a/src/Makefile.am 2016-04-13 15:51:34.439019531 +0200
76 +++ b/src/Makefile.am 2016-04-13 15:53:03.721019382 +0200
78 pulse/ext-device-manager.h \
79 pulse/ext-device-restore.h \
80 pulse/ext-stream-restore.h \
81 + pulse/ext-volume-api.h \
86 pulse/ext-device-manager.c pulse/ext-device-manager.h \
87 pulse/ext-device-restore.c pulse/ext-device-restore.h \
88 pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
89 + pulse/ext-volume-api.c pulse/ext-volume-api.h \
90 pulse/format.c pulse/format.h \
93 @@ -1018,7 +1020,8 @@ modlibexec_LTLIBRARIES = \
95 libprotocol-simple.la \
97 - libprotocol-native.la
98 + libprotocol-native.la \
102 modlibexec_LTLIBRARIES += libwebrtc-util.la
103 @@ -1065,6 +1068,20 @@ libprotocol_native_la_CFLAGS += $(DBUS_CFLAGS)
104 libprotocol_native_la_LIBADD += $(DBUS_LIBS)
107 +libvolume_api_la_SOURCES = \
108 + modules/volume-api/audio-group.c modules/volume-api/audio-group.h \
109 + modules/volume-api/binding.c modules/volume-api/binding.h \
110 + modules/volume-api/bvolume.h \
111 + modules/volume-api/device.c modules/volume-api/device.h \
112 + modules/volume-api/device-creator.c modules/volume-api/device-creator.h \
113 + modules/volume-api/mute-control.c modules/volume-api/mute-control.h \
114 + modules/volume-api/sstream.c modules/volume-api/sstream.h \
115 + modules/volume-api/stream-creator.c modules/volume-api/stream-creator.h \
116 + modules/volume-api/volume-api.c modules/volume-api/volume-api.h \
117 + modules/volume-api/volume-control.c modules/volume-api/volume-control.h
118 +libvolume_api_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
119 +libvolume_api_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
122 libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
123 libprotocol_esound_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
124 diff --git a/src/map-file b/src/map-file
125 index fbf3f22..1f64a2f 100644
129 pa_ext_stream_restore_subscribe;
130 pa_ext_stream_restore_test;
131 pa_ext_stream_restore_write;
132 +pa_ext_volume_api_balance_valid;
133 +pa_ext_volume_api_bvolume_copy_balance;
134 +pa_ext_volume_api_bvolume_get_left_right_balance;
135 +pa_ext_volume_api_bvolume_get_rear_front_balance;
136 +pa_ext_volume_api_bvolume_equal;
137 +pa_ext_volume_api_bvolume_from_cvolume;
138 +pa_ext_volume_api_bvolume_init_invalid;
139 +pa_ext_volume_api_bvolume_init_mono;
140 +pa_ext_volume_api_bvolume_remap;
141 +pa_ext_volume_api_bvolume_reset_balance;
142 +pa_ext_volume_api_bvolume_set_left_right_balance;
143 +pa_ext_volume_api_bvolume_set_rear_front_balance;
144 +pa_ext_volume_api_bvolume_snprint_balance;
145 +pa_ext_volume_api_bvolume_to_cvolume;
146 +pa_ext_volume_api_bvolume_valid;
149 pa_format_info_from_string;
150 diff --git a/src/modules/volume-api/audio-group.c b/src/modules/volume-api/audio-group.c
152 index 0000000..76bfa69
154 +++ b/src/modules/volume-api/audio-group.c
157 + This file is part of PulseAudio.
159 + Copyright 2014 Intel Corporation
161 + PulseAudio is free software; you can redistribute it and/or modify
162 + it under the terms of the GNU Lesser General Public License as published
163 + by the Free Software Foundation; either version 2.1 of the License,
164 + or (at your option) any later version.
166 + PulseAudio is distributed in the hope that it will be useful, but
167 + WITHOUT ANY WARRANTY; without even the implied warranty of
168 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
169 + General Public License for more details.
171 + You should have received a copy of the GNU Lesser General Public License
172 + along with PulseAudio; if not, write to the Free Software
173 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
177 +#ifdef HAVE_CONFIG_H
181 +#include "audio-group.h"
183 +#include <modules/volume-api/sstream.h>
185 +#include <pulsecore/core-util.h>
187 +int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group) {
188 + pa_audio_group *group_local;
193 + pa_assert(description);
196 + group_local = pa_xnew0(pa_audio_group, 1);
197 + group_local->volume_api = api;
198 + group_local->index = pa_volume_api_allocate_audio_group_index(api);
200 + r = pa_volume_api_register_name(api, name, true, &group_local->name);
204 + group_local->description = pa_xstrdup(description);
205 + group_local->proplist = pa_proplist_new();
206 + group_local->volume_streams = pa_hashmap_new(NULL, NULL);
207 + group_local->mute_streams = pa_hashmap_new(NULL, NULL);
209 + *group = group_local;
214 + pa_audio_group_free(group_local);
219 +void pa_audio_group_put(pa_audio_group *group) {
220 + const char *prop_key;
221 + void *state = NULL;
225 + pa_volume_api_add_audio_group(group->volume_api, group);
227 + group->linked = true;
229 + pa_log_debug("Created audio group #%u.", group->index);
230 + pa_log_debug(" Name: %s", group->name);
231 + pa_log_debug(" Description: %s", group->description);
232 + pa_log_debug(" Volume control: %s", group->volume_control ? group->volume_control->name : "(unset)");
233 + pa_log_debug(" Mute control: %s", group->mute_control ? group->mute_control->name : "(unset)");
234 + pa_log_debug(" Properties:");
236 + while ((prop_key = pa_proplist_iterate(group->proplist, &state)))
237 + pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(group->proplist, prop_key)));
239 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], group);
242 +void pa_audio_group_unlink(pa_audio_group *group) {
243 + pas_stream *stream;
247 + if (group->unlinked) {
248 + pa_log_debug("Unlinking audio group %s (already unlinked, this is a no-op).", group->name);
252 + group->unlinked = true;
254 + pa_log_debug("Unlinking audio group %s.", group->name);
257 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
259 + pa_volume_api_remove_audio_group(group->volume_api, group);
261 + while ((stream = pa_hashmap_first(group->mute_streams)))
262 + pas_stream_set_audio_group_for_mute(stream, NULL);
264 + while ((stream = pa_hashmap_first(group->volume_streams)))
265 + pas_stream_set_audio_group_for_volume(stream, NULL);
267 + if (group->mute_control_binding) {
268 + pa_binding_free(group->mute_control_binding);
269 + group->mute_control_binding = NULL;
272 + if (group->volume_control_binding) {
273 + pa_binding_free(group->volume_control_binding);
274 + group->volume_control_binding = NULL;
277 + pa_audio_group_set_have_own_mute_control(group, false);
278 + pa_audio_group_set_have_own_volume_control(group, false);
280 + if (group->mute_control) {
281 + pa_mute_control_remove_audio_group(group->mute_control, group);
282 + group->mute_control = NULL;
285 + if (group->volume_control) {
286 + pa_volume_control_remove_audio_group(group->volume_control, group);
287 + group->volume_control = NULL;
291 +void pa_audio_group_free(pa_audio_group *group) {
294 + if (!group->unlinked)
295 + pa_audio_group_unlink(group);
297 + if (group->mute_streams)
298 + pa_hashmap_free(group->mute_streams);
300 + if (group->volume_streams)
301 + pa_hashmap_free(group->volume_streams);
303 + if (group->proplist)
304 + pa_proplist_free(group->proplist);
306 + pa_xfree(group->description);
309 + pa_volume_api_unregister_name(group->volume_api, group->name);
314 +const char *pa_audio_group_get_name(pa_audio_group *group) {
317 + return group->name;
320 +static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume,
321 + bool set_balance) {
322 + pa_audio_group *group;
323 + pas_stream *stream;
326 + pa_assert(control);
329 + group = control->userdata;
331 + PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
332 + if (stream->own_volume_control)
333 + pa_volume_control_set_volume(stream->own_volume_control, volume, set_volume, set_balance);
339 +static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
340 + pa_audio_group *group;
341 + pas_stream *stream;
344 + pa_assert(control);
346 + group = control->userdata;
348 + PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
349 + if (stream->own_volume_control)
350 + pa_volume_control_set_volume(stream->own_volume_control, &control->volume, true, true);
354 +void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have) {
357 + if (have == group->have_own_volume_control)
361 + pa_bvolume initial_volume;
363 + if (group->volume_api->core->flat_volumes)
364 + /* Usually the initial volume should get overridden by some module
365 + * that manages audio group volume levels, but if there's no such
366 + * module, let's try to avoid too high volume in flat volume
368 + pa_bvolume_init_mono(&initial_volume, 0.3 * PA_VOLUME_NORM);
370 + pa_bvolume_init_mono(&initial_volume, PA_VOLUME_NORM);
372 + pa_assert(!group->own_volume_control);
373 + group->own_volume_control = pa_volume_control_new(group->volume_api, "audio-group-volume-control",
374 + group->description, false, false);
375 + pa_volume_control_set_owner_audio_group(group->own_volume_control, group);
376 + group->own_volume_control->set_volume = volume_control_set_volume_cb;
377 + group->own_volume_control->userdata = group;
378 + pa_volume_control_put(group->own_volume_control, &initial_volume, volume_control_set_initial_volume_cb);
380 + pa_volume_control_free(group->own_volume_control);
381 + group->own_volume_control = NULL;
384 + group->have_own_volume_control = have;
387 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
388 + pa_audio_group *group;
389 + pas_stream *stream;
392 + pa_assert(control);
394 + group = control->userdata;
396 + PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
397 + if (stream->own_mute_control)
398 + pa_mute_control_set_mute(stream->own_mute_control, mute);
404 +static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
405 + pa_audio_group *group;
406 + pas_stream *stream;
409 + pa_assert(control);
411 + group = control->userdata;
413 + PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
414 + if (stream->own_mute_control)
415 + pa_mute_control_set_mute(stream->own_mute_control, control->mute);
419 +void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) {
422 + if (have == group->have_own_mute_control)
425 + group->have_own_mute_control = have;
428 + pa_assert(!group->own_mute_control);
429 + group->own_mute_control = pa_mute_control_new(group->volume_api, "audio-group-mute-control", group->description);
430 + pa_mute_control_set_owner_audio_group(group->own_mute_control, group);
431 + group->own_mute_control->set_mute = mute_control_set_mute_cb;
432 + group->own_mute_control->userdata = group;
433 + pa_mute_control_put(group->own_mute_control, false, true, mute_control_set_initial_mute_cb);
435 + pa_mute_control_free(group->own_mute_control);
436 + group->own_mute_control = NULL;
440 +static void set_volume_control_internal(pa_audio_group *group, pa_volume_control *control) {
441 + pa_volume_control *old_control;
445 + old_control = group->volume_control;
447 + if (control == old_control)
451 + pa_volume_control_remove_audio_group(old_control, group);
453 + group->volume_control = control;
456 + pa_volume_control_add_audio_group(control, group);
458 + if (!group->linked || group->unlinked)
461 + pa_log_debug("The volume control of audio group %s changed from %s to %s.", group->name,
462 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
464 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], group);
467 +void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
470 + if (group->volume_control_binding) {
471 + pa_binding_free(group->volume_control_binding);
472 + group->volume_control_binding = NULL;
475 + set_volume_control_internal(group, control);
478 +static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *control) {
479 + pa_mute_control *old_control;
483 + old_control = group->mute_control;
485 + if (control == old_control)
489 + pa_mute_control_remove_audio_group(old_control, group);
491 + group->mute_control = control;
494 + pa_mute_control_add_audio_group(control, group);
496 + if (!group->linked || group->unlinked)
499 + pa_log_debug("The mute control of audio group %s changed from %s to %s.", group->name,
500 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
502 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], group);
505 +void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
508 + if (group->mute_control_binding) {
509 + pa_binding_free(group->mute_control_binding);
510 + group->mute_control_binding = NULL;
513 + set_mute_control_internal(group, control);
516 +void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info) {
517 + pa_binding_owner_info owner_info = {
519 + .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
523 + pa_assert(target_info);
525 + if (group->volume_control_binding)
526 + pa_binding_free(group->volume_control_binding);
528 + group->volume_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
531 +void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info) {
532 + pa_binding_owner_info owner_info = {
534 + .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
538 + pa_assert(target_info);
540 + if (group->mute_control_binding)
541 + pa_binding_free(group->mute_control_binding);
543 + group->mute_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
546 +void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream) {
550 + pa_assert_se(pa_hashmap_put(group->volume_streams, stream, stream) >= 0);
552 + if (stream->own_volume_control && group->own_volume_control)
553 + pa_volume_control_set_volume(stream->own_volume_control, &group->own_volume_control->volume, true, true);
555 + pa_log_debug("Stream %s added to audio group %s (volume).", stream->name, group->name);
558 +void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream) {
562 + pa_assert_se(pa_hashmap_remove(group->volume_streams, stream));
564 + pa_log_debug("Stream %s removed from audio group %s (volume).", stream->name, group->name);
567 +void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
571 + pa_assert_se(pa_hashmap_put(group->mute_streams, stream, stream) >= 0);
573 + if (stream->own_mute_control && group->own_mute_control)
574 + pa_mute_control_set_mute(stream->own_mute_control, group->own_mute_control->mute);
576 + pa_log_debug("Stream %s added to audio group %s (mute).", stream->name, group->name);
579 +void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream) {
583 + pa_assert_se(pa_hashmap_remove(group->mute_streams, stream));
585 + pa_log_debug("Stream %s removed from audio group %s (mute).", stream->name, group->name);
588 +pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api) {
589 + pa_binding_target_type *type;
593 + type = pa_binding_target_type_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, api->audio_groups,
594 + &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT],
595 + &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
596 + (pa_binding_target_type_get_name_cb_t) pa_audio_group_get_name);
597 + pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL,
598 + PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, volume_control));
599 + pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL,
600 + PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, mute_control));
604 diff --git a/src/modules/volume-api/audio-group.h b/src/modules/volume-api/audio-group.h
606 index 0000000..41591ba
608 +++ b/src/modules/volume-api/audio-group.h
610 +#ifndef fooaudiogrouphfoo
611 +#define fooaudiogrouphfoo
614 + This file is part of PulseAudio.
616 + Copyright 2014 Intel Corporation
618 + PulseAudio is free software; you can redistribute it and/or modify
619 + it under the terms of the GNU Lesser General Public License as published
620 + by the Free Software Foundation; either version 2.1 of the License,
621 + or (at your option) any later version.
623 + PulseAudio is distributed in the hope that it will be useful, but
624 + WITHOUT ANY WARRANTY; without even the implied warranty of
625 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
626 + General Public License for more details.
628 + You should have received a copy of the GNU Lesser General Public License
629 + along with PulseAudio; if not, write to the Free Software
630 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
634 +#include <modules/volume-api/binding.h>
635 +#include <modules/volume-api/mute-control.h>
636 +#include <modules/volume-api/volume-control.h>
638 +#include <pulse/proplist.h>
640 +#include <inttypes.h>
642 +typedef struct pa_audio_group pa_audio_group;
644 +#define PA_AUDIO_GROUP_BINDING_TARGET_TYPE "AudioGroup"
645 +#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL "volume_control"
646 +#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL "mute_control"
648 +struct pa_audio_group {
649 + pa_volume_api *volume_api;
653 + pa_proplist *proplist;
654 + pa_volume_control *volume_control;
655 + pa_mute_control *mute_control;
656 + bool have_own_volume_control;
657 + bool have_own_mute_control;
658 + pa_volume_control *own_volume_control;
659 + pa_mute_control *own_mute_control;
661 + pa_binding *volume_control_binding;
662 + pa_binding *mute_control_binding;
663 + pa_hashmap *volume_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
664 + pa_hashmap *mute_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
670 +int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group);
671 +void pa_audio_group_put(pa_audio_group *group);
672 +void pa_audio_group_unlink(pa_audio_group *group);
673 +void pa_audio_group_free(pa_audio_group *group);
675 +const char *pa_audio_group_get_name(pa_audio_group *group);
677 +/* Called by policy modules. */
678 +void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have);
679 +void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have);
680 +void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control);
681 +void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control);
682 +void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info);
683 +void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info);
685 +/* Called from sstream.c only. */
686 +void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream);
687 +void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream);
688 +void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream);
689 +void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream);
691 +/* Called from volume-api.c only. */
692 +pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api);
695 diff --git a/src/modules/volume-api/binding.c b/src/modules/volume-api/binding.c
697 index 0000000..6e73119
699 +++ b/src/modules/volume-api/binding.c
702 + This file is part of PulseAudio.
704 + Copyright 2014 Intel Corporation
706 + PulseAudio is free software; you can redistribute it and/or modify
707 + it under the terms of the GNU Lesser General Public License as published
708 + by the Free Software Foundation; either version 2.1 of the License,
709 + or (at your option) any later version.
711 + PulseAudio is distributed in the hope that it will be useful, but
712 + WITHOUT ANY WARRANTY; without even the implied warranty of
713 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
714 + General Public License for more details.
716 + You should have received a copy of the GNU Lesser General Public License
717 + along with PulseAudio; if not, write to the Free Software
718 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
722 +#ifdef HAVE_CONFIG_H
726 +#include "binding.h"
728 +#include <pulse/def.h>
729 +#include <pulse/xmalloc.h>
731 +#include <pulsecore/core-util.h>
732 +#include <pulsecore/macro.h>
734 +struct field_entry {
739 +static void set_target_type(pa_binding *binding, pa_binding_target_type *type);
740 +static void set_target_object(pa_binding *binding, void *object);
742 +pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata) {
743 + pa_binding_owner_info *info;
745 + pa_assert(set_value);
747 + info = pa_xnew0(pa_binding_owner_info, 1);
748 + info->set_value = set_value;
749 + info->userdata = userdata;
754 +pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info) {
757 + return pa_binding_owner_info_new(info->set_value, info->userdata);
760 +void pa_binding_owner_info_free(pa_binding_owner_info *info) {
766 +pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field) {
767 + pa_binding_target_info *info;
773 + info = pa_xnew0(pa_binding_target_info, 1);
774 + info->type = pa_xstrdup(type);
775 + info->name = pa_xstrdup(name);
776 + info->field = pa_xstrdup(field);
781 +int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info) {
790 + if (!pa_startswith(str, "bind:"))
793 + colon = strchr(str + 5, ':');
797 + type = pa_xstrndup(str + 5, colon - (str + 5));
802 + name = pa_xstrdup(colon + 1);
807 + *info = pa_binding_target_info_new(type, name, field);
814 + pa_log("Invalid binding target: %s", str);
818 + return -PA_ERR_INVALID;
821 +pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info) {
824 + return pa_binding_target_info_new(info->type, info->name, info->field);
827 +void pa_binding_target_info_free(pa_binding_target_info *info) {
830 + pa_xfree(info->field);
831 + pa_xfree(info->name);
832 + pa_xfree(info->type);
836 +static void field_entry_free(struct field_entry *entry) {
839 + pa_xfree(entry->name);
843 +pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
844 + pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name) {
845 + pa_binding_target_type *type;
848 + pa_assert(objects);
849 + pa_assert(put_hook);
850 + pa_assert(unlink_hook);
851 + pa_assert(get_name);
853 + type = pa_xnew0(pa_binding_target_type, 1);
854 + type->name = pa_xstrdup(name);
855 + type->objects = objects;
856 + type->put_hook = put_hook;
857 + type->unlink_hook = unlink_hook;
858 + type->get_name = get_name;
859 + type->fields = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) field_entry_free);
864 +void pa_binding_target_type_free(pa_binding_target_type *type) {
868 + pa_hashmap_free(type->fields);
870 + pa_xfree(type->name);
874 +void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset) {
875 + struct field_entry *entry;
880 + entry = pa_xnew0(struct field_entry, 1);
881 + entry->name = pa_xstrdup(name);
882 + entry->offset = offset;
884 + pa_assert_se(pa_hashmap_put(type->fields, entry->name, entry) >= 0);
887 +int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset) {
888 + struct field_entry *entry;
894 + entry = pa_hashmap_get(type->fields, field);
896 + return -PA_ERR_NOENTITY;
898 + *offset = entry->offset;
903 +static pa_hook_result_t target_type_added_cb(void *hook_data, void *call_data, void *userdata) {
904 + pa_binding_target_type *type = call_data;
905 + pa_binding *binding = userdata;
908 + pa_assert(binding);
910 + if (!pa_streq(type->name, binding->target_info->type))
913 + set_target_type(binding, type);
918 +static pa_hook_result_t target_type_removed_cb(void *hook_data, void *call_data, void *userdata) {
919 + pa_binding_target_type *type = call_data;
920 + pa_binding *binding = userdata;
923 + pa_assert(binding);
925 + if (type != binding->target_type)
928 + set_target_type(binding, NULL);
933 +static pa_hook_result_t target_put_cb(void *hook_data, void *call_data, void *userdata) {
934 + pa_binding *binding = userdata;
936 + pa_assert(call_data);
937 + pa_assert(binding);
939 + if (!pa_streq(binding->target_type->get_name(call_data), binding->target_info->name))
942 + set_target_object(binding, call_data);
947 +static pa_hook_result_t target_unlink_cb(void *hook_data, void *call_data, void *userdata) {
948 + pa_binding *binding = userdata;
950 + pa_assert(call_data);
951 + pa_assert(binding);
953 + if (call_data != binding->target_object)
956 + set_target_object(binding, NULL);
961 +static void set_target_object(pa_binding *binding, void *object) {
962 + pa_assert(binding);
964 + binding->target_object = object;
967 + if (binding->target_put_slot) {
968 + pa_hook_slot_free(binding->target_put_slot);
969 + binding->target_put_slot = NULL;
972 + if (!binding->target_unlink_slot)
973 + binding->target_unlink_slot = pa_hook_connect(binding->target_type->unlink_hook, PA_HOOK_NORMAL, target_unlink_cb,
976 + if (binding->target_field_offset_valid)
977 + binding->owner_info->set_value(binding->owner_info->userdata,
978 + *((void **) (((uint8_t *) object) + binding->target_field_offset)));
980 + binding->owner_info->set_value(binding->owner_info->userdata, NULL);
982 + if (binding->target_unlink_slot) {
983 + pa_hook_slot_free(binding->target_unlink_slot);
984 + binding->target_unlink_slot = NULL;
987 + if (binding->target_type) {
988 + if (!binding->target_put_slot)
989 + binding->target_put_slot = pa_hook_connect(binding->target_type->put_hook, PA_HOOK_NORMAL, target_put_cb, binding);
991 + if (binding->target_put_slot) {
992 + pa_hook_slot_free(binding->target_put_slot);
993 + binding->target_put_slot = NULL;
997 + binding->owner_info->set_value(binding->owner_info->userdata, NULL);
1001 +static void set_target_type(pa_binding *binding, pa_binding_target_type *type) {
1002 + pa_assert(binding);
1004 + binding->target_type = type;
1009 + if (binding->target_type_added_slot) {
1010 + pa_hook_slot_free(binding->target_type_added_slot);
1011 + binding->target_type_added_slot = NULL;
1014 + if (!binding->target_type_removed_slot)
1015 + binding->target_type_removed_slot =
1016 + pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED],
1017 + PA_HOOK_NORMAL, target_type_removed_cb, binding);
1019 + r = pa_binding_target_type_get_field_offset(type, binding->target_info->field, &binding->target_field_offset);
1021 + binding->target_field_offset_valid = true;
1023 + pa_log_warn("Reference to non-existing field \"%s\" in binding target type \"%s\".", binding->target_info->field,
1025 + binding->target_field_offset_valid = false;
1028 + set_target_object(binding, pa_hashmap_get(type->objects, binding->target_info->name));
1030 + if (binding->target_type_removed_slot) {
1031 + pa_hook_slot_free(binding->target_type_removed_slot);
1032 + binding->target_type_removed_slot = NULL;
1035 + if (!binding->target_type_added_slot)
1036 + binding->target_type_added_slot =
1037 + pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED],
1038 + PA_HOOK_NORMAL, target_type_added_cb, binding);
1040 + binding->target_field_offset_valid = false;
1042 + set_target_object(binding, NULL);
1046 +pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
1047 + const pa_binding_target_info *target_info) {
1048 + pa_binding *binding;
1051 + pa_assert(owner_info);
1052 + pa_assert(target_info);
1054 + binding = pa_xnew0(pa_binding, 1);
1055 + binding->volume_api = api;
1056 + binding->owner_info = pa_binding_owner_info_copy(owner_info);
1057 + binding->target_info = pa_binding_target_info_copy(target_info);
1059 + set_target_type(binding, pa_hashmap_get(api->binding_target_types, target_info->type));
1064 +void pa_binding_free(pa_binding *binding) {
1065 + pa_assert(binding);
1067 + if (binding->target_unlink_slot)
1068 + pa_hook_slot_free(binding->target_unlink_slot);
1070 + if (binding->target_put_slot)
1071 + pa_hook_slot_free(binding->target_put_slot);
1073 + if (binding->target_type_removed_slot)
1074 + pa_hook_slot_free(binding->target_type_removed_slot);
1076 + if (binding->target_type_added_slot)
1077 + pa_hook_slot_free(binding->target_type_added_slot);
1079 + if (binding->target_info)
1080 + pa_binding_target_info_free(binding->target_info);
1082 + if (binding->owner_info)
1083 + pa_binding_owner_info_free(binding->owner_info);
1085 + pa_xfree(binding);
1087 diff --git a/src/modules/volume-api/binding.h b/src/modules/volume-api/binding.h
1088 new file mode 100644
1089 index 0000000..ba4dea8
1091 +++ b/src/modules/volume-api/binding.h
1093 +#ifndef foobindinghfoo
1094 +#define foobindinghfoo
1097 + This file is part of PulseAudio.
1099 + Copyright 2014 Intel Corporation
1101 + PulseAudio is free software; you can redistribute it and/or modify
1102 + it under the terms of the GNU Lesser General Public License as published
1103 + by the Free Software Foundation; either version 2.1 of the License,
1104 + or (at your option) any later version.
1106 + PulseAudio is distributed in the hope that it will be useful, but
1107 + WITHOUT ANY WARRANTY; without even the implied warranty of
1108 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1109 + General Public License for more details.
1111 + You should have received a copy of the GNU Lesser General Public License
1112 + along with PulseAudio; if not, write to the Free Software
1113 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1117 +#include <modules/volume-api/volume-api.h>
1119 +typedef struct pa_binding pa_binding;
1120 +typedef struct pa_binding_owner_info pa_binding_owner_info;
1121 +typedef struct pa_binding_target_info pa_binding_target_info;
1122 +typedef struct pa_binding_target_type pa_binding_target_type;
1124 +typedef void (*pa_binding_set_value_cb_t)(void *userdata, void *value);
1126 +struct pa_binding_owner_info {
1127 + /* This is the object that has the variable that the binding is created
1131 + /* Called when the owner object's value needs to be updated. The userdata
1132 + * parameter of the callback is the same as the userdata field in this
1133 + * struct, and the value parameter is the new value for whatever variable
1134 + * the binding was created for. */
1135 + pa_binding_set_value_cb_t set_value;
1138 +pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata);
1139 +pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info);
1140 +void pa_binding_owner_info_free(pa_binding_owner_info *info);
1142 +struct pa_binding_target_info {
1143 + /* The target type name as registered with
1144 + * pa_binding_target_type_register(). */
1147 + /* The target object name as returned by the get_name callback of
1148 + * pa_binding_target_type. */
1151 + /* The target field of the target object. */
1155 +pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field);
1157 +/* The string format is "bind:TYPE:NAME". */
1158 +int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info);
1160 +pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info);
1161 +void pa_binding_target_info_free(pa_binding_target_info *info);
1163 +typedef const char *(*pa_binding_target_type_get_name_cb_t)(void *object);
1165 +struct pa_binding_target_type {
1166 + /* Identifier for this target type. */
1169 + /* name -> object. Points directly to some "master" object hashmap, so the
1170 + * hashmap is not owned by pa_binding_target_type. */
1171 + pa_hashmap *objects;
1173 + /* The hook that notifies of new objects if this target type. The call data
1174 + * of the hook must be a pointer to the new object (this should be true for
1175 + * all PUT hooks, so don't worry too much). */
1176 + pa_hook *put_hook;
1178 + /* The hook that notifies of unlinked objects of this target type. The call
1179 + * data of the hook must be a pointer to the removed object (this should be
1180 + * true for all UNLINK hooks, so don't worry too much). */
1181 + pa_hook *unlink_hook;
1183 + /* Function for getting the name of an object of this target type. */
1184 + pa_binding_target_type_get_name_cb_t get_name;
1186 + pa_hashmap *fields;
1189 +pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
1190 + pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name);
1191 +void pa_binding_target_type_free(pa_binding_target_type *type);
1193 +/* Useful when calling pa_binding_target_type_add_field(). */
1194 +#define PA_BINDING_CALCULATE_FIELD_OFFSET(type, field) ((size_t) &(((type *) 0)->field))
1196 +/* Called during the type initialization (right after
1197 + * pa_binding_target_type_new()). */
1198 +void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset);
1200 +int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset);
1202 +struct pa_binding {
1203 + pa_volume_api *volume_api;
1204 + pa_binding_owner_info *owner_info;
1205 + pa_binding_target_info *target_info;
1206 + pa_binding_target_type *target_type;
1207 + void *target_object;
1208 + size_t target_field_offset;
1209 + bool target_field_offset_valid;
1210 + pa_hook_slot *target_type_added_slot;
1211 + pa_hook_slot *target_type_removed_slot;
1212 + pa_hook_slot *target_put_slot;
1213 + pa_hook_slot *target_unlink_slot;
1216 +pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
1217 + const pa_binding_target_info *target_info);
1218 +void pa_binding_free(pa_binding *binding);
1221 diff --git a/src/modules/volume-api/bvolume.h b/src/modules/volume-api/bvolume.h
1222 new file mode 100644
1223 index 0000000..0317fb6
1225 +++ b/src/modules/volume-api/bvolume.h
1227 +#ifndef foobvolumehfoo
1228 +#define foobvolumehfoo
1231 + This file is part of PulseAudio.
1233 + Copyright 2014 Intel Corporation
1235 + PulseAudio is free software; you can redistribute it and/or modify
1236 + it under the terms of the GNU Lesser General Public License as published
1237 + by the Free Software Foundation; either version 2.1 of the License,
1238 + or (at your option) any later version.
1240 + PulseAudio is distributed in the hope that it will be useful, but
1241 + WITHOUT ANY WARRANTY; without even the implied warranty of
1242 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1243 + General Public License for more details.
1245 + You should have received a copy of the GNU Lesser General Public License
1246 + along with PulseAudio; if not, write to the Free Software
1247 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1251 +#include <pulse/ext-volume-api.h>
1253 +typedef pa_ext_volume_api_bvolume pa_bvolume;
1255 +#define pa_balance_valid pa_ext_volume_api_balance_valid
1256 +#define pa_bvolume_valid pa_ext_volume_api_bvolume_valid
1257 +#define pa_bvolume_init_invalid pa_ext_volume_api_bvolume_init_invalid
1258 +#define pa_bvolume_init_mono pa_ext_volume_api_bvolume_init_mono
1259 +#define pa_bvolume_equal pa_ext_volume_api_bvolume_equal
1260 +#define pa_bvolume_from_cvolume pa_ext_volume_api_bvolume_from_cvolume
1261 +#define pa_bvolume_to_cvolume pa_ext_volume_api_bvolume_to_cvolume
1262 +#define pa_bvolume_copy_balance pa_ext_volume_api_bvolume_copy_balance
1263 +#define pa_bvolume_reset_balance pa_ext_volume_api_bvolume_reset_balance
1264 +#define pa_bvolume_remap pa_ext_volume_api_bvolume_remap
1266 +#define PA_BVOLUME_SNPRINT_BALANCE_MAX PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX
1267 +#define pa_bvolume_snprint_balance pa_ext_volume_api_bvolume_snprint_balance
1270 diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c
1271 new file mode 100644
1272 index 0000000..1d912ba
1274 +++ b/src/modules/volume-api/device-creator.c
1277 + This file is part of PulseAudio.
1279 + Copyright 2014 Intel Corporation
1281 + PulseAudio is free software; you can redistribute it and/or modify
1282 + it under the terms of the GNU Lesser General Public License as published
1283 + by the Free Software Foundation; either version 2.1 of the License,
1284 + or (at your option) any later version.
1286 + PulseAudio is distributed in the hope that it will be useful, but
1287 + WITHOUT ANY WARRANTY; without even the implied warranty of
1288 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1289 + General Public License for more details.
1291 + You should have received a copy of the GNU Lesser General Public License
1292 + along with PulseAudio; if not, write to the Free Software
1293 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1297 +#ifdef HAVE_CONFIG_H
1298 +#include <config.h>
1301 +#include "device-creator.h"
1303 +#include <modules/volume-api/device.h>
1304 +#include <modules/volume-api/mute-control.h>
1305 +#include <modules/volume-api/volume-control.h>
1307 +#include <pulsecore/core-util.h>
1308 +#include <pulsecore/i18n.h>
1310 +struct pa_device_creator {
1311 + pa_volume_api *volume_api;
1312 + pa_hashmap *devices; /* pa_device_port/pa_sink/pa_source -> struct device */
1313 + pa_hook_slot *card_put_slot;
1314 + pa_hook_slot *card_unlink_slot;
1315 + pa_hook_slot *sink_put_slot;
1316 + pa_hook_slot *sink_unlink_slot;
1317 + pa_hook_slot *source_put_slot;
1318 + pa_hook_slot *source_unlink_slot;
1323 + DEVICE_TYPE_PORT_MONITOR,
1325 + DEVICE_TYPE_SOURCE,
1328 +struct device_volume_control {
1329 + struct device *device;
1330 + pa_volume_control *volume_control;
1334 + pa_hook_slot *volume_changed_slot;
1337 +struct device_mute_control {
1338 + struct device *device;
1339 + pa_mute_control *mute_control;
1343 + pa_hook_slot *mute_changed_slot;
1347 + pa_device_creator *creator;
1348 + enum device_type type;
1349 + pa_device_port *port;
1351 + pa_source *source;
1352 + pa_device *device;
1353 + struct device_volume_control *volume_control;
1354 + struct device_mute_control *mute_control;
1358 + pa_hook_slot *proplist_changed_slot;
1359 + pa_hook_slot *port_active_changed_slot;
1360 + struct device *monitor;
1363 +static const char *device_type_from_icon_name(const char *icon_name) {
1367 + if (pa_streq(icon_name, "audio-input-microphone"))
1368 + return "microphone";
1370 + if (pa_streq(icon_name, "audio-speakers"))
1371 + return "speakers";
1373 + if (pa_streq(icon_name, "audio-headphones"))
1374 + return "headphones";
1379 +static const char *device_type_from_port_name(pa_device_port *port) {
1382 + if (strstr(port->name, "analog")) {
1383 + if (port->direction == PA_DIRECTION_INPUT)
1384 + return "analog-input";
1386 + return "analog-output";
1389 + if (strstr(port->name, "hdmi")) {
1390 + if (port->direction == PA_DIRECTION_INPUT)
1391 + return "hdmi-input";
1393 + return "hdmi-output";
1396 + if (strstr(port->name, "iec958")) {
1397 + if (port->direction == PA_DIRECTION_INPUT)
1398 + return "spdif-input";
1400 + return "spdif-output";
1406 +static const char *device_type_from_port(pa_device_port *port) {
1407 + const char *device_type;
1411 + device_type = device_type_from_icon_name(pa_proplist_gets(port->proplist, PA_PROP_DEVICE_ICON_NAME));
1413 + return device_type;
1415 + device_type = device_type_from_port_name(port);
1417 + return device_type;
1422 +static const char *get_sink_description(pa_sink *sink) {
1423 + const char *description;
1427 + description = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION);
1429 + return description;
1431 + return sink->name;
1434 +static const char *get_source_description(pa_source *source) {
1435 + const char *description;
1437 + pa_assert(source);
1439 + description = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION);
1441 + return description;
1443 + return source->name;
1446 +static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *volume, bool set_volume, bool set_balance) {
1447 + struct device_volume_control *control;
1448 + struct device *device;
1449 + pa_bvolume bvolume;
1450 + pa_cvolume cvolume;
1453 + pa_assert(volume);
1455 + control = c->userdata;
1456 + device = control->device;
1458 + switch (device->type) {
1459 + case DEVICE_TYPE_PORT:
1460 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1461 + pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
1463 + pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
1466 + case DEVICE_TYPE_SINK:
1467 + pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
1470 + case DEVICE_TYPE_PORT_MONITOR:
1471 + case DEVICE_TYPE_SOURCE:
1472 + pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
1477 + bvolume.volume = volume->volume;
1480 + pa_bvolume_copy_balance(&bvolume, volume);
1482 + pa_bvolume_to_cvolume(&bvolume, &cvolume);
1484 + switch (device->type) {
1485 + case DEVICE_TYPE_PORT:
1486 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1487 + pa_sink_set_volume(device->sink, &cvolume, true, true);
1489 + pa_source_set_volume(device->source, &cvolume, true, true);
1492 + case DEVICE_TYPE_PORT_MONITOR:
1493 + case DEVICE_TYPE_SOURCE:
1494 + pa_source_set_volume(device->source, &cvolume, true, true);
1497 + case DEVICE_TYPE_SINK:
1498 + pa_sink_set_volume(device->sink, &cvolume, true, true);
1505 +static struct device_volume_control *device_volume_control_new(struct device *device) {
1506 + struct device_volume_control *control;
1507 + const char *name = NULL;
1508 + bool convertible_to_dB = false;
1509 + bool channel_map_is_writable;
1511 + pa_assert(device);
1513 + control = pa_xnew0(struct device_volume_control, 1);
1514 + control->device = device;
1516 + switch (device->type) {
1517 + case DEVICE_TYPE_PORT:
1518 + name = "port-volume-control";
1520 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1521 + convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
1523 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1527 + case DEVICE_TYPE_PORT_MONITOR:
1528 + name = "port-monitor-volume-control";
1529 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1532 + case DEVICE_TYPE_SINK:
1533 + name = "sink-volume-control";
1534 + convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
1537 + case DEVICE_TYPE_SOURCE:
1538 + name = "source-volume-control";
1539 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1543 + channel_map_is_writable = false;
1544 + control->volume_control = pa_volume_control_new(device->creator->volume_api, name, device->device->description,
1545 + convertible_to_dB, channel_map_is_writable);
1546 + control->volume_control->set_volume = volume_control_set_volume_cb;
1547 + control->volume_control->userdata = control;
1552 +static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
1553 + struct device_volume_control *control = userdata;
1554 + struct device *device;
1555 + pa_sink *sink = NULL;
1556 + pa_source *source = NULL;
1557 + pa_bvolume bvolume;
1559 + pa_assert(control);
1560 + pa_assert(call_data);
1562 + device = control->device;
1564 + switch (device->type) {
1565 + case DEVICE_TYPE_PORT:
1566 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1569 + source = call_data;
1572 + case DEVICE_TYPE_PORT_MONITOR:
1573 + case DEVICE_TYPE_SOURCE:
1574 + source = call_data;
1577 + case DEVICE_TYPE_SINK:
1582 + if ((sink && sink != device->sink) || (source && source != device->source))
1583 + return PA_HOOK_OK;
1586 + pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map);
1588 + pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map);
1590 + pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true);
1592 + return PA_HOOK_OK;
1595 +static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
1596 + struct device_volume_control *control;
1597 + struct device *device;
1598 + pa_cvolume cvolume;
1602 + control = c->userdata;
1603 + device = control->device;
1604 + pa_bvolume_to_cvolume(&control->volume_control->volume, &cvolume);
1606 + switch (device->type) {
1607 + case DEVICE_TYPE_PORT:
1608 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1609 + pa_sink_set_volume(device->sink, &cvolume, true, true);
1611 + pa_source_set_volume(device->source, &cvolume, true, true);
1614 + case DEVICE_TYPE_PORT_MONITOR:
1615 + case DEVICE_TYPE_SOURCE:
1616 + pa_source_set_volume(device->source, &cvolume, true, true);
1619 + case DEVICE_TYPE_SINK:
1620 + pa_sink_set_volume(device->sink, &cvolume, true, true);
1625 +static void device_volume_control_put(struct device_volume_control *control) {
1626 + struct device *device;
1627 + pa_bvolume volume;
1629 + pa_assert(control);
1631 + device = control->device;
1633 + switch (device->type) {
1634 + case DEVICE_TYPE_PORT:
1635 + if (device->port->direction == PA_DIRECTION_OUTPUT) {
1636 + control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
1637 + PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
1638 + pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
1640 + control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
1641 + PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
1642 + pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
1646 + case DEVICE_TYPE_PORT_MONITOR:
1647 + case DEVICE_TYPE_SOURCE:
1648 + control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
1649 + PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
1650 + pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
1653 + case DEVICE_TYPE_SINK:
1654 + control->volume_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
1655 + PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
1656 + pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
1660 + pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb);
1663 +static void device_volume_control_unlink(struct device_volume_control *control) {
1664 + pa_assert(control);
1666 + if (control->unlinked)
1669 + control->unlinked = true;
1671 + if (control->volume_control)
1672 + pa_volume_control_unlink(control->volume_control);
1674 + if (control->volume_changed_slot) {
1675 + pa_hook_slot_free(control->volume_changed_slot);
1676 + control->volume_changed_slot = NULL;
1680 +static void device_volume_control_free(struct device_volume_control *control) {
1681 + pa_assert(control);
1683 + if (!control->unlinked)
1684 + device_volume_control_unlink(control);
1686 + if (control->volume_control)
1687 + pa_volume_control_free(control->volume_control);
1689 + pa_xfree(control);
1692 +static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
1693 + struct device_mute_control *control;
1694 + struct device *device;
1698 + control = c->userdata;
1699 + device = control->device;
1701 + switch (device->type) {
1702 + case DEVICE_TYPE_PORT:
1703 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1704 + pa_sink_set_mute(device->sink, mute, true);
1706 + pa_source_set_mute(device->source, mute, true);
1709 + case DEVICE_TYPE_PORT_MONITOR:
1710 + case DEVICE_TYPE_SOURCE:
1711 + pa_source_set_mute(device->source, mute, true);
1714 + case DEVICE_TYPE_SINK:
1715 + pa_sink_set_mute(device->sink, mute, true);
1722 +static struct device_mute_control *device_mute_control_new(struct device *device) {
1723 + struct device_mute_control *control;
1724 + const char *name = NULL;
1726 + pa_assert(device);
1728 + control = pa_xnew0(struct device_mute_control, 1);
1729 + control->device = device;
1731 + switch (device->type) {
1732 + case DEVICE_TYPE_PORT:
1733 + name = "port-mute-control";
1736 + case DEVICE_TYPE_PORT_MONITOR:
1737 + name = "port-monitor-mute-control";
1740 + case DEVICE_TYPE_SINK:
1741 + name = "sink-mute-control";
1744 + case DEVICE_TYPE_SOURCE:
1745 + name = "source-mute-control";
1749 + control->mute_control = pa_mute_control_new(device->creator->volume_api, name, device->device->description);
1750 + control->mute_control->set_mute = mute_control_set_mute_cb;
1751 + control->mute_control->userdata = control;
1756 +static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
1757 + struct device_mute_control *control = userdata;
1758 + struct device *device;
1759 + pa_sink *sink = NULL;
1760 + pa_source *source = NULL;
1763 + pa_assert(control);
1764 + pa_assert(call_data);
1766 + device = control->device;
1768 + switch (device->type) {
1769 + case DEVICE_TYPE_PORT:
1770 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1773 + source = call_data;
1776 + case DEVICE_TYPE_PORT_MONITOR:
1777 + case DEVICE_TYPE_SOURCE:
1778 + source = call_data;
1781 + case DEVICE_TYPE_SINK:
1786 + if ((sink && sink != device->sink) || (source && source != device->source))
1787 + return PA_HOOK_OK;
1790 + mute = sink->muted;
1792 + mute = source->muted;
1794 + pa_mute_control_mute_changed(control->mute_control, mute);
1796 + return PA_HOOK_OK;
1799 +static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
1800 + struct device_volume_control *control;
1801 + struct device *device;
1805 + control = c->userdata;
1806 + device = control->device;
1808 + switch (device->type) {
1809 + case DEVICE_TYPE_PORT:
1810 + if (device->port->direction == PA_DIRECTION_OUTPUT)
1811 + pa_sink_set_mute(device->sink, c->mute, true);
1813 + pa_source_set_mute(device->source, c->mute, true);
1816 + case DEVICE_TYPE_PORT_MONITOR:
1817 + case DEVICE_TYPE_SOURCE:
1818 + pa_source_set_mute(device->source, c->mute, true);
1821 + case DEVICE_TYPE_SINK:
1822 + pa_sink_set_mute(device->sink, c->mute, true);
1827 +static void device_mute_control_put(struct device_mute_control *control) {
1828 + struct device *device;
1829 + bool mute = false;
1831 + pa_assert(control);
1833 + device = control->device;
1835 + switch (device->type) {
1836 + case DEVICE_TYPE_PORT:
1837 + if (device->port->direction == PA_DIRECTION_OUTPUT) {
1838 + control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
1839 + PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
1840 + mute = device->sink->muted;
1842 + control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
1843 + PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
1844 + mute = device->source->muted;
1848 + case DEVICE_TYPE_PORT_MONITOR:
1849 + case DEVICE_TYPE_SOURCE:
1850 + control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
1851 + PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
1852 + mute = device->source->muted;
1855 + case DEVICE_TYPE_SINK:
1856 + control->mute_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
1857 + PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
1858 + mute = device->sink->muted;
1862 + pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb);
1865 +static void device_mute_control_unlink(struct device_mute_control *control) {
1866 + pa_assert(control);
1868 + if (control->unlinked)
1871 + control->unlinked = true;
1873 + if (control->mute_control)
1874 + pa_mute_control_unlink(control->mute_control);
1876 + if (control->mute_changed_slot) {
1877 + pa_hook_slot_free(control->mute_changed_slot);
1878 + control->mute_changed_slot = NULL;
1882 +static void device_mute_control_free(struct device_mute_control *control) {
1883 + pa_assert(control);
1885 + if (!control->unlinked)
1886 + device_mute_control_unlink(control);
1888 + if (control->mute_control)
1889 + pa_mute_control_free(control->mute_control);
1891 + pa_xfree(control);
1894 +static void device_set_sink_and_source_from_port(struct device *device) {
1896 + pa_source *source;
1899 + pa_assert(device);
1901 + device->sink = NULL;
1902 + device->source = NULL;
1904 + if (!device->port->active)
1907 + switch (device->type) {
1908 + case DEVICE_TYPE_PORT:
1909 + if (device->port->direction == PA_DIRECTION_OUTPUT) {
1910 + PA_IDXSET_FOREACH(sink, device->port->card->sinks, idx) {
1911 + if (sink->active_port == device->port) {
1912 + device->sink = sink;
1917 + pa_assert(device->sink);
1919 + PA_IDXSET_FOREACH(source, device->port->card->sources, idx) {
1920 + if (source->active_port == device->port) {
1921 + device->source = source;
1926 + pa_assert(device->source);
1930 + case DEVICE_TYPE_PORT_MONITOR: {
1931 + PA_IDXSET_FOREACH(sink, device->port->card->sinks, idx) {
1932 + if (sink->active_port == device->port) {
1933 + device->sink = sink;
1934 + device->source = sink->monitor_source;
1939 + pa_assert(device->sink);
1943 + case DEVICE_TYPE_SINK:
1944 + case DEVICE_TYPE_SOURCE:
1945 + pa_assert_not_reached();
1949 +static pa_hook_result_t sink_or_source_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
1950 + struct device *device = userdata;
1951 + pa_sink *sink = NULL;
1952 + pa_source *source = NULL;
1953 + const char *description = NULL;
1955 + pa_assert(device);
1956 + pa_assert(call_data);
1958 + switch (device->type) {
1959 + case DEVICE_TYPE_PORT:
1960 + case DEVICE_TYPE_PORT_MONITOR:
1961 + pa_assert_not_reached();
1963 + case DEVICE_TYPE_SINK:
1966 + if (sink != device->sink)
1967 + return PA_HOOK_OK;
1969 + description = get_sink_description(sink);
1972 + case DEVICE_TYPE_SOURCE:
1973 + source = call_data;
1975 + if (source != device->source)
1976 + return PA_HOOK_OK;
1978 + description = get_source_description(source);
1982 + pa_device_description_changed(device->device, description);
1983 + pa_volume_control_description_changed(device->volume_control->volume_control, description);
1984 + pa_mute_control_description_changed(device->mute_control->mute_control, description);
1986 + return PA_HOOK_OK;
1989 +static struct device *device_new(pa_device_creator *creator, enum device_type type, void *core_device) {
1990 + struct device *device = NULL;
1991 + const char *name = NULL;
1992 + char *description = NULL;
1993 + pa_direction_t direction = PA_DIRECTION_OUTPUT;
1994 + const char *device_type = NULL;
1995 + bool create_volume_and_mute_controls = true;
1997 + pa_assert(creator);
1998 + pa_assert(core_device);
2000 + device = pa_xnew0(struct device, 1);
2001 + device->creator = creator;
2002 + device->type = type;
2005 + case DEVICE_TYPE_PORT:
2006 + device->port = core_device;
2007 + device_set_sink_and_source_from_port(device);
2008 + name = "port-device";
2009 + description = pa_xstrdup(device->port->description);
2010 + direction = device->port->direction;
2011 + device_type = device_type_from_port(device->port);
2013 + if (!device->sink && !device->source)
2014 + create_volume_and_mute_controls = false;
2017 + case DEVICE_TYPE_PORT_MONITOR:
2018 + device->port = core_device;
2019 + device_set_sink_and_source_from_port(device);
2020 + name = "port-monitor-device";
2021 + description = pa_sprintf_malloc(_("Monitor of %s"), device->port->description);
2022 + direction = PA_DIRECTION_INPUT;
2024 + if (!device->source)
2025 + create_volume_and_mute_controls = false;
2028 + case DEVICE_TYPE_SINK:
2029 + device->sink = core_device;
2030 + name = "sink-device";
2031 + description = pa_xstrdup(get_sink_description(device->sink));
2032 + direction = PA_DIRECTION_OUTPUT;
2035 + case DEVICE_TYPE_SOURCE:
2036 + device->source = core_device;
2037 + name = "source-device";
2038 + description = pa_xstrdup(get_source_description(device->source));
2039 + direction = PA_DIRECTION_INPUT;
2043 + device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0);
2044 + pa_xfree(description);
2046 + if (create_volume_and_mute_controls) {
2047 + device->volume_control = device_volume_control_new(device);
2048 + device->mute_control = device_mute_control_new(device);
2052 + case DEVICE_TYPE_PORT:
2053 + if (device->port->direction == PA_DIRECTION_OUTPUT)
2054 + device->monitor = device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port);
2057 + case DEVICE_TYPE_PORT_MONITOR:
2060 + case DEVICE_TYPE_SINK:
2061 + device->proplist_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED],
2062 + PA_HOOK_NORMAL, sink_or_source_proplist_changed_cb, device);
2065 + case DEVICE_TYPE_SOURCE:
2066 + device->proplist_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED],
2067 + PA_HOOK_NORMAL, sink_or_source_proplist_changed_cb, device);
2074 +static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data, void *userdata) {
2075 + struct device *device = userdata;
2076 + pa_device_port *port = call_data;
2077 + bool should_have_volume_and_mute_controls = false;
2079 + pa_assert(device);
2082 + if (port != device->port)
2083 + return PA_HOOK_OK;
2085 + device_set_sink_and_source_from_port(device);
2087 + switch (device->type) {
2088 + case DEVICE_TYPE_PORT:
2089 + should_have_volume_and_mute_controls = device->sink || device->source;
2092 + case DEVICE_TYPE_PORT_MONITOR:
2093 + should_have_volume_and_mute_controls = !!device->source;
2096 + case DEVICE_TYPE_SINK:
2097 + case DEVICE_TYPE_SOURCE:
2098 + pa_assert_not_reached();
2101 + if (should_have_volume_and_mute_controls && !device->volume_control) {
2102 + pa_assert(!device->mute_control);
2104 + device->volume_control = device_volume_control_new(device);
2105 + device_volume_control_put(device->volume_control);
2106 + pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
2108 + device->mute_control = device_mute_control_new(device);
2109 + device_mute_control_put(device->mute_control);
2110 + pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
2113 + if (!should_have_volume_and_mute_controls && device->volume_control) {
2114 + pa_assert(device->mute_control);
2116 + device_mute_control_free(device->mute_control);
2117 + device->mute_control = NULL;
2118 + device_volume_control_free(device->volume_control);
2119 + device->volume_control = NULL;
2122 + return PA_HOOK_OK;
2125 +static void device_put(struct device *device) {
2126 + pa_assert(device);
2128 + switch (device->type) {
2129 + case DEVICE_TYPE_PORT:
2130 + case DEVICE_TYPE_PORT_MONITOR:
2131 + device->port_active_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED],
2132 + PA_HOOK_NORMAL, port_active_changed_cb, device);
2134 + case DEVICE_TYPE_SINK:
2135 + case DEVICE_TYPE_SOURCE:
2139 + if (device->volume_control)
2140 + device_volume_control_put(device->volume_control);
2142 + if (device->mute_control)
2143 + device_mute_control_put(device->mute_control);
2145 + pa_device_put(device->device, device->volume_control ? device->volume_control->volume_control : NULL,
2146 + device->mute_control ? device->mute_control->mute_control : NULL);
2148 + if (device->monitor)
2149 + device_put(device->monitor);
2152 +static void device_unlink(struct device *device) {
2153 + pa_assert(device);
2155 + if (device->unlinked)
2158 + device->unlinked = true;
2160 + if (device->monitor)
2161 + device_unlink(device->monitor);
2163 + if (device->device)
2164 + pa_device_unlink(device->device);
2166 + if (device->mute_control)
2167 + device_mute_control_unlink(device->mute_control);
2169 + if (device->volume_control)
2170 + device_volume_control_unlink(device->volume_control);
2172 + if (device->port_active_changed_slot) {
2173 + pa_hook_slot_free(device->port_active_changed_slot);
2174 + device->port_active_changed_slot = NULL;
2178 +static void device_free(struct device *device) {
2179 + pa_assert(device);
2181 + if (!device->unlinked)
2182 + device_unlink(device);
2184 + if (device->monitor)
2185 + device_free(device->monitor);
2187 + if (device->proplist_changed_slot)
2188 + pa_hook_slot_free(device->proplist_changed_slot);
2190 + if (device->mute_control)
2191 + device_mute_control_free(device->mute_control);
2193 + if (device->volume_control)
2194 + device_volume_control_free(device->volume_control);
2196 + if (device->device)
2197 + pa_device_free(device->device);
2202 +static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) {
2203 + struct device *device;
2205 + pa_assert(creator);
2206 + pa_assert(core_device);
2209 + case DEVICE_TYPE_PORT:
2212 + case DEVICE_TYPE_PORT_MONITOR:
2213 + pa_assert_not_reached();
2215 + case DEVICE_TYPE_SINK:
2216 + if (!pa_hashmap_isempty(((pa_sink *) core_device)->ports))
2220 + case DEVICE_TYPE_SOURCE: {
2221 + pa_source *source = core_device;
2223 + if (source->monitor_of && !pa_hashmap_isempty(source->monitor_of->ports))
2226 + if (!pa_hashmap_isempty(((pa_source *) core_device)->ports))
2232 + device = device_new(creator, type, core_device);
2233 + pa_hashmap_put(creator->devices, core_device, device);
2234 + device_put(device);
2237 +static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *userdata) {
2238 + pa_device_creator *creator = userdata;
2239 + pa_card *card = call_data;
2240 + pa_device_port *port;
2243 + pa_assert(creator);
2246 + PA_HASHMAP_FOREACH(port, card->ports, state)
2247 + create_device(creator, DEVICE_TYPE_PORT, port);
2249 + return PA_HOOK_OK;
2252 +static pa_hook_result_t card_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2253 + pa_device_creator *creator = userdata;
2254 + pa_card *card = call_data;
2255 + pa_device_port *port;
2258 + pa_assert(creator);
2261 + PA_HASHMAP_FOREACH(port, card->ports, state)
2262 + pa_hashmap_remove_and_free(creator->devices, port);
2264 + return PA_HOOK_OK;
2267 +static pa_hook_result_t sink_put_cb(void *hook_data, void *call_data, void *userdata) {
2268 + pa_device_creator *creator = userdata;
2269 + pa_sink *sink = call_data;
2271 + pa_assert(creator);
2274 + create_device(creator, DEVICE_TYPE_SINK, sink);
2276 + return PA_HOOK_OK;
2279 +static pa_hook_result_t sink_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2280 + pa_device_creator *creator = userdata;
2281 + pa_sink *sink = call_data;
2283 + pa_assert(creator);
2286 + pa_hashmap_remove_and_free(creator->devices, sink);
2288 + return PA_HOOK_OK;
2291 +static pa_hook_result_t source_put_cb(void *hook_data, void *call_data, void *userdata) {
2292 + pa_device_creator *creator = userdata;
2293 + pa_source *source = call_data;
2295 + pa_assert(creator);
2296 + pa_assert(source);
2298 + create_device(creator, DEVICE_TYPE_SOURCE, source);
2300 + return PA_HOOK_OK;
2303 +static pa_hook_result_t source_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2304 + pa_device_creator *creator = userdata;
2305 + pa_source *source = call_data;
2307 + pa_assert(creator);
2308 + pa_assert(source);
2310 + pa_hashmap_remove_and_free(creator->devices, source);
2312 + return PA_HOOK_OK;
2315 +pa_device_creator *pa_device_creator_new(pa_volume_api *api) {
2316 + pa_device_creator *creator;
2320 + pa_source *source;
2324 + creator = pa_xnew0(pa_device_creator, 1);
2325 + creator->volume_api = api;
2326 + creator->devices = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) device_free);
2327 + creator->card_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, card_put_cb, creator);
2328 + creator->card_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL, card_unlink_cb,
2330 + creator->sink_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, sink_put_cb, creator);
2331 + creator->sink_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_NORMAL, sink_unlink_cb,
2333 + creator->source_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, source_put_cb,
2335 + creator->source_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_NORMAL,
2336 + source_unlink_cb, creator);
2338 + PA_IDXSET_FOREACH(card, api->core->cards, idx) {
2339 + pa_device_port *port;
2342 + PA_HASHMAP_FOREACH(port, card->ports, state)
2343 + create_device(creator, DEVICE_TYPE_PORT, port);
2346 + PA_IDXSET_FOREACH(sink, api->core->sinks, idx)
2347 + create_device(creator, DEVICE_TYPE_SINK, sink);
2349 + PA_IDXSET_FOREACH(source, api->core->sources, idx)
2350 + create_device(creator, DEVICE_TYPE_SOURCE, source);
2355 +void pa_device_creator_free(pa_device_creator *creator) {
2356 + pa_assert(creator);
2358 + if (creator->devices)
2359 + pa_hashmap_remove_all(creator->devices);
2361 + if (creator->source_unlink_slot)
2362 + pa_hook_slot_free(creator->source_unlink_slot);
2364 + if (creator->source_put_slot)
2365 + pa_hook_slot_free(creator->source_put_slot);
2367 + if (creator->sink_unlink_slot)
2368 + pa_hook_slot_free(creator->sink_unlink_slot);
2370 + if (creator->sink_put_slot)
2371 + pa_hook_slot_free(creator->sink_put_slot);
2373 + if (creator->card_unlink_slot)
2374 + pa_hook_slot_free(creator->card_unlink_slot);
2376 + if (creator->card_put_slot)
2377 + pa_hook_slot_free(creator->card_put_slot);
2379 + if (creator->devices)
2380 + pa_hashmap_free(creator->devices);
2382 + pa_xfree(creator);
2384 diff --git a/src/modules/volume-api/device-creator.h b/src/modules/volume-api/device-creator.h
2385 new file mode 100644
2386 index 0000000..bcec8d9
2388 +++ b/src/modules/volume-api/device-creator.h
2390 +#ifndef foodevicecreatorhfoo
2391 +#define foodevicecreatorhfoo
2394 + This file is part of PulseAudio.
2396 + Copyright 2014 Intel Corporation
2398 + PulseAudio is free software; you can redistribute it and/or modify
2399 + it under the terms of the GNU Lesser General Public License as published
2400 + by the Free Software Foundation; either version 2.1 of the License,
2401 + or (at your option) any later version.
2403 + PulseAudio is distributed in the hope that it will be useful, but
2404 + WITHOUT ANY WARRANTY; without even the implied warranty of
2405 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2406 + General Public License for more details.
2408 + You should have received a copy of the GNU Lesser General Public License
2409 + along with PulseAudio; if not, write to the Free Software
2410 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2414 +#include <modules/volume-api/volume-api.h>
2416 +typedef struct pa_device_creator pa_device_creator;
2418 +pa_device_creator *pa_device_creator_new(pa_volume_api *api);
2419 +void pa_device_creator_free(pa_device_creator *creator);
2422 diff --git a/src/modules/volume-api/device.c b/src/modules/volume-api/device.c
2423 new file mode 100644
2424 index 0000000..ea496ba
2426 +++ b/src/modules/volume-api/device.c
2429 + This file is part of PulseAudio.
2431 + Copyright 2014 Intel Corporation
2433 + PulseAudio is free software; you can redistribute it and/or modify
2434 + it under the terms of the GNU Lesser General Public License as published
2435 + by the Free Software Foundation; either version 2.1 of the License,
2436 + or (at your option) any later version.
2438 + PulseAudio is distributed in the hope that it will be useful, but
2439 + WITHOUT ANY WARRANTY; without even the implied warranty of
2440 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2441 + General Public License for more details.
2443 + You should have received a copy of the GNU Lesser General Public License
2444 + along with PulseAudio; if not, write to the Free Software
2445 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2449 +#ifdef HAVE_CONFIG_H
2450 +#include <config.h>
2453 +#include "device.h"
2455 +#include <modules/volume-api/mute-control.h>
2456 +#include <modules/volume-api/volume-control.h>
2458 +#include <pulse/direction.h>
2460 +#include <pulsecore/core-util.h>
2462 +pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
2463 + const char * const *device_types, unsigned n_device_types) {
2464 + pa_device *device;
2469 + pa_assert(description);
2470 + pa_assert(device_types || n_device_types == 0);
2472 + device = pa_xnew0(pa_device, 1);
2473 + device->volume_api = api;
2474 + device->index = pa_volume_api_allocate_device_index(api);
2475 + pa_assert_se(pa_volume_api_register_name(api, name, false, &device->name) >= 0);
2476 + device->description = pa_xstrdup(description);
2477 + device->direction = direction;
2478 + device->device_types = pa_dynarray_new(pa_xfree);
2480 + for (i = 0; i < n_device_types; i++)
2481 + pa_dynarray_append(device->device_types, pa_xstrdup(device_types[i]));
2483 + device->proplist = pa_proplist_new();
2484 + device->use_default_volume_control = true;
2485 + device->use_default_mute_control = true;
2490 +void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control) {
2491 + char *device_types_str;
2492 + const char *prop_key;
2493 + void *state = NULL;
2495 + pa_assert(device);
2497 + if (default_volume_control) {
2498 + device->default_volume_control = default_volume_control;
2499 + pa_volume_control_add_default_for_device(default_volume_control, device);
2501 + device->volume_control = default_volume_control;
2502 + pa_volume_control_add_device(default_volume_control, device);
2505 + if (default_mute_control) {
2506 + device->default_mute_control = default_mute_control;
2507 + pa_mute_control_add_default_for_device(default_mute_control, device);
2509 + device->mute_control = default_mute_control;
2510 + pa_mute_control_add_device(default_mute_control, device);
2513 + pa_volume_api_add_device(device->volume_api, device);
2515 + device->linked = true;
2517 + device_types_str = pa_join((const char * const *) pa_dynarray_get_raw_array(device->device_types),
2518 + pa_dynarray_size(device->device_types), ", ");
2520 + pa_log_debug("Created device #%u.", device->index);
2521 + pa_log_debug(" Name: %s", device->name);
2522 + pa_log_debug(" Description: %s", device->description);
2523 + pa_log_debug(" Direction: %s", pa_direction_to_string(device->direction));
2524 + pa_log_debug(" Device Types: %s", *device_types_str ? device_types_str : "(none)");
2525 + pa_log_debug(" Volume control: %s", device->volume_control ? device->volume_control->name : "(unset)");
2526 + pa_log_debug(" Mute control: %s", device->mute_control ? device->mute_control->name : "(unset)");
2527 + pa_log_debug(" Properties:");
2529 + while ((prop_key = pa_proplist_iterate(device->proplist, &state)))
2530 + pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(device->proplist, prop_key)));
2532 + pa_xfree(device_types_str);
2534 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_PUT], device);
2537 +void pa_device_unlink(pa_device *device) {
2538 + pa_assert(device);
2540 + if (device->unlinked) {
2541 + pa_log_debug("Unlinking device %s (already unlinked, this is a no-op).", device->name);
2545 + device->unlinked = true;
2547 + pa_log_debug("Unlinking device %s.", device->name);
2549 + if (device->linked)
2550 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
2552 + pa_volume_api_remove_device(device->volume_api, device);
2554 + if (device->mute_control) {
2555 + pa_mute_control_remove_device(device->mute_control, device);
2556 + device->mute_control = NULL;
2559 + if (device->default_mute_control) {
2560 + pa_mute_control_remove_default_for_device(device->default_mute_control, device);
2561 + device->default_mute_control = NULL;
2564 + if (device->volume_control) {
2565 + pa_volume_control_remove_device(device->volume_control, device);
2566 + device->volume_control = NULL;
2569 + if (device->default_volume_control) {
2570 + pa_volume_control_remove_default_for_device(device->default_volume_control, device);
2571 + device->default_volume_control = NULL;
2575 +void pa_device_free(pa_device *device) {
2576 + pa_assert(device);
2578 + if (!device->unlinked)
2579 + pa_device_unlink(device);
2581 + if (device->proplist)
2582 + pa_proplist_free(device->proplist);
2584 + if (device->device_types)
2585 + pa_dynarray_free(device->device_types);
2587 + pa_xfree(device->description);
2590 + pa_volume_api_unregister_name(device->volume_api, device->name);
2595 +static void set_volume_control_internal(pa_device *device, pa_volume_control *control) {
2596 + pa_volume_control *old_control;
2598 + pa_assert(device);
2600 + old_control = device->volume_control;
2602 + if (control == old_control)
2606 + pa_volume_control_remove_device(old_control, device);
2608 + device->volume_control = control;
2611 + pa_volume_control_add_device(control, device);
2613 + if (!device->linked || device->unlinked)
2616 + pa_log_debug("The volume control of device %s changed from %s to %s.", device->name,
2617 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2619 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED], device);
2622 +void pa_device_set_volume_control(pa_device *device, pa_volume_control *control) {
2623 + pa_assert(device);
2625 + device->use_default_volume_control = false;
2626 + set_volume_control_internal(device, control);
2629 +static void set_mute_control_internal(pa_device *device, pa_mute_control *control) {
2630 + pa_mute_control *old_control;
2632 + pa_assert(device);
2634 + old_control = device->mute_control;
2636 + if (control == old_control)
2640 + pa_mute_control_remove_device(old_control, device);
2642 + device->mute_control = control;
2645 + pa_mute_control_add_device(control, device);
2647 + pa_log_debug("The mute control of device %s changed from %s to %s.", device->name,
2648 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2650 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED], device);
2653 +void pa_device_set_mute_control(pa_device *device, pa_mute_control *control) {
2654 + pa_assert(device);
2656 + device->use_default_mute_control = false;
2657 + set_mute_control_internal(device, control);
2660 +void pa_device_description_changed(pa_device *device, const char *new_description) {
2661 + char *old_description;
2663 + pa_assert(device);
2664 + pa_assert(new_description);
2666 + old_description = device->description;
2668 + if (pa_streq(new_description, old_description))
2671 + device->description = pa_xstrdup(new_description);
2672 + pa_log_debug("The description of device %s changed from \"%s\" to \"%s\".", device->name, old_description,
2674 + pa_xfree(old_description);
2675 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED], device);
2678 +void pa_device_set_default_volume_control(pa_device *device, pa_volume_control *control) {
2679 + pa_volume_control *old_control;
2681 + pa_assert(device);
2683 + old_control = device->default_volume_control;
2685 + if (control == old_control)
2689 + pa_volume_control_remove_default_for_device(old_control, device);
2691 + device->default_volume_control = control;
2694 + pa_volume_control_add_default_for_device(control, device);
2696 + if (device->use_default_volume_control)
2697 + set_volume_control_internal(device, control);
2700 +void pa_device_set_default_mute_control(pa_device *device, pa_mute_control *control) {
2701 + pa_mute_control *old_control;
2703 + pa_assert(device);
2705 + old_control = device->default_mute_control;
2707 + if (control == old_control)
2711 + pa_mute_control_remove_default_for_device(old_control, device);
2713 + device->default_mute_control = control;
2716 + pa_mute_control_add_default_for_device(control, device);
2718 + if (device->use_default_mute_control)
2719 + set_mute_control_internal(device, control);
2721 diff --git a/src/modules/volume-api/device.h b/src/modules/volume-api/device.h
2722 new file mode 100644
2723 index 0000000..9eac7e9
2725 +++ b/src/modules/volume-api/device.h
2727 +#ifndef foodevicehfoo
2728 +#define foodevicehfoo
2731 + This file is part of PulseAudio.
2733 + Copyright 2014 Intel Corporation
2735 + PulseAudio is free software; you can redistribute it and/or modify
2736 + it under the terms of the GNU Lesser General Public License as published
2737 + by the Free Software Foundation; either version 2.1 of the License,
2738 + or (at your option) any later version.
2740 + PulseAudio is distributed in the hope that it will be useful, but
2741 + WITHOUT ANY WARRANTY; without even the implied warranty of
2742 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2743 + General Public License for more details.
2745 + You should have received a copy of the GNU Lesser General Public License
2746 + along with PulseAudio; if not, write to the Free Software
2747 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2751 +#include <modules/volume-api/volume-api.h>
2753 +#include <pulsecore/dynarray.h>
2755 +typedef struct pa_device pa_device;
2758 + pa_volume_api *volume_api;
2761 + char *description;
2762 + pa_direction_t direction;
2763 + pa_dynarray *device_types;
2764 + pa_proplist *proplist;
2765 + pa_volume_control *volume_control;
2766 + pa_mute_control *mute_control;
2768 + /* The device implementation can provide default volume and mute controls,
2769 + * which are used in case there's no policy module that wants to override
2770 + * the defaults. */
2771 + pa_volume_control *default_volume_control;
2772 + bool use_default_volume_control;
2773 + pa_mute_control *default_mute_control;
2774 + bool use_default_mute_control;
2780 +pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
2781 + const char * const *device_types, unsigned n_device_types);
2782 +void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control);
2783 +void pa_device_unlink(pa_device *device);
2784 +void pa_device_free(pa_device *device);
2786 +/* Called by policy modules. */
2787 +void pa_device_set_volume_control(pa_device *device, pa_volume_control *control);
2788 +void pa_device_set_mute_control(pa_device *device, pa_mute_control *control);
2790 +/* Called by policy modules. Note that pa_device_set_volume_control() and
2791 + * pa_device_set_mute_control() automatically disable the corresponding
2792 + * use_default flags, so these functions are mainly useful for re-enabling the
2794 +void pa_device_set_use_default_volume_control(pa_device *device, bool use);
2795 +void pa_device_set_use_default_mute_control(pa_device *device, bool use);
2797 +/* Called by the device implementation. */
2798 +void pa_device_description_changed(pa_device *device, const char *new_description);
2799 +void pa_device_set_default_volume_control(pa_device *device, pa_volume_control *control);
2800 +void pa_device_set_default_mute_control(pa_device *device, pa_mute_control *control);
2803 diff --git a/src/modules/volume-api/mute-control.c b/src/modules/volume-api/mute-control.c
2804 new file mode 100644
2805 index 0000000..adc008e
2807 +++ b/src/modules/volume-api/mute-control.c
2810 + This file is part of PulseAudio.
2812 + Copyright 2014 Intel Corporation
2814 + PulseAudio is free software; you can redistribute it and/or modify
2815 + it under the terms of the GNU Lesser General Public License as published
2816 + by the Free Software Foundation; either version 2.1 of the License,
2817 + or (at your option) any later version.
2819 + PulseAudio is distributed in the hope that it will be useful, but
2820 + WITHOUT ANY WARRANTY; without even the implied warranty of
2821 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2822 + General Public License for more details.
2824 + You should have received a copy of the GNU Lesser General Public License
2825 + along with PulseAudio; if not, write to the Free Software
2826 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2830 +#ifdef HAVE_CONFIG_H
2831 +#include <config.h>
2834 +#include "mute-control.h"
2836 +#include <modules/volume-api/audio-group.h>
2837 +#include <modules/volume-api/device.h>
2838 +#include <modules/volume-api/sstream.h>
2840 +#include <pulsecore/core-util.h>
2842 +pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) {
2843 + pa_mute_control *control;
2847 + pa_assert(description);
2849 + control = pa_xnew0(pa_mute_control, 1);
2850 + control->volume_api = api;
2851 + control->index = pa_volume_api_allocate_mute_control_index(api);
2852 + pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
2853 + control->description = pa_xstrdup(description);
2854 + control->proplist = pa_proplist_new();
2855 + control->devices = pa_hashmap_new(NULL, NULL);
2856 + control->default_for_devices = pa_hashmap_new(NULL, NULL);
2857 + control->streams = pa_hashmap_new(NULL, NULL);
2858 + control->audio_groups = pa_hashmap_new(NULL, NULL);
2863 +void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
2864 + pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb) {
2865 + const char *prop_key;
2866 + void *state = NULL;
2868 + pa_assert(control);
2869 + pa_assert(initial_mute_is_set || control->set_mute);
2870 + pa_assert(set_initial_mute_cb || !control->set_mute);
2872 + if (initial_mute_is_set)
2873 + control->mute = initial_mute;
2875 + control->mute = false;
2877 + if (set_initial_mute_cb)
2878 + set_initial_mute_cb(control);
2880 + pa_volume_api_add_mute_control(control->volume_api, control);
2882 + control->linked = true;
2884 + pa_log_debug("Created mute control #%u.", control->index);
2885 + pa_log_debug(" Name: %s", control->name);
2886 + pa_log_debug(" Description: %s", control->description);
2887 + pa_log_debug(" Mute: %s", pa_yes_no(control->mute));
2888 + pa_log_debug(" Properties:");
2890 + while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
2891 + pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(control->proplist, prop_key)));
2893 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT], control);
2896 +void pa_mute_control_unlink(pa_mute_control *control) {
2897 + pa_audio_group *group;
2898 + pa_device *device;
2899 + pas_stream *stream;
2901 + pa_assert(control);
2903 + if (control->unlinked) {
2904 + pa_log_debug("Unlinking mute control %s (already unlinked, this is a no-op).", control->name);
2908 + control->unlinked = true;
2910 + pa_log_debug("Unlinking mute control %s.", control->name);
2912 + if (control->linked)
2913 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
2915 + pa_volume_api_remove_mute_control(control->volume_api, control);
2917 + while ((group = pa_hashmap_first(control->audio_groups)))
2918 + pa_audio_group_set_mute_control(group, NULL);
2920 + while ((stream = pa_hashmap_first(control->streams)))
2921 + pas_stream_set_mute_control(stream, NULL);
2923 + while ((device = pa_hashmap_first(control->default_for_devices)))
2924 + pa_device_set_default_mute_control(device, NULL);
2926 + while ((device = pa_hashmap_first(control->devices))) {
2927 + /* Why do we have this assertion here? The concern is that if we call
2928 + * pa_device_set_mute_control() for some device that has the
2929 + * use_default_mute_control flag set, then that flag will be unset as
2930 + * a side effect, and we don't want that side effect. This assertion
2931 + * should be safe, because we just called
2932 + * pa_device_set_default_mute_control(NULL) for each device that this
2933 + * control was the default for, and that should ensure that we don't
2934 + * any more hold any references to devices that used to use this
2935 + * control as the default. */
2936 + pa_assert(!device->use_default_mute_control);
2937 + pa_device_set_mute_control(device, NULL);
2941 +void pa_mute_control_free(pa_mute_control *control) {
2942 + pa_assert(control);
2944 + if (!control->unlinked)
2945 + pa_mute_control_unlink(control);
2947 + if (control->audio_groups) {
2948 + pa_assert(pa_hashmap_isempty(control->audio_groups));
2949 + pa_hashmap_free(control->audio_groups);
2952 + if (control->streams) {
2953 + pa_assert(pa_hashmap_isempty(control->streams));
2954 + pa_hashmap_free(control->streams);
2957 + if (control->default_for_devices) {
2958 + pa_assert(pa_hashmap_isempty(control->default_for_devices));
2959 + pa_hashmap_free(control->default_for_devices);
2962 + if (control->devices) {
2963 + pa_assert(pa_hashmap_isempty(control->devices));
2964 + pa_hashmap_free(control->devices);
2967 + if (control->proplist)
2968 + pa_proplist_free(control->proplist);
2970 + pa_xfree(control->description);
2972 + if (control->name)
2973 + pa_volume_api_unregister_name(control->volume_api, control->name);
2975 + pa_xfree(control);
2978 +void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) {
2979 + pa_assert(control);
2982 + control->owner_audio_group = group;
2985 +static void set_mute_internal(pa_mute_control *control, bool mute) {
2988 + pa_assert(control);
2990 + old_mute = control->mute;
2992 + if (mute == old_mute)
2995 + control->mute = mute;
2997 + if (!control->linked || control->unlinked)
3000 + pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_yes_no(old_mute),
3001 + pa_yes_no(control->mute));
3003 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
3006 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
3009 + pa_assert(control);
3011 + if (!control->set_mute) {
3012 + pa_log_info("Tried to set the mute of mute control %s, but the mute control doesn't support the operation.",
3014 + return -PA_ERR_NOTSUPPORTED;
3017 + if (mute == control->mute)
3020 + control->set_mute_in_progress = true;
3021 + r = control->set_mute(control, mute);
3022 + control->set_mute_in_progress = false;
3025 + set_mute_internal(control, mute);
3030 +void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) {
3031 + char *old_description;
3033 + pa_assert(control);
3034 + pa_assert(new_description);
3036 + old_description = control->description;
3038 + if (pa_streq(new_description, old_description))
3041 + control->description = pa_xstrdup(new_description);
3042 + pa_log_debug("The description of mute control %s changed from \"%s\" to \"%s\".", control->name, old_description,
3044 + pa_xfree(old_description);
3045 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED], control);
3048 +void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute) {
3049 + pa_assert(control);
3051 + if (!control->linked)
3054 + if (control->set_mute_in_progress)
3057 + set_mute_internal(control, new_mute);
3060 +void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) {
3061 + pa_assert(control);
3062 + pa_assert(device);
3064 + pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0);
3067 +void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device) {
3068 + pa_assert(control);
3069 + pa_assert(device);
3071 + pa_assert_se(pa_hashmap_remove(control->devices, device));
3074 +void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device) {
3075 + pa_assert(control);
3076 + pa_assert(device);
3078 + pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0);
3081 +void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device) {
3082 + pa_assert(control);
3083 + pa_assert(device);
3085 + pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
3088 +void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream) {
3089 + pa_assert(control);
3090 + pa_assert(stream);
3092 + pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
3095 +void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream) {
3096 + pa_assert(control);
3097 + pa_assert(stream);
3099 + pa_assert_se(pa_hashmap_remove(control->streams, stream));
3102 +void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group) {
3103 + pa_assert(control);
3106 + pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
3109 +void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group) {
3110 + pa_assert(control);
3113 + pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
3115 diff --git a/src/modules/volume-api/mute-control.h b/src/modules/volume-api/mute-control.h
3116 new file mode 100644
3117 index 0000000..1f70a43
3119 +++ b/src/modules/volume-api/mute-control.h
3121 +#ifndef foomutecontrolhfoo
3122 +#define foomutecontrolhfoo
3125 + This file is part of PulseAudio.
3127 + Copyright 2014 Intel Corporation
3129 + PulseAudio is free software; you can redistribute it and/or modify
3130 + it under the terms of the GNU Lesser General Public License as published
3131 + by the Free Software Foundation; either version 2.1 of the License,
3132 + or (at your option) any later version.
3134 + PulseAudio is distributed in the hope that it will be useful, but
3135 + WITHOUT ANY WARRANTY; without even the implied warranty of
3136 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3137 + General Public License for more details.
3139 + You should have received a copy of the GNU Lesser General Public License
3140 + along with PulseAudio; if not, write to the Free Software
3141 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3145 +#include <modules/volume-api/volume-api.h>
3147 +typedef struct pa_mute_control pa_mute_control;
3149 +struct pa_mute_control {
3150 + pa_volume_api *volume_api;
3153 + char *description;
3154 + pa_proplist *proplist;
3157 + /* If this mute control is the "own mute control" of an audio group, this
3158 + * is set to point to that group, otherwise this is NULL. */
3159 + pa_audio_group *owner_audio_group;
3161 + pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
3162 + pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
3163 + pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
3164 + pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
3168 + bool set_mute_in_progress;
3170 + /* Called from pa_mute_control_set_mute(). The implementation is expected
3171 + * to return a negative error code on failure. May be NULL, if the mute
3172 + * control is read-only. */
3173 + int (*set_mute)(pa_mute_control *control, bool mute);
3178 +pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description);
3180 +typedef void (*pa_mute_control_set_initial_mute_cb_t)(pa_mute_control *control);
3182 +/* initial_mute is the preferred initial mute of the mute control
3183 + * implementation. It may be unset, if the implementation doesn't care about
3184 + * the initial state of the mute control. Read-only mute controls, however,
3185 + * must always set initial_mute.
3187 + * The implementation's initial mute preference may be overridden by policy, if
3188 + * the mute control isn't read-only. When the final initial mute is known, the
3189 + * the implementation is notified via set_initial_mute_cb (the mute can be read
3190 + * from control->mute). set_initial_mute_cb may be NULL, if the mute control is
3192 +void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
3193 + pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb);
3195 +void pa_mute_control_unlink(pa_mute_control *control);
3196 +void pa_mute_control_free(pa_mute_control *control);
3198 +/* Called by audio-group.c only. */
3199 +void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group);
3201 +/* Called by clients and policy modules. */
3202 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
3204 +/* Called by the mute control implementation. */
3205 +void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description);
3206 +void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute);
3208 +/* Called from device.c only. */
3209 +void pa_mute_control_add_device(pa_mute_control *control, pa_device *device);
3210 +void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device);
3211 +void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device);
3212 +void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device);
3214 +/* Called from sstream.c only. */
3215 +void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream);
3216 +void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream);
3218 +/* Called from audio-group.c only. */
3219 +void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group);
3220 +void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group);
3223 diff --git a/src/modules/volume-api/sstream.c b/src/modules/volume-api/sstream.c
3224 new file mode 100644
3225 index 0000000..e3531a8
3227 +++ b/src/modules/volume-api/sstream.c
3230 + This file is part of PulseAudio.
3232 + Copyright 2014 Intel Corporation
3234 + PulseAudio is free software; you can redistribute it and/or modify
3235 + it under the terms of the GNU Lesser General Public License as published
3236 + by the Free Software Foundation; either version 2.1 of the License,
3237 + or (at your option) any later version.
3239 + PulseAudio is distributed in the hope that it will be useful, but
3240 + WITHOUT ANY WARRANTY; without even the implied warranty of
3241 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3242 + General Public License for more details.
3244 + You should have received a copy of the GNU Lesser General Public License
3245 + along with PulseAudio; if not, write to the Free Software
3246 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3250 +#ifdef HAVE_CONFIG_H
3251 +#include <config.h>
3254 +#include "sstream.h"
3256 +#include <modules/volume-api/audio-group.h>
3257 +#include <modules/volume-api/binding.h>
3258 +#include <modules/volume-api/mute-control.h>
3259 +#include <modules/volume-api/volume-control.h>
3261 +#include <pulse/direction.h>
3263 +#include <pulsecore/core-util.h>
3265 +pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) {
3266 + pas_stream *stream;
3270 + pa_assert(description);
3272 + stream = pa_xnew0(pas_stream, 1);
3273 + stream->volume_api = api;
3274 + stream->index = pa_volume_api_allocate_stream_index(api);
3275 + pa_assert_se(pa_volume_api_register_name(api, name, false, &stream->name) >= 0);
3276 + stream->description = pa_xstrdup(description);
3277 + stream->direction = direction;
3278 + stream->proplist = pa_proplist_new();
3279 + stream->use_default_volume_control = true;
3280 + stream->use_default_mute_control = true;
3285 +static void set_volume_control_internal(pas_stream *stream, pa_volume_control *control) {
3286 + pa_volume_control *old_control;
3288 + pa_assert(stream);
3290 + old_control = stream->volume_control;
3292 + if (control == old_control)
3295 + if (old_control) {
3296 + /* If the old control pointed to the own volume control of an audio
3297 + * group, then the stream's audio group for volume needs to be
3298 + * updated. We set it to NULL here, and if it should be non-NULL, that
3299 + * will be fixed very soon (a few lines down). */
3300 + pas_stream_set_audio_group_for_volume(stream, NULL);
3302 + pa_volume_control_remove_stream(old_control, stream);
3305 + stream->volume_control = control;
3308 + pa_volume_control_add_stream(control, stream);
3309 + pas_stream_set_audio_group_for_volume(stream, control->owner_audio_group);
3312 + if (!stream->linked || stream->unlinked)
3315 + pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
3316 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
3318 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
3321 +static void set_mute_control_internal(pas_stream *stream, pa_mute_control *control) {
3322 + pa_mute_control *old_control;
3324 + pa_assert(stream);
3326 + old_control = stream->mute_control;
3328 + if (control == old_control)
3331 + if (old_control) {
3332 + /* If the old control pointed to the own mute control of an audio
3333 + * group, then the stream's audio group for mute needs to be updated.
3334 + * We set it to NULL here, and if it should be non-NULL, that will be
3335 + * fixed very soon (a few lines down). */
3336 + pas_stream_set_audio_group_for_mute(stream, NULL);
3338 + pa_mute_control_remove_stream(old_control, stream);
3341 + stream->mute_control = control;
3344 + pa_mute_control_add_stream(control, stream);
3345 + pas_stream_set_audio_group_for_mute(stream, control->owner_audio_group);
3348 + if (!stream->linked || stream->unlinked)
3351 + pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
3352 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
3354 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
3357 +void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
3358 + const char *prop_key;
3359 + void *state = NULL;
3361 + pa_assert(stream);
3362 + pa_assert(!stream->create_own_volume_control || stream->delete_own_volume_control);
3363 + pa_assert(!stream->create_own_mute_control || stream->delete_own_mute_control);
3365 + if (initial_properties)
3366 + pa_proplist_update(stream->proplist, PA_UPDATE_REPLACE, initial_properties);
3368 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], stream);
3370 + if (stream->use_default_volume_control)
3371 + set_volume_control_internal(stream, stream->own_volume_control);
3373 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], stream);
3375 + if (stream->use_default_mute_control)
3376 + set_mute_control_internal(stream, stream->own_mute_control);
3378 + pa_volume_api_add_stream(stream->volume_api, stream);
3380 + stream->linked = true;
3382 + pa_log_debug("Created stream #%u.", stream->index);
3383 + pa_log_debug(" Name: %s", stream->name);
3384 + pa_log_debug(" Description: %s", stream->description);
3385 + pa_log_debug(" Direction: %s", pa_direction_to_string(stream->direction));
3386 + pa_log_debug(" Volume control: %s", stream->volume_control ? stream->volume_control->name : "(unset)");
3387 + pa_log_debug(" Mute control: %s", stream->mute_control ? stream->mute_control->name : "(unset)");
3388 + pa_log_debug(" Properties:");
3390 + while ((prop_key = pa_proplist_iterate(stream->proplist, &state)))
3391 + pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(stream->proplist, prop_key)));
3393 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], stream);
3396 +void pas_stream_unlink(pas_stream *stream) {
3397 + pa_assert(stream);
3399 + if (stream->unlinked) {
3400 + pa_log_debug("Unlinking stream %s (already unlinked, this is a no-op).", stream->name);
3404 + stream->unlinked = true;
3406 + pa_log_debug("Unlinking stream %s.", stream->name);
3408 + if (stream->linked)
3409 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
3411 + pa_volume_api_remove_stream(stream->volume_api, stream);
3413 + pas_stream_set_audio_group_for_mute(stream, NULL);
3414 + pas_stream_set_audio_group_for_volume(stream, NULL);
3415 + pas_stream_set_mute_control(stream, NULL);
3416 + pas_stream_set_volume_control(stream, NULL);
3417 + pas_stream_set_have_own_mute_control(stream, false);
3418 + pas_stream_set_have_own_volume_control(stream, false);
3421 +void pas_stream_free(pas_stream *stream) {
3422 + pa_assert(stream);
3424 + if (!stream->unlinked)
3425 + pas_stream_unlink(stream);
3427 + if (stream->proplist)
3428 + pa_proplist_free(stream->proplist);
3430 + pa_xfree(stream->description);
3433 + pa_volume_api_unregister_name(stream->volume_api, stream->name);
3438 +int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) {
3439 + pa_assert(stream);
3441 + if (have == stream->have_own_volume_control)
3445 + pa_assert(!stream->own_volume_control);
3447 + if (!stream->create_own_volume_control) {
3448 + pa_log_debug("Stream %s doesn't support own volume control.", stream->name);
3449 + return -PA_ERR_NOTSUPPORTED;
3452 + stream->own_volume_control = stream->create_own_volume_control(stream);
3454 + stream->delete_own_volume_control(stream);
3455 + stream->own_volume_control = NULL;
3458 + stream->have_own_volume_control = have;
3463 +int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) {
3464 + pa_assert(stream);
3466 + if (have == stream->have_own_mute_control)
3470 + pa_assert(!stream->own_mute_control);
3472 + if (!stream->create_own_mute_control) {
3473 + pa_log_debug("Stream %s doesn't support own mute control.", stream->name);
3474 + return -PA_ERR_NOTSUPPORTED;
3477 + stream->own_mute_control = stream->create_own_mute_control(stream);
3479 + stream->delete_own_mute_control(stream);
3480 + stream->own_mute_control = NULL;
3483 + stream->have_own_mute_control = have;
3488 +void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) {
3489 + pa_assert(stream);
3491 + stream->use_default_volume_control = false;
3493 + if (stream->volume_control_binding) {
3494 + pa_binding_free(stream->volume_control_binding);
3495 + stream->volume_control_binding = NULL;
3498 + set_volume_control_internal(stream, control);
3501 +void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
3502 + pa_assert(stream);
3504 + stream->use_default_mute_control = false;
3506 + if (stream->mute_control_binding) {
3507 + pa_binding_free(stream->mute_control_binding);
3508 + stream->mute_control_binding = NULL;
3511 + set_mute_control_internal(stream, control);
3514 +void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info) {
3515 + pa_binding_owner_info owner_info = {
3516 + .userdata = stream,
3517 + .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
3520 + pa_assert(stream);
3521 + pa_assert(target_info);
3523 + stream->use_default_volume_control = false;
3525 + if (stream->volume_control_binding)
3526 + pa_binding_free(stream->volume_control_binding);
3528 + stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
3531 +void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info) {
3532 + pa_binding_owner_info owner_info = {
3533 + .userdata = stream,
3534 + .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
3537 + pa_assert(stream);
3538 + pa_assert(target_info);
3540 + stream->use_default_mute_control = false;
3542 + if (stream->mute_control_binding)
3543 + pa_binding_free(stream->mute_control_binding);
3545 + stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
3548 +void pas_stream_description_changed(pas_stream *stream, const char *new_description) {
3549 + char *old_description;
3551 + pa_assert(stream);
3552 + pa_assert(new_description);
3554 + old_description = stream->description;
3556 + if (pa_streq(new_description, old_description))
3559 + stream->description = pa_xstrdup(new_description);
3560 + pa_log_debug("The description of stream %s changed from \"%s\" to \"%s\".", stream->name, old_description,
3562 + pa_xfree(old_description);
3563 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
3566 +void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) {
3567 + pa_assert(stream);
3569 + if (group == stream->audio_group_for_volume)
3572 + if (stream->audio_group_for_volume)
3573 + pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream);
3575 + stream->audio_group_for_volume = group;
3578 + pa_audio_group_add_volume_stream(group, stream);
3581 +void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) {
3582 + pa_assert(stream);
3584 + if (group == stream->audio_group_for_mute)
3587 + if (stream->audio_group_for_mute)
3588 + pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream);
3590 + stream->audio_group_for_mute = group;
3593 + pa_audio_group_add_mute_stream(group, stream);
3595 diff --git a/src/modules/volume-api/sstream.h b/src/modules/volume-api/sstream.h
3596 new file mode 100644
3597 index 0000000..a65b34c
3599 +++ b/src/modules/volume-api/sstream.h
3601 +#ifndef foosstreamhfoo
3602 +#define foosstreamhfoo
3605 + This file is part of PulseAudio.
3607 + Copyright 2014 Intel Corporation
3609 + PulseAudio is free software; you can redistribute it and/or modify
3610 + it under the terms of the GNU Lesser General Public License as published
3611 + by the Free Software Foundation; either version 2.1 of the License,
3612 + or (at your option) any later version.
3614 + PulseAudio is distributed in the hope that it will be useful, but
3615 + WITHOUT ANY WARRANTY; without even the implied warranty of
3616 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3617 + General Public License for more details.
3619 + You should have received a copy of the GNU Lesser General Public License
3620 + along with PulseAudio; if not, write to the Free Software
3621 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3625 +#include <modules/volume-api/volume-api.h>
3627 +/* We use the "pas_" prefix in pas_stream, because there's already pa_stream in
3628 + * the client API, and there's no good alternative term for streams. The 's' in
3629 + * "pas" means "server", i.e. the point is that this stuff is for servers,
3630 + * while pa_stream is for clients. */
3632 +typedef struct pas_stream pas_stream;
3634 +struct pas_stream {
3635 + pa_volume_api *volume_api;
3638 + char *description;
3639 + pa_direction_t direction;
3640 + pa_proplist *proplist;
3641 + pa_volume_control *volume_control;
3642 + pa_mute_control *mute_control;
3643 + bool use_default_volume_control;
3644 + bool use_default_mute_control;
3645 + bool have_own_volume_control;
3646 + bool have_own_mute_control;
3647 + pa_volume_control *own_volume_control;
3648 + pa_mute_control *own_mute_control;
3650 + pa_binding *volume_control_binding;
3651 + pa_binding *mute_control_binding;
3652 + pa_audio_group *audio_group_for_volume;
3653 + pa_audio_group *audio_group_for_mute;
3658 + /* Called when the own volume control is enabled. The callback
3659 + * implementation should return a new linked volume control object. The
3660 + * callback may be NULL, in which case the own volume control can't be
3662 + pa_volume_control *(*create_own_volume_control)(pas_stream *stream);
3664 + /* Called when the own volume control is disabled. The implementation
3665 + * should free stream->own_volume_control. The callback may be NULL only if
3666 + * create_own_volume_control is NULL also. */
3667 + void (*delete_own_volume_control)(pas_stream *stream);
3669 + /* Called when the own mute control is enabled. The callback implementation
3670 + * should return a new linked mute control object. The callback may be
3671 + * NULL, in which case the own mute control can't be enabled. */
3672 + pa_mute_control *(*create_own_mute_control)(pas_stream *stream);
3674 + /* Called when the own mute control is disabled. The implementation should
3675 + * free stream->own_mute_control. The callback may be NULL only if
3676 + * create_own_mute_control is NULL also. */
3677 + void (*delete_own_mute_control)(pas_stream *stream);
3682 +pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction);
3683 +void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties);
3684 +void pas_stream_unlink(pas_stream *stream);
3685 +void pas_stream_free(pas_stream *stream);
3687 +/* Called by the stream implementation and possibly by policy modules.
3688 + * Enabling own controls may fail (the stream may not support own controls),
3689 + * disabling will never fail. */
3690 +int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have);
3691 +int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have);
3693 +/* Called by policy modules. */
3694 +void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control);
3695 +void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control);
3696 +void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info);
3697 +void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info);
3699 +/* Called by the stream implementation. */
3700 +void pas_stream_description_changed(pas_stream *stream, const char *new_description);
3702 +/* Called by audio-group.c only. Adding a stream to an audio group happens
3703 + * implicitly when the volume or mute control of a stream is set to point to
3704 + * the own control of an audio group. */
3705 +void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group);
3706 +void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group);
3709 diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c
3710 new file mode 100644
3711 index 0000000..2bd0053
3713 +++ b/src/modules/volume-api/stream-creator.c
3716 + This file is part of PulseAudio.
3718 + Copyright 2014 Intel Corporation
3720 + PulseAudio is free software; you can redistribute it and/or modify
3721 + it under the terms of the GNU Lesser General Public License as published
3722 + by the Free Software Foundation; either version 2.1 of the License,
3723 + or (at your option) any later version.
3725 + PulseAudio is distributed in the hope that it will be useful, but
3726 + WITHOUT ANY WARRANTY; without even the implied warranty of
3727 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3728 + General Public License for more details.
3730 + You should have received a copy of the GNU Lesser General Public License
3731 + along with PulseAudio; if not, write to the Free Software
3732 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3736 +#ifdef HAVE_CONFIG_H
3737 +#include <config.h>
3740 +#include "stream-creator.h"
3742 +#include <modules/volume-api/sstream.h>
3743 +#include <modules/volume-api/mute-control.h>
3744 +#include <modules/volume-api/volume-control.h>
3746 +#include <pulsecore/core-util.h>
3747 +#include <pulsecore/i18n.h>
3749 +struct pa_stream_creator {
3750 + pa_volume_api *volume_api;
3751 + pa_hashmap *streams; /* pa_sink_input/pa_source_output -> struct stream */
3752 + pa_hook_slot *sink_input_put_slot;
3753 + pa_hook_slot *sink_input_unlink_slot;
3754 + pa_hook_slot *source_output_put_slot;
3755 + pa_hook_slot *source_output_unlink_slot;
3759 + STREAM_TYPE_SINK_INPUT,
3760 + STREAM_TYPE_SOURCE_OUTPUT,
3764 + pa_stream_creator *creator;
3765 + enum stream_type type;
3766 + pa_sink_input *sink_input;
3767 + pa_source_output *source_output;
3768 + pa_client *client;
3769 + pas_stream *stream;
3773 + pa_hook_slot *proplist_changed_slot;
3774 + pa_hook_slot *client_proplist_changed_slot;
3775 + pa_hook_slot *volume_changed_slot;
3776 + pa_hook_slot *mute_changed_slot;
3779 +static char *get_stream_volume_and_mute_control_description_malloc(struct stream *stream) {
3780 + const char *application_name = NULL;
3781 + char *description;
3783 + pa_assert(stream);
3785 + if (stream->client)
3786 + application_name = pa_proplist_gets(stream->client->proplist, PA_PROP_APPLICATION_NAME);
3788 + if (application_name)
3789 + description = pa_sprintf_malloc("%s: %s", application_name, stream->stream->description);
3791 + description = pa_xstrdup(stream->stream->description);
3793 + return description;
3796 +static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
3797 + struct stream *stream;
3798 + pa_bvolume bvolume;
3799 + pa_cvolume cvolume;
3801 + pa_assert(control);
3802 + pa_assert(volume);
3804 + stream = control->userdata;
3806 + switch (stream->type) {
3807 + case STREAM_TYPE_SINK_INPUT:
3808 + pa_bvolume_from_cvolume(&bvolume, &stream->sink_input->volume, &stream->sink_input->channel_map);
3811 + case STREAM_TYPE_SOURCE_OUTPUT:
3812 + pa_bvolume_from_cvolume(&bvolume, &stream->source_output->volume, &stream->source_output->channel_map);
3817 + bvolume.volume = volume->volume;
3820 + pa_bvolume_copy_balance(&bvolume, volume);
3822 + pa_bvolume_to_cvolume(&bvolume, &cvolume);
3824 + switch (stream->type) {
3825 + case STREAM_TYPE_SINK_INPUT:
3826 + pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
3829 + case STREAM_TYPE_SOURCE_OUTPUT:
3830 + pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
3837 +static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
3838 + struct stream *stream = userdata;
3839 + pa_sink_input *input = NULL;
3840 + pa_source_output *output = NULL;
3841 + pa_bvolume bvolume;
3843 + pa_assert(stream);
3844 + pa_assert(call_data);
3846 + switch (stream->type) {
3847 + case STREAM_TYPE_SINK_INPUT:
3848 + input = call_data;
3851 + case STREAM_TYPE_SOURCE_OUTPUT:
3852 + output = call_data;
3856 + if ((input && input != stream->sink_input) || (output && output != stream->source_output))
3857 + return PA_HOOK_OK;
3860 + pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map);
3862 + pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map);
3864 + pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true);
3866 + return PA_HOOK_OK;
3869 +static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
3870 + struct stream *stream;
3871 + pa_cvolume cvolume;
3873 + pa_assert(control);
3875 + stream = control->userdata;
3876 + pa_bvolume_to_cvolume(&control->volume, &cvolume);
3878 + switch (stream->type) {
3879 + case STREAM_TYPE_SINK_INPUT:
3880 + pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
3883 + case STREAM_TYPE_SOURCE_OUTPUT:
3884 + pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
3889 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
3890 + struct stream *stream;
3892 + pa_assert(control);
3894 + stream = control->userdata;
3896 + switch (stream->type) {
3897 + case STREAM_TYPE_SINK_INPUT:
3898 + pa_sink_input_set_mute(stream->sink_input, mute, true);
3901 + case STREAM_TYPE_SOURCE_OUTPUT:
3902 + pa_source_output_set_mute(stream->source_output, mute, true);
3909 +static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
3910 + struct stream *stream = userdata;
3911 + pa_sink_input *input = NULL;
3912 + pa_source_output *output = NULL;
3915 + pa_assert(stream);
3916 + pa_assert(call_data);
3918 + switch (stream->type) {
3919 + case STREAM_TYPE_SINK_INPUT:
3920 + input = call_data;
3923 + case STREAM_TYPE_SOURCE_OUTPUT:
3924 + output = call_data;
3928 + if ((input && input != stream->sink_input) || (output && output != stream->source_output))
3929 + return PA_HOOK_OK;
3932 + mute = input->muted;
3934 + mute = output->muted;
3936 + pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
3938 + return PA_HOOK_OK;
3941 +static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
3942 + struct stream *stream;
3944 + pa_assert(control);
3946 + stream = control->userdata;
3948 + switch (stream->type) {
3949 + case STREAM_TYPE_SINK_INPUT:
3950 + pa_sink_input_set_mute(stream->sink_input, control->mute, true);
3953 + case STREAM_TYPE_SOURCE_OUTPUT:
3954 + pa_source_output_set_mute(stream->source_output, control->mute, true);
3959 +static const char *get_sink_input_description(pa_sink_input *input) {
3960 + const char *description;
3964 + description = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_NAME);
3966 + return description;
3971 +static const char *get_source_output_description(pa_source_output *output) {
3972 + const char *description;
3974 + pa_assert(output);
3976 + description = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_NAME);
3978 + return description;
3983 +static pa_volume_control *stream_create_own_volume_control_cb(pas_stream *s) {
3984 + struct stream *stream;
3985 + const char *name = NULL;
3986 + char *description;
3987 + pa_volume_control *control;
3988 + pa_bvolume volume;
3992 + stream = s->userdata;
3994 + switch (stream->type) {
3995 + case STREAM_TYPE_SINK_INPUT:
3996 + name = "sink-input-volume-control";
3999 + case STREAM_TYPE_SOURCE_OUTPUT:
4000 + name = "source-output-volume-control";
4004 + description = get_stream_volume_and_mute_control_description_malloc(stream);
4005 + control = pa_volume_control_new(stream->creator->volume_api, name, description, true, false);
4006 + pa_xfree(description);
4007 + control->set_volume = volume_control_set_volume_cb;
4008 + control->userdata = stream;
4010 + pa_assert(!stream->volume_changed_slot);
4012 + switch (stream->type) {
4013 + case STREAM_TYPE_SINK_INPUT:
4014 + stream->volume_changed_slot =
4015 + pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
4016 + sink_input_or_source_output_volume_changed_cb, stream);
4017 + pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, &stream->sink_input->channel_map);
4020 + case STREAM_TYPE_SOURCE_OUTPUT:
4021 + stream->volume_changed_slot =
4022 + pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
4023 + PA_HOOK_NORMAL, sink_input_or_source_output_volume_changed_cb, stream);
4024 + pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, &stream->source_output->channel_map);
4028 + pa_volume_control_put(control, &volume, volume_control_set_initial_volume_cb);
4033 +static void stream_delete_own_volume_control_cb(pas_stream *s) {
4034 + struct stream *stream;
4038 + stream = s->userdata;
4039 + pa_hook_slot_free(stream->volume_changed_slot);
4040 + stream->volume_changed_slot = NULL;
4041 + pa_volume_control_free(s->own_volume_control);
4044 +static pa_mute_control *stream_create_own_mute_control_cb(pas_stream *s) {
4045 + struct stream *stream;
4046 + const char *name = NULL;
4047 + char *description;
4048 + pa_mute_control *control;
4049 + bool mute = false;
4053 + stream = s->userdata;
4055 + switch (stream->type) {
4056 + case STREAM_TYPE_SINK_INPUT:
4057 + name = "sink-input-mute-control";
4060 + case STREAM_TYPE_SOURCE_OUTPUT:
4061 + name = "source-output-mute-control";
4065 + description = get_stream_volume_and_mute_control_description_malloc(stream);
4066 + control = pa_mute_control_new(stream->creator->volume_api, name, description);
4067 + pa_xfree(description);
4068 + control->set_mute = mute_control_set_mute_cb;
4069 + control->userdata = stream;
4071 + pa_assert(!stream->mute_changed_slot);
4073 + switch (stream->type) {
4074 + case STREAM_TYPE_SINK_INPUT:
4075 + stream->mute_changed_slot =
4076 + pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
4077 + sink_input_or_source_output_mute_changed_cb, stream);
4078 + mute = stream->sink_input->muted;
4081 + case STREAM_TYPE_SOURCE_OUTPUT:
4082 + stream->mute_changed_slot =
4083 + pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
4084 + PA_HOOK_NORMAL, sink_input_or_source_output_mute_changed_cb, stream);
4085 + mute = stream->source_output->muted;
4089 + pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb);
4094 +static void stream_delete_own_mute_control_cb(pas_stream *s) {
4095 + struct stream *stream;
4099 + stream = s->userdata;
4100 + pa_hook_slot_free(stream->mute_changed_slot);
4101 + stream->mute_changed_slot = NULL;
4102 + pa_mute_control_free(s->own_mute_control);
4105 +static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
4106 + struct stream *stream = userdata;
4107 + pa_sink_input *input = NULL;
4108 + pa_source_output *output = NULL;
4109 + const char *new_stream_description = NULL;
4110 + char *new_control_description;
4112 + pa_assert(stream);
4113 + pa_assert(call_data);
4115 + switch (stream->type) {
4116 + case STREAM_TYPE_SINK_INPUT:
4117 + input = call_data;
4119 + if (input != stream->sink_input)
4120 + return PA_HOOK_OK;
4122 + new_stream_description = get_sink_input_description(input);
4123 + if (!new_stream_description)
4124 + new_stream_description = stream->stream->name;
4127 + case STREAM_TYPE_SOURCE_OUTPUT:
4128 + output = call_data;
4130 + if (output != stream->source_output)
4131 + return PA_HOOK_OK;
4133 + new_stream_description = get_source_output_description(output);
4134 + if (!new_stream_description)
4135 + new_stream_description = stream->stream->name;
4139 + pas_stream_description_changed(stream->stream, new_stream_description);
4141 + new_control_description = get_stream_volume_and_mute_control_description_malloc(stream);
4143 + if (stream->stream->own_volume_control)
4144 + pa_volume_control_description_changed(stream->stream->own_volume_control, new_control_description);
4146 + if (stream->stream->own_mute_control)
4147 + pa_mute_control_description_changed(stream->stream->own_mute_control, new_control_description);
4149 + pa_xfree(new_control_description);
4151 + return PA_HOOK_OK;
4154 +static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
4155 + struct stream *stream = userdata;
4156 + pa_client *client = call_data;
4157 + char *description;
4159 + pa_assert(stream);
4160 + pa_assert(client);
4162 + if (client != stream->client)
4163 + return PA_HOOK_OK;
4165 + description = get_stream_volume_and_mute_control_description_malloc(stream);
4167 + if (stream->stream->own_volume_control)
4168 + pa_volume_control_description_changed(stream->stream->own_volume_control, description);
4170 + if (stream->stream->own_mute_control)
4171 + pa_mute_control_description_changed(stream->stream->own_mute_control, description);
4173 + pa_xfree(description);
4175 + return PA_HOOK_OK;
4178 +static struct stream *stream_new(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
4179 + struct stream *stream;
4180 + const char *name = NULL;
4181 + const char *description = NULL;
4182 + pa_direction_t direction = PA_DIRECTION_OUTPUT;
4184 + pa_assert(creator);
4185 + pa_assert(core_stream);
4187 + stream = pa_xnew0(struct stream, 1);
4188 + stream->creator = creator;
4189 + stream->type = type;
4192 + case STREAM_TYPE_SINK_INPUT:
4193 + stream->sink_input = core_stream;
4194 + stream->client = stream->sink_input->client;
4195 + name = "sink-input-stream";
4197 + description = get_sink_input_description(stream->sink_input);
4199 + description = name;
4201 + direction = PA_DIRECTION_OUTPUT;
4204 + case STREAM_TYPE_SOURCE_OUTPUT:
4205 + stream->source_output = core_stream;
4206 + stream->client = stream->source_output->client;
4207 + name = "source-output-stream";
4209 + description = get_source_output_description(stream->source_output);
4211 + description = name;
4213 + direction = PA_DIRECTION_INPUT;
4217 + stream->stream = pas_stream_new(creator->volume_api, name, description, direction);
4218 + stream->stream->create_own_volume_control = stream_create_own_volume_control_cb;
4219 + stream->stream->delete_own_volume_control = stream_delete_own_volume_control_cb;
4220 + stream->stream->create_own_mute_control = stream_create_own_mute_control_cb;
4221 + stream->stream->delete_own_mute_control = stream_delete_own_mute_control_cb;
4222 + stream->stream->userdata = stream;
4223 + pas_stream_set_have_own_volume_control(stream->stream, true);
4224 + pas_stream_set_have_own_mute_control(stream->stream, true);
4227 + case STREAM_TYPE_SINK_INPUT:
4228 + stream->proplist_changed_slot =
4229 + pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
4230 + sink_input_or_source_output_proplist_changed_cb, stream);
4233 + case STREAM_TYPE_SOURCE_OUTPUT:
4234 + stream->proplist_changed_slot =
4235 + pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
4236 + PA_HOOK_NORMAL, sink_input_or_source_output_proplist_changed_cb, stream);
4240 + stream->client_proplist_changed_slot =
4241 + pa_hook_connect(&stream->creator->volume_api->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
4242 + PA_HOOK_NORMAL, client_proplist_changed_cb, stream);
4247 +static void stream_put(struct stream *stream) {
4248 + pa_proplist *proplist = NULL;
4250 + pa_assert(stream);
4252 + switch (stream->type) {
4253 + case STREAM_TYPE_SINK_INPUT:
4254 + proplist = stream->sink_input->proplist;
4257 + case STREAM_TYPE_SOURCE_OUTPUT:
4258 + proplist = stream->source_output->proplist;
4262 + pas_stream_put(stream->stream, proplist);
4265 +static void stream_unlink(struct stream *stream) {
4266 + pa_assert(stream);
4268 + if (stream->unlinked)
4271 + stream->unlinked = true;
4273 + if (stream->stream)
4274 + pas_stream_unlink(stream->stream);
4277 +static void stream_free(struct stream *stream) {
4278 + pa_assert(stream);
4280 + if (!stream->unlinked)
4281 + stream_unlink(stream);
4283 + if (stream->client_proplist_changed_slot)
4284 + pa_hook_slot_free(stream->client_proplist_changed_slot);
4286 + if (stream->proplist_changed_slot)
4287 + pa_hook_slot_free(stream->proplist_changed_slot);
4289 + if (stream->stream)
4290 + pas_stream_free(stream->stream);
4295 +static void create_stream(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
4296 + struct stream *stream;
4298 + pa_assert(creator);
4299 + pa_assert(core_stream);
4301 + stream = stream_new(creator, type, core_stream);
4302 + pa_hashmap_put(creator->streams, core_stream, stream);
4303 + stream_put(stream);
4306 +static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *userdata) {
4307 + pa_stream_creator *creator = userdata;
4308 + pa_sink_input *input = call_data;
4310 + pa_assert(creator);
4313 + create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
4315 + return PA_HOOK_OK;
4318 +static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, void *userdata) {
4319 + pa_stream_creator *creator = userdata;
4320 + pa_sink_input *input = call_data;
4322 + pa_assert(creator);
4325 + pa_hashmap_remove_and_free(creator->streams, input);
4327 + return PA_HOOK_OK;
4330 +static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *userdata) {
4331 + pa_stream_creator *creator = userdata;
4332 + pa_source_output *output = call_data;
4334 + pa_assert(creator);
4335 + pa_assert(output);
4337 + create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
4339 + return PA_HOOK_OK;
4342 +static pa_hook_result_t source_output_unlink_cb(void *hook_data, void *call_data, void *userdata) {
4343 + pa_stream_creator *creator = userdata;
4344 + pa_source_output *output = call_data;
4346 + pa_assert(creator);
4347 + pa_assert(output);
4349 + pa_hashmap_remove_and_free(creator->streams, output);
4351 + return PA_HOOK_OK;
4354 +pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) {
4355 + pa_stream_creator *creator;
4357 + pa_sink_input *input;
4358 + pa_source_output *output;
4362 + creator = pa_xnew0(pa_stream_creator, 1);
4363 + creator->volume_api = api;
4364 + creator->streams = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) stream_free);
4365 + creator->sink_input_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_NORMAL,
4366 + sink_input_put_cb, creator);
4367 + creator->sink_input_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL,
4368 + sink_input_unlink_cb, creator);
4369 + creator->source_output_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
4370 + source_output_put_cb, creator);
4371 + creator->source_output_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL,
4372 + source_output_unlink_cb, creator);
4374 + PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx)
4375 + create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
4377 + PA_IDXSET_FOREACH(output, api->core->source_outputs, idx)
4378 + create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
4383 +void pa_stream_creator_free(pa_stream_creator *creator) {
4384 + pa_assert(creator);
4386 + if (creator->streams)
4387 + pa_hashmap_remove_all(creator->streams);
4389 + if (creator->source_output_unlink_slot)
4390 + pa_hook_slot_free(creator->source_output_unlink_slot);
4392 + if (creator->source_output_put_slot)
4393 + pa_hook_slot_free(creator->source_output_put_slot);
4395 + if (creator->sink_input_unlink_slot)
4396 + pa_hook_slot_free(creator->sink_input_unlink_slot);
4398 + if (creator->sink_input_put_slot)
4399 + pa_hook_slot_free(creator->sink_input_put_slot);
4401 + if (creator->streams)
4402 + pa_hashmap_free(creator->streams);
4404 + pa_xfree(creator);
4406 diff --git a/src/modules/volume-api/stream-creator.h b/src/modules/volume-api/stream-creator.h
4407 new file mode 100644
4408 index 0000000..97a03a4
4410 +++ b/src/modules/volume-api/stream-creator.h
4412 +#ifndef foostreamcreatorhfoo
4413 +#define foostreamcreatorhfoo
4416 + This file is part of PulseAudio.
4418 + Copyright 2014 Intel Corporation
4420 + PulseAudio is free software; you can redistribute it and/or modify
4421 + it under the terms of the GNU Lesser General Public License as published
4422 + by the Free Software Foundation; either version 2.1 of the License,
4423 + or (at your option) any later version.
4425 + PulseAudio is distributed in the hope that it will be useful, but
4426 + WITHOUT ANY WARRANTY; without even the implied warranty of
4427 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4428 + General Public License for more details.
4430 + You should have received a copy of the GNU Lesser General Public License
4431 + along with PulseAudio; if not, write to the Free Software
4432 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4436 +#include <modules/volume-api/volume-api.h>
4438 +typedef struct pa_stream_creator pa_stream_creator;
4440 +pa_stream_creator *pa_stream_creator_new(pa_volume_api *api);
4441 +void pa_stream_creator_free(pa_stream_creator *creator);
4444 diff --git a/src/modules/volume-api/volume-api.c b/src/modules/volume-api/volume-api.c
4445 new file mode 100644
4446 index 0000000..9abea7e
4448 +++ b/src/modules/volume-api/volume-api.c
4451 + This file is part of PulseAudio.
4453 + Copyright 2014 Intel Corporation
4455 + PulseAudio is free software; you can redistribute it and/or modify
4456 + it under the terms of the GNU Lesser General Public License as published
4457 + by the Free Software Foundation; either version 2.1 of the License,
4458 + or (at your option) any later version.
4460 + PulseAudio is distributed in the hope that it will be useful, but
4461 + WITHOUT ANY WARRANTY; without even the implied warranty of
4462 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4463 + General Public License for more details.
4465 + You should have received a copy of the GNU Lesser General Public License
4466 + along with PulseAudio; if not, write to the Free Software
4467 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4471 +#ifdef HAVE_CONFIG_H
4472 +#include <config.h>
4475 +#include "volume-api.h"
4477 +#include <modules/volume-api/audio-group.h>
4478 +#include <modules/volume-api/binding.h>
4479 +#include <modules/volume-api/device.h>
4480 +#include <modules/volume-api/device-creator.h>
4481 +#include <modules/volume-api/sstream.h>
4482 +#include <modules/volume-api/stream-creator.h>
4483 +#include <modules/volume-api/volume-control.h>
4485 +#include <pulsecore/core-util.h>
4486 +#include <pulsecore/shared.h>
4488 +static pa_volume_api *volume_api_new(pa_core *core);
4489 +static void volume_api_free(pa_volume_api *api);
4491 +pa_volume_api *pa_volume_api_get(pa_core *core) {
4492 + pa_volume_api *api;
4496 + api = pa_shared_get(core, "volume-api");
4499 + pa_volume_api_ref(api);
4501 + api = volume_api_new(core);
4502 + pa_assert_se(pa_shared_set(core, "volume-api", api) >= 0);
4508 +pa_volume_api *pa_volume_api_ref(pa_volume_api *api) {
4516 +void pa_volume_api_unref(pa_volume_api *api) {
4518 + pa_assert(api->refcnt > 0);
4522 + if (api->refcnt == 0) {
4523 + pa_assert_se(pa_shared_remove(api->core, "volume-api") >= 0);
4524 + volume_api_free(api);
4528 +void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
4532 + pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0);
4534 + pa_log_debug("Added binding target type %s.", type->name);
4536 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type);
4539 +void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
4543 + pa_log_debug("Removing binding target type %s.", type->name);
4545 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type);
4547 + pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name));
4550 +static void create_builtin_binding_target_types(pa_volume_api *api) {
4551 + pa_binding_target_type *type;
4555 + type = pa_audio_group_create_binding_target_type(api);
4556 + pa_volume_api_add_binding_target_type(api, type);
4559 +static void delete_builtin_binding_target_types(pa_volume_api *api) {
4560 + pa_binding_target_type *type;
4564 + type = pa_hashmap_get(api->binding_target_types, PA_AUDIO_GROUP_BINDING_TARGET_TYPE);
4565 + pa_volume_api_remove_binding_target_type(api, type);
4568 +static void create_objects_defer_event_cb(pa_mainloop_api *mainloop_api, pa_defer_event *event, void *userdata) {
4569 + pa_volume_api *volume_api = userdata;
4571 + pa_assert(volume_api);
4572 + pa_assert(event == volume_api->create_objects_defer_event);
4574 + mainloop_api->defer_free(event);
4575 + volume_api->create_objects_defer_event = NULL;
4577 + volume_api->device_creator = pa_device_creator_new(volume_api);
4578 + volume_api->stream_creator = pa_stream_creator_new(volume_api);
4581 +static pa_volume_api *volume_api_new(pa_core *core) {
4582 + pa_volume_api *api;
4587 + api = pa_xnew0(pa_volume_api, 1);
4590 + api->binding_target_types = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4591 + api->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
4592 + api->volume_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4593 + api->mute_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4594 + api->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4595 + api->streams = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4596 + api->audio_groups = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4598 + for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
4599 + pa_hook_init(&api->hooks[i], api);
4601 + create_builtin_binding_target_types(api);
4603 + /* We delay the object creation to ensure that policy modules have a chance
4604 + * to affect the initialization of the objects. If we created the objects
4605 + * immediately, policy modules wouldn't have a chance of connecting to the
4606 + * object creation hooks before the objects are created. */
4607 + api->create_objects_defer_event = core->mainloop->defer_new(core->mainloop, create_objects_defer_event_cb, api);
4609 + pa_log_debug("Created a pa_volume_api object.");
4614 +static void volume_api_free(pa_volume_api *api) {
4618 + pa_assert(api->refcnt == 0);
4620 + pa_log_debug("Freeing the pa_volume_api object.");
4622 + if (api->stream_creator)
4623 + pa_stream_creator_free(api->stream_creator);
4625 + if (api->device_creator)
4626 + pa_device_creator_free(api->device_creator);
4628 + if (api->create_objects_defer_event)
4629 + api->core->mainloop->defer_free(api->create_objects_defer_event);
4631 + if (api->binding_target_types)
4632 + delete_builtin_binding_target_types(api);
4634 + for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
4635 + pa_hook_done(&api->hooks[i]);
4637 + if (api->audio_groups) {
4638 + pa_assert(pa_hashmap_isempty(api->audio_groups));
4639 + pa_hashmap_free(api->audio_groups);
4642 + if (api->streams) {
4643 + pa_assert(pa_hashmap_isempty(api->streams));
4644 + pa_hashmap_free(api->streams);
4647 + if (api->devices) {
4648 + pa_assert(pa_hashmap_isempty(api->devices));
4649 + pa_hashmap_free(api->devices);
4652 + if (api->mute_controls) {
4653 + pa_assert(pa_hashmap_isempty(api->mute_controls));
4654 + pa_hashmap_free(api->mute_controls);
4657 + if (api->volume_controls) {
4658 + pa_assert(pa_hashmap_isempty(api->volume_controls));
4659 + pa_hashmap_free(api->volume_controls);
4663 + pa_assert(pa_hashmap_isempty(api->names));
4664 + pa_hashmap_free(api->names);
4667 + if (api->binding_target_types) {
4668 + pa_assert(pa_hashmap_isempty(api->binding_target_types));
4669 + pa_hashmap_free(api->binding_target_types);
4675 +int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered,
4676 + const char **registered_name) {
4680 + pa_assert(requested_name);
4681 + pa_assert(registered_name);
4683 + n = pa_xstrdup(requested_name);
4685 + if (pa_hashmap_put(api->names, n, n) < 0) {
4690 + if (fail_if_already_registered) {
4691 + pa_log("Name %s already registered.", requested_name);
4692 + return -PA_ERR_EXIST;
4697 + n = pa_sprintf_malloc("%s.%u", requested_name, i);
4698 + } while (pa_hashmap_put(api->names, n, n) < 0);
4701 + *registered_name = n;
4706 +void pa_volume_api_unregister_name(pa_volume_api *api, const char *name) {
4710 + pa_assert_se(pa_hashmap_remove_and_free(api->names, name) >= 0);
4713 +uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) {
4718 + idx = api->next_volume_control_index++;
4723 +static void set_main_output_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
4724 + pa_volume_control *old_control;
4728 + old_control = api->main_output_volume_control;
4730 + if (control == old_control)
4733 + api->main_output_volume_control = control;
4734 + pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
4735 + control ? control->name : "(unset)");
4736 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
4739 +static void set_main_input_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
4740 + pa_volume_control *old_control;
4744 + old_control = api->main_input_volume_control;
4746 + if (control == old_control)
4749 + api->main_input_volume_control = control;
4750 + pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
4751 + control ? control->name : "(unset)");
4752 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
4755 +void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control) {
4757 + pa_assert(control);
4759 + pa_assert_se(pa_hashmap_put(api->volume_controls, (void *) control->name, control) >= 0);
4762 +int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *control) {
4764 + pa_assert(control);
4766 + if (!pa_hashmap_remove(api->volume_controls, control->name))
4769 + if (control == api->main_output_volume_control)
4770 + set_main_output_volume_control_internal(api, NULL);
4772 + if (control == api->main_input_volume_control)
4773 + set_main_input_volume_control_internal(api, NULL);
4778 +pa_volume_control *pa_volume_api_get_volume_control_by_index(pa_volume_api *api, uint32_t idx) {
4779 + pa_volume_control *control;
4784 + PA_HASHMAP_FOREACH(control, api->volume_controls, state) {
4785 + if (control->index == idx)
4792 +uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) {
4797 + idx = api->next_mute_control_index++;
4802 +static void set_main_output_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
4803 + pa_mute_control *old_control;
4807 + old_control = api->main_output_mute_control;
4809 + if (control == old_control)
4812 + api->main_output_mute_control = control;
4813 + pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
4814 + control ? control->name : "(unset)");
4815 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
4818 +static void set_main_input_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
4819 + pa_mute_control *old_control;
4823 + old_control = api->main_input_mute_control;
4825 + if (control == old_control)
4828 + api->main_input_mute_control = control;
4829 + pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
4830 + control ? control->name : "(unset)");
4831 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
4834 +void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control) {
4836 + pa_assert(control);
4838 + pa_assert_se(pa_hashmap_put(api->mute_controls, (void *) control->name, control) >= 0);
4841 +int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *control) {
4843 + pa_assert(control);
4845 + if (!pa_hashmap_remove(api->mute_controls, control->name))
4848 + if (control == api->main_output_mute_control)
4849 + set_main_output_mute_control_internal(api, NULL);
4851 + if (control == api->main_input_mute_control)
4852 + set_main_input_mute_control_internal(api, NULL);
4857 +pa_mute_control *pa_volume_api_get_mute_control_by_index(pa_volume_api *api, uint32_t idx) {
4858 + pa_mute_control *control;
4863 + PA_HASHMAP_FOREACH(control, api->mute_controls, state) {
4864 + if (control->index == idx)
4871 +uint32_t pa_volume_api_allocate_device_index(pa_volume_api *api) {
4876 + idx = api->next_device_index++;
4881 +void pa_volume_api_add_device(pa_volume_api *api, pa_device *device) {
4883 + pa_assert(device);
4885 + pa_assert_se(pa_hashmap_put(api->devices, (void *) device->name, device) >= 0);
4888 +int pa_volume_api_remove_device(pa_volume_api *api, pa_device *device) {
4890 + pa_assert(device);
4892 + if (!pa_hashmap_remove(api->devices, device->name))
4898 +pa_device *pa_volume_api_get_device_by_index(pa_volume_api *api, uint32_t idx) {
4899 + pa_device *device;
4904 + PA_HASHMAP_FOREACH(device, api->devices, state) {
4905 + if (device->index == idx)
4912 +uint32_t pa_volume_api_allocate_stream_index(pa_volume_api *api) {
4917 + idx = api->next_stream_index++;
4922 +void pa_volume_api_add_stream(pa_volume_api *api, pas_stream *stream) {
4924 + pa_assert(stream);
4926 + pa_assert_se(pa_hashmap_put(api->streams, (void *) stream->name, stream) >= 0);
4929 +int pa_volume_api_remove_stream(pa_volume_api *api, pas_stream *stream) {
4931 + pa_assert(stream);
4933 + if (!pa_hashmap_remove(api->streams, stream->name))
4939 +pas_stream *pa_volume_api_get_stream_by_index(pa_volume_api *api, uint32_t idx) {
4940 + pas_stream *stream;
4945 + PA_HASHMAP_FOREACH(stream, api->streams, state) {
4946 + if (stream->index == idx)
4953 +uint32_t pa_volume_api_allocate_audio_group_index(pa_volume_api *api) {
4958 + idx = api->next_audio_group_index++;
4963 +void pa_volume_api_add_audio_group(pa_volume_api *api, pa_audio_group *group) {
4967 + pa_assert_se(pa_hashmap_put(api->audio_groups, (void *) group->name, group) >= 0);
4970 +int pa_volume_api_remove_audio_group(pa_volume_api *api, pa_audio_group *group) {
4974 + if (!pa_hashmap_remove(api->audio_groups, group->name))
4980 +pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint32_t idx) {
4981 + pa_audio_group *group;
4986 + PA_HASHMAP_FOREACH(group, api->audio_groups, state) {
4987 + if (group->index == idx)
4994 +void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control) {
4997 + if (api->main_output_volume_control_binding) {
4998 + pa_binding_free(api->main_output_volume_control_binding);
4999 + api->main_output_volume_control_binding = NULL;
5002 + set_main_output_volume_control_internal(api, control);
5005 +void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
5008 + if (api->main_input_volume_control_binding) {
5009 + pa_binding_free(api->main_input_volume_control_binding);
5010 + api->main_input_volume_control_binding = NULL;
5013 + set_main_input_volume_control_internal(api, control);
5016 +void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
5019 + if (api->main_output_mute_control_binding) {
5020 + pa_binding_free(api->main_output_mute_control_binding);
5021 + api->main_output_mute_control_binding = NULL;
5024 + set_main_output_mute_control_internal(api, control);
5027 +void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
5030 + if (api->main_input_mute_control_binding) {
5031 + pa_binding_free(api->main_input_mute_control_binding);
5032 + api->main_input_mute_control_binding = NULL;
5035 + set_main_input_mute_control_internal(api, control);
5038 +void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
5039 + pa_binding_owner_info owner_info = {
5041 + .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
5045 + pa_assert(target_info);
5047 + if (api->main_output_volume_control_binding)
5048 + pa_binding_free(api->main_output_volume_control_binding);
5050 + api->main_output_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
5053 +void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
5054 + pa_binding_owner_info owner_info = {
5056 + .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
5060 + pa_assert(target_info);
5062 + if (api->main_input_volume_control_binding)
5063 + pa_binding_free(api->main_input_volume_control_binding);
5065 + api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
5068 +void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
5069 + pa_binding_owner_info owner_info = {
5071 + .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
5075 + pa_assert(target_info);
5077 + if (api->main_output_mute_control_binding)
5078 + pa_binding_free(api->main_output_mute_control_binding);
5080 + api->main_output_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
5083 +void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
5084 + pa_binding_owner_info owner_info = {
5086 + .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
5090 + pa_assert(target_info);
5092 + if (api->main_input_mute_control_binding)
5093 + pa_binding_free(api->main_input_mute_control_binding);
5095 + api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
5097 diff --git a/src/modules/volume-api/volume-api.h b/src/modules/volume-api/volume-api.h
5098 new file mode 100644
5099 index 0000000..73a1410
5101 +++ b/src/modules/volume-api/volume-api.h
5103 +#ifndef foovolumeapihfoo
5104 +#define foovolumeapihfoo
5107 + This file is part of PulseAudio.
5109 + Copyright 2014 Intel Corporation
5111 + PulseAudio is free software; you can redistribute it and/or modify
5112 + it under the terms of the GNU Lesser General Public License as published
5113 + by the Free Software Foundation; either version 2.1 of the License,
5114 + or (at your option) any later version.
5116 + PulseAudio is distributed in the hope that it will be useful, but
5117 + WITHOUT ANY WARRANTY; without even the implied warranty of
5118 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5119 + General Public License for more details.
5121 + You should have received a copy of the GNU Lesser General Public License
5122 + along with PulseAudio; if not, write to the Free Software
5123 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5127 +#include <pulsecore/core.h>
5129 +typedef struct pa_volume_api pa_volume_api;
5131 +/* Avoid circular dependencies... */
5132 +typedef struct pa_audio_group pa_audio_group;
5133 +typedef struct pa_binding pa_binding;
5134 +typedef struct pa_binding_target_info pa_binding_target_info;
5135 +typedef struct pa_binding_target_type pa_binding_target_type;
5136 +typedef struct pa_device pa_device;
5137 +typedef struct pa_device_creator pa_device_creator;
5138 +typedef struct pa_mute_control pa_mute_control;
5139 +typedef struct pas_stream pas_stream;
5140 +typedef struct pa_stream_creator pa_stream_creator;
5141 +typedef struct pa_volume_control pa_volume_control;
5144 + PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED,
5145 + PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED,
5146 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT,
5147 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK,
5148 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED,
5149 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED,
5150 + PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT,
5151 + PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK,
5152 + PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED,
5153 + PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED,
5154 + PA_VOLUME_API_HOOK_DEVICE_PUT,
5155 + PA_VOLUME_API_HOOK_DEVICE_UNLINK,
5156 + PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED,
5157 + PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED,
5158 + PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED,
5160 + /* Policy modules can use this to set the initial volume control for a
5161 + * stream. The hook callback should use pas_stream_set_volume_control() to
5162 + * set the volume control. The hook callback should not do anything if
5163 + * stream->volume_control is already non-NULL. */
5164 + PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL,
5166 + /* Policy modules can use this to set the initial mute control for a
5167 + * stream. The hook callback should use pas_stream_set_mute_control() to
5168 + * set the mute control. The hook callback should not do anything if
5169 + * stream->mute_control is already non-NULL. */
5170 + PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL,
5172 + PA_VOLUME_API_HOOK_STREAM_PUT,
5173 + PA_VOLUME_API_HOOK_STREAM_UNLINK,
5174 + PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED,
5175 + PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED,
5176 + PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED,
5177 + PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT,
5178 + PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK,
5179 + PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED,
5180 + PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED,
5181 + PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
5182 + PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
5183 + PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
5184 + PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED,
5185 + PA_VOLUME_API_HOOK_MAX
5188 +struct pa_volume_api {
5191 + pa_hashmap *binding_target_types; /* name -> pa_binding_target_type */
5192 + pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
5193 + pa_hashmap *volume_controls; /* name -> pa_volume_control */
5194 + pa_hashmap *mute_controls; /* name -> pa_mute_control */
5195 + pa_hashmap *devices; /* name -> pa_device */
5196 + pa_hashmap *streams; /* name -> pas_stream */
5197 + pa_hashmap *audio_groups; /* name -> pa_audio_group */
5198 + pa_volume_control *main_output_volume_control;
5199 + pa_volume_control *main_input_volume_control;
5200 + pa_mute_control *main_output_mute_control;
5201 + pa_mute_control *main_input_mute_control;
5203 + uint32_t next_volume_control_index;
5204 + uint32_t next_mute_control_index;
5205 + uint32_t next_device_index;
5206 + uint32_t next_stream_index;
5207 + uint32_t next_audio_group_index;
5208 + pa_binding *main_output_volume_control_binding;
5209 + pa_binding *main_input_volume_control_binding;
5210 + pa_binding *main_output_mute_control_binding;
5211 + pa_binding *main_input_mute_control_binding;
5212 + pa_hook hooks[PA_VOLUME_API_HOOK_MAX];
5213 + pa_defer_event *create_objects_defer_event;
5214 + pa_device_creator *device_creator;
5215 + pa_stream_creator *stream_creator;
5218 +pa_volume_api *pa_volume_api_get(pa_core *core);
5219 +pa_volume_api *pa_volume_api_ref(pa_volume_api *api);
5220 +void pa_volume_api_unref(pa_volume_api *api);
5222 +void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
5223 +void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
5225 +/* If fail_if_already_registered is false, this function never fails. */
5226 +int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered,
5227 + const char **registered_name);
5229 +void pa_volume_api_unregister_name(pa_volume_api *api, const char *name);
5231 +uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api);
5232 +void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control);
5233 +int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *control);
5234 +pa_volume_control *pa_volume_api_get_volume_control_by_index(pa_volume_api *api, uint32_t idx);
5236 +uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api);
5237 +void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control);
5238 +int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *control);
5239 +pa_mute_control *pa_volume_api_get_mute_control_by_index(pa_volume_api *api, uint32_t idx);
5241 +uint32_t pa_volume_api_allocate_device_index(pa_volume_api *api);
5242 +void pa_volume_api_add_device(pa_volume_api *api, pa_device *device);
5243 +int pa_volume_api_remove_device(pa_volume_api *api, pa_device *device);
5244 +pa_device *pa_volume_api_get_device_by_index(pa_volume_api *api, uint32_t idx);
5246 +uint32_t pa_volume_api_allocate_stream_index(pa_volume_api *api);
5247 +void pa_volume_api_add_stream(pa_volume_api *api, pas_stream *stream);
5248 +int pa_volume_api_remove_stream(pa_volume_api *api, pas_stream *stream);
5249 +pas_stream *pa_volume_api_get_stream_by_index(pa_volume_api *api, uint32_t idx);
5251 +uint32_t pa_volume_api_allocate_audio_group_index(pa_volume_api *api);
5252 +void pa_volume_api_add_audio_group(pa_volume_api *api, pa_audio_group *group);
5253 +int pa_volume_api_remove_audio_group(pa_volume_api *api, pa_audio_group *group);
5254 +pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint32_t idx);
5256 +void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control);
5257 +void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control);
5258 +void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control);
5259 +void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control);
5260 +void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
5261 +void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
5262 +void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
5263 +void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
5266 diff --git a/src/modules/volume-api/volume-control.c b/src/modules/volume-api/volume-control.c
5267 new file mode 100644
5268 index 0000000..c7f5dbb
5270 +++ b/src/modules/volume-api/volume-control.c
5273 + This file is part of PulseAudio.
5275 + Copyright 2014 Intel Corporation
5277 + PulseAudio is free software; you can redistribute it and/or modify
5278 + it under the terms of the GNU Lesser General Public License as published
5279 + by the Free Software Foundation; either version 2.1 of the License,
5280 + or (at your option) any later version.
5282 + PulseAudio is distributed in the hope that it will be useful, but
5283 + WITHOUT ANY WARRANTY; without even the implied warranty of
5284 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5285 + General Public License for more details.
5287 + You should have received a copy of the GNU Lesser General Public License
5288 + along with PulseAudio; if not, write to the Free Software
5289 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5293 +#ifdef HAVE_CONFIG_H
5294 +#include <config.h>
5297 +#include "volume-control.h"
5299 +#include <modules/volume-api/audio-group.h>
5300 +#include <modules/volume-api/device.h>
5301 +#include <modules/volume-api/sstream.h>
5303 +#include <pulsecore/core-util.h>
5305 +pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
5306 + bool channel_map_is_writable) {
5307 + pa_volume_control *control;
5311 + pa_assert(description);
5313 + control = pa_xnew0(pa_volume_control, 1);
5314 + control->volume_api = api;
5315 + control->index = pa_volume_api_allocate_volume_control_index(api);
5316 + pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
5317 + control->description = pa_xstrdup(description);
5318 + control->proplist = pa_proplist_new();
5319 + pa_bvolume_init_invalid(&control->volume);
5320 + control->convertible_to_dB = convertible_to_dB;
5321 + control->channel_map_is_writable = channel_map_is_writable;
5322 + control->devices = pa_hashmap_new(NULL, NULL);
5323 + control->default_for_devices = pa_hashmap_new(NULL, NULL);
5324 + control->streams = pa_hashmap_new(NULL, NULL);
5325 + control->audio_groups = pa_hashmap_new(NULL, NULL);
5330 +void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
5331 + pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb) {
5332 + const char *prop_key;
5333 + void *state = NULL;
5334 + char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
5335 + char balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
5337 + pa_assert(control);
5338 + pa_assert((initial_volume && pa_bvolume_valid(initial_volume, true, true)) || control->set_volume);
5339 + pa_assert((initial_volume && pa_channel_map_valid(&initial_volume->channel_map)) || control->channel_map_is_writable);
5340 + pa_assert(set_initial_volume_cb || !control->set_volume);
5342 + if (initial_volume && pa_bvolume_valid(initial_volume, true, false))
5343 + control->volume.volume = initial_volume->volume;
5345 + control->volume.volume = PA_VOLUME_NORM / 3;
5347 + if (initial_volume && pa_bvolume_valid(initial_volume, false, true))
5348 + pa_bvolume_copy_balance(&control->volume, initial_volume);
5349 + else if (initial_volume && pa_channel_map_valid(&initial_volume->channel_map))
5350 + pa_bvolume_reset_balance(&control->volume, &initial_volume->channel_map);
5352 + pa_channel_map_init_mono(&control->volume.channel_map);
5353 + pa_bvolume_reset_balance(&control->volume, &control->volume.channel_map);
5356 + if (set_initial_volume_cb)
5357 + set_initial_volume_cb(control);
5359 + pa_volume_api_add_volume_control(control->volume_api, control);
5361 + control->linked = true;
5363 + pa_log_debug("Created volume control #%u.", control->index);
5364 + pa_log_debug(" Name: %s", control->name);
5365 + pa_log_debug(" Description: %s", control->description);
5366 + pa_log_debug(" Properties:");
5368 + while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
5369 + pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(control->proplist, prop_key)));
5371 + pa_log_debug(" Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
5372 + control->convertible_to_dB));
5373 + pa_log_debug(" Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
5374 + pa_log_debug(" Channel map is writable: %s", pa_yes_no(control->channel_map_is_writable));
5376 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT], control);
5379 +void pa_volume_control_unlink(pa_volume_control *control) {
5380 + pa_audio_group *group;
5381 + pa_device *device;
5382 + pas_stream *stream;
5384 + pa_assert(control);
5386 + if (control->unlinked) {
5387 + pa_log_debug("Unlinking volume control %s (already unlinked, this is a no-op).", control->name);
5391 + control->unlinked = true;
5393 + pa_log_debug("Unlinking volume control %s.", control->name);
5395 + if (control->linked)
5396 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
5398 + pa_volume_api_remove_volume_control(control->volume_api, control);
5400 + while ((group = pa_hashmap_first(control->audio_groups)))
5401 + pa_audio_group_set_volume_control(group, NULL);
5403 + while ((stream = pa_hashmap_first(control->streams)))
5404 + pas_stream_set_volume_control(stream, NULL);
5406 + while ((device = pa_hashmap_first(control->default_for_devices)))
5407 + pa_device_set_default_volume_control(device, NULL);
5409 + while ((device = pa_hashmap_first(control->devices))) {
5410 + /* Why do we have this assertion here? The concern is that if we call
5411 + * pa_device_set_volume_control() for some device that has the
5412 + * use_default_volume_control flag set, then that flag will be unset as
5413 + * a side effect, and we don't want that side effect. This assertion
5414 + * should be safe, because we just called
5415 + * pa_device_set_default_volume_control(NULL) for each device that this
5416 + * control was the default for, and that should ensure that we don't
5417 + * any more hold any references to devices that used to use this
5418 + * control as the default. */
5419 + pa_assert(!device->use_default_volume_control);
5420 + pa_device_set_volume_control(device, NULL);
5424 +void pa_volume_control_free(pa_volume_control *control) {
5425 + pa_assert(control);
5427 + if (!control->unlinked)
5428 + pa_volume_control_unlink(control);
5430 + if (control->audio_groups) {
5431 + pa_assert(pa_hashmap_isempty(control->audio_groups));
5432 + pa_hashmap_free(control->audio_groups);
5435 + if (control->streams) {
5436 + pa_assert(pa_hashmap_isempty(control->streams));
5437 + pa_hashmap_free(control->streams);
5440 + if (control->default_for_devices) {
5441 + pa_assert(pa_hashmap_isempty(control->default_for_devices));
5442 + pa_hashmap_free(control->default_for_devices);
5445 + if (control->devices) {
5446 + pa_assert(pa_hashmap_isempty(control->devices));
5447 + pa_hashmap_free(control->devices);
5450 + if (control->proplist)
5451 + pa_proplist_free(control->proplist);
5453 + pa_xfree(control->description);
5455 + if (control->name)
5456 + pa_volume_api_unregister_name(control->volume_api, control->name);
5458 + pa_xfree(control);
5461 +void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) {
5462 + pa_assert(control);
5465 + control->owner_audio_group = group;
5468 +static void set_volume_internal(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
5469 + pa_bvolume old_volume;
5470 + bool volume_changed;
5471 + bool balance_changed;
5473 + pa_assert(control);
5474 + pa_assert(volume);
5476 + old_volume = control->volume;
5477 + volume_changed = !pa_bvolume_equal(volume, &old_volume, set_volume, false);
5478 + balance_changed = !pa_bvolume_equal(volume, &old_volume, false, set_balance);
5480 + if (!volume_changed && !balance_changed)
5483 + if (volume_changed)
5484 + control->volume.volume = volume->volume;
5486 + if (balance_changed)
5487 + pa_bvolume_copy_balance(&control->volume, volume);
5489 + if (!control->linked || control->unlinked)
5492 + if (volume_changed) {
5493 + char old_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
5494 + char new_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
5496 + pa_log_debug("The volume of volume control %s changed from %s to %s.", control->name,
5497 + pa_volume_snprint_verbose(old_volume_str, sizeof(old_volume_str), old_volume.volume,
5498 + control->convertible_to_dB),
5499 + pa_volume_snprint_verbose(new_volume_str, sizeof(new_volume_str), control->volume.volume,
5500 + control->convertible_to_dB));
5503 + if (balance_changed) {
5504 + char old_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
5505 + char new_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
5507 + pa_log_debug("The balance of volume control %s changed from %s to %s.", control->name,
5508 + pa_bvolume_snprint_balance(old_balance_str, sizeof(old_balance_str), &control->volume),
5509 + pa_bvolume_snprint_balance(new_balance_str, sizeof(new_balance_str), &control->volume));
5512 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], control);
5515 +int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
5516 + pa_bvolume volume_local;
5519 + pa_assert(control);
5520 + pa_assert(volume);
5522 + volume_local = *volume;
5524 + if (!control->set_volume) {
5525 + pa_log_info("Tried to set the volume of volume control %s, but the volume control doesn't support the operation.",
5527 + return -PA_ERR_NOTSUPPORTED;
5531 + && !control->channel_map_is_writable
5532 + && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
5533 + pa_bvolume_remap(&volume_local, &control->volume.channel_map);
5535 + if (pa_bvolume_equal(&volume_local, &control->volume, set_volume, set_balance))
5538 + control->set_volume_in_progress = true;
5539 + r = control->set_volume(control, &volume_local, set_volume, set_balance);
5540 + control->set_volume_in_progress = false;
5543 + set_volume_internal(control, &volume_local, set_volume, set_balance);
5548 +void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) {
5549 + char *old_description;
5551 + pa_assert(control);
5552 + pa_assert(new_description);
5554 + old_description = control->description;
5556 + if (pa_streq(new_description, old_description))
5559 + control->description = pa_xstrdup(new_description);
5560 + pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
5562 + pa_xfree(old_description);
5563 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
5566 +void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
5567 + bool balance_changed) {
5568 + pa_assert(control);
5569 + pa_assert(new_volume);
5571 + if (!control->linked)
5574 + if (control->set_volume_in_progress)
5577 + set_volume_internal(control, new_volume, volume_changed, balance_changed);
5580 +void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) {
5581 + pa_assert(control);
5582 + pa_assert(device);
5584 + pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0);
5587 +void pa_volume_control_remove_device(pa_volume_control *control, pa_device *device) {
5588 + pa_assert(control);
5589 + pa_assert(device);
5591 + pa_assert_se(pa_hashmap_remove(control->devices, device));
5594 +void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device) {
5595 + pa_assert(control);
5596 + pa_assert(device);
5598 + pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0);
5601 +void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device) {
5602 + pa_assert(control);
5603 + pa_assert(device);
5605 + pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
5608 +void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream) {
5609 + pa_assert(control);
5610 + pa_assert(stream);
5612 + pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
5615 +void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream) {
5616 + pa_assert(control);
5617 + pa_assert(stream);
5619 + pa_assert_se(pa_hashmap_remove(control->streams, stream));
5622 +void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group) {
5623 + pa_assert(control);
5626 + pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
5629 +void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group) {
5630 + pa_assert(control);
5633 + pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
5635 diff --git a/src/modules/volume-api/volume-control.h b/src/modules/volume-api/volume-control.h
5636 new file mode 100644
5637 index 0000000..aaba758
5639 +++ b/src/modules/volume-api/volume-control.h
5641 +#ifndef foovolumecontrolhfoo
5642 +#define foovolumecontrolhfoo
5645 + This file is part of PulseAudio.
5647 + Copyright 2014 Intel Corporation
5649 + PulseAudio is free software; you can redistribute it and/or modify
5650 + it under the terms of the GNU Lesser General Public License as published
5651 + by the Free Software Foundation; either version 2.1 of the License,
5652 + or (at your option) any later version.
5654 + PulseAudio is distributed in the hope that it will be useful, but
5655 + WITHOUT ANY WARRANTY; without even the implied warranty of
5656 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5657 + General Public License for more details.
5659 + You should have received a copy of the GNU Lesser General Public License
5660 + along with PulseAudio; if not, write to the Free Software
5661 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5665 +#include <modules/volume-api/bvolume.h>
5666 +#include <modules/volume-api/volume-api.h>
5668 +typedef struct pa_volume_control pa_volume_control;
5670 +struct pa_volume_control {
5671 + pa_volume_api *volume_api;
5674 + char *description;
5675 + pa_proplist *proplist;
5676 + pa_bvolume volume;
5677 + bool convertible_to_dB;
5678 + bool channel_map_is_writable;
5680 + /* If this volume control is the "own volume control" of an audio group,
5681 + * this is set to point to that group, otherwise this is NULL. */
5682 + pa_audio_group *owner_audio_group;
5684 + pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
5685 + pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
5686 + pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
5687 + pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
5691 + bool set_volume_in_progress;
5693 + /* Called from pa_volume_control_set_volume(). The implementation is
5694 + * expected to return a negative error code on failure. May be NULL, if the
5695 + * volume control is read-only. */
5696 + int (*set_volume)(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
5701 +pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
5702 + bool channel_map_is_writable);
5704 +typedef void (*pa_volume_control_set_initial_volume_cb_t)(pa_volume_control *control);
5706 +/* initial_volume is the preferred initial volume of the volume control
5707 + * implementation. It may be NULL or partially invalid, if the implementation
5708 + * doesn't care about the initial state of the volume control, as long as these
5709 + * two rules are followed:
5711 + * 1) Read-only volume controls must always specify fully valid initial
5713 + * 2) Volume controls with read-only channel map must always specify a valid
5714 + * channel map in initial_volume.
5716 + * The implementation's initial volume preference may be overridden by policy,
5717 + * if the volume control isn't read-only. When the final initial volume is
5718 + * known, the implementation is notified via set_initial_volume_cb (the volume
5719 + * can be read from control->volume). set_initial_volume_cb may be NULL, if the
5720 + * volume control is read-only. */
5721 +void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
5722 + pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb);
5724 +void pa_volume_control_unlink(pa_volume_control *control);
5725 +void pa_volume_control_free(pa_volume_control *control);
5727 +/* Called by audio-group.c only. */
5728 +void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group);
5730 +/* Called by clients and policy modules. */
5731 +int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
5733 +/* Called by the volume control implementation. */
5734 +void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description);
5735 +void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
5736 + bool balance_changed);
5738 +/* Called from device.c only. */
5739 +void pa_volume_control_add_device(pa_volume_control *control, pa_device *device);
5740 +void pa_volume_control_remove_device(pa_volume_control *control, pa_device *device);
5741 +void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device);
5742 +void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device);
5744 +/* Called from sstream.c only. */
5745 +void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream);
5746 +void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream);
5748 +/* Called from audio-group.c only. */
5749 +void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group);
5750 +void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group);
5753 diff --git a/src/pulse/ext-volume-api.c b/src/pulse/ext-volume-api.c
5754 new file mode 100644
5755 index 0000000..8e93bce
5757 +++ b/src/pulse/ext-volume-api.c
5760 + This file is part of PulseAudio.
5762 + Copyright 2014 Intel Corporation
5764 + PulseAudio is free software; you can redistribute it and/or modify
5765 + it under the terms of the GNU Lesser General Public License as published
5766 + by the Free Software Foundation; either version 2.1 of the License,
5767 + or (at your option) any later version.
5769 + PulseAudio is distributed in the hope that it will be useful, but
5770 + WITHOUT ANY WARRANTY; without even the implied warranty of
5771 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5772 + General Public License for more details.
5774 + You should have received a copy of the GNU Lesser General Public License
5775 + along with PulseAudio; if not, write to the Free Software
5776 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5780 +#ifdef HAVE_CONFIG_H
5781 +#include <config.h>
5784 +#include "ext-volume-api.h"
5786 +#include <pulsecore/core-util.h>
5787 +#include <pulsecore/i18n.h>
5788 +#include <pulsecore/macro.h>
5792 +int pa_ext_volume_api_balance_valid(double balance) {
5793 + return balance >= 0.0 && balance <= 1.0;
5796 +int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance) {
5799 + pa_assert(volume);
5801 + if (check_volume && !PA_VOLUME_IS_VALID(volume->volume))
5804 + if (!check_balance)
5807 + if (!pa_channel_map_valid(&volume->channel_map))
5810 + for (channel = 0; channel < volume->channel_map.channels; channel++) {
5811 + if (!pa_ext_volume_api_balance_valid(volume->balance[channel]))
5818 +void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) {
5821 + pa_assert(volume);
5823 + volume->volume = PA_VOLUME_INVALID;
5825 + for (i = 0; i < PA_CHANNELS_MAX; i++)
5826 + volume->balance[i] = -1.0;
5828 + pa_channel_map_init(&volume->channel_map);
5831 +void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume) {
5832 + pa_assert(bvolume);
5833 + pa_assert(PA_VOLUME_IS_VALID(volume));
5835 + bvolume->volume = volume;
5836 + bvolume->balance[0] = 1.0;
5837 + pa_channel_map_init_mono(&bvolume->channel_map);
5840 +int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
5841 + int check_volume, int check_balance) {
5847 + if (check_volume && a->volume != b->volume)
5850 + if (!check_balance)
5853 + if (!pa_channel_map_equal(&a->channel_map, &b->channel_map))
5856 + for (i = 0; i < a->channel_map.channels; i++) {
5857 + if (fabs(a->balance[i] - b->balance[i]) > 0.00001)
5864 +void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume,
5865 + const pa_channel_map *map) {
5868 + pa_assert(bvolume);
5869 + pa_assert(cvolume);
5871 + pa_assert(cvolume->channels == map->channels);
5873 + bvolume->volume = pa_cvolume_max(cvolume);
5874 + bvolume->channel_map = *map;
5876 + for (i = 0; i < map->channels; i++) {
5877 + if (bvolume->volume != PA_VOLUME_MUTED)
5878 + bvolume->balance[i] = ((double) cvolume->values[i]) / ((double) bvolume->volume);
5880 + bvolume->balance[i] = 1.0;
5884 +void pa_ext_volume_api_bvolume_to_cvolume(const pa_ext_volume_api_bvolume *bvolume, pa_cvolume *cvolume) {
5887 + pa_assert(bvolume);
5888 + pa_assert(cvolume);
5889 + pa_assert(pa_ext_volume_api_bvolume_valid(bvolume, true, true));
5891 + cvolume->channels = bvolume->channel_map.channels;
5893 + for (i = 0; i < bvolume->channel_map.channels; i++)
5894 + cvolume->values[i] = bvolume->volume * bvolume->balance[i];
5897 +void pa_ext_volume_api_bvolume_copy_balance(pa_ext_volume_api_bvolume *to,
5898 + const pa_ext_volume_api_bvolume *from) {
5902 + memcpy(to->balance, from->balance, sizeof(from->balance));
5903 + to->channel_map = from->channel_map;
5906 +void pa_ext_volume_api_bvolume_reset_balance(pa_ext_volume_api_bvolume *volume, const pa_channel_map *map) {
5909 + pa_assert(volume);
5911 + pa_assert(pa_channel_map_valid(map));
5913 + for (i = 0; i < map->channels; i++)
5914 + volume->balance[i] = 1.0;
5916 + volume->channel_map = *map;
5919 +void pa_ext_volume_api_bvolume_remap(pa_ext_volume_api_bvolume *volume, const pa_channel_map *to) {
5921 + pa_cvolume cvolume;
5923 + pa_assert(volume);
5925 + pa_assert(pa_ext_volume_api_bvolume_valid(volume, false, true));
5926 + pa_assert(pa_channel_map_valid(to));
5928 + cvolume.channels = volume->channel_map.channels;
5930 + for (i = 0; i < cvolume.channels; i++)
5931 + cvolume.values[i] = volume->balance[i] * (double) PA_VOLUME_NORM;
5933 + pa_cvolume_remap(&cvolume, &volume->channel_map, to);
5935 + for (i = 0; i < to->channels; i++)
5936 + volume->balance[i] = (double) cvolume.values[i] / (double) PA_VOLUME_NORM;
5938 + volume->channel_map = *to;
5941 +double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_bvolume *volume) {
5942 + pa_ext_volume_api_bvolume bvolume;
5943 + pa_cvolume cvolume;
5946 + pa_assert(volume);
5948 + bvolume.volume = PA_VOLUME_NORM;
5949 + pa_ext_volume_api_bvolume_copy_balance(&bvolume, volume);
5950 + pa_ext_volume_api_bvolume_to_cvolume(&bvolume, &cvolume);
5951 + ret = pa_cvolume_get_balance(&cvolume, &volume->channel_map);
5956 +void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance) {
5957 + pa_cvolume cvolume;
5958 + pa_volume_t old_volume;
5960 + pa_assert(volume);
5962 + if (!pa_channel_map_can_balance(&volume->channel_map))
5965 + pa_cvolume_reset(&cvolume, volume->channel_map.channels);
5966 + pa_cvolume_set_balance(&cvolume, &volume->channel_map, balance);
5967 + old_volume = volume->volume;
5968 + pa_ext_volume_api_bvolume_from_cvolume(volume, &cvolume, &volume->channel_map);
5969 + volume->volume = old_volume;
5972 +double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) {
5973 + pa_ext_volume_api_bvolume bvolume;
5974 + pa_cvolume cvolume;
5977 + pa_assert(volume);
5979 + bvolume.volume = PA_VOLUME_NORM;
5980 + pa_ext_volume_api_bvolume_copy_balance(&bvolume, volume);
5981 + pa_ext_volume_api_bvolume_to_cvolume(&bvolume, &cvolume);
5982 + ret = pa_cvolume_get_fade(&cvolume, &volume->channel_map);
5987 +void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance) {
5988 + pa_cvolume cvolume;
5989 + pa_volume_t old_volume;
5991 + pa_assert(volume);
5993 + if (!pa_channel_map_can_fade(&volume->channel_map))
5996 + pa_cvolume_reset(&cvolume, volume->channel_map.channels);
5997 + pa_cvolume_set_fade(&cvolume, &volume->channel_map, balance);
5998 + old_volume = volume->volume;
5999 + pa_ext_volume_api_bvolume_from_cvolume(volume, &cvolume, &volume->channel_map);
6000 + volume->volume = old_volume;
6003 +char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len,
6004 + const pa_ext_volume_api_bvolume *volume) {
6007 + bool first = true;
6010 + pa_assert(buf_len > 0);
6011 + pa_assert(volume);
6015 + if (!pa_ext_volume_api_bvolume_valid(volume, true, true)) {
6016 + pa_snprintf(buf, buf_len, _("(invalid)"));
6022 + for (channel = 0; channel < volume->channel_map.channels && buf_len > 1; channel++) {
6023 + buf_len -= pa_snprintf(e, buf_len, "%s%s: %u%%",
6024 + first ? "" : ", ",
6025 + pa_channel_position_to_string(volume->channel_map.map[channel]),
6026 + (unsigned) (volume->balance[channel] * 100 + 0.5));
6034 diff --git a/src/pulse/ext-volume-api.h b/src/pulse/ext-volume-api.h
6035 new file mode 100644
6036 index 0000000..36b7748
6038 +++ b/src/pulse/ext-volume-api.h
6040 +#ifndef fooextvolumeapihfoo
6041 +#define fooextvolumeapihfoo
6044 + This file is part of PulseAudio.
6046 + Copyright 2014 Intel Corporation
6048 + PulseAudio is free software; you can redistribute it and/or modify
6049 + it under the terms of the GNU Lesser General Public License as published
6050 + by the Free Software Foundation; either version 2.1 of the License,
6051 + or (at your option) any later version.
6053 + PulseAudio is distributed in the hope that it will be useful, but
6054 + WITHOUT ANY WARRANTY; without even the implied warranty of
6055 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6056 + General Public License for more details.
6058 + You should have received a copy of the GNU Lesser General Public License
6059 + along with PulseAudio; if not, write to the Free Software
6060 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
6064 +#include <pulse/cdecl.h>
6065 +#include <pulse/context.h>
6066 +#include <pulse/volume.h>
6068 +/* This API is temporary, and has no stability guarantees whatsoever. Think
6069 + * twice before making anything that relies on this API. This is undocumented
6070 + * for a reason. */
6074 +typedef struct pa_ext_volume_api_bvolume pa_ext_volume_api_bvolume;
6076 +struct pa_ext_volume_api_bvolume {
6077 + pa_volume_t volume;
6078 + double balance[PA_CHANNELS_MAX];
6079 + pa_channel_map channel_map;
6082 +int pa_ext_volume_api_balance_valid(double balance) PA_GCC_CONST;
6083 +int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance)
6085 +void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume);
6086 +void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume);
6087 +int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
6088 + int check_volume, int check_balance) PA_GCC_PURE;
6089 +void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume,
6090 + const pa_channel_map *map);
6091 +void pa_ext_volume_api_bvolume_to_cvolume(const pa_ext_volume_api_bvolume *bvolume, pa_cvolume *cvolume);
6092 +void pa_ext_volume_api_bvolume_copy_balance(pa_ext_volume_api_bvolume *to,
6093 + const pa_ext_volume_api_bvolume *from);
6094 +void pa_ext_volume_api_bvolume_reset_balance(pa_ext_volume_api_bvolume *volume, const pa_channel_map *map);
6095 +void pa_ext_volume_api_bvolume_remap(pa_ext_volume_api_bvolume *volume, const pa_channel_map *to);
6096 +double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE;
6097 +void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance);
6098 +double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE;
6099 +void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance);
6101 +#define PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX 500
6102 +char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_size,
6103 + const pa_ext_volume_api_bvolume *volume);
6111 --- a/po/POTFILES.in 2016-04-13 17:40:00.818008672 +0200
6112 +++ b/po/POTFILES.in 2016-04-13 17:40:30.885008622 +0200
6115 src/utils/pasuspender.c
6116 src/utils/pax11publish.c
6117 +src/modules/volume-api/device-creator.c
6118 +src/pulse/ext-volume-api.c