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