• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <signal.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <locale.h>
33 #include <ctype.h>
34 
35 #include <sndfile.h>
36 
37 #include <pulse/pulseaudio.h>
38 #include <pulse/ext-device-restore.h>
39 
40 #include <pulsecore/i18n.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/sndfile-util.h>
45 
46 static pa_context *context = NULL;
47 static pa_mainloop_api *mainloop_api = NULL;
48 
49 static char
50     *list_type = NULL,
51     *sample_name = NULL,
52     *sink_name = NULL,
53     *source_name = NULL,
54     *module_name = NULL,
55     *module_args = NULL,
56     *card_name = NULL,
57     *profile_name = NULL,
58     *port_name = NULL,
59     *formats = NULL;
60 
61 static uint32_t
62     sink_input_idx = PA_INVALID_INDEX,
63     source_output_idx = PA_INVALID_INDEX,
64     sink_idx = PA_INVALID_INDEX;
65 
66 static bool short_list_format = false;
67 static uint32_t module_index;
68 static int32_t latency_offset;
69 static bool suspend;
70 static pa_cvolume volume;
71 static enum volume_flags {
72     VOL_UINT     = 0,
73     VOL_PERCENT  = 1,
74     VOL_LINEAR   = 2,
75     VOL_DECIBEL  = 3,
76     VOL_ABSOLUTE = 0 << 4,
77     VOL_RELATIVE = 1 << 4,
78 } volume_flags;
79 
80 static enum mute_flags {
81     INVALID_MUTE = -1,
82     UNMUTE = 0,
83     MUTE = 1,
84     TOGGLE_MUTE = 2
85 } mute = INVALID_MUTE;
86 
87 static pa_proplist *proplist = NULL;
88 
89 static SNDFILE *sndfile = NULL;
90 static pa_stream *sample_stream = NULL;
91 static pa_sample_spec sample_spec;
92 static pa_channel_map channel_map;
93 static size_t sample_length = 0;
94 
95 /* This variable tracks the number of ongoing asynchronous operations. When a
96  * new operation begins, this is incremented simply with actions++, and when
97  * an operation finishes, this is decremented with the complete_action()
98  * function, which shuts down the program if actions reaches zero. */
99 static int actions = 0;
100 
101 static bool nl = false;
102 
103 static enum {
104     NONE,
105     EXIT,
106     STAT,
107     INFO,
108     UPLOAD_SAMPLE,
109     PLAY_SAMPLE,
110     REMOVE_SAMPLE,
111     LIST,
112     MOVE_SINK_INPUT,
113     MOVE_SOURCE_OUTPUT,
114     LOAD_MODULE,
115     UNLOAD_MODULE,
116     SUSPEND_SINK,
117     SUSPEND_SOURCE,
118     SET_CARD_PROFILE,
119     SET_SINK_PORT,
120     SET_DEFAULT_SINK,
121     SET_SOURCE_PORT,
122     SET_DEFAULT_SOURCE,
123     SET_SINK_VOLUME,
124     SET_SOURCE_VOLUME,
125     SET_SINK_INPUT_VOLUME,
126     SET_SOURCE_OUTPUT_VOLUME,
127     SET_SINK_MUTE,
128     SET_SOURCE_MUTE,
129     SET_SINK_INPUT_MUTE,
130     SET_SOURCE_OUTPUT_MUTE,
131     SET_SINK_FORMATS,
132     SET_PORT_LATENCY_OFFSET,
133     SUBSCRIBE
134 } action = NONE;
135 
quit(int ret)136 static void quit(int ret) {
137     pa_assert(mainloop_api);
138     mainloop_api->quit(mainloop_api, ret);
139 }
140 
context_drain_complete(pa_context * c,void * userdata)141 static void context_drain_complete(pa_context *c, void *userdata) {
142     pa_context_disconnect(c);
143 }
144 
drain(void)145 static void drain(void) {
146     pa_operation *o;
147 
148     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
149         pa_context_disconnect(context);
150     else
151         pa_operation_unref(o);
152 }
153 
complete_action(void)154 static void complete_action(void) {
155     pa_assert(actions > 0);
156 
157     if (!(--actions))
158         drain();
159 }
160 
stat_callback(pa_context * c,const pa_stat_info * i,void * userdata)161 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
162     char s[PA_BYTES_SNPRINT_MAX];
163     if (!i) {
164         pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
165         quit(1);
166         return;
167     }
168 
169     pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
170     printf(ngettext("Currently in use: %u block containing %s bytes total.\n",
171                     "Currently in use: %u blocks containing %s bytes total.\n",
172                     i->memblock_total),
173            i->memblock_total, s);
174 
175     pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
176     printf(ngettext("Allocated during whole lifetime: %u block containing %s bytes total.\n",
177                     "Allocated during whole lifetime: %u blocks containing %s bytes total.\n",
178                     i->memblock_allocated),
179            i->memblock_allocated, s);
180 
181     pa_bytes_snprint(s, sizeof(s), i->scache_size);
182     printf(_("Sample cache size: %s\n"), s);
183 
184     complete_action();
185 }
186 
get_server_info_callback(pa_context * c,const pa_server_info * i,void * useerdata)187 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
188     char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
189 
190     if (!i) {
191         pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
192         quit(1);
193         return;
194     }
195 
196     printf(_("Server String: %s\n"
197              "Library Protocol Version: %u\n"
198              "Server Protocol Version: %u\n"
199              "Is Local: %s\n"
200              "Client Index: %u\n"
201              "Tile Size: %zu\n"),
202              pa_context_get_server(c),
203              pa_context_get_protocol_version(c),
204              pa_context_get_server_protocol_version(c),
205              pa_yes_no_localised(pa_context_is_local(c)),
206              pa_context_get_index(c),
207              pa_context_get_tile_size(c, NULL));
208 
209     pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
210     pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
211 
212     printf(_("User Name: %s\n"
213              "Host Name: %s\n"
214              "Server Name: %s\n"
215              "Server Version: %s\n"
216              "Default Sample Specification: %s\n"
217              "Default Channel Map: %s\n"
218              "Default Sink: %s\n"
219              "Default Source: %s\n"
220              "Cookie: %04x:%04x\n"),
221            i->user_name,
222            i->host_name,
223            i->server_name,
224            i->server_version,
225            ss,
226            cm,
227            i->default_sink_name,
228            i->default_source_name,
229            i->cookie >> 16,
230            i->cookie & 0xFFFFU);
231 
232     complete_action();
233 }
234 
get_available_str(int available)235 static const char* get_available_str(int available) {
236     switch (available) {
237         case PA_PORT_AVAILABLE_UNKNOWN: return _("availability unknown");
238         case PA_PORT_AVAILABLE_YES: return _("available");
239         case PA_PORT_AVAILABLE_NO: return _("not available");
240     }
241 
242     pa_assert_not_reached();
243 }
244 
get_device_port_type(unsigned int type)245 static const char* get_device_port_type(unsigned int type) {
246     static char buf[32];
247     switch (type) {
248     case PA_DEVICE_PORT_TYPE_UNKNOWN: return _("Unknown");
249     case PA_DEVICE_PORT_TYPE_AUX: return _("Aux");
250     case PA_DEVICE_PORT_TYPE_SPEAKER: return _("Speaker");
251     case PA_DEVICE_PORT_TYPE_HEADPHONES: return _("Headphones");
252     case PA_DEVICE_PORT_TYPE_LINE: return _("Line");
253     case PA_DEVICE_PORT_TYPE_MIC: return _("Mic");
254     case PA_DEVICE_PORT_TYPE_HEADSET: return _("Headset");
255     case PA_DEVICE_PORT_TYPE_HANDSET: return _("Handset");
256     case PA_DEVICE_PORT_TYPE_EARPIECE: return _("Earpiece");
257     case PA_DEVICE_PORT_TYPE_SPDIF: return _("SPDIF");
258     case PA_DEVICE_PORT_TYPE_HDMI: return _("HDMI");
259     case PA_DEVICE_PORT_TYPE_TV: return _("TV");
260     case PA_DEVICE_PORT_TYPE_RADIO: return _("Radio");
261     case PA_DEVICE_PORT_TYPE_VIDEO: return _("Video");
262     case PA_DEVICE_PORT_TYPE_USB: return _("USB");
263     case PA_DEVICE_PORT_TYPE_BLUETOOTH: return _("Bluetooth");
264     case PA_DEVICE_PORT_TYPE_PORTABLE: return _("Portable");
265     case PA_DEVICE_PORT_TYPE_HANDSFREE: return _("Handsfree");
266     case PA_DEVICE_PORT_TYPE_CAR: return _("Car");
267     case PA_DEVICE_PORT_TYPE_HIFI: return _("HiFi");
268     case PA_DEVICE_PORT_TYPE_PHONE: return _("Phone");
269     case PA_DEVICE_PORT_TYPE_NETWORK: return _("Network");
270     case PA_DEVICE_PORT_TYPE_ANALOG: return _("Analog");
271     }
272     snprintf(buf, sizeof(buf), "%s-%u", _("Unknown"), type);
273     return buf;
274 }
275 
get_sink_info_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)276 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
277 
278     static const char *state_table[] = {
279         [1+PA_SINK_INVALID_STATE] = "n/a",
280         [1+PA_SINK_RUNNING] = "RUNNING",
281         [1+PA_SINK_IDLE] = "IDLE",
282         [1+PA_SINK_SUSPENDED] = "SUSPENDED"
283     };
284 
285     char
286         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
287         cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
288         v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
289         cm[PA_CHANNEL_MAP_SNPRINT_MAX],
290         f[PA_FORMAT_INFO_SNPRINT_MAX];
291     char *pl;
292 
293     if (is_last < 0) {
294         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
295         quit(1);
296         return;
297     }
298 
299     if (is_last) {
300         complete_action();
301         return;
302     }
303 
304     pa_assert(i);
305 
306     if (nl && !short_list_format)
307         printf("\n");
308     nl = true;
309 
310     if (short_list_format) {
311         printf("%u\t%s\t%s\t%s\t%s\n",
312                i->index,
313                i->name,
314                pa_strnull(i->driver),
315                pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
316                state_table[1+i->state]);
317         return;
318     }
319 
320     printf(_("Sink #%u\n"
321              "\tState: %s\n"
322              "\tName: %s\n"
323              "\tDescription: %s\n"
324              "\tDriver: %s\n"
325              "\tSample Specification: %s\n"
326              "\tChannel Map: %s\n"
327              "\tOwner Module: %u\n"
328              "\tMute: %s\n"
329              "\tVolume: %s\n"
330              "\t        balance %0.2f\n"
331              "\tBase Volume: %s\n"
332              "\tMonitor Source: %s\n"
333              "\tLatency: %0.0f usec, configured %0.0f usec\n"
334              "\tFlags: %s%s%s%s%s%s%s\n"
335              "\tProperties:\n\t\t%s\n"),
336            i->index,
337            state_table[1+i->state],
338            i->name,
339            pa_strnull(i->description),
340            pa_strnull(i->driver),
341            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
342            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
343            i->owner_module,
344            pa_yes_no_localised(i->mute),
345            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME),
346            pa_cvolume_get_balance(&i->volume, &i->channel_map),
347            pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME),
348            pa_strnull(i->monitor_source_name),
349            (double) i->latency, (double) i->configured_latency,
350            i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
351            i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
352            i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
353            i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
354            i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
355            i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
356            i->flags & PA_SINK_SET_FORMATS ? "SET_FORMATS " : "",
357            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
358 
359     pa_xfree(pl);
360 
361     if (i->ports) {
362         pa_sink_port_info **p;
363 
364         printf(_("\tPorts:\n"));
365         for (p = i->ports; *p; p++)
366             printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
367                     (*p)->name, (*p)->description, get_device_port_type((*p)->type),
368                     (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
369                     (*p)->availability_group ?: "", get_available_str((*p)->available));
370     }
371 
372     if (i->active_port)
373         printf(_("\tActive Port: %s\n"),
374                i->active_port->name);
375 
376     if (i->formats) {
377         uint8_t j;
378 
379         printf(_("\tFormats:\n"));
380         for (j = 0; j < i->n_formats; j++)
381             printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
382     }
383 }
384 
get_source_info_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)385 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
386 
387     static const char *state_table[] = {
388         [1+PA_SOURCE_INVALID_STATE] = "n/a",
389         [1+PA_SOURCE_RUNNING] = "RUNNING",
390         [1+PA_SOURCE_IDLE] = "IDLE",
391         [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
392     };
393 
394     char
395         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
396         cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
397         v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
398         cm[PA_CHANNEL_MAP_SNPRINT_MAX],
399         f[PA_FORMAT_INFO_SNPRINT_MAX];
400     char *pl;
401 
402     if (is_last < 0) {
403         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
404         quit(1);
405         return;
406     }
407 
408     if (is_last) {
409         complete_action();
410         return;
411     }
412 
413     pa_assert(i);
414 
415     if (nl && !short_list_format)
416         printf("\n");
417     nl = true;
418 
419     if (short_list_format) {
420         printf("%u\t%s\t%s\t%s\t%s\n",
421                i->index,
422                i->name,
423                pa_strnull(i->driver),
424                pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
425                state_table[1+i->state]);
426         return;
427     }
428 
429     printf(_("Source #%u\n"
430              "\tState: %s\n"
431              "\tName: %s\n"
432              "\tDescription: %s\n"
433              "\tDriver: %s\n"
434              "\tSample Specification: %s\n"
435              "\tChannel Map: %s\n"
436              "\tOwner Module: %u\n"
437              "\tMute: %s\n"
438              "\tVolume: %s\n"
439              "\t        balance %0.2f\n"
440              "\tBase Volume: %s\n"
441              "\tMonitor of Sink: %s\n"
442              "\tLatency: %0.0f usec, configured %0.0f usec\n"
443              "\tFlags: %s%s%s%s%s%s\n"
444              "\tProperties:\n\t\t%s\n"),
445            i->index,
446            state_table[1+i->state],
447            i->name,
448            pa_strnull(i->description),
449            pa_strnull(i->driver),
450            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
451            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
452            i->owner_module,
453            pa_yes_no_localised(i->mute),
454            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SOURCE_DECIBEL_VOLUME),
455            pa_cvolume_get_balance(&i->volume, &i->channel_map),
456            pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SOURCE_DECIBEL_VOLUME),
457            i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
458            (double) i->latency, (double) i->configured_latency,
459            i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
460            i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
461            i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
462            i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
463            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
464            i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
465            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
466 
467     pa_xfree(pl);
468 
469     if (i->ports) {
470         pa_source_port_info **p;
471 
472         printf(_("\tPorts:\n"));
473         for (p = i->ports; *p; p++)
474             printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
475                     (*p)->name, (*p)->description, get_device_port_type((*p)->type),
476                     (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
477                     (*p)->availability_group ?: "", get_available_str((*p)->available));
478     }
479 
480     if (i->active_port)
481         printf(_("\tActive Port: %s\n"),
482                i->active_port->name);
483 
484     if (i->formats) {
485         uint8_t j;
486 
487         printf(_("\tFormats:\n"));
488         for (j = 0; j < i->n_formats; j++)
489             printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
490     }
491 }
492 
get_module_info_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)493 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
494     char t[32];
495     char *pl;
496 
497     if (is_last < 0) {
498         pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
499         quit(1);
500         return;
501     }
502 
503     if (is_last) {
504         complete_action();
505         return;
506     }
507 
508     pa_assert(i);
509 
510     if (nl && !short_list_format)
511         printf("\n");
512     nl = true;
513 
514     pa_snprintf(t, sizeof(t), "%u", i->n_used);
515 
516     if (short_list_format) {
517         printf("%u\t%s\t%s\t\n", i->index, i->name, i->argument ? i->argument : "");
518         return;
519     }
520 
521     printf(_("Module #%u\n"
522              "\tName: %s\n"
523              "\tArgument: %s\n"
524              "\tUsage counter: %s\n"
525              "\tProperties:\n\t\t%s\n"),
526            i->index,
527            i->name,
528            i->argument ? i->argument : "",
529            i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
530            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
531 
532     pa_xfree(pl);
533 }
534 
get_client_info_callback(pa_context * c,const pa_client_info * i,int is_last,void * userdata)535 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
536     char t[32];
537     char *pl;
538 
539     if (is_last < 0) {
540         pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
541         quit(1);
542         return;
543     }
544 
545     if (is_last) {
546         complete_action();
547         return;
548     }
549 
550     pa_assert(i);
551 
552     if (nl && !short_list_format)
553         printf("\n");
554     nl = true;
555 
556     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
557 
558     if (short_list_format) {
559         printf("%u\t%s\t%s\n",
560                i->index,
561                pa_strnull(i->driver),
562                pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
563         return;
564     }
565 
566     printf(_("Client #%u\n"
567              "\tDriver: %s\n"
568              "\tOwner Module: %s\n"
569              "\tProperties:\n\t\t%s\n"),
570            i->index,
571            pa_strnull(i->driver),
572            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
573            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
574 
575     pa_xfree(pl);
576 }
577 
get_card_info_callback(pa_context * c,const pa_card_info * i,int is_last,void * userdata)578 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
579     char t[32];
580     char *pl;
581 
582     if (is_last < 0) {
583         pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
584         complete_action();
585         return;
586     }
587 
588     if (is_last) {
589         complete_action();
590         return;
591     }
592 
593     pa_assert(i);
594 
595     if (nl && !short_list_format)
596         printf("\n");
597     nl = true;
598 
599     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
600 
601     if (short_list_format) {
602         printf("%u\t%s\t%s\n", i->index, i->name, pa_strnull(i->driver));
603         return;
604     }
605 
606     printf(_("Card #%u\n"
607              "\tName: %s\n"
608              "\tDriver: %s\n"
609              "\tOwner Module: %s\n"
610              "\tProperties:\n\t\t%s\n"),
611            i->index,
612            i->name,
613            pa_strnull(i->driver),
614            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
615            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
616 
617     pa_xfree(pl);
618 
619     if (i->n_profiles > 0) {
620         pa_card_profile_info2 **p;
621 
622         printf(_("\tProfiles:\n"));
623         for (p = i->profiles2; *p; p++)
624             printf(_("\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"), (*p)->name,
625                 (*p)->description, (*p)->n_sinks, (*p)->n_sources, (*p)->priority, pa_yes_no_localised((*p)->available));
626     }
627 
628     if (i->active_profile)
629         printf(_("\tActive Profile: %s\n"),
630                i->active_profile->name);
631 
632     if (i->ports) {
633         pa_card_port_info **p;
634 
635         printf(_("\tPorts:\n"));
636         for (p = i->ports; *p; p++) {
637             pa_card_profile_info **pr = (*p)->profiles;
638             printf(_("\t\t%s: %s (type: %s, priority: %u, latency offset: %" PRId64 " usec%s%s, %s)\n"), (*p)->name,
639                 (*p)->description, get_device_port_type((*p)->type), (*p)->priority, (*p)->latency_offset,
640                 (*p)->availability_group ? _(", availability group: ") : "", (*p)->availability_group ?: "",
641                 get_available_str((*p)->available));
642 
643             if (!pa_proplist_isempty((*p)->proplist)) {
644                 printf(_("\t\t\tProperties:\n\t\t\t\t%s\n"), pl = pa_proplist_to_string_sep((*p)->proplist, "\n\t\t\t\t"));
645                 pa_xfree(pl);
646             }
647 
648             if (pr) {
649                 printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
650                 pr++;
651                 while (*pr) {
652                     printf(", %s", pa_strnull((*pr)->name));
653                     pr++;
654                 }
655                 printf("\n");
656             }
657         }
658     }
659 }
660 
get_sink_input_info_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)661 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
662     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
663     char *pl;
664 
665     if (is_last < 0) {
666         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
667         quit(1);
668         return;
669     }
670 
671     if (is_last) {
672         complete_action();
673         return;
674     }
675 
676     pa_assert(i);
677 
678     if (nl && !short_list_format)
679         printf("\n");
680     nl = true;
681 
682     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
683     pa_snprintf(k, sizeof(k), "%u", i->client);
684 
685     if (short_list_format) {
686         printf("%u\t%u\t%s\t%s\t%s\n",
687                i->index,
688                i->sink,
689                i->client != PA_INVALID_INDEX ? k : "-",
690                pa_strnull(i->driver),
691                pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
692         return;
693     }
694 
695     printf(_("Sink Input #%u\n"
696              "\tDriver: %s\n"
697              "\tOwner Module: %s\n"
698              "\tClient: %s\n"
699              "\tSink: %u\n"
700              "\tSample Specification: %s\n"
701              "\tChannel Map: %s\n"
702              "\tFormat: %s\n"
703              "\tCorked: %s\n"
704              "\tMute: %s\n"
705              "\tVolume: %s\n"
706              "\t        balance %0.2f\n"
707              "\tBuffer Latency: %0.0f usec\n"
708              "\tSink Latency: %0.0f usec\n"
709              "\tResample method: %s\n"
710              "\tProperties:\n\t\t%s\n"),
711            i->index,
712            pa_strnull(i->driver),
713            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
714            i->client != PA_INVALID_INDEX ? k : _("n/a"),
715            i->sink,
716            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
717            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
718            pa_format_info_snprint(f, sizeof(f), i->format),
719            pa_yes_no_localised(i->corked),
720            pa_yes_no_localised(i->mute),
721            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
722            pa_cvolume_get_balance(&i->volume, &i->channel_map),
723            (double) i->buffer_usec,
724            (double) i->sink_usec,
725            i->resample_method ? i->resample_method : _("n/a"),
726            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
727 
728     pa_xfree(pl);
729 }
730 
get_source_output_info_callback(pa_context * c,const pa_source_output_info * i,int is_last,void * userdata)731 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
732     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
733     char *pl;
734 
735     if (is_last < 0) {
736         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
737         quit(1);
738         return;
739     }
740 
741     if (is_last) {
742         complete_action();
743         return;
744     }
745 
746     pa_assert(i);
747 
748     if (nl && !short_list_format)
749         printf("\n");
750     nl = true;
751 
752     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
753     pa_snprintf(k, sizeof(k), "%u", i->client);
754 
755     if (short_list_format) {
756         printf("%u\t%u\t%s\t%s\t%s\n",
757                i->index,
758                i->source,
759                i->client != PA_INVALID_INDEX ? k : "-",
760                pa_strnull(i->driver),
761                pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
762         return;
763     }
764 
765     printf(_("Source Output #%u\n"
766              "\tDriver: %s\n"
767              "\tOwner Module: %s\n"
768              "\tClient: %s\n"
769              "\tSource: %u\n"
770              "\tSample Specification: %s\n"
771              "\tChannel Map: %s\n"
772              "\tFormat: %s\n"
773              "\tCorked: %s\n"
774              "\tMute: %s\n"
775              "\tVolume: %s\n"
776              "\t        balance %0.2f\n"
777              "\tBuffer Latency: %0.0f usec\n"
778              "\tSource Latency: %0.0f usec\n"
779              "\tResample method: %s\n"
780              "\tProperties:\n\t\t%s\n"),
781            i->index,
782            pa_strnull(i->driver),
783            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
784            i->client != PA_INVALID_INDEX ? k : _("n/a"),
785            i->source,
786            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
787            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
788            pa_format_info_snprint(f, sizeof(f), i->format),
789            pa_yes_no_localised(i->corked),
790            pa_yes_no_localised(i->mute),
791            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
792            pa_cvolume_get_balance(&i->volume, &i->channel_map),
793            (double) i->buffer_usec,
794            (double) i->source_usec,
795            i->resample_method ? i->resample_method : _("n/a"),
796            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
797 
798     pa_xfree(pl);
799 }
800 
get_sample_info_callback(pa_context * c,const pa_sample_info * i,int is_last,void * userdata)801 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
802     char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
803     char *pl;
804 
805     if (is_last < 0) {
806         pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
807         quit(1);
808         return;
809     }
810 
811     if (is_last) {
812         complete_action();
813         return;
814     }
815 
816     pa_assert(i);
817 
818     if (nl && !short_list_format)
819         printf("\n");
820     nl = true;
821 
822     pa_bytes_snprint(t, sizeof(t), i->bytes);
823 
824     if (short_list_format) {
825         printf("%u\t%s\t%s\t%0.3f\n",
826                i->index,
827                i->name,
828                pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "-",
829                (double) i->duration/1000000.0);
830         return;
831     }
832 
833     printf(_("Sample #%u\n"
834              "\tName: %s\n"
835              "\tSample Specification: %s\n"
836              "\tChannel Map: %s\n"
837              "\tVolume: %s\n"
838              "\t        balance %0.2f\n"
839              "\tDuration: %0.1fs\n"
840              "\tSize: %s\n"
841              "\tLazy: %s\n"
842              "\tFilename: %s\n"
843              "\tProperties:\n\t\t%s\n"),
844            i->index,
845            i->name,
846            pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
847            pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
848            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
849            pa_cvolume_get_balance(&i->volume, &i->channel_map),
850            (double) i->duration/1000000.0,
851            t,
852            pa_yes_no_localised(i->lazy),
853            i->filename ? i->filename : _("n/a"),
854            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
855 
856     pa_xfree(pl);
857 }
858 
simple_callback(pa_context * c,int success,void * userdata)859 static void simple_callback(pa_context *c, int success, void *userdata) {
860     if (!success) {
861         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
862         quit(1);
863         return;
864     }
865 
866     complete_action();
867 }
868 
index_callback(pa_context * c,uint32_t idx,void * userdata)869 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
870     if (idx == PA_INVALID_INDEX) {
871         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
872         quit(1);
873         return;
874     }
875 
876     printf("%u\n", idx);
877 
878     complete_action();
879 }
880 
volume_relative_adjust(pa_cvolume * cv)881 static void volume_relative_adjust(pa_cvolume *cv) {
882     pa_assert(volume_flags & VOL_RELATIVE);
883 
884     /* Relative volume change is additive in case of UINT or PERCENT
885      * and multiplicative for LINEAR or DECIBEL */
886     if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
887         unsigned i;
888         for (i = 0; i < cv->channels; i++) {
889             if (cv->values[i] + volume.values[i] < PA_VOLUME_NORM)
890                 cv->values[i] = PA_VOLUME_MUTED;
891             else
892                 cv->values[i] = cv->values[i] + volume.values[i] - PA_VOLUME_NORM;
893         }
894     }
895     if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL)
896         pa_sw_cvolume_multiply(cv, cv, &volume);
897 }
898 
unload_module_by_name_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)899 static void unload_module_by_name_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
900     static bool unloaded = false;
901 
902     if (is_last < 0) {
903         pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
904         quit(1);
905         return;
906     }
907 
908     if (is_last) {
909         if (unloaded == false)
910             pa_log(_("Failed to unload module: Module %s not loaded"), module_name);
911         complete_action();
912         return;
913     }
914 
915     pa_assert(i);
916 
917     if (pa_streq(module_name, i->name)) {
918         unloaded = true;
919         actions++;
920         pa_operation_unref(pa_context_unload_module(c, i->index, simple_callback, NULL));
921     }
922 }
923 
fill_volume(pa_cvolume * cv,unsigned supported)924 static void fill_volume(pa_cvolume *cv, unsigned supported) {
925     if (volume.channels == 1) {
926         pa_cvolume_set(&volume, supported, volume.values[0]);
927     } else if (volume.channels != supported) {
928         pa_log(ngettext("Failed to set volume: You tried to set volumes for %d channel, whereas channel(s) supported = %d\n",
929                         "Failed to set volume: You tried to set volumes for %d channels, whereas channel(s) supported = %d\n",
930                         volume.channels),
931                volume.channels, supported);
932         quit(1);
933         return;
934     }
935 
936     if (volume_flags & VOL_RELATIVE)
937         volume_relative_adjust(cv);
938     else
939         *cv = volume;
940 }
941 
get_sink_volume_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)942 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
943     pa_cvolume cv;
944 
945     if (is_last < 0) {
946         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
947         quit(1);
948         return;
949     }
950 
951     if (is_last)
952         return;
953 
954     pa_assert(i);
955 
956     cv = i->volume;
957     fill_volume(&cv, i->channel_map.channels);
958 
959     pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
960 }
961 
get_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)962 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
963     pa_cvolume cv;
964 
965     if (is_last < 0) {
966         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
967         quit(1);
968         return;
969     }
970 
971     if (is_last)
972         return;
973 
974     pa_assert(i);
975 
976     cv = i->volume;
977     fill_volume(&cv, i->channel_map.channels);
978 
979     pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
980 }
981 
get_sink_input_volume_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)982 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
983     pa_cvolume cv;
984 
985     if (is_last < 0) {
986         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
987         quit(1);
988         return;
989     }
990 
991     if (is_last)
992         return;
993 
994     pa_assert(i);
995 
996     cv = i->volume;
997     fill_volume(&cv, i->channel_map.channels);
998 
999     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
1000 }
1001 
get_source_output_volume_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)1002 static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
1003     pa_cvolume cv;
1004 
1005     if (is_last < 0) {
1006         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1007         quit(1);
1008         return;
1009     }
1010 
1011     if (is_last)
1012         return;
1013 
1014     pa_assert(o);
1015 
1016     cv = o->volume;
1017     fill_volume(&cv, o->channel_map.channels);
1018 
1019     pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
1020 }
1021 
sink_toggle_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1022 static void sink_toggle_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1023     if (is_last < 0) {
1024         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1025         quit(1);
1026         return;
1027     }
1028 
1029     if (is_last)
1030         return;
1031 
1032     pa_assert(i);
1033 
1034     pa_operation_unref(pa_context_set_sink_mute_by_name(c, i->name, !i->mute, simple_callback, NULL));
1035 }
1036 
source_toggle_mute_callback(pa_context * c,const pa_source_info * o,int is_last,void * userdata)1037 static void source_toggle_mute_callback(pa_context *c, const pa_source_info *o, int is_last, void *userdata) {
1038     if (is_last < 0) {
1039         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1040         quit(1);
1041         return;
1042     }
1043 
1044     if (is_last)
1045         return;
1046 
1047     pa_assert(o);
1048 
1049     pa_operation_unref(pa_context_set_source_mute_by_name(c, o->name, !o->mute, simple_callback, NULL));
1050 }
1051 
sink_input_toggle_mute_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1052 static void sink_input_toggle_mute_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1053     if (is_last < 0) {
1054         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1055         quit(1);
1056         return;
1057     }
1058 
1059     if (is_last)
1060         return;
1061 
1062     pa_assert(i);
1063 
1064     pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, !i->mute, simple_callback, NULL));
1065 }
1066 
source_output_toggle_mute_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)1067 static void source_output_toggle_mute_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
1068     if (is_last < 0) {
1069         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1070         quit(1);
1071         return;
1072     }
1073 
1074     if (is_last)
1075         return;
1076 
1077     pa_assert(o);
1078 
1079     pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
1080 }
1081 
1082 /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
1083 #define MAX_FORMATS 256
1084 
set_sink_formats(pa_context * c,uint32_t sink,const char * str)1085 static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
1086     pa_format_info *f_arr[MAX_FORMATS] = { 0, };
1087     char *format = NULL;
1088     const char *state = NULL;
1089     int i = 0;
1090     pa_operation *o = NULL;
1091 
1092     while ((format = pa_split(str, ";", &state))) {
1093         pa_format_info *f = pa_format_info_from_string(pa_strip(format));
1094 
1095         if (!f) {
1096             pa_log(_("Failed to set format: invalid format string %s"), format);
1097             goto error;
1098         }
1099 
1100         f_arr[i++] = f;
1101         pa_xfree(format);
1102     }
1103 
1104     o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
1105     if (o) {
1106         pa_operation_unref(o);
1107         actions++;
1108     }
1109 
1110 done:
1111     if (format)
1112         pa_xfree(format);
1113     while (f_arr[i] && i--)
1114         pa_format_info_free(f_arr[i]);
1115 
1116     return;
1117 
1118 error:
1119     while (f_arr[i] && i--)
1120         pa_format_info_free(f_arr[i]);
1121     quit(1);
1122     goto done;
1123 }
1124 
stream_state_callback(pa_stream * s,void * userdata)1125 static void stream_state_callback(pa_stream *s, void *userdata) {
1126     pa_assert(s);
1127 
1128     switch (pa_stream_get_state(s)) {
1129         case PA_STREAM_CREATING:
1130         case PA_STREAM_READY:
1131             break;
1132 
1133         case PA_STREAM_TERMINATED:
1134             drain();
1135             break;
1136 
1137         case PA_STREAM_FAILED:
1138         default:
1139             pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
1140             quit(1);
1141     }
1142 }
1143 
stream_write_callback(pa_stream * s,size_t length,void * userdata)1144 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
1145     sf_count_t l;
1146     float *d;
1147     pa_assert(s && length && sndfile);
1148 
1149     d = pa_xmalloc(length);
1150 
1151     pa_assert(sample_length >= length);
1152     l = (sf_count_t) (length/pa_frame_size(&sample_spec));
1153 
1154     if ((sf_readf_float(sndfile, d, l)) != l) {
1155         pa_xfree(d);
1156         pa_log(_("Premature end of file"));
1157         quit(1);
1158         return;
1159     }
1160 
1161     pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
1162 
1163     sample_length -= length;
1164 
1165     if (sample_length <= 0) {
1166         pa_stream_set_write_callback(sample_stream, NULL, NULL);
1167         pa_stream_finish_upload(sample_stream);
1168     }
1169 }
1170 
subscription_event_type_to_string(pa_subscription_event_type_t t)1171 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
1172 
1173     switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
1174 
1175     case PA_SUBSCRIPTION_EVENT_NEW:
1176         return _("new");
1177 
1178     case PA_SUBSCRIPTION_EVENT_CHANGE:
1179         return _("change");
1180 
1181     case PA_SUBSCRIPTION_EVENT_REMOVE:
1182         return _("remove");
1183     }
1184 
1185     return _("unknown");
1186 }
1187 
subscription_event_facility_to_string(pa_subscription_event_type_t t)1188 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
1189 
1190     switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
1191 
1192     case PA_SUBSCRIPTION_EVENT_SINK:
1193         return _("sink");
1194 
1195     case PA_SUBSCRIPTION_EVENT_SOURCE:
1196         return _("source");
1197 
1198     case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
1199         return _("sink-input");
1200 
1201     case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
1202         return _("source-output");
1203 
1204     case PA_SUBSCRIPTION_EVENT_MODULE:
1205         return _("module");
1206 
1207     case PA_SUBSCRIPTION_EVENT_CLIENT:
1208         return _("client");
1209 
1210     case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
1211         return _("sample-cache");
1212 
1213     case PA_SUBSCRIPTION_EVENT_SERVER:
1214         return _("server");
1215 
1216     case PA_SUBSCRIPTION_EVENT_CARD:
1217         return _("card");
1218     }
1219 
1220     return _("unknown");
1221 }
1222 
context_subscribe_callback(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)1223 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
1224     pa_assert(c);
1225 
1226     printf(_("Event '%s' on %s #%u\n"),
1227            subscription_event_type_to_string(t),
1228            subscription_event_facility_to_string(t),
1229            idx);
1230     fflush(stdout);
1231 }
1232 
context_state_callback(pa_context * c,void * userdata)1233 static void context_state_callback(pa_context *c, void *userdata) {
1234     pa_operation *o = NULL;
1235 
1236     pa_assert(c);
1237 
1238     switch (pa_context_get_state(c)) {
1239         case PA_CONTEXT_CONNECTING:
1240         case PA_CONTEXT_AUTHORIZING:
1241         case PA_CONTEXT_SETTING_NAME:
1242             break;
1243 
1244         case PA_CONTEXT_READY:
1245             switch (action) {
1246                 case STAT:
1247                     o = pa_context_stat(c, stat_callback, NULL);
1248                     break;
1249 
1250                 case INFO:
1251                     o = pa_context_get_server_info(c, get_server_info_callback, NULL);
1252                     break;
1253 
1254                 case PLAY_SAMPLE:
1255                     o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
1256                     break;
1257 
1258                 case REMOVE_SAMPLE:
1259                     o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
1260                     break;
1261 
1262                 case UPLOAD_SAMPLE:
1263                     sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
1264                     pa_assert(sample_stream);
1265 
1266                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
1267                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
1268                     pa_stream_connect_upload(sample_stream, sample_length);
1269                     actions++;
1270                     break;
1271 
1272                 case EXIT:
1273                     o = pa_context_exit_daemon(c, simple_callback, NULL);
1274                     break;
1275 
1276                 case LIST:
1277                     if (list_type) {
1278                         if (pa_streq(list_type, "modules"))
1279                             o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
1280                         else if (pa_streq(list_type, "sinks"))
1281                             o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
1282                         else if (pa_streq(list_type, "sources"))
1283                             o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
1284                         else if (pa_streq(list_type, "sink-inputs"))
1285                             o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
1286                         else if (pa_streq(list_type, "source-outputs"))
1287                             o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
1288                         else if (pa_streq(list_type, "clients"))
1289                             o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
1290                         else if (pa_streq(list_type, "samples"))
1291                             o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
1292                         else if (pa_streq(list_type, "cards"))
1293                             o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
1294                         else
1295                             pa_assert_not_reached();
1296                     } else {
1297                         o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
1298                         if (o) {
1299                             pa_operation_unref(o);
1300                             actions++;
1301                         }
1302 
1303                         o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
1304                         if (o) {
1305                             pa_operation_unref(o);
1306                             actions++;
1307                         }
1308 
1309                         o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
1310                         if (o) {
1311                             pa_operation_unref(o);
1312                             actions++;
1313                         }
1314                         o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
1315                         if (o) {
1316                             pa_operation_unref(o);
1317                             actions++;
1318                         }
1319 
1320                         o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
1321                         if (o) {
1322                             pa_operation_unref(o);
1323                             actions++;
1324                         }
1325 
1326                         o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
1327                         if (o) {
1328                             pa_operation_unref(o);
1329                             actions++;
1330                         }
1331 
1332                         o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
1333                         if (o) {
1334                             pa_operation_unref(o);
1335                             actions++;
1336                         }
1337 
1338                         o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
1339                         if (o) {
1340                             pa_operation_unref(o);
1341                             actions++;
1342                         }
1343 
1344                         o = NULL;
1345                     }
1346                     break;
1347 
1348                 case MOVE_SINK_INPUT:
1349                     o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
1350                     break;
1351 
1352                 case MOVE_SOURCE_OUTPUT:
1353                     o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
1354                     break;
1355 
1356                 case LOAD_MODULE:
1357                     o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
1358                     break;
1359 
1360                 case UNLOAD_MODULE:
1361                     if (module_name)
1362                         o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
1363                     else
1364                         o = pa_context_unload_module(c, module_index, simple_callback, NULL);
1365                     break;
1366 
1367                 case SUSPEND_SINK:
1368                     if (sink_name)
1369                         o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
1370                     else
1371                         o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
1372                     break;
1373 
1374                 case SUSPEND_SOURCE:
1375                     if (source_name)
1376                         o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
1377                     else
1378                         o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
1379                     break;
1380 
1381                 case SET_CARD_PROFILE:
1382                     o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
1383                     break;
1384 
1385                 case SET_SINK_PORT:
1386                     o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
1387                     break;
1388 
1389                 case SET_DEFAULT_SINK:
1390                     o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
1391                     break;
1392 
1393                 case SET_SOURCE_PORT:
1394                     o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
1395                     break;
1396 
1397                 case SET_DEFAULT_SOURCE:
1398                     o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
1399                     break;
1400 
1401                 case SET_SINK_MUTE:
1402                     if (mute == TOGGLE_MUTE)
1403                         o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
1404                     else
1405                         o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
1406                     break;
1407 
1408                 case SET_SOURCE_MUTE:
1409                     if (mute == TOGGLE_MUTE)
1410                         o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
1411                     else
1412                         o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
1413                     break;
1414 
1415                 case SET_SINK_INPUT_MUTE:
1416                     if (mute == TOGGLE_MUTE)
1417                         o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
1418                     else
1419                         o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
1420                     break;
1421 
1422                 case SET_SOURCE_OUTPUT_MUTE:
1423                     if (mute == TOGGLE_MUTE)
1424                         o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
1425                     else
1426                         o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
1427                     break;
1428 
1429                 case SET_SINK_VOLUME:
1430                     o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
1431                     break;
1432 
1433                 case SET_SOURCE_VOLUME:
1434                     o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
1435                     break;
1436 
1437                 case SET_SINK_INPUT_VOLUME:
1438                     o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
1439                     break;
1440 
1441                 case SET_SOURCE_OUTPUT_VOLUME:
1442                     o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
1443                     break;
1444 
1445                 case SET_SINK_FORMATS:
1446                     set_sink_formats(c, sink_idx, formats);
1447                     break;
1448 
1449                 case SET_PORT_LATENCY_OFFSET:
1450                     o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
1451                     break;
1452 
1453                 case SUBSCRIBE:
1454                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
1455 
1456                     o = pa_context_subscribe(c,
1457                                              PA_SUBSCRIPTION_MASK_SINK|
1458                                              PA_SUBSCRIPTION_MASK_SOURCE|
1459                                              PA_SUBSCRIPTION_MASK_SINK_INPUT|
1460                                              PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1461                                              PA_SUBSCRIPTION_MASK_MODULE|
1462                                              PA_SUBSCRIPTION_MASK_CLIENT|
1463                                              PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
1464                                              PA_SUBSCRIPTION_MASK_SERVER|
1465                                              PA_SUBSCRIPTION_MASK_CARD,
1466                                              NULL,
1467                                              NULL);
1468                     break;
1469 
1470                 default:
1471                     pa_assert_not_reached();
1472             }
1473 
1474             if (o) {
1475                 pa_operation_unref(o);
1476                 actions++;
1477             }
1478 
1479             if (actions == 0) {
1480                 pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
1481                 quit(1);
1482             }
1483 
1484             break;
1485 
1486         case PA_CONTEXT_TERMINATED:
1487             quit(0);
1488             break;
1489 
1490         case PA_CONTEXT_FAILED:
1491         default:
1492             pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
1493             quit(1);
1494     }
1495 }
1496 
exit_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)1497 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
1498     pa_log(_("Got SIGINT, exiting."));
1499     quit(0);
1500 }
1501 
parse_volume(const char * vol_spec,pa_volume_t * vol,enum volume_flags * vol_flags)1502 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
1503     double v;
1504     char *vs;
1505     const char *atod_input;
1506 
1507     pa_assert(vol_spec);
1508     pa_assert(vol);
1509     pa_assert(vol_flags);
1510 
1511     vs = pa_xstrdup(vol_spec);
1512 
1513     *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
1514     if (strchr(vs, '.'))
1515         *vol_flags |= VOL_LINEAR;
1516     if (pa_endswith(vs, "%")) {
1517         *vol_flags |= VOL_PERCENT;
1518         vs[strlen(vs)-1] = 0;
1519     }
1520     if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
1521         *vol_flags |= VOL_DECIBEL;
1522         vs[strlen(vs)-2] = 0;
1523     }
1524 
1525     atod_input = vs;
1526 
1527     if (atod_input[0] == '+')
1528         atod_input++; /* pa_atod() doesn't accept leading '+', so skip it. */
1529 
1530     if (pa_atod(atod_input, &v) < 0) {
1531         pa_log(_("Invalid volume specification"));
1532         pa_xfree(vs);
1533         return -1;
1534     }
1535 
1536     pa_xfree(vs);
1537 
1538     if (*vol_flags & VOL_RELATIVE) {
1539         if ((*vol_flags & 0x0F) == VOL_UINT)
1540             v += (double) PA_VOLUME_NORM;
1541         if ((*vol_flags & 0x0F) == VOL_PERCENT)
1542             v += 100.0;
1543         if ((*vol_flags & 0x0F) == VOL_LINEAR)
1544             v += 1.0;
1545     }
1546     if ((*vol_flags & 0x0F) == VOL_PERCENT)
1547         v = v * (double) PA_VOLUME_NORM / 100;
1548     if ((*vol_flags & 0x0F) == VOL_LINEAR)
1549         v = pa_sw_volume_from_linear(v);
1550     if ((*vol_flags & 0x0F) == VOL_DECIBEL)
1551         v = pa_sw_volume_from_dB(v);
1552 
1553     if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
1554         pa_log(_("Volume outside permissible range.\n"));
1555         return -1;
1556     }
1557 
1558     *vol = (pa_volume_t) v;
1559 
1560     return 0;
1561 }
1562 
parse_volumes(char * args[],unsigned n)1563 static int parse_volumes(char *args[], unsigned n) {
1564     unsigned i;
1565 
1566     if (n >= PA_CHANNELS_MAX) {
1567         pa_log(_("Invalid number of volume specifications.\n"));
1568         return -1;
1569     }
1570 
1571     volume.channels = n;
1572     for (i = 0; i < volume.channels; i++) {
1573         enum volume_flags flags;
1574 
1575         if (parse_volume(args[i], &volume.values[i], &flags) < 0)
1576             return -1;
1577 
1578         if (i > 0 && flags != volume_flags) {
1579             pa_log(_("Inconsistent volume specification.\n"));
1580             return -1;
1581         } else
1582             volume_flags = flags;
1583     }
1584 
1585     return 0;
1586 }
1587 
parse_mute(const char * mute_text)1588 static enum mute_flags parse_mute(const char *mute_text) {
1589     int b;
1590 
1591     pa_assert(mute_text);
1592 
1593     if (pa_streq("toggle", mute_text))
1594         return TOGGLE_MUTE;
1595 
1596     b = pa_parse_boolean(mute_text);
1597     switch (b) {
1598         case 0:
1599             return UNMUTE;
1600         case 1:
1601             return MUTE;
1602         default:
1603             return INVALID_MUTE;
1604     }
1605 }
1606 
help(const char * argv0)1607 static void help(const char *argv0) {
1608 
1609     printf("%s %s %s\n",    argv0, _("[options]"), "stat");
1610     printf("%s %s %s\n",    argv0, _("[options]"), "info");
1611     printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
1612     printf("%s %s %s\n",    argv0, _("[options]"), "exit");
1613     printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
1614     printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
1615     printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
1616     printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
1617     printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("NAME|#N"));
1618     printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
1619     printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
1620     printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
1621     printf("%s %s %s %s\n", argv0, _("[options]"), "set-default-(sink|source)", _("NAME"));
1622     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
1623     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME [VOLUME ...]"));
1624     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME [VOLUME ...]"));
1625     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0|toggle"));
1626     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
1627     printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
1628     printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
1629     printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
1630     printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
1631              "can be used to specify the default sink, source and monitor.\n"));
1632 
1633     printf(_("\n"
1634              "  -h, --help                            Show this help\n"
1635              "      --version                         Show version\n\n"
1636              "  -s, --server=SERVER                   The name of the server to connect to\n"
1637              "  -n, --client-name=NAME                How to call this client on the server\n"));
1638 }
1639 
1640 enum {
1641     ARG_VERSION = 256
1642 };
1643 
main(int argc,char * argv[])1644 int main(int argc, char *argv[]) {
1645     pa_mainloop *m = NULL;
1646     int ret = 1, c;
1647     char *server = NULL, *bn;
1648 
1649     static const struct option long_options[] = {
1650         {"server",      1, NULL, 's'},
1651         {"client-name", 1, NULL, 'n'},
1652         {"version",     0, NULL, ARG_VERSION},
1653         {"help",        0, NULL, 'h'},
1654         {NULL,          0, NULL, 0}
1655     };
1656 
1657     setlocale(LC_ALL, "");
1658 #ifdef ENABLE_NLS
1659     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
1660 #endif
1661 
1662     bn = pa_path_get_filename(argv[0]);
1663 
1664     proplist = pa_proplist_new();
1665 
1666     while ((c = getopt_long(argc, argv, "+s:n:h", long_options, NULL)) != -1) {
1667         switch (c) {
1668             case 'h' :
1669                 help(bn);
1670                 ret = 0;
1671                 goto quit;
1672 
1673             case ARG_VERSION:
1674                 printf(_("pactl %s\n"
1675                          "Compiled with libpulse %s\n"
1676                          "Linked with libpulse %s\n"),
1677                        PACKAGE_VERSION,
1678                        pa_get_headers_version(),
1679                        pa_get_library_version());
1680                 ret = 0;
1681                 goto quit;
1682 
1683             case 's':
1684                 pa_xfree(server);
1685                 server = pa_xstrdup(optarg);
1686                 break;
1687 
1688             case 'n': {
1689                 char *t;
1690 
1691                 if (!(t = pa_locale_to_utf8(optarg)) ||
1692                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
1693 
1694                     pa_log(_("Invalid client name '%s'"), t ? t : optarg);
1695                     pa_xfree(t);
1696                     goto quit;
1697                 }
1698 
1699                 pa_xfree(t);
1700                 break;
1701             }
1702 
1703             default:
1704                 goto quit;
1705         }
1706     }
1707 
1708     if (optind < argc) {
1709         if (pa_streq(argv[optind], "stat")) {
1710             action = STAT;
1711 
1712         } else if (pa_streq(argv[optind], "info"))
1713             action = INFO;
1714 
1715         else if (pa_streq(argv[optind], "exit"))
1716             action = EXIT;
1717 
1718         else if (pa_streq(argv[optind], "list")) {
1719             action = LIST;
1720 
1721             for (int i = optind+1; i < argc; i++) {
1722                 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
1723                     pa_streq(argv[i], "sinks")   || pa_streq(argv[i], "sink-inputs") ||
1724                     pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
1725                     pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
1726                     list_type = pa_xstrdup(argv[i]);
1727                 } else if (pa_streq(argv[i], "short")) {
1728                     short_list_format = true;
1729                 } else {
1730                     pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
1731                     goto quit;
1732                 }
1733             }
1734 
1735         } else if (pa_streq(argv[optind], "upload-sample")) {
1736             struct SF_INFO sfi;
1737             action = UPLOAD_SAMPLE;
1738 
1739             if (optind+1 >= argc) {
1740                 pa_log(_("Please specify a sample file to load"));
1741                 goto quit;
1742             }
1743 
1744             if (optind+2 < argc)
1745                 sample_name = pa_xstrdup(argv[optind+2]);
1746             else {
1747                 char *f = pa_path_get_filename(argv[optind+1]);
1748                 sample_name = pa_xstrndup(f, strcspn(f, "."));
1749             }
1750 
1751             pa_zero(sfi);
1752             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
1753                 pa_log(_("Failed to open sound file."));
1754                 goto quit;
1755             }
1756 
1757             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1758                 pa_log(_("Failed to determine sample specification from file."));
1759                 goto quit;
1760             }
1761             sample_spec.format = PA_SAMPLE_FLOAT32;
1762 
1763             if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1764                 if (sample_spec.channels > 2)
1765                     pa_log(_("Warning: Failed to determine sample specification from file."));
1766                 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1767             }
1768 
1769             pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1770             sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1771 
1772         } else if (pa_streq(argv[optind], "play-sample")) {
1773             action = PLAY_SAMPLE;
1774             if (argc != optind+2 && argc != optind+3) {
1775                 pa_log(_("You have to specify a sample name to play"));
1776                 goto quit;
1777             }
1778 
1779             sample_name = pa_xstrdup(argv[optind+1]);
1780 
1781             if (optind+2 < argc)
1782                 sink_name = pa_xstrdup(argv[optind+2]);
1783 
1784         } else if (pa_streq(argv[optind], "remove-sample")) {
1785             action = REMOVE_SAMPLE;
1786             if (argc != optind+2) {
1787                 pa_log(_("You have to specify a sample name to remove"));
1788                 goto quit;
1789             }
1790 
1791             sample_name = pa_xstrdup(argv[optind+1]);
1792 
1793         } else if (pa_streq(argv[optind], "move-sink-input")) {
1794             action = MOVE_SINK_INPUT;
1795             if (argc != optind+3) {
1796                 pa_log(_("You have to specify a sink input index and a sink"));
1797                 goto quit;
1798             }
1799 
1800             sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1801             sink_name = pa_xstrdup(argv[optind+2]);
1802 
1803         } else if (pa_streq(argv[optind], "move-source-output")) {
1804             action = MOVE_SOURCE_OUTPUT;
1805             if (argc != optind+3) {
1806                 pa_log(_("You have to specify a source output index and a source"));
1807                 goto quit;
1808             }
1809 
1810             source_output_idx = (uint32_t) atoi(argv[optind+1]);
1811             source_name = pa_xstrdup(argv[optind+2]);
1812 
1813         } else if (pa_streq(argv[optind], "load-module")) {
1814             int i;
1815             size_t n = 0;
1816             char *p;
1817 
1818             action = LOAD_MODULE;
1819 
1820             if (argc <= optind+1) {
1821                 pa_log(_("You have to specify a module name and arguments."));
1822                 goto quit;
1823             }
1824 
1825             module_name = argv[optind+1];
1826 
1827             for (i = optind+2; i < argc; i++)
1828                 n += strlen(argv[i])+1;
1829 
1830             if (n > 0) {
1831                 p = module_args = pa_xmalloc(n);
1832 
1833                 for (i = optind+2; i < argc; i++)
1834                     p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1835             }
1836 
1837         } else if (pa_streq(argv[optind], "unload-module")) {
1838             action = UNLOAD_MODULE;
1839 
1840             if (argc != optind+2) {
1841                 pa_log(_("You have to specify a module index or name"));
1842                 goto quit;
1843             }
1844 
1845             if (pa_atou(argv[optind + 1], &module_index) < 0)
1846                 module_name = argv[optind + 1];
1847 
1848         } else if (pa_streq(argv[optind], "suspend-sink")) {
1849             int b;
1850 
1851             action = SUSPEND_SINK;
1852 
1853             if (argc > optind+3 || optind+1 >= argc) {
1854                 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1855                 goto quit;
1856             }
1857 
1858             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
1859                 pa_log(_("Invalid suspend specification."));
1860                 goto quit;
1861             }
1862 
1863             suspend = !!b;
1864 
1865             if (argc > optind+2)
1866                 sink_name = pa_xstrdup(argv[optind+1]);
1867 
1868         } else if (pa_streq(argv[optind], "suspend-source")) {
1869             int b;
1870 
1871             action = SUSPEND_SOURCE;
1872 
1873             if (argc > optind+3 || optind+1 >= argc) {
1874                 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1875                 goto quit;
1876             }
1877 
1878             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
1879                 pa_log(_("Invalid suspend specification."));
1880                 goto quit;
1881             }
1882 
1883             suspend = !!b;
1884 
1885             if (argc > optind+2)
1886                 source_name = pa_xstrdup(argv[optind+1]);
1887         } else if (pa_streq(argv[optind], "set-card-profile")) {
1888             action = SET_CARD_PROFILE;
1889 
1890             if (argc != optind+3) {
1891                 pa_log(_("You have to specify a card name/index and a profile name"));
1892                 goto quit;
1893             }
1894 
1895             card_name = pa_xstrdup(argv[optind+1]);
1896             profile_name = pa_xstrdup(argv[optind+2]);
1897 
1898         } else if (pa_streq(argv[optind], "set-sink-port")) {
1899             action = SET_SINK_PORT;
1900 
1901             if (argc != optind+3) {
1902                 pa_log(_("You have to specify a sink name/index and a port name"));
1903                 goto quit;
1904             }
1905 
1906             sink_name = pa_xstrdup(argv[optind+1]);
1907             port_name = pa_xstrdup(argv[optind+2]);
1908 
1909         } else if (pa_streq(argv[optind], "set-default-sink")) {
1910             action = SET_DEFAULT_SINK;
1911 
1912             if (argc != optind+2) {
1913                 pa_log(_("You have to specify a sink name"));
1914                 goto quit;
1915             }
1916 
1917             sink_name = pa_xstrdup(argv[optind+1]);
1918 
1919         } else if (pa_streq(argv[optind], "set-source-port")) {
1920             action = SET_SOURCE_PORT;
1921 
1922             if (argc != optind+3) {
1923                 pa_log(_("You have to specify a source name/index and a port name"));
1924                 goto quit;
1925             }
1926 
1927             source_name = pa_xstrdup(argv[optind+1]);
1928             port_name = pa_xstrdup(argv[optind+2]);
1929 
1930         } else if (pa_streq(argv[optind], "set-default-source")) {
1931             action = SET_DEFAULT_SOURCE;
1932 
1933             if (argc != optind+2) {
1934                 pa_log(_("You have to specify a source name"));
1935                 goto quit;
1936             }
1937 
1938             source_name = pa_xstrdup(argv[optind+1]);
1939 
1940         } else if (pa_streq(argv[optind], "set-sink-volume")) {
1941             action = SET_SINK_VOLUME;
1942 
1943             if (argc < optind+3) {
1944                 pa_log(_("You have to specify a sink name/index and a volume"));
1945                 goto quit;
1946             }
1947 
1948             sink_name = pa_xstrdup(argv[optind+1]);
1949 
1950             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
1951                 goto quit;
1952 
1953         } else if (pa_streq(argv[optind], "set-source-volume")) {
1954             action = SET_SOURCE_VOLUME;
1955 
1956             if (argc < optind+3) {
1957                 pa_log(_("You have to specify a source name/index and a volume"));
1958                 goto quit;
1959             }
1960 
1961             source_name = pa_xstrdup(argv[optind+1]);
1962 
1963             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
1964                 goto quit;
1965 
1966         } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1967             action = SET_SINK_INPUT_VOLUME;
1968 
1969             if (argc < optind+3) {
1970                 pa_log(_("You have to specify a sink input index and a volume"));
1971                 goto quit;
1972             }
1973 
1974             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1975                 pa_log(_("Invalid sink input index"));
1976                 goto quit;
1977             }
1978 
1979             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
1980                 goto quit;
1981 
1982         } else if (pa_streq(argv[optind], "set-source-output-volume")) {
1983             action = SET_SOURCE_OUTPUT_VOLUME;
1984 
1985             if (argc < optind+3) {
1986                 pa_log(_("You have to specify a source output index and a volume"));
1987                 goto quit;
1988             }
1989 
1990             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
1991                 pa_log(_("Invalid source output index"));
1992                 goto quit;
1993             }
1994 
1995             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
1996                 goto quit;
1997 
1998         } else if (pa_streq(argv[optind], "set-sink-mute")) {
1999             action = SET_SINK_MUTE;
2000 
2001             if (argc != optind+3) {
2002                 pa_log(_("You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"));
2003                 goto quit;
2004             }
2005 
2006             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
2007                 pa_log(_("Invalid mute specification"));
2008                 goto quit;
2009             }
2010 
2011             sink_name = pa_xstrdup(argv[optind+1]);
2012 
2013         } else if (pa_streq(argv[optind], "set-source-mute")) {
2014             action = SET_SOURCE_MUTE;
2015 
2016             if (argc != optind+3) {
2017                 pa_log(_("You have to specify a source name/index and a mute action (0, 1, or 'toggle')"));
2018                 goto quit;
2019             }
2020 
2021             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
2022                 pa_log(_("Invalid mute specification"));
2023                 goto quit;
2024             }
2025 
2026             source_name = pa_xstrdup(argv[optind+1]);
2027 
2028         } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
2029             action = SET_SINK_INPUT_MUTE;
2030 
2031             if (argc != optind+3) {
2032                 pa_log(_("You have to specify a sink input index and a mute action (0, 1, or 'toggle')"));
2033                 goto quit;
2034             }
2035 
2036             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
2037                 pa_log(_("Invalid sink input index specification"));
2038                 goto quit;
2039             }
2040 
2041             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
2042                 pa_log(_("Invalid mute specification"));
2043                 goto quit;
2044             }
2045 
2046         } else if (pa_streq(argv[optind], "set-source-output-mute")) {
2047             action = SET_SOURCE_OUTPUT_MUTE;
2048 
2049             if (argc != optind+3) {
2050                 pa_log(_("You have to specify a source output index and a mute action (0, 1, or 'toggle')"));
2051                 goto quit;
2052             }
2053 
2054             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
2055                 pa_log(_("Invalid source output index specification"));
2056                 goto quit;
2057             }
2058 
2059             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
2060                 pa_log(_("Invalid mute specification"));
2061                 goto quit;
2062             }
2063 
2064         } else if (pa_streq(argv[optind], "subscribe"))
2065 
2066             action = SUBSCRIBE;
2067 
2068         else if (pa_streq(argv[optind], "set-sink-formats")) {
2069             int32_t tmp;
2070 
2071             if (argc != optind+3 || pa_atoi(argv[optind+1], &tmp) < 0) {
2072                 pa_log(_("You have to specify a sink index and a semicolon-separated list of supported formats"));
2073                 goto quit;
2074             }
2075 
2076             sink_idx = tmp;
2077             action = SET_SINK_FORMATS;
2078             formats = pa_xstrdup(argv[optind+2]);
2079 
2080         } else if (pa_streq(argv[optind], "set-port-latency-offset")) {
2081             action = SET_PORT_LATENCY_OFFSET;
2082 
2083             if (argc != optind+4) {
2084                 pa_log(_("You have to specify a card name/index, a port name and a latency offset"));
2085                 goto quit;
2086             }
2087 
2088             card_name = pa_xstrdup(argv[optind+1]);
2089             port_name = pa_xstrdup(argv[optind+2]);
2090             if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {
2091                 pa_log(_("Could not parse latency offset"));
2092                 goto quit;
2093             }
2094 
2095         } else if (pa_streq(argv[optind], "help")) {
2096             help(bn);
2097             ret = 0;
2098             goto quit;
2099         }
2100     }
2101 
2102     if (action == NONE) {
2103         pa_log(_("No valid command specified."));
2104         goto quit;
2105     }
2106 
2107     if (!(m = pa_mainloop_new())) {
2108         pa_log(_("pa_mainloop_new() failed."));
2109         goto quit;
2110     }
2111 
2112     mainloop_api = pa_mainloop_get_api(m);
2113 
2114     pa_assert_se(pa_signal_init(mainloop_api) == 0);
2115     pa_signal_new(SIGINT, exit_signal_callback, NULL);
2116     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
2117     pa_disable_sigpipe();
2118 
2119     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
2120         pa_log(_("pa_context_new() failed."));
2121         goto quit;
2122     }
2123 
2124     pa_context_set_state_callback(context, context_state_callback, NULL);
2125     if (pa_context_connect(context, server, 0, NULL) < 0) {
2126         pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
2127         goto quit;
2128     }
2129 
2130     if (pa_mainloop_run(m, &ret) < 0) {
2131         pa_log(_("pa_mainloop_run() failed."));
2132         goto quit;
2133     }
2134 
2135 quit:
2136     if (sample_stream)
2137         pa_stream_unref(sample_stream);
2138 
2139     if (context)
2140         pa_context_unref(context);
2141 
2142     if (m) {
2143         pa_signal_done();
2144         pa_mainloop_free(m);
2145     }
2146 
2147     pa_xfree(server);
2148     pa_xfree(list_type);
2149     pa_xfree(sample_name);
2150     pa_xfree(sink_name);
2151     pa_xfree(source_name);
2152     pa_xfree(module_args);
2153     pa_xfree(card_name);
2154     pa_xfree(profile_name);
2155     pa_xfree(port_name);
2156     pa_xfree(formats);
2157 
2158     if (sndfile)
2159         sf_close(sndfile);
2160 
2161     if (proplist)
2162         pa_proplist_free(proplist);
2163 
2164     return ret;
2165 }
2166