1 /*
2 * Copyright (C) 2018-2021 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 /**
21 * \file
22 *
23 * Piano roll backend.
24 *
25 * This is meant to be serialized along with each project.
26 */
27
28 #include <stdlib.h>
29
30 #include "audio/channel.h"
31 #include "gui/backend/event.h"
32 #include "gui/backend/event_manager.h"
33 #include "gui/backend/piano_roll.h"
34 #include "audio/track.h"
35 #include "project.h"
36 #include "settings/settings.h"
37 #include "utils/arrays.h"
38 #include "utils/objects.h"
39 #include "zrythm_app.h"
40
41 DRUM_LABELS;
42
43 MidiNoteDescriptor *
midi_note_descriptor_new(void)44 midi_note_descriptor_new (void)
45 {
46 MidiNoteDescriptor * self =
47 object_new (MidiNoteDescriptor);
48
49 return self;
50 }
51
52 void
midi_note_descriptor_free(MidiNoteDescriptor * self)53 midi_note_descriptor_free (
54 MidiNoteDescriptor * self)
55 {
56 g_free_and_null (self->custom_name);
57 g_free_and_null (self->note_name);
58 g_free_and_null (self->note_name_pango);
59
60 object_zero_and_free (self);
61 }
62
63 /**
64 * Inits the descriptors to the default values.
65 *
66 * FIXME move them to tracks since each track
67 * might have different arrangement of drums.
68 */
69 static void
init_descriptors(PianoRoll * self)70 init_descriptors (
71 PianoRoll * self)
72 {
73 int idx = 0;
74 for (int i = 127; i >= 0; i--)
75 {
76 /* do piano */
77 MidiNoteDescriptor * descr =
78 midi_note_descriptor_new ();
79 self->piano_descriptors[idx] = descr;
80
81 descr->index = idx;
82 descr->value = i;
83 descr->marked = 0;
84 descr->visible = 1;
85 descr->custom_name = g_strdup ("");
86
87 descr->note_name =
88 g_strdup_printf (
89 "%s%d",
90 chord_descriptor_note_to_string (i % 12),
91 i / 12 - 1);
92 descr->note_name_pango =
93 g_strdup_printf (
94 "%s<sup>%d</sup>",
95 chord_descriptor_note_to_string (i % 12),
96 i / 12 - 1);
97 idx++;
98 }
99
100 /* do drum - put 35 to 81 first */
101 idx = 0;
102 for (int i = 35; i <= 81; i++)
103 {
104 MidiNoteDescriptor * descr =
105 midi_note_descriptor_new ();
106 self->drum_descriptors[idx] = descr;
107
108 descr->index = idx;
109 descr->value = i;
110 descr->marked = 0;
111 descr->visible = 1;
112 descr->custom_name =
113 g_strdup (drum_labels[idx]);
114
115 descr->note_name =
116 g_strdup_printf (
117 "%s%d",
118 chord_descriptor_note_to_string (i % 12),
119 i / 12 - 1);
120 descr->note_name_pango =
121 g_strdup_printf (
122 "%s<sup>%d</sup>",
123 chord_descriptor_note_to_string (i % 12),
124 i / 12 - 1);
125 idx++;
126 }
127 for (int i = 0; i < 128; i++)
128 {
129 if (i >= 35 && i <= 81)
130 continue;
131
132 MidiNoteDescriptor * descr =
133 midi_note_descriptor_new ();
134 self->drum_descriptors[idx] = descr;
135
136 descr->index = idx;
137 descr->value = i;
138 descr->marked = 0;
139 descr->visible = 1;
140 descr->custom_name =
141 g_strdup_printf (
142 "#%d: %s%d",
143 i,
144 chord_descriptor_note_to_string (i % 12),
145 i / 12 - 1);
146
147 descr->note_name =
148 g_strdup_printf (
149 "%s%d",
150 chord_descriptor_note_to_string (i % 12),
151 i / 12 - 1);
152 descr->note_name_pango =
153 g_strdup_printf (
154 "%s<sup>%d</sup>",
155 chord_descriptor_note_to_string (i % 12),
156 i / 12 - 1);
157 idx++;
158 }
159
160 g_warn_if_fail (idx == 128);
161 }
162
163 /* 1 = black */
164 static const int notes[12] = {
165 0,
166 1,
167 0,
168 1,
169 0,
170 0,
171 1,
172 0,
173 1,
174 0,
175 1,
176 0 };
177
178 /**
179 * Returns if the key is black.
180 */
181 int
piano_roll_is_key_black(int note)182 piano_roll_is_key_black (
183 int note)
184 {
185 return notes[note % 12] == 1;
186 }
187
188 /**
189 * Adds the note if it doesn't exist in the array.
190 */
191 void
piano_roll_add_current_note(PianoRoll * self,int note)192 piano_roll_add_current_note (
193 PianoRoll * self,
194 int note)
195 {
196 if (!array_contains_int (
197 self->current_notes,
198 self->num_current_notes,
199 note))
200 {
201 array_append (
202 self->current_notes,
203 self->num_current_notes,
204 note);
205 }
206 }
207
208 /**
209 * Removes the note if it exists in the array.
210 */
211 void
piano_roll_remove_current_note(PianoRoll * self,int note)212 piano_roll_remove_current_note (
213 PianoRoll * self,
214 int note)
215 {
216 if (array_contains_int (
217 self->current_notes,
218 self->num_current_notes,
219 note))
220 {
221 array_delete_primitive (
222 self->current_notes,
223 self->num_current_notes,
224 note);
225 }
226 }
227
228 /**
229 * Returns 1 if it contains the given note, 0
230 * otherwise.
231 */
232 int
piano_roll_contains_current_note(PianoRoll * self,int note)233 piano_roll_contains_current_note (
234 PianoRoll * self,
235 int note)
236 {
237 return
238 array_contains_int (
239 self->current_notes,
240 self->num_current_notes, note);
241 }
242
243 /**
244 * Inits the PianoRoll after a Project has been
245 * loaded.
246 */
247 void
piano_roll_init_loaded(PianoRoll * self)248 piano_roll_init_loaded (
249 PianoRoll * self)
250 {
251 if (!ZRYTHM_TESTING)
252 {
253 self->highlighting =
254 g_settings_get_enum (
255 S_UI, "piano-roll-highlight");
256 }
257
258 init_descriptors (self);
259 }
260
261
262 /**
263 * Returns the MidiNoteDescriptor matching the value
264 * (0-127).
265 */
266 const MidiNoteDescriptor *
piano_roll_find_midi_note_descriptor_by_val(PianoRoll * self,bool drum_mode,const uint8_t val)267 piano_roll_find_midi_note_descriptor_by_val (
268 PianoRoll * self,
269 bool drum_mode,
270 const uint8_t val)
271 {
272 g_return_val_if_fail (val < 128, NULL);
273
274 MidiNoteDescriptor * descr;
275 for (int i = 0; i < 128; i++)
276 {
277 if (drum_mode)
278 descr = self->drum_descriptors[i];
279 else
280 descr = self->piano_descriptors[i];
281
282 if (descr->value == (int) val)
283 return descr;
284 }
285 g_return_val_if_reached (NULL);
286 }
287
288 void
midi_note_descriptor_set_custom_name(MidiNoteDescriptor * descr,char * str)289 midi_note_descriptor_set_custom_name (
290 MidiNoteDescriptor * descr,
291 char * str)
292 {
293 descr->custom_name = g_strdup (str);
294 }
295
296 void
piano_roll_set_highlighting(PianoRoll * self,PianoRollHighlighting highlighting)297 piano_roll_set_highlighting (
298 PianoRoll * self,
299 PianoRollHighlighting highlighting)
300 {
301 self->highlighting = highlighting;
302
303 g_settings_set_enum (
304 S_UI, "piano-roll-highlight",
305 highlighting);
306
307 EVENTS_PUSH (
308 ET_PIANO_ROLL_HIGHLIGHTING_CHANGED, NULL);
309 }
310
311 /**
312 * Returns the current track whose regions are
313 * being shown in the piano roll.
314 */
315 Track *
piano_roll_get_current_track(const PianoRoll * self)316 piano_roll_get_current_track (
317 const PianoRoll * self)
318 {
319 /* TODO */
320 return NULL;
321 }
322
323 void
piano_roll_set_notes_zoom(PianoRoll * self,float notes_zoom,int fire_events)324 piano_roll_set_notes_zoom (
325 PianoRoll * self,
326 float notes_zoom,
327 int fire_events)
328 {
329 if (notes_zoom < 1.f || notes_zoom > 4.5f)
330 return;
331
332 self->notes_zoom = notes_zoom;
333
334 if (fire_events)
335 {
336 EVENTS_PUSH (
337 ET_PIANO_ROLL_KEY_HEIGHT_CHANGED, NULL);
338 }
339 }
340
341 /**
342 * Sets the MIDI modifier.
343 */
344 void
piano_roll_set_midi_modifier(PianoRoll * self,MidiModifier modifier)345 piano_roll_set_midi_modifier (
346 PianoRoll * self,
347 MidiModifier modifier)
348 {
349 self->midi_modifier = modifier;
350
351 #if 0
352 g_settings_set_enum (
353 S_UI, "piano-roll-midi-modifier",
354 modifier);
355 #endif
356
357 EVENTS_PUSH (
358 ET_PIANO_ROLL_MIDI_MODIFIER_CHANGED,
359 NULL);
360 }
361
362 void
piano_roll_init(PianoRoll * self)363 piano_roll_init (PianoRoll * self)
364 {
365 self->schema_version =
366 PIANO_ROLL_SCHEMA_VERSION;
367 self->notes_zoom = 3.f;
368
369 self->midi_modifier = MIDI_MODIFIER_VELOCITY;
370
371 editor_settings_init (&self->editor_settings);
372
373 if (!ZRYTHM_TESTING)
374 {
375 self->highlighting =
376 g_settings_get_enum (
377 S_UI, "piano-roll-highlight");
378 self->midi_modifier =
379 g_settings_get_enum (
380 S_UI, "piano-roll-midi-modifier");
381 }
382
383 init_descriptors (self);
384 }
385
386 /**
387 * Only clones what is needed for serialization.
388 */
389 PianoRoll *
piano_roll_clone(const PianoRoll * src)390 piano_roll_clone (
391 const PianoRoll * src)
392 {
393 PianoRoll * self = object_new (PianoRoll);
394 self->schema_version = PIANO_ROLL_SCHEMA_VERSION;
395
396 self->notes_zoom = src->notes_zoom;
397 self->midi_modifier = src->midi_modifier;
398 self->editor_settings = src->editor_settings;
399
400 return self;
401 }
402
403 PianoRoll *
piano_roll_new(void)404 piano_roll_new (void)
405 {
406 PianoRoll * self = object_new (PianoRoll);
407 self->schema_version = PIANO_ROLL_SCHEMA_VERSION;
408
409 return self;
410 }
411
412 void
piano_roll_free(PianoRoll * self)413 piano_roll_free (
414 PianoRoll * self)
415 {
416 for (int i = 0; i < 128; i++)
417 {
418 object_free_w_func_and_null (
419 midi_note_descriptor_free,
420 self->piano_descriptors[i]);
421 object_free_w_func_and_null (
422 midi_note_descriptor_free,
423 self->drum_descriptors[i]);
424 }
425
426 object_zero_and_free (self);
427 }
428