From 5567e12699dcc6584a620d2682430e3882dc0907 Mon Sep 17 00:00:00 2001 From: Ashok Sidipotu Date: Thu, 14 Dec 2023 16:19:21 +0530 Subject: audiomixer: add new channel volume API - 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 --- src/audiomixer.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++----- src/audiomixer.h | 7 +++ src/audiomixertest.c | 131 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 250 insertions(+), 31 deletions(-) diff --git a/src/audiomixer.c b/src/audiomixer.c index 0351738..19b8bf1 100644 --- a/src/audiomixer.c +++ b/src/audiomixer.c @@ -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); } diff --git a/src/audiomixer.h b/src/audiomixer.h index 2e50420..1f089d1 100644 --- a/src/audiomixer.h +++ b/src/audiomixer.h @@ -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); diff --git a/src/audiomixertest.c b/src/audiomixertest.c index ddb7ac9..641fe70 100644 --- a/src/audiomixertest.c +++ b/src/audiomixertest.c @@ -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); -- cgit 1.2.3-korg