[pulseaudio-discuss] [PATCH 1/5] pactl: Allow to set volume of each channel independently (Bug #39190)

Peter Meerwald pmeerw at pmeerw.net
Fri Feb 14 10:11:47 CET 2014


From: Parin Porecha <parinporecha at gmail.com>

Example: pactl set-sink-volume "sink_name" 32000 40000
If the number of volumes provided is different than the number of channels
(excluding the case where a single volume is provided), an error message
is displayed explaining why the volumes could not be set.

patch proposed by Parin Porecha, commit message slightly edited
---
 src/utils/pactl.c | 183 ++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 130 insertions(+), 53 deletions(-)

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 40e6689..f199f77 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -69,7 +69,7 @@ static bool short_list_format = false;
 static uint32_t module_index;
 static int32_t latency_offset;
 static bool suspend;
-static pa_volume_t volume;
+static pa_cvolume volume;
 static enum volume_flags {
     VOL_UINT     = 0,
     VOL_PERCENT  = 1,
@@ -837,12 +837,17 @@ static void volume_relative_adjust(pa_cvolume *cv) {
     /* Relative volume change is additive in case of UINT or PERCENT
      * and multiplicative for LINEAR or DECIBEL */
     if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
-        pa_volume_t v = pa_cvolume_avg(cv);
-        v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
-        pa_cvolume_set(cv, 1, v);
+        unsigned i;
+        for (i = 0; i < cv->channels; i++)
+        {
+            if (cv->values[i] + volume.values[i] < PA_VOLUME_NORM)
+                cv->values[i] = PA_VOLUME_MUTED;
+            else
+                cv->values[i] += volume.values[i] - PA_VOLUME_NORM;
+        }
     }
     if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
-        pa_sw_cvolume_multiply_scalar(cv, cv, volume);
+        pa_sw_cvolume_multiply(cv, cv, &volume);
     }
 }
 
@@ -873,6 +878,7 @@ static void unload_module_by_name_callback(pa_context *c, const pa_module_info *
 
 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
     pa_cvolume cv;
+    unsigned channels_supported;
 
     if (is_last < 0) {
         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
@@ -885,13 +891,29 @@ static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int i
 
     pa_assert(i);
 
-    cv = i->volume;
-    volume_relative_adjust(&cv);
+    channels_supported = (&i->channel_map)->channels;
+    if ((volume.channels < channels_supported && volume.channels != 1) || (volume.channels > channels_supported)) {
+        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"), volume.channels, channels_supported);
+        quit(1);
+        return;
+    }
+
+    if (volume.channels == 1)
+        pa_cvolume_set(&volume, channels_supported, volume.values[0]);
+    volume.channels = channels_supported;
+
+    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
+        cv = i->volume;
+        volume_relative_adjust(&cv);
+    }
+    else
+        cv = volume;
     pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
 }
 
 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
     pa_cvolume cv;
+    unsigned channels_supported;
 
     if (is_last < 0) {
         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
@@ -904,13 +926,29 @@ static void get_source_volume_callback(pa_context *c, const pa_source_info *i, i
 
     pa_assert(i);
 
-    cv = i->volume;
-    volume_relative_adjust(&cv);
+    channels_supported = (&i->channel_map)->channels;
+    if ((volume.channels < channels_supported && volume.channels != 1) || (volume.channels > channels_supported)) {
+        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"), volume.channels, channels_supported);
+        quit(1);
+        return;
+    }
+
+    if (volume.channels == 1)
+        pa_cvolume_set(&volume, channels_supported, volume.values[0]);
+    volume.channels = channels_supported;
+
+    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
+        cv = i->volume;
+        volume_relative_adjust(&cv);
+    }
+    else
+        cv = volume;
     pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
 }
 
 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
     pa_cvolume cv;
+    unsigned channels_supported;
 
     if (is_last < 0) {
         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
@@ -923,13 +961,29 @@ static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_in
 
     pa_assert(i);
 
-    cv = i->volume;
-    volume_relative_adjust(&cv);
+    channels_supported = (&i->channel_map)->channels;
+    if ((volume.channels < channels_supported && volume.channels != 1) || (volume.channels > channels_supported)) {
+        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"), volume.channels, channels_supported);
+        quit(1);
+        return;
+    }
+
+    if (volume.channels == 1)
+        pa_cvolume_set(&volume, channels_supported, volume.values[0]);
+    volume.channels = channels_supported;
+
+    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
+        cv = i->volume;
+        volume_relative_adjust(&cv);
+    }
+    else
+        cv = volume;
     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
 }
 
 static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
     pa_cvolume cv;
