diff --git a/src/jack-keyboard.c b/src/jack-keyboard.c index 8a092fe..924dc57 100644 --- a/src/jack-keyboard.c +++ b/src/jack-keyboard.c @@ -218,7 +218,7 @@ process_received_message_async(gpointer evp) if (ev->data[2] == 0) piano_keyboard_set_note_off(keyboard, ev->data[1]); else - piano_keyboard_set_note_on(keyboard, ev->data[1]); + piano_keyboard_set_note_on(keyboard, ev->data[1], ev->data[2]); } if (b0 == MIDI_NOTE_OFF) { @@ -1028,6 +1028,7 @@ velocity_event_handler(GtkRange *range, gpointer notused) assert(current_velocity); *current_velocity = gtk_range_get_value(range); + keyboard->current_velocity = gtk_range_get_value(range); } #ifdef HAVE_X11 @@ -1555,6 +1556,14 @@ init_gtk_1(int *argc, char ***argv) void init_gtk_2(void) { + /* PianoKeyboard widget. */ + keyboard = PIANO_KEYBOARD(piano_keyboard_new()); + + if (!enable_gui) { + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(keyboard)); + return; + } + GtkTable *table; GtkWidget *label; GtkCellRenderer *renderer; @@ -1563,9 +1572,7 @@ init_gtk_2(void) table = GTK_TABLE(gtk_table_new(4, 8, FALSE)); gtk_table_set_row_spacings(table, 5); gtk_table_set_col_spacings(table, 5); - - if (enable_gui) - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table)); /* Channel label and spin. */ label = gtk_label_new("Channel:"); @@ -1651,13 +1658,7 @@ init_gtk_2(void) g_signal_connect(G_OBJECT(sustain_button), "pressed", G_CALLBACK(sustain_event_handler), (void *)1); g_signal_connect(G_OBJECT(sustain_button), "released", G_CALLBACK(sustain_event_handler), (void *)0); - /* PianoKeyboard widget. */ - keyboard = PIANO_KEYBOARD(piano_keyboard_new()); - - if (enable_gui) - gtk_table_attach_defaults(table, GTK_WIDGET(keyboard), 0, 8, 3, 4); - else - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(keyboard)); + gtk_table_attach_defaults(table, GTK_WIDGET(keyboard), 0, 8, 3, 4); g_signal_connect(G_OBJECT(keyboard), "note-on", G_CALLBACK(note_on_event_handler), NULL); g_signal_connect(G_OBJECT(keyboard), "note-off", G_CALLBACK(note_off_event_handler), NULL); diff --git a/src/pianokeyboard.c b/src/pianokeyboard.c index 6ffff36..ff7cff8 100644 --- a/src/pianokeyboard.c +++ b/src/pianokeyboard.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "pianokeyboard.h" @@ -77,17 +79,12 @@ draw_keyboard_cue(PianoKeyboard *pk) static void draw_note(PianoKeyboard *pk, int note) { + GtkWidget *widget = GTK_WIDGET(pk); if (note < pk->min_note) return; if (note > pk->max_note) return; - int is_white, x, w, h; - - GdkColor black = {0, 0, 0, 0}; - GdkColor white = {0, 65535, 65535, 65535}; - - GdkGC *gc = GTK_WIDGET(pk)->style->fg_gc[0]; - GtkWidget *widget; + int is_white, x, w, h, pressed; is_white = pk->notes[note].white; @@ -95,28 +92,23 @@ draw_note(PianoKeyboard *pk, int note) w = pk->notes[note].w; h = pk->notes[note].h; - if (pk->notes[note].pressed || pk->notes[note].sustained) - is_white = !is_white; - - if (is_white) - gdk_gc_set_rgb_fg_color(gc, &white); - else - gdk_gc_set_rgb_fg_color(gc, &black); - - gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, TRUE, x, 0, w, h); - gdk_gc_set_rgb_fg_color(gc, &black); - gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, FALSE, x, 0, w, h); - - if (pk->enable_keyboard_cue) - draw_keyboard_cue(pk); - - /* We need to redraw black keys that partially obscure the white one. */ + pressed = (int)(pk->notes[note].pressed || pk->notes[note].sustained); + + if (is_white) { + piano_keyboard_draw_white_key(widget, x, 0, w, h, pressed, pk->notes[note].velocity); + } else { + piano_keyboard_draw_black_key(widget, x, 0, w, h, pressed, pk->notes[note].velocity); + } + if (note < NNOTES - 2 && !pk->notes[note + 1].white) draw_note(pk, note + 1); - + if (note > 0 && !pk->notes[note - 1].white) draw_note(pk, note - 1); - + + if (pk->enable_keyboard_cue) + draw_keyboard_cue(pk); + /* * XXX: This doesn't really belong here. Originally I wanted to pack PianoKeyboard into GtkFrame * packed into GtkAlignment. I failed to make it behave the way I want. GtkFrame would need @@ -147,6 +139,7 @@ press_key(PianoKeyboard *pk, int key) pk->notes[key].sustained = 0; pk->notes[key].pressed = 1; + pk->notes[key].velocity = pk->current_velocity; g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key); draw_note(pk, key); @@ -487,7 +480,7 @@ recompute_dimensions(PianoKeyboard *pk) (white_key * key_width) - (black_key_width * black_key_left_shift(note)); pk->notes[note].w = black_key_width; - pk->notes[note].h = (height * 2) / 3; + pk->notes[note].h = (height * 3) / 5; pk->notes[note].white = 0; continue; } @@ -623,10 +616,11 @@ piano_keyboard_sustain_release(PianoKeyboard *pk) } void -piano_keyboard_set_note_on(PianoKeyboard *pk, int note) +piano_keyboard_set_note_on(PianoKeyboard *pk, int note, int vel) { if (pk->notes[note].pressed == 0) { pk->notes[note].pressed = 1; + pk->notes[note].velocity = vel; draw_note(pk, note); } } @@ -673,3 +667,137 @@ piano_keyboard_enable_all_midi_notes(PianoKeyboard* pk) recompute_dimensions(pk); } +void +piano_keyboard_draw_white_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int vel) +{ + cairo_pattern_t *pat; + GdkWindow *window = widget->window; + cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window)); + cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER); + cairo_set_line_width(c, 1); + + cairo_rectangle(c, x, y, w, h); + cairo_clip_preserve(c); + + pat = cairo_pattern_create_linear (x, y, x, y + h); + cairo_pattern_add_color_stop_rgb (pat, 0.0, 0.25, 0.25, 0.2); + cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.957, 0.914, 0.925); + cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.796, 0.787, 0.662); + cairo_set_source(c, pat); + cairo_fill(c); + + cairo_move_to(c, x + 0.5, y); + cairo_line_to(c, x + 0.5, y + h); + cairo_set_source_rgba(c, 1, 1, 1, 0.75); + cairo_stroke(c); + + cairo_move_to(c, x + w - 0.5, y); + cairo_line_to(c, x + w - 0.5, y + h); + cairo_set_source_rgba(c, 0, 0, 0, 0.5); + cairo_stroke(c); + + if (pressed) + piano_keyboard_draw_pressed(c, x, y, w, h, vel); + + piano_keyboard_draw_key_shadow(c, x, y, w, h); + + cairo_destroy(c); +} + +void +piano_keyboard_draw_black_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int vel) +{ + cairo_pattern_t *pat; + GdkWindow *window = widget->window; + cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window)); + cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER); + cairo_set_line_width(c, 1); + + cairo_rectangle(c, x, y, w, h); + cairo_clip_preserve(c); + + pat = cairo_pattern_create_linear (x, y, x, y + h); + cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0); + cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.27, 0.27, 0.27); + cairo_pattern_add_color_stop_rgb (pat, 1.0, 0, 0, 0); + cairo_set_source(c, pat); + cairo_fill(c); + + pat = cairo_pattern_create_linear (x + 1, y, x + 1, y + h - w); + cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0); + cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.55, 0.55, 0.55); + cairo_pattern_add_color_stop_rgb (pat, 0.5, 0.45, 0.45, 0.45); + cairo_pattern_add_color_stop_rgb (pat, 0.5001, 0.35, 0.35, 0.35); + cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.25, 0.25, 0.25); + cairo_set_source(c, pat); + cairo_rectangle(c, x + 1, y, w - 2, y + h - w); + cairo_fill(c); + + if (pressed) + piano_keyboard_draw_pressed(c, x, y, w, h, vel); + + piano_keyboard_draw_key_shadow(c, x, y, w, h); + + cairo_destroy(c); +} + +void +piano_keyboard_draw_pressed (cairo_t *c, int x, int y, int w, int h, int vel) +{ + float m = w * .15; // margin + float s = w - m * 2.; // size + float _vel = ((float)vel / 127.); + float hue = _vel * 140 + 220; // hue 220 .. 360 - blue over pink to red + float sat = .5 + _vel * 0.3; // saturation 0.5 .. 0.8 + float val = 1. - _vel * 0.2; // lightness 1.0 .. 0.8 + cairo_rectangle(c, x + m, y + h - m - s * 2, s, s * 2); + hsv HSV = {hue, sat, val}; + rgb RGB = hsv2rgb(HSV); + cairo_set_source_rgb(c, RGB.r, RGB.g, RGB.b); + cairo_fill(c); +} + +void +piano_keyboard_draw_key_shadow (cairo_t *c, int x, int y, int w, int h) +{ + cairo_pattern_t *pat; + pat = cairo_pattern_create_linear (x, y, x, y + (int)(h * 0.2)); + cairo_pattern_add_color_stop_rgba (pat, 0.0, 0, 0, 0, 0.4); + cairo_pattern_add_color_stop_rgba (pat, 1.0, 0, 0, 0, 0); + cairo_rectangle(c, x, y, w, (int)(h * 0.2)); + cairo_set_source(c, pat); + cairo_fill(c); +} + +rgb +hsv2rgb(hsv HSV) +{ + rgb RGB; + double H = HSV.h, S = HSV.s, V = HSV.v, + P, Q, T, + fract; + + (H == 360.)?(H = 0.):(H /= 60.); + fract = H - floor(H); + + P = V*(1. - S); + Q = V*(1. - S*fract); + T = V*(1. - S*(1. - fract)); + + if (0. <= H && H < 1.) + RGB = (rgb){.r = V, .g = T, .b = P}; + else if (1. <= H && H < 2.) + RGB = (rgb){.r = Q, .g = V, .b = P}; + else if (2. <= H && H < 3.) + RGB = (rgb){.r = P, .g = V, .b = T}; + else if (3. <= H && H < 4.) + RGB = (rgb){.r = P, .g = Q, .b = V}; + else if (4. <= H && H < 5.) + RGB = (rgb){.r = T, .g = P, .b = V}; + else if (5. <= H && H < 6.) + RGB = (rgb){.r = V, .g = P, .b = Q}; + else + RGB = (rgb){.r = 0., .g = 0., .b = 0.}; + + return RGB; +} diff --git a/src/pianokeyboard.h b/src/pianokeyboard.h index 817c84e..8ad3820 100644 --- a/src/pianokeyboard.h +++ b/src/pianokeyboard.h @@ -6,10 +6,10 @@ * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -65,6 +65,7 @@ struct Note { int w; /* Width of the key, in pixels. */ int h; /* Height of the key, in pixels. */ int white; /* 1 if key is white; 0 otherwise. */ + int velocity; }; struct _PianoKeyboard @@ -78,6 +79,7 @@ struct _PianoKeyboard int note_being_pressed_using_mouse; int min_note; int max_note; + int current_velocity; volatile struct Note notes[NNOTES]; /* Table used to translate from PC keyboard character to MIDI note number. */ GArray *key_bindings; @@ -88,16 +90,33 @@ struct _PianoKeyboardClass GtkDrawingAreaClass parent_class; }; +typedef struct { + double r; + double g; + double b; +} rgb; + +typedef struct { + double h; + double s; + double v; +} hsv; + GType piano_keyboard_get_type (void) G_GNUC_CONST; GtkWidget* piano_keyboard_new (void); 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_on (PianoKeyboard *pk, int note, int vel); void piano_keyboard_set_note_off (PianoKeyboard *pk, int note); void piano_keyboard_set_keyboard_cue (PianoKeyboard *pk, int enabled); void piano_keyboard_set_octave (PianoKeyboard *pk, int octave); gboolean piano_keyboard_set_keyboard_layout (PianoKeyboard *pk, const char *layout); void piano_keyboard_enable_all_midi_notes(PianoKeyboard* pk); +void piano_keyboard_draw_white_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int val); +void piano_keyboard_draw_black_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int val); +void piano_keyboard_draw_pressed (cairo_t *c, int x, int y, int w, int h, int val); +void piano_keyboard_draw_key_shadow (cairo_t *c, int x, int y, int w, int h); +rgb hsv2rgb(hsv HSV); G_END_DECLS