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