Virtual-Keyboard significant overhaul:
authorRobin Gareus <robin@gareus.org>
Mon, 21 Oct 2019 03:41:34 +0000 (05:41 +0200)
committerRobin Gareus <robin@gareus.org>
Mon, 21 Oct 2019 03:42:05 +0000 (05:42 +0200)
* fix key-range off-by-one 0..127
* allow to shift octave
* allow to configure note range
* highlight grand-piano range and keyboard-shortcut range
* proper piano layout (black key offsets)
* add support for DVORAK keyboard layout

(still incomplete: settings are not yet saved/restored,
_piano_key_velocity should become a HSliderController, ...)

gtk2_ardour/gtk_pianokeyboard.c
gtk2_ardour/gtk_pianokeyboard.h
gtk2_ardour/virtual_keyboard_window.cc
gtk2_ardour/virtual_keyboard_window.h

index 07643aef2984fe4469e84ee7fb225b96c7eb3a6b..e9212be4390ed3741774793cf643809fbcb4a653 100644 (file)
 
 #include "gtk_pianokeyboard.h"
 
+#ifndef MIN
+#define MIN(A,B) ((A) < (B)) ? (A) : (B)
+#endif
+
+#ifndef MAX
+#define MAX(A,B) ((A) > (B)) ? (A) : (B)
+#endif
+
 #define PIANO_KEYBOARD_DEFAULT_WIDTH 730
 #define PIANO_KEYBOARD_DEFAULT_HEIGHT 70
 
@@ -51,18 +59,23 @@ enum {
        LAST_SIGNAL
 };
 
