1 /*
2 * Copyright (C) 2019-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 * Project helper.
24 */
25
26 #ifndef __TEST_HELPERS_PROJECT_H__
27 #define __TEST_HELPERS_PROJECT_H__
28
29 #include "zrythm-test-config.h"
30
31 #include "audio/audio_region.h"
32 #include "audio/automation_region.h"
33 #include "audio/chord_region.h"
34 #include "audio/chord_track.h"
35 #include "audio/engine_dummy.h"
36 #include "audio/marker_track.h"
37 #include "audio/master_track.h"
38 #include "audio/midi_note.h"
39 #include "audio/modulator_track.h"
40 #include "audio/recording_manager.h"
41 #include "audio/region.h"
42 #include "audio/router.h"
43 #include "audio/tempo_track.h"
44 #include "audio/tracklist.h"
45 #include "project.h"
46 #include "utils/cairo.h"
47 #include "utils/objects.h"
48 #include "utils/flags.h"
49 #include "utils/ui.h"
50 #include "zrythm.h"
51
52 #include "tests/helpers/zrythm.h"
53
54 #include <glib.h>
55 #include <glib/gi18n.h>
56
57 /**
58 * @addtogroup tests
59 *
60 * @{
61 */
62
63 /** MidiNote value to use. */
64 #define MN_VAL 78
65 /** MidiNote velocity to use. */
66 #define MN_VEL 23
67
68 /** First AP value. */
69 #define AP_VAL1 0.6f
70 /** Second AP value. */
71 #define AP_VAL2 0.9f
72
73 /** Marker name. */
74 #define MARKER_NAME "Marker name"
75
76 #define MUSICAL_SCALE_TYPE SCALE_IONIAN
77 #define MUSICAL_SCALE_ROOT NOTE_A
78
79 #define MOVE_TICKS 400
80
81 #define TOTAL_TL_SELECTIONS 6
82
83 #define MIDI_REGION_NAME "Midi region"
84 #define AUDIO_REGION_NAME "Audio region"
85 #define MIDI_TRACK_NAME "Midi track"
86 #define AUDIO_TRACK_NAME "Audio track"
87
88 /* initial positions */
89 #define MIDI_REGION_LANE 2
90 #define AUDIO_REGION_LANE 3
91
92 /* target positions */
93 #define TARGET_MIDI_TRACK_NAME "Target midi tr"
94 #define TARGET_AUDIO_TRACK_NAME "Target audio tr"
95
96 /* TODO test moving lanes */
97 #define TARGET_MIDI_REGION_LANE 0
98 #define TARGET_AUDIO_REGION_LANE 5
99
100 char *
101 test_project_save (void);
102
103 COLD
104 void
105 test_project_reload (
106 const char * prj_file);
107
108 void
109 test_project_save_and_reload (void);
110
111 /**
112 * Stop dummy audio engine processing so we can
113 * process manually.
114 */
115 void
116 test_project_stop_dummy_engine (void);
117
118 void
119 test_project_check_vs_original_state (
120 Position * p1,
121 Position * p2,
122 int check_selections);
123
124 void
125 test_project_rebootstrap_timeline (
126 Position * p1,
127 Position * p2);
128
129 char *
test_project_save(void)130 test_project_save (void)
131 {
132 /* save the project */
133 int ret =
134 project_save (
135 PROJECT, PROJECT->dir, 0, 0, F_NO_ASYNC);
136 g_assert_cmpint (ret, ==, 0);
137 char * prj_file =
138 g_build_filename (
139 PROJECT->dir, PROJECT_FILE, NULL);
140
141 object_free_w_func_and_null (
142 project_free, PROJECT);
143
144 return prj_file;
145 }
146
147 void
test_project_reload(const char * prj_file)148 test_project_reload (
149 const char * prj_file)
150 {
151 int ret = project_load (prj_file, 0);
152 g_assert_cmpint (ret, ==, 0);
153 }
154
155 void
test_project_save_and_reload(void)156 test_project_save_and_reload (void)
157 {
158 /* save the project */
159 char * prj_file = test_project_save ();
160 g_assert_nonnull (prj_file);
161
162 /* recreate the recording manager to drop any
163 * events */
164 object_free_w_func_and_null (
165 recording_manager_free,
166 ZRYTHM->recording_manager);
167 ZRYTHM->recording_manager =
168 recording_manager_new ();
169
170 /* reload it */
171 test_project_reload (prj_file);
172 g_free (prj_file);
173 }
174
175 /**
176 * Checks that the objects are back to their original
177 * state.
178 * @param check_selections Also checks that the
179 * selections are back to where they were.
180 */
181 void
test_project_check_vs_original_state(Position * p1,Position * p2,int check_selections)182 test_project_check_vs_original_state (
183 Position * p1,
184 Position * p2,
185 int check_selections)
186 {
187 bool after_reload = false;
188
189 check_vs_orig_state:
190
191 if (check_selections)
192 {
193 g_assert_cmpint (
194 TL_SELECTIONS->num_regions, ==, 4);
195 g_assert_cmpint (
196 TL_SELECTIONS->num_markers, ==, 1);
197 g_assert_cmpint (
198 TL_SELECTIONS->num_scale_objects, ==, 1);
199 }
200
201 Track * midi_track =
202 tracklist_find_track_by_name (
203 TRACKLIST, MIDI_TRACK_NAME);
204 g_assert_nonnull (midi_track);
205 Track * audio_track =
206 tracklist_find_track_by_name (
207 TRACKLIST, AUDIO_TRACK_NAME);
208 g_assert_nonnull (audio_track);
209
210 Position p1_before_move, p2_before_move;
211 p1_before_move = *p1;
212 p2_before_move = *p2;
213 position_add_ticks (
214 &p1_before_move, - MOVE_TICKS);
215 position_add_ticks (
216 &p2_before_move, - MOVE_TICKS);
217
218 /* check midi region */
219 g_assert_cmpint (
220 midi_track->lanes[MIDI_REGION_LANE]->
221 num_regions, ==, 1);
222 ArrangerObject * obj =
223 (ArrangerObject *)
224 midi_track->lanes[MIDI_REGION_LANE]->regions[0];
225 g_assert_cmppos (&obj->pos, p1);
226 g_assert_cmppos (&obj->end_pos, p2);
227 ZRegion * r = (ZRegion *) obj;
228 g_assert_cmpint (r->num_midi_notes, ==, 1);
229 MidiNote * mn = r->midi_notes[0];
230 obj = (ArrangerObject *) mn;
231 g_assert_cmpuint (mn->val, ==, MN_VAL);
232 g_assert_cmpuint (mn->vel->vel, ==, MN_VEL);
233 g_assert_cmppos (&obj->pos, p1);
234 g_assert_cmppos (&obj->end_pos, p2);
235 g_assert_true (
236 region_identifier_is_equal (
237 &obj->region_id, &r->id));
238
239 /* check audio region */
240 g_assert_cmpint (
241 audio_track->lanes[AUDIO_REGION_LANE]->
242 num_regions, ==, 1);
243 obj =
244 (ArrangerObject *)
245 audio_track->lanes[AUDIO_REGION_LANE]->
246 regions[0];
247 g_assert_cmppos (&obj->pos, p1);
248
249 /* check automation region */
250 AutomationTracklist * atl =
251 track_get_automation_tracklist (P_MASTER_TRACK);
252 g_assert_nonnull (atl);
253 AutomationTrack * at =
254 channel_get_automation_track (
255 P_MASTER_TRACK->channel,
256 PORT_FLAG_STEREO_BALANCE);
257 g_assert_nonnull (at);
258 g_assert_cmpint (at->num_regions, ==, 1);
259 obj =
260 (ArrangerObject *) at->regions[0];
261 g_assert_cmppos (&obj->pos, p1);
262 g_assert_cmppos (&obj->end_pos, p2);
263 r =
264 (ZRegion *) obj;
265 g_assert_cmpint (r->num_aps, ==, 2);
266 AutomationPoint * ap =
267 r->aps[0];
268 obj = (ArrangerObject *) ap;
269 g_assert_cmppos (&obj->pos, p1);
270 g_assert_cmpfloat_with_epsilon (
271 ap->fvalue, AP_VAL1, 0.000001f);
272 ap =
273 r->aps[1];
274 obj = (ArrangerObject *) ap;
275 g_assert_cmppos (&obj->pos, p2);
276 g_assert_cmpfloat_with_epsilon (
277 ap->fvalue, AP_VAL2, 0.000001f);
278
279 /* check marker */
280 Marker * m;
281 g_assert_cmpint (
282 P_MARKER_TRACK->num_markers, ==, 3);
283 obj =
284 (ArrangerObject *) P_MARKER_TRACK->markers[0];
285 m = (Marker *) obj;
286 g_assert_true (m);
287 g_assert_cmpstr (m->name, ==, "start");
288 obj =
289 (ArrangerObject *) P_MARKER_TRACK->markers[2];
290 m = (Marker *) obj;
291 g_assert_cmppos (&obj->pos, p1);
292 g_assert_cmpstr (m->name, ==, MARKER_NAME);
293
294 /* check scale object */
295 g_assert_cmpint (
296 P_CHORD_TRACK->num_scales, ==, 1);
297 obj =
298 (ArrangerObject *)
299 P_CHORD_TRACK->scales[0];
300 ScaleObject * s = (ScaleObject *) obj;
301 g_assert_cmppos (&obj->pos, p1);
302 g_assert_cmpint (
303 s->scale->type, ==, MUSICAL_SCALE_TYPE);
304 g_assert_cmpint (
305 s->scale->root_key, ==, MUSICAL_SCALE_ROOT);
306
307 /* save the project and reopen it. some callers
308 * undo after this step so this checks if the undo
309 * history works after reopening the project */
310 if (!after_reload)
311 {
312 test_project_save_and_reload ();
313 after_reload = true;
314 goto check_vs_orig_state;
315 }
316 }
317
318 /**
319 * Bootstraps the test with test data.
320 */
321 void
test_project_rebootstrap_timeline(Position * p1,Position * p2)322 test_project_rebootstrap_timeline (
323 Position * p1,
324 Position * p2)
325 {
326 test_helper_zrythm_init ();
327
328 /* pause engine */
329 EngineState state;
330 engine_wait_for_pause (
331 AUDIO_ENGINE, &state, false);
332
333 /* remove any previous work */
334 chord_track_clear (P_CHORD_TRACK);
335 marker_track_clear (P_MARKER_TRACK);
336 tempo_track_clear (P_TEMPO_TRACK);
337 //modulator_track_clear (P_MODULATOR_TRACK);
338 for (int i = TRACKLIST->num_tracks - 1;
339 i > P_MASTER_TRACK->pos; i--)
340 {
341 Track * track = TRACKLIST->tracks[i];
342 tracklist_remove_track (
343 TRACKLIST, track, F_REMOVE_PL, F_FREE,
344 F_NO_PUBLISH_EVENTS, F_NO_RECALC_GRAPH);
345 }
346 track_clear (P_MASTER_TRACK);
347
348 /* Create and add a MidiRegion with a MidiNote */
349 position_set_to_bar (p1, 2);
350 position_set_to_bar (p2, 4);
351 Track * track =
352 track_new (
353 TRACK_TYPE_MIDI, TRACKLIST->num_tracks,
354 MIDI_TRACK_NAME,
355 F_WITH_LANE);
356 tracklist_append_track (
357 TRACKLIST, track, F_NO_PUBLISH_EVENTS,
358 F_NO_RECALC_GRAPH);
359 unsigned int track_name_hash =
360 track_get_name_hash (track);
361 ZRegion * r =
362 midi_region_new (
363 p1, p2,
364 track_get_name_hash (track),
365 MIDI_REGION_LANE, 0);
366 ArrangerObject * r_obj = (ArrangerObject *) r;
367 track_add_region (
368 track, r, NULL, MIDI_REGION_LANE, 1, 0);
369 arranger_object_set_name (
370 r_obj, MIDI_REGION_NAME,
371 F_NO_PUBLISH_EVENTS);
372 g_assert_cmpuint (
373 r->id.track_name_hash, ==, track_name_hash);
374 g_assert_cmpint (
375 r->id.lane_pos, ==, MIDI_REGION_LANE);
376 g_assert_cmpint (
377 r->id.type, ==, REGION_TYPE_MIDI);
378 arranger_selections_add_object (
379 (ArrangerSelections *) TL_SELECTIONS,
380 (ArrangerObject *) r);
381
382 /* add a midi note to the region */
383 MidiNote * mn =
384 midi_note_new (
385 &r->id, p1, p2, MN_VAL, MN_VEL);
386 midi_region_add_midi_note (r, mn, 0);
387 ArrangerObject * mn_obj =
388 (ArrangerObject *) mn;
389 g_assert_true (
390 region_identifier_is_equal (
391 &mn_obj->region_id, &r->id));
392 arranger_selections_add_object (
393 (ArrangerSelections *) MA_SELECTIONS,
394 (ArrangerObject *) mn);
395
396 /* Create and add an automation region with
397 * 2 AutomationPoint's */
398 AutomationTrack * at =
399 channel_get_automation_track (
400 P_MASTER_TRACK->channel,
401 PORT_FLAG_STEREO_BALANCE);
402 track_name_hash =
403 track_get_name_hash (P_MASTER_TRACK);
404 r =
405 automation_region_new (
406 p1, p2, track_name_hash, at->index, 0);
407 track_add_region (
408 P_MASTER_TRACK, r, at, 0, F_GEN_NAME, 0);
409 g_assert_cmpuint (
410 r->id.track_name_hash, ==, track_name_hash);
411 g_assert_cmpint (r->id.at_idx, ==, at->index);
412 g_assert_cmpint (
413 r->id.type, ==, REGION_TYPE_AUTOMATION);
414 arranger_selections_add_object (
415 (ArrangerSelections *) TL_SELECTIONS,
416 (ArrangerObject *) r);
417
418 /* add 2 automation points to the region */
419 AutomationPoint * ap =
420 automation_point_new_float (
421 AP_VAL1, AP_VAL1, p1);
422 automation_region_add_ap (
423 r, ap, F_NO_PUBLISH_EVENTS);
424 arranger_selections_add_object (
425 (ArrangerSelections *) AUTOMATION_SELECTIONS,
426 (ArrangerObject *) ap);
427 ap =
428 automation_point_new_float (
429 AP_VAL2, AP_VAL1, p2);
430 automation_region_add_ap (
431 r, ap, F_NO_PUBLISH_EVENTS);
432 arranger_selections_add_object (
433 (ArrangerSelections *) AUTOMATION_SELECTIONS,
434 (ArrangerObject *) ap);
435
436 /* Create and add a chord region with
437 * 2 Chord's */
438 r =
439 chord_region_new (p1, p2, 0);
440 track_add_region (
441 P_CHORD_TRACK, r, NULL, 0, F_GEN_NAME,
442 F_NO_PUBLISH_EVENTS);
443 arranger_selections_add_object (
444 (ArrangerSelections *) TL_SELECTIONS,
445 (ArrangerObject *) r);
446
447 /* add 2 chords to the region */
448 ChordObject * c =
449 chord_object_new (&r->id, 0, 1);
450 chord_region_add_chord_object (
451 r, c, F_NO_PUBLISH_EVENTS);
452 arranger_object_pos_setter (
453 (ArrangerObject *) c, p1);
454 arranger_selections_add_object (
455 (ArrangerSelections *) CHORD_SELECTIONS,
456 (ArrangerObject *) c);
457 c =
458 chord_object_new (&r->id, 0, 1);
459 chord_region_add_chord_object (
460 r, c, F_NO_PUBLISH_EVENTS);
461 arranger_object_pos_setter (
462 (ArrangerObject *) c, p2);
463 arranger_selections_add_object (
464 (ArrangerSelections *) CHORD_SELECTIONS,
465 (ArrangerObject *) c);
466
467 /* create and add a Marker */
468 Marker * m = marker_new (MARKER_NAME);
469 marker_track_add_marker (
470 P_MARKER_TRACK, m);
471 g_assert_cmpint (m->index, ==, 2);
472 arranger_selections_add_object (
473 (ArrangerSelections *) TL_SELECTIONS,
474 (ArrangerObject *) m);
475 arranger_object_pos_setter (
476 (ArrangerObject *) m, p1);
477
478 /* create and add a ScaleObject */
479 MusicalScale * ms =
480 musical_scale_new (
481 MUSICAL_SCALE_TYPE, MUSICAL_SCALE_ROOT);
482 ScaleObject * s = scale_object_new (ms);
483 chord_track_add_scale (
484 P_CHORD_TRACK, s);
485 arranger_selections_add_object (
486 (ArrangerSelections *) TL_SELECTIONS,
487 (ArrangerObject *) s);
488 arranger_object_pos_setter (
489 (ArrangerObject *) s, p1);
490
491 /* Create and add an audio region */
492 position_set_to_bar (p1, 2);
493 track =
494 track_new (
495 TRACK_TYPE_AUDIO, TRACKLIST->num_tracks,
496 AUDIO_TRACK_NAME, F_WITH_LANE);
497 tracklist_append_track (
498 TRACKLIST, track, F_NO_PUBLISH_EVENTS,
499 F_NO_RECALC_GRAPH);
500 char audio_file_path[2000];
501 sprintf (
502 audio_file_path, "%s%s%s",
503 TESTS_SRCDIR, G_DIR_SEPARATOR_S,
504 "test.wav");
505 track_name_hash = track_get_name_hash (track);
506 r =
507 audio_region_new (
508 -1, audio_file_path, true, NULL, 0, NULL, 0, 0,
509 p1, track_name_hash, AUDIO_REGION_LANE, 0);
510 AudioClip * clip =
511 audio_region_get_clip (r);
512 g_assert_cmpint (clip->num_frames, >, 151000);
513 g_assert_cmpint (clip->num_frames, <, 152000);
514 track_add_region (
515 track, r, NULL, AUDIO_REGION_LANE, 1, 0);
516 r_obj = (ArrangerObject *) r;
517 arranger_object_set_name (
518 r_obj, AUDIO_REGION_NAME,
519 F_NO_PUBLISH_EVENTS);
520 g_assert_cmpuint (
521 r->id.track_name_hash, ==, track_name_hash);
522 g_assert_cmpint (
523 r->id.lane_pos, ==, AUDIO_REGION_LANE);
524 g_assert_cmpint (
525 r->id.type, ==, REGION_TYPE_AUDIO);
526 arranger_selections_add_object (
527 (ArrangerSelections *) TL_SELECTIONS,
528 (ArrangerObject *) r);
529
530 /* create the target tracks */
531 track =
532 track_new (
533 TRACK_TYPE_MIDI, TRACKLIST->num_tracks,
534 TARGET_MIDI_TRACK_NAME,
535 F_WITH_LANE);
536 tracklist_append_track (
537 TRACKLIST, track, F_NO_PUBLISH_EVENTS,
538 F_NO_RECALC_GRAPH);
539 track =
540 track_new (
541 TRACK_TYPE_AUDIO, TRACKLIST->num_tracks,
542 TARGET_AUDIO_TRACK_NAME,
543 F_WITH_LANE);
544 tracklist_append_track (
545 TRACKLIST, track, F_NO_PUBLISH_EVENTS,
546 F_NO_RECALC_GRAPH);
547
548 int beats_per_bar =
549 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
550 bpm_t bpm =
551 tempo_track_get_current_bpm (P_TEMPO_TRACK);
552 engine_update_frames_per_tick (
553 AUDIO_ENGINE, beats_per_bar, bpm,
554 AUDIO_ENGINE->sample_rate, true, true);
555
556 router_recalc_graph (ROUTER, F_NOT_SOFT);
557
558 engine_resume (
559 AUDIO_ENGINE, &state);
560
561 test_project_save_and_reload ();
562 }
563
564 /**
565 * Stop dummy audio engine processing so we can
566 * process manually.
567 */
568 void
test_project_stop_dummy_engine(void)569 test_project_stop_dummy_engine (void)
570 {
571 AUDIO_ENGINE->stop_dummy_audio_thread = true;
572 g_usleep (100000);
573 }
574
575 /**
576 * @}
577 */
578
579 #endif
580