/*================================================================== * SwamiUISpanWin.c - User interface piano and key/velocity span routines * * 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 "SwamiUISpanWin.h" #include "SwamiUIMidiCtrl.h" #include "SwamiUIObject.h" #include "widgets/piano.h" #include "widgets/keyspan.h" #include "widgets/ptrstrip.h" /* signals */ enum { ZONE_SELECT, ZONE_UNSELECT, LAST_SIGNAL }; static void swamiui_spanwin_class_init (SwamiUISpanWinClass *klass); static void swamiui_spanwin_init (SwamiUISpanWin *spanwin); static void spanwin_cb_ptrstrip_change (GtkWidget *ptrstrip, guint ptrndx, SwamiUISpanWin *spanwin); static void spanwin_cb_zone_select (SwamiUISpanWin *spanwin, IPItem *zone); static void spanwin_cb_zone_unselect (SwamiUISpanWin *spanwin, IPItem *zone); static void spanwin_cb_span_changed (KeySpan *keyspan, SwamiUISpanWin *spanwin); static void spanwin_cb_link_adjustments (GtkAdjustment *padj, GtkAdjustment *radj); static void spanwin_cb_scrollbar_automatic (GtkAdjustment *adj, GtkWidget *scrollbar); static void spanwin_cb_piano_note_on (Piano *piano, int note, gpointer data); static void spanwin_cb_piano_note_off (Piano *piano, int note, gpointer data); static void spanwin_cb_list_select (GtkList *list, GtkWidget *widget, SwamiUISpanWin *spanwin); static void spanwin_cb_list_unselect (GtkList *list, GtkWidget *widget, SwamiUISpanWin *spanwin); static gboolean spanwin_cb_keyspan_button_press (GtkWidget *keyspan, GdkEventButton *event, GtkList *list); static gint spanwin_GCompareFunc_find_zone_litem (gconstpointer a, gconstpointer b); static IPZone *swamiui_spanwin_get_single_sel (SwamiUISpanWin *spanwin); static void swamiui_spanwin_update (SwamiUISpanWin *spanwin); static void swamiui_spanwin_update_rootkey_ptrstrip (SwamiUISpanWin *spanwin); static void swamiui_spanwin_draw_velocity_bar (SwamiUISpanWin *spanwin); /* data */ static guint spanwin_signals[LAST_SIGNAL] = {0}; /* default piano octave */ #define SWAMIUI_SPANWIN_DEFAULT_OCTAVE 3 #define SWAMIUI_SPANWIN_DEFAULT_VELOCITY 127 /* default piano velocity */ /** default virtual piano key table */ guint swamiui_spanwin_default_keytable[] = { /* lower keyboard */ GDK_z, GDK_s, GDK_x, GDK_d, GDK_c, GDK_v, GDK_g, GDK_b, GDK_h, GDK_n, GDK_j, GDK_m, GDK_comma, GDK_l, GDK_period, GDK_semicolon, GDK_slash, /* upper keyboard */ GDK_q, GDK_2, GDK_w, GDK_3, GDK_e, GDK_r, GDK_5, GDK_t, GDK_6, GDK_y, GDK_7, GDK_u, GDK_i, GDK_9, GDK_o, GDK_0, GDK_p, GDK_bracketleft, GDK_equal, GDK_bracketright }; /* piano keytable */ static guint swamiui_spanwin_keytable[SWAMIUI_SPANWIN_PIANO_TOTAL_NUMKEYS]; /* start and end colors for velocity bar gradient */ static guchar velbar_scolor[3] = { 0, 0, 0x40 }; static guchar velbar_ecolor[3] = { 0, 0, 0xFF }; guint swamiui_spanwin_get_type (void) { static guint obj_type = 0; if (!obj_type) { GtkTypeInfo obj_info = { "SwamiUISpanWin", sizeof (SwamiUISpanWin), sizeof (SwamiUISpanWinClass), (GtkClassInitFunc) swamiui_spanwin_class_init, (GtkObjectInitFunc) swamiui_spanwin_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; obj_type = gtk_type_unique (gtk_hbox_get_type (), &obj_info); } return obj_type; } static void swamiui_spanwin_class_init (SwamiUISpanWinClass *klass) { GtkObjectClass *object_class; object_class = (GtkObjectClass *)klass; spanwin_signals[ZONE_SELECT] = gtk_signal_new ("select-zone", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUISpanWinClass, zone_select), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); spanwin_signals[ZONE_UNSELECT] = gtk_signal_new ("unselect-zone", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUISpanWinClass, zone_unselect), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, spanwin_signals, LAST_SIGNAL); klass->zone_select = spanwin_cb_zone_select; klass->zone_unselect = spanwin_cb_zone_unselect; swamiui_spanwin_update_keytable (); /* load piano key table */ } static void swamiui_spanwin_init (SwamiUISpanWin *spanwin) { GtkWidget *vbox; GtkWidget *view; GtkWidget *hscrollbar, *vscrollbar; GtkAdjustment *hadj, *vadj; int i; spanwin->item = NULL; /* everything except the vertical scrollbar goes in here */ vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (spanwin), vbox, TRUE, TRUE, 0); /* horizontal scrollbar controls both viewports */ hscrollbar = gtk_hscrollbar_new (NULL); gtk_widget_show (hscrollbar); hadj = gtk_range_get_adjustment (GTK_RANGE (hscrollbar)); gtk_signal_connect (GTK_OBJECT (hadj), "changed", GTK_SIGNAL_FUNC (spanwin_cb_scrollbar_automatic), hscrollbar); /* piano viewport */ view = gtk_viewport_new (NULL, NULL); gtk_viewport_set_hadjustment (GTK_VIEWPORT (view), GTK_ADJUSTMENT (hadj)); gtk_container_border_width (GTK_CONTAINER (view), 0); gtk_viewport_set_shadow_type (GTK_VIEWPORT (view), GTK_SHADOW_OUT); gtk_widget_show (GTK_WIDGET (view)); gtk_box_pack_start (GTK_BOX (vbox), view, FALSE, FALSE, 0); spanwin->view_box = gtk_vbox_new (FALSE, 0); gtk_widget_show (GTK_WIDGET (spanwin->view_box)); gtk_container_add (GTK_CONTAINER (view), spanwin->view_box); spanwin->ptrstrip = ptrstrip_new (); gtk_widget_set_usize (spanwin->ptrstrip, PIANO_DEFAULT_SIZEX, -1); ptrstrip_new_pointer (PTRSTRIP (spanwin->ptrstrip), -1); gtk_signal_connect (GTK_OBJECT (spanwin->ptrstrip), "pointer_change", (GtkSignalFunc) spanwin_cb_ptrstrip_change, spanwin); gtk_widget_show (spanwin->ptrstrip); gtk_box_pack_start (GTK_BOX (spanwin->view_box), spanwin->ptrstrip, FALSE, FALSE, 0); /* create the piano widget */ for (i = 0; i < 128; i++) spanwin->pianokeys[i] = FALSE; spanwin->octave = SWAMIUI_SPANWIN_DEFAULT_OCTAVE; spanwin->velocity = SWAMIUI_SPANWIN_DEFAULT_VELOCITY; spanwin->midi = NULL; spanwin->piano = piano_new (spanwin->pianokeys); gtk_widget_show (spanwin->piano); gtk_signal_connect (GTK_OBJECT (spanwin->piano), "note-on", GTK_SIGNAL_FUNC (spanwin_cb_piano_note_on), spanwin); gtk_signal_connect (GTK_OBJECT (spanwin->piano), "note-off", GTK_SIGNAL_FUNC (spanwin_cb_piano_note_off), spanwin); gtk_box_pack_start (GTK_BOX (spanwin->view_box), spanwin->piano, FALSE, FALSE, 0); /* create box to pack preview in so it stays left justified */ spanwin->velbar_box = gtk_hbox_new (FALSE, 0); /* don't show velocity bar box since it alternates with the piano */ gtk_box_pack_start (GTK_BOX (spanwin->view_box), spanwin->velbar_box, FALSE, FALSE, 0); /* create the velocity widget */ spanwin->velbar = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (spanwin->velbar), PIANO_DEFAULT_SIZEX, PIANO_DEFAULT_SIZEY); gtk_preview_set_expand (GTK_PREVIEW (spanwin->velbar), FALSE); /* draw the velocity bar */ swamiui_spanwin_draw_velocity_bar (spanwin); gtk_widget_show (spanwin->velbar); gtk_box_pack_start (GTK_BOX (spanwin->velbar_box), spanwin->velbar, FALSE, FALSE, 0); /* vertical scrollbar controls keyrange widget view only */ vscrollbar = gtk_vscrollbar_new (NULL); gtk_widget_show (vscrollbar); vadj = GTK_RANGE (vscrollbar)->adjustment; gtk_signal_connect (GTK_OBJECT (vadj), "changed", GTK_SIGNAL_FUNC (spanwin_cb_scrollbar_automatic), vscrollbar); gtk_box_pack_start (GTK_BOX (spanwin), vscrollbar, FALSE, FALSE, 0); /* keyrange viewport */ view = gtk_viewport_new (NULL, vadj); gtk_widget_set_usize (view, KEYSPAN_WIDTH, 0); gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hscrollbar, FALSE, FALSE, 0); /* link the horizontal scrollbar to the keyrange viewport */ gtk_signal_connect (GTK_OBJECT (hadj), "value_changed", GTK_SIGNAL_FUNC (spanwin_cb_link_adjustments), (gpointer) gtk_viewport_get_hadjustment (GTK_VIEWPORT (view))); gtk_container_border_width (GTK_CONTAINER (view), 0); gtk_viewport_set_shadow_type (GTK_VIEWPORT (view), GTK_SHADOW_NONE); gtk_widget_show (GTK_WIDGET (view)); spanwin->span_list = gtk_list_new (); gtk_list_set_selection_mode (GTK_LIST (spanwin->span_list), GTK_SELECTION_EXTENDED); gtk_signal_connect (GTK_OBJECT (spanwin->span_list), "select-child", GTK_SIGNAL_FUNC (spanwin_cb_list_select), spanwin); gtk_signal_connect (GTK_OBJECT (spanwin->span_list), "unselect-child", GTK_SIGNAL_FUNC (spanwin_cb_list_unselect), spanwin); gtk_container_set_border_width (GTK_CONTAINER (spanwin->span_list), 0); gtk_widget_show (spanwin->span_list); gtk_container_add (GTK_CONTAINER (view), spanwin->span_list); } static void spanwin_cb_ptrstrip_change (GtkWidget *ptrstrip, guint ptrndx, SwamiUISpanWin *spanwin) { IPGenAmount amt, getamt; PtrStripPointer *psptr; IPZone *zone; int ptrxpos, newxpos; int keynum; gboolean set; if (ptrndx != 0 || !(PTRSTRIP (ptrstrip)->pointers)) return; /* we want first pointer */ psptr = (PtrStripPointer *) (PTRSTRIP (ptrstrip)->pointers->data); ptrxpos = psptr->xpos; if (spanwin->item && INSTP_IS_INST (spanwin->item) && (zone = swamiui_spanwin_get_single_sel (spanwin))) { /* -- inst zone root key override? */ /* ptr at very left hand side of the pointer bar disables rootkey override, which means the rootkey from the sample is used */ if (ptrxpos > 3) { /* NO, ptr is not far left */ keynum = piano_xpos_to_key (ptrxpos); newxpos = piano_key_to_xpos (keynum); } else { /* YES, ptr is at far left */ keynum = -1; newxpos = 0; } set = swami_zone_get_gen (swami_object, INSTP_ZONE (zone), IPGEN_OVERRIDE_ROOT_KEY, &getamt); /* check if new keynum is different then old */ if ((!set && keynum != -1) || (set && getamt.sword != keynum)) { amt.sword = keynum; /* set the override root key gen */ swami_zone_set_gen (swami_object, zone, IPGEN_OVERRIDE_ROOT_KEY, amt); } else /* new keynum is the same as old, stop signal propagation */ gtk_signal_emit_stop_by_name (GTK_OBJECT (ptrstrip), "pointer_change"); } else if (spanwin->item && INSTP_IS_SAMPLE (spanwin->item)) { /* -- sample root key */ int origpitch; keynum = piano_xpos_to_key (ptrxpos); newxpos = piano_key_to_xpos (keynum); origpitch = swami_item_get_int (swami_object, spanwin->item, "origpitch"); if (keynum != origpitch) swami_item_set_int (swami_object, spanwin->item, "origpitch", keynum); else /* new keynum is the same as old, stop signal propagation */ gtk_signal_emit_stop_by_name (GTK_OBJECT (ptrstrip), "pointer_change"); } else return; /* not instrument or sample selection! */ psptr->xpos = newxpos; /* lock the ptr to key centers */ } /* class zone-select signal callback */ static void spanwin_cb_zone_select (SwamiUISpanWin *spanwin, IPItem *zone) { swamiui_spanwin_update_rootkey_ptrstrip (spanwin); } /* class zone-unselect signal callback */ static void spanwin_cb_zone_unselect (SwamiUISpanWin *spanwin, IPItem *zone) { swamiui_spanwin_update_rootkey_ptrstrip (spanwin); } /* range of a span changed */ static void spanwin_cb_span_changed (KeySpan *keyspan, SwamiUISpanWin *spanwin) { IPZone *zone; IPGenAmount amt; zone = INSTP_ZONE (gtk_object_get_data (GTK_OBJECT (keyspan), "zone")); amt.range.low = keyspan->lokey; amt.range.high = keyspan->hikey; if (spanwin->mode == SWAMIUI_SPANWIN_PIANO) swami_zone_set_gen (SWAMI_OBJECT (swamiui_object), zone, IPGEN_KEY_RANGE, amt); else swami_zone_set_gen (SWAMI_OBJECT (swamiui_object), zone, IPGEN_VELOCITY_RANGE, amt); } static void spanwin_cb_link_adjustments (GtkAdjustment *padj, GtkAdjustment *radj) { gtk_adjustment_set_value (radj, padj->value); } static void spanwin_cb_scrollbar_automatic (GtkAdjustment *adj, GtkWidget *scrollbar) { if (GTK_WIDGET_VISIBLE (scrollbar)) { if ((adj->upper - adj->lower) <= adj->page_size) gtk_widget_hide (scrollbar); } else { if ((adj->upper - adj->lower) > adj->page_size) gtk_widget_show (scrollbar); } } /* piano callback for note on */ static void spanwin_cb_piano_note_on (Piano *piano, int note, gpointer data) { SwamiUISpanWin *spanwin = data; SwamiUIMidiCtrl *midictrl; int chan = 0; midictrl = SWAMIUI_MIDICTRL (swamiui_lookup_object ("SwamiUIMidiCtrl")); if (midictrl) chan = midictrl->chan; if (spanwin->midi) swami_midi_note_on (spanwin->midi, chan, note, spanwin->velocity); } /* piano callback for note off */ static void spanwin_cb_piano_note_off (Piano *piano, int note, gpointer data) { SwamiUISpanWin *spanwin = data; SwamiUIMidiCtrl *midictrl; int chan = 0; midictrl = SWAMIUI_MIDICTRL (swamiui_lookup_object ("SwamiUIMidiCtrl")); if (midictrl) chan = midictrl->chan; if (spanwin->midi) swami_midi_note_off (spanwin->midi, chan, note, 127); } /* list zone select callback */ static void spanwin_cb_list_select (GtkList *list, GtkWidget *widget, SwamiUISpanWin *spanwin) { IPZone *zone; zone = gtk_object_get_data (GTK_OBJECT (widget), "zone"); if (zone) gtk_signal_emit (GTK_OBJECT (spanwin), spanwin_signals[ZONE_SELECT], zone); } /* list zone unselect callback */ static void spanwin_cb_list_unselect (GtkList *list, GtkWidget *widget, SwamiUISpanWin *spanwin) { IPZone *zone; zone = gtk_object_get_data (GTK_OBJECT (widget), "zone"); if (zone) gtk_signal_emit (GTK_OBJECT (spanwin), spanwin_signals[ZONE_UNSELECT], zone); } /* keyspan button press event, we need to do list selections since keyspan grabs mouse */ static gboolean spanwin_cb_keyspan_button_press (GtkWidget *keyspan, GdkEventButton *event, GtkList *list) { GtkWidget *item; GtkArg arg; if (event->button != 1) return (FALSE); gtk_list_unselect_all (list); arg.name = "parent"; gtk_object_getv (GTK_OBJECT (keyspan), 1, &arg); item = GTK_WIDGET (GTK_VALUE_POINTER (arg)); gtk_list_select_child (list, item); return (FALSE); } GtkWidget * swamiui_spanwin_new (void) { return (GTK_WIDGET (gtk_type_new (swamiui_spanwin_get_type ()))); } /** * Set the mode of a spanwin object * @spanwin Spanwin object * @mode Velocity or key mode enum */ void swamiui_spanwin_set_mode (SwamiUISpanWin *spanwin, SwamiUISpanWinMode mode) { if (mode == spanwin->mode) return; if (mode == SWAMIUI_SPANWIN_PIANO) /* piano mode */ { gtk_widget_hide (spanwin->velbar_box); gtk_widget_show (spanwin->piano); } else /* velocity bar mode */ { gtk_widget_hide (spanwin->piano); gtk_widget_show (spanwin->velbar_box); } spanwin->mode = mode; swamiui_spanwin_update (spanwin); } /** * Set the patch item to sync to * @spanwin SpanWin object * @item Item to sync spanwin to (IPPreset, IPInst or NULL to disable) */ void swamiui_spanwin_set_item (SwamiUISpanWin *spanwin, IPItem *item) { g_return_if_fail (spanwin != NULL); g_return_if_fail (SWAMIUI_IS_SPANWIN (spanwin)); if (item && !INSTP_IS_PRESET (item) && !INSTP_IS_INST (item) && !INSTP_IS_SAMPLE (item)) item = NULL; if (spanwin->item == item) return; spanwin->item = item; swamiui_spanwin_update (spanwin); } /** * Select a keyspan by zone * @spanwin Span Win object to select keyspan in * @zone SoundFont zone of keyspan to select */ void swamiui_spanwin_select_keyspan (SwamiUISpanWin *spanwin, IPZone *zone) { GList *children, *p; children = gtk_container_children (GTK_CONTAINER (spanwin->span_list)); p = g_list_find_custom (children, zone, spanwin_GCompareFunc_find_zone_litem); if (p) gtk_list_select_child (GTK_LIST (spanwin->span_list), GTK_WIDGET (p->data)); g_list_free (children); } /** * Unselect a keyspan by zone * @spanwin Span Win object to unselect keyspan in * @zone SoundFont zone of keyspan to unselect */ void swamiui_spanwin_unselect_keyspan (SwamiUISpanWin *spanwin, IPZone *zone) { GList *children, *p; children = gtk_container_children (GTK_CONTAINER (spanwin->span_list)); p = g_list_find_custom (children, zone, spanwin_GCompareFunc_find_zone_litem); if (p) gtk_list_unselect_child (GTK_LIST (spanwin->span_list), GTK_WIDGET (p->data)); g_list_free (children); } /** * Unselect all keyspans in list * @spanwin SpanWin object to unselect all keyspans of. */ void swamiui_spanwin_unselect_all_keyspans (SwamiUISpanWin *spanwin) { g_return_if_fail (spanwin != NULL); g_return_if_fail (SWAMIUI_IS_SPANWIN (spanwin)); gtk_list_unselect_all (GTK_LIST (spanwin->span_list)); } /* GCompareFunc to find a GtkList item by IPZone */ static gint spanwin_GCompareFunc_find_zone_litem (gconstpointer a, gconstpointer b) { GtkObject *litem = (GtkObject *)a; return (!(gtk_object_get_data (litem, "zone") == b)); } /** * Get list of selected keyspan zones * spanwin Span Win object to get selected zones from * Returns: List of selected IPZone items or NULL if none selected. List * should be freed with g_list_free when finished with. */ GList * swamiui_spanwin_get_keyspan_selection (SwamiUISpanWin *spanwin) { GList *sel, *children; GList *zone_list; GtkWidget *litem; IPZone *zone; sel = GTK_LIST (spanwin->span_list)->selection; while (sel) { litem = GTK_WIDGET (sel->data); children = gtk_container_children (GTK_CONTAINER (litem)); zone = gtk_object_get_data (GTK_OBJECT (children->data), "zone"); g_list_free (children); zone_list = g_list_append (zone_list, zone); sel = g_list_next (sel); } return (zone_list); } /* get single selected zone or NULL if no or multiple zones selected */ static IPZone * swamiui_spanwin_get_single_sel (SwamiUISpanWin *spanwin) { GList *sel, *children; GtkWidget *litem; IPZone *zone; sel = GTK_LIST (spanwin->span_list)->selection; if (sel && !sel->next) { litem = GTK_WIDGET (sel->data); children = gtk_container_children (GTK_CONTAINER (litem)); zone = gtk_object_get_data (GTK_OBJECT (children->data), "zone"); g_list_free (children); return (zone); } return (NULL); } /* update spans */ static void swamiui_spanwin_update (SwamiUISpanWin *spanwin) { IPZone *zone = NULL; GtkWidget *keyspan; GtkWidget *listitem; IPGenAmount amt; if (spanwin->item) { if (INSTP_IS_PRESET (spanwin->item)) zone = INSTP_PRESET (spanwin->item)->zone; else if (INSTP_IS_INST (spanwin->item)) zone = INSTP_INST (spanwin->item)->zone; } /* remove all the spans from the gtk list widget */ gtk_list_clear_items (GTK_LIST (spanwin->span_list), 0, -1); if (zone && !zone->refitem) /* global zone? */ zone = instp_zone_next (zone); /* yes: skip */ while (zone) /* loop over zones */ { if (spanwin->mode == SWAMIUI_SPANWIN_PIANO) /* piano mode? */ instp_zone_get_gen (zone, IPGEN_KEY_RANGE, &amt); else /* velocity mode */ instp_zone_get_gen (zone, IPGEN_VELOCITY_RANGE, &amt); /* create the new span widget */ keyspan = keyspan_new (); keyspan_set_span (KEYSPAN (keyspan), amt.range.low, amt.range.high); gtk_object_set_data (GTK_OBJECT (keyspan), "zone", zone); gtk_widget_show (keyspan); gtk_signal_connect (GTK_OBJECT (keyspan), "button-press-event", GTK_SIGNAL_FUNC (spanwin_cb_keyspan_button_press), spanwin->span_list); /* set the span's mode */ if (spanwin->mode == SWAMIUI_SPANWIN_PIANO) keyspan_set_mode (KEYSPAN (keyspan), KEYSPAN_KEYMODE); else keyspan_set_mode (KEYSPAN (keyspan), KEYSPAN_VELMODE); gtk_signal_connect (GTK_OBJECT (keyspan), "span_change", GTK_SIGNAL_FUNC (spanwin_cb_span_changed), spanwin); gtk_widget_show (GTK_WIDGET (keyspan)); listitem = gtk_list_item_new (); gtk_object_set_data (GTK_OBJECT (listitem), "zone", zone); gtk_container_set_border_width (GTK_CONTAINER (listitem), 0); gtk_widget_show (listitem); gtk_container_add (GTK_CONTAINER (listitem), keyspan); gtk_container_add (GTK_CONTAINER (spanwin->span_list), listitem); zone = instp_zone_next (zone); } swamiui_spanwin_update_rootkey_ptrstrip (spanwin); } static void swamiui_spanwin_update_rootkey_ptrstrip (SwamiUISpanWin *spanwin) { IPZone *zone; IPGenAmount amt; gboolean set; int xpos; if (spanwin->mode != SWAMIUI_SPANWIN_PIANO) xpos = -1; /* disable ptrstrip in velocity mode */ else if (spanwin->item && INSTP_IS_INST (spanwin->item) && (zone = swamiui_spanwin_get_single_sel (spanwin))) { /* if item is an instrument and only 1 zone selected.. */ set = swami_zone_get_gen (swami_object, zone, IPGEN_OVERRIDE_ROOT_KEY, &amt); if (set) xpos = piano_key_to_xpos (amt.sword); else xpos = 0; } else if (spanwin->item && INSTP_IS_SAMPLE (spanwin->item)) { /* item is a sample? */ int origpitch; origpitch = swami_item_get_int (swami_object, spanwin->item, "origpitch"); xpos = piano_key_to_xpos (origpitch); } else xpos = -1; /* disable ptrstrip (no item) */ gtk_signal_handler_block_by_func (GTK_OBJECT (spanwin->ptrstrip), (GtkSignalFunc) spanwin_cb_ptrstrip_change, spanwin); ptrstrip_set_pointer (PTRSTRIP (spanwin->ptrstrip), 0, xpos); gtk_signal_handler_unblock_by_func (GTK_OBJECT (spanwin->ptrstrip), (GtkSignalFunc) spanwin_cb_ptrstrip_change, spanwin); } /** * Set the start octave for the virtual keyboard * @spanwin Spanwin object * @octave Octave number that virtual computer keyboard starts at. */ void swamiui_spanwin_piano_set_octave (SwamiUISpanWin *spanwin, int octave) { spanwin->octave = octave; } /** * Set the velocity of the virtual keyboard * @spanwin Spanwin object * @velocity MIDI velocity value */ void swamiui_spanwin_piano_set_velocity (SwamiUISpanWin *spanwin, int velocity) { spanwin->velocity = velocity; } /** * Get the MIDI note corresponding to a key press/release * @spanwin Spanwin object * @key GDK key * Returns: MIDI note number corresponding to key, determined by settings of * piano. -1 if un-mapped key or note out of range. */ int swamiui_spanwin_piano_key_to_note (SwamiUISpanWin *spanwin, int key) { int i; int note; g_return_val_if_fail (spanwin != NULL, -1); g_return_val_if_fail (SWAMIUI_IS_SPANWIN (spanwin), -1); for (i = 0; i < SWAMIUI_SPANWIN_PIANO_TOTAL_NUMKEYS; i++) { if (key == swamiui_spanwin_keytable[i]) { note = i; if (i >= SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS) note = note - SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS + 12; note += spanwin->octave * 12; if (note > 127) return (-1); /* note out of range? */ return (note); } } return (-1); /* key not found */ } /** * Piano note on * @spanwin Spanwin object * @note MIDI note number to turn on * * Turns on note visually on piano as well as sends an event to it's connected * MIDI driver (if any). */ void swamiui_spanwin_piano_note_on (SwamiUISpanWin *spanwin, int note) { g_return_if_fail (spanwin != NULL); g_return_if_fail (SWAMIUI_IS_SPANWIN (spanwin)); /* callback will be triggered which will send MIDI event */ piano_note_on (PIANO (spanwin->piano), note); } /** * Piano note off * @spanwin Spanwin object * @note MIDI note number to turn off * * Turns off note visually on piano as well as sends an event to it's connected * MIDI driver (if any). */ void swamiui_spanwin_piano_note_off (SwamiUISpanWin *spanwin, int note) { g_return_if_fail (spanwin != NULL); g_return_if_fail (SWAMIUI_IS_SPANWIN (spanwin)); /* callback will be triggered which will send MIDI event */ piano_note_off (PIANO (spanwin->piano), note); } /** * Get current key table for virtual keyboard * @spanwin SpanWin object * Returns: Pointer to an array of key codes of size * #SWAMIUI_SPANWIN_PIANO_TOTAL_NUMKEYS which should not be modified * directly. */ guint * swamiui_spanwin_get_keytable (SwamiUISpanWin *spanwin) { return (swamiui_spanwin_keytable); } /** * Update the virtual keyboard keytable from preferences */ void swamiui_spanwin_update_keytable (void) { guint *keyvals; guint *srcvals; int i, c; char *s; /* initialize piano key table low octave */ s = swami_config_get_string ("gui", "piano_lowoctkeys"); c = swamiui_spanwin_parse_octkeys (s, &keyvals); if (c == SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS) srcvals = keyvals; /* if swami config variable contains valid # of keys */ else srcvals = swamiui_spanwin_default_keytable; /* use default keytable */ /* copy keys to key table */ for (i = 0; i < SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS; i++) swamiui_spanwin_keytable[i] = srcvals[i]; if (c) g_free (keyvals); /* initialize swami_vkeyb key table hi octave */ s = swami_config_get_string ("gui", "piano_hioctkeys"); c = swamiui_spanwin_parse_octkeys (s, &keyvals); if (c == SWAMIUI_SPANWIN_PIANO_HI_NUMKEYS) srcvals = keyvals; /* if swami config variable contains valid # of keys */ else /* swamicfg not valid, use default */ srcvals = &swamiui_spanwin_default_keytable[SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS]; /* copy keys to key table */ for (i = 0; i < SWAMIUI_SPANWIN_PIANO_HI_NUMKEYS; i++) swamiui_spanwin_keytable[i + SWAMIUI_SPANWIN_PIANO_LOW_NUMKEYS] = srcvals[i]; if (c) g_free (keyvals); } /** * Parses a swami config key string (key names seperated by commas) * @str Config string to parse * @Pointer to store a pointer to an array of key integers which should * be freed when finished with. * Returns: Number of key names parsed or 0 on error */ int swamiui_spanwin_parse_octkeys (char *str, guint **keys) { guint *keyvals; char **keynames; int i = 0; *keys = NULL; /* allocate for array of maximum number of expected keys */ keyvals = g_malloc (SWAMIUI_SPANWIN_PIANO_HI_NUMKEYS * sizeof (guint)); keynames = g_strsplit (str, ",", SWAMIUI_SPANWIN_PIANO_HI_NUMKEYS); while (keynames[i]) { keyvals[i] = gdk_keyval_from_name (keynames[i]); if (keyvals[i] == GDK_VoidSymbol) /* invalid keyname? */ { g_free (keyvals); g_strfreev (keynames); return (0); } i++; } g_strfreev (keynames); if (i) *keys = keyvals; else g_free (keyvals); return (i); } /** * Composes a swami config key name string * @keyvals An array of unsigned integer key values * @keycount The number of keys in keyvals array * Returns: Pointer to key names string or NULL (string should be free'd) */ char * swamiui_spanwin_encode_octkeys (guint *keyvals, int keycount) { char **keynames; char *s; int i; /* allocate for array of key name strings */ keynames = g_malloc ((keycount + 1) * sizeof (char *)); for (i = 0; i < keycount; i++) /* loop over each key */ { keynames[i] = gdk_keyval_name (keyvals[i]); /* get key name */ if (!keynames[i]) /* if no key name for key, fail */ { g_free (keynames); return (NULL); } } keynames[keycount] = NULL; s = g_strjoinv (",", keynames); /* create the comma delimited key string */ g_free (keynames); return (s); } static void swamiui_spanwin_draw_velocity_bar (SwamiUISpanWin *spanwin) { gint i, i2, lastlevel = 0; float levelinc, level = 0.0; gint rval, gval, bval; float rf, gf, bf, rinc, ginc, binc; guchar *linebuf; /* one line of velocity gradient */ /* allocate buffer space for one line of velocity bar */ linebuf = g_new (guchar, PIANO_DEFAULT_SIZEX * 3); rval = velbar_scolor[0]; gval = velbar_scolor[1]; bval = velbar_scolor[2]; rf = (float) rval; gf = (float) gval; bf = (float) bval; rinc = (float) (velbar_ecolor[0] - rval + 1) / PIANO_DEFAULT_SIZEX; ginc = (float) (velbar_ecolor[1] - gval + 1) / PIANO_DEFAULT_SIZEX; binc = (float) (velbar_ecolor[2] - bval + 1) / PIANO_DEFAULT_SIZEX; /* 128 steps for 128 levels of velocity */ levelinc = 128.0 / PIANO_DEFAULT_SIZEX; /* draw a single line of the velocity bar */ for (i = 0, i2 = 0; i < PIANO_DEFAULT_SIZEX; i++, i2 += 3) { linebuf[i2] = rval; linebuf[i2 + 1] = gval; linebuf[i2 + 2] = bval; if (lastlevel != (int) level) { lastlevel = (int) level; rval = (int) rf; gval = (int) gf; bval = (int) bf; } level += levelinc; rf += rinc; gf += ginc; bf += binc; } /* duplicate the line for each row */ for (i = 0; i < PIANO_DEFAULT_SIZEY; i++) gtk_preview_draw_row (GTK_PREVIEW (spanwin->velbar), linebuf, 0, i, PIANO_DEFAULT_SIZEX); }