]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/cli-command.c
cli: Allow source-output volumes/mute to be set via CLI
[pulseaudio] / src / pulsecore / cli-command.c
index de4995eb495b98cd4dda20947d24c99a6755b3b2..53042c10949b8547d3df14161cec18fd5cf626ad 100644 (file)
@@ -48,7 +48,6 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/core-scache.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/sound-file.h>
 #include <pulsecore/play-memchunk.h>
 #include <pulsecore/sound-file-stream.h>
@@ -97,10 +96,12 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa
 static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@@ -131,19 +132,19 @@ static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer
 static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 
 /* A method table for all available commands */
 
 static const struct command commands[] = {
-    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
     { "help",                    pa_cli_command_help,               "Show this help",               1 },
     { "list-modules",            pa_cli_command_modules,            "List loaded modules",          1 },
+    { "list-cards",              pa_cli_command_cards,              "List cards",                   1 },
     { "list-sinks",              pa_cli_command_sinks,              "List loaded sinks",            1 },
     { "list-sources",            pa_cli_command_sources,            "List loaded sources",          1 },
     { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
     { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
     { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
-    { "list-cards",              pa_cli_command_cards,              "List cards",                   1 },
     { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
     { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
     { "ls",                      pa_cli_command_info,               NULL,                           1 },
@@ -152,42 +153,46 @@ static const struct command commands[] = {
     { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index)", 2},
     { "describe-module",         pa_cli_command_describe,           "Describe a module (arg: name)", 2},
     { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)", 3},
-    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},
     { "set-source-volume",       pa_cli_command_source_volume,      "Set the volume of a source (args: index|name, volume)", 3},
     { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3},
-    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
     { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
+    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},
+    { "set-source-output-volume",pa_cli_command_source_output_volume,"Set the volume of a source output (args: index, volume)", 3},
+    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
+    { "set-source-output-mute",  pa_cli_command_source_output_mute, "Set the mute switch of a source output (args: index, bool)", 3},
+    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
+    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
+    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index|name, profile-name)", 3},
+    { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index|name, port-name)", 3},
+    { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index|name, port-name)", 3},
+    { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
+    { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
+    { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
+    { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
+    { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
     { "update-sink-proplist",    pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
     { "update-source-proplist",  pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
     { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
-    { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source_output (args: index, properties)", 3},
-    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
-    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
-    { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
-    { "kill-sink-input",         pa_cli_command_kill_sink_input,    "Kill a sink input (args: index)", 2},
-    { "kill-source-output",      pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
+    { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source output (args: index, properties)", 3},
     { "list-samples",            pa_cli_command_scache_list,        "List all entries in the sample cache", 1},
     { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3},
     { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},
     { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3},
     { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
     { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
-    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
-    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
-    { "shared",                  pa_cli_command_list_shared_props,  NULL, 1},
-    { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
-    { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
-    { "vacuum",                  pa_cli_command_vacuum,             NULL, 1},
-    { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
-    { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
-    { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
-    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index, name)", 3},
-    { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index, name)", 3},
-    { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index, name)", 3},
+    { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
+    { "kill-sink-input",         pa_cli_command_kill_sink_input,    "Kill a sink input (args: index)", 2},
+    { "kill-source-output",      pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
     { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
     { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
     { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
     { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show backtrace in log messages (args: frames)", 2},
+    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
+    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
+    { "dump-volumes",            pa_cli_command_dump_volumes,       "Debug: Show the state of all volumes", 1 },
+    { "shared",                  pa_cli_command_list_shared_props,  "Debug: Show shared properties", 1},
+    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
+    { "vacuum",                  pa_cli_command_vacuum,             NULL, 1},
     { NULL, NULL, NULL, 0 }
 };
 
@@ -585,7 +590,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
         return -1;
     }
 
-    if (!pa_sink_input_is_volume_writable(si)) {
+    if (!si->volume_writable) {
         pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n");
         return -1;
     }
@@ -632,7 +637,59 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
     }
 
     pa_cvolume_set(&cvolume, 1, volume);
-    pa_source_set_volume(source, &cvolume, TRUE);
+    pa_source_set_volume(source, &cvolume, TRUE, TRUE);
+    return 0;
+}
+
+static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_source_output *so;
+    pa_volume_t volume;
+    pa_cvolume cvolume;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    if (!so->volume_writable) {
+        pa_strbuf_puts(buf, "This source output's volume can't be changed.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_source_output_set_volume(so, &cvolume, TRUE, TRUE);
     return 0;
 }
 
@@ -904,6 +961,46 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
     return 0;
 }
 
+static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_source_output *so;
+    uint32_t idx;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(v)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    pa_source_output_set_mute(so, mute, TRUE);
+    return 0;
+}
+
 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n;
     pa_sink *s;
@@ -1575,8 +1672,10 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_card *card;
     pa_bool_t nl;
     uint32_t idx;
-    char txt[256];
     time_t now;
+#ifdef HAVE_CTIME_R
+    char txt[256];
+#endif
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1661,6 +1760,62 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     return 0;
 }
 
+static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_sink *s;
+    pa_source *so;
+    pa_sink_input *i;
+    pa_source_output *o;
+    uint32_t s_idx, i_idx;
+    char v_str[PA_CVOLUME_SNPRINT_MAX];
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    PA_IDXSET_FOREACH(s, c->sinks, s_idx) {
+        pa_strbuf_printf(buf, "Sink %d: ", s_idx);
+        pa_strbuf_printf(buf, "reference = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &s->reference_volume));
+        pa_strbuf_printf(buf, "real = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &s->real_volume));
+        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &s->soft_volume));
+        pa_strbuf_printf(buf, "current_hw = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &s->thread_info.current_hw_volume));
+        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(s->save_volume));
+
+        PA_IDXSET_FOREACH(i, s->inputs, i_idx) {
+            pa_strbuf_printf(buf, "\tInput %d: ", i_idx);
+            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->volume));
+            pa_strbuf_printf(buf, "reference_ratio = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->reference_ratio));
+            pa_strbuf_printf(buf, "real_ratio = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->real_ratio));
+            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->soft_volume));
+            pa_strbuf_printf(buf, "volume_factor = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->volume_factor));
+            pa_strbuf_printf(buf, "volume_factor_sink = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &i->volume_factor_sink));
+            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(i->save_volume));
+        }
+    }
+
+    PA_IDXSET_FOREACH(so, c->sources, s_idx) {
+        pa_strbuf_printf(buf, "Source %d: ", s_idx);
+        pa_strbuf_printf(buf, "reference = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &so->reference_volume));
+        pa_strbuf_printf(buf, "real = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &so->real_volume));
+        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &so->soft_volume));
+        pa_strbuf_printf(buf, "current_hw = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &so->thread_info.current_hw_volume));
+        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(so->save_volume));
+
+        PA_IDXSET_FOREACH(o, so->outputs, i_idx) {
+            pa_strbuf_printf(buf, "\tOutput %d: ", i_idx);
+            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->volume));
+            pa_strbuf_printf(buf, "reference_ratio = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->reference_ratio));
+            pa_strbuf_printf(buf, "real_ratio = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->real_ratio));
+            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->soft_volume));
+            pa_strbuf_printf(buf, "volume_factor = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->volume_factor));
+            pa_strbuf_printf(buf, "volume_factor_source = %s, ", pa_cvolume_snprint(v_str, sizeof(v_str), &o->volume_factor_source));
+            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(o->save_volume));
+        }
+    }
+
+    return 0;
+}
+
 int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
     const char *cs;
 
@@ -1741,7 +1896,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
                             sorted_files = pa_xnew(char*, count);
                             for (i = 0; i < count; ++i)
                                 sorted_files[i] = pa_dynarray_get(files, i);
-                            pa_dynarray_free(files, NULL, NULL);
+                            pa_dynarray_free(files, NULL);
 
                             for (i = 0; i < count; ++i) {
                                 for (unsigned j = 0; j < count; ++j) {
@@ -1783,7 +1938,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
                     if (filename[0] == PA_PATH_SEP_CHAR) {
 
                         *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
-                        pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+                        pa_log_debug("Checking for existence of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
 
                     } else {
                         const char *paths, *state = NULL;
@@ -1799,7 +1954,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
                             pa_xfree(p);
 
                             *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
-                            pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+                            pa_log_debug("Checking for existence of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
 
                             pa_xfree(pathname);