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:
parent
b96d8e7ffa
commit
b523583bb4
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue