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

Peter Meerwald pmeerw at pmeerw.net
Sat Mar 15 03:41:15 PDT 2014


Hello Tanu,

I've totally forgotten about the patch my now :)
let's see if Parin steps in, otherwise I'll do the clearup

thanks, p.

> On Thu, 2014-02-20 at 23:14 +0100, Peter Meerwald wrote:
> > 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
> > code refactoring and commit message slightly edited by Peter Meerwald
> > 
> > Cc: Parin Porecha <parinporecha at gmail.com>
> > ---
> >  src/utils/pactl.c | 131 +++++++++++++++++++++++++++++++++---------------------
> >  1 file changed, 80 insertions(+), 51 deletions(-)
> > 
> > diff --git a/src/utils/pactl.c b/src/utils/pactl.c
> > index 40e6689..8d5e197 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,13 +837,16 @@ 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);
> > -    }
> > -    if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
> > -        pa_sw_cvolume_multiply_scalar(cv, cv, volume);
> > +        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;
> 
> I'd prefer this form for clarity:
> 
> cv->values[i] = cv->values[i] + volume.values[i] - PA_VOLUME_NORM;
> 
> The reason is that volume.values[i] - PA_VOLUME_NORM may underflow. That
> shouldn't matter, because the underflow gets fixed by a subsequent
> overflow, but having these underflows and overflows can confuse the
> reader (I initially thought that this code was buggy, before I realized
> that the overflow would compensate the underflow).
> 
> > +        }
> >      }
> > +    if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL)
> > +        pa_sw_cvolume_multiply(cv, cv, &volume);
> >  }
> >  
> >  static void unload_module_by_name_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
> > @@ -871,8 +874,26 @@ static void unload_module_by_name_callback(pa_context *c, const pa_module_info *
> >      }
> >  }
> >  
> > +static void fill_volume(pa_cvolume *cv, unsigned supported) {
> > +    if (volume.channels == 1) {
> > +        pa_cvolume_set(&volume, supported, volume.values[0]);
> > +        volume.channels = supported;
> 
> This assignment is redundant, because pa_cvolume_set() already sets the
> channels.
> 
> > +    } else if (volume.channels != supported) {
> > +        pa_log(_("Failed to set volume: You tried to set volumes for %d channels, whereas channel/s supported = %d\n"),
> > +            volume.channels, supported);
> 
> Cosmetic: The continuation line should be aligned with the beginning of
> the function arguments.
> 
> > +        quit(1);
> > +        return;
> > +    }
> > +
> > +    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE)
> 
> The comparison is redundant. This is sufficient:
> 
>     if (volume_flags & VOL_RELATIVE)
> 
> I know that the redundant comparison is used elsewhere in the code. If
> you don't like the resulting inconsistency, make a cleanup patch that
> removes the redundant comparisons everywhere.
> 
> > +        volume_relative_adjust(cv);
> > +    else
> > +        *cv = volume;
> > +}
> > +
> >  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)));
> > @@ -886,12 +907,15 @@ 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;
> 
> Simpler: channels_supported = i->channel_map.channels.
> 
> The channels_supported variable seems pretty redundant, though, so it
> could be just removed.
> 
> > +    fill_volume(&cv, channels_supported);
> > +
> >      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)));
> > @@ -905,12 +929,15 @@ 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;
> 
> See the previous note.
> 
> > +    fill_volume(&cv, channels_supported);
> > +
> >      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)));
> > @@ -924,12 +951,15 @@ 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;
> 
> See the previous note.
> 
> > +    fill_volume(&cv, channels_supported);
> > +
> >      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)));
> > @@ -943,7 +973,9 @@ 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;
> 
> See the previous note.
> 
> > +    fill_volume(&cv, channels_supported);
> > +
> >      pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
> >  }
> >  
> > @@ -1309,43 +1341,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 +1814,47 @@ int main(int argc, char *argv[]) {
> >              source_name = pa_xstrdup(argv[optind+1]);
> >  
> >          } else if (pa_streq(argv[optind], "set-sink-volume")) {
> > +            unsigned i;
> >              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 (parse_volume(argv[optind+2+i],
> > +                    &volume.values[i], &volume_flags) < 0)
> > +                    goto quit;
> > +            }
> 
> It should be checked that volume.channels is valid. &volume.values[i]
> will point to a bad location if i reaches PA_CHANNELS_MAX.
> 
> Also, the if condition doesn't need to be wrapped.
> 
> >  
> >          } else if (pa_streq(argv[optind], "set-source-volume")) {
> > +            unsigned i;
> >              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 (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
> > +                    goto quit;
> > +            }
> 
> See the previous note.
> 
> >  
> >          } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
> > +            unsigned i;
> >              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 +1864,18 @@ 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 (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
> > +                    goto quit;
> > +            }
> 
> See the previous note.
> 
> >  
> >          } else if (pa_streq(argv[optind], "set-source-output-volume")) {
> > +            unsigned i;
> >              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 +1885,12 @@ 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 (parse_volume(argv[optind+2+i], &volume.values[i], &volume_flags) < 0)
> > +                    goto quit;
> > +            }
> 
> See the previous note.
> 
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)


More information about the pulseaudio-discuss mailing list