Introduce meta-agl-profile-core and meta-agl-profile-graphics
[AGL/meta-agl.git] / meta-agl-profile-core / recipes-multimedia / pulseaudio / pulseaudio / 0030-volume-api-Add-libvolume-api.patch
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
5
6 This library implements the "core" of the new volume system, which
7 will be used by several modules.
8
9 Change-Id: Ib25ada1392e83237a3908e6064ee0ad6dff7afaf
10 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
11 ---
12  Makefile.am                             |    3 +
13  src/Makefile.am                         |   19 +-
14  src/map-file                            |   15 +
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
58
59 diff --git a/Makefile.am b/Makefile.am
60 index 9431d4a..cf4a648 100644
61 --- a/Makefile.am
62 +++ b/Makefile.am
63 @@ -57,6 +57,9 @@
64  moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h 
65  moduledevinternaldir = $(includedir)/pulsemodule/pulse
66  
67 +moduledevvolumeapi_DATA = $(top_srcdir)/src/modules/volume-api/*.h
68 +moduledevvolumeapidir   = $(includedir)/pulsemodule/modules/volume-api
69 +
70  if HAVE_GLIB20
71  pkgconfig_DATA += \
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
77 @@ -792,6 +792,7 @@
78                 pulse/ext-device-manager.h \
79                 pulse/ext-device-restore.h \
80                 pulse/ext-stream-restore.h \
81 +               pulse/ext-volume-api.h \
82                 pulse/format.h \
83                 pulse/gccmacro.h \
84                 pulse/introspect.h \
85 @@ -838,6 +839,7 @@
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 \
91                 pulse/gccmacro.h \
92                 pulse/internal.h \
93 @@ -1018,7 +1020,8 @@ modlibexec_LTLIBRARIES = \
94                 libprotocol-cli.la \
95                 libprotocol-simple.la \
96                 libprotocol-http.la \
97 -               libprotocol-native.la
98 +               libprotocol-native.la \
99 +               libvolume-api.la
100  
101  if HAVE_WEBRTC
102  modlibexec_LTLIBRARIES += libwebrtc-util.la
103 @@ -1065,6 +1068,20 @@ libprotocol_native_la_CFLAGS += $(DBUS_CFLAGS)
104  libprotocol_native_la_LIBADD += $(DBUS_LIBS)
105  endif
106  
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
120 +
121  if HAVE_ESOUND
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
126 --- a/src/map-file
127 +++ b/src/map-file
128 @@ -172,6 +172,21 @@
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;
147  pa_format_info_copy;
148  pa_format_info_free;
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
151 new file mode 100644
152 index 0000000..76bfa69
153 --- /dev/null
154 +++ b/src/modules/volume-api/audio-group.c
155 @@ -0,0 +1,448 @@
156 +/***
157 +  This file is part of PulseAudio.
158 +
159 +  Copyright 2014 Intel Corporation
160 +
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.
165 +
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.
170 +
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
174 +  USA.
175 +***/
176 +
177 +#ifdef HAVE_CONFIG_H
178 +#include <config.h>
179 +#endif
180 +
181 +#include "audio-group.h"
182 +
183 +#include <modules/volume-api/sstream.h>
184 +
185 +#include <pulsecore/core-util.h>
186 +
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;
189 +    int r;
190 +
191 +    pa_assert(api);
192 +    pa_assert(name);
193 +    pa_assert(description);
194 +    pa_assert(group);
195 +
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);
199 +
200 +    r = pa_volume_api_register_name(api, name, true, &group_local->name);
201 +    if (r < 0)
202 +        goto fail;
203 +
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);
208 +
209 +    *group = group_local;
210 +
211 +    return 0;
212 +
213 +fail:
214 +    pa_audio_group_free(group_local);
215 +
216 +    return r;
217 +}
218 +
219 +void pa_audio_group_put(pa_audio_group *group) {
220 +    const char *prop_key;
221 +    void *state = NULL;
222 +
223 +    pa_assert(group);
224 +
225 +    pa_volume_api_add_audio_group(group->volume_api, group);
226 +
227 +    group->linked = true;
228 +
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:");
235 +
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)));
238 +
239 +    pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], group);
240 +}
241 +
242 +void pa_audio_group_unlink(pa_audio_group *group) {
243 +    pas_stream *stream;
244 +
245 +    pa_assert(group);
246 +
247 +    if (group->unlinked) {
248 +        pa_log_debug("Unlinking audio group %s (already unlinked, this is a no-op).", group->name);
249 +        return;
250 +    }
251 +
252 +    group->unlinked = true;
253 +
254 +    pa_log_debug("Unlinking audio group %s.", group->name);
255 +
256 +    if (group->linked)
257 +        pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
258 +
259 +    pa_volume_api_remove_audio_group(group->volume_api, group);
260 +
261 +    while ((stream = pa_hashmap_first(group->mute_streams)))
262 +        pas_stream_set_audio_group_for_mute(stream, NULL);
263 +
264 +    while ((stream = pa_hashmap_first(group->volume_streams)))
265 +        pas_stream_set_audio_group_for_volume(stream, NULL);
266 +
267 +    if (group->mute_control_binding) {
268 +        pa_binding_free(group->mute_control_binding);
269 +        group->mute_control_binding = NULL;
270 +    }
271 +
272 +    if (group->volume_control_binding) {
273 +        pa_binding_free(group->volume_control_binding);
274 +        group->volume_control_binding = NULL;
275 +    }
276 +
277 +    pa_audio_group_set_have_own_mute_control(group, false);
278 +    pa_audio_group_set_have_own_volume_control(group, false);
279 +
280 +    if (group->mute_control) {
281 +        pa_mute_control_remove_audio_group(group->mute_control, group);
282 +        group->mute_control = NULL;
283 +    }
284 +
285 +    if (group->volume_control) {
286 +        pa_volume_control_remove_audio_group(group->volume_control, group);
287 +        group->volume_control = NULL;
288 +    }
289 +}
290 +
291 +void pa_audio_group_free(pa_audio_group *group) {
292 +    pa_assert(group);
293 +
294 +    if (!group->unlinked)
295 +        pa_audio_group_unlink(group);
296 +
297 +    if (group->mute_streams)
298 +        pa_hashmap_free(group->mute_streams);
299 +
300 +    if (group->volume_streams)
301 +        pa_hashmap_free(group->volume_streams);
302 +
303 +    if (group->proplist)
304 +        pa_proplist_free(group->proplist);
305 +
306 +    pa_xfree(group->description);
307 +
308 +    if (group->name)
309 +        pa_volume_api_unregister_name(group->volume_api, group->name);
310 +
311 +    pa_xfree(group);
312 +}
313 +
314 +const char *pa_audio_group_get_name(pa_audio_group *group) {
315 +    pa_assert(group);
316 +
317 +    return group->name;
318 +}
319 +
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;
324 +    void *state;
325 +
326 +    pa_assert(control);
327 +    pa_assert(volume);
328 +
329 +    group = control->userdata;
330 +
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);
334 +    }
335 +
336 +    return 0;
337 +}
338 +
339 +static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
340 +    pa_audio_group *group;
341 +    pas_stream *stream;
342 +    void *state;
343 +
344 +    pa_assert(control);
345 +
346 +    group = control->userdata;
347 +
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);
351 +    }
352 +}
353 +
354 +void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have) {
355 +    pa_assert(group);
356 +
357 +    if (have == group->have_own_volume_control)
358 +        return;
359 +
360 +    if (have) {
361 +        pa_bvolume initial_volume;
362 +
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
367 +             * mode. */
368 +            pa_bvolume_init_mono(&initial_volume, 0.3 * PA_VOLUME_NORM);
369 +        else
370 +            pa_bvolume_init_mono(&initial_volume, PA_VOLUME_NORM);
371 +
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);
379 +    } else {
380 +        pa_volume_control_free(group->own_volume_control);
381 +        group->own_volume_control = NULL;
382 +    }
383 +
384 +    group->have_own_volume_control = have;
385 +}
386 +
387 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
388 +    pa_audio_group *group;
389 +    pas_stream *stream;
390 +    void *state;
391 +
392 +    pa_assert(control);
393 +
394 +    group = control->userdata;
395 +
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);
399 +    }
400 +
401 +    return 0;
402 +}
403 +
404 +static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
405 +    pa_audio_group *group;
406 +    pas_stream *stream;
407 +    void *state;
408 +
409 +    pa_assert(control);
410 +
411 +    group = control->userdata;
412 +
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);
416 +    }
417 +}
418 +
419 +void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) {
420 +    pa_assert(group);
421 +
422 +    if (have == group->have_own_mute_control)
423 +        return;
424 +
425 +    group->have_own_mute_control = have;
426 +
427 +    if (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);
434 +    } else {
435 +        pa_mute_control_free(group->own_mute_control);
436 +        group->own_mute_control = NULL;
437 +    }
438 +}
439 +
440 +static void set_volume_control_internal(pa_audio_group *group, pa_volume_control *control) {
441 +    pa_volume_control *old_control;
442 +
443 +    pa_assert(group);
444 +
445 +    old_control = group->volume_control;
446 +
447 +    if (control == old_control)
448 +        return;
449 +
450 +    if (old_control)
451 +        pa_volume_control_remove_audio_group(old_control, group);
452 +
453 +    group->volume_control = control;
454 +
455 +    if (control)
456 +        pa_volume_control_add_audio_group(control, group);
457 +
458 +    if (!group->linked || group->unlinked)
459 +        return;
460 +
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)");
463 +
464 +    pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], group);
465 +}
466 +
467 +void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
468 +    pa_assert(group);
469 +
470 +    if (group->volume_control_binding) {
471 +        pa_binding_free(group->volume_control_binding);
472 +        group->volume_control_binding = NULL;
473 +    }
474 +
475 +    set_volume_control_internal(group, control);
476 +}
477 +
478 +static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *control) {
479 +    pa_mute_control *old_control;
480 +
481 +    pa_assert(group);
482 +
483 +    old_control = group->mute_control;
484 +
485 +    if (control == old_control)
486 +        return;
487 +
488 +    if (old_control)
489 +        pa_mute_control_remove_audio_group(old_control, group);
490 +
491 +    group->mute_control = control;
492 +
493 +    if (control)
494 +        pa_mute_control_add_audio_group(control, group);
495 +
496 +    if (!group->linked || group->unlinked)
497 +        return;
498 +
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)");
501 +
502 +    pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], group);
503 +}
504 +
505 +void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
506 +    pa_assert(group);
507 +
508 +    if (group->mute_control_binding) {
509 +        pa_binding_free(group->mute_control_binding);
510 +        group->mute_control_binding = NULL;
511 +    }
512 +
513 +    set_mute_control_internal(group, control);
514 +}
515 +
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 = {
518 +        .userdata = group,
519 +        .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
520 +    };
521 +
522 +    pa_assert(group);
523 +    pa_assert(target_info);
524 +
525 +    if (group->volume_control_binding)
526 +        pa_binding_free(group->volume_control_binding);
527 +
528 +    group->volume_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
529 +}
530 +
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 = {
533 +        .userdata = group,
534 +        .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
535 +    };
536 +
537 +    pa_assert(group);
538 +    pa_assert(target_info);
539 +
540 +    if (group->mute_control_binding)
541 +        pa_binding_free(group->mute_control_binding);
542 +
543 +    group->mute_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
544 +}
545 +
546 +void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream) {
547 +    pa_assert(group);
548 +    pa_assert(stream);
549 +
550 +    pa_assert_se(pa_hashmap_put(group->volume_streams, stream, stream) >= 0);
551 +
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);
554 +
555 +    pa_log_debug("Stream %s added to audio group %s (volume).", stream->name, group->name);
556 +}
557 +
558 +void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream) {
559 +    pa_assert(group);
560 +    pa_assert(stream);
561 +
562 +    pa_assert_se(pa_hashmap_remove(group->volume_streams, stream));
563 +
564 +    pa_log_debug("Stream %s removed from audio group %s (volume).", stream->name, group->name);
565 +}
566 +
567 +void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
568 +    pa_assert(group);
569 +    pa_assert(stream);
570 +
571 +    pa_assert_se(pa_hashmap_put(group->mute_streams, stream, stream) >= 0);
572 +
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);
575 +
576 +    pa_log_debug("Stream %s added to audio group %s (mute).", stream->name, group->name);
577 +}
578 +
579 +void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream) {
580 +    pa_assert(group);
581 +    pa_assert(stream);
582 +
583 +    pa_assert_se(pa_hashmap_remove(group->mute_streams, stream));
584 +
585 +    pa_log_debug("Stream %s removed from audio group %s (mute).", stream->name, group->name);
586 +}
587 +
588 +pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api) {
589 +    pa_binding_target_type *type;
590 +
591 +    pa_assert(api);
592 +
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));
601 +
602 +    return type;
603 +}
604 diff --git a/src/modules/volume-api/audio-group.h b/src/modules/volume-api/audio-group.h
605 new file mode 100644
606 index 0000000..41591ba
607 --- /dev/null
608 +++ b/src/modules/volume-api/audio-group.h
609 @@ -0,0 +1,85 @@
610 +#ifndef fooaudiogrouphfoo
611 +#define fooaudiogrouphfoo
612 +
613 +/***
614 +  This file is part of PulseAudio.
615 +
616 +  Copyright 2014 Intel Corporation
617 +
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.
622 +
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.
627 +
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
631 +  USA.
632 +***/
633 +
634 +#include <modules/volume-api/binding.h>
635 +#include <modules/volume-api/mute-control.h>
636 +#include <modules/volume-api/volume-control.h>
637 +
638 +#include <pulse/proplist.h>
639 +
640 +#include <inttypes.h>
641 +
642 +typedef struct pa_audio_group pa_audio_group;
643 +
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"
647 +
648 +struct pa_audio_group {
649 +    pa_volume_api *volume_api;
650 +    uint32_t index;
651 +    const char *name;
652 +    char *description;
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;
660 +
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) */
665 +
666 +    bool linked;
667 +    bool unlinked;
668 +};
669 +
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);
674 +
675 +const char *pa_audio_group_get_name(pa_audio_group *group);
676 +
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);
684 +
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);
690 +
691 +/* Called from volume-api.c only. */
692 +pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api);
693 +
694 +#endif
695 diff --git a/src/modules/volume-api/binding.c b/src/modules/volume-api/binding.c
696 new file mode 100644
697 index 0000000..6e73119
698 --- /dev/null
699 +++ b/src/modules/volume-api/binding.c
700 @@ -0,0 +1,386 @@
701 +/***
702 +  This file is part of PulseAudio.
703 +
704 +  Copyright 2014 Intel Corporation
705 +
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.
710 +
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.
715 +
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
719 +  USA.
720 +***/
721 +
722 +#ifdef HAVE_CONFIG_H
723 +#include <config.h>
724 +#endif
725 +
726 +#include "binding.h"
727 +
728 +#include <pulse/def.h>
729 +#include <pulse/xmalloc.h>
730 +
731 +#include <pulsecore/core-util.h>
732 +#include <pulsecore/macro.h>
733 +
734 +struct field_entry {
735 +    char *name;
736 +    size_t offset;
737 +};
738 +
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);
741 +
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;
744 +
745 +    pa_assert(set_value);
746 +
747 +    info = pa_xnew0(pa_binding_owner_info, 1);
748 +    info->set_value = set_value;
749 +    info->userdata = userdata;
750 +
751 +    return info;
752 +}
753 +
754 +pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info) {
755 +    pa_assert(info);
756 +
757 +    return pa_binding_owner_info_new(info->set_value, info->userdata);
758 +}
759 +
760 +void pa_binding_owner_info_free(pa_binding_owner_info *info) {
761 +    pa_assert(info);
762 +
763 +    pa_xfree(info);
764 +}
765 +
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;
768 +
769 +    pa_assert(type);
770 +    pa_assert(name);
771 +    pa_assert(field);
772 +
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);
777 +
778 +    return info;
779 +}
780 +
781 +int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info) {
782 +    const char *colon;
783 +    char *type = NULL;
784 +    char *name = NULL;
785 +
786 +    pa_assert(str);
787 +    pa_assert(field);
788 +    pa_assert(info);
789 +
790 +    if (!pa_startswith(str, "bind:"))
791 +        goto fail;
792 +
793 +    colon = strchr(str + 5, ':');
794 +    if (!colon)
795 +        goto fail;
796 +
797 +    type = pa_xstrndup(str + 5, colon - (str + 5));
798 +
799 +    if (!*type)
800 +        goto fail;
801 +
802 +    name = pa_xstrdup(colon + 1);
803 +
804 +    if (!*name)
805 +        goto fail;
806 +
807 +    *info = pa_binding_target_info_new(type, name, field);
808 +    pa_xfree(name);
809 +    pa_xfree(type);
810 +
811 +    return 0;
812 +
813 +fail:
814 +    pa_log("Invalid binding target: %s", str);
815 +    pa_xfree(name);
816 +    pa_xfree(type);
817 +
818 +    return -PA_ERR_INVALID;
819 +}
820 +
821 +pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info) {
822 +    pa_assert(info);
823 +
824 +    return pa_binding_target_info_new(info->type, info->name, info->field);
825 +}
826 +
827 +void pa_binding_target_info_free(pa_binding_target_info *info) {
828 +    pa_assert(info);
829 +
830 +    pa_xfree(info->field);
831 +    pa_xfree(info->name);
832 +    pa_xfree(info->type);
833 +    pa_xfree(info);
834 +}
835 +
836 +static void field_entry_free(struct field_entry *entry) {
837 +    pa_assert(entry);
838 +
839 +    pa_xfree(entry->name);
840 +    pa_xfree(entry);
841 +}
842 +
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;
846 +
847 +    pa_assert(name);
848 +    pa_assert(objects);
849 +    pa_assert(put_hook);
850 +    pa_assert(unlink_hook);
851 +    pa_assert(get_name);
852 +
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);
860 +
861 +    return type;
862 +}
863 +
864 +void pa_binding_target_type_free(pa_binding_target_type *type) {
865 +    pa_assert(type);
866 +
867 +    if (type->fields)
868 +        pa_hashmap_free(type->fields);
869 +
870 +    pa_xfree(type->name);
871 +    pa_xfree(type);
872 +}
873 +
874 +void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset) {
875 +    struct field_entry *entry;
876 +
877 +    pa_assert(type);
878 +    pa_assert(name);
879 +
880 +    entry = pa_xnew0(struct field_entry, 1);
881 +    entry->name = pa_xstrdup(name);
882 +    entry->offset = offset;
883 +
884 +    pa_assert_se(pa_hashmap_put(type->fields, entry->name, entry) >= 0);
885 +}
886 +
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;
889 +
890 +    pa_assert(type);
891 +    pa_assert(field);
892 +    pa_assert(offset);
893 +
894 +    entry = pa_hashmap_get(type->fields, field);
895 +    if (!entry)
896 +        return -PA_ERR_NOENTITY;
897 +
898 +    *offset = entry->offset;
899 +
900 +    return 0;
901 +}
902 +
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;
906 +
907 +    pa_assert(type);
908 +    pa_assert(binding);
909 +
910 +    if (!pa_streq(type->name, binding->target_info->type))
911 +        return PA_HOOK_OK;
912 +
913 +    set_target_type(binding, type);
914 +
915 +    return PA_HOOK_OK;
916 +}
917 +
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;
921 +
922 +    pa_assert(type);
923 +    pa_assert(binding);
924 +
925 +    if (type != binding->target_type)
926 +        return PA_HOOK_OK;
927 +
928 +    set_target_type(binding, NULL);
929 +
930 +    return PA_HOOK_OK;
931 +}
932 +
933 +static pa_hook_result_t target_put_cb(void *hook_data, void *call_data, void *userdata) {
934 +    pa_binding *binding = userdata;
935 +
936 +    pa_assert(call_data);
937 +    pa_assert(binding);
938 +
939 +    if (!pa_streq(binding->target_type->get_name(call_data), binding->target_info->name))
940 +        return PA_HOOK_OK;
941 +
942 +    set_target_object(binding, call_data);
943 +
944 +    return PA_HOOK_OK;
945 +}
946 +
947 +static pa_hook_result_t target_unlink_cb(void *hook_data, void *call_data, void *userdata) {
948 +    pa_binding *binding = userdata;
949 +
950 +    pa_assert(call_data);
951 +    pa_assert(binding);
952 +
953 +    if (call_data != binding->target_object)
954 +        return PA_HOOK_OK;
955 +
956 +    set_target_object(binding, NULL);
957 +
958 +    return PA_HOOK_OK;
959 +}
960 +
961 +static void set_target_object(pa_binding *binding, void *object) {
962 +    pa_assert(binding);
963 +
964 +    binding->target_object = object;
965 +
966 +    if (object) {
967 +        if (binding->target_put_slot) {
968 +            pa_hook_slot_free(binding->target_put_slot);
969 +            binding->target_put_slot = NULL;
970 +        }
971 +
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,
974 +                                                          binding);
975 +
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)));
979 +        else
980 +            binding->owner_info->set_value(binding->owner_info->userdata, NULL);
981 +    } else {
982 +        if (binding->target_unlink_slot) {
983 +            pa_hook_slot_free(binding->target_unlink_slot);
984 +            binding->target_unlink_slot = NULL;
985 +        }
986 +
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);
990 +        } else {
991 +            if (binding->target_put_slot) {
992 +                pa_hook_slot_free(binding->target_put_slot);
993 +                binding->target_put_slot = NULL;
994 +            }
995 +        }
996 +
997 +        binding->owner_info->set_value(binding->owner_info->userdata, NULL);
998 +    }
999 +}
1000 +
1001 +static void set_target_type(pa_binding *binding, pa_binding_target_type *type) {
1002 +    pa_assert(binding);
1003 +
1004 +    binding->target_type = type;
1005 +
1006 +    if (type) {
1007 +        int r;
1008 +
1009 +        if (binding->target_type_added_slot) {
1010 +            pa_hook_slot_free(binding->target_type_added_slot);
1011 +            binding->target_type_added_slot = NULL;
1012 +        }
1013 +
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);
1018 +
1019 +        r = pa_binding_target_type_get_field_offset(type, binding->target_info->field, &binding->target_field_offset);
1020 +        if (r >= 0)
1021 +            binding->target_field_offset_valid = true;
1022 +        else {
1023 +            pa_log_warn("Reference to non-existing field \"%s\" in binding target type \"%s\".", binding->target_info->field,
1024 +                        type->name);
1025 +            binding->target_field_offset_valid = false;
1026 +        }
1027 +
1028 +        set_target_object(binding, pa_hashmap_get(type->objects, binding->target_info->name));
1029 +    } else {
1030 +        if (binding->target_type_removed_slot) {
1031 +            pa_hook_slot_free(binding->target_type_removed_slot);
1032 +            binding->target_type_removed_slot = NULL;
1033 +        }
1034 +
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);
1039 +
1040 +        binding->target_field_offset_valid = false;
1041 +
1042 +        set_target_object(binding, NULL);
1043 +    }
1044 +}
1045 +
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;
1049 +
1050 +    pa_assert(api);
1051 +    pa_assert(owner_info);
1052 +    pa_assert(target_info);
1053 +
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);
1058 +
1059 +    set_target_type(binding, pa_hashmap_get(api->binding_target_types, target_info->type));
1060 +
1061 +    return binding;
1062 +}
1063 +
1064 +void pa_binding_free(pa_binding *binding) {
1065 +    pa_assert(binding);
1066 +
1067 +    if (binding->target_unlink_slot)
1068 +        pa_hook_slot_free(binding->target_unlink_slot);
1069 +
1070 +    if (binding->target_put_slot)
1071 +        pa_hook_slot_free(binding->target_put_slot);
1072 +
1073 +    if (binding->target_type_removed_slot)
1074 +        pa_hook_slot_free(binding->target_type_removed_slot);
1075 +
1076 +    if (binding->target_type_added_slot)
1077 +        pa_hook_slot_free(binding->target_type_added_slot);
1078 +
1079 +    if (binding->target_info)
1080 +        pa_binding_target_info_free(binding->target_info);
1081 +
1082 +    if (binding->owner_info)
1083 +        pa_binding_owner_info_free(binding->owner_info);
1084 +
1085 +    pa_xfree(binding);
1086 +}
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
1090 --- /dev/null
1091 +++ b/src/modules/volume-api/binding.h
1092 @@ -0,0 +1,128 @@
1093 +#ifndef foobindinghfoo
1094 +#define foobindinghfoo
1095 +
1096 +/***
1097 +  This file is part of PulseAudio.
1098 +
1099 +  Copyright 2014 Intel Corporation
1100 +
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.
1105 +
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.
1110 +
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
1114 +  USA.
1115 +***/
1116 +
1117 +#include <modules/volume-api/volume-api.h>
1118 +
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;
1123 +
1124 +typedef void (*pa_binding_set_value_cb_t)(void *userdata, void *value);
1125 +
1126 +struct pa_binding_owner_info {
1127 +    /* This is the object that has the variable that the binding is created
1128 +     * for. */
1129 +    void *userdata;
1130 +
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;
1136 +};
1137 +
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);
1141 +
1142 +struct pa_binding_target_info {
1143 +    /* The target type name as registered with
1144 +     * pa_binding_target_type_register(). */
1145 +    char *type;
1146 +
1147 +    /* The target object name as returned by the get_name callback of
1148 +     * pa_binding_target_type. */
1149 +    char *name;
1150 +
1151 +    /* The target field of the target object. */
1152 +    char *field;
1153 +};
1154 +
1155 +pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field);
1156 +
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);
1159 +
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);
1162 +
1163 +typedef const char *(*pa_binding_target_type_get_name_cb_t)(void *object);
1164 +
1165 +struct pa_binding_target_type {
1166 +    /* Identifier for this target type. */
1167 +    char *name;
1168 +
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;
1172 +
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;
1177 +
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;
1182 +
1183 +    /* Function for getting the name of an object of this target type. */
1184 +    pa_binding_target_type_get_name_cb_t get_name;
1185 +
1186 +    pa_hashmap *fields;
1187 +};
1188 +
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);
1192 +
1193 +/* Useful when calling pa_binding_target_type_add_field(). */
1194 +#define PA_BINDING_CALCULATE_FIELD_OFFSET(type, field) ((size_t) &(((type *) 0)->field))
1195 +
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);
1199 +
1200 +int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset);
1201 +
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;
1214 +};
1215 +
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);
1219 +
1220 +#endif
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
1224 --- /dev/null
1225 +++ b/src/modules/volume-api/bvolume.h
1226 @@ -0,0 +1,43 @@
1227 +#ifndef foobvolumehfoo
1228 +#define foobvolumehfoo
1229 +
1230 +/***
1231 +  This file is part of PulseAudio.
1232 +
1233 +  Copyright 2014 Intel Corporation
1234 +
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.
1239 +
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.
1244 +
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
1248 +  USA.
1249 +***/
1250 +
1251 +#include <pulse/ext-volume-api.h>
1252 +
1253 +typedef pa_ext_volume_api_bvolume pa_bvolume;
1254 +
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
1265 +
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
1268 +
1269 +#endif
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
1273 --- /dev/null
1274 +++ b/src/modules/volume-api/device-creator.c
1275 @@ -0,0 +1,1108 @@
1276 +/***
1277 +  This file is part of PulseAudio.
1278 +
1279 +  Copyright 2014 Intel Corporation
1280 +
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.
1285 +
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.
1290 +
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
1294 +  USA.
1295 +***/
1296 +
1297 +#ifdef HAVE_CONFIG_H
1298 +#include <config.h>
1299 +#endif
1300 +
1301 +#include "device-creator.h"
1302 +
1303 +#include <modules/volume-api/device.h>
1304 +#include <modules/volume-api/mute-control.h>
1305 +#include <modules/volume-api/volume-control.h>
1306 +
1307 +#include <pulsecore/core-util.h>
1308 +#include <pulsecore/i18n.h>
1309 +
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;
1319 +};
1320 +
1321 +enum device_type {
1322 +    DEVICE_TYPE_PORT,
1323 +    DEVICE_TYPE_PORT_MONITOR,
1324 +    DEVICE_TYPE_SINK,
1325 +    DEVICE_TYPE_SOURCE,
1326 +};
1327 +
1328 +struct device_volume_control {
1329 +    struct device *device;
1330 +    pa_volume_control *volume_control;
1331 +
1332 +    bool unlinked;
1333 +
1334 +    pa_hook_slot *volume_changed_slot;
1335 +};
1336 +
1337 +struct device_mute_control {
1338 +    struct device *device;
1339 +    pa_mute_control *mute_control;
1340 +
1341 +    bool unlinked;
1342 +
1343 +    pa_hook_slot *mute_changed_slot;
1344 +};
1345 +
1346 +struct device {
1347 +    pa_device_creator *creator;
1348 +    enum device_type type;
1349 +    pa_device_port *port;
1350 +    pa_sink *sink;
1351 +    pa_source *source;
1352 +    pa_device *device;
1353 +    struct device_volume_control *volume_control;
1354 +    struct device_mute_control *mute_control;
1355 +
1356 +    bool unlinked;
1357 +
1358 +    pa_hook_slot *proplist_changed_slot;
1359 +    pa_hook_slot *port_active_changed_slot;
1360 +    struct device *monitor;
1361 +};
1362 +
1363 +static const char *device_type_from_icon_name(const char *icon_name) {
1364 +    if (!icon_name)
1365 +        return NULL;
1366 +
1367 +    if (pa_streq(icon_name, "audio-input-microphone"))
1368 +        return "microphone";
1369 +
1370 +    if (pa_streq(icon_name, "audio-speakers"))
1371 +        return "speakers";
1372 +
1373 +    if (pa_streq(icon_name, "audio-headphones"))
1374 +        return "headphones";
1375 +
1376 +    return NULL;
1377 +}
1378 +
1379 +static const char *device_type_from_port_name(pa_device_port *port) {
1380 +    pa_assert(port);
1381 +
1382 +    if (strstr(port->name, "analog")) {
1383 +        if (port->direction == PA_DIRECTION_INPUT)
1384 +            return "analog-input";
1385 +        else
1386 +            return "analog-output";
1387 +    }
1388 +
1389 +    if (strstr(port->name, "hdmi")) {
1390 +        if (port->direction == PA_DIRECTION_INPUT)
1391 +            return "hdmi-input";
1392 +        else
1393 +            return "hdmi-output";
1394 +    }
1395 +
1396 +    if (strstr(port->name, "iec958")) {
1397 +        if (port->direction == PA_DIRECTION_INPUT)
1398 +            return "spdif-input";
1399 +        else
1400 +            return "spdif-output";
1401 +    }
1402 +
1403 +    return NULL;
1404 +}
1405 +
1406 +static const char *device_type_from_port(pa_device_port *port) {
1407 +    const char *device_type;
1408 +
1409 +    pa_assert(port);
1410 +
1411 +    device_type = device_type_from_icon_name(pa_proplist_gets(port->proplist, PA_PROP_DEVICE_ICON_NAME));
1412 +    if (device_type)
1413 +        return device_type;
1414 +
1415 +    device_type = device_type_from_port_name(port);
1416 +    if (device_type)
1417 +        return device_type;
1418 +
1419 +    return NULL;
1420 +}
1421 +
1422 +static const char *get_sink_description(pa_sink *sink) {
1423 +    const char *description;
1424 +
1425 +    pa_assert(sink);
1426 +
1427 +    description = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION);
1428 +    if (description)
1429 +        return description;
1430 +
1431 +    return sink->name;
1432 +}
1433 +
1434 +static const char *get_source_description(pa_source *source) {
1435 +    const char *description;
1436 +
1437 +    pa_assert(source);
1438 +
1439 +    description = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION);
1440 +    if (description)
1441 +        return description;
1442 +
1443 +    return source->name;
1444 +}
1445 +
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;
1451 +
1452 +    pa_assert(c);
1453 +    pa_assert(volume);
1454 +
1455 +    control = c->userdata;
1456 +    device = control->device;
1457 +
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);
1462 +            else
1463 +                pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
1464 +            break;
1465 +
1466 +        case DEVICE_TYPE_SINK:
1467 +            pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
1468 +            break;
1469 +
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);
1473 +            break;
1474 +    }
1475 +
1476 +    if (set_volume)
1477 +        bvolume.volume = volume->volume;
1478 +
1479 +    if (set_balance)
1480 +        pa_bvolume_copy_balance(&bvolume, volume);
1481 +
1482 +    pa_bvolume_to_cvolume(&bvolume, &cvolume);
1483 +
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);
1488 +            else
1489 +                pa_source_set_volume(device->source, &cvolume, true, true);
1490 +            break;
1491 +
1492 +        case DEVICE_TYPE_PORT_MONITOR:
1493 +        case DEVICE_TYPE_SOURCE:
1494 +            pa_source_set_volume(device->source, &cvolume, true, true);
1495 +            break;
1496 +
1497 +        case DEVICE_TYPE_SINK:
1498 +            pa_sink_set_volume(device->sink, &cvolume, true, true);
1499 +            break;
1500 +    }
1501 +
1502 +    return 0;
1503 +}
1504 +
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;
1510 +
1511 +    pa_assert(device);
1512 +
1513 +    control = pa_xnew0(struct device_volume_control, 1);
1514 +    control->device = device;
1515 +
1516 +    switch (device->type) {
1517 +        case DEVICE_TYPE_PORT:
1518 +            name = "port-volume-control";
1519 +
1520 +            if (device->port->direction == PA_DIRECTION_OUTPUT)
1521 +                convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
1522 +            else
1523 +                convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1524 +
1525 +            break;
1526 +
1527 +        case DEVICE_TYPE_PORT_MONITOR:
1528 +            name = "port-monitor-volume-control";
1529 +            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1530 +            break;
1531 +
1532 +        case DEVICE_TYPE_SINK:
1533 +            name = "sink-volume-control";
1534 +            convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
1535 +            break;
1536 +
1537 +        case DEVICE_TYPE_SOURCE:
1538 +            name = "source-volume-control";
1539 +            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
1540 +            break;
1541 +    }
1542 +
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;
1548 +
1549 +    return control;
1550 +}
1551 +
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;
1558 +
1559 +    pa_assert(control);
1560 +    pa_assert(call_data);
1561 +
1562 +    device = control->device;
1563 +
1564 +    switch (device->type) {
1565 +        case DEVICE_TYPE_PORT:
1566 +            if (device->port->direction == PA_DIRECTION_OUTPUT)
1567 +                sink = call_data;
1568 +            else
1569 +                source = call_data;
1570 +            break;
1571 +
1572 +        case DEVICE_TYPE_PORT_MONITOR:
1573 +        case DEVICE_TYPE_SOURCE:
1574 +            source = call_data;
1575 +            break;
1576 +
1577 +        case DEVICE_TYPE_SINK:
1578 +            sink = call_data;
1579 +            break;
1580 +    }
1581 +
1582 +    if ((sink && sink != device->sink) || (source && source != device->source))
1583 +        return PA_HOOK_OK;
1584 +
1585 +    if (sink)
1586 +        pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map);
1587 +    else
1588 +        pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map);
1589 +
1590 +    pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true);
1591 +
1592 +    return PA_HOOK_OK;
1593 +}
1594 +
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;
1599 +
1600 +    pa_assert(c);
1601 +
1602 +    control = c->userdata;
1603 +    device = control->device;
1604 +    pa_bvolume_to_cvolume(&control->volume_control->volume, &cvolume);
1605 +
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);
1610 +            else
1611 +                pa_source_set_volume(device->source, &cvolume, true, true);
1612 +            break;
1613 +
1614 +        case DEVICE_TYPE_PORT_MONITOR:
1615 +        case DEVICE_TYPE_SOURCE:
1616 +            pa_source_set_volume(device->source, &cvolume, true, true);
1617 +            break;
1618 +
1619 +        case DEVICE_TYPE_SINK:
1620 +            pa_sink_set_volume(device->sink, &cvolume, true, true);
1621 +            break;
1622 +    }
1623 +}
1624 +
1625 +static void device_volume_control_put(struct device_volume_control *control) {
1626 +    struct device *device;
1627 +    pa_bvolume volume;
1628 +
1629 +    pa_assert(control);
1630 +
1631 +    device = control->device;
1632 +
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);
1639 +            } else {
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);
1643 +            }
1644 +            break;
1645 +
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);
1651 +            break;
1652 +
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);
1657 +            break;
1658 +    }
1659 +
1660 +    pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb);
1661 +}
1662 +
1663 +static void device_volume_control_unlink(struct device_volume_control *control) {
1664 +    pa_assert(control);
1665 +
1666 +    if (control->unlinked)
1667 +        return;
1668 +
1669 +    control->unlinked = true;
1670 +
1671 +    if (control->volume_control)
1672 +        pa_volume_control_unlink(control->volume_control);
1673 +
1674 +    if (control->volume_changed_slot) {
1675 +        pa_hook_slot_free(control->volume_changed_slot);
1676 +        control->volume_changed_slot = NULL;
1677 +    }
1678 +}
1679 +
1680 +static void device_volume_control_free(struct device_volume_control *control) {
1681 +    pa_assert(control);
1682 +
1683 +    if (!control->unlinked)
1684 +        device_volume_control_unlink(control);
1685 +
1686 +    if (control->volume_control)
1687 +        pa_volume_control_free(control->volume_control);
1688 +
1689 +    pa_xfree(control);
1690 +}
1691 +
1692 +static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
1693 +    struct device_mute_control *control;
1694 +    struct device *device;
1695 +
1696 +    pa_assert(c);
1697 +
1698 +    control = c->userdata;
1699 +    device = control->device;
1700 +
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);
1705 +            else
1706 +                pa_source_set_mute(device->source, mute, true);
1707 +            break;
1708 +
1709 +        case DEVICE_TYPE_PORT_MONITOR:
1710 +        case DEVICE_TYPE_SOURCE:
1711 +            pa_source_set_mute(device->source, mute, true);
1712 +            break;
1713 +
1714 +        case DEVICE_TYPE_SINK:
1715 +            pa_sink_set_mute(device->sink, mute, true);
1716 +            break;
1717 +    }
1718 +
1719 +    return 0;
1720 +}
1721 +
1722 +static struct device_mute_control *device_mute_control_new(struct device *device) {
1723 +    struct device_mute_control *control;
1724 +    const char *name = NULL;
1725 +
1726 +    pa_assert(device);
1727 +
1728 +    control = pa_xnew0(struct device_mute_control, 1);
1729 +    control->device = device;
1730 +
1731 +    switch (device->type) {
1732 +        case DEVICE_TYPE_PORT:
1733 +            name = "port-mute-control";
1734 +            break;
1735 +
1736 +        case DEVICE_TYPE_PORT_MONITOR:
1737 +            name = "port-monitor-mute-control";
1738 +            break;
1739 +
1740 +        case DEVICE_TYPE_SINK:
1741 +            name = "sink-mute-control";
1742 +            break;
1743 +
1744 +        case DEVICE_TYPE_SOURCE:
1745 +            name = "source-mute-control";
1746 +            break;
1747 +    }
1748 +
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;
1752 +
1753 +    return control;
1754 +}
1755 +
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;
1761 +    bool mute;
1762 +
1763 +    pa_assert(control);
1764 +    pa_assert(call_data);
1765 +
1766 +    device = control->device;
1767 +
1768 +    switch (device->type) {
1769 +        case DEVICE_TYPE_PORT:
1770 +            if (device->port->direction == PA_DIRECTION_OUTPUT)
1771 +                sink = call_data;
1772 +            else
1773 +                source = call_data;
1774 +            break;
1775 +
1776 +        case DEVICE_TYPE_PORT_MONITOR:
1777 +        case DEVICE_TYPE_SOURCE:
1778 +            source = call_data;
1779 +            break;
1780 +
1781 +        case DEVICE_TYPE_SINK:
1782 +            sink = call_data;
1783 +            break;
1784 +    }
1785 +
1786 +    if ((sink && sink != device->sink) || (source && source != device->source))
1787 +        return PA_HOOK_OK;
1788 +
1789 +    if (sink)
1790 +        mute = sink->muted;
1791 +    else
1792 +        mute = source->muted;
1793 +
1794 +    pa_mute_control_mute_changed(control->mute_control, mute);
1795 +
1796 +    return PA_HOOK_OK;
1797 +}
1798 +
1799 +static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
1800 +    struct device_volume_control *control;
1801 +    struct device *device;
1802 +
1803 +    pa_assert(c);
1804 +
1805 +    control = c->userdata;
1806 +    device = control->device;
1807 +
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);
1812 +            else
1813 +                pa_source_set_mute(device->source, c->mute, true);
1814 +            break;
1815 +
1816 +        case DEVICE_TYPE_PORT_MONITOR:
1817 +        case DEVICE_TYPE_SOURCE:
1818 +            pa_source_set_mute(device->source, c->mute, true);
1819 +            break;
1820 +
1821 +        case DEVICE_TYPE_SINK:
1822 +            pa_sink_set_mute(device->sink, c->mute, true);
1823 +            break;
1824 +    }
1825 +}
1826 +
1827 +static void device_mute_control_put(struct device_mute_control *control) {
1828 +    struct device *device;
1829 +    bool mute = false;
1830 +
1831 +    pa_assert(control);
1832 +
1833 +    device = control->device;
1834 +
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;
1841 +            } else {
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;
1845 +            }
1846 +            break;
1847 +
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;
1853 +            break;
1854 +
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;
1859 +            break;
1860 +    }
1861 +
1862 +    pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb);
1863 +}
1864 +
1865 +static void device_mute_control_unlink(struct device_mute_control *control) {
1866 +    pa_assert(control);
1867 +
1868 +    if (control->unlinked)
1869 +        return;
1870 +
1871 +    control->unlinked = true;
1872 +
1873 +    if (control->mute_control)
1874 +        pa_mute_control_unlink(control->mute_control);
1875 +
1876 +    if (control->mute_changed_slot) {
1877 +        pa_hook_slot_free(control->mute_changed_slot);
1878 +        control->mute_changed_slot = NULL;
1879 +    }
1880 +}
1881 +
1882 +static void device_mute_control_free(struct device_mute_control *control) {
1883 +    pa_assert(control);
1884 +
1885 +    if (!control->unlinked)
1886 +        device_mute_control_unlink(control);
1887 +
1888 +    if (control->mute_control)
1889 +        pa_mute_control_free(control->mute_control);
1890 +
1891 +    pa_xfree(control);
1892 +}
1893 +
1894 +static void device_set_sink_and_source_from_port(struct device *device) {
1895 +    pa_sink *sink;
1896 +    pa_source *source;
1897 +    uint32_t idx;
1898 +
1899 +    pa_assert(device);
1900 +
1901 +    device->sink = NULL;
1902 +    device->source = NULL;
1903 +
1904 +    if (!device->port->active)
1905 +        return;
1906 +
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;
1913 +                        break;
1914 +                    }
1915 +                }
1916 +
1917 +                pa_assert(device->sink);
1918 +            } else {
1919 +                PA_IDXSET_FOREACH(source, device->port->card->sources, idx) {
1920 +                    if (source->active_port == device->port) {
1921 +                        device->source = source;
1922 +                        break;
1923 +                    }
1924 +                }
1925 +
1926 +                pa_assert(device->source);
1927 +            }
1928 +            break;
1929 +
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;
1935 +                    break;
1936 +                }
1937 +            }
1938 +
1939 +            pa_assert(device->sink);
1940 +            break;
1941 +        }
1942 +
1943 +        case DEVICE_TYPE_SINK:
1944 +        case DEVICE_TYPE_SOURCE:
1945 +            pa_assert_not_reached();
1946 +    }
1947 +}
1948 +
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;
1954 +
1955 +    pa_assert(device);
1956 +    pa_assert(call_data);
1957 +
1958 +    switch (device->type) {
1959 +        case DEVICE_TYPE_PORT:
1960 +        case DEVICE_TYPE_PORT_MONITOR:
1961 +            pa_assert_not_reached();
1962 +
1963 +        case DEVICE_TYPE_SINK:
1964 +            sink = call_data;
1965 +
1966 +            if (sink != device->sink)
1967 +                return PA_HOOK_OK;
1968 +
1969 +            description = get_sink_description(sink);
1970 +            break;
1971 +
1972 +        case DEVICE_TYPE_SOURCE:
1973 +            source = call_data;
1974 +
1975 +            if (source != device->source)
1976 +                return PA_HOOK_OK;
1977 +
1978 +            description = get_source_description(source);
1979 +            break;
1980 +    }
1981 +
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);
1985 +
1986 +    return PA_HOOK_OK;
1987 +}
1988 +
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;
1996 +
1997 +    pa_assert(creator);
1998 +    pa_assert(core_device);
1999 +
2000 +    device = pa_xnew0(struct device, 1);
2001 +    device->creator = creator;
2002 +    device->type = type;
2003 +
2004 +    switch (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);
2012 +
2013 +            if (!device->sink && !device->source)
2014 +                create_volume_and_mute_controls = false;
2015 +            break;
2016 +
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;
2023 +
2024 +            if (!device->source)
2025 +                create_volume_and_mute_controls = false;
2026 +            break;
2027 +
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;
2033 +            break;
2034 +
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;
2040 +            break;
2041 +    }
2042 +
2043 +    device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0);
2044 +    pa_xfree(description);
2045 +
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);
2049 +    }
2050 +
2051 +    switch (type) {
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);
2055 +            break;
2056 +
2057 +        case DEVICE_TYPE_PORT_MONITOR:
2058 +            break;
2059 +
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);
2063 +            break;
2064 +
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);
2068 +            break;
2069 +    }
2070 +
2071 +    return device;
2072 +}
2073 +
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;
2078 +
2079 +    pa_assert(device);
2080 +    pa_assert(port);
2081 +
2082 +    if (port != device->port)
2083 +        return PA_HOOK_OK;
2084 +
2085 +    device_set_sink_and_source_from_port(device);
2086 +
2087 +    switch (device->type) {
2088 +        case DEVICE_TYPE_PORT:
2089 +            should_have_volume_and_mute_controls = device->sink || device->source;
2090 +            break;
2091 +
2092 +        case DEVICE_TYPE_PORT_MONITOR:
2093 +            should_have_volume_and_mute_controls = !!device->source;
2094 +            break;
2095 +
2096 +        case DEVICE_TYPE_SINK:
2097 +        case DEVICE_TYPE_SOURCE:
2098 +            pa_assert_not_reached();
2099 +    }
2100 +
2101 +    if (should_have_volume_and_mute_controls && !device->volume_control) {
2102 +        pa_assert(!device->mute_control);
2103 +
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);
2107 +
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);
2111 +    }
2112 +
2113 +    if (!should_have_volume_and_mute_controls && device->volume_control) {
2114 +        pa_assert(device->mute_control);
2115 +
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;
2120 +    }
2121 +
2122 +    return PA_HOOK_OK;
2123 +}
2124 +
2125 +static void device_put(struct device *device) {
2126 +    pa_assert(device);
2127 +
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);
2133 +
2134 +        case DEVICE_TYPE_SINK:
2135 +        case DEVICE_TYPE_SOURCE:
2136 +            break;
2137 +    }
2138 +
2139 +    if (device->volume_control)
2140 +        device_volume_control_put(device->volume_control);
2141 +
2142 +    if (device->mute_control)
2143 +        device_mute_control_put(device->mute_control);
2144 +
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);
2147 +
2148 +    if (device->monitor)
2149 +        device_put(device->monitor);
2150 +}
2151 +
2152 +static void device_unlink(struct device *device) {
2153 +    pa_assert(device);
2154 +
2155 +    if (device->unlinked)
2156 +        return;
2157 +
2158 +    device->unlinked = true;
2159 +
2160 +    if (device->monitor)
2161 +        device_unlink(device->monitor);
2162 +
2163 +    if (device->device)
2164 +        pa_device_unlink(device->device);
2165 +
2166 +    if (device->mute_control)
2167 +        device_mute_control_unlink(device->mute_control);
2168 +
2169 +    if (device->volume_control)
2170 +        device_volume_control_unlink(device->volume_control);
2171 +
2172 +    if (device->port_active_changed_slot) {
2173 +        pa_hook_slot_free(device->port_active_changed_slot);
2174 +        device->port_active_changed_slot = NULL;
2175 +    }
2176 +}
2177 +
2178 +static void device_free(struct device *device) {
2179 +    pa_assert(device);
2180 +
2181 +    if (!device->unlinked)
2182 +        device_unlink(device);
2183 +
2184 +    if (device->monitor)
2185 +        device_free(device->monitor);
2186 +
2187 +    if (device->proplist_changed_slot)
2188 +        pa_hook_slot_free(device->proplist_changed_slot);
2189 +
2190 +    if (device->mute_control)
2191 +        device_mute_control_free(device->mute_control);
2192 +
2193 +    if (device->volume_control)
2194 +        device_volume_control_free(device->volume_control);
2195 +
2196 +    if (device->device)
2197 +        pa_device_free(device->device);
2198 +
2199 +    pa_xfree(device);
2200 +}
2201 +
2202 +static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) {
2203 +    struct device *device;
2204 +
2205 +    pa_assert(creator);
2206 +    pa_assert(core_device);
2207 +
2208 +    switch (type) {
2209 +        case DEVICE_TYPE_PORT:
2210 +            break;
2211 +
2212 +        case DEVICE_TYPE_PORT_MONITOR:
2213 +            pa_assert_not_reached();
2214 +
2215 +        case DEVICE_TYPE_SINK:
2216 +            if (!pa_hashmap_isempty(((pa_sink *) core_device)->ports))
2217 +                return;
2218 +            break;
2219 +
2220 +        case DEVICE_TYPE_SOURCE: {
2221 +            pa_source *source = core_device;
2222 +
2223 +            if (source->monitor_of && !pa_hashmap_isempty(source->monitor_of->ports))
2224 +                return;
2225 +
2226 +            if (!pa_hashmap_isempty(((pa_source *) core_device)->ports))
2227 +                return;
2228 +            break;
2229 +        }
2230 +    }
2231 +
2232 +    device = device_new(creator, type, core_device);
2233 +    pa_hashmap_put(creator->devices, core_device, device);
2234 +    device_put(device);
2235 +}
2236 +
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;
2241 +    void *state;
2242 +
2243 +    pa_assert(creator);
2244 +    pa_assert(card);
2245 +
2246 +    PA_HASHMAP_FOREACH(port, card->ports, state)
2247 +        create_device(creator, DEVICE_TYPE_PORT, port);
2248 +
2249 +    return PA_HOOK_OK;
2250 +}
2251 +
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;
2256 +    void *state;
2257 +
2258 +    pa_assert(creator);
2259 +    pa_assert(card);
2260 +
2261 +    PA_HASHMAP_FOREACH(port, card->ports, state)
2262 +        pa_hashmap_remove_and_free(creator->devices, port);
2263 +
2264 +    return PA_HOOK_OK;
2265 +}
2266 +
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;
2270 +
2271 +    pa_assert(creator);
2272 +    pa_assert(sink);
2273 +
2274 +    create_device(creator, DEVICE_TYPE_SINK, sink);
2275 +
2276 +    return PA_HOOK_OK;
2277 +}
2278 +
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;
2282 +
2283 +    pa_assert(creator);
2284 +    pa_assert(sink);
2285 +
2286 +    pa_hashmap_remove_and_free(creator->devices, sink);
2287 +
2288 +    return PA_HOOK_OK;
2289 +}
2290 +
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;
2294 +
2295 +    pa_assert(creator);
2296 +    pa_assert(source);
2297 +
2298 +    create_device(creator, DEVICE_TYPE_SOURCE, source);
2299 +
2300 +    return PA_HOOK_OK;
2301 +}
2302 +
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;
2306 +
2307 +    pa_assert(creator);
2308 +    pa_assert(source);
2309 +
2310 +    pa_hashmap_remove_and_free(creator->devices, source);
2311 +
2312 +    return PA_HOOK_OK;
2313 +}
2314 +
2315 +pa_device_creator *pa_device_creator_new(pa_volume_api *api) {
2316 +    pa_device_creator *creator;
2317 +    pa_card *card;
2318 +    uint32_t idx;
2319 +    pa_sink *sink;
2320 +    pa_source *source;
2321 +
2322 +    pa_assert(api);
2323 +
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,
2329 +                                                creator);
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,
2332 +                                                creator);
2333 +    creator->source_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, source_put_cb,
2334 +                                               creator);
2335 +    creator->source_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_NORMAL,
2336 +                                                  source_unlink_cb, creator);
2337 +
2338 +    PA_IDXSET_FOREACH(card, api->core->cards, idx) {
2339 +        pa_device_port *port;
2340 +        void *state;
2341 +
2342 +        PA_HASHMAP_FOREACH(port, card->ports, state)
2343 +            create_device(creator, DEVICE_TYPE_PORT, port);
2344 +    }
2345 +
2346 +    PA_IDXSET_FOREACH(sink, api->core->sinks, idx)
2347 +        create_device(creator, DEVICE_TYPE_SINK, sink);
2348 +
2349 +    PA_IDXSET_FOREACH(source, api->core->sources, idx)
2350 +        create_device(creator, DEVICE_TYPE_SOURCE, source);
2351 +
2352 +    return creator;
2353 +}
2354 +
2355 +void pa_device_creator_free(pa_device_creator *creator) {
2356 +    pa_assert(creator);
2357 +
2358 +    if (creator->devices)
2359 +        pa_hashmap_remove_all(creator->devices);
2360 +
2361 +    if (creator->source_unlink_slot)
2362 +        pa_hook_slot_free(creator->source_unlink_slot);
2363 +
2364 +    if (creator->source_put_slot)
2365 +        pa_hook_slot_free(creator->source_put_slot);
2366 +
2367 +    if (creator->sink_unlink_slot)
2368 +        pa_hook_slot_free(creator->sink_unlink_slot);
2369 +
2370 +    if (creator->sink_put_slot)
2371 +        pa_hook_slot_free(creator->sink_put_slot);
2372 +
2373 +    if (creator->card_unlink_slot)
2374 +        pa_hook_slot_free(creator->card_unlink_slot);
2375 +
2376 +    if (creator->card_put_slot)
2377 +        pa_hook_slot_free(creator->card_put_slot);
2378 +
2379 +    if (creator->devices)
2380 +        pa_hashmap_free(creator->devices);
2381 +
2382 +    pa_xfree(creator);
2383 +}
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
2387 --- /dev/null
2388 +++ b/src/modules/volume-api/device-creator.h
2389 @@ -0,0 +1,32 @@
2390 +#ifndef foodevicecreatorhfoo
2391 +#define foodevicecreatorhfoo
2392 +
2393 +/***
2394 +  This file is part of PulseAudio.
2395 +
2396 +  Copyright 2014 Intel Corporation
2397 +
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.
2402 +
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.
2407 +
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
2411 +  USA.
2412 +***/
2413 +
2414 +#include <modules/volume-api/volume-api.h>
2415 +
2416 +typedef struct pa_device_creator pa_device_creator;
2417 +
2418 +pa_device_creator *pa_device_creator_new(pa_volume_api *api);
2419 +void pa_device_creator_free(pa_device_creator *creator);
2420 +
2421 +#endif
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
2425 --- /dev/null
2426 +++ b/src/modules/volume-api/device.c
2427 @@ -0,0 +1,293 @@
2428 +/***
2429 +  This file is part of PulseAudio.
2430 +
2431 +  Copyright 2014 Intel Corporation
2432 +
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.
2437 +
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.
2442 +
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
2446 +  USA.
2447 +***/
2448 +
2449 +#ifdef HAVE_CONFIG_H
2450 +#include <config.h>
2451 +#endif
2452 +
2453 +#include "device.h"
2454 +
2455 +#include <modules/volume-api/mute-control.h>
2456 +#include <modules/volume-api/volume-control.h>
2457 +
2458 +#include <pulse/direction.h>
2459 +
2460 +#include <pulsecore/core-util.h>
2461 +
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;
2465 +    unsigned i;
2466 +
2467 +    pa_assert(api);
2468 +    pa_assert(name);
2469 +    pa_assert(description);
2470 +    pa_assert(device_types || n_device_types == 0);
2471 +
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);
2479 +
2480 +    for (i = 0; i < n_device_types; i++)
2481 +        pa_dynarray_append(device->device_types, pa_xstrdup(device_types[i]));
2482 +
2483 +    device->proplist = pa_proplist_new();
2484 +    device->use_default_volume_control = true;
2485 +    device->use_default_mute_control = true;
2486 +
2487 +    return device;
2488 +}
2489 +
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;
2494 +
2495 +    pa_assert(device);
2496 +
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);
2500 +
2501 +        device->volume_control = default_volume_control;
2502 +        pa_volume_control_add_device(default_volume_control, device);
2503 +    }
2504 +
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);
2508 +
2509 +        device->mute_control = default_mute_control;
2510 +        pa_mute_control_add_device(default_mute_control, device);
2511 +    }
2512 +
2513 +    pa_volume_api_add_device(device->volume_api, device);
2514 +
2515 +    device->linked = true;
2516 +
2517 +    device_types_str = pa_join((const char * const *) pa_dynarray_get_raw_array(device->device_types),
2518 +                               pa_dynarray_size(device->device_types), ", ");
2519 +
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:");
2528 +
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)));
2531 +
2532 +    pa_xfree(device_types_str);
2533 +
2534 +    pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_PUT], device);
2535 +}
2536 +
2537 +void pa_device_unlink(pa_device *device) {
2538 +    pa_assert(device);
2539 +
2540 +    if (device->unlinked) {
2541 +        pa_log_debug("Unlinking device %s (already unlinked, this is a no-op).", device->name);
2542 +        return;
2543 +    }
2544 +
2545 +    device->unlinked = true;
2546 +
2547 +    pa_log_debug("Unlinking device %s.", device->name);
2548 +
2549 +    if (device->linked)
2550 +        pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
2551 +
2552 +    pa_volume_api_remove_device(device->volume_api, device);
2553 +
2554 +    if (device->mute_control) {
2555 +        pa_mute_control_remove_device(device->mute_control, device);
2556 +        device->mute_control = NULL;
2557 +    }
2558 +
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;
2562 +    }
2563 +
2564 +    if (device->volume_control) {
2565 +        pa_volume_control_remove_device(device->volume_control, device);
2566 +        device->volume_control = NULL;
2567 +    }
2568 +
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;
2572 +    }
2573 +}
2574 +
2575 +void pa_device_free(pa_device *device) {
2576 +    pa_assert(device);
2577 +
2578 +    if (!device->unlinked)
2579 +        pa_device_unlink(device);
2580 +
2581 +    if (device->proplist)
2582 +        pa_proplist_free(device->proplist);
2583 +
2584 +    if (device->device_types)
2585 +        pa_dynarray_free(device->device_types);
2586 +
2587 +    pa_xfree(device->description);
2588 +
2589 +    if (device->name)
2590 +        pa_volume_api_unregister_name(device->volume_api, device->name);
2591 +
2592 +    pa_xfree(device);
2593 +}
2594 +
2595 +static void set_volume_control_internal(pa_device *device, pa_volume_control *control) {
2596 +    pa_volume_control *old_control;
2597 +
2598 +    pa_assert(device);
2599 +
2600 +    old_control = device->volume_control;
2601 +
2602 +    if (control == old_control)
2603 +        return;
2604 +
2605 +    if (old_control)
2606 +        pa_volume_control_remove_device(old_control, device);
2607 +
2608 +    device->volume_control = control;
2609 +
2610 +    if (control)
2611 +        pa_volume_control_add_device(control, device);
2612 +
2613 +    if (!device->linked || device->unlinked)
2614 +        return;
2615 +
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)");
2618 +
2619 +    pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED], device);
2620 +}
2621 +
2622 +void pa_device_set_volume_control(pa_device *device, pa_volume_control *control) {
2623 +    pa_assert(device);
2624 +
2625 +    device->use_default_volume_control = false;
2626 +    set_volume_control_internal(device, control);
2627 +}
2628 +
2629 +static void set_mute_control_internal(pa_device *device, pa_mute_control *control) {
2630 +    pa_mute_control *old_control;
2631 +
2632 +    pa_assert(device);
2633 +
2634 +    old_control = device->mute_control;
2635 +
2636 +    if (control == old_control)
2637 +        return;
2638 +
2639 +    if (old_control)
2640 +        pa_mute_control_remove_device(old_control, device);
2641 +
2642 +    device->mute_control = control;
2643 +
2644 +    if (control)
2645 +        pa_mute_control_add_device(control, device);
2646 +
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)");
2649 +
2650 +    pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED], device);
2651 +}
2652 +
2653 +void pa_device_set_mute_control(pa_device *device, pa_mute_control *control) {
2654 +    pa_assert(device);
2655 +
2656 +    device->use_default_mute_control = false;
2657 +    set_mute_control_internal(device, control);
2658 +}
2659 +
2660 +void pa_device_description_changed(pa_device *device, const char *new_description) {
2661 +    char *old_description;
2662 +
2663 +    pa_assert(device);
2664 +    pa_assert(new_description);
2665 +
2666 +    old_description = device->description;
2667 +
2668 +    if (pa_streq(new_description, old_description))
2669 +        return;
2670 +
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,
2673 +                 new_description);
2674 +    pa_xfree(old_description);
2675 +    pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED], device);
2676 +}
2677 +
2678 +void pa_device_set_default_volume_control(pa_device *device, pa_volume_control *control) {
2679 +    pa_volume_control *old_control;
2680 +
2681 +    pa_assert(device);
2682 +
2683 +    old_control = device->default_volume_control;
2684 +
2685 +    if (control == old_control)
2686 +        return;
2687 +
2688 +    if (old_control)
2689 +        pa_volume_control_remove_default_for_device(old_control, device);
2690 +
2691 +    device->default_volume_control = control;
2692 +
2693 +    if (control)
2694 +        pa_volume_control_add_default_for_device(control, device);
2695 +
2696 +    if (device->use_default_volume_control)
2697 +        set_volume_control_internal(device, control);
2698 +}
2699 +
2700 +void pa_device_set_default_mute_control(pa_device *device, pa_mute_control *control) {
2701 +    pa_mute_control *old_control;
2702 +
2703 +    pa_assert(device);
2704 +
2705 +    old_control = device->default_mute_control;
2706 +
2707 +    if (control == old_control)
2708 +        return;
2709 +
2710 +    if (old_control)
2711 +        pa_mute_control_remove_default_for_device(old_control, device);
2712 +
2713 +    device->default_mute_control = control;
2714 +
2715 +    if (control)
2716 +        pa_mute_control_add_default_for_device(control, device);
2717 +
2718 +    if (device->use_default_mute_control)
2719 +        set_mute_control_internal(device, control);
2720 +}
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
2724 --- /dev/null
2725 +++ b/src/modules/volume-api/device.h
2726 @@ -0,0 +1,76 @@
2727 +#ifndef foodevicehfoo
2728 +#define foodevicehfoo
2729 +
2730 +/***
2731 +  This file is part of PulseAudio.
2732 +
2733 +  Copyright 2014 Intel Corporation
2734 +
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.
2739 +
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.
2744 +
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
2748 +  USA.
2749 +***/
2750 +
2751 +#include <modules/volume-api/volume-api.h>
2752 +
2753 +#include <pulsecore/dynarray.h>
2754 +
2755 +typedef struct pa_device pa_device;
2756 +
2757 +struct pa_device {
2758 +    pa_volume_api *volume_api;
2759 +    uint32_t index;
2760 +    const char *name;
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;
2767 +
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;
2775 +
2776 +    bool linked;
2777 +    bool unlinked;
2778 +};
2779 +
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);
2785 +
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);
2789 +
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
2793 + * flags. */
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);
2796 +
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);
2801 +
2802 +#endif
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
2806 --- /dev/null
2807 +++ b/src/modules/volume-api/mute-control.c
2808 @@ -0,0 +1,306 @@
2809 +/***
2810 +  This file is part of PulseAudio.
2811 +
2812 +  Copyright 2014 Intel Corporation
2813 +
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.
2818 +
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.
2823 +
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
2827 +  USA.
2828 +***/
2829 +
2830 +#ifdef HAVE_CONFIG_H
2831 +#include <config.h>
2832 +#endif
2833 +
2834 +#include "mute-control.h"
2835 +
2836 +#include <modules/volume-api/audio-group.h>
2837 +#include <modules/volume-api/device.h>
2838 +#include <modules/volume-api/sstream.h>
2839 +
2840 +#include <pulsecore/core-util.h>
2841 +
2842 +pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) {
2843 +    pa_mute_control *control;
2844 +
2845 +    pa_assert(api);
2846 +    pa_assert(name);
2847 +    pa_assert(description);
2848 +
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);
2859 +
2860 +    return control;
2861 +}
2862 +
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;
2867 +
2868 +    pa_assert(control);
2869 +    pa_assert(initial_mute_is_set || control->set_mute);
2870 +    pa_assert(set_initial_mute_cb || !control->set_mute);
2871 +
2872 +    if (initial_mute_is_set)
2873 +        control->mute = initial_mute;
2874 +    else
2875 +        control->mute = false;
2876 +
2877 +    if (set_initial_mute_cb)
2878 +        set_initial_mute_cb(control);
2879 +
2880 +    pa_volume_api_add_mute_control(control->volume_api, control);
2881 +
2882 +    control->linked = true;
2883 +
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:");
2889 +
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)));
2892 +
2893 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT], control);
2894 +}
2895 +
2896 +void pa_mute_control_unlink(pa_mute_control *control) {
2897 +    pa_audio_group *group;
2898 +    pa_device *device;
2899 +    pas_stream *stream;
2900 +
2901 +    pa_assert(control);
2902 +
2903 +    if (control->unlinked) {
2904 +        pa_log_debug("Unlinking mute control %s (already unlinked, this is a no-op).", control->name);
2905 +        return;
2906 +    }
2907 +
2908 +    control->unlinked = true;
2909 +
2910 +    pa_log_debug("Unlinking mute control %s.", control->name);
2911 +
2912 +    if (control->linked)
2913 +        pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
2914 +
2915 +    pa_volume_api_remove_mute_control(control->volume_api, control);
2916 +
2917 +    while ((group = pa_hashmap_first(control->audio_groups)))
2918 +        pa_audio_group_set_mute_control(group, NULL);
2919 +
2920 +    while ((stream = pa_hashmap_first(control->streams)))
2921 +        pas_stream_set_mute_control(stream, NULL);
2922 +
2923 +    while ((device = pa_hashmap_first(control->default_for_devices)))
2924 +        pa_device_set_default_mute_control(device, NULL);
2925 +
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);
2938 +    }
2939 +}
2940 +
2941 +void pa_mute_control_free(pa_mute_control *control) {
2942 +    pa_assert(control);
2943 +
2944 +    if (!control->unlinked)
2945 +        pa_mute_control_unlink(control);
2946 +
2947 +    if (control->audio_groups) {
2948 +        pa_assert(pa_hashmap_isempty(control->audio_groups));
2949 +        pa_hashmap_free(control->audio_groups);
2950 +    }
2951 +
2952 +    if (control->streams) {
2953 +        pa_assert(pa_hashmap_isempty(control->streams));
2954 +        pa_hashmap_free(control->streams);
2955 +    }
2956 +
2957 +    if (control->default_for_devices) {
2958 +        pa_assert(pa_hashmap_isempty(control->default_for_devices));
2959 +        pa_hashmap_free(control->default_for_devices);
2960 +    }
2961 +
2962 +    if (control->devices) {
2963 +        pa_assert(pa_hashmap_isempty(control->devices));
2964 +        pa_hashmap_free(control->devices);
2965 +    }
2966 +
2967 +    if (control->proplist)
2968 +        pa_proplist_free(control->proplist);
2969 +
2970 +    pa_xfree(control->description);
2971 +
2972 +    if (control->name)
2973 +        pa_volume_api_unregister_name(control->volume_api, control->name);
2974 +
2975 +    pa_xfree(control);
2976 +}
2977 +
2978 +void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) {
2979 +    pa_assert(control);
2980 +    pa_assert(group);
2981 +
2982 +    control->owner_audio_group = group;
2983 +}
2984 +
2985 +static void set_mute_internal(pa_mute_control *control, bool mute) {
2986 +    bool old_mute;
2987 +
2988 +    pa_assert(control);
2989 +
2990 +    old_mute = control->mute;
2991 +
2992 +    if (mute == old_mute)
2993 +        return;
2994 +
2995 +    control->mute = mute;
2996 +
2997 +    if (!control->linked || control->unlinked)
2998 +        return;
2999 +
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));
3002 +
3003 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
3004 +}
3005 +
3006 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
3007 +    int r;
3008 +
3009 +    pa_assert(control);
3010 +
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.",
3013 +                    control->name);
3014 +        return -PA_ERR_NOTSUPPORTED;
3015 +    }
3016 +
3017 +    if (mute == control->mute)
3018 +        return 0;
3019 +
3020 +    control->set_mute_in_progress = true;
3021 +    r = control->set_mute(control, mute);
3022 +    control->set_mute_in_progress = false;
3023 +
3024 +    if (r >= 0)
3025 +        set_mute_internal(control, mute);
3026 +
3027 +    return r;
3028 +}
3029 +
3030 +void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) {
3031 +    char *old_description;
3032 +
3033 +    pa_assert(control);
3034 +    pa_assert(new_description);
3035 +
3036 +    old_description = control->description;
3037 +
3038 +    if (pa_streq(new_description, old_description))
3039 +        return;
3040 +
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,
3043 +                 new_description);
3044 +    pa_xfree(old_description);
3045 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED], control);
3046 +}
3047 +
3048 +void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute) {
3049 +    pa_assert(control);
3050 +
3051 +    if (!control->linked)
3052 +        return;
3053 +
3054 +    if (control->set_mute_in_progress)
3055 +        return;
3056 +
3057 +    set_mute_internal(control, new_mute);
3058 +}
3059 +
3060 +void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) {
3061 +    pa_assert(control);
3062 +    pa_assert(device);
3063 +
3064 +    pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0);
3065 +}
3066 +
3067 +void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device) {
3068 +    pa_assert(control);
3069 +    pa_assert(device);
3070 +
3071 +    pa_assert_se(pa_hashmap_remove(control->devices, device));
3072 +}
3073 +
3074 +void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device) {
3075 +    pa_assert(control);
3076 +    pa_assert(device);
3077 +
3078 +    pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0);
3079 +}
3080 +
3081 +void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device) {
3082 +    pa_assert(control);
3083 +    pa_assert(device);
3084 +
3085 +    pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
3086 +}
3087 +
3088 +void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream) {
3089 +    pa_assert(control);
3090 +    pa_assert(stream);
3091 +
3092 +    pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
3093 +}
3094 +
3095 +void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream) {
3096 +    pa_assert(control);
3097 +    pa_assert(stream);
3098 +
3099 +    pa_assert_se(pa_hashmap_remove(control->streams, stream));
3100 +}
3101 +
3102 +void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group) {
3103 +    pa_assert(control);
3104 +    pa_assert(group);
3105 +
3106 +    pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
3107 +}
3108 +
3109 +void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group) {
3110 +    pa_assert(control);
3111 +    pa_assert(group);
3112 +
3113 +    pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
3114 +}
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
3118 --- /dev/null
3119 +++ b/src/modules/volume-api/mute-control.h
3120 @@ -0,0 +1,102 @@
3121 +#ifndef foomutecontrolhfoo
3122 +#define foomutecontrolhfoo
3123 +
3124 +/***
3125 +  This file is part of PulseAudio.
3126 +
3127 +  Copyright 2014 Intel Corporation
3128 +
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.
3133 +
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.
3138 +
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
3142 +  USA.
3143 +***/
3144 +
3145 +#include <modules/volume-api/volume-api.h>
3146 +
3147 +typedef struct pa_mute_control pa_mute_control;
3148 +
3149 +struct pa_mute_control {
3150 +    pa_volume_api *volume_api;
3151 +    uint32_t index;
3152 +    const char *name;
3153 +    char *description;
3154 +    pa_proplist *proplist;
3155 +    bool mute;
3156 +
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;
3160 +
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) */
3165 +
3166 +    bool linked;
3167 +    bool unlinked;
3168 +    bool set_mute_in_progress;
3169 +
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);
3174 +
3175 +    void *userdata;
3176 +};
3177 +
3178 +pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description);
3179 +
3180 +typedef void (*pa_mute_control_set_initial_mute_cb_t)(pa_mute_control *control);
3181 +
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.
3186 + *
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
3191 + * read-only. */
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);
3194 +
3195 +void pa_mute_control_unlink(pa_mute_control *control);
3196 +void pa_mute_control_free(pa_mute_control *control);
3197 +
3198 +/* Called by audio-group.c only. */
3199 +void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group);
3200 +
3201 +/* Called by clients and policy modules. */
3202 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
3203 +
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);
3207 +
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);
3213 +
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);
3217 +
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);
3221 +
3222 +#endif
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
3226 --- /dev/null
3227 +++ b/src/modules/volume-api/sstream.c
3228 @@ -0,0 +1,366 @@
3229 +/***
3230 +  This file is part of PulseAudio.
3231 +
3232 +  Copyright 2014 Intel Corporation
3233 +
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.
3238 +
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.
3243 +
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
3247 +  USA.
3248 +***/
3249 +
3250 +#ifdef HAVE_CONFIG_H
3251 +#include <config.h>
3252 +#endif
3253 +
3254 +#include "sstream.h"
3255 +
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>
3260 +
3261 +#include <pulse/direction.h>
3262 +
3263 +#include <pulsecore/core-util.h>
3264 +
3265 +pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) {
3266 +    pas_stream *stream;
3267 +
3268 +    pa_assert(api);
3269 +    pa_assert(name);
3270 +    pa_assert(description);
3271 +
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;
3281 +
3282 +    return stream;
3283 +}
3284 +
3285 +static void set_volume_control_internal(pas_stream *stream, pa_volume_control *control) {
3286 +    pa_volume_control *old_control;
3287 +
3288 +    pa_assert(stream);
3289 +
3290 +    old_control = stream->volume_control;
3291 +
3292 +    if (control == old_control)
3293 +        return;
3294 +
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);
3301 +
3302 +        pa_volume_control_remove_stream(old_control, stream);
3303 +    }
3304 +
3305 +    stream->volume_control = control;
3306 +
3307 +    if (control) {
3308 +        pa_volume_control_add_stream(control, stream);
3309 +        pas_stream_set_audio_group_for_volume(stream, control->owner_audio_group);
3310 +    }
3311 +
3312 +    if (!stream->linked || stream->unlinked)
3313 +        return;
3314 +
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)");
3317 +
3318 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
3319 +}
3320 +
3321 +static void set_mute_control_internal(pas_stream *stream, pa_mute_control *control) {
3322 +    pa_mute_control *old_control;
3323 +
3324 +    pa_assert(stream);
3325 +
3326 +    old_control = stream->mute_control;
3327 +
3328 +    if (control == old_control)
3329 +        return;
3330 +
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);
3337 +
3338 +        pa_mute_control_remove_stream(old_control, stream);
3339 +    }
3340 +
3341 +    stream->mute_control = control;
3342 +
3343 +    if (control) {
3344 +        pa_mute_control_add_stream(control, stream);
3345 +        pas_stream_set_audio_group_for_mute(stream, control->owner_audio_group);
3346 +    }
3347 +
3348 +    if (!stream->linked || stream->unlinked)
3349 +        return;
3350 +
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)");
3353 +
3354 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
3355 +}
3356 +
3357 +void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
3358 +    const char *prop_key;
3359 +    void *state = NULL;
3360 +
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);
3364 +
3365 +    if (initial_properties)
3366 +        pa_proplist_update(stream->proplist, PA_UPDATE_REPLACE, initial_properties);
3367 +
3368 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], stream);
3369 +
3370 +    if (stream->use_default_volume_control)
3371 +        set_volume_control_internal(stream, stream->own_volume_control);
3372 +
3373 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], stream);
3374 +
3375 +    if (stream->use_default_mute_control)
3376 +        set_mute_control_internal(stream, stream->own_mute_control);
3377 +
3378 +    pa_volume_api_add_stream(stream->volume_api, stream);
3379 +
3380 +    stream->linked = true;
3381 +
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:");
3389 +
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)));
3392 +
3393 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], stream);
3394 +}
3395 +
3396 +void pas_stream_unlink(pas_stream *stream) {
3397 +    pa_assert(stream);
3398 +
3399 +    if (stream->unlinked) {
3400 +        pa_log_debug("Unlinking stream %s (already unlinked, this is a no-op).", stream->name);
3401 +        return;
3402 +    }
3403 +
3404 +    stream->unlinked = true;
3405 +
3406 +    pa_log_debug("Unlinking stream %s.", stream->name);
3407 +
3408 +    if (stream->linked)
3409 +        pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
3410 +
3411 +    pa_volume_api_remove_stream(stream->volume_api, stream);
3412 +
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);
3419 +}
3420 +
3421 +void pas_stream_free(pas_stream *stream) {
3422 +    pa_assert(stream);
3423 +
3424 +    if (!stream->unlinked)
3425 +        pas_stream_unlink(stream);
3426 +
3427 +    if (stream->proplist)
3428 +        pa_proplist_free(stream->proplist);
3429 +
3430 +    pa_xfree(stream->description);
3431 +
3432 +    if (stream->name)
3433 +        pa_volume_api_unregister_name(stream->volume_api, stream->name);
3434 +
3435 +    pa_xfree(stream);
3436 +}
3437 +
3438 +int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) {
3439 +    pa_assert(stream);
3440 +
3441 +    if (have == stream->have_own_volume_control)
3442 +        return 0;
3443 +
3444 +    if (have) {
3445 +        pa_assert(!stream->own_volume_control);
3446 +
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;
3450 +        }
3451 +
3452 +        stream->own_volume_control = stream->create_own_volume_control(stream);
3453 +    } else {
3454 +        stream->delete_own_volume_control(stream);
3455 +        stream->own_volume_control = NULL;
3456 +    }
3457 +
3458 +    stream->have_own_volume_control = have;
3459 +
3460 +    return 0;
3461 +}
3462 +
3463 +int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) {
3464 +    pa_assert(stream);
3465 +
3466 +    if (have == stream->have_own_mute_control)
3467 +        return 0;
3468 +
3469 +    if (have) {
3470 +        pa_assert(!stream->own_mute_control);
3471 +
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;
3475 +        }
3476 +
3477 +        stream->own_mute_control = stream->create_own_mute_control(stream);
3478 +    } else {
3479 +        stream->delete_own_mute_control(stream);
3480 +        stream->own_mute_control = NULL;
3481 +    }
3482 +
3483 +    stream->have_own_mute_control = have;
3484 +
3485 +    return 0;
3486 +}
3487 +
3488 +void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) {
3489 +    pa_assert(stream);
3490 +
3491 +    stream->use_default_volume_control = false;
3492 +
3493 +    if (stream->volume_control_binding) {
3494 +        pa_binding_free(stream->volume_control_binding);
3495 +        stream->volume_control_binding = NULL;
3496 +    }
3497 +
3498 +    set_volume_control_internal(stream, control);
3499 +}
3500 +
3501 +void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
3502 +    pa_assert(stream);
3503 +
3504 +    stream->use_default_mute_control = false;
3505 +
3506 +    if (stream->mute_control_binding) {
3507 +        pa_binding_free(stream->mute_control_binding);
3508 +        stream->mute_control_binding = NULL;
3509 +    }
3510 +
3511 +    set_mute_control_internal(stream, control);
3512 +}
3513 +
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,
3518 +    };
3519 +
3520 +    pa_assert(stream);
3521 +    pa_assert(target_info);
3522 +
3523 +    stream->use_default_volume_control = false;
3524 +
3525 +    if (stream->volume_control_binding)
3526 +        pa_binding_free(stream->volume_control_binding);
3527 +
3528 +    stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
3529 +}
3530 +
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,
3535 +    };
3536 +
3537 +    pa_assert(stream);
3538 +    pa_assert(target_info);
3539 +
3540 +    stream->use_default_mute_control = false;
3541 +
3542 +    if (stream->mute_control_binding)
3543 +        pa_binding_free(stream->mute_control_binding);
3544 +
3545 +    stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
3546 +}
3547 +
3548 +void pas_stream_description_changed(pas_stream *stream, const char *new_description) {
3549 +    char *old_description;
3550 +
3551 +    pa_assert(stream);
3552 +    pa_assert(new_description);
3553 +
3554 +    old_description = stream->description;
3555 +
3556 +    if (pa_streq(new_description, old_description))
3557 +        return;
3558 +
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,
3561 +                 new_description);
3562 +    pa_xfree(old_description);
3563 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
3564 +}
3565 +
3566 +void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) {
3567 +    pa_assert(stream);
3568 +
3569 +    if (group == stream->audio_group_for_volume)
3570 +        return;
3571 +
3572 +    if (stream->audio_group_for_volume)
3573 +        pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream);
3574 +
3575 +    stream->audio_group_for_volume = group;
3576 +
3577 +    if (group)
3578 +        pa_audio_group_add_volume_stream(group, stream);
3579 +}
3580 +
3581 +void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) {
3582 +    pa_assert(stream);
3583 +
3584 +    if (group == stream->audio_group_for_mute)
3585 +        return;
3586 +
3587 +    if (stream->audio_group_for_mute)
3588 +        pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream);
3589 +
3590 +    stream->audio_group_for_mute = group;
3591 +
3592 +    if (group)
3593 +        pa_audio_group_add_mute_stream(group, stream);
3594 +}
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
3598 --- /dev/null
3599 +++ b/src/modules/volume-api/sstream.h
3600 @@ -0,0 +1,108 @@
3601 +#ifndef foosstreamhfoo
3602 +#define foosstreamhfoo
3603 +
3604 +/***
3605 +  This file is part of PulseAudio.
3606 +
3607 +  Copyright 2014 Intel Corporation
3608 +
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.
3613 +
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.
3618 +
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
3622 +  USA.
3623 +***/
3624 +
3625 +#include <modules/volume-api/volume-api.h>
3626 +
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. */
3631 +
3632 +typedef struct pas_stream pas_stream;
3633 +
3634 +struct pas_stream {
3635 +    pa_volume_api *volume_api;
3636 +    uint32_t index;
3637 +    const char *name;
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;
3649 +
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;
3654 +
3655 +    bool linked;
3656 +    bool unlinked;
3657 +
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
3661 +     * enabled. */
3662 +    pa_volume_control *(*create_own_volume_control)(pas_stream *stream);
3663 +
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);
3668 +
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);
3673 +
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);
3678 +
3679 +    void *userdata;
3680 +};
3681 +
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);
3686 +
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);
3692 +
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);
3698 +
3699 +/* Called by the stream implementation. */
3700 +void pas_stream_description_changed(pas_stream *stream, const char *new_description);
3701 +
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);
3707 +
3708 +#endif
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
3712 --- /dev/null
3713 +++ b/src/modules/volume-api/stream-creator.c
3714 @@ -0,0 +1,691 @@
3715 +/***
3716 +  This file is part of PulseAudio.
3717 +
3718 +  Copyright 2014 Intel Corporation
3719 +
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.
3724 +
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.
3729 +
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
3733 +  USA.
3734 +***/
3735 +
3736 +#ifdef HAVE_CONFIG_H
3737 +#include <config.h>
3738 +#endif
3739 +
3740 +#include "stream-creator.h"
3741 +
3742 +#include <modules/volume-api/sstream.h>
3743 +#include <modules/volume-api/mute-control.h>
3744 +#include <modules/volume-api/volume-control.h>
3745 +
3746 +#include <pulsecore/core-util.h>
3747 +#include <pulsecore/i18n.h>
3748 +
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;
3756 +};
3757 +
3758 +enum stream_type {
3759 +    STREAM_TYPE_SINK_INPUT,
3760 +    STREAM_TYPE_SOURCE_OUTPUT,
3761 +};
3762 +
3763 +struct stream {
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;
3770 +
3771 +    bool unlinked;
3772 +
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;
3777 +};
3778 +
3779 +static char *get_stream_volume_and_mute_control_description_malloc(struct stream *stream) {
3780 +    const char *application_name = NULL;
3781 +    char *description;
3782 +
3783 +    pa_assert(stream);
3784 +
3785 +    if (stream->client)
3786 +        application_name = pa_proplist_gets(stream->client->proplist, PA_PROP_APPLICATION_NAME);
3787 +
3788 +    if (application_name)
3789 +        description = pa_sprintf_malloc("%s: %s", application_name, stream->stream->description);
3790 +    else
3791 +        description = pa_xstrdup(stream->stream->description);
3792 +
3793 +    return description;
3794 +}
3795 +
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;
3800 +
3801 +    pa_assert(control);
3802 +    pa_assert(volume);
3803 +
3804 +    stream = control->userdata;
3805 +
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);
3809 +            break;
3810 +
3811 +        case STREAM_TYPE_SOURCE_OUTPUT:
3812 +            pa_bvolume_from_cvolume(&bvolume, &stream->source_output->volume, &stream->source_output->channel_map);
3813 +            break;
3814 +    }
3815 +
3816 +    if (set_volume)
3817 +        bvolume.volume = volume->volume;
3818 +
3819 +    if (set_balance)
3820 +        pa_bvolume_copy_balance(&bvolume, volume);
3821 +
3822 +    pa_bvolume_to_cvolume(&bvolume, &cvolume);
3823 +
3824 +    switch (stream->type) {
3825 +        case STREAM_TYPE_SINK_INPUT:
3826 +            pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
3827 +            break;
3828 +
3829 +        case STREAM_TYPE_SOURCE_OUTPUT:
3830 +            pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
3831 +            break;
3832 +    }
3833 +
3834 +    return 0;
3835 +}
3836 +
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;
3842 +
3843 +    pa_assert(stream);
3844 +    pa_assert(call_data);
3845 +
3846 +    switch (stream->type) {
3847 +        case STREAM_TYPE_SINK_INPUT:
3848 +            input = call_data;
3849 +            break;
3850 +
3851 +        case STREAM_TYPE_SOURCE_OUTPUT:
3852 +            output = call_data;
3853 +            break;
3854 +    }
3855 +
3856 +    if ((input && input != stream->sink_input) || (output && output != stream->source_output))
3857 +        return PA_HOOK_OK;
3858 +
3859 +    if (input)
3860 +        pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map);
3861 +    else
3862 +        pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map);
3863 +
3864 +    pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true);
3865 +
3866 +    return PA_HOOK_OK;
3867 +}
3868 +
3869 +static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
3870 +    struct stream *stream;
3871 +    pa_cvolume cvolume;
3872 +
3873 +    pa_assert(control);
3874 +
3875 +    stream = control->userdata;
3876 +    pa_bvolume_to_cvolume(&control->volume, &cvolume);
3877 +
3878 +    switch (stream->type) {
3879 +        case STREAM_TYPE_SINK_INPUT:
3880 +            pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
3881 +            break;
3882 +
3883 +        case STREAM_TYPE_SOURCE_OUTPUT:
3884 +            pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
3885 +            break;
3886 +    }
3887 +}
3888 +
3889 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
3890 +    struct stream *stream;
3891 +
3892 +    pa_assert(control);
3893 +
3894 +    stream = control->userdata;
3895 +
3896 +    switch (stream->type) {
3897 +        case STREAM_TYPE_SINK_INPUT:
3898 +            pa_sink_input_set_mute(stream->sink_input, mute, true);
3899 +            break;
3900 +
3901 +        case STREAM_TYPE_SOURCE_OUTPUT:
3902 +            pa_source_output_set_mute(stream->source_output, mute, true);
3903 +            break;
3904 +    }
3905 +
3906 +    return 0;
3907 +}
3908 +
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;
3913 +    bool mute;
3914 +
3915 +    pa_assert(stream);
3916 +    pa_assert(call_data);
3917 +
3918 +    switch (stream->type) {
3919 +        case STREAM_TYPE_SINK_INPUT:
3920 +            input = call_data;
3921 +            break;
3922 +
3923 +        case STREAM_TYPE_SOURCE_OUTPUT:
3924 +            output = call_data;
3925 +            break;
3926 +    }
3927 +
3928 +    if ((input && input != stream->sink_input) || (output && output != stream->source_output))
3929 +        return PA_HOOK_OK;
3930 +
3931 +    if (input)
3932 +        mute = input->muted;
3933 +    else
3934 +        mute = output->muted;
3935 +
3936 +    pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
3937 +
3938 +    return PA_HOOK_OK;
3939 +}
3940 +
3941 +static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
3942 +    struct stream *stream;
3943 +
3944 +    pa_assert(control);
3945 +
3946 +    stream = control->userdata;
3947 +
3948 +    switch (stream->type) {
3949 +        case STREAM_TYPE_SINK_INPUT:
3950 +            pa_sink_input_set_mute(stream->sink_input, control->mute, true);
3951 +            break;
3952 +
3953 +        case STREAM_TYPE_SOURCE_OUTPUT:
3954 +            pa_source_output_set_mute(stream->source_output, control->mute, true);
3955 +            break;
3956 +    }
3957 +}
3958 +
3959 +static const char *get_sink_input_description(pa_sink_input *input) {
3960 +    const char *description;
3961 +
3962 +    pa_assert(input);
3963 +
3964 +    description = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_NAME);
3965 +    if (description)
3966 +        return description;
3967 +
3968 +    return NULL;
3969 +}
3970 +
3971 +static const char *get_source_output_description(pa_source_output *output) {
3972 +    const char *description;
3973 +
3974 +    pa_assert(output);
3975 +
3976 +    description = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_NAME);
3977 +    if (description)
3978 +        return description;
3979 +
3980 +    return NULL;
3981 +}
3982 +
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;
3989 +
3990 +    pa_assert(s);
3991 +
3992 +    stream = s->userdata;
3993 +
3994 +    switch (stream->type) {
3995 +        case STREAM_TYPE_SINK_INPUT:
3996 +            name = "sink-input-volume-control";
3997 +            break;
3998 +
3999 +        case STREAM_TYPE_SOURCE_OUTPUT:
4000 +            name = "source-output-volume-control";
4001 +            break;
4002 +    }
4003 +
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;
4009 +
4010 +    pa_assert(!stream->volume_changed_slot);
4011 +
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);
4018 +            break;
4019 +
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);
4025 +            break;
4026 +    }
4027 +
4028 +    pa_volume_control_put(control, &volume, volume_control_set_initial_volume_cb);
4029 +
4030 +    return control;
4031 +}
4032 +
4033 +static void stream_delete_own_volume_control_cb(pas_stream *s) {
4034 +    struct stream *stream;
4035 +
4036 +    pa_assert(s);
4037 +
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);
4042 +}
4043 +
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;
4050 +
4051 +    pa_assert(s);
4052 +
4053 +    stream = s->userdata;
4054 +
4055 +    switch (stream->type) {
4056 +        case STREAM_TYPE_SINK_INPUT:
4057 +            name = "sink-input-mute-control";
4058 +            break;
4059 +
4060 +        case STREAM_TYPE_SOURCE_OUTPUT:
4061 +            name = "source-output-mute-control";
4062 +            break;
4063 +    }
4064 +
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;
4070 +
4071 +    pa_assert(!stream->mute_changed_slot);
4072 +
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;
4079 +            break;
4080 +
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;
4086 +            break;
4087 +    }
4088 +
4089 +    pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb);
4090 +
4091 +    return control;
4092 +}
4093 +
4094 +static void stream_delete_own_mute_control_cb(pas_stream *s) {
4095 +    struct stream *stream;
4096 +
4097 +    pa_assert(s);
4098 +
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);
4103 +}
4104 +
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;
4111 +
4112 +    pa_assert(stream);
4113 +    pa_assert(call_data);
4114 +
4115 +    switch (stream->type) {
4116 +        case STREAM_TYPE_SINK_INPUT:
4117 +            input = call_data;
4118 +
4119 +            if (input != stream->sink_input)
4120 +                return PA_HOOK_OK;
4121 +
4122 +            new_stream_description = get_sink_input_description(input);
4123 +            if (!new_stream_description)
4124 +                new_stream_description = stream->stream->name;
4125 +            break;
4126 +
4127 +        case STREAM_TYPE_SOURCE_OUTPUT:
4128 +            output = call_data;
4129 +
4130 +            if (output != stream->source_output)
4131 +                return PA_HOOK_OK;
4132 +
4133 +            new_stream_description = get_source_output_description(output);
4134 +            if (!new_stream_description)
4135 +                new_stream_description = stream->stream->name;
4136 +            break;
4137 +    }
4138 +
4139 +    pas_stream_description_changed(stream->stream, new_stream_description);
4140 +
4141 +    new_control_description = get_stream_volume_and_mute_control_description_malloc(stream);
4142 +
4143 +    if (stream->stream->own_volume_control)
4144 +        pa_volume_control_description_changed(stream->stream->own_volume_control, new_control_description);
4145 +
4146 +    if (stream->stream->own_mute_control)
4147 +        pa_mute_control_description_changed(stream->stream->own_mute_control, new_control_description);
4148 +
4149 +    pa_xfree(new_control_description);
4150 +
4151 +    return PA_HOOK_OK;
4152 +}
4153 +
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;
4158 +
4159 +    pa_assert(stream);
4160 +    pa_assert(client);
4161 +
4162 +    if (client != stream->client)
4163 +        return PA_HOOK_OK;
4164 +
4165 +    description = get_stream_volume_and_mute_control_description_malloc(stream);
4166 +
4167 +    if (stream->stream->own_volume_control)
4168 +        pa_volume_control_description_changed(stream->stream->own_volume_control, description);
4169 +
4170 +    if (stream->stream->own_mute_control)
4171 +        pa_mute_control_description_changed(stream->stream->own_mute_control, description);
4172 +
4173 +    pa_xfree(description);
4174 +
4175 +    return PA_HOOK_OK;
4176 +}
4177 +
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;
4183 +
4184 +    pa_assert(creator);
4185 +    pa_assert(core_stream);
4186 +
4187 +    stream = pa_xnew0(struct stream, 1);
4188 +    stream->creator = creator;
4189 +    stream->type = type;
4190 +
4191 +    switch (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";
4196 +
4197 +            description = get_sink_input_description(stream->sink_input);
4198 +            if (!description)
4199 +                description = name;
4200 +
4201 +            direction = PA_DIRECTION_OUTPUT;
4202 +            break;
4203 +
4204 +        case STREAM_TYPE_SOURCE_OUTPUT:
4205 +            stream->source_output = core_stream;
4206 +            stream->client = stream->source_output->client;
4207 +            name = "source-output-stream";
4208 +
4209 +            description = get_source_output_description(stream->source_output);
4210 +            if (!description)
4211 +                description = name;
4212 +
4213 +            direction = PA_DIRECTION_INPUT;
4214 +            break;
4215 +    }
4216 +
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);
4225 +
4226 +    switch (type) {
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);
4231 +            break;
4232 +
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);
4237 +            break;
4238 +    }
4239 +
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);
4243 +
4244 +    return stream;
4245 +}
4246 +
4247 +static void stream_put(struct stream *stream) {
4248 +    pa_proplist *proplist = NULL;
4249 +
4250 +    pa_assert(stream);
4251 +
4252 +    switch (stream->type) {
4253 +        case STREAM_TYPE_SINK_INPUT:
4254 +            proplist = stream->sink_input->proplist;
4255 +            break;
4256 +
4257 +        case STREAM_TYPE_SOURCE_OUTPUT:
4258 +            proplist = stream->source_output->proplist;
4259 +            break;
4260 +    }
4261 +
4262 +    pas_stream_put(stream->stream, proplist);
4263 +}
4264 +
4265 +static void stream_unlink(struct stream *stream) {
4266 +    pa_assert(stream);
4267 +
4268 +    if (stream->unlinked)
4269 +        return;
4270 +
4271 +    stream->unlinked = true;
4272 +
4273 +    if (stream->stream)
4274 +        pas_stream_unlink(stream->stream);
4275 +}
4276 +
4277 +static void stream_free(struct stream *stream) {
4278 +    pa_assert(stream);
4279 +
4280 +    if (!stream->unlinked)
4281 +        stream_unlink(stream);
4282 +
4283 +    if (stream->client_proplist_changed_slot)
4284 +        pa_hook_slot_free(stream->client_proplist_changed_slot);
4285 +
4286 +    if (stream->proplist_changed_slot)
4287 +        pa_hook_slot_free(stream->proplist_changed_slot);
4288 +
4289 +    if (stream->stream)
4290 +        pas_stream_free(stream->stream);
4291 +
4292 +    pa_xfree(stream);
4293 +}
4294 +
4295 +static void create_stream(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
4296 +    struct stream *stream;
4297 +
4298 +    pa_assert(creator);
4299 +    pa_assert(core_stream);
4300 +
4301 +    stream = stream_new(creator, type, core_stream);
4302 +    pa_hashmap_put(creator->streams, core_stream, stream);
4303 +    stream_put(stream);
4304 +}
4305 +
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;
4309 +
4310 +    pa_assert(creator);
4311 +    pa_assert(input);
4312 +
4313 +    create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
4314 +
4315 +    return PA_HOOK_OK;
4316 +}
4317 +
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;
4321 +
4322 +    pa_assert(creator);
4323 +    pa_assert(input);
4324 +
4325 +    pa_hashmap_remove_and_free(creator->streams, input);
4326 +
4327 +    return PA_HOOK_OK;
4328 +}
4329 +
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;
4333 +
4334 +    pa_assert(creator);
4335 +    pa_assert(output);
4336 +
4337 +    create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
4338 +
4339 +    return PA_HOOK_OK;
4340 +}
4341 +
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;
4345 +
4346 +    pa_assert(creator);
4347 +    pa_assert(output);
4348 +
4349 +    pa_hashmap_remove_and_free(creator->streams, output);
4350 +
4351 +    return PA_HOOK_OK;
4352 +}
4353 +
4354 +pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) {
4355 +    pa_stream_creator *creator;
4356 +    uint32_t idx;
4357 +    pa_sink_input *input;
4358 +    pa_source_output *output;
4359 +
4360 +    pa_assert(api);
4361 +
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);
4373 +
4374 +    PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx)
4375 +        create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
4376 +
4377 +    PA_IDXSET_FOREACH(output, api->core->source_outputs, idx)
4378 +        create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
4379 +
4380 +    return creator;
4381 +}
4382 +
4383 +void pa_stream_creator_free(pa_stream_creator *creator) {
4384 +    pa_assert(creator);
4385 +
4386 +    if (creator->streams)
4387 +        pa_hashmap_remove_all(creator->streams);
4388 +
4389 +    if (creator->source_output_unlink_slot)
4390 +        pa_hook_slot_free(creator->source_output_unlink_slot);
4391 +
4392 +    if (creator->source_output_put_slot)
4393 +        pa_hook_slot_free(creator->source_output_put_slot);
4394 +
4395 +    if (creator->sink_input_unlink_slot)
4396 +        pa_hook_slot_free(creator->sink_input_unlink_slot);
4397 +
4398 +    if (creator->sink_input_put_slot)
4399 +        pa_hook_slot_free(creator->sink_input_put_slot);
4400 +
4401 +    if (creator->streams)
4402 +        pa_hashmap_free(creator->streams);
4403 +
4404 +    pa_xfree(creator);
4405 +}
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
4409 --- /dev/null
4410 +++ b/src/modules/volume-api/stream-creator.h
4411 @@ -0,0 +1,32 @@
4412 +#ifndef foostreamcreatorhfoo
4413 +#define foostreamcreatorhfoo
4414 +
4415 +/***
4416 +  This file is part of PulseAudio.
4417 +
4418 +  Copyright 2014 Intel Corporation
4419 +
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.
4424 +
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.
4429 +
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
4433 +  USA.
4434 +***/
4435 +
4436 +#include <modules/volume-api/volume-api.h>
4437 +
4438 +typedef struct pa_stream_creator pa_stream_creator;
4439 +
4440 +pa_stream_creator *pa_stream_creator_new(pa_volume_api *api);
4441 +void pa_stream_creator_free(pa_stream_creator *creator);
4442 +
4443 +#endif
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
4447 --- /dev/null
4448 +++ b/src/modules/volume-api/volume-api.c
4449 @@ -0,0 +1,647 @@
4450 +/***
4451 +  This file is part of PulseAudio.
4452 +
4453 +  Copyright 2014 Intel Corporation
4454 +
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.
4459 +
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.
4464 +
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
4468 +  USA.
4469 +***/
4470 +
4471 +#ifdef HAVE_CONFIG_H
4472 +#include <config.h>
4473 +#endif
4474 +
4475 +#include "volume-api.h"
4476 +
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>
4484 +
4485 +#include <pulsecore/core-util.h>
4486 +#include <pulsecore/shared.h>
4487 +
4488 +static pa_volume_api *volume_api_new(pa_core *core);
4489 +static void volume_api_free(pa_volume_api *api);
4490 +
4491 +pa_volume_api *pa_volume_api_get(pa_core *core) {
4492 +    pa_volume_api *api;
4493 +
4494 +    pa_assert(core);
4495 +
4496 +    api = pa_shared_get(core, "volume-api");
4497 +
4498 +    if (api)
4499 +        pa_volume_api_ref(api);
4500 +    else {
4501 +        api = volume_api_new(core);
4502 +        pa_assert_se(pa_shared_set(core, "volume-api", api) >= 0);
4503 +    }
4504 +
4505 +    return api;
4506 +}
4507 +
4508 +pa_volume_api *pa_volume_api_ref(pa_volume_api *api) {
4509 +    pa_assert(api);
4510 +
4511 +    api->refcnt++;
4512 +
4513 +    return api;
4514 +}
4515 +
4516 +void pa_volume_api_unref(pa_volume_api *api) {
4517 +    pa_assert(api);
4518 +    pa_assert(api->refcnt > 0);
4519 +
4520 +    api->refcnt--;
4521 +
4522 +    if (api->refcnt == 0) {
4523 +        pa_assert_se(pa_shared_remove(api->core, "volume-api") >= 0);
4524 +        volume_api_free(api);
4525 +    }
4526 +}
4527 +
4528 +void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
4529 +    pa_assert(api);
4530 +    pa_assert(type);
4531 +
4532 +    pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0);
4533 +
4534 +    pa_log_debug("Added binding target type %s.", type->name);
4535 +
4536 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type);
4537 +}
4538 +
4539 +void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
4540 +    pa_assert(api);
4541 +    pa_assert(type);
4542 +
4543 +    pa_log_debug("Removing binding target type %s.", type->name);
4544 +
4545 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type);
4546 +
4547 +    pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name));
4548 +}
4549 +
4550 +static void create_builtin_binding_target_types(pa_volume_api *api) {
4551 +    pa_binding_target_type *type;
4552 +
4553 +    pa_assert(api);
4554 +
4555 +    type = pa_audio_group_create_binding_target_type(api);
4556 +    pa_volume_api_add_binding_target_type(api, type);
4557 +}
4558 +
4559 +static void delete_builtin_binding_target_types(pa_volume_api *api) {
4560 +    pa_binding_target_type *type;
4561 +
4562 +    pa_assert(api);
4563 +
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);
4566 +}
4567 +
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;
4570 +
4571 +    pa_assert(volume_api);
4572 +    pa_assert(event == volume_api->create_objects_defer_event);
4573 +
4574 +    mainloop_api->defer_free(event);
4575 +    volume_api->create_objects_defer_event = NULL;
4576 +
4577 +    volume_api->device_creator = pa_device_creator_new(volume_api);
4578 +    volume_api->stream_creator = pa_stream_creator_new(volume_api);
4579 +}
4580 +
4581 +static pa_volume_api *volume_api_new(pa_core *core) {
4582 +    pa_volume_api *api;
4583 +    unsigned i;
4584 +
4585 +    pa_assert(core);
4586 +
4587 +    api = pa_xnew0(pa_volume_api, 1);
4588 +    api->core = core;
4589 +    api->refcnt = 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);
4597 +
4598 +    for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
4599 +        pa_hook_init(&api->hooks[i], api);
4600 +
4601 +    create_builtin_binding_target_types(api);
4602 +
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);
4608 +
4609 +    pa_log_debug("Created a pa_volume_api object.");
4610 +
4611 +    return api;
4612 +}
4613 +
4614 +static void volume_api_free(pa_volume_api *api) {
4615 +    unsigned i;
4616 +
4617 +    pa_assert(api);
4618 +    pa_assert(api->refcnt == 0);
4619 +
4620 +    pa_log_debug("Freeing the pa_volume_api object.");
4621 +
4622 +    if (api->stream_creator)
4623 +        pa_stream_creator_free(api->stream_creator);
4624 +
4625 +    if (api->device_creator)
4626 +        pa_device_creator_free(api->device_creator);
4627 +
4628 +    if (api->create_objects_defer_event)
4629 +        api->core->mainloop->defer_free(api->create_objects_defer_event);
4630 +
4631 +    if (api->binding_target_types)
4632 +        delete_builtin_binding_target_types(api);
4633 +
4634 +    for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
4635 +        pa_hook_done(&api->hooks[i]);
4636 +
4637 +    if (api->audio_groups) {
4638 +        pa_assert(pa_hashmap_isempty(api->audio_groups));
4639 +        pa_hashmap_free(api->audio_groups);
4640 +    }
4641 +
4642 +    if (api->streams) {
4643 +        pa_assert(pa_hashmap_isempty(api->streams));
4644 +        pa_hashmap_free(api->streams);
4645 +    }
4646 +
4647 +    if (api->devices) {
4648 +        pa_assert(pa_hashmap_isempty(api->devices));
4649 +        pa_hashmap_free(api->devices);
4650 +    }
4651 +
4652 +    if (api->mute_controls) {
4653 +        pa_assert(pa_hashmap_isempty(api->mute_controls));
4654 +        pa_hashmap_free(api->mute_controls);
4655 +    }
4656 +
4657 +    if (api->volume_controls) {
4658 +        pa_assert(pa_hashmap_isempty(api->volume_controls));
4659 +        pa_hashmap_free(api->volume_controls);
4660 +    }
4661 +
4662 +    if (api->names) {
4663 +        pa_assert(pa_hashmap_isempty(api->names));
4664 +        pa_hashmap_free(api->names);
4665 +    }
4666 +
4667 +    if (api->binding_target_types) {
4668 +        pa_assert(pa_hashmap_isempty(api->binding_target_types));
4669 +        pa_hashmap_free(api->binding_target_types);
4670 +    }
4671 +
4672 +    pa_xfree(api);
4673 +}
4674 +
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) {
4677 +    char *n;
4678 +
4679 +    pa_assert(api);
4680 +    pa_assert(requested_name);
4681 +    pa_assert(registered_name);
4682 +
4683 +    n = pa_xstrdup(requested_name);
4684 +
4685 +    if (pa_hashmap_put(api->names, n, n) < 0) {
4686 +        unsigned i = 1;
4687 +
4688 +        pa_xfree(n);
4689 +
4690 +        if (fail_if_already_registered) {
4691 +            pa_log("Name %s already registered.", requested_name);
4692 +            return -PA_ERR_EXIST;
4693 +        }
4694 +
4695 +        do {
4696 +            i++;
4697 +            n = pa_sprintf_malloc("%s.%u", requested_name, i);
4698 +        } while (pa_hashmap_put(api->names, n, n) < 0);
4699 +    }
4700 +
4701 +    *registered_name = n;
4702 +
4703 +    return 0;
4704 +}
4705 +
4706 +void pa_volume_api_unregister_name(pa_volume_api *api, const char *name) {
4707 +    pa_assert(api);
4708 +    pa_assert(name);
4709 +
4710 +    pa_assert_se(pa_hashmap_remove_and_free(api->names, name) >= 0);
4711 +}
4712 +
4713 +uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) {
4714 +    uint32_t idx;
4715 +
4716 +    pa_assert(api);
4717 +
4718 +    idx = api->next_volume_control_index++;
4719 +
4720 +    return idx;
4721 +}
4722 +
4723 +static void set_main_output_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
4724 +    pa_volume_control *old_control;
4725 +
4726 +    pa_assert(api);
4727 +
4728 +    old_control = api->main_output_volume_control;
4729 +
4730 +    if (control == old_control)
4731 +        return;
4732 +
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);
4737 +}
4738 +
4739 +static void set_main_input_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
4740 +    pa_volume_control *old_control;
4741 +
4742 +    pa_assert(api);
4743 +
4744 +    old_control = api->main_input_volume_control;
4745 +
4746 +    if (control == old_control)
4747 +        return;
4748 +
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);
4753 +}
4754 +
4755 +void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control) {
4756 +    pa_assert(api);
4757 +    pa_assert(control);
4758 +
4759 +    pa_assert_se(pa_hashmap_put(api->volume_controls, (void *) control->name, control) >= 0);
4760 +}
4761 +
4762 +int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *control) {
4763 +    pa_assert(api);
4764 +    pa_assert(control);
4765 +
4766 +    if (!pa_hashmap_remove(api->volume_controls, control->name))
4767 +        return -1;
4768 +
4769 +    if (control == api->main_output_volume_control)
4770 +        set_main_output_volume_control_internal(api, NULL);
4771 +
4772 +    if (control == api->main_input_volume_control)
4773 +        set_main_input_volume_control_internal(api, NULL);
4774 +
4775 +    return 0;
4776 +}
4777 +
4778 +pa_volume_control *pa_volume_api_get_volume_control_by_index(pa_volume_api *api, uint32_t idx) {
4779 +    pa_volume_control *control;
4780 +    void *state;
4781 +
4782 +    pa_assert(api);
4783 +
4784 +    PA_HASHMAP_FOREACH(control, api->volume_controls, state) {
4785 +        if (control->index == idx)
4786 +            return control;
4787 +    }
4788 +
4789 +    return NULL;
4790 +}
4791 +
4792 +uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) {
4793 +    uint32_t idx;
4794 +
4795 +    pa_assert(api);
4796 +
4797 +    idx = api->next_mute_control_index++;
4798 +
4799 +    return idx;
4800 +}
4801 +
4802 +static void set_main_output_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
4803 +    pa_mute_control *old_control;
4804 +
4805 +    pa_assert(api);
4806 +
4807 +    old_control = api->main_output_mute_control;
4808 +
4809 +    if (control == old_control)
4810 +        return;
4811 +
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);
4816 +}
4817 +
4818 +static void set_main_input_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
4819 +    pa_mute_control *old_control;
4820 +
4821 +    pa_assert(api);
4822 +
4823 +    old_control = api->main_input_mute_control;
4824 +
4825 +    if (control == old_control)
4826 +        return;
4827 +
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);
4832 +}
4833 +
4834 +void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control) {
4835 +    pa_assert(api);
4836 +    pa_assert(control);
4837 +
4838 +    pa_assert_se(pa_hashmap_put(api->mute_controls, (void *) control->name, control) >= 0);
4839 +}
4840 +
4841 +int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *control) {
4842 +    pa_assert(api);
4843 +    pa_assert(control);
4844 +
4845 +    if (!pa_hashmap_remove(api->mute_controls, control->name))
4846 +        return -1;
4847 +
4848 +    if (control == api->main_output_mute_control)
4849 +        set_main_output_mute_control_internal(api, NULL);
4850 +
4851 +    if (control == api->main_input_mute_control)
4852 +        set_main_input_mute_control_internal(api, NULL);
4853 +
4854 +    return 0;
4855 +}
4856 +
4857 +pa_mute_control *pa_volume_api_get_mute_control_by_index(pa_volume_api *api, uint32_t idx) {
4858 +    pa_mute_control *control;
4859 +    void *state;
4860 +
4861 +    pa_assert(api);
4862 +
4863 +    PA_HASHMAP_FOREACH(control, api->mute_controls, state) {
4864 +        if (control->index == idx)
4865 +            return control;
4866 +    }
4867 +
4868 +    return NULL;
4869 +}
4870 +
4871 +uint32_t pa_volume_api_allocate_device_index(pa_volume_api *api) {
4872 +    uint32_t idx;
4873 +
4874 +    pa_assert(api);
4875 +
4876 +    idx = api->next_device_index++;
4877 +
4878 +    return idx;
4879 +}
4880 +
4881 +void pa_volume_api_add_device(pa_volume_api *api, pa_device *device) {
4882 +    pa_assert(api);
4883 +    pa_assert(device);
4884 +
4885 +    pa_assert_se(pa_hashmap_put(api->devices, (void *) device->name, device) >= 0);
4886 +}
4887 +
4888 +int pa_volume_api_remove_device(pa_volume_api *api, pa_device *device) {
4889 +    pa_assert(api);
4890 +    pa_assert(device);
4891 +
4892 +    if (!pa_hashmap_remove(api->devices, device->name))
4893 +        return -1;
4894 +
4895 +    return 0;
4896 +}
4897 +
4898 +pa_device *pa_volume_api_get_device_by_index(pa_volume_api *api, uint32_t idx) {
4899 +    pa_device *device;
4900 +    void *state;
4901 +
4902 +    pa_assert(api);
4903 +
4904 +    PA_HASHMAP_FOREACH(device, api->devices, state) {
4905 +        if (device->index == idx)
4906 +            return device;
4907 +    }
4908 +
4909 +    return NULL;
4910 +}
4911 +
4912 +uint32_t pa_volume_api_allocate_stream_index(pa_volume_api *api) {
4913 +    uint32_t idx;
4914 +
4915 +    pa_assert(api);
4916 +
4917 +    idx = api->next_stream_index++;
4918 +
4919 +    return idx;
4920 +}
4921 +
4922 +void pa_volume_api_add_stream(pa_volume_api *api, pas_stream *stream) {
4923 +    pa_assert(api);
4924 +    pa_assert(stream);
4925 +
4926 +    pa_assert_se(pa_hashmap_put(api->streams, (void *) stream->name, stream) >= 0);
4927 +}
4928 +
4929 +int pa_volume_api_remove_stream(pa_volume_api *api, pas_stream *stream) {
4930 +    pa_assert(api);
4931 +    pa_assert(stream);
4932 +
4933 +    if (!pa_hashmap_remove(api->streams, stream->name))
4934 +        return -1;
4935 +
4936 +    return 0;
4937 +}
4938 +
4939 +pas_stream *pa_volume_api_get_stream_by_index(pa_volume_api *api, uint32_t idx) {
4940 +    pas_stream *stream;
4941 +    void *state;
4942 +
4943 +    pa_assert(api);
4944 +
4945 +    PA_HASHMAP_FOREACH(stream, api->streams, state) {
4946 +        if (stream->index == idx)
4947 +            return stream;
4948 +    }
4949 +
4950 +    return NULL;
4951 +}
4952 +
4953 +uint32_t pa_volume_api_allocate_audio_group_index(pa_volume_api *api) {
4954 +    uint32_t idx;
4955 +
4956 +    pa_assert(api);
4957 +
4958 +    idx = api->next_audio_group_index++;
4959 +
4960 +    return idx;
4961 +}
4962 +
4963 +void pa_volume_api_add_audio_group(pa_volume_api *api, pa_audio_group *group) {
4964 +    pa_assert(api);
4965 +    pa_assert(group);
4966 +
4967 +    pa_assert_se(pa_hashmap_put(api->audio_groups, (void *) group->name, group) >= 0);
4968 +}
4969 +
4970 +int pa_volume_api_remove_audio_group(pa_volume_api *api, pa_audio_group *group) {
4971 +    pa_assert(api);
4972 +    pa_assert(group);
4973 +
4974 +    if (!pa_hashmap_remove(api->audio_groups, group->name))
4975 +        return -1;
4976 +
4977 +    return 0;
4978 +}
4979 +
4980 +pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint32_t idx) {
4981 +    pa_audio_group *group;
4982 +    void *state;
4983 +
4984 +    pa_assert(api);
4985 +
4986 +    PA_HASHMAP_FOREACH(group, api->audio_groups, state) {
4987 +        if (group->index == idx)
4988 +            return group;
4989 +    }
4990 +
4991 +    return NULL;
4992 +}
4993 +
4994 +void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control) {
4995 +    pa_assert(api);
4996 +
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;
5000 +    }
5001 +
5002 +    set_main_output_volume_control_internal(api, control);
5003 +}
5004 +
5005 +void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
5006 +    pa_assert(api);
5007 +
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;
5011 +    }
5012 +
5013 +    set_main_input_volume_control_internal(api, control);
5014 +}
5015 +
5016 +void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
5017 +    pa_assert(api);
5018 +
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;
5022 +    }
5023 +
5024 +    set_main_output_mute_control_internal(api, control);
5025 +}
5026 +
5027 +void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
5028 +    pa_assert(api);
5029 +
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;
5033 +    }
5034 +
5035 +    set_main_input_mute_control_internal(api, control);
5036 +}
5037 +
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 = {
5040 +        .userdata = api,
5041 +        .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
5042 +    };
5043 +
5044 +    pa_assert(api);
5045 +    pa_assert(target_info);
5046 +
5047 +    if (api->main_output_volume_control_binding)
5048 +        pa_binding_free(api->main_output_volume_control_binding);
5049 +
5050 +    api->main_output_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
5051 +}
5052 +
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 = {
5055 +        .userdata = api,
5056 +        .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
5057 +    };
5058 +
5059 +    pa_assert(api);
5060 +    pa_assert(target_info);
5061 +
5062 +    if (api->main_input_volume_control_binding)
5063 +        pa_binding_free(api->main_input_volume_control_binding);
5064 +
5065 +    api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
5066 +}
5067 +
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 = {
5070 +        .userdata = api,
5071 +        .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
5072 +    };
5073 +
5074 +    pa_assert(api);
5075 +    pa_assert(target_info);
5076 +
5077 +    if (api->main_output_mute_control_binding)
5078 +        pa_binding_free(api->main_output_mute_control_binding);
5079 +
5080 +    api->main_output_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
5081 +}
5082 +
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 = {
5085 +        .userdata = api,
5086 +        .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
5087 +    };
5088 +
5089 +    pa_assert(api);
5090 +    pa_assert(target_info);
5091 +
5092 +    if (api->main_input_mute_control_binding)
5093 +        pa_binding_free(api->main_input_mute_control_binding);
5094 +
5095 +    api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
5096 +}
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
5100 --- /dev/null
5101 +++ b/src/modules/volume-api/volume-api.h
5102 @@ -0,0 +1,163 @@
5103 +#ifndef foovolumeapihfoo
5104 +#define foovolumeapihfoo
5105 +
5106 +/***
5107 +  This file is part of PulseAudio.
5108 +
5109 +  Copyright 2014 Intel Corporation
5110 +
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.
5115 +
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.
5120 +
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
5124 +  USA.
5125 +***/
5126 +
5127 +#include <pulsecore/core.h>
5128 +
5129 +typedef struct pa_volume_api pa_volume_api;
5130 +
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;
5142 +
5143 +enum {
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,
5159 +
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,
5165 +
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,
5171 +
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
5186 +};
5187 +
5188 +struct pa_volume_api {
5189 +    pa_core *core;
5190 +    unsigned refcnt;
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;
5202 +
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;
5216 +};
5217 +
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);
5221 +
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);
5224 +
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);
5228 +
5229 +void pa_volume_api_unregister_name(pa_volume_api *api, const char *name);
5230 +
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);
5235 +
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);
5240 +
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);
5245 +
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);
5250 +
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);
5255 +
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);
5264 +
5265 +#endif
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
5269 --- /dev/null
5270 +++ b/src/modules/volume-api/volume-control.c
5271 @@ -0,0 +1,363 @@
5272 +/***
5273 +  This file is part of PulseAudio.
5274 +
5275 +  Copyright 2014 Intel Corporation
5276 +
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.
5281 +
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.
5286 +
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
5290 +  USA.
5291 +***/
5292 +
5293 +#ifdef HAVE_CONFIG_H
5294 +#include <config.h>
5295 +#endif
5296 +
5297 +#include "volume-control.h"
5298 +
5299 +#include <modules/volume-api/audio-group.h>
5300 +#include <modules/volume-api/device.h>
5301 +#include <modules/volume-api/sstream.h>
5302 +
5303 +#include <pulsecore/core-util.h>
5304 +
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;
5308 +
5309 +    pa_assert(api);
5310 +    pa_assert(name);
5311 +    pa_assert(description);
5312 +
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);
5326 +
5327 +    return control;
5328 +}
5329 +
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];
5336 +
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);
5341 +
5342 +    if (initial_volume && pa_bvolume_valid(initial_volume, true, false))
5343 +        control->volume.volume = initial_volume->volume;
5344 +    else
5345 +        control->volume.volume = PA_VOLUME_NORM / 3;
5346 +
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);
5351 +    else {
5352 +        pa_channel_map_init_mono(&control->volume.channel_map);
5353 +        pa_bvolume_reset_balance(&control->volume, &control->volume.channel_map);
5354 +    }
5355 +
5356 +    if (set_initial_volume_cb)
5357 +        set_initial_volume_cb(control);
5358 +
5359 +    pa_volume_api_add_volume_control(control->volume_api, control);
5360 +
5361 +    control->linked = true;
5362 +
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:");
5367 +
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)));
5370 +
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));
5375 +
5376 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT], control);
5377 +}
5378 +
5379 +void pa_volume_control_unlink(pa_volume_control *control) {
5380 +    pa_audio_group *group;
5381 +    pa_device *device;
5382 +    pas_stream *stream;
5383 +
5384 +    pa_assert(control);
5385 +
5386 +    if (control->unlinked) {
5387 +        pa_log_debug("Unlinking volume control %s (already unlinked, this is a no-op).", control->name);
5388 +        return;
5389 +    }
5390 +
5391 +    control->unlinked = true;
5392 +
5393 +    pa_log_debug("Unlinking volume control %s.", control->name);
5394 +
5395 +    if (control->linked)
5396 +        pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
5397 +
5398 +    pa_volume_api_remove_volume_control(control->volume_api, control);
5399 +
5400 +    while ((group = pa_hashmap_first(control->audio_groups)))
5401 +        pa_audio_group_set_volume_control(group, NULL);
5402 +
5403 +    while ((stream = pa_hashmap_first(control->streams)))
5404 +        pas_stream_set_volume_control(stream, NULL);
5405 +
5406 +    while ((device = pa_hashmap_first(control->default_for_devices)))
5407 +        pa_device_set_default_volume_control(device, NULL);
5408 +
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);
5421 +    }
5422 +}
5423 +
5424 +void pa_volume_control_free(pa_volume_control *control) {
5425 +    pa_assert(control);
5426 +
5427 +    if (!control->unlinked)
5428 +        pa_volume_control_unlink(control);
5429 +
5430 +    if (control->audio_groups) {
5431 +        pa_assert(pa_hashmap_isempty(control->audio_groups));
5432 +        pa_hashmap_free(control->audio_groups);
5433 +    }
5434 +
5435 +    if (control->streams) {
5436 +        pa_assert(pa_hashmap_isempty(control->streams));
5437 +        pa_hashmap_free(control->streams);
5438 +    }
5439 +
5440 +    if (control->default_for_devices) {
5441 +        pa_assert(pa_hashmap_isempty(control->default_for_devices));
5442 +        pa_hashmap_free(control->default_for_devices);
5443 +    }
5444 +
5445 +    if (control->devices) {
5446 +        pa_assert(pa_hashmap_isempty(control->devices));
5447 +        pa_hashmap_free(control->devices);
5448 +    }
5449 +
5450 +    if (control->proplist)
5451 +        pa_proplist_free(control->proplist);
5452 +
5453 +    pa_xfree(control->description);
5454 +
5455 +    if (control->name)
5456 +        pa_volume_api_unregister_name(control->volume_api, control->name);
5457 +
5458 +    pa_xfree(control);
5459 +}
5460 +
5461 +void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) {
5462 +    pa_assert(control);
5463 +    pa_assert(group);
5464 +
5465 +    control->owner_audio_group = group;
5466 +}
5467 +
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;
5472 +
5473 +    pa_assert(control);
5474 +    pa_assert(volume);
5475 +
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);
5479 +
5480 +    if (!volume_changed && !balance_changed)
5481 +        return;
5482 +
5483 +    if (volume_changed)
5484 +        control->volume.volume = volume->volume;
5485 +
5486 +    if (balance_changed)
5487 +        pa_bvolume_copy_balance(&control->volume, volume);
5488 +
5489 +    if (!control->linked || control->unlinked)
5490 +        return;
5491 +
5492 +    if (volume_changed) {
5493 +        char old_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
5494 +        char new_volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
5495 +
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));
5501 +    }
5502 +
5503 +    if (balance_changed) {
5504 +        char old_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
5505 +        char new_balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
5506 +
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));
5510 +    }
5511 +
5512 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], control);
5513 +}
5514 +
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;
5517 +    int r;
5518 +
5519 +    pa_assert(control);
5520 +    pa_assert(volume);
5521 +
5522 +    volume_local = *volume;
5523 +
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.",
5526 +                    control->name);
5527 +        return -PA_ERR_NOTSUPPORTED;
5528 +    }
5529 +
5530 +    if (set_balance
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);
5534 +
5535 +    if (pa_bvolume_equal(&volume_local, &control->volume, set_volume, set_balance))
5536 +        return 0;
5537 +
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;
5541 +
5542 +    if (r >= 0)
5543 +        set_volume_internal(control, &volume_local, set_volume, set_balance);
5544 +
5545 +    return r;
5546 +}
5547 +
5548 +void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) {
5549 +    char *old_description;
5550 +
5551 +    pa_assert(control);
5552 +    pa_assert(new_description);
5553 +
5554 +    old_description = control->description;
5555 +
5556 +    if (pa_streq(new_description, old_description))
5557 +        return;
5558 +
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,
5561 +                 new_description);
5562 +    pa_xfree(old_description);
5563 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
5564 +}
5565 +
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);
5570 +
5571 +    if (!control->linked)
5572 +        return;
5573 +
5574 +    if (control->set_volume_in_progress)
5575 +        return;
5576 +
5577 +    set_volume_internal(control, new_volume, volume_changed, balance_changed);
5578 +}
5579 +
5580 +void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) {
5581 +    pa_assert(control);
5582 +    pa_assert(device);
5583 +
5584 +    pa_assert_se(pa_hashmap_put(control->devices, device, device) >= 0);
5585 +}
5586 +
5587 +void pa_volume_control_remove_device(pa_volume_control *control, pa_device *device) {
5588 +    pa_assert(control);
5589 +    pa_assert(device);
5590 +
5591 +    pa_assert_se(pa_hashmap_remove(control->devices, device));
5592 +}
5593 +
5594 +void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device) {
5595 +    pa_assert(control);
5596 +    pa_assert(device);
5597 +
5598 +    pa_assert_se(pa_hashmap_put(control->default_for_devices, device, device) >= 0);
5599 +}
5600 +
5601 +void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device) {
5602 +    pa_assert(control);
5603 +    pa_assert(device);
5604 +
5605 +    pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
5606 +}
5607 +
5608 +void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream) {
5609 +    pa_assert(control);
5610 +    pa_assert(stream);
5611 +
5612 +    pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
5613 +}
5614 +
5615 +void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream) {
5616 +    pa_assert(control);
5617 +    pa_assert(stream);
5618 +
5619 +    pa_assert_se(pa_hashmap_remove(control->streams, stream));
5620 +}
5621 +
5622 +void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group) {
5623 +    pa_assert(control);
5624 +    pa_assert(group);
5625 +
5626 +    pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
5627 +}
5628 +
5629 +void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group) {
5630 +    pa_assert(control);
5631 +    pa_assert(group);
5632 +
5633 +    pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
5634 +}
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
5638 --- /dev/null
5639 +++ b/src/modules/volume-api/volume-control.h
5640 @@ -0,0 +1,112 @@
5641 +#ifndef foovolumecontrolhfoo
5642 +#define foovolumecontrolhfoo
5643 +
5644 +/***
5645 +  This file is part of PulseAudio.
5646 +
5647 +  Copyright 2014 Intel Corporation
5648 +
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.
5653 +
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.
5658 +
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
5662 +  USA.
5663 +***/
5664 +
5665 +#include <modules/volume-api/bvolume.h>
5666 +#include <modules/volume-api/volume-api.h>
5667 +
5668 +typedef struct pa_volume_control pa_volume_control;
5669 +
5670 +struct pa_volume_control {
5671 +    pa_volume_api *volume_api;
5672 +    uint32_t index;
5673 +    const char *name;
5674 +    char *description;
5675 +    pa_proplist *proplist;
5676 +    pa_bvolume volume;
5677 +    bool convertible_to_dB;
5678 +    bool channel_map_is_writable;
5679 +
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;
5683 +
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) */
5688 +
5689 +    bool linked;
5690 +    bool unlinked;
5691 +    bool set_volume_in_progress;
5692 +
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);
5697 +
5698 +    void *userdata;
5699 +};
5700 +
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);
5703 +
5704 +typedef void (*pa_volume_control_set_initial_volume_cb_t)(pa_volume_control *control);
5705 +
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:
5710 + *
5711 + *   1) Read-only volume controls must always specify fully valid initial
5712 + *      volume.
5713 + *   2) Volume controls with read-only channel map must always specify a valid
5714 + *      channel map in initial_volume.
5715 + *
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);
5723 +
5724 +void pa_volume_control_unlink(pa_volume_control *control);
5725 +void pa_volume_control_free(pa_volume_control *control);
5726 +
5727 +/* Called by audio-group.c only. */
5728 +void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group);
5729 +
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);
5732 +
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);
5737 +
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);
5743 +
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);
5747 +
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);
5751 +
5752 +#endif
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
5756 --- /dev/null
5757 +++ b/src/pulse/ext-volume-api.c
5758 @@ -0,0 +1,275 @@
5759 +/***
5760 +  This file is part of PulseAudio.
5761 +
5762 +  Copyright 2014 Intel Corporation
5763 +
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.
5768 +
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.
5773 +
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
5777 +  USA.
5778 +***/
5779 +
5780 +#ifdef HAVE_CONFIG_H
5781 +#include <config.h>
5782 +#endif
5783 +
5784 +#include "ext-volume-api.h"
5785 +
5786 +#include <pulsecore/core-util.h>
5787 +#include <pulsecore/i18n.h>
5788 +#include <pulsecore/macro.h>
5789 +
5790 +#include <math.h>
5791 +
5792 +int pa_ext_volume_api_balance_valid(double balance) {
5793 +    return balance >= 0.0 && balance <= 1.0;
5794 +}
5795 +
5796 +int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance) {
5797 +    unsigned channel;
5798 +
5799 +    pa_assert(volume);
5800 +
5801 +    if (check_volume && !PA_VOLUME_IS_VALID(volume->volume))
5802 +        return 0;
5803 +
5804 +    if (!check_balance)
5805 +        return 1;
5806 +
5807 +    if (!pa_channel_map_valid(&volume->channel_map))
5808 +        return 0;
5809 +
5810 +    for (channel = 0; channel < volume->channel_map.channels; channel++) {
5811 +        if (!pa_ext_volume_api_balance_valid(volume->balance[channel]))
5812 +            return 0;
5813 +    }
5814 +
5815 +    return 1;
5816 +}
5817 +
5818 +void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) {
5819 +    unsigned i;
5820 +
5821 +    pa_assert(volume);
5822 +
5823 +    volume->volume = PA_VOLUME_INVALID;
5824 +
5825 +    for (i = 0; i < PA_CHANNELS_MAX; i++)
5826 +        volume->balance[i] = -1.0;
5827 +
5828 +    pa_channel_map_init(&volume->channel_map);
5829 +}
5830 +
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));
5834 +
5835 +    bvolume->volume = volume;
5836 +    bvolume->balance[0] = 1.0;
5837 +    pa_channel_map_init_mono(&bvolume->channel_map);
5838 +}
5839 +
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) {
5842 +    unsigned i;
5843 +
5844 +    pa_assert(a);
5845 +    pa_assert(b);
5846 +
5847 +    if (check_volume && a->volume != b->volume)
5848 +        return 0;
5849 +
5850 +    if (!check_balance)
5851 +        return 1;
5852 +
5853 +    if (!pa_channel_map_equal(&a->channel_map, &b->channel_map))
5854 +        return 0;
5855 +
5856 +    for (i = 0; i < a->channel_map.channels; i++) {
5857 +        if (fabs(a->balance[i] - b->balance[i]) > 0.00001)
5858 +            return 0;
5859 +    }
5860 +
5861 +    return 1;
5862 +}
5863 +
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) {
5866 +    unsigned i;
5867 +
5868 +    pa_assert(bvolume);
5869 +    pa_assert(cvolume);
5870 +    pa_assert(map);
5871 +    pa_assert(cvolume->channels == map->channels);
5872 +
5873 +    bvolume->volume = pa_cvolume_max(cvolume);
5874 +    bvolume->channel_map = *map;
5875 +
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);
5879 +        else
5880 +            bvolume->balance[i] = 1.0;
5881 +    }
5882 +}
5883 +
5884 +void pa_ext_volume_api_bvolume_to_cvolume(const pa_ext_volume_api_bvolume *bvolume, pa_cvolume *cvolume) {
5885 +    unsigned i;
5886 +
5887 +    pa_assert(bvolume);
5888 +    pa_assert(cvolume);
5889 +    pa_assert(pa_ext_volume_api_bvolume_valid(bvolume, true, true));
5890 +
5891 +    cvolume->channels = bvolume->channel_map.channels;
5892 +
5893 +    for (i = 0; i < bvolume->channel_map.channels; i++)
5894 +        cvolume->values[i] = bvolume->volume * bvolume->balance[i];
5895 +}
5896 +
5897 +void pa_ext_volume_api_bvolume_copy_balance(pa_ext_volume_api_bvolume *to,
5898 +                                            const pa_ext_volume_api_bvolume *from) {
5899 +    pa_assert(to);
5900 +    pa_assert(from);
5901 +
5902 +    memcpy(to->balance, from->balance, sizeof(from->balance));
5903 +    to->channel_map = from->channel_map;
5904 +}
5905 +
5906 +void pa_ext_volume_api_bvolume_reset_balance(pa_ext_volume_api_bvolume *volume, const pa_channel_map *map) {
5907 +    unsigned i;
5908 +
5909 +    pa_assert(volume);
5910 +    pa_assert(map);
5911 +    pa_assert(pa_channel_map_valid(map));
5912 +
5913 +    for (i = 0; i < map->channels; i++)
5914 +        volume->balance[i] = 1.0;
5915 +
5916 +    volume->channel_map = *map;
5917 +}
5918 +
5919 +void pa_ext_volume_api_bvolume_remap(pa_ext_volume_api_bvolume *volume, const pa_channel_map *to) {
5920 +    unsigned i;
5921 +    pa_cvolume cvolume;
5922 +
5923 +    pa_assert(volume);
5924 +    pa_assert(to);
5925 +    pa_assert(pa_ext_volume_api_bvolume_valid(volume, false, true));
5926 +    pa_assert(pa_channel_map_valid(to));
5927 +
5928 +    cvolume.channels = volume->channel_map.channels;
5929 +
5930 +    for (i = 0; i < cvolume.channels; i++)
5931 +        cvolume.values[i] = volume->balance[i] * (double) PA_VOLUME_NORM;
5932 +
5933 +    pa_cvolume_remap(&cvolume, &volume->channel_map, to);
5934 +
5935 +    for (i = 0; i < to->channels; i++)
5936 +        volume->balance[i] = (double) cvolume.values[i] / (double) PA_VOLUME_NORM;
5937 +
5938 +    volume->channel_map = *to;
5939 +}
5940 +
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;
5944 +    double ret;
5945 +
5946 +    pa_assert(volume);
5947 +
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);
5952 +
5953 +    return ret;
5954 +}
5955 +
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;
5959 +
5960 +    pa_assert(volume);
5961 +
5962 +    if (!pa_channel_map_can_balance(&volume->channel_map))
5963 +        return;
5964 +
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;
5970 +}
5971 +
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;
5975 +    double ret;
5976 +
5977 +    pa_assert(volume);
5978 +
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);
5983 +
5984 +    return ret;
5985 +}
5986 +
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;
5990 +
5991 +    pa_assert(volume);
5992 +
5993 +    if (!pa_channel_map_can_fade(&volume->channel_map))
5994 +        return;
5995 +
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;
6001 +}
6002 +
6003 +char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len,
6004 +                                                const pa_ext_volume_api_bvolume *volume) {
6005 +    char *e;
6006 +    unsigned channel;
6007 +    bool first = true;
6008 +
6009 +    pa_assert(buf);
6010 +    pa_assert(buf_len > 0);
6011 +    pa_assert(volume);
6012 +
6013 +    pa_init_i18n();
6014 +
6015 +    if (!pa_ext_volume_api_bvolume_valid(volume, true, true)) {
6016 +        pa_snprintf(buf, buf_len, _("(invalid)"));
6017 +        return buf;
6018 +    }
6019 +
6020 +    *(e = buf) = 0;
6021 +
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));
6027 +
6028 +        e = strchr(e, 0);
6029 +        first = false;
6030 +    }
6031 +
6032 +    return buf;
6033 +}
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
6037 --- /dev/null
6038 +++ b/src/pulse/ext-volume-api.h
6039 @@ -0,0 +1,68 @@
6040 +#ifndef fooextvolumeapihfoo
6041 +#define fooextvolumeapihfoo
6042 +
6043 +/***
6044 +  This file is part of PulseAudio.
6045 +
6046 +  Copyright 2014 Intel Corporation
6047 +
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.
6052 +
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.
6057 +
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
6061 +  USA.
6062 +***/
6063 +
6064 +#include <pulse/cdecl.h>
6065 +#include <pulse/context.h>
6066 +#include <pulse/volume.h>
6067 +
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. */
6071 +
6072 +PA_C_DECL_BEGIN
6073 +
6074 +typedef struct pa_ext_volume_api_bvolume pa_ext_volume_api_bvolume;
6075 +
6076 +struct pa_ext_volume_api_bvolume {
6077 +    pa_volume_t volume;
6078 +    double balance[PA_CHANNELS_MAX];
6079 +    pa_channel_map channel_map;
6080 +};
6081 +
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)
6084 +        PA_GCC_PURE;
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);
6100 +
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);
6104 +
6105 +PA_C_DECL_END
6106 +
6107 +#endif
6108 -- 
6109 2.1.4
6110
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
6113 @@ -198,3 +198,5 @@
6114  src/utils/padsp.c
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