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