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/automation_track.h"
23 #include "audio/port.h"
24 #include "audio/router.h"
25 #include "audio/tempo_track.h"
26 #include "audio/track.h"
27 #include "gui/backend/event.h"
28 #include "gui/backend/event_manager.h"
29 #include "project.h"
30 #include "utils/arrays.h"
31 #include "utils/error.h"
32 #include "utils/flags.h"
33 #include "utils/math.h"
34 #include "utils/objects.h"
35 #include "zrythm_app.h"
36
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
39
40 /**
41 * Inits the tempo track.
42 */
43 void
tempo_track_init(Track * self)44 tempo_track_init (
45 Track * self)
46 {
47 self->type = TRACK_TYPE_TEMPO;
48 self->main_height = TRACK_DEF_HEIGHT / 2;
49
50 gdk_rgba_parse (&self->color, "#2f6c52");
51 self->icon_name =
52 g_strdup ("filename-bpm-amarok");
53
54 /* create bpm port */
55 self->bpm_port =
56 port_new_with_type_and_owner (
57 TYPE_CONTROL, FLOW_INPUT, _("BPM"),
58 PORT_OWNER_TYPE_TRACK, self);
59 self->bpm_port->minf = 60.f;
60 self->bpm_port->maxf = 360.f;
61 self->bpm_port->deff = 140.f;
62 port_set_control_value (
63 self->bpm_port, self->bpm_port->deff, false,
64 false);
65 self->bpm_port->id.flags |= PORT_FLAG_BPM;
66 self->bpm_port->id.flags |=
67 PORT_FLAG_AUTOMATABLE;
68
69 /* create time sig ports */
70 self->beats_per_bar_port =
71 port_new_with_type_and_owner (
72 TYPE_CONTROL, FLOW_INPUT, _("Beats per bar"),
73 PORT_OWNER_TYPE_TRACK, self);
74 self->beats_per_bar_port->minf =
75 TEMPO_TRACK_MIN_BEATS_PER_BAR;
76 self->beats_per_bar_port->maxf =
77 TEMPO_TRACK_MAX_BEATS_PER_BAR;
78 self->beats_per_bar_port->deff =
79 TEMPO_TRACK_MIN_BEATS_PER_BAR;
80 port_set_control_value (
81 self->beats_per_bar_port,
82 TEMPO_TRACK_DEFAULT_BEATS_PER_BAR,
83 false, false);
84 self->beats_per_bar_port->id.flags2 |=
85 PORT_FLAG2_BEATS_PER_BAR;
86 self->beats_per_bar_port->id.flags |=
87 PORT_FLAG_AUTOMATABLE;
88 self->beats_per_bar_port->id.flags |=
89 PORT_FLAG_INTEGER;
90
91 self->beat_unit_port =
92 port_new_with_type_and_owner (
93 TYPE_CONTROL, FLOW_INPUT, _("Beat unit"),
94 PORT_OWNER_TYPE_TRACK, self);
95 self->beat_unit_port->minf =
96 TEMPO_TRACK_MIN_BEAT_UNIT;
97 self->beat_unit_port->maxf =
98 TEMPO_TRACK_MAX_BEAT_UNIT;
99 self->beat_unit_port->deff =
100 TEMPO_TRACK_MIN_BEAT_UNIT;
101 port_set_control_value (
102 self->beat_unit_port,
103 TEMPO_TRACK_DEFAULT_BEAT_UNIT, false, false);
104 self->beat_unit_port->id.flags2 |=
105 PORT_FLAG2_BEAT_UNIT;
106 self->beat_unit_port->id.flags |=
107 PORT_FLAG_AUTOMATABLE;
108 self->beat_unit_port->id.flags |=
109 PORT_FLAG_INTEGER;
110
111 /* set invisible */
112 self->visible = false;
113 }
114
115 /**
116 * Creates the default tempo track.
117 */
118 Track *
tempo_track_default(int track_pos)119 tempo_track_default (
120 int track_pos)
121 {
122 Track * self =
123 track_new (
124 TRACK_TYPE_TEMPO, track_pos, _("Tempo"),
125 F_WITHOUT_LANE);
126
127 return self;
128 }
129
130 /**
131 * Returns the BPM at the given pos.
132 */
133 bpm_t
tempo_track_get_bpm_at_pos(Track * self,Position * pos)134 tempo_track_get_bpm_at_pos (
135 Track * self,
136 Position * pos)
137 {
138 AutomationTrack * at =
139 automation_track_find_from_port_id (
140 &self->bpm_port->id, false);
141 return
142 automation_track_get_val_at_pos (
143 at, pos, false, false);
144 }
145
146 /**
147 * Returns the current BPM.
148 */
149 bpm_t
tempo_track_get_current_bpm(Track * self)150 tempo_track_get_current_bpm (
151 Track * self)
152 {
153 return
154 port_get_control_value (
155 self->bpm_port, false);
156 }
157
158 const char *
tempo_track_get_current_bpm_as_str(void * self)159 tempo_track_get_current_bpm_as_str (
160 void * self)
161 {
162 Track * t = (Track *) self;
163 g_return_val_if_fail (
164 IS_TRACK_AND_NONNULL (t), NULL);
165
166 static char * bpm_str = NULL;
167 if (bpm_str)
168 g_free (bpm_str);
169
170 bpm_t bpm = tempo_track_get_current_bpm (t);
171 bpm_str = g_strdup_printf ("%.2f", bpm);
172
173 return bpm_str;
174 }
175
176 /**
177 * Sets the BPM.
178 *
179 * @param update_snap_points Whether to update the
180 * snap points.
181 * @param stretch_audio_region Whether to stretch
182 * audio regions. This should only be true when
183 * the BPM change is final.
184 * @param start_bpm The BPM at the start of the
185 * action, if not temporary.
186 */
187 void
tempo_track_set_bpm(Track * self,bpm_t bpm,bpm_t start_bpm,bool temporary,bool fire_events)188 tempo_track_set_bpm (
189 Track * self,
190 bpm_t bpm,
191 bpm_t start_bpm,
192 bool temporary,
193 bool fire_events)
194 {
195 if (AUDIO_ENGINE->transport_type ==
196 AUDIO_ENGINE_NO_JACK_TRANSPORT)
197 {
198 g_debug (
199 "%s: bpm <%f>, temporary <%d>",
200 __func__, (double) bpm, temporary);
201 }
202
203 if (bpm < TEMPO_TRACK_MIN_BPM)
204 {
205 bpm = TEMPO_TRACK_MIN_BPM;
206 }
207 else if (bpm > TEMPO_TRACK_MAX_BPM)
208 {
209 bpm = TEMPO_TRACK_MAX_BPM;
210 }
211
212 if (temporary)
213 {
214 port_set_control_value (
215 self->bpm_port, bpm, false, false);
216 }
217 else
218 {
219 GError * err = NULL;
220 bool ret =
221 transport_action_perform_bpm_change (
222 start_bpm, bpm, false, &err);
223 if (!ret)
224 {
225 HANDLE_ERROR (
226 err, "%s",
227 _("Failed to change BPM"));
228 }
229 }
230
231 if (fire_events)
232 {
233 EVENTS_PUSH (ET_BPM_CHANGED, NULL);
234 }
235 }
236
237 void
tempo_track_set_bpm_from_str(void * _self,const char * str)238 tempo_track_set_bpm_from_str (
239 void * _self,
240 const char * str)
241 {
242 Track * self = (Track *) _self;
243 g_return_if_fail (IS_TRACK_AND_NONNULL (self));
244
245 bpm_t bpm = (float) atof (str);
246 if (math_floats_equal (bpm, 0))
247 g_warning ("invalid BPM %s", str);
248
249 tempo_track_set_bpm (
250 self, bpm, tempo_track_get_current_bpm (self),
251 Z_F_NOT_TEMPORARY, F_PUBLISH_EVENTS);
252 }
253
254 int
tempo_track_beat_unit_enum_to_int(BeatUnit ebeat_unit)255 tempo_track_beat_unit_enum_to_int (
256 BeatUnit ebeat_unit)
257 {
258 int bu = 0;
259 switch (ebeat_unit)
260 {
261 case BEAT_UNIT_2:
262 bu = 2;
263 break;
264 case BEAT_UNIT_4:
265 bu = 4;
266 break;
267 case BEAT_UNIT_8:
268 bu = 8;
269 break;
270 case BEAT_UNIT_16:
271 bu = 16;
272 break;
273 default:
274 g_warn_if_reached ();
275 break;
276 }
277
278 return bu;
279 }
280
281 void
tempo_track_set_beat_unit_from_enum(Track * self,BeatUnit ebeat_unit)282 tempo_track_set_beat_unit_from_enum (
283 Track * self,
284 BeatUnit ebeat_unit)
285 {
286 g_return_if_fail (
287 !engine_get_run (AUDIO_ENGINE)
288 ||
289 (ROUTER
290 &&
291 router_is_processing_kickoff_thread (ROUTER)));
292
293 port_set_control_value (
294 self->beat_unit_port, ebeat_unit,
295 F_NOT_NORMALIZED, F_PUBLISH_EVENTS);
296 EVENTS_PUSH (
297 ET_TIME_SIGNATURE_CHANGED, NULL);
298 }
299
300 BeatUnit
tempo_track_beat_unit_to_enum(int beat_unit)301 tempo_track_beat_unit_to_enum (
302 int beat_unit)
303 {
304 switch (beat_unit)
305 {
306 case 2:
307 return BEAT_UNIT_2;
308 case 4:
309 return BEAT_UNIT_4;
310 case 8:
311 return BEAT_UNIT_8;
312 case 16:
313 return BEAT_UNIT_16;;
314 default:
315 break;
316 }
317 g_return_val_if_reached (0);
318 }
319
320 BeatUnit
tempo_track_get_beat_unit_enum(Track * self)321 tempo_track_get_beat_unit_enum (
322 Track * self)
323 {
324 return
325 tempo_track_beat_unit_to_enum (
326 tempo_track_get_beat_unit (self));
327 }
328
329 void
tempo_track_set_beat_unit(Track * self,int beat_unit)330 tempo_track_set_beat_unit (
331 Track * self,
332 int beat_unit)
333 {
334 BeatUnit ebu =
335 tempo_track_beat_unit_to_enum (beat_unit);
336 tempo_track_set_beat_unit_from_enum (
337 self, ebu);
338 }
339
340 /**
341 * Updates beat unit and anything depending on it.
342 */
343 void
tempo_track_set_beats_per_bar(Track * self,int beats_per_bar)344 tempo_track_set_beats_per_bar (
345 Track * self,
346 int beats_per_bar)
347 {
348 g_return_if_fail (
349 !engine_get_run (AUDIO_ENGINE)
350 ||
351 (ROUTER
352 &&
353 router_is_processing_kickoff_thread (ROUTER)));
354
355 port_set_control_value (
356 self->beats_per_bar_port, beats_per_bar,
357 F_NOT_NORMALIZED, F_PUBLISH_EVENTS);
358 EVENTS_PUSH (
359 ET_TIME_SIGNATURE_CHANGED, NULL);
360 }
361
362 int
tempo_track_get_beats_per_bar(Track * self)363 tempo_track_get_beats_per_bar (
364 Track * self)
365 {
366 return
367 math_round_float_to_int (
368 self->beats_per_bar_port->control);
369 }
370
371 int
tempo_track_get_beat_unit(Track * self)372 tempo_track_get_beat_unit (
373 Track * self)
374 {
375 BeatUnit ebu =
376 math_round_float_to_int (
377 self->beat_unit_port->control);
378 return tempo_track_beat_unit_enum_to_int (ebu);
379 }
380
381 /**
382 * Removes all objects from the tempo track.
383 *
384 * Mainly used in testing.
385 */
386 void
tempo_track_clear(Track * self)387 tempo_track_clear (
388 Track * self)
389 {
390 /* TODO */
391 /*g_warn_if_reached ();*/
392 }
393