1 /*
2 * Copyright (C) 2020 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <math.h>
21
22 #include "audio/chord_descriptor.h"
23 #include "gui/backend/piano_roll.h"
24 #include "gui/widgets/piano_keyboard.h"
25 #include "utils/ui.h"
26 #include "zrythm.h"
27 #include "zrythm_app.h"
28
G_DEFINE_TYPE(PianoKeyboardWidget,piano_keyboard_widget,GTK_TYPE_DRAWING_AREA)29 G_DEFINE_TYPE (
30 PianoKeyboardWidget, piano_keyboard_widget,
31 GTK_TYPE_DRAWING_AREA)
32
33 /**
34 * Draws an orange circle if the note is enabled.
35 */
36 static void
37 draw_orange_circle (
38 PianoKeyboardWidget * self,
39 cairo_t * cr,
40 double key_width,
41 double cur_offset,
42 int i)
43 {
44 double height =
45 (double)
46 gtk_widget_get_allocated_height (
47 GTK_WIDGET (self));
48 if (self->chord_descr)
49 {
50 if (self->chord_descr->notes[
51 self->start_key + i])
52 {
53 double circle_radius = key_width / 3.0;
54 bool is_black =
55 piano_roll_is_key_black (
56 self->start_key + i);
57 gdk_cairo_set_source_rgba (
58 cr, &UI_COLORS->dark_orange);
59 cairo_set_source_rgba (cr, 1, 0, 0, 1);
60 cairo_arc (
61 cr,
62 cur_offset + key_width / 2.0,
63 is_black ?
64 height / 3.0 :
65 height / 1.2,
66 circle_radius, 0, 2 * M_PI);
67 cairo_fill (cr);
68 }
69 }
70 }
71
72 static gboolean
piano_keyboard_draw_cb(GtkWidget * widget,cairo_t * cr,PianoKeyboardWidget * self)73 piano_keyboard_draw_cb (
74 GtkWidget * widget,
75 cairo_t * cr,
76 PianoKeyboardWidget * self)
77 {
78 GtkStyleContext *context =
79 gtk_widget_get_style_context (widget);
80
81 int width =
82 gtk_widget_get_allocated_width (widget);
83 int height =
84 gtk_widget_get_allocated_height (widget);
85
86 gtk_render_background (
87 context, cr, 0, 0, width, height);
88
89 int num_white_keys = 0;
90 for (int i = 0; i < self->num_keys; i++)
91 {
92 if (!piano_roll_is_key_black (
93 self->start_key + i))
94 num_white_keys++;
95 }
96
97 /* draw all white keys */
98 double key_width =
99 (double) width / (double) num_white_keys;
100 double cur_offset = 0.0;
101 for (int i = 0; i < self->num_keys; i++)
102 {
103 bool is_black =
104 piano_roll_is_key_black (
105 self->start_key + i);
106 if (is_black)
107 continue;
108
109 cairo_set_source_rgba (cr, 0, 0, 0, 1);
110 cairo_rectangle (
111 cr, cur_offset, 0, key_width, height);
112 cairo_stroke_preserve (cr);
113 cairo_set_source_rgba (cr, 1, 1, 1, 1);
114 cairo_fill (cr);
115
116 /* draw orange circle if part of chord */
117 draw_orange_circle (
118 self, cr, key_width, cur_offset, i);
119
120 cur_offset += key_width;
121 }
122
123 /* draw all black keys */
124 /*int num_black_keys = self->num_keys - num_white_keys;*/
125 cur_offset = 0.0;
126 for (int i = 0; i < self->num_keys; i++)
127 {
128 bool is_black =
129 piano_roll_is_key_black (
130 self->start_key + i);
131 if (!is_black)
132 {
133 bool is_next_black =
134 piano_roll_is_next_key_black (
135 self->start_key + i);
136
137 if (is_next_black)
138 cur_offset += key_width / 2.0;
139 else
140 cur_offset += key_width;
141
142 continue;
143 }
144
145 cairo_set_source_rgba (cr, 0, 0, 0, 1);
146 cairo_rectangle (
147 cr, cur_offset, 0, key_width, height / 1.4);
148 cairo_fill (cr);
149
150 /* draw orange circle if part of chord */
151 draw_orange_circle (
152 self, cr, key_width, cur_offset, i);
153
154 cur_offset += key_width / 2.0;
155 }
156
157 return FALSE;
158 }
159
160 void
piano_keyboard_widget_refresh(PianoKeyboardWidget * self)161 piano_keyboard_widget_refresh (
162 PianoKeyboardWidget * self)
163 {
164 gtk_widget_queue_draw (GTK_WIDGET (self));
165 }
166
167 /**
168 * Creates a piano keyboard widget.
169 */
170 PianoKeyboardWidget *
piano_keyboard_widget_new_for_chord_key(ChordDescriptor * descr)171 piano_keyboard_widget_new_for_chord_key (
172 ChordDescriptor * descr)
173 {
174 PianoKeyboardWidget * self =
175 piano_keyboard_widget_new (
176 GTK_ORIENTATION_HORIZONTAL);
177
178 self->chord_descr = descr;
179 self->editable = true;
180 self->playable = false;
181 self->scrollable = false;
182 self->start_key = 0;
183 self->num_keys = 48;
184
185 return self;
186 }
187
188 /**
189 * Creates a piano keyboard widget.
190 */
191 PianoKeyboardWidget *
piano_keyboard_widget_new(GtkOrientation orientation)192 piano_keyboard_widget_new (
193 GtkOrientation orientation)
194 {
195 PianoKeyboardWidget * self =
196 g_object_new (PIANO_KEYBOARD_WIDGET_TYPE, NULL);
197
198 g_signal_connect (
199 G_OBJECT(self), "draw",
200 G_CALLBACK (piano_keyboard_draw_cb), self);
201
202 return self;
203 }
204
205 static void
piano_keyboard_widget_class_init(PianoKeyboardWidgetClass * _klass)206 piano_keyboard_widget_class_init (
207 PianoKeyboardWidgetClass * _klass)
208 {
209 }
210
211 static void
piano_keyboard_widget_init(PianoKeyboardWidget * self)212 piano_keyboard_widget_init (
213 PianoKeyboardWidget * self)
214 {
215 gtk_widget_set_visible (GTK_WIDGET (self), true);
216
217 self->editable = true;
218 self->playable = false;
219 self->scrollable = false;
220 self->start_key = 0;
221 self->num_keys = 36;
222 }
223