]> code.delx.au - pulseaudio/blobdiff - src/utils/pactl.c
pacat: Don't use any buffer attr if we don't set any latency/process time params
[pulseaudio] / src / utils / pactl.c
index 53c67666340b2e353ef39fb2b12c6d55dcfd32e8..ee67c4259196bf77881d897547cb2ef1eaf232a1 100644 (file)
 #include <pulsecore/log.h>
 #include <pulsecore/sndfile-util.h>
 
-#define BUFSIZE (16*1024)
-
 static pa_context *context = NULL;
 static pa_mainloop_api *mainloop_api = NULL;
 
-static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL, *card_name = NULL, *profile_name = NULL;
-static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX;
+static char
+    *sample_name = NULL,
+    *sink_name = NULL,
+    *source_name = NULL,
+    *module_name = NULL,
+    *module_args = NULL,
+    *card_name = NULL,
+    *profile_name = NULL,
+    *port_name = NULL;
+
+static uint32_t
+    sink_input_idx = PA_INVALID_INDEX,
+    source_output_idx = PA_INVALID_INDEX;
+
 static uint32_t module_index;
 static pa_bool_t suspend;
+static pa_bool_t mute;
+static pa_volume_t volume;
 
 static pa_proplist *proplist = NULL;
 
@@ -61,7 +73,6 @@ static pa_stream *sample_stream = NULL;
 static pa_sample_spec sample_spec;
 static pa_channel_map channel_map;
 static size_t sample_length = 0;
-
 static int actions = 1;
 
 static pa_bool_t nl = FALSE;
