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, ...)
This commit is contained in:
Robin Gareus 2019-10-21 05:41:34 +02:00
parent b96d8e7ffa
commit b523583bb4
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
4 changed files with 401 additions and 99 deletions

View File

@ -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;

View 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);

View 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*)
{

View 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;