From 3a3017b46f227945d0d9328be776c95509e1ffb8 Mon Sep 17 00:00:00 2001 From: Tim LaBerge Date: Sun, 16 Dec 2012 09:29:16 -0800 Subject: [PATCH] Add support for linkable parameters. These changes allow gdigi to link parameters to the expression pedal, LFO1, and LFO2. The changes have been implemented only for RP355, but should be easily extendible to other devices. The design is as follows: The effect group entries for the linkable effects have only a placeholder entry "None" at startup. During startup, we send an unsolicited REQUEST_MODIFIER_LINKABLE_LIST. When we receive RECEIVE_MODIFIER_LINKABLE_LIST in response, we use it to construct the ModifierLinkableList and update the combo boxes for the Pedal Assign and LFO effects. Whenever the selected chorus/fx effect changes, the list of linkable parameters also changes, so the device will send us a NOTIFY_MODIFIER_GROUP_CHANGED. Again, we send a REQUEST_MODIFIER_LINKABLE_LIST and use the response to rebuild ModifierLinkableList. Then we tear down and rebuild the state associated with the affected combo boxes. The changes made are as follows: 1) Add settings for pedal1_assign. This is the min/max value returned by the pedal. 2) Add separate EffectGroup for both pedal assignment and the LFO's for the non-combo box settings. 3) Fix up a few more missing effects in the xml and modifier lists. There is additional work to do here, but that will be in future commits. 4) Fixed a missing break in the RECEIVE_DEVICE_NOTIFICATION case in push_message(). 5) When we receive a NOTIFY_MODIFIER_GROUP_CHANGED message, free the exisiting modifier group and send a REQUEST_MODIFIER_LINKABLE_LIST so we can rebuild it. 6) When we receive a RECEIVE_MODIFIER_LINKABLE_LIST, protect the calls to create_modifier_group() with GDK_THREADS_ENTER() so we don't crash because of interactions with the event/gui threads. 7) Added a BUG assert to an error leg in get_message_list(). 8) Fix memory leak in get_digitech_devices() found by valgrind. 9) Add a function to tear down the state associated with the entries of a pedal assign or LFO combo box. 10) Add a function to build up the entries of a pedal assign or LFO combo box from the modifier linkable list received from the device. 11) Use gtk_combo_box_text_append_text() throughout. 12) A few whitespace/readability changes and more comments. --- effects.c | 24 +++++++-- gdigi.c | 18 +++++++ gui.c | 157 ++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 143 insertions(+), 56 deletions(-) diff --git a/effects.c b/effects.c index e54b617..c93a0da 100644 --- a/effects.c +++ b/effects.c @@ -1579,6 +1579,11 @@ static EffectSettings gnx3k_reverb_settings[] = { {"Level", REVERB_LEVEL, REVERB_POSITION, &values_0_to_99}, }; +static EffectSettings pedal1_assign_settings[] = { + {"Pedal Min 1", EXP_MIN, EXP_POSITION, &values_0_to_99,}, + {"Pedal Max 1", EXP_MAX, EXP_POSITION, &values_0_to_99,}, +}; + static EffectSettings lfo1_settings[] = { {"Heel", LFO_MIN, LFO1_POSITION, &values_0_to_99}, {"Toe", LFO_MAX, LFO1_POSITION, &values_0_to_99}, @@ -1953,16 +1958,19 @@ static EffectGroup rp355_chorusfx_group[] = { }; static EffectGroup rp355_pedal1_assign_group[] = { - { 0, "Placeholder", NULL, 0}, - }; + { 0, NULL, pedal1_assign_settings, G_N_ELEMENTS(pedal1_assign_settings)}, + { 0, "None", NULL, 0}, +}; static EffectGroup rp355_lfo2_group[] = { - { 0, "Placeholder", lfo2_settings, G_N_ELEMENTS(lfo2_settings)}, + { 0, NULL, lfo2_settings, G_N_ELEMENTS(lfo2_settings)}, + { 0, "None", NULL, 0}, }; static EffectGroup rp355_lfo1_group[] = { - { 0, "Placeholder", lfo1_settings, G_N_ELEMENTS(lfo1_settings)}, - }; + { 0, NULL, lfo1_settings, G_N_ELEMENTS(lfo1_settings)}, + { 0, "None", NULL, 0}, +}; static EffectGroup rp500_chorusfx_group[] = { {CHORUS_TYPE_CE, "CE Chorus", chorusfx_ce_settings, G_N_ELEMENTS(chorusfx_ce_settings)}, @@ -3362,6 +3370,11 @@ static Modifier modifiers[] = { {"Phaser Regen", PHASER_REGEN, CHORUSFX_POSITION, &values_0_to_99}, {"Phaser Waveform", PHASER_WAVE, CHORUSFX_POSITION, &values_waveform}, {"Phaser Level", PHASER_LEVEL, CHORUSFX_POSITION, &values_0_to_99}, + {"Phaser Intensity", MX_PHASER_INTENSITY, CHORUSFX_POSITION, &values_0_to_99}, + {"Trig Phaser Speed", TRIG_PHASER_SPEED, CHORUSFX_POSITION, &values_0_to_99}, + {"Trig Phaser Sens", TRIG_PHASER_SENS, CHORUSFX_POSITION, &values_0_to_99}, + {"Trig Phaser LFO", TRIG_PHASER_LFO_START, CHORUSFX_POSITION, &values_0_to_99}, + {"Trig Phaser Level", TRIG_PHASER_LEVEL, CHORUSFX_POSITION, &values_0_to_99}, {"Chorus Speed", CHORUS_SPEED, CHORUSFX_POSITION, &values_0_to_99}, {"Chorus Depth", CHORUS_DEPTH, CHORUSFX_POSITION, &values_0_to_99}, {"Chorus Level", CHORUS_LEVEL, CHORUSFX_POSITION, &values_0_to_99}, @@ -3972,6 +3985,7 @@ XmlSettings xml_settings[] = { {PHASER_REGEN, CHORUSFX_POSITION, "Phaser Regen", &values_0_to_99,}, {PHASER_WAVE, CHORUSFX_POSITION, "Phaser Waveform", &values_waveform, xml_waveform_labels, G_N_ELEMENTS(xml_waveform_labels)}, {PHASER_LEVEL, CHORUSFX_POSITION, "Phaser Level", &values_0_to_99,}, + {MX_PHASER_INTENSITY, CHORUSFX_POSITION, "Intensity", &values_1_to_4,}, {CHORUS_SPEED, CHORUSFX_POSITION, "Chorus Speed", &values_0_to_99,}, {CHORUS_DEPTH, CHORUSFX_POSITION, "Chorus Depth", &values_0_to_99,}, {CHORUS_LEVEL, CHORUSFX_POSITION, "Chorus Level", &values_0_to_99,}, diff --git a/gdigi.c b/gdigi.c index dc1ff24..77ac66d 100644 --- a/gdigi.c +++ b/gdigi.c @@ -462,6 +462,8 @@ void push_message (GString *msg) str[9], str[10], str[11], str[12]); } + break; + case NOTIFY_MODIFIER_GROUP_CHANGED: { int i; @@ -472,10 +474,18 @@ void push_message (GString *msg) } printf("\n"); } + debug_msg(DEBUG_MSG2HOST, "NOTIFY_MODIFIER_GROUP_CHANGED: Modifier group " "id %d changed", (str[9] << 8) | (str[10])); + + if (ModifierLinkableList) { + modifier_group_free(ModifierLinkableList); + ModifierLinkableList = NULL; + } + + send_message(REQUEST_MODIFIER_LINKABLE_LIST, "\x00\x01", 2); break; } default: @@ -526,9 +536,14 @@ void push_message (GString *msg) g_string_free(msg, TRUE); + GDK_THREADS_ENTER(); + create_modifier_group(EXP_POSITION, EXP_ASSIGN1); create_modifier_group(LFO1_POSITION, LFO_TYPE); create_modifier_group(LFO2_POSITION, LFO_TYPE); + + GDK_THREADS_LEAVE(); + return; @@ -1130,6 +1145,7 @@ GList *get_message_list(MessageID id) g_error("get_message_list() doesn't support followning id: %d", id); g_string_free(data, TRUE); g_list_free(list); + g_assert(!"BUG"); return NULL; } @@ -1420,6 +1436,7 @@ static gint get_digitech_devices(GList **devices) number++; *devices = g_list_append(*devices, GINT_TO_POINTER(card_num)); } + free(name); snd_card_next(&card_num); } @@ -1485,6 +1502,7 @@ int main(int argc, char *argv[]) { } if (device != NULL) { + /* enable GUI mode */ set_option(GUI_MODE_ON_OFF, USB_POSITION, 1); diff --git a/gui.c b/gui.c index a508599..a829942 100644 --- a/gui.c +++ b/gui.c @@ -563,6 +563,7 @@ void combo_box_changed_cb(GtkComboBox *widget, gpointer data) EffectSettingsGroup *settings = NULL; gchar *name = NULL; gint x; + g_object_get(G_OBJECT(widget), "active", &x, NULL); vbox = g_object_get_data(G_OBJECT(widget), "vbox"); @@ -625,15 +626,22 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p g_signal_connect(G_OBJECT(combo_box), "changed", G_CALLBACK(combo_box_changed_cb), group); g_object_set_data(G_OBJECT(combo_box), "vbox", vbox); } - gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), - NULL, group[x].label); + + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), group[x].label); cmbox_no++; if ((group[x].settings != NULL) && (group[x].settings_amt > 0)) { - widget = create_grid(group[x].settings, group[x].settings_amt, widget_table); + /* + * Create a grid for each combo box entry to contain per + * combo box entry settings. + */ + widget = create_grid(group[x].settings, + group[x].settings_amt, + widget_table); g_object_ref_sink(widget); - } else + } else { widget = NULL; + } settings = g_slice_new(EffectSettingsGroup); settings->id = id; @@ -644,11 +652,15 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p widget_tree_add(G_OBJECT(combo_box), id, position, group[x].type, x); name = g_strdup_printf("SettingsGroup%d", cmbox_no); - g_object_set_data_full(G_OBJECT(combo_box), name, settings, ((GDestroyNotify)effect_settings_group_free)); + g_object_set_data_full(G_OBJECT(combo_box), + name, settings, + ((GDestroyNotify)effect_settings_group_free)); g_free(name); } else { if ((group[x].settings != NULL) && (group[x].settings_amt > 0)) { - widget = create_grid(group[x].settings, group[x].settings_amt, widget_table); + widget = create_grid(group[x].settings, + group[x].settings_amt, + widget_table); gtk_box_pack_end(GTK_BOX(vbox), widget, FALSE, TRUE, 0); } } @@ -660,7 +672,67 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p } /** - * Given a linkable effect, build the combo box for the linkable parameters. + * Populate a combo box with text entries from the modifier group. + */ +void update_modifier_combo_box(GObject *combo_box, EffectGroup *group, gint amt, gint id, gint position) +{ + gint x; + EffectSettingsGroup *settings = NULL; + + for (x = 0; xid = id; + settings->type = group[x].type; + settings->position = position; + settings->child = NULL; + + name = g_strdup_printf("SettingsGroup%d", x); + g_object_set_data(G_OBJECT(combo_box), name, settings); + g_free(name); + + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), group[x].label); + widget_tree_add(combo_box, id, position, group[x].type, x); + } + + return; +} + +static void widget_tree_elem_free(GList *); + +static void clean_modifier_combo_box (GObject *ComboBox, GList *list) +{ + EffectSettingsGroup *settings = NULL; + WidgetTreeElem *el; + gchar *name; + GList *link, *next; + + link = g_list_first(list); + + while (link != NULL) { + next = link->next; + el = link->data; + if (el->value != -1) { + /* Useless assignment, but silences compiler warning. */ + link = g_list_remove_link(list, link); + + g_assert(ComboBox == el->widget); + name = g_strdup_printf("SettingsGroup%d", el->x); + settings = g_object_steal_data(G_OBJECT(ComboBox), name); + + g_free(name); + g_slice_free(EffectSettingsGroup, settings); + g_slice_free(WidgetTreeElem, el); + } + link = next; + } + gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(ComboBox)); +} + +/** + * Given a linkable effect, update the combo box for the linkable parameters. * * @param[in] pos Position * @param[in] id Id @@ -668,27 +740,30 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p void create_modifier_group (guint pos, guint id) { - guint i; + + GtkWidget *vbox; gpointer key; WidgetTreeElem *el; GList *list; - EffectSettingsGroup *settings = NULL, *orig_settings = NULL; GObject *AssignComboBox; - GtkWidget *child_widget = NULL; - gchar *name = NULL; debug_msg(DEBUG_GROUP, "Building modifier group for position %d id %d \"%s\"", pos, id, get_xml_settings(id, pos)->label); key = GINT_TO_POINTER((pos << 16) | id); list = g_tree_lookup(widget_tree, key); + + /* + * The list will be destroyed and recreated, but we don't want to + * handle the teardown ourselves. So steal it from the tree. + */ + g_tree_steal(widget_tree, key); if (!list) { g_warning("No widget tree entry for position %d id %d!\n", pos, id); return; } - /* The only element should be the one with the placeholder. */ el = g_list_nth_data(list, 0); if (!el) { g_warning("No effect settings group for position %d id %d!\n", @@ -697,46 +772,18 @@ create_modifier_group (guint pos, guint id) } AssignComboBox = el->widget; + g_assert(AssignComboBox != NULL); - name = g_strdup_printf("SettingsGroup%d", 0); - orig_settings = g_object_get_data(G_OBJECT(AssignComboBox), name); - if (orig_settings) { - child_widget = orig_settings->child; - /* Steal the data so we don't trigger the destroy method on the grid. */ - g_object_steal_data(AssignComboBox, name); - } - /* Remove the placeholder. */ - gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(AssignComboBox), 0); + vbox = g_object_get_data(AssignComboBox, "vbox"); + g_assert(vbox != NULL); + clean_modifier_combo_box(AssignComboBox, list); - for (i = 0; i < ModifierLinkableList->group_amt; i++) { - gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(AssignComboBox), - NULL, - ModifierLinkableList->group[i].label); + update_modifier_combo_box(AssignComboBox, + ModifierLinkableList->group, + ModifierLinkableList->group_amt, + id, pos); - settings = g_slice_new(EffectSettingsGroup); - settings->type = ModifierLinkableList->group[i].type; - settings->id = id; - settings->position = pos; - settings->child = NULL; - if (child_widget) { - settings->child = child_widget; - g_object_ref_sink(settings->child); - } - - name = g_strdup_printf("SettingsGroup%d", i); - - debug_msg(DEBUG_GROUP, "%d: \"%s\"", - i, - ModifierLinkableList->group[i].label); - - widget_tree_add(G_OBJECT(AssignComboBox), id, pos, - ModifierLinkableList->group[i].type, i); - g_object_set_data_full(G_OBJECT(AssignComboBox), name, settings, - ((GDestroyNotify)effect_settings_group_free)); - } - - // Get the current setting. get_option(id, pos); } @@ -769,6 +816,7 @@ GtkWidget *create_vbox(Effect *widgets, gint amt, gchar *label) for (x = 0; x