clean pulseaudio recipes
[scm/bb/meta-tizen.git] / recipes-multimedia / pulseaudio / pulseaudio_5.0 / 0080-pactl-Add-support-for-the-new-volume-API.patch
1 From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
2 Date: Wed, 21 May 2014 14:05:47 +0300
3 Subject: pactl: Add support for the new volume API
4
5 Change-Id: I2bb6625c1cd575366388ec8dc3dd4fd2097c9a4a
6 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
7 ---
8  man/pactl.1.xml.in |  50 +++-
9  src/utils/pactl.c  | 727 +++++++++++++++++++++++++++++++++++++++++++++++++++--
10  2 files changed, 759 insertions(+), 18 deletions(-)
11
12 diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
13 index 29071b3..cd54e4c 100644
14 --- a/man/pactl.1.xml.in
15 +++ b/man/pactl.1.xml.in
16 @@ -80,9 +80,12 @@ USA.
17  
18      <option>
19        <p><opt>list</opt> [<arg>short</arg>] [<arg>TYPE</arg>]</p>
20 -      <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams, etc.  <arg>TYPE</arg> must be one of:
21 -      modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards.  If not specified, all info is listed.  If
22 -      short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc>
23 +      <optdesc><p>Dump all currently loaded modules, available sinks, sources,
24 +      streams, etc.  <arg>TYPE</arg> must be one of: modules, sinks, sources,
25 +      sink-inputs, source-outputs, clients, samples, cards, volume-controls,
26 +      mute-controls, devices, streams, audio-groups. If not specified, all info
27 +      is listed. If short is given, output is in a tabular format, for easy
28 +      parsing by scripts. </p></optdesc>
29      </option>
30  
31      <option>
32 @@ -244,7 +247,46 @@ USA.
33        <arg>FORMATS</arg> is specified as a semi-colon (;) separated list of formats in the form
34        'encoding[, key1=value1, key2=value2, ...]' (for example, AC3 at 32000, 44100 and 48000 Hz would be specified as
35        'ac3-iec61937, format.rate = "[ 32000, 44100, 48000 ]"').
36 -      </p></optdesc> </option>
37 +      </p></optdesc>
38 +    </option>
39 +
40 +    <option>
41 +      <p><opt>set-volume-control-volume</opt> <arg>CONTROL</arg>
42 +        <arg>VOLUME</arg> <arg>[BALANCE ...]</arg>
43 +      </p>
44 +      <optdesc><p>Set the overall volume of the specified volume control
45 +        (identified by its name or index). <arg>VOLUME</arg> can be specified
46 +        as an integer (e.g. 2000, 16384), a linear factor (e.g. 0.4, 1.100), a
47 +        percentage (e.g.  10%, 100%) or a decibel value (e.g. 0dB, 20dB).  If
48 +        the volume specification start with a + or - the volume adjustment will
49 +        be relative to the current source output volume. Optionally, you can
50 +        also provide the channel balance (see also set-volume-control-balance).
51 +      </p></optdesc>
52 +    </option>
53 +
54 +    <option>
55 +      <p><opt>set-volume-control-balance</opt> <arg>CONTROL</arg>
56 +        <arg>BALANCE ...</arg>
57 +      </p>
58 +      <optdesc><p>Set the channel balance of the specified volume control
59 +        (identified by its name or index). The balance is given as separate
60 +        values for each channel. The balance values must be between 0.0 and
61 +        1.0. The number of values must match the volume control's channel map.
62 +      </p></optdesc>
63 +    </option>
64 +
65 +    <option>
66 +      <p>
67 +        <opt>set-mute-control-mute</opt> <arg>CONTROL</arg> <arg>1|0|toggle
68 +        </arg>
69 +      </p>
70 +      <optdesc><p>
71 +        Set the mute state of the specified mute control (identified by its
72 +        name or index). If the mute value is "toggle", then the mute control
73 +        will be muted if it was previously unmuted, and unmuted if it was
74 +        previously muted.
75 +      </p></optdesc>
76 +    </option>
77  
78      <option>
79        <p><opt>subscribe</opt></p>
80 diff --git a/src/utils/pactl.c b/src/utils/pactl.c
81 index 958d700..f947681 100644
82 --- a/src/utils/pactl.c
83 +++ b/src/utils/pactl.c
84 @@ -39,6 +39,7 @@
85  #include <pulse/pulseaudio.h>
86  #include <pulse/ext-device-restore.h>
87  #include <pulse/ext-node-manager.h>
88 +#include <pulse/ext-volume-api.h>
89  
90  #include <pulsecore/i18n.h>
91  #include <pulsecore/macro.h>
92 @@ -59,7 +60,9 @@ static char
93      *card_name = NULL,
94      *profile_name = NULL,
95      *port_name = NULL,
96 -    *formats = NULL;
97 +    *formats = NULL,
98 +    *volume_control_name = NULL,
99 +    *mute_control_name = NULL;
100  
101  static uint32_t
102      sink_input_idx = PA_INVALID_INDEX,
103 @@ -101,6 +104,14 @@ static bool nl = false;
104  static uint32_t src_node_id;
105  static uint32_t dst_node_id;
106  static uint32_t conn_id;
107 +bool volume_api_connected = false;
108 +pa_ext_volume_api_bvolume bvolume;
109 +bool volume_valid = false;
110 +bool balance_valid = false;
111 +uint32_t main_output_volume_control = PA_INVALID_INDEX;
112 +uint32_t main_input_volume_control = PA_INVALID_INDEX;
113 +uint32_t main_output_mute_control = PA_INVALID_INDEX;
114 +uint32_t main_input_mute_control = PA_INVALID_INDEX;
115  
116  static enum {
117      NONE,
118 @@ -132,6 +143,8 @@ static enum {
119      SET_SOURCE_OUTPUT_MUTE,
120      SET_SINK_FORMATS,
121      SET_PORT_LATENCY_OFFSET,
122 +    SET_VOLUME_CONTROL_VOLUME,
123 +    SET_MUTE_CONTROL_MUTE,
124      SUBSCRIBE,
125      NODE_CONNECT,
126      NODE_DISCONNECT
127 @@ -838,18 +851,18 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
128      complete_action();
129  }
130  
131 -static void volume_relative_adjust(pa_cvolume *cv) {
132 +static void volume_relative_adjust(pa_cvolume *cv, pa_volume_t adjustment) {
133      pa_assert((volume_flags & VOL_RELATIVE) == VOL_RELATIVE);
134  
135      /* Relative volume change is additive in case of UINT or PERCENT
136       * and multiplicative for LINEAR or DECIBEL */
137      if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
138          pa_volume_t v = pa_cvolume_avg(cv);
139 -        v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
140 +        v = v + adjustment < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + adjustment - PA_VOLUME_NORM;
141          pa_cvolume_set(cv, 1, v);
142      }
143      if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
144 -        pa_sw_cvolume_multiply_scalar(cv, cv, volume);
145 +        pa_sw_cvolume_multiply_scalar(cv, cv, adjustment);
146      }
147  }
148  
149 @@ -893,7 +906,7 @@ static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int i
150      pa_assert(i);
151  
152      cv = i->volume;
153 -    volume_relative_adjust(&cv);
154 +    volume_relative_adjust(&cv, volume);
155      pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
156  }
157  
158 @@ -912,7 +925,7 @@ static void get_source_volume_callback(pa_context *c, const pa_source_info *i, i
159      pa_assert(i);
160  
161      cv = i->volume;
162 -    volume_relative_adjust(&cv);
163 +    volume_relative_adjust(&cv, volume);
164      pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
165  }
166  
167 @@ -931,7 +944,7 @@ static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_in
168      pa_assert(i);
169  
170      cv = i->volume;
171 -    volume_relative_adjust(&cv);
172 +    volume_relative_adjust(&cv, volume);
173      pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
174  }
175  
176 @@ -950,7 +963,7 @@ static void get_source_output_volume_callback(pa_context *c, const pa_source_out
177      pa_assert(o);
178  
179      cv = o->volume;
180 -    volume_relative_adjust(&cv);
181 +    volume_relative_adjust(&cv, volume);
182      pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
183  }
184  
185 @@ -1189,6 +1202,575 @@ static void context_subscribe_callback(pa_context *c, pa_subscription_event_type
186      fflush(stdout);
187  }
188  
189 +static void get_volume_control_info_callback(pa_context *c, const pa_ext_volume_api_volume_control_info *info,
190 +                                             int is_last, void *userdata) {
191 +    char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
192 +    char balance_str[PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX];
193 +    char *proplist_str;
194 +
195 +    pa_assert(c);
196 +
197 +    if (is_last < 0) {
198 +        pa_log(_("Failed to get volume control information: %s"), pa_strerror(pa_context_errno(c)));
199 +        quit(1);
200 +        return;
201 +    }
202 +
203 +    if (is_last) {
204 +        complete_action();
205 +        return;
206 +    }
207 +
208 +    pa_assert(info);
209 +
210 +    if (action == INFO) {
211 +        if (info->index == main_output_volume_control)
212 +            printf(_("Main output volume control: %s\n"), info->name);
213 +
214 +        if (info->index == main_input_volume_control)
215 +            printf(_("Main input volume control: %s\n"), info->name);
216 +
217 +        return;
218 +    }
219 +
220 +    if (action == SET_VOLUME_CONTROL_VOLUME) {
221 +        pa_ext_volume_api_bvolume bv;
222 +
223 +        if (balance_valid && bvolume.channel_map.channels != info->volume.channel_map.channels) {
224 +            pa_log(_("Incompatible number of channels, expected %u channels."), info->volume.channel_map.channels);
225 +            quit(1);
226 +        }
227 +
228 +        bv = info->volume;
229 +
230 +        if (volume_valid) {
231 +            if (volume_flags & VOL_RELATIVE) {
232 +                pa_cvolume cv;
233 +
234 +                pa_cvolume_set(&cv, 1, info->volume.volume);
235 +                volume_relative_adjust(&cv, bvolume.volume);
236 +                bv.volume = cv.values[0];
237 +            } else
238 +                bv.volume = bvolume.volume;
239 +        }
240 +
241 +        if (balance_valid)
242 +            memcpy(bv.balance, bvolume.balance, sizeof(bv.balance));
243 +
244 +        pa_operation_unref(pa_ext_volume_api_set_volume_control_volume_by_name(c, volume_control_name, &bv,
245 +                                                                               volume_valid, balance_valid,
246 +                                                                               simple_callback, NULL));
247 +        actions++;
248 +
249 +        return;
250 +    }
251 +
252 +    pa_assert(action == LIST);
253 +
254 +    if (nl && !short_list_format)
255 +        printf("\n");
256 +    nl = true;
257 +
258 +    if (short_list_format) {
259 +        printf("%u\t%s\t%u\n", info->index, info->name, info->volume.volume);
260 +        return;
261 +    }
262 +
263 +    pa_volume_snprint_verbose(volume_str, sizeof(volume_str), info->volume.volume, info->convertible_to_dB);
264 +    pa_ext_volume_api_bvolume_snprint_balance(balance_str, sizeof(balance_str), &info->volume);
265 +    proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
266 +
267 +    printf(_("Volume Control #%u\n"
268 +             "\tName: %s\n"
269 +             "\tDescription: %s\n"
270 +             "\tVolume: %s\n"
271 +             "\tBalance: %s\n"
272 +             "\tProperties: %s%s\n"),
273 +             info->index,
274 +             info->name,
275 +             info->description,
276 +             volume_str,
277 +             balance_str,
278 +             *proplist_str ? "\n\t\t" : _("(none)"),
279 +             proplist_str);
280 +
281 +    pa_xfree(proplist_str);
282 +}
283 +
284 +static void get_mute_control_info_callback(pa_context *c, const pa_ext_volume_api_mute_control_info *info, int is_last,
285 +                                           void *userdata) {
286 +    char *proplist_str;
287 +
288 +    pa_assert(c);
289 +
290 +    if (is_last < 0) {
291 +        pa_log(_("Failed to get mute control information: %s"), pa_strerror(pa_context_errno(c)));
292 +        quit(1);
293 +        return;
294 +    }
295 +
296 +    if (is_last) {
297 +        complete_action();
298 +        return;
299 +    }
300 +
301 +    pa_assert(info);
302 +
303 +    if (action == INFO) {
304 +        if (info->index == main_output_mute_control)
305 +            printf(_("Main output mute control: %s\n"), info->name);
306 +
307 +        if (info->index == main_input_mute_control)
308 +            printf(_("Main input mute control: %s\n"), info->name);
309 +
310 +        return;
311 +    }
312 +
313 +    if (action == SET_MUTE_CONTROL_MUTE) {
314 +        pa_operation_unref(pa_ext_volume_api_set_mute_control_mute_by_index(c, info->index, info->mute ? false : true,
315 +                                                                                   simple_callback, NULL));
316 +        actions++;
317 +        return;
318 +    }
319 +
320 +    pa_assert(action == LIST);
321 +
322 +    if (nl && !short_list_format)
323 +        printf("\n");
324 +    nl = true;
325 +
326 +    if (short_list_format) {
327 +        printf("%u\t%s\t%s\n", info->index, info->name, pa_yes_no(info->mute));
328 +        return;
329 +    }
330 +
331 +    proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
332 +
333 +    printf(_("Mute Control #%u\n"
334 +             "\tName: %s\n"
335 +             "\tDescription: %s\n"
336 +             "\tMute: %s\n"
337 +             "\tProperties: %s%s\n"),
338 +             info->index,
339 +             info->name,
340 +             info->description,
341 +             pa_yes_no(info->mute),
342 +             *proplist_str ? "\n\t\t" : _("(none)"),
343 +             proplist_str);
344 +
345 +    pa_xfree(proplist_str);
346 +}
347 +
348 +static void volume_api_get_server_info_callback(pa_context *c, const pa_ext_volume_api_server_info *info, void *userdata) {
349 +    pa_assert(c);
350 +
351 +    if (!info) {
352 +        pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
353 +        quit(1);
354 +        return;
355 +    }
356 +
357 +    main_output_volume_control = info->main_output_volume_control;
358 +    main_input_volume_control = info->main_input_volume_control;
359 +    main_output_mute_control = info->main_output_mute_control;
360 +    main_input_mute_control = info->main_input_mute_control;
361 +
362 +    if (main_output_volume_control == PA_INVALID_INDEX)
363 +        printf(_("Main output volume control: (unset)\n"));
364 +
365 +    if (main_input_volume_control == PA_INVALID_INDEX)
366 +        printf(_("Main input volume control: (unset)\n"));
367 +
368 +    if (main_output_mute_control == PA_INVALID_INDEX)
369 +        printf(_("Main output mute control: (unset)\n"));
370 +
371 +    if (main_input_mute_control == PA_INVALID_INDEX)
372 +        printf(_("Main input mute control: (unset)\n"));
373 +
374 +    if (main_output_volume_control != PA_INVALID_INDEX || main_input_volume_control != PA_INVALID_INDEX) {
375 +        pa_operation_unref(pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL));
376 +        actions++;
377 +    }
378 +
379 +    if (main_output_mute_control != PA_INVALID_INDEX || main_input_mute_control != PA_INVALID_INDEX) {
380 +        pa_operation_unref(pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL));
381 +        actions++;
382 +    }
383 +
384 +    complete_action();
385 +}
386 +
387 +static void get_device_info_callback(pa_context *c, const pa_ext_volume_api_device_info *info, int is_last,
388 +                                     void *userdata) {
389 +    char *device_types_str = NULL;
390 +    char *volume_control_str;
391 +    char *mute_control_str;
392 +    char *proplist_str;
393 +
394 +    pa_assert(c);
395 +
396 +    if (is_last < 0) {
397 +        pa_log(_("Failed to get device information: %s"), pa_strerror(pa_context_errno(c)));
398 +        quit(1);
399 +        return;
400 +    }
401 +
402 +    if (is_last) {
403 +        complete_action();
404 +        return;
405 +    }
406 +
407 +    pa_assert(info);
408 +
409 +    if (nl && !short_list_format)
410 +        printf("\n");
411 +    nl = true;
412 +
413 +    if (info->n_device_types > 0)
414 +        device_types_str = pa_join(info->device_types, info->n_device_types, ", ");
415 +    else
416 +        device_types_str = pa_xstrdup(_("(none)"));
417 +
418 +    if (info->volume_control != PA_INVALID_INDEX)
419 +        volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
420 +    else
421 +        volume_control_str = pa_xstrdup(_("(unset)"));
422 +
423 +    if (info->mute_control != PA_INVALID_INDEX)
424 +        mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
425 +    else
426 +        mute_control_str = pa_xstrdup(_("(unset)"));
427 +
428 +    if (short_list_format) {
429 +        printf("%u\t%s\t%s\t%s\t%s\t%s\n", info->index, info->name, pa_direction_to_string(info->direction), device_types_str,
430 +               volume_control_str, mute_control_str);
431 +        pa_xfree(mute_control_str);
432 +        pa_xfree(volume_control_str);
433 +        pa_xfree(device_types_str);
434 +        return;
435 +    }
436 +
437 +    proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
438 +
439 +    printf(_("Device #%u\n"
440 +             "\tName: %s\n"
441 +             "\tDescription: %s\n"
442 +             "\tDirection: %s\n"
443 +             "\tDevice Types: %s\n"
444 +             "\tVolume Control: %s\n"
445 +             "\tMute Control: %s\n"
446 +             "\tProperties: %s%s\n"),
447 +             info->index,
448 +             info->name,
449 +             info->description,
450 +             pa_direction_to_string(info->direction),
451 +             device_types_str,
452 +             volume_control_str,
453 +             mute_control_str,
454 +             *proplist_str ? "\n\t\t" : _("(none)"),
455 +             proplist_str);
456 +
457 +    pa_xfree(proplist_str);
458 +    pa_xfree(mute_control_str);
459 +    pa_xfree(volume_control_str);
460 +    pa_xfree(device_types_str);
461 +}
462 +
463 +static void get_stream_info_callback(pa_context *c, const pa_ext_volume_api_stream_info *info, int is_last,
464 +                                     void *userdata) {
465 +    char *volume_control_str;
466 +    char *mute_control_str;
467 +    char *proplist_str;
468 +
469 +    pa_assert(c);
470 +
471 +    if (is_last < 0) {
472 +        pa_log(_("Failed to get stream information: %s"), pa_strerror(pa_context_errno(c)));
473 +        quit(1);
474 +        return;
475 +    }
476 +
477 +    if (is_last) {
478 +        complete_action();
479 +        return;
480 +    }
481 +
482 +    pa_assert(info);
483 +
484 +    if (nl && !short_list_format)
485 +        printf("\n");
486 +    nl = true;
487 +
488 +    if (info->volume_control != PA_INVALID_INDEX)
489 +        volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
490 +    else
491 +        volume_control_str = pa_xstrdup(_("(unset)"));
492 +
493 +    if (info->mute_control != PA_INVALID_INDEX)
494 +        mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
495 +    else
496 +        mute_control_str = pa_xstrdup(_("(unset)"));
497 +
498 +    if (short_list_format) {
499 +        printf("%u\t%s\t%s\t%s\t%s\n", info->index, info->name, pa_direction_to_string(info->direction), volume_control_str,
500 +               mute_control_str);
501 +        pa_xfree(mute_control_str);
502 +        pa_xfree(volume_control_str);
503 +        return;
504 +    }
505 +
506 +    proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
507 +
508 +    printf(_("Stream #%u\n"
509 +             "\tName: %s\n"
510 +             "\tDescription: %s\n"
511 +             "\tDirection: %s\n"
512 +             "\tVolume Control: %s\n"
513 +             "\tMute Control: %s\n"
514 +             "\tProperties: %s%s\n"),
515 +             info->index,
516 +             info->name,
517 +             info->description,
518 +             pa_direction_to_string(info->direction),
519 +             volume_control_str,
520 +             mute_control_str,
521 +             *proplist_str ? "\n\t\t" : _("(none)"),
522 +             proplist_str);
523 +
524 +    pa_xfree(proplist_str);
525 +    pa_xfree(mute_control_str);
526 +    pa_xfree(volume_control_str);
527 +}
528 +
529 +static void get_audio_group_info_callback(pa_context *c, const pa_ext_volume_api_audio_group_info *info, int is_last,
530 +                                          void *userdata) {
531 +    char *volume_control_str;
532 +    char *mute_control_str;
533 +    char *proplist_str;
534 +
535 +    pa_assert(c);
536 +
537 +    if (is_last < 0) {
538 +        pa_log(_("Failed to get audio group information: %s"), pa_strerror(pa_context_errno(c)));
539 +        quit(1);
540 +        return;
541 +    }
542 +
543 +    if (is_last) {
544 +        complete_action();
545 +        return;
546 +    }
547 +
548 +    pa_assert(info);
549 +
550 +    if (nl && !short_list_format)
551 +        printf("\n");
552 +    nl = true;
553 +
554 +    if (info->volume_control != PA_INVALID_INDEX)
555 +        volume_control_str = pa_sprintf_malloc("%u", info->volume_control);
556 +    else
557 +        volume_control_str = pa_xstrdup(_("(unset)"));
558 +
559 +    if (info->mute_control != PA_INVALID_INDEX)
560 +        mute_control_str = pa_sprintf_malloc("%u", info->mute_control);
561 +    else
562 +        mute_control_str = pa_xstrdup(_("(unset)"));
563 +
564 +    if (short_list_format) {
565 +        printf("%u\t%s\t%s\t%s\n", info->index, info->name, volume_control_str, mute_control_str);
566 +        pa_xfree(mute_control_str);
567 +        pa_xfree(volume_control_str);
568 +        return;
569 +    }
570 +
571 +    proplist_str = pa_proplist_to_string_sep(info->proplist, "\n\t\t");
572 +
573 +    printf(_("Audio Group #%u\n"
574 +             "\tName: %s\n"
575 +             "\tDescription: %s\n"
576 +             "\tVolume Control: %s\n"
577 +             "\tMute Control: %s\n"
578 +             "\tProperties: %s%s\n"),
579 +             info->index,
580 +             info->name,
581 +             info->description,
582 +             volume_control_str,
583 +             mute_control_str,
584 +             *proplist_str ? "\n\t\t" : _("(none)"),
585 +             proplist_str);
586 +
587 +    pa_xfree(proplist_str);
588 +    pa_xfree(mute_control_str);
589 +    pa_xfree(volume_control_str);
590 +}
591 +
592 +static const char *volume_api_subscription_event_facility_to_string(pa_ext_volume_api_subscription_event_type_t type) {
593 +
594 +    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
595 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_SERVER:
596 +            return _("server (volume API)");
597 +
598 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_VOLUME_CONTROL:
599 +            return _("volume-control");
600 +
601 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_MUTE_CONTROL:
602 +            return _("mute-control");
603 +
604 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_DEVICE:
605 +            return _("device");
606 +
607 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_STREAM:
608 +            return _("stream");
609 +
610 +        case PA_EXT_VOLUME_API_SUBSCRIPTION_EVENT_AUDIO_GROUP:
611 +            return _("audio-group");
612 +    }
613 +
614 +    return _("unknown");
615 +}
616 +
617 +static void volume_api_subscribe_cb(pa_context *c, pa_ext_volume_api_subscription_event_type_t event_type, uint32_t idx,
618 +                                    void *userdata) {
619 +    pa_assert(c);
620 +
621 +    printf(_("Event '%s' on %s #%u\n"),
622 +           subscription_event_type_to_string(event_type),
623 +           volume_api_subscription_event_facility_to_string(event_type),
624 +           idx);
625 +    fflush(stdout);
626 +}
627 +
628 +static void volume_api_state_cb(pa_context *c, void *userdata) {
629 +    pa_ext_volume_api_state_t state;
630 +
631 +    pa_assert(c);
632 +
633 +    state = pa_ext_volume_api_get_state(c);
634 +
635 +    switch (state) {
636 +        case PA_EXT_VOLUME_API_STATE_READY: {
637 +            pa_operation *o = NULL;
638 +
639 +            volume_api_connected = true;
640 +
641 +            switch (action) {
642 +                case INFO:
643 +                    o = pa_ext_volume_api_get_server_info(c, volume_api_get_server_info_callback, NULL);
644 +                    actions++;
645 +                    break;
646 +
647 +                case LIST:
648 +                    if (!list_type) {
649 +                        o = pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL);
650 +                        pa_operation_unref(o);
651 +                        o = pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL);
652 +                        pa_operation_unref(o);
653 +                        o = pa_ext_volume_api_get_device_info_list(c, get_device_info_callback, NULL);
654 +                        pa_operation_unref(o);
655 +                        o = pa_ext_volume_api_get_stream_info_list(c, get_stream_info_callback, NULL);
656 +                        pa_operation_unref(o);
657 +                        o = pa_ext_volume_api_get_audio_group_info_list(c, get_audio_group_info_callback, NULL);
658 +                        pa_operation_unref(o);
659 +                        o = NULL;
660 +                        actions += 4;
661 +                    } else if (pa_streq(list_type, "volume-controls")) {
662 +                        o = pa_ext_volume_api_get_volume_control_info_list(c, get_volume_control_info_callback, NULL);
663 +                        actions++;
664 +                    } else if (pa_streq(list_type, "mute-controls")) {
665 +                        o = pa_ext_volume_api_get_mute_control_info_list(c, get_mute_control_info_callback, NULL);
666 +                        actions++;
667 +                    } else if (pa_streq(list_type, "devices")) {
668 +                        o = pa_ext_volume_api_get_device_info_list(c, get_device_info_callback, NULL);
669 +                        actions++;
670 +                    } else if (pa_streq(list_type, "streams")) {
671 +                        o = pa_ext_volume_api_get_stream_info_list(c, get_stream_info_callback, NULL);
672 +                        actions++;
673 +                    } else if (pa_streq(list_type, "audio-groups")) {
674 +                        o = pa_ext_volume_api_get_audio_group_info_list(c, get_audio_group_info_callback, NULL);
675 +                        actions++;
676 +                    }
677 +                    break;
678 +
679 +                case SET_VOLUME_CONTROL_VOLUME:
680 +                    if (!balance_valid && !(volume_flags & VOL_RELATIVE)) {
681 +                        pa_assert(volume_valid);
682 +                        o = pa_ext_volume_api_set_volume_control_volume_by_name(c, volume_control_name, &bvolume, true,
683 +                                                                                false, simple_callback, NULL);
684 +                    } else
685 +                        o = pa_ext_volume_api_get_volume_control_info_by_name(c, volume_control_name,
686 +                                                                              get_volume_control_info_callback, NULL);
687 +
688 +                    actions++;
689 +                    break;
690 +
691 +                case SET_MUTE_CONTROL_MUTE:
692 +                    if (mute == TOGGLE_MUTE)
693 +                        o = pa_ext_volume_api_get_mute_control_info_by_name(c, mute_control_name,
694 +                                                                                  get_mute_control_info_callback, NULL);
695 +                    else
696 +                        o = pa_ext_volume_api_set_mute_control_mute_by_name(c, mute_control_name, mute, simple_callback,
697 +                                                                            NULL);
698 +
699 +                    actions++;
700 +                    break;
701 +
702 +                case SUBSCRIBE:
703 +                    pa_ext_volume_api_set_subscribe_callback(c, volume_api_subscribe_cb, NULL);
704 +                    o = pa_ext_volume_api_subscribe(c, PA_EXT_VOLUME_API_SUBSCRIPTION_MASK_ALL, NULL, NULL);
705 +                    break;
706 +
707 +                default:
708 +                    break;
709 +            }
710 +
711 +            if (o)
712 +                pa_operation_unref(o);
713 +
714 +            complete_action();
715 +            break;
716 +        }
717 +
718 +        case PA_EXT_VOLUME_API_STATE_FAILED:
719 +            pa_log("Volume API context failed: %s", pa_strerror(pa_context_errno(c)));
720 +
721 +            /* If the main context failed too, let's not do anything, because
722 +             * calling complete_action() would reset the context error code to
723 +             * PA_ERR_BADSTATE, meaning that the original error code would be
724 +             * lost. */
725 +            if (pa_context_get_state(c) == PA_CONTEXT_FAILED)
726 +                break;
727 +
728 +            if (action == INFO || (action == LIST && !list_type) || action == SUBSCRIBE) {
729 +                /* In these cases we shouldn't exit with an error if the volume
730 +                 * API happens to be or become unavailable. If we haven't yet
731 +                 * connected to the volume API, then we need to complete the
732 +                 * "connect to volume API" action. */
733 +
734 +                if (!volume_api_connected)
735 +                    complete_action();
736 +            } else
737 +                quit(1);
738 +
739 +            break;
740 +
741 +        default:
742 +            break;
743 +    }
744 +}
745 +
746 +static void connect_to_volume_api(void) {
747 +    int r;
748 +
749 +    pa_assert(context);
750 +
751 +    pa_ext_volume_api_set_state_callback(context, volume_api_state_cb, NULL);
752 +
753 +    r = pa_ext_volume_api_connect(context);
754 +    if (r >= 0)
755 +        actions++;
756 +}
757 +
758  static void context_state_callback(pa_context *c, void *userdata) {
759      pa_operation *o = NULL;
760  
761 @@ -1215,6 +1797,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
762  
763                  case INFO:
764                      o = pa_context_get_server_info(c, get_server_info_callback, NULL);
765 +                    connect_to_volume_api();
766                      break;
767  
768                  case PLAY_SAMPLE:
769 @@ -1259,7 +1842,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
770                              o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
771                         else if (pa_streq(list_type, "nodes"))
772                             o = pa_ext_node_manager_read_nodes(c, node_list_callback, NULL);
773 -                        else
774 +                        else if (pa_streq(list_type, "volume-controls")
775 +                                     || pa_streq(list_type, "mute-controls")
776 +                                     || pa_streq(list_type, "devices")
777 +                                     || pa_streq(list_type, "streams")
778 +                                     || pa_streq(list_type, "audio-groups")) {
779 +                            connect_to_volume_api();
780 +                            o = NULL;
781 +                        } else
782                              pa_assert_not_reached();
783                      } else {
784                          o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
785 @@ -1309,6 +1899,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
786                              actions++;
787                          }
788  
789 +                        connect_to_volume_api();
790                          o = NULL;
791                      }
792                      break;
793 @@ -1442,6 +2033,11 @@ static void context_state_callback(pa_context *c, void *userdata) {
794                      o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
795                      break;
796  
797 +                case SET_VOLUME_CONTROL_VOLUME:
798 +                case SET_MUTE_CONTROL_MUTE:
799 +                    connect_to_volume_api();
800 +                    break;
801 +
802                  case SUBSCRIBE:
803                      pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
804  
805 @@ -1457,6 +2053,14 @@ static void context_state_callback(pa_context *c, void *userdata) {
806                                               PA_SUBSCRIPTION_MASK_CARD,
807                                               NULL,
808                                               NULL);
809 +
810 +                    if (o) {
811 +                        pa_operation_unref(o);
812 +                        actions++;
813 +                        o = NULL;
814 +                    }
815 +
816 +                    connect_to_volume_api();
817                      break;
818                 case NODE_CONNECT:
819                     pa_operation_unref(pa_ext_node_manager_connect_nodes(c,
820 @@ -1599,6 +2203,9 @@ static void help(const char *argv0) {
821      printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
822      printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
823      printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
824 +    printf("%s %s %s %s\n", argv0, _("[options]"), "set-volume-control-volume", _("NAME|#N VOLUME [BALANCE ...]"));
825 +    printf("%s %s %s %s\n", argv0, _("[options]"), "set-volume-control-balance", _("NAME|#N BALANCE ..."));
826 +    printf("%s %s %s %s\n", argv0, _("[options]"), "set-mute-control-mute", _("NAME|#N 1|0|toggle"));
827      printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
828      printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
829               "can be used to specify the default sink, source and monitor.\n"));
830 @@ -1613,6 +2220,40 @@ static void help(const char *argv0) {
831               "  -n, --client-name=NAME                How to call this client on the server\n"));
832  }
833  
834 +static int parse_balance(char *argv[], unsigned first_arg, unsigned n_channels, pa_ext_volume_api_bvolume *bv) {
835 +    pa_ext_volume_api_bvolume bv_local;
836 +    unsigned i;
837 +
838 +    pa_assert(n_channels > 0);
839 +    pa_assert(bv);
840 +
841 +    if (n_channels > PA_CHANNELS_MAX) {
842 +        pa_log("Too many channels, the maximum is %u.", PA_CHANNELS_MAX);
843 +        return -1;
844 +    }
845 +
846 +    bv_local = *bv;
847 +
848 +    for (i = 0; i < n_channels; i++) {
849 +        const char *balance_str;
850 +        double balance;
851 +
852 +        balance_str = argv[first_arg + i];
853 +
854 +        if (pa_atod(balance_str, &balance) < 0 || !pa_ext_volume_api_balance_valid(balance)) {
855 +            pa_log(_("Invalid balance value: %s"), balance_str);
856 +            return -1;
857 +        }
858 +
859 +        bv_local.balance[i] = balance;
860 +    }
861 +
862 +    bv_local.channel_map.channels = n_channels;
863 +    *bv = bv_local;
864 +
865 +    return 0;
866 +}
867 +
868  enum {
869      ARG_VERSION = 256
870  };
871 @@ -1698,15 +2339,19 @@ int main(int argc, char *argv[]) {
872              action = LIST;
873  
874              for (int i = optind+1; i < argc; i++) {
875 -                if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
876 -                    pa_streq(argv[i], "sinks")   || pa_streq(argv[i], "sink-inputs") ||
877 -                    pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
878 -                    pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes")) {
879 +                if (pa_streq(argv[i], "modules")         || pa_streq(argv[i], "clients") ||
880 +                    pa_streq(argv[i], "sinks")           || pa_streq(argv[i], "sink-inputs") ||
881 +                    pa_streq(argv[i], "sources")         || pa_streq(argv[i], "source-outputs") ||
882 +                    pa_streq(argv[i], "samples")         || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes") ||
883 +                    pa_streq(argv[i], "volume-controls") || pa_streq(argv[i], "mute-controls") ||
884 +                    pa_streq(argv[i], "devices")         || pa_streq(argv[i], "streams") ||
885 +                    pa_streq(argv[i], "audio-groups")) {
886                      list_type = pa_xstrdup(argv[i]);
887                  } else if (pa_streq(argv[i], "short")) {
888                      short_list_format = true;
889                  } else {
890 -                    pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
891 +                    pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, "
892 +                             "clients, samples, cards, volume-controls, mute-controls, devices, streams, audio-groups");
893                      goto quit;
894                  }
895              }
896 @@ -2092,6 +2737,58 @@ int main(int argc, char *argv[]) {
897  
898             conn_id = (uint32_t) atoi(argv[optind+1]);
899  
900 +        } else if (pa_streq(argv[optind], "set-volume-control-volume")) {
901 +            action = SET_VOLUME_CONTROL_VOLUME;
902 +
903 +            if (argc < optind + 3) {
904 +                pa_log(_("You have to specify a volume control name/index and a volume, and optionally balance parameters."));
905 +                goto quit;
906 +            }
907 +
908 +            volume_control_name = pa_xstrdup(argv[optind + 1]);
909 +
910 +            if (parse_volume(argv[optind + 2], &bvolume.volume, &volume_flags) < 0)
911 +                goto quit;
912 +
913 +            volume_valid = true;
914 +
915 +            if (argc > optind + 3) {
916 +                if (parse_balance(argv, optind + 3, argc - (optind + 3), &bvolume) < 0)
917 +                    goto quit;
918 +
919 +                balance_valid = true;
920 +            }
921 +
922 +        } else if (pa_streq(argv[optind], "set-volume-control-balance")) {
923 +            action = SET_VOLUME_CONTROL_VOLUME;
924 +
925 +            if (argc < optind + 3) {
926 +                pa_log(_("You have to specify a volume control name/index and balance parameters."));
927 +                goto quit;
928 +            }
929 +
930 +            volume_control_name = pa_xstrdup(argv[optind + 1]);
931 +
932 +            if (parse_balance(argv, optind + 2, argc - (optind + 2), &bvolume) < 0)
933 +                goto quit;
934 +
935 +            balance_valid = true;
936 +
937 +        } else if (pa_streq(argv[optind], "set-mute-control-mute")) {
938 +            action = SET_MUTE_CONTROL_MUTE;
939 +
940 +            if (argc != optind + 3) {
941 +                pa_log(_("You have to specify a mute control name/index and a mute value."));
942 +                goto quit;
943 +            }
944 +
945 +            mute_control_name = pa_xstrdup(argv[optind + 1]);
946 +
947 +            if ((mute = parse_mute(argv[optind + 2])) == INVALID_MUTE) {
948 +                pa_log(_("Invalid mute specification"));
949 +                goto quit;
950 +            }
951 +
952         } else if (pa_streq(argv[optind], "help")) {
953              help(bn);
954              ret = 0;
955 @@ -2154,6 +2851,8 @@ quit:
956      pa_xfree(profile_name);
957      pa_xfree(port_name);
958      pa_xfree(formats);
959 +    pa_xfree(mute_control_name);
960 +    pa_xfree(volume_control_name);
961  
962      if (sndfile)
963          sf_close(sndfile);