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 #include <stdlib.h>
21 
22 #include "audio/marker_track.h"
23 #include "audio/track.h"
24 #include "project.h"
25 #include "gui/backend/event.h"
26 #include "gui/backend/event_manager.h"
27 #include "utils/arrays.h"
28 #include "utils/flags.h"
29 #include "utils/math.h"
30 #include "utils/mem.h"
31 #include "utils/object_utils.h"
32 #include "utils/objects.h"
33 #include "zrythm_app.h"
34 
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37 
38 /**
39  * Inits the marker track.
40  */
41 void
marker_track_init(Track * self)42 marker_track_init (
43   Track * self)
44 {
45   self->type = TRACK_TYPE_MARKER;
46   self->main_height = TRACK_DEF_HEIGHT / 2;
47   self->icon_name = g_strdup ("markers");
48 
49   gdk_rgba_parse (&self->color, "#a328aa");
50 }
51 
52 /**
53  * Creates the default marker track.
54  */
55 MarkerTrack *
marker_track_default(int track_pos)56 marker_track_default (
57   int   track_pos)
58 {
59   Track * self =
60     track_new (
61       TRACK_TYPE_MARKER, track_pos, _("Markers"),
62       F_WITHOUT_LANE);
63 
64 #if 0
65   /* hack to allow setting positions */
66   double frames_per_tick_before =
67     AUDIO_ENGINE->frames_per_tick;
68   if (math_doubles_equal (
69         frames_per_tick_before, 0))
70     {
71       AUDIO_ENGINE->frames_per_tick = 512;
72     }
73 #endif
74 
75   /* add start and end markers */
76   Marker * marker;
77   Position pos;
78   marker = marker_new (_("start"));
79   ArrangerObject * m_obj =
80     (ArrangerObject *) marker;
81   position_set_to_bar (&pos, 1);
82   arranger_object_pos_setter (m_obj, &pos);
83   marker->type = MARKER_TYPE_START;
84   marker_track_add_marker (self, marker);
85   marker = marker_new (_("end"));
86   m_obj =
87     (ArrangerObject *) marker;
88   position_set_to_bar (&pos, 129);
89   arranger_object_pos_setter (m_obj, &pos);
90   marker->type = MARKER_TYPE_END;
91   marker_track_add_marker (self, marker);
92 
93 #if 0
94   if (math_doubles_equal (
95         frames_per_tick_before, 0))
96     {
97       AUDIO_ENGINE->frames_per_tick = 0;
98     }
99 #endif
100 
101   return self;
102 }
103 
104 /**
105  * Returns the start marker.
106  */
107 Marker *
marker_track_get_start_marker(const Track * self)108 marker_track_get_start_marker (
109   const Track * self)
110 {
111   g_return_val_if_fail (
112     self->type == TRACK_TYPE_MARKER, NULL);
113 
114   Marker * marker;
115   for (int i = 0; i < self->num_markers; i++)
116     {
117       marker = self->markers[i];
118       if (marker->type == MARKER_TYPE_START)
119         return marker;
120     }
121   g_critical (
122     "start marker not found in track '%s' "
123     "(num markers %d)",
124     self->name, self->num_markers);
125   return NULL;
126 }
127 
128 /**
129  * Returns the end marker.
130  */
131 Marker *
marker_track_get_end_marker(const Track * self)132 marker_track_get_end_marker (
133   const Track * self)
134 {
135   g_return_val_if_fail (
136     self->type == TRACK_TYPE_MARKER, NULL);
137 
138   Marker * marker;
139   for (int i = 0; i < self->num_markers; i++)
140     {
141       marker = self->markers[i];
142       if (marker->type == MARKER_TYPE_END)
143         return marker;
144     }
145   g_critical (
146     "end marker not found in track '%s' "
147     "(num markers %d)",
148     self->name, self->num_markers);
149   return NULL;
150 }
151 
152 /**
153  * Inserts a marker to the track.
154  */
155 void
marker_track_insert_marker(MarkerTrack * self,Marker * marker,int pos)156 marker_track_insert_marker (
157   MarkerTrack * self,
158   Marker *      marker,
159   int           pos)
160 {
161   g_return_if_fail (
162     self->type == TRACK_TYPE_MARKER && marker);
163 
164   marker_set_track_name_hash (
165     marker, track_get_name_hash (self));
166   array_double_size_if_full (
167     self->markers, self->num_markers,
168     self->markers_size, Marker *);
169   array_insert (
170     self->markers, self->num_markers, pos, marker);
171 
172   for (int i = pos; i < self->num_markers; i++)
173     {
174       Marker * m = self->markers[i];
175       marker_set_index (m, i);
176     }
177 
178   marker_track_validate (self);
179 
180   EVENTS_PUSH (
181     ET_ARRANGER_OBJECT_CREATED, marker);
182 }
183 
184 /**
185  * Adds a marker to the track.
186  */
187 void
marker_track_add_marker(MarkerTrack * self,Marker * marker)188 marker_track_add_marker (
189   MarkerTrack * self,
190   Marker *      marker)
191 {
192   marker_track_insert_marker (
193     self, marker, self->num_markers);
194 }
195 
196 /**
197  * Removes all objects from the marker track.
198  *
199  * Mainly used in testing.
200  */
201 void
marker_track_clear(MarkerTrack * self)202 marker_track_clear (
203   MarkerTrack * self)
204 {
205   for (int i = 0; i < self->num_markers; i++)
206     {
207       Marker * marker = self->markers[i];
208       if (marker->type == MARKER_TYPE_START ||
209           marker->type == MARKER_TYPE_END)
210         continue;
211       marker_track_remove_marker (self, marker, 1);
212     }
213 }
214 
215 bool
marker_track_validate(MarkerTrack * self)216 marker_track_validate (
217   MarkerTrack * self)
218 {
219   for (int i = 0; i < self->num_markers; i++)
220     {
221       Marker * m = self->markers[i];
222       g_return_val_if_fail (m->index == i, false);
223     }
224   return true;
225 }
226 
227 /**
228  * Removes a marker, optionally freeing it.
229  */
230 void
marker_track_remove_marker(MarkerTrack * self,Marker * marker,int free)231 marker_track_remove_marker (
232   MarkerTrack * self,
233   Marker *      marker,
234   int           free)
235 {
236   /* deselect */
237   arranger_selections_remove_object (
238     (ArrangerSelections *) TL_SELECTIONS,
239     (ArrangerObject *) marker);
240 
241   int pos = -1;
242   array_delete_return_pos (
243     self->markers, self->num_markers, marker, pos);
244   g_return_if_fail (pos >= 0);
245 
246   for (int i = pos; i < self->num_markers; i++)
247     {
248       Marker * m = self->markers[i];
249       marker_set_index (m, i);
250     }
251 
252   if (free)
253     free_later (marker, arranger_object_free);
254 
255   EVENTS_PUSH (
256     ET_ARRANGER_OBJECT_REMOVED,
257     ARRANGER_OBJECT_TYPE_MARKER);
258 }
259