+    unsigned channels_supported;
 
     if (is_last < 0) {
         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
@@ -942,8 +996,23 @@ static void get_source_output_volume_callback(pa_context *c, const pa_source_out
 
     pa_assert(o);
 
-    cv = o->volume;
-    volume_relative_adjust(&cv);
+    channels_supported = (&o->channel_map)->channels;
+    if ((volume.channels < channels_supported && volume.channels != 1) || (volume.channels > channels_supported)) {
+        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"), volume.channels, channels_supported);
+        quit(1);
+        return;
+    }
+
+    if (volume.channels == 1)
+        pa_cvolume_set(&volume, channels_supported, volume.values[0]);
+    volume.channels = channels_supported;
+
+    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
+        cv = o->volume;
+        volume_relative_adjust(&cv);
+    }
+    else
+        cv = volume;
     pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
 }
 
@@ -1309,43 +1378,19 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     break;
 
                 case SET_SINK_VOLUME:
-                    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-                        pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
-                    } else {
-                        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));
-                    }
+                    pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
                     break;
 
                 case SET_SOURCE_VOLUME:
-                    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-                        pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
-                    } else {
-                        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));
-                    }
+                    pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
                     break;
 
                 case SET_SINK_INPUT_VOLUME:
-                    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-                        pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
-                    } else {
-                        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));
-                    }
+                    pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
                     break;
 
                 case SET_SOURCE_OUTPUT_VOLUME:
-                    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
-                        pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
-                    } else {
-                        pa_cvolume v;
-                        pa_cvolume_set(&v, 1, volume);
-                        pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL));
-                    }
+                    pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
                     break;
 
                 case SET_SINK_FORMATS:
@@ -1806,35 +1851,52 @@ int main(int argc, char *argv[]) {
             source_name = pa_xstrdup(argv[optind+1]);
 
         } else if (pa_streq(argv[optind], "set-sink-volume")) {
+            unsigned i=0;
             action = SET_SINK_VOLUME;
 
-            if (argc != optind+3) {
+            if (argc < optind+3) {
                 pa_log(_("You have to specify a sink name/index and a volume"));
                 goto quit;
             }
 
             sink_name = pa_xstrdup(argv[optind+1]);
 
-            if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
-                goto quit;
+            volume.channels = argc-3;
+
+            for(i=0; i < volume.channels; i++)
+            {
+                if (argv[optind+2+i] == NULL)
+                    volume.channels--;
+                else if (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
+                    goto quit;
+            }
 
         } else if (pa_streq(argv[optind], "set-source-volume")) {
+            unsigned i=0;
             action = SET_SOURCE_VOLUME;
 
-            if (argc != optind+3) {
+            if (argc < optind+3) {
                 pa_log(_("You have to specify a source name/index and a volume"));
                 goto quit;
             }
 
             source_name = pa_xstrdup(argv[optind+1]);
 
-            if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
-                goto quit;
+            volume.channels = argc-3;
+
+            for(i=0; i < volume.channels; i++)
+            {
+                if (argv[optind+2+i] == NULL)
+                    volume.channels--;
+                else if (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
+                    goto quit;
+            }
 
         } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
+            unsigned i=0;
             action = SET_SINK_INPUT_VOLUME;
 
-            if (argc != optind+3) {
+            if (argc < optind+3) {
                 pa_log(_("You have to specify a sink input index and a volume"));
                 goto quit;
             }
@@ -1844,13 +1906,21 @@ int main(int argc, char *argv[]) {
                 goto quit;
             }
 
-            if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
-                goto quit;
+            volume.channels = argc-3;
+
+            for(i=0; i < volume.channels; i++)
+            {
+                if (argv[optind+2+i] == NULL)
+                    volume.channels--;
+                else if (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
+                    goto quit;
+            }
 
         } else if (pa_streq(argv[optind], "set-source-output-volume")) {
+            unsigned i=0;
             action = SET_SOURCE_OUTPUT_VOLUME;
 
-            if (argc != optind+3) {
+            if (argc < optind+3) {
                 pa_log(_("You have to specify a source output index and a volume"));
                 goto quit;
             }
@@ -1860,8 +1930,15 @@ int main(int argc, char *argv[]) {
                 goto quit;
             }
 
-            if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
-                goto quit;
+            volume.channels = argc-3;
+
+            for(i=0; i < volume.channels; i++)
+            {
+                if (argv[optind+2+i] == NULL)
+                    volume.channels--;
+                else if (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
+                    goto quit;
+            }
 
         } else if (pa_streq(argv[optind], "set-sink-mute")) {
             action = SET_SINK_MUTE;
-- 
1.8.3.2



More information about the pulseaudio-discuss mailing list