-static guint   piano_keyboard_signals[LAST_SIGNAL] = { 0 };
+static guint piano_keyboard_signals[LAST_SIGNAL] = { 0 };
 
 static void
 draw_keyboard_cue(PianoKeyboard *pk, cairo_t* cr)
 {
-       int             w = pk->notes[0].w;
-       int             h = pk->notes[0].h;
+       int w = pk->notes[0].w;
+       int h = pk->notes[0].h;
+
+       int first_note_in_lower_row = (pk->octave + 1) * 12;
+       int last_note_in_lower_row = (pk->octave + 2) * 12 - 1;
+       int first_note_in_higher_row = (pk->octave + 2) * 12;
+       int last_note_in_higher_row = (pk->octave + 3) * 12 + 4;
 
-       int             first_note_in_lower_row = (pk->octave + 5) * 12;
-       int             last_note_in_lower_row = (pk->octave + 6) * 12 - 1;
-       int             first_note_in_higher_row = (pk->octave + 6) * 12;
-       int             last_note_in_higher_row = (pk->octave + 7) * 12 + 4;
+       first_note_in_lower_row  = MIN (127, MAX (0, first_note_in_lower_row));
+       last_note_in_lower_row   = MIN (127, MAX (0, last_note_in_lower_row));
+       first_note_in_higher_row = MIN (127, MAX (0, first_note_in_higher_row));
+       last_note_in_higher_row  = MIN (127, MAX (0, last_note_in_higher_row));
 
        cairo_set_source_rgb (cr, 1.0f, 0.0f, 0.0f);
        cairo_move_to (cr, pk->notes[first_note_in_lower_row].x + 3, h - 6);
@@ -95,23 +108,32 @@ queue_note_draw (PianoKeyboard* pk, int note)
 static void
 draw_note(PianoKeyboard *pk, cairo_t* cr, int note)
 {
-       int             is_white = pk->notes[note].white;
+       if (note < pk->min_note || note > pk->max_note) {
+               return;
+       }
 
-       int             x = pk->notes[note].x;
-       int             w = pk->notes[note].w;
-       int             h = pk->notes[note].h;
+       int is_white = pk->notes[note].white;
+       int x = pk->notes[note].x;
+       int w = pk->notes[note].w;
+       int h = pk->notes[note].h;
 
        if (pk->notes[note].pressed || pk->notes[note].sustained) {
                if (is_white) {
-                       cairo_set_source_rgb (cr, 0.60f, 0.60f, 0.60f);
+                       cairo_set_source_rgb (cr, 0.7, 0.5, 0.5);
+               } else {
+                       cairo_set_source_rgb (cr, 0.6, 0.4, 0.4);
+               }
+       } else if (pk->highlight_grand_piano_range && (note < PIANO_MIN_NOTE || note > PIANO_MAX_NOTE)) {
+               if (is_white) {
+                       cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
                } else {
-                       cairo_set_source_rgb (cr, 0.50f, 0.50f, 0.50f);
+                       cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
                }
        } else {
                if (is_white) {
-                       cairo_set_source_rgb (cr, 1.0f, 1.0f, 1.0f);
+                       cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
                } else {
-                       cairo_set_source_rgb (cr, 0.0f, 0.0f, 0.0f);
+                       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
                }
        }
 
@@ -204,9 +226,8 @@ rest (PianoKeyboard* pk)
 static void
 stop_unsustained_notes(PianoKeyboard *pk)
 {
-       int             i;
-
-       for (i = 0; i < NNOTES; i++) {
+       int i;
+       for (i = 0; i < NNOTES; ++i) {
                if (pk->notes[i].pressed && !pk->notes[i].sustained) {
                        pk->notes[i].pressed = 0;
                        g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
@@ -218,9 +239,8 @@ stop_unsustained_notes(PianoKeyboard *pk)
 static void
 stop_sustained_notes(PianoKeyboard *pk)
 {
-       int             i;
-
-       for (i = 0; i < NNOTES; i++) {
+       int i;
+       for (i = 0; i < NNOTES; ++i) {
                if (pk->notes[i].sustained) {
                        pk->notes[i].pressed = 0;
                        pk->notes[i].sustained = 0;
@@ -270,7 +290,7 @@ bind_keys_qwerty(PianoKeyboard *pk)
        bind_key(pk, "space", 128);
 
        /* Lower keyboard row - "zxcvbnm". */
-       bind_key(pk, "z", 12);  /* C0 */
+       bind_key(pk, "z", 12); /* C0 */
        bind_key(pk, "s", 13);
        bind_key(pk, "x", 14);
        bind_key(pk, "d", 15);
@@ -323,7 +343,7 @@ bind_keys_azerty(PianoKeyboard *pk)
        bind_key(pk, "space", 128);
 
        /* Lower keyboard row - "wxcvbn,". */
-       bind_key(pk, "w", 12);  /* C0 */
+       bind_key(pk, "w", 12); /* C0 */
        bind_key(pk, "s", 13);
        bind_key(pk, "x", 14);
        bind_key(pk, "d", 15);
@@ -358,14 +378,68 @@ bind_keys_azerty(PianoKeyboard *pk)
        bind_key(pk, "p", 40);
 }
 
+static void
+bind_keys_dvorak(PianoKeyboard *pk)
+{
+       clear_notes(pk);
+
+       bind_key(pk, "space", 128);
+
+       /* Lower keyboard row - ";qjkxbm". */
+       bind_key(pk, "semicolon", 12); /* C0 */
+       bind_key(pk, "o", 13);
+       bind_key(pk, "q", 14);
+       bind_key(pk, "e", 15);
+       bind_key(pk, "j", 16);
+       bind_key(pk, "k", 17);
+       bind_key(pk, "i", 18);
+       bind_key(pk, "x", 19);
+       bind_key(pk, "d", 20);
+       bind_key(pk, "b", 21);
+       bind_key(pk, "h", 22);
+       bind_key(pk, "m", 23);
+       bind_key(pk, "w", 24); /* overlaps with upper row */
+       bind_key(pk, "n", 25);
+       bind_key(pk, "v", 26);
+       bind_key(pk, "s", 27);
+       bind_key(pk, "z", 28);
+
+       /* Upper keyboard row, first octave - "',.pyfg". */
+       bind_key(pk, "apostrophe", 24);
+       bind_key(pk, "2", 25);
+       bind_key(pk, "comma", 26);
+       bind_key(pk, "3", 27);
+       bind_key(pk, "period", 28);
+       bind_key(pk, "p", 29);
+       bind_key(pk, "5", 30);
+       bind_key(pk, "y", 31);
+       bind_key(pk, "6", 32);
+       bind_key(pk, "f", 33);
+       bind_key(pk, "7", 34);
+       bind_key(pk, "g", 35);
+
+       /* Upper keyboard row, the rest - "crl". */
+       bind_key(pk, "c", 36);
+       bind_key(pk, "9", 37);
+       bind_key(pk, "r", 38);
+       bind_key(pk, "0", 39);
+       bind_key(pk, "l", 40);
+#if 0
+       bind_key(pk, "slash", 41); /* extra F */
+       bind_key(pk, "bracketright", 42);
+       bind_key(pk, "equal", 43);
+#endif
+}
+
 static gint
 keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
 {
-       int             note;
-       char            *key;
-       guint           keyval;
-       GdkKeymapKey    kk;
-       PianoKeyboard   *pk = PIANO_KEYBOARD(mk);
+       int   note;
+       char* key;
+       guint keyval;
+
+       GdkKeymapKey  kk;
+       PianoKeyboard* pk = PIANO_KEYBOARD(mk);
 
        (void) ignored;
 
@@ -387,7 +461,6 @@ keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
        note = key_binding(pk, key);
 
        if (note < 0) {
-               /* Key was not bound.  Maybe it's one of the keys handled in jack-keyboard.c. */
                return FALSE;
        }
 
@@ -414,27 +487,31 @@ keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
 }
 
 static int
-get_note_for_xy(PianoKeyboard *pk, int x, int y)
+get_note_for_xy (PianoKeyboard *pk, int x, int y)
 {
-       int             height = GTK_WIDGET(pk)->allocation.height;
-       int             note;
+       int height = GTK_WIDGET(pk)->allocation.height;
+       int note;
 
-       if (y <= height / 2) {
-               for (note = 0; note < NNOTES - 1; note++) {
-                       if (pk->notes[note].white)
+       if (y <= ((height * 2) / 3)) { /* might be a black key */
+               for (note = 0; note <= pk->max_note; ++note) {
+                       if (pk->notes[note].white) {
                                continue;
+                       }
 
-                       if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
+                       if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w) {
                                return note;
+                       }
                }
        }
 
-       for (note = 0; note < NNOTES - 1; note++) {
-               if (!pk->notes[note].white)
+       for (note = 0; note <= pk->max_note; ++note) {
+               if (!pk->notes[note].white) {
                        continue;
+               }
 
-               if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
+               if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w) {
                        return note;
+               }
        }
 
        return -1;
@@ -459,10 +536,10 @@ get_velocity_for_note_at_y(PianoKeyboard *pk, int note, int y)
 static gboolean
 mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ignored)
 {
-       int             x = event->x;
-       int             y = event->y;
+       int x = event->x;
+       int y = event->y;
 
-       int             note = get_note_for_xy(pk, x, y);
+       int note = get_note_for_xy (pk, x, y);
 
        (void) ignored;
 
@@ -470,29 +547,26 @@ mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ig
                return TRUE;
 
        if (event->type == GDK_BUTTON_PRESS) {
-               /* This is possible when you make the window a little wider and then click
-                  on the grey area. */
                if (note < 0) {
                        return TRUE;
                }
 
-               if (pk->note_being_pressed_using_mouse >= 0)
+               if (pk->note_being_pressed_using_mouse >= 0) {
                        release_key(pk, pk->note_being_pressed_using_mouse);
+               }
 
-               press_key(pk, note, get_velocity_for_note_at_y (pk, note, y));
+               press_key (pk, note, get_velocity_for_note_at_y (pk, note, y));
                pk->note_being_pressed_using_mouse = note;
 
        } else if (event->type == GDK_BUTTON_RELEASE) {
                if (note >= 0) {
                        release_key(pk, note);
-
                } else {
-                       if (pk->note_being_pressed_using_mouse >= 0)
+                       if (pk->note_being_pressed_using_mouse >= 0) {
                                release_key(pk, pk->note_being_pressed_using_mouse);
+                       }
                }
-
                pk->note_being_pressed_using_mouse = -1;
-
        }
 
        return TRUE;
@@ -501,17 +575,17 @@ mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ig
 static gboolean
 mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer ignored)
 {
-       int             note;
+       int note;
 
        (void) ignored;
 
        if ((event->state & GDK_BUTTON1_MASK) == 0)
                return TRUE;
 
-       int             x = event->x;
-       int             y = event->y;
+       int x = event->x;
+       int y = event->y;
 
-       note = get_note_for_xy(pk, x, y);
+       note = get_note_for_xy (pk, x, y);
 
        if (note != pk->note_being_pressed_using_mouse && note >= 0) {
                if (pk->note_being_pressed_using_mouse >= 0) {
@@ -534,7 +608,7 @@ piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event)
        gdk_cairo_region (cr, event->region);
        cairo_clip (cr);
 
-       for (i = 0; i < NNOTES; i++) {
+       for (i = 0; i < NNOTES; ++i) {
                GdkRectangle r;
 
                r.x = pk->notes[i].x;
@@ -566,39 +640,82 @@ piano_keyboard_size_request(GtkWidget* w, GtkRequisition *requisition)
        requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
 }
 
-static void
-recompute_dimensions(PianoKeyboard *pk)
+static int
+is_black (int key)
 {
-       int             number_of_white_keys = (NNOTES - 1) * (7.0 / 12.0);
+       int note_in_octave = key % 12;
+       switch (note_in_octave) {
+               case 1:
+               case 3:
+               case 6:
+               case 8:
+               case 10:
+                       return 1;
+               default:
+                       return 0;
+       }
+       return 0;
+}
 
-       int             key_width;
-       int             black_key_width;
-       int             useful_width;
+static double
+black_key_left_shift (int key)
+{
+       int note_in_octave = key % 12;
+       switch (note_in_octave)
+       {
+               case 1:
+                       return 2.0/3.0;
+               case 3:
+                       return 1.0/3.0;
+               case 6:
+                       return 2.0/3.0;
+               case 8:
+                       return 0.5;
+               case 10:
+                       return 1.0/3.0;
+               default:
+                       return 0;
+       }
+       return 0;
+}
 
-       int             note;
-       int             white_key = 0;
-       int             note_in_octave;
+static void
+recompute_dimensions (PianoKeyboard *pk)
+{
+       int note;
+       int number_of_white_keys = 0;
+       int skipped_white_keys = 0;
 
-       int             width = GTK_WIDGET(pk)->allocation.width;
-       int             height = GTK_WIDGET(pk)->allocation.height;
+       for (note = pk->min_note; note <= pk->max_note; ++note) {
+               if (!is_black(note)) {
+                       ++number_of_white_keys;
+               }
+       }
+       for (note = 0; note < pk->min_note; ++note) {
+               if (!is_black(note)) {
+                       ++skipped_white_keys;
+               }
+       }
 
-       key_width = width / number_of_white_keys;
-       black_key_width = key_width * 0.8;
-       useful_width = number_of_white_keys * key_width;
-       pk->widget_margin = (width - useful_width) / 2;
+       int width = GTK_WIDGET(pk)->allocation.width;
+       int height = GTK_WIDGET(pk)->allocation.height;
 
-       for (note = 0, white_key = 0; note < NNOTES - 2; note++) {
-               note_in_octave = note % 12;
+       int key_width = width / number_of_white_keys;
+       int black_key_width = key_width * 0.8;
+       int useful_width = number_of_white_keys * key_width;
 
-               if (note_in_octave == 1 || note_in_octave == 3 || note_in_octave == 6 ||
-                       note_in_octave == 8 || note_in_octave == 10) {
+       pk->widget_margin = (width - useful_width) / 2;
 
+       int white_key;
+       for (note = 0, white_key = -skipped_white_keys; note < NNOTES; ++note) {
+               if (is_black(note)) {
                        /* This note is black key. */
-                       pk->notes[note].x = pk->widget_margin + white_key * key_width - black_key_width / 2;
+                       pk->notes[note].x = pk->widget_margin +
+                           (white_key * key_width) -
+                           (black_key_width * black_key_left_shift(note));
                        pk->notes[note].w = black_key_width;
-                       pk->notes[note].h = height * .6;
+                       pk->notes[note].h = (height * 2) / 3;
                        pk->notes[note].white = 0;
-
                        continue;
                }
 
@@ -667,7 +784,7 @@ g_cclosure_user_marshal_VOID__INT_INT (GClosure     *closure,
 static void
 piano_keyboard_class_init(PianoKeyboardClass *klass)
 {
-       GtkWidgetClass  *widget_klass;
+       GtkWidgetClasswidget_klass;
 
        /* Set up signals. */
        piano_keyboard_signals[NOTE_ON_SIGNAL] = g_signal_new ("note-on",
@@ -735,9 +852,13 @@ piano_keyboard_new(void)
 
        pk->maybe_stop_sustained_notes = 0;
        pk->sustain_new_notes = 0;
-       pk->enable_keyboard_cue = 0;
+       pk->enable_keyboard_cue = FALSE;
+       pk->highlight_grand_piano_range = FALSE;
        pk->octave = 4;
+       pk->octave_range = 7;
        pk->note_being_pressed_using_mouse = -1;
+       pk->min_note = PIANO_MIN_NOTE;
+       pk->max_note = PIANO_MAX_NOTE;
        pk->last_key = 0;
        pk->monophonic = FALSE;
 
@@ -754,9 +875,17 @@ piano_keyboard_new(void)
 }
 
 void
-piano_keyboard_set_keyboard_cue(PianoKeyboard *pk, int enabled)
+piano_keyboard_set_keyboard_cue (PianoKeyboard *pk, gboolean enabled)
 {
        pk->enable_keyboard_cue = enabled;
+       gtk_widget_queue_draw(GTK_WIDGET(pk));
+}
+
+void
+piano_keyboard_set_grand_piano_highlight (PianoKeyboard *pk, gboolean enabled)
+{
+       pk->highlight_grand_piano_range = enabled;
+       gtk_widget_queue_draw(GTK_WIDGET(pk));
 }
 
 void
@@ -816,10 +945,88 @@ piano_keyboard_set_note_off(PianoKeyboard *pk, int note)
 }
 
 void
-piano_keyboard_set_octave(PianoKeyboard *pk, int octave)
+piano_keyboard_set_octave (PianoKeyboard *pk, int octave)
 {
        stop_unsustained_notes(pk);
+
+       if (pk->octave < -1) {
+               pk->octave = -1;
+       } else if (pk->octave > 7) {
+               pk->octave = 7;
+       }
+
        pk->octave = octave;
+       piano_keyboard_set_octave_range (pk, pk->octave_range);
+       gtk_widget_queue_draw(GTK_WIDGET(pk));
+}
+
+void
+piano_keyboard_set_octave_range (PianoKeyboard *pk, int octave_range)
+{
+       stop_unsustained_notes(pk);
+
+       if (octave_range < 2) {
+               octave_range = 2;
+       }
+       if (octave_range > 11) {
+               octave_range = 11;
+       }
+
+       pk->octave_range = octave_range;
+
+       /* -1 <= pk->octave <= 7
+        * key-bindings are at offset 12 .. 40
+        * default piano range: octave = 4, range = 7 -> note 21..108
+        */
+
+       switch (octave_range) {
+               default:
+                       assert (0);
+                       break;
+               case 2:
+               case 3:
+                       pk->min_note = (pk->octave + 1) * 12;
+                       break;
+               case 4:
+               case 5:
+                       pk->min_note = (pk->octave + 0) * 12;
+                       break;
+               case 6:
+                       pk->min_note = (pk->octave + 1) * 12;
+                       break;
+               case 7:
+               case 8:
+                       pk->min_note = (pk->octave - 2) * 12;
+                       break;
+               case 9:
+               case 10:
+                       pk->min_note = (pk->octave - 3) * 12;
+                       break;
+               case 11:
+                       pk->min_note = (pk->octave - 4) * 12;
+                       break;
+       }
+
+       int upper_offset = 0;
+
+       if (pk->min_note < 3) {
+               upper_offset = 0;
+               pk->min_note = 0;
+       } else if (octave_range > 5) {
+               /* extend down to A */
+               upper_offset = 3;
+               pk->min_note -= 3;
+       }
+
+       pk->max_note = MIN (127, upper_offset + pk->min_note + octave_range * 12);
+
+       if (pk->max_note == 127) {
+               pk->min_note = MAX (0, pk->max_note - octave_range * 12);
+       }
+
+       printf ("Oct: %d, Range: %d  MIDI %d .. %d\n", pk->octave, octave_range, pk->min_note, pk->max_note);
+
+       recompute_dimensions(pk);
        gtk_widget_queue_draw(GTK_WIDGET(pk));
 }
 
@@ -837,6 +1044,9 @@ piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout)
        } else if (!g_ascii_strcasecmp(layout, "AZERTY")) {
                bind_keys_azerty(pk);
 
+       } else if (!g_ascii_strcasecmp(layout, "DVORAK")) {
+               bind_keys_dvorak(pk);
+
        } else {
                /* Unknown layout name. */
                return TRUE;
index b9d3581c74f75a1e6005b6a7fe03bdaa2c0a023b..70077da508bed5c7a7b22926f775ad40b34e4961 100644 (file)
@@ -35,7 +35,9 @@ G_BEGIN_DECLS
 typedef struct _PianoKeyboard             PianoKeyboard;
 typedef struct _PianoKeyboardClass        PianoKeyboardClass;
 
-#define NNOTES (127)
+#define NNOTES (128)
+#define PIANO_MIN_NOTE 21
+#define PIANO_MAX_NOTE 108
 
 #define OCTAVE_MIN (-1)
 #define OCTAVE_MAX (7)
@@ -55,10 +57,14 @@ struct _PianoKeyboard
        GtkDrawingArea da;
        int            maybe_stop_sustained_notes;
        int            sustain_new_notes;
-       int            enable_keyboard_cue;
+       gboolean       enable_keyboard_cue;
+       gboolean       highlight_grand_piano_range;
        int            octave;
+       int            octave_range;
        int            widget_margin;
        int            note_being_pressed_using_mouse;
+       int            min_note;
+       int            max_note;
        int            last_key;
        gboolean       monophonic;
        struct PKNote  notes[NNOTES];
@@ -81,9 +87,11 @@ void piano_keyboard_sustain_press (PianoKeyboard *pk);
 void piano_keyboard_sustain_release (PianoKeyboard *pk);
 void piano_keyboard_set_note_on (PianoKeyboard *pk, int note);
 void piano_keyboard_set_note_off (PianoKeyboard *pk, int note);
-void piano_keyboard_set_keyboard_cue (PianoKeyboard *pk, int enabled);
+void piano_keyboard_set_keyboard_cue (PianoKeyboard *pk, gboolean enabled);
+void piano_keyboard_set_grand_piano_highlight (PianoKeyboard *pk, gboolean enabled);
 void piano_keyboard_set_monophonic (PianoKeyboard *pk, gboolean monophonic);
 void piano_keyboard_set_octave (PianoKeyboard *pk, int octave);
+void piano_keyboard_set_octave_range(PianoKeyboard *pk, int octave_range);
 
 gboolean piano_keyboard_set_keyboard_layout (PianoKeyboard *pk, const char *layout);
 void piano_keyboard_set_velocities (PianoKeyboard *pk, int min_vel, int max_vel, int key_vel);
index 31bde09b72d9e090799f7ec03ec4b0729454f593..299a380cbb15fc3920a5ac457b8c2ab214638a59 100644 (file)
@@ -37,18 +37,22 @@ using namespace ArdourWidgets;
 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
 
 VirtualKeyboardWindow::VirtualKeyboardWindow ()
-       : ArdourWindow (_("Virtual Keyboard"))
+       : ArdourWindow (_("Virtual MIDI Keyboard"))
        , _piano_channel (*manage (new Adjustment (1, 1, 16, 1, 1)))
        , _bank_msb (*manage (new Adjustment (0, 0, 127, 1, 16)))
        , _bank_lsb (*manage (new Adjustment (0, 0, 127, 1, 16)))
        , _patchpgm (*manage (new Adjustment (1, 1, 128, 1, 16)))
        , _cfg_display ("Config", ArdourButton::led_default_elements)
        , _pgm_display ("Bank/Patch", ArdourButton::led_default_elements)
-       , _yaxis_velocity ("Y-Axis Click Velocity", ArdourButton::led_default_elements)
+       , _yaxis_velocity ("Y-Axis", ArdourButton::led_default_elements)
+       , _highlight_grand_piano ("Grand Piano", ArdourButton::led_default_elements)
+       , _highlight_key_range ("Key Bindings", ArdourButton::led_default_elements)
        , _send_panic ("Panic", ArdourButton::default_elements)
        , _piano_key_velocity (*manage (new Adjustment (100, 1, 127, 1, 16)))
        , _piano_min_velocity (*manage (new Adjustment (1  , 1, 127, 1, 16)))
        , _piano_max_velocity (*manage (new Adjustment (127, 1, 127, 1, 16)))
+       , _piano_octave_key (*manage (new Adjustment (4, -1, 7, 1, 1)))
+       , _piano_octave_range (*manage (new Adjustment (7, 2, 11, 1, 1)))
        , _pitch_adjustment (8192, 0, 16383, 1, 256)
 {
        _piano = (PianoKeyboard*)piano_keyboard_new();
@@ -64,11 +68,15 @@ VirtualKeyboardWindow::VirtualKeyboardWindow ()
                                sigc::bind (sigc::mem_fun (*this, &VirtualKeyboardWindow::select_keyboard_layout), 1)));
        _keyboard_layout.AddMenuElem (MenuElem ("AZERTY",
                                sigc::bind (sigc::mem_fun (*this, &VirtualKeyboardWindow::select_keyboard_layout), 2)));
+       _keyboard_layout.AddMenuElem (MenuElem ("DVORAK",
+                               sigc::bind (sigc::mem_fun (*this, &VirtualKeyboardWindow::select_keyboard_layout), 3)));
        _keyboard_layout.set_active (_("QWERTY"));
 
        _cfg_display.set_active (false);
        _pgm_display.set_active (false);
        _yaxis_velocity.set_active (false);
+       _highlight_grand_piano.set_active (false);
+       _highlight_key_range.set_active (false);
 
        _pitchbend = boost::shared_ptr<VKBDControl> (new VKBDControl ("PB", 8192, 16383));
        _pitch_slider = manage (new VSliderController(&_pitch_adjustment, _pitchbend, 0, PX_SCALE (15)));
@@ -78,21 +86,50 @@ VirtualKeyboardWindow::VirtualKeyboardWindow ()
        _pitchbend->ValueChanged.connect_same_thread (_cc_connections,
                        boost::bind (&VirtualKeyboardWindow::pitch_bend_event_handler, this, _1));
 
+       set_tooltip (_highlight_grand_piano, "Shade keys outside the range of a Grand Piano (A0-C8).");
+       set_tooltip (_highlight_key_range, "Indicate which notes can be controlled by keyboard-shortcuts.");
+       set_tooltip (_yaxis_velocity, "When enabled, mouse-click y-axis position defines the velocity.");
+
+       set_tooltip (_piano_octave_key, "The center octave, and lowest octave for keyboard control.");
+       set_tooltip (_piano_octave_range, "Available octave range, centered around the key-octave.");
+       set_tooltip (_keyboard_layout, "Keyboard layout to use for keyboard control.");
+
+       set_tooltip (_piano_key_velocity, "The default velocity to use with keyboard control, and when y-axis click-position is disabled.");
+       set_tooltip (_piano_min_velocity, "Velocity to use when clicking at the top-end of a key.");
+       set_tooltip (_piano_max_velocity, "Velocity to use when clicking at the bottom-end of a key.");
+
        set_tooltip (_send_panic, "Send MIDI Panic message for current channel");
+
        _pitch_slider_tooltip->set_tip ("Pitchbend: 8192");
 
        /* config */
-       HBox* cfg_box = manage (new HBox);
-       cfg_box->set_spacing (4);
-       cfg_box->pack_start (*manage (new Label (_("Key Velocity:"))), false, false);
-       cfg_box->pack_start (_piano_key_velocity, false, false);
-       cfg_box->pack_start (_yaxis_velocity, false, false, 8);
-       cfg_box->pack_start (*manage (new Label (_("Min Velocity:"))), false, false);
-       cfg_box->pack_start (_piano_min_velocity, false, false);
-       cfg_box->pack_start (*manage (new Label (_("Max Velocity:"))), false, false);
-       cfg_box->pack_start (_piano_max_velocity, false, false);
-       cfg_box->pack_start (_keyboard_layout, false, false, 8);
-       cfg_box->show_all ();
+       Table* cfg_tbl = manage (new Table);
+       cfg_tbl->attach (_yaxis_velocity,                      0, 1, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Velocity:"))), 0, 1, 1, 2, SHRINK, SHRINK, 4, 0);
+
+       cfg_tbl->attach (_piano_min_velocity,                  1, 2, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Min"))),       1, 2, 1, 2, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (_piano_max_velocity,                  2, 3, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Max"))),       2, 3, 1, 2, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (_piano_key_velocity,                  3, 4, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Key"))),       3, 4, 1, 2, SHRINK, SHRINK, 4, 0);
+
+       cfg_tbl->attach (*manage (new ArdourVSpacer),          4, 5, 0, 2, SHRINK, FILL,   4, 0);
+
+       cfg_tbl->attach (_piano_octave_key,                    5, 6, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Octave"))),    5, 6, 1, 2, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (_piano_octave_range,                  6, 7, 0, 1, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->attach (*manage (new Label (_("Range"))),     6, 7, 1, 2, SHRINK, SHRINK, 4, 0);
+
+       cfg_tbl->attach (*manage (new ArdourVSpacer),          7, 8, 0, 2, SHRINK, FILL,   4, 0);
+
+       cfg_tbl->attach (_highlight_grand_piano,               8, 9, 0, 1, FILL,   SHRINK, 4, 2);
+       cfg_tbl->attach (_highlight_key_range,                 8, 9, 1, 2, FILL,   SHRINK, 4, 2);
+
+       cfg_tbl->attach (*manage (new ArdourVSpacer),          9,10, 0, 2, SHRINK, FILL,   4, 0);
+
+       cfg_tbl->attach (_keyboard_layout,                    10,11, 0, 2, SHRINK, SHRINK, 4, 0);
+       cfg_tbl->show_all ();
 
        /* bank/patch */
        Table* pgm_tbl = manage (new Table);
@@ -156,7 +193,7 @@ VirtualKeyboardWindow::VirtualKeyboardWindow ()
        box1->pack_start (*box2, false, false);
 
        _cfg_box = manage (new HBox ());
-       _cfg_box->pack_start (*cfg_box, true, false);
+       _cfg_box->pack_start (*cfg_tbl, true, false);
        _cfg_box->set_no_show_all (true);
 
        _pgm_box = manage (new HBox ());
@@ -178,9 +215,14 @@ VirtualKeyboardWindow::VirtualKeyboardWindow ()
        _piano_min_velocity.signal_value_changed ().connect (sigc::bind (sigc::mem_fun (*this, &VirtualKeyboardWindow::update_velocity_settings), 1));
        _piano_max_velocity.signal_value_changed ().connect (sigc::bind (sigc::mem_fun (*this, &VirtualKeyboardWindow::update_velocity_settings), 2));
 
+       _piano_octave_key.signal_value_changed ().connect (sigc::mem_fun (*this, &VirtualKeyboardWindow::update_octave_key));
+       _piano_octave_range.signal_value_changed ().connect (sigc::mem_fun (*this, &VirtualKeyboardWindow::update_octave_range));
+
        _cfg_display.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::toggle_config), false);
        _pgm_display.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::toggle_bankpatch), false);
        _yaxis_velocity.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::toggle_yaxis_velocity), false);
+       _highlight_grand_piano.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::toggle_highlight_piano), false);
+       _highlight_key_range.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::toggle_highlight_key), false);
        _send_panic.signal_button_release_event().connect (sigc::mem_fun(*this, &VirtualKeyboardWindow::send_panic_message), false);
 
        g_signal_connect (G_OBJECT (_piano), "note-on", G_CALLBACK (VirtualKeyboardWindow::_note_on_event_handler), this);
@@ -304,6 +346,9 @@ VirtualKeyboardWindow::select_keyboard_layout (int l)
                case 2:
                        piano_keyboard_set_keyboard_layout (_piano, "AZERTY");
                        break;
+               case 3:
+                       piano_keyboard_set_keyboard_layout (_piano, "DVORAK");
+                       break;
        }
 }
 
@@ -333,6 +378,18 @@ VirtualKeyboardWindow::toggle_bankpatch (GdkEventButton*)
        return false;
 }
 
+void
+VirtualKeyboardWindow::update_octave_key ()
+{
+       piano_keyboard_set_octave (_piano, _piano_octave_key.get_value_as_int ());
+}
+
+void
+VirtualKeyboardWindow::update_octave_range ()
+{
+       piano_keyboard_set_octave_range (_piano, _piano_octave_range.get_value_as_int ());
+}
+
 bool
 VirtualKeyboardWindow::toggle_yaxis_velocity (GdkEventButton*)
 {
@@ -341,6 +398,24 @@ VirtualKeyboardWindow::toggle_yaxis_velocity (GdkEventButton*)
        return false;
 }
 
+bool
+VirtualKeyboardWindow::toggle_highlight_piano (GdkEventButton*)
+{
+       bool a = ! _highlight_grand_piano.get_active ();
+       _highlight_grand_piano.set_active (a);
+       piano_keyboard_set_grand_piano_highlight (_piano, a);
+       return false;
+}
+
+bool
+VirtualKeyboardWindow::toggle_highlight_key (GdkEventButton*)
+{
+       bool a = ! _highlight_key_range.get_active ();
+       _highlight_key_range.set_active (a);
+       piano_keyboard_set_keyboard_cue (_piano, a);
+       return false;
+}
+
 bool
 VirtualKeyboardWindow::send_panic_message (GdkEventButton*)
 {
index 20d11562f5466270acf1895e63e60283ead9ac3e..44a71867c0e4c92157117213aaf672eef520406e 100644 (file)
@@ -115,12 +115,16 @@ private:
 
        void select_keyboard_layout (int);
        void update_velocity_settings (int);
+       void update_octave_key ();
+       void update_octave_range ();
        void bank_patch ();
        void update_sensitivity ();
        void pitch_slider_adjusted ();
        bool toggle_config (GdkEventButton*);
        bool toggle_bankpatch (GdkEventButton*);
        bool toggle_yaxis_velocity (GdkEventButton*);
+       bool toggle_highlight_piano (GdkEventButton*);
+       bool toggle_highlight_key (GdkEventButton*);
        bool send_panic_message (GdkEventButton*);
 
        PianoKeyboard*  _piano;
@@ -137,6 +141,8 @@ private:
        ArdourWidgets::ArdourButton   _cfg_display;
        ArdourWidgets::ArdourButton   _pgm_display;
        ArdourWidgets::ArdourButton   _yaxis_velocity;
+       ArdourWidgets::ArdourButton   _highlight_grand_piano;
+       ArdourWidgets::ArdourButton   _highlight_key_range;
        ArdourWidgets::ArdourButton   _send_panic;
        ArdourWidgets::ArdourDropdown _keyboard_layout;
 
@@ -144,6 +150,9 @@ private:
        Gtk::SpinButton _piano_min_velocity;
        Gtk::SpinButton _piano_max_velocity;
 
+       Gtk::SpinButton _piano_octave_key;
+       Gtk::SpinButton _piano_octave_range;
+
        boost::shared_ptr<VKBDControl>    _pitchbend;
        Gtk::Adjustment                   _pitch_adjustment;
        ArdourWidgets::VSliderController* _pitch_slider;