diff --git a/AUTHORS b/AUTHORS index ca7b296..cff15ac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,4 @@ Edward Tomasz NapieraƂa - +Hans Petter Selasky +Jeff Snyder +Dan Muresan diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e3ed6e..b96b54d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ cmake_minimum_required(VERSION 2.8) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") -set(VERSION "2.6") +set(VERSION "2.7.1") set(JackEnable ON CACHE BOOL "Enable support for Jack") set(LashEnable ON CACHE BOOL "Enable support for Lash") diff --git a/Makefile b/Makefile index 27b7b83..d2aaf5f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION?=2.6 +VERSION?=2.7.1 help: @echo "Targets: configure all clean install package" diff --git a/NEWS b/NEWS index 3be85f8..8bcef5e 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,26 @@ +User-visible changes between 2.6 and 2.7.1 include: + + - fix a warning regarding the redefinition of NNOTES + + - raise NNOTES from 127 to 128 (only notes 0..125 were being shown + because 127 was < NNOTES and 126 is black) + + - only show the keys that exist on a real piano by default, and add a + "-f" ('full midi keyboard') command line option to show all midi + notes. + + - make the dimensions slightly closer to a real piano by: + > making the black key hight two third of the white key height + > making the black key positions vary based on their position in + the octave + + - adding a QWERTY_REV layout that has the lower keys on the upper row + of the keyboard + + - adding QWERTY_UK and QWERTY_UK_REV layouts that extend the QWERTY + and QWERTY_REV layouts with the punctuation keys available on those + rows + User-visible changes between 2.5 and 2.6 include: - Bugfix: A key volume of zero means key off. diff --git a/src/jack-keyboard.c b/src/jack-keyboard.c index 266a0c3..033137e 100644 --- a/src/jack-keyboard.c +++ b/src/jack-keyboard.c @@ -61,8 +61,6 @@ #include #endif -#define NNOTES 127 - #define VELOCITY_MAX 127 #define VELOCITY_HIGH 100 #define VELOCITY_NORMAL 64 @@ -1699,11 +1697,11 @@ show_version(void) void usage(void) { - fprintf(stderr, "usage: jack-keyboard [-CGKTVktur] [ -a ] [-c ] [-b ] [-p ] [-l ]\n"); + fprintf(stderr, "usage: jack-keyboard [-CGKTVkturf] [ -a ] [-c ] [-b ] [-p ] [-l ]\n"); fprintf(stderr, " where is MIDI channel to use for output, from 1 to 16,\n"); fprintf(stderr, " is MIDI bank to use, from 0 to 16383,\n"); fprintf(stderr, " is MIDI program to use, from 0 to 127,\n"); - fprintf(stderr, " and is QWERTY, QWERTZ, AZERTY or DVORAK.\n"); + fprintf(stderr, " and is QWERTY, QWERTY_REV, QWERTY_UK, QWERTY_UK_REV, QWERTZ, AZERTY or DVORAK.\n"); fprintf(stderr, "See manual page for details.\n"); exit(EX_USAGE); @@ -1712,7 +1710,7 @@ usage(void) int main(int argc, char *argv[]) { - int ch, enable_keyboard_cue = 0, initial_channel = 1, initial_bank = 0, initial_program = 0; + int ch, enable_keyboard_cue = 0, initial_channel = 1, initial_bank = 0, initial_program = 0, full_midi_keyboard = 0; char *keyboard_layout = NULL, *autoconnect_port_name = NULL; #ifdef HAVE_LASH @@ -1729,7 +1727,7 @@ main(int argc, char *argv[]) g_log_set_default_handler(log_handler, NULL); - while ((ch = getopt(argc, argv, "CGKTVa:nktur:c:b:p:l:")) != -1) { + while ((ch = getopt(argc, argv, "CGKTVa:nktur:c:b:p:l:f")) != -1) { switch (ch) { case 'C': enable_keyboard_cue = 1; @@ -1826,6 +1824,10 @@ main(int argc, char *argv[]) break; + case 'f': + full_midi_keyboard = 1; + break; + case '?': default: usage(); @@ -1837,11 +1839,14 @@ main(int argc, char *argv[]) init_gtk_2(); + if (full_midi_keyboard) + piano_keyboard_enable_all_midi_notes(keyboard); + if (keyboard_layout != NULL) { int ret = piano_keyboard_set_keyboard_layout(keyboard, keyboard_layout); if (ret) { - g_critical("Invalid layout, proper choices are QWERTY, QWERTZ, AZERTY and DVORAK."); + g_critical("Invalid layout, proper choices are QWERTY, QWERTY_REV, QWERTY_UK, QWERTY_UK_REV, QWERTZ, AZERTY and DVORAK."); exit(EX_USAGE); } } diff --git a/src/pianokeyboard.c b/src/pianokeyboard.c index 6e7c57b..9f6628d 100644 --- a/src/pianokeyboard.c +++ b/src/pianokeyboard.c @@ -77,6 +77,10 @@ draw_keyboard_cue(PianoKeyboard *pk) static void draw_note(PianoKeyboard *pk, int note) { + if (note < pk->min_note) + return; + if (note > pk->max_note) + return; int is_white, x, w, h; GdkColor black = {0, 0, 0, 0}; @@ -256,7 +260,7 @@ bind_keys_qwerty(PianoKeyboard *pk) bind_key(pk, "m", 23); /* Upper keyboard row, first octave - "qwertyu". */ - bind_key(pk, "q", 24); + bind_key(pk, "q", 24); /* C1 */ bind_key(pk, "2", 25); bind_key(pk, "w", 26); bind_key(pk, "3", 27); @@ -270,13 +274,93 @@ bind_keys_qwerty(PianoKeyboard *pk) bind_key(pk, "u", 35); /* Upper keyboard row, the rest - "iop". */ - bind_key(pk, "i", 36); + bind_key(pk, "i", 36); /* C2 */ bind_key(pk, "9", 37); bind_key(pk, "o", 38); bind_key(pk, "0", 39); bind_key(pk, "p", 40); } +static void +bind_keys_qwerty_uk(PianoKeyboard *pk) +{ + bind_keys_qwerty(pk); + + /* Lower keyboard row - "zxcvbnm". */ + bind_key(pk, "backslash", 11); /* B0 */ + /* ... */ + bind_key(pk, "comma", 24); /* C1 */ + bind_key(pk, "l", 25); + bind_key(pk, "period", 26); + bind_key(pk, "semicolon", 27); + bind_key(pk, "slash", 28); + + /* Upper keyboard row, the rest - "iop". */ + bind_key(pk, "bracketleft", 41); /* F6 */ + bind_key(pk, "equal", 42); + bind_key(pk, "bracketright", 43); +} +static void +bind_keys_qwerty_rev(PianoKeyboard *pk) +{ + clear_notes(pk); + + /* Lower keyboard row - "zxcvbnm". */ + bind_key(pk, "z", 24); /* C1 */ + bind_key(pk, "s", 25); + bind_key(pk, "x", 26); + bind_key(pk, "d", 27); + bind_key(pk, "c", 28); + bind_key(pk, "v", 29); + bind_key(pk, "g", 30); + bind_key(pk, "b", 31); + bind_key(pk, "h", 32); + bind_key(pk, "n", 33); + bind_key(pk, "j", 34); + bind_key(pk, "m", 35); + + /* Upper keyboard row, first octave - "qwertyu". */ + bind_key(pk, "q", 12); /* C0 */ + bind_key(pk, "2", 13); + bind_key(pk, "w", 14); + bind_key(pk, "3", 15); + bind_key(pk, "e", 16); + bind_key(pk, "r", 17); + bind_key(pk, "5", 18); + bind_key(pk, "t", 19); + bind_key(pk, "6", 20); + bind_key(pk, "y", 21); + bind_key(pk, "7", 22); + bind_key(pk, "u", 23); + + /* Upper keyboard row, the rest - "iop". */ + bind_key(pk, "i", 24); /* C1 */ + bind_key(pk, "9", 25); + bind_key(pk, "o", 26); + bind_key(pk, "0", 27); + bind_key(pk, "p", 28); +} + +static void +bind_keys_qwerty_uk_rev(PianoKeyboard *pk) +{ + bind_keys_qwerty_rev(pk); + + /* Lower keyboard row - "zxcvbnm". */ + bind_key(pk, "backslash", 23); /* B-1 */ + /* ... */ + bind_key(pk, "comma", 36); /* C2 */ + bind_key(pk, "l", 37); + bind_key(pk, "period", 38); + bind_key(pk, "semicolon", 39); + bind_key(pk, "slash", 40); + + /* Upper keyboard row, the rest - "iop". */ + bind_key(pk, "bracketleft", 29); + bind_key(pk, "equal", 30); + bind_key(pk, "bracketright", 31); +} + static void bind_keys_qwertz(PianoKeyboard *pk) { @@ -431,8 +515,8 @@ get_note_for_xy(PianoKeyboard *pk, int x, int y) height = GTK_WIDGET(pk)->allocation.height; - if (y <= height / 2) { - for (note = 0; note < NNOTES - 1; note++) { + if (y <= ((height * 2) / 3)) { /* might be a black key */ + for (note = 0; note <= pk->max_note; ++note) { if (pk->notes[note].white) continue; @@ -441,7 +525,7 @@ get_note_for_xy(PianoKeyboard *pk, int x, int y) } } - for (note = 0; note < NNOTES - 1; note++) { + for (note = 0; note <= pk->max_note; ++note) { if (!pk->notes[note].white) continue; @@ -534,13 +618,48 @@ piano_keyboard_size_request(GtkWidget *widget, GtkRequisition *requisition) requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT; } +static int is_black(int key) +{ + int note_in_octave = key % 12; + if( note_in_octave == 1 || note_in_octave == 3 || + note_in_octave == 6 || note_in_octave == 8 || note_in_octave == 10) + return 1; + return 0; +} + +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; +} + static void recompute_dimensions(PianoKeyboard *pk) { - int number_of_white_keys, key_width, black_key_width, useful_width, note, - white_key = 0, note_in_octave, width, height; + int number_of_white_keys = 0, skipped_white_keys = 0, key_width, black_key_width, useful_width, note, + white_key, width, height; - number_of_white_keys = (NNOTES - 1) * (7.0 / 12.0); + 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; width = GTK_WIDGET(pk)->allocation.width; height = GTK_WIDGET(pk)->allocation.height; @@ -550,18 +669,15 @@ recompute_dimensions(PianoKeyboard *pk) 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) { - + 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 / 2; + pk->notes[note].h = (height * 2) / 3; pk->notes[note].white = 0; - continue; } @@ -663,6 +779,8 @@ piano_keyboard_new(void) pk->note_being_pressed_using_mouse = -1; memset((void *)pk->notes, 0, sizeof(struct Note) * NNOTES); pk->key_bindings = g_hash_table_new(g_str_hash, g_str_equal); + pk->min_note = PIANO_MIN_NOTE; + pk->max_note = PIANO_MAX_NOTE; bind_keys_qwerty(pk); return (widget); @@ -727,6 +845,15 @@ piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout) if (!strcasecmp(layout, "QWERTY")) { bind_keys_qwerty(pk); + } else if (!strcasecmp(layout, "QWERTY_REV")) { + bind_keys_qwerty_rev(pk); + + } else if (!strcasecmp(layout, "QWERTY_UK")) { + bind_keys_qwerty_uk(pk); + + } else if (!strcasecmp(layout, "QWERTY_UK_REV")) { + bind_keys_qwerty_uk_rev(pk); + } else if (!strcasecmp(layout, "QWERTZ")) { bind_keys_qwertz(pk); @@ -744,3 +871,11 @@ piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout) return (FALSE); } +void +piano_keyboard_enable_all_midi_notes(PianoKeyboard* pk) +{ + pk->min_note = 0; + pk->max_note = NNOTES-1; + recompute_dimensions(pk); +} + diff --git a/src/pianokeyboard.h b/src/pianokeyboard.h index 8278b99..559302c 100644 --- a/src/pianokeyboard.h +++ b/src/pianokeyboard.h @@ -42,7 +42,17 @@ G_BEGIN_DECLS typedef struct _PianoKeyboard PianoKeyboard; typedef struct _PianoKeyboardClass PianoKeyboardClass; -#define NNOTES 127 +/* A note about note numbers: + + 0 = C-1 (midi minmum) + 21 = A0 (piano minimum) + 60 = C4 (middle C) + 108 = C7 (piano maximum) + 127 = G9 (midi maximum) +*/ +#define NNOTES 128 +#define PIANO_MIN_NOTE 21 +#define PIANO_MAX_NOTE 108 #define OCTAVE_MIN -1 #define OCTAVE_MAX 7 @@ -66,6 +76,8 @@ struct _PianoKeyboard int octave; int widget_margin; int note_being_pressed_using_mouse; + int min_note; + int max_note; volatile struct Note notes[NNOTES]; /* Table used to translate from PC keyboard character to MIDI note number. */ GHashTable *key_bindings; @@ -85,6 +97,7 @@ 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); G_END_DECLS