/*================================================================== * SwamiUIMidiCtrl.c - MIDI control interface object * * Swami * Copyright (C) 1999-2003 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net *==================================================================*/ #include #include #include #include #include "SwamiUIMidiCtrl.h" #include "SwamiUIObject.h" /* for swamiui_object */ #include "SwamiUISpanWin.h" /* for setting piano octave */ #include "glade_interface.h" #include "i18n.h" #include "util.h" #define MIDICTRL_VAL(midictrl, index) \ (((int *)midictrl->vals->data)[16 * index + midictrl->chan]) #define MIDICTRL_VAL_ARRAY(midictrl, index) \ (&((int *)midictrl->vals->data)[16 * index]) #define MIDICTRL_PARAMS(index) \ (&g_array_index (midictrl_class->params, MidiCtrlParams, index)) #define MIDICTRL_GET_ADJ_INDEX(adj) \ (GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (adj), \ "_ctrl_index")) - 1) #define MIDICTRL_SET_ADJ_INDEX(adj, index) \ gtk_object_set_data (GTK_OBJECT (adj), "_ctrl_index", \ GINT_TO_POINTER (index + 1)) /* Swami MIDI control parameters */ typedef struct { char *key; /* key identifier for set/get by name */ char *name; /* name of MIDI control */ int low; /* lowest value */ int high; /* highest value */ int def; /* default */ /* func to set control */ SwamiUIMidiCtrlFunc func; /* function to set value for control */ gpointer data; /* custom parameter to pass to func */ } MidiCtrlParams; /* function prototypes */ static void swamiui_midictrl_class_init (SwamiUIMidiCtrlClass *klass); static void swamiui_midictrl_init (SwamiUIMidiCtrl *midictrl); static void real_adjustment_init (SwamiUIMidiCtrl *midictrl, GtkAdjustment *adj, int index); static void cb_midictrl_selection_done (GtkWidget *menu, gpointer data); static void cb_midictrl_adj_changed (GtkWidget *adj, gpointer data); static int key_to_index (const char *key); static void send_midi_event (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_piano_octave (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_piano_velocity (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_chan (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_bank (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_preset (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_CC (SwamiUIMidiCtrl *midictrl, int ctrl_index, void *data); static void set_bend_range (SwamiUIMidiCtrl *midictrl, int ctrl_index); static void set_pitch_wheel (SwamiUIMidiCtrl *midictrl, int ctrl_index); /* keep up to date with enums above */ static MidiCtrlParams midictrl_params[] = { { "chan", NULL, 1, 16, 1, (SwamiUIMidiCtrlFunc)set_chan, NULL }, { "bank", NULL, 0, 127, 0, (SwamiUIMidiCtrlFunc)set_bank, NULL }, { "preset", NULL, 0, 127, 0, (SwamiUIMidiCtrlFunc)set_preset, NULL}, { "volume", N_("Volume"), 0, 127, 100, set_CC, GINT_TO_POINTER (SWAMI_MIDI_CC_VOLUME) }, { "reverb", N_("Reverb"), 0, 127, 0, set_CC, GINT_TO_POINTER (SWAMI_MIDI_CC_REVERB) }, { "chorus", N_("Chorus"), 0, 127, 0, set_CC, GINT_TO_POINTER (SWAMI_MIDI_CC_CHORUS) }, { "octave", N_("Octave"), 0, 10, 3, (SwamiUIMidiCtrlFunc)set_piano_octave, NULL }, { "velocity", N_("Velocity"), 0, 127, 127, (SwamiUIMidiCtrlFunc)set_piano_velocity, NULL }, { "bend_range", N_("Bend Range"), 0, 127, 2, (SwamiUIMidiCtrlFunc)set_bend_range, NULL }, { "pitch", N_("Pitch Wheel"), 0, 0x3FFF, 0x2000, (SwamiUIMidiCtrlFunc)set_pitch_wheel, NULL } }; #define MIDICTRL_COUNT (sizeof (midictrl_params) / sizeof (MidiCtrlParams)) SwamiUIMidiCtrlClass *midictrl_class = NULL; guint swamiui_midictrl_get_type (void) { static guint obj_type = 0; if (!obj_type) { GtkTypeInfo obj_info = { "SwamiUIMidiCtrl", sizeof (SwamiUIMidiCtrl), sizeof (SwamiUIMidiCtrlClass), (GtkClassInitFunc) swamiui_midictrl_class_init, (GtkObjectInitFunc) swamiui_midictrl_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; obj_type = gtk_type_unique (gtk_vbox_get_type (), &obj_info); } return obj_type; } static void swamiui_midictrl_class_init (SwamiUIMidiCtrlClass *klass) { MidiCtrlParams *p; int i; midictrl_class = klass; midictrl_class->params = g_array_new (FALSE, FALSE, sizeof (MidiCtrlParams)); /* add the controls */ for (i = 0; i < MIDICTRL_COUNT; i++) { p = &midictrl_params[i]; swamiui_midictrl_add_control (p->key, p->name, p->low, p->high, p->def, p->func, p->data); } } static void swamiui_midictrl_init (SwamiUIMidiCtrl *midictrl) { GtkWidget *glade_MidiCtrl; GtkWidget *widg; GtkWidget *opt; GtkWidget *menu; GtkWidget *item; GtkObject *adj; GtkAdjustment *radj; int chan, i; int def; char *s; midictrl->midi = NULL; midictrl->chan = 0; midictrl->adj_list = NULL; midictrl->block_adj_changed = FALSE; /* create control value array */ midictrl->vals = g_array_new (FALSE, FALSE, sizeof (int) * 16); g_array_set_size (midictrl->vals, midictrl_class->params->len); /* initialize the control values */ for (i = 0; i < midictrl_class->params->len; i++) { def = MIDICTRL_PARAMS (i)->def; for (chan = 0; chan < 16; chan++) MIDICTRL_VAL_ARRAY (midictrl, i)[chan] = def; } /* hack the guts out of the MidiCtrl (i.e. hack around glade dialogs) */ widg = create_glade_MidiCtrl (); glade_MidiCtrl = swamiui_util_rip_guts (widg, "glade_MidiCtrl"); gtk_box_pack_start (GTK_BOX (midictrl), glade_MidiCtrl, TRUE, TRUE, 0); midictrl->glade_widg = glade_MidiCtrl; /* set a reference for the midictrl object */ gtk_object_ref (GTK_OBJECT (midictrl)); gtk_object_set_data (GTK_OBJECT (glade_MidiCtrl), "midictrl", midictrl); opt = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "OpMenuCtrls"); gtk_option_menu_remove_menu (GTK_OPTION_MENU (opt)); menu = gtk_menu_new (); for (i = 0; i < midictrl_class->params->len; i++) { /* create menu item */ s = MIDICTRL_PARAMS (i)->name; if (s) { item = gtk_menu_item_new_with_label (_(s)); gtk_object_set_data (GTK_OBJECT (item), "key", MIDICTRL_PARAMS (i)->key); gtk_widget_show (item); gtk_menu_append (GTK_MENU (menu), item); } } gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); gtk_option_menu_set_history (GTK_OPTION_MENU (opt), 0); adj = gtk_adjustment_new (100.0, 0.0, 127.0, 1.0, 10.0, 0.0); /* connect menu selection done to change adjustment control */ gtk_signal_connect (GTK_OBJECT (menu), "selection-done", GTK_SIGNAL_FUNC (cb_midictrl_selection_done), adj); widg = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "SpinBtnCtrlVal"); gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (widg), GTK_ADJUSTMENT (adj)); widg = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "HScaleCtrlVal"); gtk_range_set_adjustment (GTK_RANGE (widg), GTK_ADJUSTMENT (adj)); swamiui_midictrl_adjustment_register (midictrl, GTK_ADJUSTMENT (adj), "volume"); widg = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "SpinBtnChan"); radj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widg)); swamiui_midictrl_adjustment_register (midictrl, radj, "chan"); widg = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "SpinBtnBank"); radj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widg)); swamiui_midictrl_adjustment_register (midictrl, radj, "bank"); widg = gtk_object_get_data (GTK_OBJECT (glade_MidiCtrl), "SpinBtnPreset"); radj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widg)); swamiui_midictrl_adjustment_register (midictrl, radj, "preset"); } /** * Create a new MIDI control object * Returns: The new MIDI control object widget */ GtkWidget * swamiui_midictrl_new (void) { return (GTK_WIDGET (gtk_type_new (swamiui_midictrl_get_type ()))); } /** * Set MIDI driver of a MIDI control object * @midictrl Swami MIDI control object * @midi MIDI driver to use or NULL to disable */ void swamiui_midictrl_set_midi_driver (SwamiUIMidiCtrl *midictrl, SwamiMidi *midi) { g_return_if_fail (midictrl != NULL); g_return_if_fail (SWAMIUI_IS_MIDICTRL (midictrl)); g_return_if_fail (!midi || SWAMI_IS_MIDI (midi)); midictrl->midi = midi; } /** * Add a MIDI control to the MIDI control class * @key String key identifier * @name String description of control * @low Lowest allowable value * @high Highest allowable value * @def Default value * @func Function used to set control value * @data Extra parameter to pass to function */ void swamiui_midictrl_add_control (char *key, char *name, int low, int high, int def, SwamiUIMidiCtrlFunc func, gpointer data) { MidiCtrlParams params; params.key = g_strdup (key); params.name = g_strdup (name); params.low = low; params.high = high; params.def = def; params.func = func; params.data = data; g_array_append_val (midictrl_class->params, params); } /** * One time register of a new GtkAdjustment control * @midictrl MIDI control object * @adj Adjustment widget to register and initialize * @key Control key identifier to initialize adjustment to */ void swamiui_midictrl_adjustment_register (SwamiUIMidiCtrl *midictrl, GtkAdjustment *adj, const char *key) { g_return_if_fail (midictrl != NULL); g_return_if_fail (SWAMIUI_IS_MIDICTRL (midictrl)); g_return_if_fail (adj != NULL); g_return_if_fail (GTK_IS_ADJUSTMENT (adj)); midictrl->adj_list = g_list_append (midictrl->adj_list, adj); /* connect adjustment to handler */ gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (cb_midictrl_adj_changed), midictrl); swamiui_midictrl_adjustment_init (midictrl, adj, key); } /** * Initialize an adjustment from a control * @midictrl MIDI control object * @adj Adjustment widget to initialize * @key Control key identifier to initialize adjustment to */ void swamiui_midictrl_adjustment_init (SwamiUIMidiCtrl *midictrl, GtkAdjustment *adj, const char *key) { int index; g_return_if_fail (adj != NULL); g_return_if_fail (GTK_IS_ADJUSTMENT (adj)); if (!key || (index = key_to_index (key)) < 0) { MIDICTRL_SET_ADJ_INDEX (adj, -1); return; } real_adjustment_init (midictrl, adj, index); } static void real_adjustment_init (SwamiUIMidiCtrl *midictrl, GtkAdjustment *adj, int index) { MidiCtrlParams *params; MIDICTRL_SET_ADJ_INDEX (adj, index); params = MIDICTRL_PARAMS (index); adj->lower = (float)params->low; adj->upper = (float)params->high; adj->value = (float)MIDICTRL_VAL (midictrl, index); midictrl->block_adj_changed = TRUE; gtk_adjustment_changed (adj); gtk_adjustment_value_changed (adj); midictrl->block_adj_changed = FALSE; } /** * Update an adjustment to its MIDI control * @midictrl MIDI control object * @adj Adjustment to update */ void swamiui_midictrl_adjustment_update (SwamiUIMidiCtrl *midictrl, GtkAdjustment *adj) { int index; g_return_if_fail (midictrl != NULL); g_return_if_fail (SWAMIUI_IS_MIDICTRL (midictrl)); g_return_if_fail (adj != NULL); g_return_if_fail (GTK_IS_ADJUSTMENT (adj)); index = MIDICTRL_GET_ADJ_INDEX (adj); if (index < 0) return; adj->value = (float)MIDICTRL_VAL (midictrl, index); gtk_adjustment_value_changed (adj); } /** * Set a MIDI control via a Swami MIDI control object * @midictrl Swami MIDI control object * @key String key ID of the control * @val Value to set control to * * Sets a MIDI control via a MIDI control object. Widget is updated, value is * set and MIDI event is sent. */ void swamiui_midictrl_set (SwamiUIMidiCtrl *midictrl, const char *key, int val) { GList *p; int index, i; g_return_if_fail (midictrl != NULL); g_return_if_fail (SWAMIUI_IS_MIDICTRL (midictrl)); g_return_if_fail (key != NULL); index = key_to_index (key); if (index < 0) { SWAMI_PARAM_ERROR ("key"); return; } MIDICTRL_VAL (midictrl, index) = val; /* set the control value */ send_midi_event (midictrl, index); /* send the MIDI event */ /* update adjustments that use this control */ p = midictrl->adj_list; while (p) { i = MIDICTRL_GET_ADJ_INDEX (p->data); if (i == index) swamiui_midictrl_adjustment_update (midictrl, GTK_ADJUSTMENT(p->data)); p = g_list_next (p); } } /** * Send values of all controls to MIDI driver * @midictrl Midi control object */ void swamiui_midictrl_midi_update_all (SwamiUIMidiCtrl *midictrl) { int i; for (i = 0; i < midictrl_class->params->len; i++) send_midi_event (midictrl, i); } static void cb_midictrl_selection_done (GtkWidget *menu, gpointer data) { GtkAdjustment *adj = GTK_ADJUSTMENT (data); SwamiUIMidiCtrl *midictrl; GtkWidget *menu_item; char *key; int index; /* get index of control for selected menu item */ menu_item = gtk_menu_get_active (GTK_MENU (menu)); key = (gchar *)gtk_object_get_data (GTK_OBJECT (menu_item), "key"); index = key_to_index (key); midictrl = SWAMIUI_MIDICTRL (swamiui_util_lookup_widget (menu, "midictrl")); real_adjustment_init (midictrl, adj, index); } static void cb_midictrl_adj_changed (GtkWidget *adj, gpointer data) { SwamiUIMidiCtrl *midictrl = SWAMIUI_MIDICTRL (data); int val, ctrl_index; if (midictrl->block_adj_changed) /* are we blocking? */ return; ctrl_index = MIDICTRL_GET_ADJ_INDEX (adj); val = (int)(GTK_ADJUSTMENT (adj)->value + 0.5); MIDICTRL_VAL (midictrl, ctrl_index) = val; /* set value */ send_midi_event (midictrl, ctrl_index); /* send MIDI event */ } /* turn a control key string into an array index */ static int key_to_index (const char *key) { int i; for (i = 0; i < midictrl_class->params->len; i++) { if (strcmp (MIDICTRL_PARAMS(i)->key, key) == 0) break; } if (i >= midictrl_class->params->len) /* found control? */ return (-1); return (i); } static void send_midi_event (SwamiUIMidiCtrl *midictrl, int ctrl_index) { MidiCtrlParams *params; params = MIDICTRL_PARAMS (ctrl_index); if (params->func) (*params->func)(midictrl, ctrl_index, params->data); } static void set_piano_octave (SwamiUIMidiCtrl *midictrl, int ctrl_index) { GtkObject *spanwin; int octave; octave = MIDICTRL_VAL (midictrl, ctrl_index); spanwin = swamiui_lookup_object ("SwamiUISpanWin"); swamiui_spanwin_piano_set_octave (SWAMIUI_SPANWIN (spanwin), octave); } static void set_piano_velocity (SwamiUIMidiCtrl *midictrl, int ctrl_index) { GtkObject *spanwin; int velocity; velocity = MIDICTRL_VAL (midictrl, ctrl_index); spanwin = swamiui_lookup_object ("SwamiUISpanWin"); swamiui_spanwin_piano_set_velocity (SWAMIUI_SPANWIN (spanwin), velocity); } static void set_chan (SwamiUIMidiCtrl *midictrl, int ctrl_index) { midictrl->chan = MIDICTRL_VAL (midictrl, ctrl_index) - 1; } static void set_bank (SwamiUIMidiCtrl *midictrl, int ctrl_index) { if (!midictrl->midi) return; swami_midi_set_bank (midictrl->midi, midictrl->chan, MIDICTRL_VAL (midictrl, ctrl_index)); } static void set_preset (SwamiUIMidiCtrl *midictrl, int ctrl_index) { if (!midictrl->midi) return; swami_midi_set_preset (midictrl->midi, midictrl->chan, MIDICTRL_VAL (midictrl, ctrl_index)); } static void set_CC (SwamiUIMidiCtrl *midictrl, int ctrl_index, void *data) { int control = GPOINTER_TO_INT (data); if (!midictrl->midi) return; swami_midi_send_event (midictrl->midi, SWAMI_MIDI_SET_CONTROL, midictrl->chan, control, MIDICTRL_VAL (midictrl, ctrl_index)); } static void set_bend_range (SwamiUIMidiCtrl *midictrl, int ctrl_index) { if (!midictrl->midi) return; swami_midi_set_bend_range (midictrl->midi, midictrl->chan, MIDICTRL_VAL (midictrl, ctrl_index)); } static void set_pitch_wheel (SwamiUIMidiCtrl *midictrl, int ctrl_index) { if (!midictrl->midi) return; swami_midi_set_pitch_wheel (midictrl->midi, midictrl->chan, MIDICTRL_VAL (midictrl, ctrl_index)); }