@@ -80,7 +91,15 @@ static enum {
     UNLOAD_MODULE,
     SUSPEND_SINK,
     SUSPEND_SOURCE,
-    SET_CARD_PROFILE
+    SET_CARD_PROFILE,
+    SET_SINK_PORT,
+    SET_SOURCE_PORT,
+    SET_SINK_VOLUME,
+    SET_SOURCE_VOLUME,
+    SET_SINK_INPUT_VOLUME,
+    SET_SINK_MUTE,
+    SET_SOURCE_MUTE,
+    SET_SINK_INPUT_MUTE
 } action = NONE;
 
 static void quit(int ret) {
@@ -94,6 +113,7 @@ static void context_drain_complete(pa_context *c, void *userdata) {
 
 static void drain(void) {
     pa_operation *o;
+
     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
         pa_context_disconnect(context);
     else
@@ -108,9 +128,9 @@ static void complete_action(void) {
 }
 
 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
-    char s[128];
+    char s[PA_BYTES_SNPRINT_MAX];
     if (!i) {
-        pa_log(_("Failed to get statistics: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -131,15 +151,28 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
     char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 
     if (!i) {
-        pa_log(_("Failed to get server information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
 
+    printf(_("Server String: %s\n"
+             "Library Protocol Version: %u\n"
+             "Server Protocol Version: %u\n"
+             "Is Local: %s\n"
+             "Client Index: %u\n"
+             "Tile Size: %zu\n"),
+             pa_context_get_server(c),
+             pa_context_get_protocol_version(c),
+             pa_context_get_server_protocol_version(c),
+             pa_yes_no(pa_context_is_local(c)),
+             pa_context_get_index(c),
+             pa_context_get_tile_size(c, NULL));
+
     pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
     pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
 
-    printf(_("User name: %s\n"
+    printf(_("User Name: %s\n"
              "Host Name: %s\n"
              "Server Name: %s\n"
              "Server Version: %s\n"
@@ -147,7 +180,7 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
              "Default Channel Map: %s\n"
              "Default Sink: %s\n"
              "Default Source: %s\n"
-             "Cookie: %08x\n"),
+             "Cookie: %04x:%04x\n"),
            i->user_name,
            i->host_name,
            i->server_name,
@@ -156,7 +189,8 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
            cm,
            i->default_sink_name,
            i->default_source_name,
-           i->cookie);
+           i->cookie >> 16,
+           i->cookie & 0xFFFFU);
 
     complete_action();
 }
@@ -180,7 +214,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get sink information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -239,6 +273,18 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
+
+    if (i->ports) {
+        pa_sink_port_info **p;
+
+        printf(_("\tPorts:\n"));
+        for (p = i->ports; *p; p++)
+            printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
+    }
+
+    if (i->active_port)
+        printf(_("\tActive Port: %s\n"),
+               i->active_port->name);
 }
 
 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
@@ -260,7 +306,7 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get source information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -319,6 +365,18 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
+
+    if (i->ports) {
+        pa_source_port_info **p;
+
+        printf(_("\tPorts:\n"));
+        for (p = i->ports; *p; p++)
+            printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
+    }
+
+    if (i->active_port)
+        printf(_("\tActive Port: %s\n"),
+               i->active_port->name);
 }
 
 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
@@ -326,7 +384,7 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -363,7 +421,7 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get client information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -398,7 +456,7 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get card information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
         complete_action();
         return;
     }
@@ -447,7 +505,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get sink input information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -505,7 +563,7 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get source output information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -552,11 +610,11 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
 }
 
 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
-    char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
-        pa_log(_("Failed to get sample information: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -604,7 +662,7 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
 
 static void simple_callback(pa_context *c, int success, void *userdata) {
     if (!success) {
-        pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -614,7 +672,7 @@ static void simple_callback(pa_context *c, int success, void *userdata) {
 
 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
     if (idx == PA_INVALID_INDEX) {
-        pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
+        pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
         quit(1);
         return;
     }
@@ -638,7 +696,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
 
         case PA_STREAM_FAILED:
         default:
-            pa_log(_("Failed to upload sample: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
             quit(1);
     }
 }
@@ -655,7 +713,7 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
 
     if ((sf_readf_float(sndfile, d, l)) != l) {
         pa_xfree(d);
-        pa_log(_("Premature end of file\n"));
+        pa_log(_("Premature end of file"));
         quit(1);
         return;
     }
@@ -687,7 +745,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     break;
 
                 case PLAY_SAMPLE:
-                    pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
+                    pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
                     break;
 
                 case REMOVE_SAMPLE:
@@ -753,6 +811,50 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
                     break;
 
+                case SET_SINK_PORT:
+                    pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
+                    break;
+
+                case SET_SOURCE_PORT:
+                    pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
+                    break;
+
+                case SET_SINK_MUTE:
+                    pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
+                    break;
+
+                case SET_SOURCE_MUTE:
+                    pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
+                    break;
+
+                case SET_SINK_INPUT_MUTE:
+                    pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
+                    break;
+
+                case SET_SINK_VOLUME: {
+                    pa_cvolume v;
+
+                    pa_cvolume_set(&v, 1, volume);
+                    pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
+                    break;
+                }
+
+                case SET_SOURCE_VOLUME: {
+                    pa_cvolume v;
+
+                    pa_cvolume_set(&v, 1, volume);
+                    pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
+                    break;
+                }
+
+                case SET_SINK_INPUT_VOLUME: {
+                    pa_cvolume v;
+
+                    pa_cvolume_set(&v, 1, volume);
+                    pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
+                    break;
+                }
+
                 default:
                     pa_assert_not_reached();
             }
@@ -764,13 +866,13 @@ static void context_state_callback(pa_context *c, void *userdata) {
 
         case PA_CONTEXT_FAILED:
         default:
-            pa_log(_("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
+            pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
             quit(1);
     }
 }
 
 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
-    pa_log(_("Got SIGINT, exiting.\n"));
+    pa_log(_("Got SIGINT, exiting."));
     quit(0);
 }
 
@@ -782,18 +884,30 @@ static void help(const char *argv0) {
              "%s [options] upload-sample FILENAME [NAME]\n"
              "%s [options] play-sample NAME [SINK]\n"
              "%s [options] remove-sample NAME\n"
-             "%s [options] move-sink-input ID SINK\n"
-             "%s [options] move-source-output ID SOURCE\n"
+             "%s [options] move-sink-input SINKINPUT SINK\n"
+             "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
              "%s [options] load-module NAME [ARGS ...]\n"
-             "%s [options] unload-module ID\n"
-             "%s [options] suspend-sink [SINK] 1|0\n"
-             "%s [options] suspend-source [SOURCE] 1|0\n"
-             "%s [options] set-card-profile [CARD] [PROFILE] \n\n"
+             "%s [options] unload-module MODULE\n"
+             "%s [options] suspend-sink SINK 1|0\n"
+             "%s [options] suspend-source SOURCE 1|0\n"
+             "%s [options] set-card-profile CARD PROFILE\n"
+             "%s [options] set-sink-port SINK PORT\n"
+             "%s [options] set-source-port SOURCE PORT\n"
+             "%s [options] set-sink-volume SINK VOLUME\n"
+             "%s [options] set-source-volume SOURCE VOLUME\n"
+             "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+             "%s [options] set-sink-mute SINK 1|0\n"
+             "%s [options] set-source-mute SOURCE 1|0\n"
+             "%s [options] set-sink-input-mute SINKINPUT 1|0\n\n"
              "  -h, --help                            Show this help\n"
              "      --version                         Show version\n\n"
              "  -s, --server=SERVER                   The name of the server to connect to\n"
              "  -n, --client-name=NAME                How to call this client on the server\n"),
-           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
+           argv0, argv0, argv0, argv0, argv0,
+           argv0, argv0, argv0, argv0, argv0,
+           argv0, argv0, argv0, argv0, argv0,
+           argv0, argv0, argv0, argv0, argv0,
+           argv0);
 }
 
 enum {
@@ -802,7 +916,6 @@ enum {
 
 int main(int argc, char *argv[]) {
     pa_mainloop* m = NULL;
-    char tmp[PATH_MAX];
     int ret = 1, c;
     char *server = NULL, *bn;
 
@@ -849,7 +962,7 @@ int main(int argc, char *argv[]) {
                 if (!(t = pa_locale_to_utf8(optarg)) ||
                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
 
-                    pa_log(_("Invalid client name '%s'\n"), t ? t : optarg);
+                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
                     pa_xfree(t);
                     goto quit;
                 }
@@ -875,41 +988,32 @@ int main(int argc, char *argv[]) {
             action = UPLOAD_SAMPLE;
 
             if (optind+1 >= argc) {
-                pa_log(_("Please specify a sample file to load\n"));
+                pa_log(_("Please specify a sample file to load"));
                 goto quit;
             }
 
             if (optind+2 < argc)
                 sample_name = pa_xstrdup(argv[optind+2]);
             else {
-                char *f = strrchr(argv[optind+1], '/');
-                size_t n;
-                if (f)
-                    f++;
-                else
-                    f = argv[optind];
-
-                n = strcspn(f, ".");
-                strncpy(tmp, f, n);
-                tmp[n] = 0;
-                sample_name = pa_xstrdup(tmp);
+                char *f = pa_path_get_filename(argv[optind+1]);
+                sample_name = pa_xstrndup(f, strcspn(f, "."));
             }
 
             pa_zero(sfi);
             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
-                pa_log(_("Failed to open sound file.\n"));
+                pa_log(_("Failed to open sound file."));
                 goto quit;
             }
 
             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
-                pa_log(_("Failed to determine sample specification from file.\n"));
+                pa_log(_("Failed to determine sample specification from file."));
                 goto quit;
             }
             sample_spec.format = PA_SAMPLE_FLOAT32;
 
             if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
                 if (sample_spec.channels > 2)
-                     pa_log(_("Warning: Failed to determine sample specification from file.\n"));
+                     pa_log(_("Warning: Failed to determine sample specification from file."));
                 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
             }
 
@@ -919,19 +1023,19 @@ int main(int argc, char *argv[]) {
         } else if (pa_streq(argv[optind], "play-sample")) {
             action = PLAY_SAMPLE;
             if (argc != optind+2 && argc != optind+3) {
-                pa_log(_("You have to specify a sample name to play\n"));
+                pa_log(_("You have to specify a sample name to play"));
                 goto quit;
             }
 
             sample_name = pa_xstrdup(argv[optind+1]);
 
             if (optind+2 < argc)
-                device = pa_xstrdup(argv[optind+2]);
+                sink_name = pa_xstrdup(argv[optind+2]);
 
         } else if (pa_streq(argv[optind], "remove-sample")) {
             action = REMOVE_SAMPLE;
             if (argc != optind+2) {
-                pa_log(_("You have to specify a sample name to remove\n"));
+                pa_log(_("You have to specify a sample name to remove"));
                 goto quit;
             }
 
@@ -940,7 +1044,7 @@ int main(int argc, char *argv[]) {
         } else if (pa_streq(argv[optind], "move-sink-input")) {
             action = MOVE_SINK_INPUT;
             if (argc != optind+3) {
-                pa_log(_("You have to specify a sink input index and a sink\n"));
+                pa_log(_("You have to specify a sink input index and a sink"));
                 goto quit;
             }
 
@@ -950,7 +1054,7 @@ int main(int argc, char *argv[]) {
         } else if (pa_streq(argv[optind], "move-source-output")) {
             action = MOVE_SOURCE_OUTPUT;
             if (argc != optind+3) {
-                pa_log(_("You have to specify a source output index and a source\n"));
+                pa_log(_("You have to specify a source output index and a source"));
                 goto quit;
             }
 
@@ -965,7 +1069,7 @@ int main(int argc, char *argv[]) {
             action = LOAD_MODULE;
 
             if (argc <= optind+1) {
-                pa_log(_("You have to specify a module name and arguments.\n"));
+                pa_log(_("You have to specify a module name and arguments."));
                 goto quit;
             }
 
@@ -985,7 +1089,7 @@ int main(int argc, char *argv[]) {
             action = UNLOAD_MODULE;
 
             if (argc != optind+2) {
-                pa_log(_("You have to specify a module index\n"));
+                pa_log(_("You have to specify a module index"));
                 goto quit;
             }
 
@@ -995,7 +1099,7 @@ int main(int argc, char *argv[]) {
             action = SUSPEND_SINK;
 
             if (argc > optind+3 || optind+1 >= argc) {
-                pa_log(_("You may not specify more than one sink. You have to specify a boolean value.\n"));
+                pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
                 goto quit;
             }
 
@@ -1008,7 +1112,7 @@ int main(int argc, char *argv[]) {
             action = SUSPEND_SOURCE;
 
             if (argc > optind+3 || optind+1 >= argc) {
-                pa_log(_("You may not specify more than one source. You have to specify a boolean value.\n"));
+                pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
                 goto quit;
             }
 
@@ -1020,13 +1124,145 @@ int main(int argc, char *argv[]) {
             action = SET_CARD_PROFILE;
 
             if (argc != optind+3) {
-                pa_log(_("You have to specify a card name/index and a profile name\n"));
+                pa_log(_("You have to specify a card name/index and a profile name"));
                 goto quit;
             }
 
             card_name = pa_xstrdup(argv[optind+1]);
             profile_name = pa_xstrdup(argv[optind+2]);
 
+        } else if (pa_streq(argv[optind], "set-sink-port")) {
+            action = SET_SINK_PORT;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink name/index and a port name"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+            port_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-source-port")) {
+            action = SET_SOURCE_PORT;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source name/index and a port name"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+            port_name = pa_xstrdup(argv[optind+2]);
+
+        } else if (pa_streq(argv[optind], "set-sink-volume")) {
+            uint32_t v;
+            action = SET_SINK_VOLUME;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink name/index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+2], &v) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+            volume = (pa_volume_t) v;
+
+        } else if (pa_streq(argv[optind], "set-source-volume")) {
+            uint32_t v;
+            action = SET_SOURCE_VOLUME;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source name/index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+2], &v) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+            volume = (pa_volume_t) v;
+
+        } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
+            uint32_t v;
+            action = SET_SINK_INPUT_VOLUME;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink input index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
+                pa_log(_("Invalid sink input index"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+2], &v) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            volume = (pa_volume_t) v;
+
+        } else if (pa_streq(argv[optind], "set-sink-mute")) {
+            int b;
+            action = SET_SINK_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink name/index and a mute boolean"));
+                goto quit;
+            }
+
+            if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            sink_name = pa_xstrdup(argv[optind+1]);
+            mute = b;
+
+        } else if (pa_streq(argv[optind], "set-source-mute")) {
+            int b;
+            action = SET_SOURCE_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source name/index and a mute boolean"));
+                goto quit;
+            }
+
+            if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            source_name = pa_xstrdup(argv[optind+1]);
+            mute = b;
+
+        } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
+            int b;
+            action = SET_SINK_INPUT_MUTE;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a sink input index and a mute boolean"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
+                pa_log(_("Invalid sink input index specification"));
+                goto quit;
+            }
+
+            if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
+                pa_log(_("Invalid volume specification"));
+                goto quit;
+            }
+
+            mute = b;
+
         } else if (pa_streq(argv[optind], "help")) {
             help(bn);
             ret = 0;
@@ -1035,12 +1271,12 @@ int main(int argc, char *argv[]) {
     }
 
     if (action == NONE) {
-        pa_log(_("No valid command specified.\n"));
+        pa_log(_("No valid command specified."));
         goto quit;
     }
 
     if (!(m = pa_mainloop_new())) {
-        pa_log(_("pa_mainloop_new() failed.\n"));
+        pa_log(_("pa_mainloop_new() failed."));
         goto quit;
     }
 
@@ -1052,7 +1288,7 @@ int main(int argc, char *argv[]) {
     pa_disable_sigpipe();
 
     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
-        pa_log(_("pa_context_new() failed.\n"));
+        pa_log(_("pa_context_new() failed."));
         goto quit;
     }
 
@@ -1063,7 +1299,7 @@ int main(int argc, char *argv[]) {
     }
 
     if (pa_mainloop_run(m, &ret) < 0) {
-        pa_log(_("pa_mainloop_run() failed.\n"));
+        pa_log(_("pa_mainloop_run() failed."));
         goto quit;
     }
 
@@ -1080,7 +1316,6 @@ quit:
     }
 
     pa_xfree(server);
-    pa_xfree(device);
     pa_xfree(sample_name);
     pa_xfree(sink_name);
     pa_xfree(source_name);