From e0edca5a2abd65d869348e4bddb9d07ecc156450 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 2 Aug 2010 21:52:21 +0000 Subject: [PATCH] first, incomplete pass at step entry dialog, along with various SVG and PNG files for notes and dynamics notation git-svn-id: svn://localhost/ardour2/branches/3.0@7529 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour_ui.cc | 2 +- gtk2_ardour/gtk_pianokeyboard.c | 688 +++++++++++++++++++++++++ gtk2_ardour/gtk_pianokeyboard.h | 66 +++ gtk2_ardour/icons/chord.png | Bin 0 -> 769 bytes gtk2_ardour/icons/eighthnote.png | Bin 0 -> 597 bytes gtk2_ardour/icons/forte.png | Bin 0 -> 517 bytes gtk2_ardour/icons/fortissimo.png | Bin 0 -> 775 bytes gtk2_ardour/icons/fortississimo.png | Bin 0 -> 963 bytes gtk2_ardour/icons/halfnote.png | Bin 0 -> 430 bytes gtk2_ardour/icons/mezzforte.png | Bin 0 -> 802 bytes gtk2_ardour/icons/mezzoforte.png | Bin 0 -> 802 bytes gtk2_ardour/icons/mezzopiano.png | Bin 0 -> 784 bytes gtk2_ardour/icons/pianissimo.png | Bin 0 -> 696 bytes gtk2_ardour/icons/pianississimo.png | Bin 0 -> 746 bytes gtk2_ardour/icons/piano.png | Bin 0 -> 481 bytes gtk2_ardour/icons/quarternote.png | Bin 0 -> 335 bytes gtk2_ardour/icons/sixteenthnote.png | Bin 0 -> 724 bytes gtk2_ardour/icons/sixtyfourthnote.png | Bin 0 -> 1004 bytes gtk2_ardour/icons/thirtysecondnote.png | Bin 0 -> 858 bytes gtk2_ardour/icons/wholenote.png | Bin 0 -> 414 bytes gtk2_ardour/midi_time_axis.cc | 71 +-- gtk2_ardour/midi_time_axis.h | 2 + gtk2_ardour/step_entry.cc | 243 +++++++++ gtk2_ardour/step_entry.h | 84 +++ gtk2_ardour/wscript | 2 + icons/Music_dynamic_forte.svg | 20 + icons/Music_dynamic_fortissimo.svg | 20 + icons/Music_dynamic_fortississimo.svg | 20 + icons/Music_dynamic_mezzo_forte.svg | 20 + icons/Music_dynamic_pianissimo.svg | 20 + icons/Music_dynamic_pianississimo.svg | 20 + icons/Music_dynamic_piano.svg | 20 + icons/chord.svg | 66 +++ icons/eighthnote.svg | 64 +++ icons/halfnote.svg | 64 +++ icons/mezzopiano.svg | 75 +++ icons/quarternote.svg | 64 +++ icons/sixteenthnote.svg | 64 +++ icons/sixyfourthnote.svg | 64 +++ icons/thirtysecondnote.svg | 64 +++ icons/wholenote.svg | 64 +++ 41 files changed, 1857 insertions(+), 30 deletions(-) create mode 100644 gtk2_ardour/gtk_pianokeyboard.c create mode 100644 gtk2_ardour/gtk_pianokeyboard.h create mode 100644 gtk2_ardour/icons/chord.png create mode 100644 gtk2_ardour/icons/eighthnote.png create mode 100644 gtk2_ardour/icons/forte.png create mode 100644 gtk2_ardour/icons/fortissimo.png create mode 100644 gtk2_ardour/icons/fortississimo.png create mode 100644 gtk2_ardour/icons/halfnote.png create mode 100644 gtk2_ardour/icons/mezzforte.png create mode 100644 gtk2_ardour/icons/mezzoforte.png create mode 100644 gtk2_ardour/icons/mezzopiano.png create mode 100644 gtk2_ardour/icons/pianissimo.png create mode 100644 gtk2_ardour/icons/pianississimo.png create mode 100644 gtk2_ardour/icons/piano.png create mode 100644 gtk2_ardour/icons/quarternote.png create mode 100644 gtk2_ardour/icons/sixteenthnote.png create mode 100644 gtk2_ardour/icons/sixtyfourthnote.png create mode 100644 gtk2_ardour/icons/thirtysecondnote.png create mode 100644 gtk2_ardour/icons/wholenote.png create mode 100644 gtk2_ardour/step_entry.cc create mode 100644 gtk2_ardour/step_entry.h create mode 100644 icons/Music_dynamic_forte.svg create mode 100644 icons/Music_dynamic_fortissimo.svg create mode 100644 icons/Music_dynamic_fortississimo.svg create mode 100644 icons/Music_dynamic_mezzo_forte.svg create mode 100644 icons/Music_dynamic_pianissimo.svg create mode 100644 icons/Music_dynamic_pianississimo.svg create mode 100644 icons/Music_dynamic_piano.svg create mode 100644 icons/chord.svg create mode 100644 icons/eighthnote.svg create mode 100644 icons/halfnote.svg create mode 100644 icons/mezzopiano.svg create mode 100644 icons/quarternote.svg create mode 100644 icons/sixteenthnote.svg create mode 100644 icons/sixyfourthnote.svg create mode 100644 icons/thirtysecondnote.svg create mode 100644 icons/wholenote.svg diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 004f37075d..14d8215a62 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -1555,7 +1555,7 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode) _session->cancel_audition (); return; } - + if (_session->config.get_external_sync()) { switch (_session->config.get_sync_source()) { case JACK: diff --git a/gtk2_ardour/gtk_pianokeyboard.c b/gtk2_ardour/gtk_pianokeyboard.c new file mode 100644 index 0000000000..58e742e32c --- /dev/null +++ b/gtk2_ardour/gtk_pianokeyboard.c @@ -0,0 +1,688 @@ +/*- + * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This is piano_keyboard, piano keyboard-like GTK+ widget. It contains + * no MIDI-specific code. + * + * For questions and comments, contact Edward Tomasz Napierala . + */ + +#include +#include +#include +#include +#include +#include + +#include "gtk_pianokeyboard.h" + +#define PIANO_KEYBOARD_DEFAULT_WIDTH 730 +#define PIANO_KEYBOARD_DEFAULT_HEIGHT 70 + +enum { + NOTE_ON_SIGNAL, + NOTE_OFF_SIGNAL, + LAST_SIGNAL +}; + +static guint piano_keyboard_signals[LAST_SIGNAL] = { 0 }; + +static void +draw_keyboard_cue(PianoKeyboard *pk) +{ + int w = pk->notes[0].w; + int h = pk->notes[0].h; + + GdkGC *gc = GTK_WIDGET(pk)->style->fg_gc[0]; + + 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; + + gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_lower_row].x + 3, + h - 6, pk->notes[last_note_in_lower_row].x + w - 3, h - 6); + + gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_higher_row].x + 3, + h - 9, pk->notes[last_note_in_higher_row].x + w - 3, h - 9); +} + +static void +draw_note(PianoKeyboard *pk, int note) +{ + 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 = 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) + 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. */ + 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); + + /* + * 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 + * to adapt to the "proper" size of PianoKeyboard, i.e. to the useful_width, not allocated width; + * that didn't work. + */ + widget = GTK_WIDGET(pk); + gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, pk->widget_margin, 0, + widget->allocation.width - pk->widget_margin * 2 + 1, widget->allocation.height); +} + +static int +press_key(PianoKeyboard *pk, int key) +{ + assert(key >= 0); + assert(key < NNOTES); + + pk->maybe_stop_sustained_notes = 0; + + /* This is for keyboard autorepeat protection. */ + if (pk->notes[key].pressed) + return 0; + + if (pk->sustain_new_notes) + pk->notes[key].sustained = 1; + else + pk->notes[key].sustained = 0; + + pk->notes[key].pressed = 1; + + g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key); + draw_note(pk, key); + + return 1; +} + +static int +release_key(PianoKeyboard *pk, int key) +{ + assert(key >= 0); + assert(key < NNOTES); + + pk->maybe_stop_sustained_notes = 0; + + if (!pk->notes[key].pressed) + return 0; + + if (pk->sustain_new_notes) + pk->notes[key].sustained = 1; + + pk->notes[key].pressed = 0; + + if (pk->notes[key].sustained) + return 0; + + g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", key); + draw_note(pk, key); + + return 1; +} + +static void +stop_unsustained_notes(PianoKeyboard *pk) +{ + 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); + draw_note(pk, i); + } + } +} + +static void +stop_sustained_notes(PianoKeyboard *pk) +{ + int i; + + for (i = 0; i < NNOTES; i++) { + if (pk->notes[i].sustained) { + pk->notes[i].pressed = 0; + pk->notes[i].sustained = 0; + g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i); + draw_note(pk, i); + } + } +} + +static int +key_binding(PianoKeyboard *pk, const char *key) +{ + gpointer notused, note; + gboolean found; + + assert(pk->key_bindings != NULL); + + found = g_hash_table_lookup_extended(pk->key_bindings, key, ¬used, ¬e); + + if (!found) + return -1; + + return (intptr_t)note; +} + +static void +bind_key(PianoKeyboard *pk, const char *key, int note) +{ + assert(pk->key_bindings != NULL); + + g_hash_table_insert(pk->key_bindings, (gpointer)key, (gpointer)((intptr_t)note)); +} + +static void +clear_notes(PianoKeyboard *pk) +{ + assert(pk->key_bindings != NULL); + + g_hash_table_remove_all(pk->key_bindings); +} + +static void +bind_keys_qwerty(PianoKeyboard *pk) +{ + clear_notes(pk); + + /* Lower keyboard row - "zxcvbnm". */ + bind_key(pk, "z", 12); /* C0 */ + bind_key(pk, "s", 13); + bind_key(pk, "x", 14); + bind_key(pk, "d", 15); + bind_key(pk, "c", 16); + bind_key(pk, "v", 17); + bind_key(pk, "g", 18); + bind_key(pk, "b", 19); + bind_key(pk, "h", 20); + bind_key(pk, "n", 21); + bind_key(pk, "j", 22); + bind_key(pk, "m", 23); + + /* Upper keyboard row, first octave - "qwertyu". */ + bind_key(pk, "q", 24); + bind_key(pk, "2", 25); + bind_key(pk, "w", 26); + bind_key(pk, "3", 27); + bind_key(pk, "e", 28); + bind_key(pk, "r", 29); + bind_key(pk, "5", 30); + bind_key(pk, "t", 31); + bind_key(pk, "6", 32); + bind_key(pk, "y", 33); + bind_key(pk, "7", 34); + bind_key(pk, "u", 35); + + /* Upper keyboard row, the rest - "iop". */ + bind_key(pk, "i", 36); + bind_key(pk, "9", 37); + bind_key(pk, "o", 38); + bind_key(pk, "0", 39); + bind_key(pk, "p", 40); +} + +static void +bind_keys_qwertz(PianoKeyboard *pk) +{ + bind_keys_qwerty(pk); + + /* The only difference between QWERTY and QWERTZ is that the "y" and "z" are swapped together. */ + bind_key(pk, "y", 12); + bind_key(pk, "z", 33); +} + +static void +bind_keys_azerty(PianoKeyboard *pk) +{ + clear_notes(pk); + + /* Lower keyboard row - "wxcvbn,". */ + bind_key(pk, "w", 12); /* C0 */ + bind_key(pk, "s", 13); + bind_key(pk, "x", 14); + bind_key(pk, "d", 15); + bind_key(pk, "c", 16); + bind_key(pk, "v", 17); + bind_key(pk, "g", 18); + bind_key(pk, "b", 19); + bind_key(pk, "h", 20); + bind_key(pk, "n", 21); + bind_key(pk, "j", 22); + bind_key(pk, "comma", 23); + + /* Upper keyboard row, first octave - "azertyu". */ + bind_key(pk, "a", 24); + bind_key(pk, "eacute", 25); + bind_key(pk, "z", 26); + bind_key(pk, "quotedbl", 27); + bind_key(pk, "e", 28); + bind_key(pk, "r", 29); + bind_key(pk, "parenleft", 30); + bind_key(pk, "t", 31); + bind_key(pk, "minus", 32); + bind_key(pk, "y", 33); + bind_key(pk, "egrave", 34); + bind_key(pk, "u", 35); + + /* Upper keyboard row, the rest - "iop". */ + bind_key(pk, "i", 36); + bind_key(pk, "ccedilla", 37); + bind_key(pk, "o", 38); + bind_key(pk, "agrave", 39); + bind_key(pk, "p", 40); +} + +static gint +keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer notused) +{ + int note; + char *key; + guint keyval; + GdkKeymapKey kk; + PianoKeyboard *pk = PIANO_KEYBOARD(mk); + + /* We're not using event->keyval, because we need keyval with level set to 0. + E.g. if user holds Shift and presses '7', we want to get a '7', not '&'. */ + kk.keycode = event->hardware_keycode; + kk.level = 0; + kk.group = 0; + + keyval = gdk_keymap_lookup_key(NULL, &kk); + + key = gdk_keyval_name(gdk_keyval_to_lower(keyval)); + + if (key == NULL) { + g_message("gtk_keyval_name() returned NULL; please report this."); + return FALSE; + } + + 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; + } + + note += pk->octave * 12; + + assert(note >= 0); + assert(note < NNOTES); + + if (event->type == GDK_KEY_PRESS) { + press_key(pk, note); + + } else if (event->type == GDK_KEY_RELEASE) { + release_key(pk, note); + } + + return TRUE; +} + +static int +get_note_for_xy(PianoKeyboard *pk, int x, int y) +{ + 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) + continue; + + 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) + continue; + + if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w) + return note; + } + + return -1; +} + +static gboolean +mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer notused) +{ + int x = event->x; + int y = event->y; + + int note = get_note_for_xy(pk, x, y); + + if (event->button != 1) + 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) + release_key(pk, pk->note_being_pressed_using_mouse); + + press_key(pk, note); + 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) + release_key(pk, pk->note_being_pressed_using_mouse); + } + + pk->note_being_pressed_using_mouse = -1; + + } + + return TRUE; +} + +static gboolean +mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer notused) +{ + int note; + + if ((event->state & GDK_BUTTON1_MASK) == 0) + return TRUE; + + note = get_note_for_xy(pk, event->x, event->y); + + if (note != pk->note_being_pressed_using_mouse && note >= 0) { + + if (pk->note_being_pressed_using_mouse >= 0) + release_key(pk, pk->note_being_pressed_using_mouse); + press_key(pk, note); + pk->note_being_pressed_using_mouse = note; + } + + return TRUE; +} + +static gboolean +piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event) +{ + int i; + PianoKeyboard *pk = PIANO_KEYBOARD(widget); + + for (i = 0; i < NNOTES; i++) + draw_note(pk, i); + + return TRUE; +} + +static void +piano_keyboard_size_request(GtkWidget *widget, GtkRequisition *requisition) +{ + requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH; + requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT; +} + +static void +recompute_dimensions(PianoKeyboard *pk) +{ + int number_of_white_keys = (NNOTES - 1) * (7.0 / 12.0); + + int key_width; + int black_key_width; + int useful_width; + + int note; + int white_key = 0; + int note_in_octave; + + int width = GTK_WIDGET(pk)->allocation.width; + int height = GTK_WIDGET(pk)->allocation.height; + + 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) { + + /* This note is black key. */ + pk->notes[note].x = pk->widget_margin + white_key * key_width - black_key_width / 2; + pk->notes[note].w = black_key_width; + pk->notes[note].h = height / 2; + pk->notes[note].white = 0; + + continue; + } + + /* This note is white key. */ + pk->notes[note].x = pk->widget_margin + white_key * key_width; + pk->notes[note].w = key_width; + pk->notes[note].h = height; + pk->notes[note].white = 1; + + white_key++; + } +} + +static void +piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + /* XXX: Are these two needed? */ + g_return_if_fail(widget != NULL); + g_return_if_fail(allocation != NULL); + + widget->allocation = *allocation; + + recompute_dimensions(PIANO_KEYBOARD(widget)); + + if (GTK_WIDGET_REALIZED(widget)) { + gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); + } +} + +static void +piano_keyboard_class_init(PianoKeyboardClass *klass) +{ + GtkWidgetClass *widget_klass; + + /* Set up signals. */ + piano_keyboard_signals[NOTE_ON_SIGNAL] = g_signal_new ("note-on", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + + piano_keyboard_signals[NOTE_OFF_SIGNAL] = g_signal_new ("note-off", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + + widget_klass = (GtkWidgetClass*) klass; + + widget_klass->expose_event = piano_keyboard_expose; + widget_klass->size_request = piano_keyboard_size_request; + widget_klass->size_allocate = piano_keyboard_size_allocate; +} + +static void +piano_keyboard_init(GtkWidget *mk) +{ + gtk_widget_add_events(mk, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); + + g_signal_connect(G_OBJECT(mk), "button-press-event", G_CALLBACK(mouse_button_event_handler), NULL); + g_signal_connect(G_OBJECT(mk), "button-release-event", G_CALLBACK(mouse_button_event_handler), NULL); + g_signal_connect(G_OBJECT(mk), "motion-notify-event", G_CALLBACK(mouse_motion_event_handler), NULL); + g_signal_connect(G_OBJECT(mk), "key-press-event", G_CALLBACK(keyboard_event_handler), NULL); + g_signal_connect(G_OBJECT(mk), "key-release-event", G_CALLBACK(keyboard_event_handler), NULL); +} + +GType +piano_keyboard_get_type(void) +{ + static GType mk_type = 0; + + if (!mk_type) { + static const GTypeInfo mk_info = { + sizeof(PianoKeyboardClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) piano_keyboard_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PianoKeyboard), + 0, /* n_preallocs */ + (GInstanceInitFunc) piano_keyboard_init, + }; + + mk_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "PianoKeyboard", &mk_info, 0); + } + + return mk_type; +} + +GtkWidget * +piano_keyboard_new(void) +{ + GtkWidget *widget = gtk_type_new(piano_keyboard_get_type()); + + PianoKeyboard *pk = PIANO_KEYBOARD(widget); + + pk->maybe_stop_sustained_notes = 0; + pk->sustain_new_notes = 0; + pk->enable_keyboard_cue = 0; + pk->octave = 4; + 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); + bind_keys_qwerty(pk); + + return widget; +} + +void +piano_keyboard_set_keyboard_cue(PianoKeyboard *pk, int enabled) +{ + pk->enable_keyboard_cue = enabled; +} + +void +piano_keyboard_sustain_press(PianoKeyboard *pk) +{ + if (!pk->sustain_new_notes) { + pk->sustain_new_notes = 1; + pk->maybe_stop_sustained_notes = 1; + } +} + +void +piano_keyboard_sustain_release(PianoKeyboard *pk) +{ + if (pk->maybe_stop_sustained_notes) + stop_sustained_notes(pk); + + pk->sustain_new_notes = 0; +} + +void +piano_keyboard_set_note_on(PianoKeyboard *pk, int note) +{ + if (pk->notes[note].pressed == 0) { + pk->notes[note].pressed = 1; + draw_note(pk, note); + } +} + +void +piano_keyboard_set_note_off(PianoKeyboard *pk, int note) +{ + if (pk->notes[note].pressed || pk->notes[note].sustained) { + pk->notes[note].pressed = 0; + pk->notes[note].sustained = 0; + draw_note(pk, note); + } +} + +void +piano_keyboard_set_octave(PianoKeyboard *pk, int octave) +{ + stop_unsustained_notes(pk); + pk->octave = octave; + gtk_widget_queue_draw(GTK_WIDGET(pk)); +} + +gboolean +piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout) +{ + assert(layout); + + if (!strcasecmp(layout, "QWERTY")) { + bind_keys_qwerty(pk); + + } else if (!strcasecmp(layout, "QWERTZ")) { + bind_keys_qwertz(pk); + + } else if (!strcasecmp(layout, "AZERTY")) { + bind_keys_azerty(pk); + + } else { + /* Unknown layout name. */ + return TRUE; + } + + return FALSE; +} + diff --git a/gtk2_ardour/gtk_pianokeyboard.h b/gtk2_ardour/gtk_pianokeyboard.h new file mode 100644 index 0000000000..27d9a8a5c2 --- /dev/null +++ b/gtk2_ardour/gtk_pianokeyboard.h @@ -0,0 +1,66 @@ +#ifndef __PIANO_KEYBOARD_H__ +#define __PIANO_KEYBOARD_H__ + +#include +#include + +G_BEGIN_DECLS + +#define TYPE_PIANO_KEYBOARD (piano_keyboard_get_type ()) +#define PIANO_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_PIANO_KEYBOARD, PianoKeyboard)) +#define PIANO_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_PIANO_KEYBOARD, PianoKeyboardClass)) +#define IS_PIANO_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_PIANO_KEYBOARD)) +#define IS_PIANO_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_PIANO_KEYBOARD)) +#define PIANO_KEYBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_PIANO_KEYBOARD, PianoKeyboardClass)) + +typedef struct _PianoKeyboard PianoKeyboard; +typedef struct _PianoKeyboardClass PianoKeyboardClass; + +#define NNOTES 127 + +#define OCTAVE_MIN -1 +#define OCTAVE_MAX 7 + +struct Note { + int pressed; /* 1 if key is in pressed down state. */ + int sustained; /* 1 if note is sustained. */ + int x; /* Distance between the left edge of the key + * and the left edge of the widget, in pixels. */ + 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. */ +}; + +struct _PianoKeyboard +{ + GtkDrawingArea da; + int maybe_stop_sustained_notes; + int sustain_new_notes; + int enable_keyboard_cue; + int octave; + int widget_margin; + int note_being_pressed_using_mouse; + volatile struct Note notes[NNOTES]; + /* Table used to translate from PC keyboard character to MIDI note number. */ + GHashTable *key_bindings; +}; + +struct _PianoKeyboardClass +{ + GtkDrawingAreaClass parent_class; +}; + +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_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); + +G_END_DECLS + +#endif /* __PIANO_KEYBOARD_H__ */ + diff --git a/gtk2_ardour/icons/chord.png b/gtk2_ardour/icons/chord.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7053dc9730cfbf8d6260fb68a86ee08dfc3e7e GIT binary patch literal 769 zcmV+c1OEJpP)gY8-f+B=uK_$jQQ84%w1Z7SRHj08II~FrHTy%Hu z%(6Ce9~g$2^UVLeb7r1*rfQnTRup2o9(Q6VcHt~O#Cs*zhpVlCLrjn3C0w-5@L4=E z2pnR%7_Z>rDb~;DdQ)4y+ye?%_O3X@v=g7;{2ulrLn^+dCluZqb=4%?i#srf&$?~3 z3F)V;}HUXiBb^u{=H* zV!8mghM3m637RqZ5O((fpHqmb!c({lH(*CMbTY*BKAsuj03O7RJ(yMZ2EBp%))~L1 z+p4N*8X=~I&iud>@VmIKyLdDGa4vZvra8Qzy~Xo-RKJ-{{VUR4beC|mwjKV4W4I8H z_5`mC=@l>H=^oZUu{5;1P1EmqqU8GcXd9v5V{ggzQKskrpE{0TCypDO#GANn45bao z5FN&A+DKo5PfM=9T+cXoDN3$?#tMX(E)Oy7(SA_Z@BxmNTsK=<;}Fx;cwPHiPB#DN z@KuQEp~2t~)BSkW#*P1492olW?$$2Dw6DocLo43Z)A!4E2V|^|{tNJr*uUN26S$}3 z`t){!S8#jD_16p!$UE8}!NqR>h4$}gwdDHXq;V+to09A2Lrf#=D7ikpk#S4OE4e;{ z4WSJ0ZoM@F&%iVA3_JtRz%%d+{J#QML;QaO8x*!}2q}oW00000NkvXXu0mjfTli&8 literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/eighthnote.png b/gtk2_ardour/icons/eighthnote.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c067504869a44e86c668ba581568e2cf31633d GIT binary patch literal 597 zcmV-b0;>IqP)spfvMZAlUs&ZtdY*!<(T{ZSM3H}QTeW8dubEDlD$c@6;|A7vMA|B0+wlErs z*qalDLwJxIg$tpG*K?zAI}~wvt>_1Q{cYWghoOl5YeAo57mnc(ej?&YDB|YTqEc1P zr>cCJs`4T(;|Kb2bJge{4aRU92_vD1`__zh;1?Lhdfdu_c4`b07!E}o%7UhTC$8nNO_kJkiDNLlQOxaNFt>0f*{I&hg2_L`#?ACr(PGSRQm&kYFX{yS_*Gy~S zt1lFB9q!@MQq3@rJEslz{84Tix_7B55 j?F;@@s>=6Gv4#2t%i?^U;ni-<00000NkvXXu0mjf$ixkz literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/forte.png b/gtk2_ardour/icons/forte.png new file mode 100644 index 0000000000000000000000000000000000000000..84ccc839dfa6c633d049995715118a5e59bc1eb3 GIT binary patch literal 517 zcmV+g0{Z=lP)`T_eL*FF~2kC%lR{Nu87ce zVG8%~3_IAw-H31)*c{#=2CS=HVK=Z>03Kr=i+R7&1$G|01?)ABViK>iwlgkl67Q?@ zma=WJ(4LPAyj{WeaWdP^U^_Qv{)i!8O6deH9-0ATFbB z+&=-YRQqrGb^C9?^@!MS^=N6vG=BNJAqyj$*aEy(!L~3l3fPu%o$jNN={70rKmLg- z!t(xo$IO`T-X(14-~QPw96u)~f#!LcsDW(YI`vxm1?+r{Z|VGVFC zJKw@AmW$|ac7Zj(>kf{jJ>2gKYk*1r=d+&Ee}cKgz#_sQY3<)w?4~g?00000NkvXX Hu0mjf#qZ%q literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/fortissimo.png b/gtk2_ardour/icons/fortissimo.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba6da19902353aa618c0d5c92031f2306864d1b GIT binary patch literal 775 zcmV+i1Ni)jP){S#Y1g(-5+AjzyNXf`WW}Yi{4DSl=ob-IW8MyY0VL)<8^a$_!#KV> ztNBUbze(^2=m2{4yX^4ifSTbSD&MEy53-o=0sfj~pMV082i~YQXZZcVyy4#i>on$r zYO`t08`RJa-+;DVHQK0ZdElGF-_W)`qdiGmz?GQ!+o5ecff--|II7w;hphqIwQU#h zL)S%z&vYAb0_f4?HsO@&dzEbjT2$)+Dh@xc`ff8qg~VL|#%#iTIQU0~V zPx$iZ;zl5Pf*@!D4mqf&UG> zTuT96&i7LhS;)YbMWp6+@GPbxut5ESy5QkAQ-4mx9!|^RJPD6G$)_WTTf@%-TT-4g90JA&|5B}HKC33- zpb3aBB;SD+)tae4_XpFMM+KBjV84pjjrI{Jnt*4ftyi^MNqm|DqC;m4XaWMl%YzW^ z`!**ax|F;lykO*j&q;im0*0gB0Ik41)0;~%0nfGVGSCLxH~hsc=1~EssNX(9xS#JN z1e{OcZ)Y`+3J8GHKv}1w1ax`=a)f(+MEN10eKqDH!V+ZdH(1s=G~@sP002ovPDHLk FV1oAAS=|5t literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/fortississimo.png b/gtk2_ardour/icons/fortississimo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c686554d5a1c991d18545f6e3c756b1e55c1105 GIT binary patch literal 963 zcmV;!13dhRP)sUYH3T!^4F#@MQhBDk<1-l4VFn~4=K^-@z5nx@CaoG~Y- zGxL2D9GJy<&i{SqIWzB^c}ECA+Y?K%8}H&*9L3Y<)bN*L58lIRe21M4`IqBa?89#< zzZkQ(;xIzRxvhfVfFm{hXa&Cs$7=YkZOz_^^GSjUY{ZT@p3L~WGyZLi;PEm(< z!oL|8JQl}qwK2O6XOd)xV`&WHVAN(3{zl9s{8zXHU3?a`zY_i?oXz+yhVXUNPWNke zu%Zn1#JNWk?LgGJ6_0gSoO?3SKInrVOSFCc8h*Hs-VH8_RixGic=Wb7QSigQ;- z{uoC3;IG9`iWhIB$>f!|1EbMQns6%W*GF~>gy7Ibszk7$rBi=-6b%9 zUn=fu)p^Sz_%8nFlfNPLwnW7dLb%ZBboOE^R$@3qrf~s3;AG^-6MlKdPsNK*M}9W) zTYB(QF=m0eUiJG?w&GK2J`j;VnDNJZ#aquH=xlOJl4a3dF$SrfmhCEwHV=4?EJ11DdCuYant*#M`15$KhTmSn&*GW3W=q2z#n0koERS*V&!HwD=l5bm13!Rm l@yYzG_z_*d7_%V+{{or3eh?%L^HBf*002ovPDHLkV1mc!)4Biv literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/halfnote.png b/gtk2_ardour/icons/halfnote.png new file mode 100644 index 0000000000000000000000000000000000000000..3996c49a43e6ff7920f6816cd3f9cad1ca3cf022 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^JV30@!3HF)(yEr+qAXP8FD1G)j8z}|`Mr}_Q z#}JF&w-@$%$rOql{doSw5)+BI&&!qx1*vrKD@-#plbWr&&3Edtl8fvf;!S7S zbaZMOba^BMFCOwc!N%NZV(jxu$J{2SAHA?jz2kAzC;5_Yo7cQ| zML!4zTl9aFF@GUaxy?8wFM0bfu`i3$w0R1`p4{|0YtdMmvmo}Qc!lI+>u*|*Ri=Gl z`@D6N{#Ca9Ks9TUx$m!fwcPZvsojIziRse?rlx9gzSN3KX5*dnNZ_~YJlX5-cd?2; W_nPQ7H9Qm;whW%GelF{r5}E*Ad8pO^ literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/mezzforte.png b/gtk2_ardour/icons/mezzforte.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4b7b0826e0ddb8eba64eb97730c0b694971439 GIT binary patch literal 802 zcmV+-1Ks?IP)tZZ~Pt zXcm^2{qN4~@6DUt2_ixxI}iuPfHd$Gcma%x2#p5m16~2*>HUoaISMSA&6B_m;Ixi! zHwq*H6b#5CAOggIDb@0g0BHd}I-lf(&Ydw@s+@x+Anr-Lz}2dN8FN79fpt394SWJ# z0twe}6L1%p(Xbu|_5)9W1vP2|&H?Y#@fBc|$Ik$8Ku?T?d8Tunx;6^LM9gUdb^<>f z!#PjZ=RMk>@zYwy&eyyCdI@#V4!l>GtByfhqg5O)0GIU*i`sPq699LB=`v8$+V2Cc zPDF_U5A=<@fF5HwsrfT%w2NwZ(`e&m^5wY$l3gah7}162RTwwM$5fjz+E&%<<+a!2 zc!MV@UT6!GB}}r_D&$PQ#5HooTzac(Hku9Qfo6~6ArH*9N-z=N29Obv??Djs0G)&_ zB_gvy5cDb(KuVvxDYdMK%(1lfI^#DY^2D?5Pm74iB{Lp0zi;Vtz-T_03_uVB?Z6rl z$*JDw>@`sd1i%e#F%M?Z&;yTED*!F3WhsSz2sjIT0}kta2TMQEo@Fmc1G3#1Ce4vA z7Poum9{FX~{vF_xC0CcOC(2Zu$69cSwU@}0$iSRWCW~3$<^VUs{ z)(k`)Z5MC^*oyqOip6ptVNFX@DyIE4`>PY?l;=Jv04G=EuTGdK;UDcu gy%LgWAzexT4VgPC+nR)m*Z=?k07*qoM6N<$f+b~F=l}o! literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/mezzoforte.png b/gtk2_ardour/icons/mezzoforte.png new file mode 100644 index 0000000000000000000000000000000000000000..bd4b7b0826e0ddb8eba64eb97730c0b694971439 GIT binary patch literal 802 zcmV+-1Ks?IP)tZZ~Pt zXcm^2{qN4~@6DUt2_ixxI}iuPfHd$Gcma%x2#p5m16~2*>HUoaISMSA&6B_m;Ixi! zHwq*H6b#5CAOggIDb@0g0BHd}I-lf(&Ydw@s+@x+Anr-Lz}2dN8FN79fpt394SWJ# z0twe}6L1%p(Xbu|_5)9W1vP2|&H?Y#@fBc|$Ik$8Ku?T?d8Tunx;6^LM9gUdb^<>f z!#PjZ=RMk>@zYwy&eyyCdI@#V4!l>GtByfhqg5O)0GIU*i`sPq699LB=`v8$+V2Cc zPDF_U5A=<@fF5HwsrfT%w2NwZ(`e&m^5wY$l3gah7}162RTwwM$5fjz+E&%<<+a!2 zc!MV@UT6!GB}}r_D&$PQ#5HooTzac(Hku9Qfo6~6ArH*9N-z=N29Obv??Djs0G)&_ zB_gvy5cDb(KuVvxDYdMK%(1lfI^#DY^2D?5Pm74iB{Lp0zi;Vtz-T_03_uVB?Z6rl z$*JDw>@`sd1i%e#F%M?Z&;yTED*!F3WhsSz2sjIT0}kta2TMQEo@Fmc1G3#1Ce4vA z7Poum9{FX~{vF_xC0CcOC(2Zu$69cSwU@}0$iSRWCW~3$<^VUs{ z)(k`)Z5MC^*oyqOip6ptVNFX@DyIE4`>PY?l;=Jv04G=EuTGdK;UDcu gy%LgWAzexT4VgPC+nR)m*Z=?k07*qoM6N<$f+b~F=l}o! literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/mezzopiano.png b/gtk2_ardour/icons/mezzopiano.png new file mode 100644 index 0000000000000000000000000000000000000000..ba3b39fa14f072f86beee019231d0b4e3d2bf5fb GIT binary patch literal 784 zcmV+r1MmEaP)M5T(6n(LqEBDy@jLATcOY!;{5=vKc~`{%9uZbaL{0T)oNw6ird_eNabh#BB>P@0z(=$6-H+kE<9r+q!_h`~7AIzL z=3HvOmz=l*m$3`)Ce0MC;%%(QZ^`y@-+3qg#yZ@U1g9~EClfmmeI^pymK=EvCozbR zn+3d-bYCa_1FVV0J0W;Jdm3zeVylB4N$l}pgE)hYv4BfSGl{|E$k}B3Q^f5umWAM8 z65JVVGTIj+j3dn?OG5W(XvVOi*^L*H6N?(#N%$NaL$J{FA^SF14pkqP2NOG!CiqgE z7u_N3HGg1!(rs!S**UM)I&Qi=hJpCs6W@oD{ge3KpV(n2rK||PvXnBWOZ~natF>NC z?e%7s!)@+=mAqU!%gUAq`!ul^aXY@pMA+M~Y)x~lY;50Qo+fU6SiT81Z2BJgDcFFS zsdos!hI6;f;(W!9;3GJe_+PlYz0jUbf~SK$kl2=Bi}|GG-PN$q%uJcY zs($_wot07+;R&3owSE|E6wkyC?5?$53O<45v90gdT2J)BE~RYHU1oiGN_Vy2hgIR! zXA!R7|5deb#EsS_EK}kwnX3-CiKUdXz|3sfU;8bequew{N+|_zVkqIk>mJ|+!!V4u z@PYrn+@N8>wQYdvzUvHk(F9%Aerr2jnt O0000H;`>`q%`hw*t?resC!W9f-G0p_t zGt9!cWU`81i^b=q*7l=K{R483x0|5S!_=Y z0}HS`U~QRZLNZk4KIFw4(&+mz8EZqL?OALKz6QQCapyB^6mv4&>Y%w1JFp1N>HSg& zo{`1v4BWjmWwC3ILjKD^cevgzm>5R*D)7A_(Yl1M3HZ58Gc&Y*5VPwU*c1{y$ha+m zYtOi@z#YxFi-BAJuXCU~1drknPAm6s74O2tq-j=e(_Hr&dvP_xhOzHYts2Fr<7X!5 z!4cfVN9BRii_Jl|ArwEA4Bt(rzu-C+|D~&8ATF>afp4zUwJVwCBN{WVQMQHuXW(|0 z=xx1!MbJzMQ`0^cT~QPoWLKq9S&3yC^xfF5!)VIBhsLNw`I9+^Tgv&3r`rXz#%WM4 z%1%74p&7z)H2tpw6+FdUd{(Z|VdbgVobGAL?R28*xKgRK;(jvxP1*htWyagdo2#7(4J753o4mtvG~7c!kTDn&_tE8hX)%kr7{mtN4J2*oMK)dX*(y!36}o zi1+ts$6NH{dBg+0q78HL1+VcfatCla{(c!_Yq}FndzB=;DF!UZ+C&iW3{x-&cN6?Z z)q&JH@noZPE=kc#kG!qpYtH$-A+Jx7(KQ>|v9G@9nNw^XClyL7b zA##WRQn8MV+uN*Hy`(o}2FDX_U&gJ7_z=bUoN|*RH%W1X)$j)x+O$`lq;F@zZxl!2 z@dW#ZEs179rU^KQ&5BK0!@6sWY1*qK;nr-DF6>skcb^pv1^Ew)zRCKfu z@4Li3^Iu*iNpDRi>Brcty|k07*qoM6N<$f?<$ZI{*Lx literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/piano.png b/gtk2_ardour/icons/piano.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb168ef3c60761003647d718a361ffd5ea1d80a GIT binary patch literal 481 zcmV<70UrK|P)q+e~j+5FEo-`@Pa9o*293#VE!Y-eC)u*?cZBgZBS9s?>iIHT;PV&d{vB(;uM?qalQa``las Xn3{;qnqMzT00000NkvXXu0mjfWo*mz literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/quarternote.png b/gtk2_ardour/icons/quarternote.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd23e37fa0d3fbeb07fe9857b86448530734691 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^JV30@!3HF)(yEr+qAXP8FD1G)j8!4coGoCJv zAr`%FuWZzFbrfiL_+Dqx1&=j%Z+Ua@cDgvNzWPDtp3@`NyoDb)q~7YTP}LBbRXoSZ z`$X2d9fCU}w;G=`nf!nLnUqPHUYc1-%l7mv4{)tNekh)`*?y1o1sM}PDDkhxuZJmQ4aieAdW)H)t!KKNW> znfd-aFfi{k&-`Y1=AHLdmZc`g#AG}^F&XbnOvX-=`i|yNVhXP|DQ*LIhr5F!&pyIQY`~`>rtyAZ zm){@m`4O4F19&gM7U1PjXtdtK7i{s>NwAxaR?jIE=g4h8shHYy7rq%_uQF zG`PmE;|ksxDqP2TJUUBxhBVdo|jP4GrG*1ipar z8ePEg1~)!h>tc~-v)%Jfi0Mc}6z(;w(@|~RP2;CF@V17vH*l^ETr{Y=hUp^D9=3rO z*0{ccPjH~fv%5`E7%R(Ci0LTyt9IOts$TwxYxNr2IvxYhG}=cYLIYO-0000=#POFk_2f&4?$E$J*WiQAA{7$P$P<-N`wpyE4)!rC}LJ*GYvjeh|sOm!|aSMS+=m^w z5np8Ae7x8z7^YQa@d;+&8FltcLrhD11>X{4id9)0t;*s_Jc1!S7h;;*3wSc#9Nx65 zEMCVloQ`!NrYTLqa06Z&9#mzq3Qr?oaW7z45Mo->27e0Ma7Tz~LKC^|Y#T2LF})IE z>dWKR>Z}Kv$Za*2pvt}nu`$FnrYeh_*oBErmg3r~EMCHO>iP@|ur^=sYYLv3v)hL` zcp>}m4>8Th4;X3!z6R?ccar6JRV{?)aSncIs)`3fOe@>q4`T~1#UlLNi`B;Bbbo2r;b-stbM#lez`} z=~z{xmfp4y(}WD)iv5_{E%*oq@IjmJCcGPBIzNxUYyxgoWwF01i`&%N{Wbe%VQcP# zgSe7Fjd}P^UF8;HcTeE92fTxs*rVQqJ>7!; zKGBUVw|Qz+blpr(`t4Db#Q|)^yb;T|&35DBUf#SD*~b_^qDztOFZ@0lOL3(J&9sw@ zC*h~j$anx>k2&6*5Yr^}iG3>0#OL@!XBp4J-2Z?>OaoYxfu|j3()TAL=J|)Jve?_- z=6n<%sSC^TpgPES2Ht|cR;wk%v_bB^r?9UIi`x)>TkE})d!Bj{QI;ji}$(# a4%{Pa0000y zVj3L;yaqc50mHo^rWXbQ!`2Yfju6w(Qo!^0@NeVCuqVWHM!)b`cok3Jw>tMAUh5Zz zwN+WXjBByK&OH!fdbnSBGQ@OIRTc+v4c@Ht+e1v3^aGFMoe)#3%3@xgihbx{SBPn( zEesQQJH#|vmBle^#tg2;)_!2}9KRJ}T3(gKFW87fxT_8PGxp$*dVCw+4KbZvmBkV4 z!bFH^qF4AlypLOPpw8V=2vP*YXt3#09;=C#tg8CO5}tb#78_@lm`d zuklr_rlKl~1M&v6t$z9q^49qizQOoN8@R?7Fco5&!Cu^ruj}i0KQC|b3#R3#{{&BC zHNNi~uJOaT2Xpe0TN=2=f54;i%Uio-aEl?z$;M> z0$znfgMe3K_LP2>2W-Z75^%}87Jgz^FJfRbOUCq zvX~pG6X(c>y)FN9-zg``)5ML~4MUx=xEvqj@&-44RF%c`4eAWz$%ZKTq|+#dvA$`s zzkW3u!Q%~U7dQ0A@LYqs4{&Ey7AJdwcgz1ePZ!7W9M)H5akME4L)~syh-nKRk=t<| kU&y=EbX68Jy?~4HCkWIE-sNN`Q~&?~07*qoM6N<$f`GV+i2wiq literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/wholenote.png b/gtk2_ardour/icons/wholenote.png new file mode 100644 index 0000000000000000000000000000000000000000..cca075df8dc88538d3e7350480f6a96832e84e72 GIT binary patch literal 414 zcmV;P0b%}$P)@GV__7Ass=brO;k9&inD71*; zWCTAkiUIVZ3)js$!>>F`?^~ImD2i5a3E$9-1FYgN=COsLW+ulDHu5aJ4f^`yaU3Gj zW+Bhg-O|gx;0U#n=L&Nn)+*swoRrS;Ed7JJ_k6;7h{;Oh1&{t`uY)!n;$KCq;cEx; zF`g<~5aM?S^Lz*GqsBzaoQ7D@7*Uz;QJj3PXj7F7?ywx@S$c(8Y-<0#2m4W+EJkti zA&Qd;{Al)`u#K5KOD}`UA`NRS(ijGt^^6DH-~#nLOV3-GH=(jikBt?QZU6uP07*qo IM6N<$f*`Z05&!@I literal 0 HcmV?d00001 diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index b1d886a04b..89a138f8b9 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -81,6 +81,7 @@ #include "rgb_macros.h" #include "selection.h" #include "simplerect.h" +#include "step_entry.h" #include "utils.h" #include "ardour/midi_track.h" @@ -918,6 +919,11 @@ MidiTimeAxisView::start_step_editing () } midi_track()->set_step_editing (true); + + StepEntry* se = new StepEntry (*this); + + se->set_position (WIN_POS_MOUSE); + se->present (); } void @@ -951,39 +957,46 @@ MidiTimeAxisView::check_step_edit () incoming.read_contents (size, buf); if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) { - - if (step_edit_region == 0) { - - step_edit_region = add_region (step_edit_insert_position); - RegionView* rv = view()->find_view (step_edit_region); - step_edit_region_view = dynamic_cast(rv); - } - - if (step_edit_region && step_edit_region_view) { - - if (step_edit_beat_pos < 0.0) { - framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position(); - if (frames_from_start < 0) { - continue; - } - step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start); - } - - bool success; - Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position); - - if (!success) { - continue; - } - - step_edit_region_view->step_add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats); - step_edit_beat_pos += beats; - } + step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0); } - } } +int +MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration) +{ + if (step_edit_region == 0) { + + step_edit_region = add_region (step_edit_insert_position); + RegionView* rv = view()->find_view (step_edit_region); + step_edit_region_view = dynamic_cast(rv); + } + + if (step_edit_region && step_edit_region_view) { + if (step_edit_beat_pos < 0.0) { + framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position(); + if (frames_from_start < 0) { + return 1; + } + step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start); + } + + if (beat_duration == 0.0) { + bool success; + beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position); + + if (!success) { + return -1; + } + } + + step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration); + step_edit_beat_pos += beat_duration; + } + + return 0; +} + void MidiTimeAxisView::step_edit_rest () { diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 0ed44b96c0..2c0c8b2d44 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -91,6 +91,8 @@ class MidiTimeAxisView : public RouteTimeAxisView void stop_step_editing (); void check_step_edit (); void step_edit_rest (); + int step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, + Evoral::MusicalTime beat_duration); const MidiMultipleChannelSelector& channel_selector() { return _channel_selector; } diff --git a/gtk2_ardour/step_entry.cc b/gtk2_ardour/step_entry.cc new file mode 100644 index 0000000000..aa81fead3d --- /dev/null +++ b/gtk2_ardour/step_entry.cc @@ -0,0 +1,243 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "midi_time_axis.h" +#include "step_entry.h" +#include "utils.h" + +#include "i18n.h" + +using namespace Gtk; + +static void +_note_off_event_handler (GtkWidget* widget, int note, gpointer arg) +{ + ((StepEntry*)arg)->note_off_event_handler (note); +} + + +StepEntry::StepEntry (MidiTimeAxisView& mtv) + : ArdourDialog (_("Step Entry Editor")) + , triplet_button ("3") + , sustain_button ("sustain") + , rest_button ("rest") + , channel_adjustment (0, 15, 0, 1, 4) + , channel_spinner (channel_adjustment) + , _piano (0) + , piano (0) + , _mtv (&mtv) +{ + RadioButtonGroup length_group = length_1_button.get_group(); + length_2_button.set_group (length_group); + length_4_button.set_group (length_group); + length_8_button.set_group (length_group); + length_12_button.set_group (length_group); + length_16_button.set_group (length_group); + length_32_button.set_group (length_group); + length_64_button.set_group (length_group); + + Widget* w; + + w = manage (new Image (::get_icon (X_("wholenote")))); + w->show(); + length_1_button.add (*w); + w = manage (new Image (::get_icon (X_("halfnote")))); + w->show(); + length_2_button.add (*w); + w = manage (new Image (::get_icon (X_("quarternote")))); + w->show(); + length_4_button.add (*w); + w = manage (new Image (::get_icon (X_("eighthnote")))); + w->show(); + length_8_button.add (*w); + w = manage (new Image (::get_icon (X_("sixteenthnote")))); + w->show(); + length_16_button.add (*w); + w = manage (new Image (::get_icon (X_("thirtysecondnote")))); + w->show(); + length_32_button.add (*w); + w = manage (new Image (::get_icon (X_("sixtyfourthnote")))); + w->show(); + length_64_button.add (*w); + + length_1_button.property_draw_indicator() = false; + length_2_button.property_draw_indicator() = false; + length_4_button.property_draw_indicator() = false; + length_8_button.property_draw_indicator() = false; + length_16_button.property_draw_indicator() = false; + length_32_button.property_draw_indicator() = false; + length_64_button.property_draw_indicator() = false; + + note_length_box.pack_start (length_1_button, false, false); + note_length_box.pack_start (length_2_button, false, false); + note_length_box.pack_start (length_4_button, false, false); + note_length_box.pack_start (length_8_button, false, false); + note_length_box.pack_start (length_16_button, false, false); + note_length_box.pack_start (length_32_button, false, false); + note_length_box.pack_start (length_64_button, false, false); + + RadioButtonGroup velocity_group = velocity_ppp_button.get_group(); + velocity_pp_button.set_group (velocity_group); + velocity_p_button.set_group (velocity_group); + velocity_mp_button.set_group (velocity_group); + velocity_mf_button.set_group (velocity_group); + velocity_f_button.set_group (velocity_group); + velocity_ff_button.set_group (velocity_group); + velocity_fff_button.set_group (velocity_group); + + w = manage (new Image (::get_icon (X_("pianississimo")))); + w->show(); + velocity_ppp_button.add (*w); + w = manage (new Image (::get_icon (X_("pianissimo")))); + w->show(); + velocity_pp_button.add (*w); + w = manage (new Image (::get_icon (X_("piano")))); + w->show(); + velocity_p_button.add (*w); + w = manage (new Image (::get_icon (X_("mezzopiano")))); + w->show(); + velocity_mp_button.add (*w); + w = manage (new Image (::get_icon (X_("mezzoforte")))); + w->show(); + velocity_mf_button.add (*w); + w = manage (new Image (::get_icon (X_("forte")))); + w->show(); + velocity_f_button.add (*w); + w = manage (new Image (::get_icon (X_("fortissimo")))); + w->show(); + velocity_ff_button.add (*w); + w = manage (new Image (::get_icon (X_("fortississimo")))); + w->show(); + velocity_fff_button.add (*w); + + velocity_ppp_button.property_draw_indicator() = false; + velocity_pp_button.property_draw_indicator() = false; + velocity_p_button.property_draw_indicator() = false; + velocity_mp_button.property_draw_indicator() = false; + velocity_mf_button.property_draw_indicator() = false; + velocity_f_button.property_draw_indicator() = false; + velocity_ff_button.property_draw_indicator() = false; + velocity_fff_button.property_draw_indicator() = false; + + note_velocity_box.pack_start (velocity_ppp_button, false, false); + note_velocity_box.pack_start (velocity_pp_button, false, false); + note_velocity_box.pack_start (velocity_p_button, false, false); + note_velocity_box.pack_start (velocity_mp_button, false, false); + note_velocity_box.pack_start (velocity_mf_button, false, false); + note_velocity_box.pack_start (velocity_f_button, false, false); + note_velocity_box.pack_start (velocity_ff_button, false, false); + note_velocity_box.pack_start (velocity_fff_button, false, false); + + Label* l = manage (new Label); + l->set_markup ("."); + l->show (); + dot_button.add (*l); + + w = manage (new Image (::get_icon (X_("chord")))); + w->show(); + chord_button.add (*w); + + upper_box.set_spacing (6); + upper_box.pack_start (chord_button, false, false); + upper_box.pack_start (note_length_box, false, false, 12); + upper_box.pack_start (triplet_button, false, false); + upper_box.pack_start (dot_button, false, false); + upper_box.pack_start (sustain_button, false, false); + upper_box.pack_start (rest_button, false, false); + upper_box.pack_start (note_velocity_box, false, false, 12); + upper_box.pack_start (channel_spinner, false, false); + + _piano = (PianoKeyboard*) piano_keyboard_new (); + piano = Glib::wrap ((GtkWidget*) _piano); + + g_signal_connect(G_OBJECT(_piano), "note-off", G_CALLBACK(_note_off_event_handler), this); + + rest_button.signal_clicked().connect (sigc::mem_fun (*this, &StepEntry::rest_click)); + + packer.set_spacing (6); + packer.pack_start (upper_box, false, false); + packer.pack_start (*piano, false, false); + packer.show_all (); + + get_vbox()->add (packer); +} + +StepEntry::~StepEntry() +{ +} + +void +StepEntry::note_off_event_handler (int note) +{ + Evoral::MusicalTime length = 1.0; + uint8_t velocity = 64; + + if (length_64_button.get_active()) { + length = 1.0/64.0; + } else if (length_32_button.get_active()) { + length = 1.0/32.0; + } else if (length_16_button.get_active()) { + length = 1.0/16.0; + } else if (length_8_button.get_active()) { + length = 1.0/8.0; + } else if (length_4_button.get_active()) { + length = 1.0/4.0; + } else if (length_2_button.get_active()) { + length = 1.0/2.0; + } else if (length_1_button.get_active()) { + length = 1.0/1.0; + } + + if (dot_button.get_active()) { + length *= 0.5; + } + + if (velocity_ppp_button.get_active()) { + velocity = 16; + } else if (velocity_pp_button.get_active()) { + velocity = 32; + } else if (velocity_p_button.get_active()) { + velocity = 48; + } else if (velocity_mp_button.get_active()) { + velocity = 64; + } else if (velocity_mf_button.get_active()) { + velocity = 80; + } else if (velocity_f_button.get_active()) { + velocity = 96; + } else if (velocity_ff_button.get_active()) { + velocity = 112; + } else if (velocity_fff_button.get_active()) { + velocity = 127; + } + + if (!triplet_button.get_active()) { + _mtv->step_add_note (channel_adjustment.get_value(), note, velocity, length); + } else { + length *= 2.0/3.0; + _mtv->step_add_note (channel_adjustment.get_value(), note, velocity, length); + _mtv->step_add_note (channel_adjustment.get_value(), note, velocity, length); + _mtv->step_add_note (channel_adjustment.get_value(), note, velocity, length); + } +} + +void +StepEntry::rest_click () +{ + _mtv->step_edit_rest (); +} diff --git a/gtk2_ardour/step_entry.h b/gtk2_ardour/step_entry.h new file mode 100644 index 0000000000..cabd9eba47 --- /dev/null +++ b/gtk2_ardour/step_entry.h @@ -0,0 +1,84 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __gtk2_ardour_step_entry_h__ +#define __gtk2_ardour_step_entry_h__ + +#include +#include +#include +#include +#include + +#include "ardour_dialog.h" +#include "gtk_pianokeyboard.h" + +class MidiTimeAxisView; + +class StepEntry : public ArdourDialog +{ + public: + StepEntry (MidiTimeAxisView&); + ~StepEntry (); + + void note_off_event_handler (int note); + + private: + Gtk::VBox packer; + Gtk::HBox upper_box; + Gtk::HBox note_length_box; + Gtk::HBox note_velocity_box; + + Gtk::ToggleButton chord_button; + Gtk::ToggleButton triplet_button; + Gtk::ToggleButton dot_button; + + Gtk::Button sustain_button; + Gtk::Button rest_button; + + Gtk::RadioButton length_1_button; + Gtk::RadioButton length_2_button; + Gtk::RadioButton length_4_button; + Gtk::RadioButton length_8_button; + Gtk::RadioButton length_12_button; + Gtk::RadioButton length_16_button; + Gtk::RadioButton length_32_button; + Gtk::RadioButton length_64_button; + + Gtk::RadioButton velocity_ppp_button; + Gtk::RadioButton velocity_pp_button; + Gtk::RadioButton velocity_p_button; + Gtk::RadioButton velocity_mp_button; + Gtk::RadioButton velocity_mf_button; + Gtk::RadioButton velocity_f_button; + Gtk::RadioButton velocity_ff_button; + Gtk::RadioButton velocity_fff_button; + + Gtk::Adjustment channel_adjustment; + Gtk::SpinButton channel_spinner; + + PianoKeyboard* _piano; + Gtk::Widget* piano; + MidiTimeAxisView* _mtv; + + void rest_click (); + void sustain_click (); +}; + +#endif /* __gtk2_ardour_step_entry_h__ */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 04a121d18d..e47ba21682 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -116,6 +116,7 @@ gtk2_ardour_sources = [ 'group_tabs.cc', 'gtk-custom-hruler.c', 'gtk-custom-ruler.c', + 'gtk_pianokeyboard.c', 'interthread_progress_window.cc', 'io_selector.cc', 'keyboard.cc', @@ -187,6 +188,7 @@ gtk2_ardour_sources = [ 'simplerect.cc', 'splash.cc', 'startup.cc', + 'step_entry.cc', 'streamview.cc', 'strip_silence_dialog.cc', 'tape_region_view.cc', diff --git a/icons/Music_dynamic_forte.svg b/icons/Music_dynamic_forte.svg new file mode 100644 index 0000000000..31bb3aa8d3 --- /dev/null +++ b/icons/Music_dynamic_forte.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_fortissimo.svg b/icons/Music_dynamic_fortissimo.svg new file mode 100644 index 0000000000..49e93f0599 --- /dev/null +++ b/icons/Music_dynamic_fortissimo.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_fortississimo.svg b/icons/Music_dynamic_fortississimo.svg new file mode 100644 index 0000000000..adbf682f6d --- /dev/null +++ b/icons/Music_dynamic_fortississimo.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_mezzo_forte.svg b/icons/Music_dynamic_mezzo_forte.svg new file mode 100644 index 0000000000..7633a497ae --- /dev/null +++ b/icons/Music_dynamic_mezzo_forte.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_pianissimo.svg b/icons/Music_dynamic_pianissimo.svg new file mode 100644 index 0000000000..36a261d7ef --- /dev/null +++ b/icons/Music_dynamic_pianissimo.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_pianississimo.svg b/icons/Music_dynamic_pianississimo.svg new file mode 100644 index 0000000000..aa0e7b5072 --- /dev/null +++ b/icons/Music_dynamic_pianississimo.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/Music_dynamic_piano.svg b/icons/Music_dynamic_piano.svg new file mode 100644 index 0000000000..e689d39596 --- /dev/null +++ b/icons/Music_dynamic_piano.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/icons/chord.svg b/icons/chord.svg new file mode 100644 index 0000000000..d96749bee3 --- /dev/null +++ b/icons/chord.svg @@ -0,0 +1,66 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/eighthnote.svg b/icons/eighthnote.svg new file mode 100644 index 0000000000..8f838513fc --- /dev/null +++ b/icons/eighthnote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/halfnote.svg b/icons/halfnote.svg new file mode 100644 index 0000000000..73cb4790f9 --- /dev/null +++ b/icons/halfnote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/mezzopiano.svg b/icons/mezzopiano.svg new file mode 100644 index 0000000000..1eac80b766 --- /dev/null +++ b/icons/mezzopiano.svg @@ -0,0 +1,75 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/quarternote.svg b/icons/quarternote.svg new file mode 100644 index 0000000000..2140b5a871 --- /dev/null +++ b/icons/quarternote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/sixteenthnote.svg b/icons/sixteenthnote.svg new file mode 100644 index 0000000000..cb5a2c4468 --- /dev/null +++ b/icons/sixteenthnote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/sixyfourthnote.svg b/icons/sixyfourthnote.svg new file mode 100644 index 0000000000..02f099608c --- /dev/null +++ b/icons/sixyfourthnote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/thirtysecondnote.svg b/icons/thirtysecondnote.svg new file mode 100644 index 0000000000..5e788e0d73 --- /dev/null +++ b/icons/thirtysecondnote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/wholenote.svg b/icons/wholenote.svg new file mode 100644 index 0000000000..4bc9e8b2ac --- /dev/null +++ b/icons/wholenote.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file