From b523583bb412a2545a0923674e0ef6715792a38f Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 21 Oct 2019 05:41:34 +0200 Subject: [PATCH] Virtual-Keyboard significant overhaul: * 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 | 374 +++++++++++++++++++------ gtk2_ardour/gtk_pianokeyboard.h | 14 +- gtk2_ardour/virtual_keyboard_window.cc | 103 ++++++- gtk2_ardour/virtual_keyboard_window.h | 9 + 4 files changed, 401 insertions(+), 99 deletions(-) diff --git a/gtk2_ardour/gtk_pianokeyboard.c b/gtk2_ardour/gtk_pianokeyboard.c index 07643aef29..e9212be439 100644 --- a/gtk2_ardour/gtk_pianokeyboard.c +++ b/gtk2_ardour/gtk_pianokeyboard.c @@ -41,6 +41,14 @@ #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 + 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; + 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; + + 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.50f, 0.50f, 0.50f); + 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.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; + } + } + + int width = GTK_WIDGET(pk)->allocation.width; + int height = GTK_WIDGET(pk)->allocation.height; + + 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; - 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; - for (note = 0, white_key = 0; note < NNOTES - 2; note++) { - note_in_octave = note % 12; - - if (note_in_octave == 1 || note_in_octave == 3 || note_in_octave == 6 || - note_in_octave == 8 || note_in_octave == 10) { - + 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; + GtkWidgetClass* widget_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; diff --git a/gtk2_ardour/gtk_pianokeyboard.h b/gtk2_ardour/gtk_pianokeyboard.h index b9d3581c74..70077da508 100644 --- a/gtk2_ardour/gtk_pianokeyboard.h +++ b/gtk2_ardour/gtk_pianokeyboard.h @@ -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); diff --git a/gtk2_ardour/virtual_keyboard_window.cc b/gtk2_ardour/virtual_keyboard_window.cc index 31bde09b72..299a380cbb 100644 --- a/gtk2_ardour/virtual_keyboard_window.cc +++ b/gtk2_ardour/virtual_keyboard_window.cc @@ -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 (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*) { diff --git a/gtk2_ardour/virtual_keyboard_window.h b/gtk2_ardour/virtual_keyboard_window.h index 20d11562f5..44a71867c0 100644 --- a/gtk2_ardour/virtual_keyboard_window.h +++ b/gtk2_ardour/virtual_keyboard_window.h @@ -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 _pitchbend; Gtk::Adjustment _pitch_adjustment; ArdourWidgets::VSliderController* _pitch_slider;