audiomixer: add new channel volume API 32/29532/5
authorAshok Sidipotu <ashok.sidipotu@collabora.com>
Thu, 14 Dec 2023 10:49:21 +0000 (16:19 +0530)
committerAshok Sidipotu <ashok.sidipotu@collabora.com>
Fri, 15 Dec 2023 07:09:40 +0000 (08:09 +0100)
- Add API to control left and right channel volumes.
- Fix a bug in treble gain update. The changed gain value of Treble is
  not updated in the audiomixer as the gain changed
  callback(on_eq_params_changed) doesnt look beyond bass for gain
  controls. Fix this issue by continuing  to look beyond bass control.
- Adjust the floating point comparision epsilon value from 0.000001 to
  0.00001.

Bug-AGL: SPEC-4931
Change-Id: I2a242d08f194b66abd84bb31a97364884e8d2a1d
Signed-off-by: Ashok Sidipotu <ashok.sidipotu@collabora.com>
src/audiomixer.c
src/audiomixer.h
src/audiomixertest.c

index 0351738..19b8bf1 100644 (file)
@@ -42,6 +42,11 @@ struct action
 {
        struct audiomixer *audiomixer;
        union {
+               struct {
+                       guint32 id;
+                       gfloat lvolume;
+                       gfloat rvolume;
+               } change_channel_volume;
                struct {
                        guint32 id;
                        gfloat volume;
@@ -59,13 +64,44 @@ struct action
 };
 
 static gboolean
-get_mixer_controls (struct audiomixer * self, guint32 node_id, gdouble * vol, gboolean * mute)
+get_mixer_controls (struct audiomixer *self, guint32 node_id, gdouble *vol,
+       gdouble *lvol, gdouble *rvol, gboolean *mute)
 {
        g_autoptr (GVariant) v = NULL;
+       g_autoptr (GVariantIter) iter = NULL;
+       gdouble val;
+       gboolean bval;
+
        g_signal_emit_by_name (self->mixer_api, "get-volume", node_id, &v);
-       return v &&
-               g_variant_lookup (v, "volume", "d", vol) &&
-               g_variant_lookup (v, "mute", "b", mute);
+       if (!v)
+               return FALSE;
+
+       if (g_variant_lookup (v, "volume", "d", &val))
+               *vol = val;
+       if (g_variant_lookup (v, "mute", "b", &bval))
+               *mute = bval;
+
+       if (g_variant_lookup (v, "channelVolumes", "a{sv}", &iter)) {
+               const gchar *idx_str = NULL;
+
+               while (g_variant_iter_loop (iter, "{&sv}", &idx_str, &v)) {
+                       const gchar *channel_str = NULL;
+                       if (g_variant_lookup (v, "channel", "&s", &channel_str)) {
+                               if (g_variant_lookup (v, "volume", "d", &val)) {
+
+                                       /* look for stereo channels only */
+                                       if (g_str_equal (channel_str, "FL"))
+                                               *lvol = val;
+                                       else if (g_str_equal (channel_str, "FR"))
+                                               *rvol = val;
+                                       else
+                                               g_warning ("unknown channel %s", channel_str);
+
+                               }
+                       }
+               }
+       }
+       return TRUE;
 }
 
 static gboolean
@@ -152,11 +188,11 @@ static void
 add_control (struct audiomixer *self, const char *name, guint32 node_id)
 {
        struct mixer_control_impl *mixctl = NULL;
-       gdouble volume = 1.0;
+       gdouble volume = 1.0, lvol = 1.0, rvol = 1.0;
        gboolean mute = FALSE;
 
        /* get current values */
-       if (!get_mixer_controls (self, node_id, &volume, &mute)) {
+       if (!get_mixer_controls (self, node_id, &volume, &lvol, &rvol, &mute)) {
                g_warning ("failed to get object controls when populating controls for %s",
                        name);
                return;
@@ -166,18 +202,20 @@ add_control (struct audiomixer *self, const char *name, guint32 node_id)
        mixctl = g_new0 (struct mixer_control_impl, 1);
        snprintf (mixctl->pub.name, sizeof (mixctl->pub.name), "%s", name);
        mixctl->pub.volume = volume;
+       mixctl->pub.lvolume = lvol;
+       mixctl->pub.rvolume = rvol;
        mixctl->pub.mute = mute;
        mixctl->node_id = node_id;
        g_ptr_array_add (self->mixer_controls, mixctl);
 
-       g_debug ("added control %s its volume is %f", mixctl->pub.name, volume);
+       g_debug ("added control %s", mixctl->pub.name);
 }
 
 static void
 volume_changed (struct audiomixer * self, guint32 node_id)
 {
        g_autoptr (GMutexLocker) locker = g_mutex_locker_new (&self->lock);
-       gdouble vol = 1.0;
+       gdouble vol = 1.0, lvol = 1.0, rvol = 1.0;
        gboolean mute = FALSE;
 
        for (guint i = 0; i < self->mixer_controls->len; i++) {
@@ -188,15 +226,29 @@ volume_changed (struct audiomixer * self, guint32 node_id)
                if (ctl->node_id != node_id)
                        continue;
 
-               if (!get_mixer_controls (self, node_id, &vol, &mute)) {
+               if (!get_mixer_controls (self, node_id, &vol, &lvol, &rvol, &mute)) {
                        g_warning ("failed to get object controls when volume changed");
                        return;
                }
 
                if ((ctl->pub.volume - 0.01f) > vol || (ctl->pub.volume + 0.01f) < vol) {
+                       /* if volume changed */
                        ctl->pub.volume = vol;
                        change_mask |= MIXER_CONTROL_CHANGE_FLAG_VOLUME;
                }
+
+               if (!(fabs (ctl->pub.lvolume - lvol) < 0.00001)) {
+                       /* if left channel volume changed */
+                       ctl->pub.lvolume = lvol;
+                       change_mask |= MIXER_CONTROL_CHANGE_FLAG_CHANNEL_VOLUME;
+               }
+
+               if (!(fabs (ctl->pub.rvolume - rvol) < 0.00001)) {
+                       /* if right channel volume changed */
+                       ctl->pub.rvolume = rvol;
+                       change_mask |= MIXER_CONTROL_CHANGE_FLAG_CHANNEL_VOLUME;
+               }
+
                if (ctl->pub.mute != mute) {
                        ctl->pub.mute = mute;
                        change_mask |= MIXER_CONTROL_CHANGE_FLAG_MUTE;
@@ -317,7 +369,7 @@ on_eq_params_changed (WpPipewireObject *obj, const gchar *param_name,
                                return;
                        }
 
-               if (!(fabs (ctl->pub.gain - gain) < 0.000001)) {
+               if (!(fabs (ctl->pub.gain - gain) < 0.00001)) {
                        /* if gain changed */
                        ctl->pub.gain = gain;
                        change_mask |= MIXER_CONTROL_CHANGE_FLAG_GAIN;
@@ -325,6 +377,8 @@ on_eq_params_changed (WpPipewireObject *obj, const gchar *param_name,
                                self->events->value_changed (self->events_data, change_mask, &ctl->pub);
                        }
                }
+               else
+                       continue;
 
                break;
        }
@@ -599,6 +653,71 @@ audiomixer_change_volume(struct audiomixer *self,
                g_free);
 }
 
+static gboolean
+do_change_channel_volume (struct action *action)
+{
+       struct audiomixer *self = action->audiomixer;
+       g_auto (GVariantBuilder) b =
+               G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+       g_auto (GVariantBuilder) l_vol =
+               G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+       g_auto (GVariantBuilder) r_vol =
+               G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+       g_auto (GVariantBuilder) channel_vols =
+               G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+
+       gboolean ret = FALSE;
+
+       /* left channel */
+       g_variant_builder_add (&l_vol, "{sv}",
+               "volume", g_variant_new_double (action->change_channel_volume.lvolume));
+       g_variant_builder_add (&l_vol, "{sv}",
+               "channel", g_variant_new_string ("FL"));
+
+       g_variant_builder_add (&channel_vols, "{sv}", "0",
+               g_variant_builder_end (&l_vol));
+
+       /* right channel */
+       g_variant_builder_add (&r_vol, "{sv}",
+               "volume", g_variant_new_double (action->change_channel_volume.rvolume));
+       g_variant_builder_add (&r_vol, "{sv}",
+               "channel", g_variant_new_string ("FR"));
+
+       g_variant_builder_add (&channel_vols, "{sv}", "1",
+               g_variant_builder_end (&r_vol));
+
+       g_variant_builder_add (&b, "{sv}",
+               "channelVolumes", g_variant_builder_end (&channel_vols));
+
+       g_signal_emit_by_name (self->mixer_api, "set-volume",
+               action->change_volume.id, g_variant_builder_end (&b), &ret);
+       if (!ret)
+               g_warning ("mixer api set-volume(channel vols) failed");
+
+       return G_SOURCE_REMOVE;
+}
+
+void
+audiomixer_change_channel_volume(struct audiomixer *self,
+       const struct mixer_control *control,
+       double left_channel_volume, double right_channel_volume)
+{
+       const struct mixer_control_impl *impl =
+               (const struct mixer_control_impl *) control;
+       struct action * action;
+
+       g_return_if_fail (self->initialized == 1);
+
+       /* schedule the action to run on the audiomixer thread */
+       action = g_new0 (struct action, 1);
+       action->audiomixer = self;
+       action->change_volume.id = impl->node_id;
+       action->change_channel_volume.lvolume = (gfloat)left_channel_volume;
+       action->change_channel_volume.rvolume = (gfloat)right_channel_volume;
+       wp_core_idle_add (self->core, NULL, (GSourceFunc)do_change_channel_volume,
+               action, g_free);
+}
+
 static gboolean
 do_change_mute (struct action * action)
 {
@@ -666,7 +785,7 @@ audiomixer_change_gain(struct audiomixer *self,
        const struct mixer_control *control,
        float gain)
 {
-       const struct mixer_control_impl *impl = control;
+       const struct mixer_control_impl *impl = (struct mixer_control_impl *)control;
        struct action * action;
 
        g_return_if_fail (self->initialized == 1);
@@ -676,7 +795,7 @@ audiomixer_change_gain(struct audiomixer *self,
        action->audiomixer = self;
        action->change_gain.id = impl->node_id;
        action->change_gain.gain = gain;
-       action->change_gain.control = impl->pub.name;
+       action->change_gain.control = (gchar *)impl->pub.name;
        wp_core_idle_add (self->core, NULL, (GSourceFunc) do_change_gain, action,
                g_free);
 }
index 2e50420..1f089d1 100644 (file)
@@ -20,6 +20,8 @@ struct mixer_control
 {
        char name[32];
        double volume;
+       double lvolume; /* left channel volume */
+       double rvolume; /* right channel volume */
        float gain;
        bool mute;
 };
@@ -30,6 +32,7 @@ struct audiomixer_events
 
        void (*value_changed) (void *data,
 #define MIXER_CONTROL_CHANGE_FLAG_VOLUME (1<<0)
+#define MIXER_CONTROL_CHANGE_FLAG_CHANNEL_VOLUME (1<<0)
 #define MIXER_CONTROL_CHANGE_FLAG_MUTE   (1<<1)
 #define MIXER_CONTROL_CHANGE_FLAG_GAIN (1<<2)
                                unsigned int change_mask,
@@ -62,6 +65,10 @@ void audiomixer_change_volume(struct audiomixer *self,
        const struct mixer_control *control,
        double volume);
 
+void audiomixer_change_channel_volume (struct audiomixer *self,
+       const struct mixer_control *control,
+       double left_channel_volume, double right_channel_volume);
+
 void audiomixer_change_gain(struct audiomixer *self,
        const struct mixer_control *control,
        float gain);
index ddb7ac9..641fe70 100644 (file)
@@ -69,7 +69,7 @@ set_gain (AudioMixerTest *self, gint id, gfloat gain)
                if(id == i) {
 
                        if (g_str_equal ("bass", ctrl->name) || g_str_equal ("treble", ctrl->name)) {
-                               if (fabs (ctrl->gain - gain) < 0.000001)
+                               if (fabs (ctrl->gain - gain) < 0.00001)
                                        g_warning ("gain already at requested level %f", ctrl->gain);
                                else {
                                        audiomixer_change_gain (self->am, ctrl, gain);
@@ -100,10 +100,54 @@ set_volume (AudioMixerTest *self, gint id, double vol)
                                g_warning ("volume cannot be applied for %s control", ctrl->name);
                        else {
 
-                               if (fabs (ctrl->volume - vol) < 0.000001)
+                               if (fabs (ctrl->volume - vol) < 0.00001)
                                        g_warning ("volume is already at requested level %f", ctrl->volume);
                                else {
-                                       audiomixer_change_volume (self->am, ctrl, (double)vol);
+                                       audiomixer_change_volume (self->am, ctrl, vol);
+                                       ret = TRUE;
+                               }
+
+                       }
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+
+static gint
+set_channels_volume (AudioMixerTest *self, gint id, double lvol, double rvol)
+{
+       gint ret = -1;
+       int i;
+       gboolean set_left_channel_volume = TRUE;
+       gboolean set_right_channel_volume = TRUE;
+
+       for (i = 0; i < self->nctrls; i++) {
+               const struct mixer_control *ctrl = self->ctrls[i];
+
+               if (id == i) {
+                       if (g_str_equal ("bass", ctrl->name) || g_str_equal ("treble", ctrl->name))
+                               g_warning ("volume cannot be applied for %s control", ctrl->name);
+                       else {
+
+                               if (fabs (ctrl->lvolume - lvol) < 0.00001) {
+                                       set_left_channel_volume = FALSE;
+                                       lvol = ctrl->lvolume;
+                                       g_warning ("left volume is already at requested level %f",
+                                               ctrl->lvolume);
+                               }
+
+                               if (fabs (ctrl->rvolume - rvol) < 0.00001) {
+                                       set_right_channel_volume = FALSE;
+                                       rvol = ctrl->rvolume;
+                                       g_warning ("right volume is already at requested level %f",
+                                               ctrl->rvolume);
+                               }
+
+                               if (set_left_channel_volume || set_right_channel_volume) {
+                                       audiomixer_change_channel_volume (self->am, ctrl, lvol, rvol);
                                        ret = TRUE;
                                }
 
@@ -129,8 +173,9 @@ print_ctrls (AudioMixerTest *self)
                        fprintf(stdout, "\n%2d. %-25s   [gain: %.2f]", i, ctrl->name,
                                ctrl->gain);
                else
-                       fprintf(stdout, "\n%2d. %-25s   [vol: %.2f, mute:%d]", i, ctrl->name,
-                               ctrl->volume, ctrl->mute);
+                       fprintf (stdout, "\n%2d. %-25s   [vol: %.2f, lvol: %.2f, rvol: %.2f,"
+                               " mute:%d]", i, ctrl->name, ctrl->volume, ctrl->lvolume, ctrl->rvolume,
+                               ctrl->mute);
        }
        fprintf (stdout, "\n");
 }
@@ -149,21 +194,33 @@ static void show_help (void)
                "  -h, --help           Show this help\n"
                "  -p, --print-controls prints controls\n"
                "  -i, --id             control id(serial#) of the control, take a look at the controls to get the id of control\n"
-               "                       Examples\n"
+               "                       Examples:\n"
                "                       audio-mixer-test -> prints the controls and help text\n"
                "                       audio-mixer-test -p -> prints only the controls\n"
-               "  -v, --set-volume     set volume level for volume controls(all controls except bass and treble)\n"
-               "                       Examples\n"
-               "                       audio-mixer-test -v 0.2       -> sets volume of the 1st control with 0.2\n"
-               "                       audio-mixer-test -i 9 -v 0.2  -> sets volume of the 9th control with 0.2\n"
-               "  -g, --set-gain       gain level for gain controls like bass and treble\n"
-               "                       Examples\n"
-               "                       audio-mixer-test -i 11 -g 0.8 -> sets gain of the 11th control with 0.8\n"
+               "  -v, --set-volume     set volume level for a volume control(all controls except bass and treble) this option sets\n"
+               "                       volume for all the channels, for setting channel specific volumes check -l or -r options\n"
+               "                       Examples:\n"
+               "                       audio-mixer-test -v 0.2                   -> sets volume(for all channels) of the 1st control(default id)\n"
+               "                                                                    with 0.2\n"
+               "                       audio-mixer-test -i 9 -v 0.2              -> sets volume of the 9th control with 0.2\n"
+               "  -l, --left-chan-vol  set left channel volume for a volume control(all controls except bass and treble)\n"
+               "  -r, --right-chan-vol set right channel volume for a volume control(all controls except bass and treble)\n"
+               "                       only stereo channels are supported for now, and vol update on both the channels is required\n"
+               "                       Examples:\n"
+               "                       audio-mixer-test -l 0.2                   -> gives an error as only one channel is updated\n"
+               "                       audio-mixer-test -l 0.1 -r 0.2            -> sets left channel volume of 0.1 and right channel volume of\n"
+               "                                                                    0.2 on the 1st control (default id) with 0.2\n"
+               "                       audio-mixer-test -i 9 -l 0.1 -r 0.2       -> sets left channel volume of 0.1 and right channel volume\n"
+               "                                                                    of 0.2 on the 9th control with 0.2\n"
+               "  -g, --set-gain       set gain level for gain controls like bass and treble\n"
+               "                       Examples:\n"
+               "                       audio-mixer-test -i 12 -g 0.8             -> sets gain of the 11th control with 0.8\n"
                "  -m, --set-mute       mute/unmute volume controls(all controls except bass and treble) takes no arguments\n"
-               "                       Examples\n"
-               "                       audio-mixer-test -m           -> mutes the 1st control\n"
-               "                       audio-mixer-test -m           -> unmutes the 1st control, if it is issued after the above command\n"
-               "                       audio-mixer-test -i 9 -m      -> mutes 9th control (Multimedia) with 0.8\n");
+               "                       Examples:\n"
+               "                       audio-mixer-test -m                       -> mutes the 1st control (default id)\n"
+               "                       audio-mixer-test -m                       -> unmutes the 1st control (default id), if it is issued after\n"
+               "                                                                    the above command\n"
+               "                       audio-mixer-test -i 9 -m                  -> mutes 9th control (Multimedia) with 0.8\n");
 }
 
 static void
@@ -192,8 +249,10 @@ main (gint argc, gchar **argv)
        struct audiomixer_events audiomixer_events = { 0 };
 
        gint id = -1;
-       double vol = 0.0;
+       double vol = 0.0, lvol = 0.0, rvol = 0.0;
        gfloat gain = 0.0;
+       gboolean set_right_channel_volume = FALSE;
+       gboolean set_left_channel_volume = FALSE;
 
        self.loop = g_main_loop_new (NULL, FALSE);
 
@@ -245,13 +304,15 @@ main (gint argc, gchar **argv)
                { "help",                                               no_argument,                            NULL, 'h' },
                { "print-controls",     no_argument,                            NULL, 'p' },
                { "id",                                                 required_argument,      NULL, 'i' },
+               { "left-chan-vol",      required_argument,      NULL, 'l' },
+               { "right-chan-vol",     required_argument,      NULL, 'r' },
                { "set-volume",                 required_argument,      NULL, 'v' },
                { "set-mute",                           no_argument,                            NULL, 'm' },
                { "set-gain",                           required_argument,      NULL, 'g' },
                { NULL, 0, NULL, 0}
        };
 
-       while ((c = getopt_long (argc, argv, "hpi:v:mg:", long_options, NULL)) != -1) {
+       while ((c = getopt_long (argc, argv, "hpi:v:mg:l:r:", long_options, NULL)) != -1) {
                switch(c) {
                case 'h':
                        show_help ();
@@ -285,6 +346,26 @@ main (gint argc, gchar **argv)
 
                        break;
 
+               case 'l':
+                       if (id == -1) {
+                               g_warning ("control id not given defaulting it to 0(Master Playback)");
+                               id = 0;
+                       }
+
+                       lvol = (double)atof (optarg);
+                       set_left_channel_volume = TRUE;
+                       break;
+
+               case 'r':
+                       if (id == -1) {
+                               g_warning ("control id not given defaulting it to 0(Master Playback)");
+                               id = 0;
+                       }
+
+                       rvol = (double)atof (optarg);
+                       set_right_channel_volume = TRUE;
+                       break;
+
                case 'm':
                        if (id == -1) {
                                g_warning ("control id not given defaulting it to 0(Master Playback)");
@@ -322,6 +403,18 @@ main (gint argc, gchar **argv)
                }
        }
 
+       if (set_left_channel_volume && set_right_channel_volume) {
+               ret = set_channels_volume (&self, id, lvol, rvol);
+               if (ret != TRUE)
+                       g_warning ("set_channels_volume failed");
+               else
+                       /* wait for volume to be acked */
+                       g_main_loop_run (self.loop);
+       }
+       else if (set_left_channel_volume || set_right_channel_volume) {
+               g_warning ("set volume of both left and right channels");
+       }
+
 exit:
        /* clean up at program exit */
        audio_mixer_clear (&self);