From 8c06e7aff741185cc72407e586365c1a11cccf9b Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Fri, 9 Feb 2024 09:30:14 +1030 Subject: [PATCH] Update the gain widget to support updating direct monitor mix controls The 4th Gen Solo and 2i2 have controls to set custom Mix A/B gains when Direct Monitor is enabled. Update those controls when the Mix A/B gains are updated so that they are remembered. --- src/widget-gain.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/src/widget-gain.c b/src/widget-gain.c index 375f1a9..b799078 100644 --- a/src/widget-gain.c +++ b/src/widget-gain.c @@ -2,10 +2,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "gtkdial.h" +#include "stringhelper.h" #include "widget-gain.h" struct gain { struct alsa_elem *elem; + struct alsa_elem *direct_monitor_elem; + struct alsa_elem *monitor_mix_elem[2]; GtkWidget *vbox; GtkWidget *dial; GtkWidget *label; @@ -15,8 +18,28 @@ struct gain { static void gain_changed(GtkWidget *widget, struct gain *data) { int value = gtk_dial_get_value(GTK_DIAL(data->dial)); - alsa_set_elem_value(data->elem, value); + + // check if there is a corresponding Direct Monitor Mix control to + // update as well + + // Direct Monitor control? + if (!data->direct_monitor_elem) + return; + + // Direct Monitor enabled? + int direct_monitor = alsa_get_elem_value(data->direct_monitor_elem); + + if (!direct_monitor) + return; + + // Get the corresponding Mix control + struct alsa_elem *monitor_mix = data->monitor_mix_elem[direct_monitor - 1]; + if (!monitor_mix) + return; + + // Update it + alsa_set_elem_value(monitor_mix, value); } static void gain_updated( @@ -56,6 +79,70 @@ static void gain_updated( gtk_label_set_text(GTK_LABEL(data->label), s); } +// 4th Gen Solo and 2i2 have Mix & Direct Monitor controls which +// interact. If direct monitor is enabled and the Mix A/B controls are +// changed, then the Monitor Mix Playback Volume controls are changed +// too so that the mix settings are restored when direct monitor is +// later enabled again. +static void find_direct_monitor_controls(struct gain *data) { + struct alsa_elem *elem = data->elem; + GArray *elems = elem->card->elems; + + // Card has no direct monitor control? + struct alsa_elem *direct_monitor_elem = get_elem_by_prefix( + elems, + "Direct Monitor Playback" + ); + if (!direct_monitor_elem) + return; + + // Card has no mixer? + if (strncmp(elem->name, "Mix ", 4) != 0 || + !strstr(elem->name, "Playback Volume")) + return; + + char mix_letter = elem->name[4]; + int input_num = get_num_from_string(elem->name); + + // Find the Monitor Mix control for the 4th Gen Solo + if (strstr(direct_monitor_elem->name, "Switch")) { + char s[80]; + sprintf( + s, + "Monitor Mix %c Input %02d Playback Volume", + mix_letter, input_num + ); + + struct alsa_elem *monitor_mix_elem = get_elem_by_name(elems, s); + if (!monitor_mix_elem) + return; + + data->direct_monitor_elem = direct_monitor_elem; + data->monitor_mix_elem[0] = monitor_mix_elem; + + // Find the Monitor Mix controls for the 4th Gen 2i2 + } else if (strstr(direct_monitor_elem->name, "Enum")) { + for (int i = 0; i <= 1; i++) { + char s[80]; + sprintf( + s, + "Monitor %d Mix %c Input %02d Playback Volume", + i + 1, mix_letter, input_num + ); + + struct alsa_elem *monitor_mix_elem = get_elem_by_name(elems, s); + if (!monitor_mix_elem) + return; + + data->direct_monitor_elem = direct_monitor_elem; + data->monitor_mix_elem[i] = monitor_mix_elem; + } + + } else { + fprintf(stderr, "Couldn't find direct monitor mix control\n"); + } +} + //GList *make_gain_alsa_elem(struct alsa_elem *elem) { GtkWidget *make_gain_alsa_elem( struct alsa_elem *elem, @@ -63,7 +150,7 @@ GtkWidget *make_gain_alsa_elem( int widget_taper, int can_control ) { - struct gain *data = g_malloc(sizeof(struct gain)); + struct gain *data = calloc(1, sizeof(struct gain)); data->elem = elem; data->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_set_hexpand(data->vbox, TRUE); @@ -110,6 +197,8 @@ GtkWidget *make_gain_alsa_elem( data->zero_is_off = zero_is_off; + find_direct_monitor_controls(data); + g_signal_connect( data->dial, "value-changed", G_CALLBACK(gain_changed), data );