From: Paul Davis Date: Mon, 2 Aug 2010 21:52:21 +0000 (+0000) Subject: first, incomplete pass at step entry dialog, along with various SVG and PNG files... X-Git-Tag: 3.0-alpha5~1730 X-Git-Url: https://main.carlh.net/gitweb/?a=commitdiff_plain;h=e0edca5a2abd65d869348e4bddb9d07ecc156450;p=ardour.git 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 --- 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 0000000000..0b7053dc97 Binary files /dev/null and b/gtk2_ardour/icons/chord.png differ diff --git a/gtk2_ardour/icons/eighthnote.png b/gtk2_ardour/icons/eighthnote.png new file mode 100644 index 0000000000..b5c0675048 Binary files /dev/null and b/gtk2_ardour/icons/eighthnote.png differ diff --git a/gtk2_ardour/icons/forte.png b/gtk2_ardour/icons/forte.png new file mode 100644 index 0000000000..84ccc839df Binary files /dev/null and b/gtk2_ardour/icons/forte.png differ diff --git a/gtk2_ardour/icons/fortissimo.png b/gtk2_ardour/icons/fortissimo.png new file mode 100644 index 0000000000..9ba6da1990 Binary files /dev/null and b/gtk2_ardour/icons/fortissimo.png differ diff --git a/gtk2_ardour/icons/fortississimo.png b/gtk2_ardour/icons/fortississimo.png new file mode 100644 index 0000000000..8c686554d5 Binary files /dev/null and b/gtk2_ardour/icons/fortississimo.png differ diff --git a/gtk2_ardour/icons/halfnote.png b/gtk2_ardour/icons/halfnote.png new file mode 100644 index 0000000000..3996c49a43 Binary files /dev/null and b/gtk2_ardour/icons/halfnote.png differ diff --git a/gtk2_ardour/icons/mezzforte.png b/gtk2_ardour/icons/mezzforte.png new file mode 100644 index 0000000000..bd4b7b0826 Binary files /dev/null and b/gtk2_ardour/icons/mezzforte.png differ diff --git a/gtk2_ardour/icons/mezzoforte.png b/gtk2_ardour/icons/mezzoforte.png new file mode 100644 index 0000000000..bd4b7b0826 Binary files /dev/null and b/gtk2_ardour/icons/mezzoforte.png differ diff --git a/gtk2_ardour/icons/mezzopiano.png b/gtk2_ardour/icons/mezzopiano.png new file mode 100644 index 0000000000..ba3b39fa14 Binary files /dev/null and b/gtk2_ardour/icons/mezzopiano.png differ diff --git a/gtk2_ardour/icons/pianissimo.png b/gtk2_ardour/icons/pianissimo.png new file mode 100644 index 0000000000..615803173e Binary files /dev/null and b/gtk2_ardour/icons/pianissimo.png differ diff --git a/gtk2_ardour/icons/pianississimo.png b/gtk2_ardour/icons/pianississimo.png new file mode 100644 index 0000000000..926c3c4c75 Binary files /dev/null and b/gtk2_ardour/icons/pianississimo.png differ diff --git a/gtk2_ardour/icons/piano.png b/gtk2_ardour/icons/piano.png new file mode 100644 index 0000000000..fbb168ef3c Binary files /dev/null and b/gtk2_ardour/icons/piano.png differ diff --git a/gtk2_ardour/icons/quarternote.png b/gtk2_ardour/icons/quarternote.png new file mode 100644 index 0000000000..bdd23e37fa Binary files /dev/null and b/gtk2_ardour/icons/quarternote.png differ diff --git a/gtk2_ardour/icons/sixteenthnote.png b/gtk2_ardour/icons/sixteenthnote.png new file mode 100644 index 0000000000..af92d17e1c Binary files /dev/null and b/gtk2_ardour/icons/sixteenthnote.png differ diff --git a/gtk2_ardour/icons/sixtyfourthnote.png b/gtk2_ardour/icons/sixtyfourthnote.png new file mode 100644 index 0000000000..1066b75f2e Binary files /dev/null and b/gtk2_ardour/icons/sixtyfourthnote.png differ diff --git a/gtk2_ardour/icons/thirtysecondnote.png b/gtk2_ardour/icons/thirtysecondnote.png new file mode 100644 index 0000000000..21027d6d42 Binary files /dev/null and b/gtk2_ardour/icons/thirtysecondnote.png differ diff --git a/gtk2_ardour/icons/wholenote.png b/gtk2_ardour/icons/wholenote.png new file mode 100644 index 0000000000..cca075df8d Binary files /dev/null and b/gtk2_ardour/icons/wholenote.png differ 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,37 +957,44 @@ MidiTimeAxisView::check_step_edit () incoming.read_contents (size, buf); if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) { + step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0); + } + } +} - 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) { +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 (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; - } + if (!success) { + return -1; + } + } - 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_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration); + step_edit_beat_pos += beat_duration; + } - } + return 0; } void 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