1 /*
2  * Copyright (C) 2018-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 "zrythm-config.h"
21 
22 #include "audio/audio_region.h"
23 #include "audio/engine.h"
24 #include "audio/marker.h"
25 #include "audio/marker_track.h"
26 #include "audio/midi_event.h"
27 #include "audio/tempo_track.h"
28 #include "audio/transport.h"
29 #include "project.h"
30 #include "gui/backend/event.h"
31 #include "gui/backend/event_manager.h"
32 #include "gui/widgets/arranger.h"
33 #include "gui/widgets/arranger_playhead.h"
34 #include "gui/widgets/bot_dock_edge.h"
35 #include "gui/widgets/center_dock.h"
36 #include "gui/widgets/digital_meter.h"
37 #include "gui/widgets/main_window.h"
38 #include "gui/widgets/midi_arranger.h"
39 #include "gui/widgets/midi_modifier_arranger.h"
40 #include "gui/widgets/editor_ruler.h"
41 #include "gui/widgets/timeline_arranger.h"
42 #include "gui/widgets/timeline_ruler.h"
43 #include "gui/widgets/top_bar.h"
44 #include "settings/settings.h"
45 #include "utils/debug.h"
46 #include "utils/flags.h"
47 #include "utils/math.h"
48 #include "utils/objects.h"
49 #include "zrythm_app.h"
50 
51 #include <gtk/gtk.h>
52 
53 /**
54  * A buffer of n bars after the end of the last
55  * object.
56  */
57 #define BARS_END_BUFFER 4
58 
59 static void
init_common(Transport * self)60 init_common (
61   Transport * self)
62 {
63   self->schema_version = TRANSPORT_SCHEMA_VERSION;
64 
65   /* set playstate */
66   self->play_state = PLAYSTATE_PAUSED;
67 
68   self->loop =
69     ZRYTHM_TESTING ? true :
70     g_settings_get_boolean (
71       S_TRANSPORT, "loop");
72   self->metronome_enabled =
73     ZRYTHM_TESTING ? true :
74     g_settings_get_boolean (
75       S_TRANSPORT, "metronome-enabled");
76   self->punch_mode =
77     ZRYTHM_TESTING ? true :
78     g_settings_get_boolean (
79       S_TRANSPORT, "punch-mode");
80   self->start_playback_on_midi_input =
81     ZRYTHM_TESTING ? false :
82     g_settings_get_boolean (
83       S_TRANSPORT, "start-on-midi-input");
84   self->recording_mode =
85     ZRYTHM_TESTING ? RECORDING_MODE_TAKES :
86     g_settings_get_enum (
87       S_TRANSPORT, "recording-mode");
88 
89   zix_sem_init (&self->paused, 0);
90 }
91 
92 /**
93  * Initialize loaded transport.
94  *
95  * @param engine Owner engine, if any.
96  * @param tempo_track Tempo track, used to
97  *   initialize the caches. Only needed on the
98  *   active project transport.
99  */
100 void
transport_init_loaded(Transport * self,AudioEngine * engine,Track * tempo_track)101 transport_init_loaded (
102   Transport *   self,
103   AudioEngine * engine,
104   Track *       tempo_track)
105 {
106   self->audio_engine = engine;
107 
108   z_return_if_fail_cmp (self->total_bars, >, 0);
109 
110   init_common (self);
111 
112   if (tempo_track)
113     {
114       int beats_per_bar =
115         tempo_track_get_beats_per_bar (
116           tempo_track);
117       int beat_unit =
118         tempo_track_get_beat_unit (
119           tempo_track);
120       transport_update_caches (
121         self, beats_per_bar, beat_unit);
122     }
123 
124 #define INIT_LOADED_PORT(x) \
125   port_init_loaded (self->x, self)
126 
127   INIT_LOADED_PORT (roll);
128   INIT_LOADED_PORT (stop);
129   INIT_LOADED_PORT (backward);
130   INIT_LOADED_PORT (forward);
131   INIT_LOADED_PORT (loop_toggle);
132   INIT_LOADED_PORT (rec_toggle);
133 
134 #undef INIT_LOADED_PORT
135 }
136 
137 /**
138  * Create a new transport.
139  */
140 Transport *
transport_new(AudioEngine * engine)141 transport_new (
142   AudioEngine * engine)
143 {
144   g_message (
145     "%s: Creating transport...", __func__);
146 
147   Transport * self = object_new (Transport);
148   self->audio_engine = engine;
149   self->schema_version =
150     TRANSPORT_SCHEMA_VERSION;
151 
152   if (engine)
153     {
154       engine->transport = self;
155     }
156 
157   // set inital total number of beats
158   // this is applied to the ruler
159   self->total_bars =
160     TRANSPORT_DEFAULT_TOTAL_BARS;
161 
162   g_return_val_if_fail (
163     engine->sample_rate > 0, NULL);
164 
165   /* hack to allow setting positions */
166   /*double frames_per_tick_before =*/
167     /*AUDIO_ENGINE->frames_per_tick;*/
168   /*if (math_doubles_equal (*/
169         /*frames_per_tick_before, 0))*/
170     /*{*/
171       /*AUDIO_ENGINE->frames_per_tick = 512;*/
172     /*}*/
173 
174   /* set positions */
175   position_init (&self->playhead_pos);
176   position_init (&self->cue_pos);
177   position_init (&self->loop_start_pos);
178   position_init (&self->loop_end_pos);
179   position_init (&self->punch_in_pos);
180   position_init (&self->punch_out_pos);
181   position_init (&self->range_1);
182   position_init (&self->range_2);
183 
184   double ticks_per_bar =
185     TICKS_PER_QUARTER_NOTE * 4.0;
186   self->loop_end_pos.ticks =
187     4 * ticks_per_bar;
188   self->punch_in_pos.ticks =
189     2 * ticks_per_bar;
190   self->punch_out_pos.ticks =
191     4 * ticks_per_bar;
192 
193   self->range_1.ticks = 1 * ticks_per_bar;
194   self->range_2.ticks = 1 * ticks_per_bar;
195 
196   /*if (math_doubles_equal (*/
197         /*frames_per_tick_before, 0))*/
198     /*{*/
199       /*AUDIO_ENGINE->frames_per_tick = 0;*/
200     /*}*/
201 
202   /* create ports */
203   self->roll =
204     port_new_with_type (
205       TYPE_EVENT, FLOW_INPUT, "Roll");
206   port_set_owner (
207     self->roll, PORT_OWNER_TYPE_TRANSPORT, self);
208   self->roll->id.flags |= PORT_FLAG_TOGGLE;
209   self->roll->id.flags2 |=
210     PORT_FLAG2_TRANSPORT_ROLL;
211 
212   self->stop =
213     port_new_with_type (
214       TYPE_EVENT, FLOW_INPUT, "Stop");
215   port_set_owner (
216     self->stop, PORT_OWNER_TYPE_TRANSPORT, self);
217   self->stop->id.flags |= PORT_FLAG_TOGGLE;
218   self->stop->id.flags2 |=
219     PORT_FLAG2_TRANSPORT_STOP;
220 
221   self->backward =
222     port_new_with_type (
223       TYPE_EVENT, FLOW_INPUT, "Backward");
224   port_set_owner (
225     self->backward, PORT_OWNER_TYPE_TRANSPORT,
226     self);
227   self->backward->id.flags |= PORT_FLAG_TOGGLE;
228   self->backward->id.flags2 |=
229     PORT_FLAG2_TRANSPORT_BACKWARD;
230 
231   self->forward =
232     port_new_with_type (
233       TYPE_EVENT, FLOW_INPUT, "Forward");
234   port_set_owner (
235     self->forward, PORT_OWNER_TYPE_TRANSPORT,
236     self);
237   self->forward->id.flags |= PORT_FLAG_TOGGLE;
238   self->forward->id.flags2 |=
239     PORT_FLAG2_TRANSPORT_FORWARD;
240 
241   self->loop_toggle =
242     port_new_with_type (
243       TYPE_EVENT, FLOW_INPUT, "Loop toggle");
244   port_set_owner (
245     self->loop_toggle, PORT_OWNER_TYPE_TRANSPORT,
246     self);
247   self->loop_toggle->id.flags |= PORT_FLAG_TOGGLE;
248   self->loop_toggle->id.flags2 |=
249     PORT_FLAG2_TRANSPORT_LOOP_TOGGLE;
250 
251   self->rec_toggle =
252     port_new_with_type (
253       TYPE_EVENT, FLOW_INPUT, "Rec toggle");
254   port_set_owner (
255     self->rec_toggle, PORT_OWNER_TYPE_TRANSPORT,
256     self);
257   self->rec_toggle->id.flags |= PORT_FLAG_TOGGLE;
258   self->rec_toggle->id.flags2 |=
259     PORT_FLAG2_TRANSPORT_REC_TOGGLE;
260 
261   init_common (self);
262 
263   return self;
264 }
265 
266 /**
267  * Clones the transport values.
268  */
269 Transport *
transport_clone(const Transport * src)270 transport_clone (
271   const Transport * src)
272 {
273   Transport * self =
274     object_new (Transport);
275   self->schema_version =
276     TRANSPORT_SCHEMA_VERSION;
277 
278   self->total_bars = src->total_bars;
279   self->has_range = src->has_range;
280   self->position = src->position;
281 
282   position_set_to_pos (
283     &self->loop_start_pos, &src->loop_start_pos);
284   position_set_to_pos (
285     &self->playhead_pos, &src->playhead_pos);
286   position_set_to_pos (
287     &self->loop_end_pos, &src->loop_end_pos);
288   position_set_to_pos (
289     &self->cue_pos, &src->cue_pos);
290   position_set_to_pos (
291     &self->punch_in_pos, &src->punch_in_pos);
292   position_set_to_pos (
293     &self->punch_out_pos, &src->punch_out_pos);
294   position_set_to_pos (
295     &self->range_1, &src->range_1);
296   position_set_to_pos (
297     &self->range_2, &src->range_2);
298 
299 #define CLONE_PORT_IF_EXISTS(x) \
300   if (src->x) \
301     self->x = port_clone (src->x)
302 
303   CLONE_PORT_IF_EXISTS (roll);
304   CLONE_PORT_IF_EXISTS (stop);
305   CLONE_PORT_IF_EXISTS (backward);
306   CLONE_PORT_IF_EXISTS (forward);
307   CLONE_PORT_IF_EXISTS (loop_toggle);
308   CLONE_PORT_IF_EXISTS (rec_toggle);
309 
310 #undef CLONE_PORT_IF_EXISTS
311 
312   return self;
313 }
314 
315 /**
316  * Prepares audio regions for stretching (sets the
317  * \ref ZRegion.before_length).
318  *
319  * @param selections If NULL, all audio regions
320  *   are used. If non-NULL, only the regions in the
321  *   selections are used.
322  */
323 void
transport_prepare_audio_regions_for_stretch(Transport * self,TimelineSelections * sel)324 transport_prepare_audio_regions_for_stretch (
325   Transport *          self,
326   TimelineSelections * sel)
327 {
328   if (sel)
329     {
330       for (int i = 0; i < sel->num_regions; i++)
331         {
332           ZRegion * region = sel->regions[i];
333           region->before_length =
334             arranger_object_get_length_in_ticks (
335               (ArrangerObject *) region);
336         }
337     }
338   else
339     {
340       for (int i = 0; i < TRACKLIST->num_tracks; i++)
341         {
342           Track * track = TRACKLIST->tracks[i];
343 
344           if (track->type != TRACK_TYPE_AUDIO)
345             continue;
346 
347           for (int j = 0; j < track->num_lanes; j++)
348             {
349               TrackLane * lane = track->lanes[j];
350 
351               for (int k = 0; k < lane->num_regions;
352                    k++)
353                 {
354                   ZRegion * region =
355                     lane->regions[k];
356                   region->before_length =
357                     arranger_object_get_length_in_ticks (
358                       (ArrangerObject *) region);
359                 } // foreach region
360             } // foreach lane
361         } // foreach track
362     }
363 }
364 
365 /**
366  * Stretches audio regions.
367  *
368  * @param selections If NULL, all audio regions
369  *   are used. If non-NULL, only the regions in the
370  *   selections are used.
371  * @param with_fixed_ratio Stretch all regions with
372  *   a fixed ratio. If this is off, the current
373  *   region length and \ref ZRegion.before_length
374  *   will be used to calculate the ratio.
375  */
376 void
transport_stretch_audio_regions(Transport * self,TimelineSelections * sel,bool with_fixed_ratio,double time_ratio)377 transport_stretch_audio_regions (
378   Transport *          self,
379   TimelineSelections * sel,
380   bool                 with_fixed_ratio,
381   double               time_ratio)
382 {
383   if (sel)
384     {
385       for (int i = 0; i < sel->num_regions; i++)
386         {
387           ZRegion * region =
388             TL_SELECTIONS->regions[i];
389 
390           /* don't stretch audio regions with
391            * musical mode off */
392           if (region->id.type == REGION_TYPE_AUDIO
393               && !region_get_musical_mode (region))
394             continue;
395 
396           ArrangerObject * r_obj =
397             (ArrangerObject *) region;
398           double ratio =
399             with_fixed_ratio ? time_ratio :
400             arranger_object_get_length_in_ticks (
401               r_obj) /
402             region->before_length;
403           region_stretch (region, ratio);
404         }
405     }
406   else
407     {
408       for (int i = 0; i < TRACKLIST->num_tracks; i++)
409         {
410           Track * track = TRACKLIST->tracks[i];
411 
412           if (track->type != TRACK_TYPE_AUDIO)
413             continue;
414 
415           for (int j = 0; j < track->num_lanes; j++)
416             {
417               TrackLane * lane = track->lanes[j];
418 
419               for (int k = 0; k < lane->num_regions;
420                    k++)
421                 {
422                   ZRegion * region =
423                     lane->regions[k];
424 
425                   /* don't stretch regions with
426                    * musical mode off */
427                   if (!region_get_musical_mode (
428                          region))
429                     continue;
430 
431                   ArrangerObject * r_obj =
432                     (ArrangerObject *) region;
433                   double ratio =
434                     with_fixed_ratio ? time_ratio :
435                     arranger_object_get_length_in_ticks (
436                       r_obj) /
437                     region->before_length;
438                   region_stretch (region, ratio);
439                 }
440             }
441         }
442     }
443 }
444 
445 void
transport_set_punch_mode_enabled(Transport * self,bool enabled)446 transport_set_punch_mode_enabled (
447   Transport * self,
448   bool        enabled)
449 {
450   self->punch_mode = enabled;
451 
452   if (!ZRYTHM_TESTING)
453     {
454       g_settings_set_boolean (
455         S_TRANSPORT, "punch-mode", enabled);
456     }
457 }
458 
459 void
transport_set_start_playback_on_midi_input(Transport * self,bool enabled)460 transport_set_start_playback_on_midi_input (
461   Transport * self,
462   bool        enabled)
463 {
464   self->start_playback_on_midi_input = enabled;
465   g_settings_set_boolean (
466     S_TRANSPORT, "start-on-midi-input", enabled);
467 }
468 
469 void
transport_set_recording_mode(Transport * self,TransportRecordingMode mode)470 transport_set_recording_mode (
471   Transport * self,
472   TransportRecordingMode mode)
473 {
474   self->recording_mode = mode;
475 
476   if (!ZRYTHM_TESTING)
477     {
478       g_settings_set_enum (
479         S_TRANSPORT, "recording-mode", mode);
480     }
481 }
482 
483 /**
484  * Updates beat unit and anything depending on it.
485  */
486 void
transport_update_caches(Transport * self,int beats_per_bar,int beat_unit)487 transport_update_caches (
488   Transport * self,
489   int         beats_per_bar,
490   int         beat_unit)
491 {
492   /**
493    * Regarding calculation:
494    * 3840 = TICKS_PER_QUARTER_NOTE * 4 to get the ticks
495    * per full note.
496    * Divide by beat unit (e.g. if beat unit is 2,
497    * it means it is a 1/2th note, so multiply 1/2
498    * with the ticks per note
499    */
500   self->ticks_per_beat =
501     3840 / beat_unit;
502   self->ticks_per_bar =
503     self->ticks_per_beat * beats_per_bar;
504   self->sixteenths_per_beat = 16 / beat_unit;
505   self->sixteenths_per_bar =
506     (self->sixteenths_per_beat * beats_per_bar);
507   g_warn_if_fail (self->ticks_per_bar > 0.0);
508   g_warn_if_fail (self->ticks_per_beat > 0.0);
509 }
510 
511 void
transport_request_pause(Transport * self)512 transport_request_pause (
513   Transport * self)
514 {
515   self->play_state = PLAYSTATE_PAUSE_REQUESTED;
516 
517   TRANSPORT->playhead_before_pause =
518     self->playhead_pos;
519   if (!ZRYTHM_TESTING &&
520       g_settings_get_boolean (
521         S_TRANSPORT, "return-to-cue"))
522     {
523       transport_move_playhead (
524         self, &self->cue_pos, F_PANIC,
525         F_NO_SET_CUE_POINT, F_PUBLISH_EVENTS);
526     }
527 }
528 
529 void
transport_request_roll(Transport * self)530 transport_request_roll (
531   Transport * self)
532 {
533   if (!ZRYTHM_TESTING)
534     {
535       /* handle countin */
536       PrerollCountBars bars =
537         g_settings_get_enum (
538           S_TRANSPORT, "metronome-countin");
539       int num_bars =
540         transport_preroll_count_bars_enum_to_int (
541           bars);
542       double frames_per_bar =
543         AUDIO_ENGINE->frames_per_tick *
544         (double) TRANSPORT->ticks_per_bar;
545       self->countin_frames_remaining =
546         (long) ((double) num_bars * frames_per_bar);
547       if (self->metronome_enabled)
548         {
549           sample_processor_queue_metronome_countin (
550             SAMPLE_PROCESSOR);
551         }
552 
553       if (self->recording)
554         {
555           /* handle preroll */
556           bars =
557             g_settings_get_enum (
558               S_TRANSPORT, "recording-preroll");
559           num_bars =
560             transport_preroll_count_bars_enum_to_int (
561               bars);
562           Position pos;
563           position_set_to_pos (
564             &pos, &self->playhead_pos);
565           position_add_bars (&pos, - num_bars);
566           position_print (&pos);
567           if (pos.frames < 0)
568             position_init (&pos);
569           self->preroll_frames_remaining =
570             self->playhead_pos.frames - pos.frames;
571           transport_set_playhead_pos (self, &pos);
572 #if 0
573           g_debug (
574             "preroll %ld frames",
575             self->preroll_frames_remaining);
576 #endif
577         }
578     }
579 
580   self->play_state = PLAYSTATE_ROLL_REQUESTED;
581 }
582 
583 
584 /**
585  * Moves the playhead by the time corresponding to
586  * given samples, taking into account the loop
587  * end point.
588  */
589 void
transport_add_to_playhead(Transport * self,const long frames)590 transport_add_to_playhead (
591   Transport *     self,
592   const long      frames)
593 {
594   transport_position_add_frames (
595     self, &TRANSPORT->playhead_pos, frames);
596   EVENTS_PUSH (ET_PLAYHEAD_POS_CHANGED, NULL);
597 }
598 
599 /**
600  * Setter for playhead Position.
601  */
602 void
transport_set_playhead_pos(Transport * self,Position * pos)603 transport_set_playhead_pos (
604   Transport * self,
605   Position *  pos)
606 {
607   position_set_to_pos (
608     &self->playhead_pos, pos);
609   EVENTS_PUSH (
610     ET_PLAYHEAD_POS_CHANGED_MANUALLY, NULL);
611 }
612 
613 void
transport_set_playhead_to_bar(Transport * self,int bar)614 transport_set_playhead_to_bar (
615   Transport * self,
616   int         bar)
617 {
618   Position pos;
619   position_set_to_bar (&pos, bar);
620   transport_set_playhead_pos (self, &pos);
621 }
622 
623 /**
624  * Getter for playhead Position.
625  */
626 void
transport_get_playhead_pos(Transport * self,Position * pos)627 transport_get_playhead_pos (
628   Transport * self,
629   Position *  pos)
630 {
631   g_return_if_fail (self && pos);
632 
633   position_set_to_pos (
634     pos, &self->playhead_pos);
635 }
636 
637 /**
638  * Moves playhead to given pos.
639  *
640  * This is only for moves other than while playing
641  * and for looping while playing.
642  *
643  * @param target Position to set to.
644  * @param panic Send MIDI panic or not.
645  * @param set_cue_point Also set the cue point at
646  *   this position.
647  */
648 void
transport_move_playhead(Transport * self,Position * target,bool panic,bool set_cue_point,bool fire_events)649 transport_move_playhead (
650   Transport * self,
651   Position *  target,
652   bool        panic,
653   bool        set_cue_point,
654   bool        fire_events)
655 {
656   int i, j, k, l;
657   /* send MIDI note off on currently playing timeline
658    * objects */
659   Track * track;
660   ZRegion * region;
661   MidiNote * midi_note;
662   MidiEvents * midi_events;
663   TrackLane * lane;
664   for (i = 0; i < TRACKLIST->num_tracks; i++)
665     {
666       track = TRACKLIST->tracks[i];
667 
668       for (k = 0; k < track->num_lanes; k++)
669         {
670           lane = track->lanes[k];
671 
672           for (l = 0;
673                l < lane->num_regions; l++)
674             {
675               region = lane->regions[l];
676 
677               if (!region_is_hit (
678                     region, PLAYHEAD->frames, 1))
679                 continue;
680 
681               for (j = 0;
682                    j < region->num_midi_notes;
683                    j++)
684                 {
685                   midi_note = region->midi_notes[j];
686 
687                   if (midi_note_hit (
688                         midi_note, PLAYHEAD->frames))
689                     {
690                       midi_events =
691                         track->processor->
692                           piano_roll->
693                             midi_events;
694 
695                       zix_sem_wait (
696                         &midi_events->access_sem);
697                       midi_events_add_note_off (
698                         midi_events, 1,
699                         midi_note->val,
700                         0, 1);
701                       zix_sem_post (
702                         &midi_events->access_sem);
703                     }
704                 }
705             }
706         }
707     }
708 
709   /* move to new pos */
710   position_set_to_pos (&self->playhead_pos, target);
711 
712   if (set_cue_point)
713     {
714       /* move cue point */
715       position_set_to_pos (&self->cue_pos, target);
716     }
717 
718   if (fire_events)
719     {
720       /* FIXME use another flag to decide when
721        * to do this */
722       self->last_manual_playhead_change =
723         g_get_monotonic_time ();
724 
725       EVENTS_PUSH (
726         ET_PLAYHEAD_POS_CHANGED_MANUALLY, NULL);
727     }
728 }
729 
730 /**
731  * Sets whether metronome is enabled or not.
732  */
733 void
transport_set_metronome_enabled(Transport * self,const int enabled)734 transport_set_metronome_enabled (
735   Transport * self,
736   const int   enabled)
737 {
738   self->metronome_enabled = enabled;
739   g_settings_set_boolean (
740     S_TRANSPORT, "metronome-enabled", enabled);
741 }
742 
743 /**
744  * Returns the PPQN (Parts/Ticks Per Quarter Note).
745  */
746 double
transport_get_ppqn(Transport * self)747 transport_get_ppqn (
748   Transport * self)
749 {
750   int beat_unit =
751     tempo_track_get_beat_unit (P_TEMPO_TRACK);
752   double res =
753     self->ticks_per_beat *
754     ((double) beat_unit / 4.0);
755   return res;
756 }
757 
758 /**
759  * Updates the frames in all transport positions
760  *
761  * @param update_from_ticks Whether to update the
762  *   positions based on ticks (true) or frames
763  *   (false).
764  */
765 void
transport_update_positions(Transport * self,bool update_from_ticks)766 transport_update_positions (
767   Transport * self,
768   bool        update_from_ticks)
769 {
770   position_update (
771     &self->playhead_pos, update_from_ticks);
772   position_update (
773     &self->cue_pos, update_from_ticks);
774   position_update (
775     &self->loop_start_pos, update_from_ticks);
776   position_update (
777     &self->loop_end_pos, update_from_ticks);
778   position_update (
779     &self->punch_in_pos, update_from_ticks);
780   position_update (
781     &self->punch_out_pos, update_from_ticks);
782 }
783 
784 #define GATHER_MARKERS \
785   /* gather all markers */ \
786   Position markers[60]; \
787   int num_markers = 0, i; \
788   for (i = 0; \
789        i < P_MARKER_TRACK->num_markers; i++) \
790     { \
791       ArrangerObject * m_obj = \
792         (ArrangerObject *) \
793         P_MARKER_TRACK->markers[i]; \
794        position_set_to_pos ( \
795           &markers[num_markers++], \
796           &m_obj->pos); \
797     } \
798   position_set_to_pos ( \
799     &markers[num_markers++], \
800     &self->cue_pos); \
801   position_set_to_pos ( \
802     &markers[num_markers++], \
803     &self->loop_start_pos); \
804   position_set_to_pos ( \
805     &markers[num_markers++], \
806     &self->loop_end_pos); \
807   position_set_to_pos ( \
808     &markers[num_markers++], \
809     &POSITION_START); \
810   position_sort_array ( \
811     markers, (size_t) num_markers)
812 
813 /**
814  * Moves the playhead to the prev Marker.
815  */
816 void
transport_goto_prev_marker(Transport * self)817 transport_goto_prev_marker (
818   Transport * self)
819 {
820   GATHER_MARKERS;
821 
822   for (i = num_markers - 1; i >= 0; i--)
823     {
824       if (position_is_before (
825             &markers[i], &self->playhead_pos) &&
826           TRANSPORT_IS_ROLLING &&
827           i > 0 &&
828           (position_to_ms (&self->playhead_pos) -
829              position_to_ms (&markers[i])) <
830            180)
831         {
832           transport_move_playhead (
833             self, &markers[i - 1], F_PANIC,
834             F_SET_CUE_POINT,
835             F_PUBLISH_EVENTS);
836           break;
837         }
838       else if (
839         position_is_before (
840           &markers[i], &self->playhead_pos))
841         {
842           transport_move_playhead (
843             self, &markers[i],
844             F_PANIC, F_SET_CUE_POINT,
845             F_PUBLISH_EVENTS);
846           break;
847         }
848     }
849 }
850 
851 /**
852  * Moves the playhead to the next Marker.
853  */
854 void
transport_goto_next_marker(Transport * self)855 transport_goto_next_marker (
856   Transport * self)
857 {
858   GATHER_MARKERS;
859 
860   for (i = 0; i < num_markers; i++)
861     {
862       if (position_is_after (
863             &markers[i], &self->playhead_pos))
864         {
865           transport_move_playhead (
866             self, &markers[i], F_PANIC,
867             F_SET_CUE_POINT, F_PUBLISH_EVENTS);
868           break;
869         }
870     }
871 }
872 
873 /**
874  * Enables or disables loop.
875  */
876 void
transport_set_loop(Transport * self,bool enabled)877 transport_set_loop (
878   Transport * self,
879   bool        enabled)
880 {
881   self->loop = enabled;
882 
883   if (!ZRYTHM_TESTING)
884     {
885       g_settings_set_boolean (
886         S_TRANSPORT, "loop", enabled);
887     }
888 
889   EVENTS_PUSH (ET_LOOP_TOGGLED, NULL);
890 }
891 
892 #if 0
893 /**
894  * Adds frames to the given global frames, while
895  * adjusting the new frames to loop back if the
896  * loop point was crossed.
897  *
898  * @return The new frames adjusted.
899  */
900 long
901 transport_frames_add_frames (
902   const Transport * self,
903   const long        gframes,
904   const nframes_t   frames)
905 {
906   long new_frames = gframes + (long) frames;
907 
908   /* if start frames were before the loop-end point
909    * and the new frames are after (loop crossed) */
910   if (TRANSPORT_IS_LOOPING &&
911       gframes < self->loop_end_pos.frames &&
912       new_frames >= self->loop_end_pos.frames)
913     {
914       /* adjust the new frames */
915       new_frames +=
916         self->loop_start_pos.frames -
917         self->loop_end_pos.frames;
918     }
919 
920   return new_frames;
921 }
922 #endif
923 
924 /**
925  * Adds frames to the given position similar to
926  * position_add_frames(), except that it adjusts
927  * the new Position to loop back if the loop end
928  * point was crossed.
929  */
930 void
transport_position_add_frames(const Transport * self,Position * pos,const long frames)931 transport_position_add_frames (
932   const Transport * self,
933   Position *        pos,
934   const long        frames)
935 {
936   Position pos_before_adding = *pos;
937   position_add_frames (pos, frames);
938 
939   /* if start frames were before the loop-end point
940    * and the new frames are after (loop crossed) */
941   if (TRANSPORT_IS_LOOPING &&
942       pos_before_adding.frames <
943         self->loop_end_pos.frames &&
944       pos->frames >=
945         self->loop_end_pos.frames)
946     {
947       /* adjust the new frames */
948       position_add_ticks (
949         pos,
950         self->loop_start_pos.ticks -
951           self->loop_end_pos.ticks);
952 
953       g_warn_if_fail (
954         pos->frames < self->loop_end_pos.frames);
955     }
956 
957   /*long new_global_frames =*/
958     /*transport_frames_add_frames (*/
959       /*self, pos->frames, frames);*/
960   /*position_from_frames (*/
961     /*pos, new_global_frames);*/
962 
963   /* set the frames manually again because
964    * position_from_frames rounds them */
965   /*pos->frames = new_global_frames;*/
966 }
967 
968 /**
969  * Sets if the project has range and updates UI.
970  */
971 void
transport_set_has_range(Transport * self,bool has_range)972 transport_set_has_range (
973   Transport * self,
974   bool        has_range)
975 {
976   self->has_range = has_range;
977 
978   EVENTS_PUSH (ET_RANGE_SELECTION_CHANGED, NULL);
979 }
980 
981 /**
982  * Stores the position of the range in \ref pos.
983  */
984 void
transport_get_range_pos(Transport * self,bool first,Position * pos)985 transport_get_range_pos (
986   Transport * self,
987   bool        first,
988   Position *  pos)
989 {
990   bool range1_first =
991     position_is_before_or_equal (
992       &self->range_1, &self->range_2);
993 
994   if (first)
995     {
996       if (range1_first)
997         {
998           position_set_to_pos (pos, &self->range_1);
999         }
1000       else
1001         {
1002           position_set_to_pos (pos, &self->range_2);
1003         }
1004     }
1005   else
1006     {
1007       if (range1_first)
1008         {
1009           position_set_to_pos (pos, &self->range_2);
1010         }
1011       else
1012         {
1013           position_set_to_pos (pos, &self->range_1);
1014         }
1015     }
1016 }
1017 
1018 /**
1019  * Set the range1 or range2 position.
1020  *
1021  * @param range1 True to set range1, false to set
1022  *   range2.
1023  */
1024 void
transport_set_range(Transport * self,bool range1,const Position * start_pos,const Position * pos,bool snap)1025 transport_set_range (
1026   Transport *      self,
1027   bool             range1,
1028   const Position * start_pos,
1029   const Position * pos,
1030   bool             snap)
1031 {
1032   Position * pos_to_set =
1033     range1 ? &self->range_1 : &self->range_2;
1034 
1035   Position init_pos;
1036   position_init (&init_pos);
1037   if (position_is_before (pos, &init_pos))
1038     {
1039       position_set_to_pos (
1040         pos_to_set, &init_pos);
1041     }
1042   else
1043     {
1044       position_set_to_pos (
1045         pos_to_set, pos);
1046     }
1047 
1048   if (snap)
1049     {
1050       position_snap (
1051         start_pos, pos_to_set, NULL, NULL,
1052         SNAP_GRID_TIMELINE);
1053     }
1054 }
1055 
1056 bool
transport_position_is_inside_punch_range(Transport * self,Position * pos)1057 transport_position_is_inside_punch_range (
1058   Transport * self,
1059   Position *  pos)
1060 {
1061   return
1062     pos->frames >= self->punch_in_pos.frames &&
1063     pos->frames < self->punch_out_pos.frames;
1064 }
1065 
1066 /**
1067  * Recalculates the total bars based on the last
1068  * object's position.
1069  *
1070  * @param sel If given, only these objects will
1071  *   be checked, otherwise every object in the
1072  *   project will be checked.
1073  */
1074 void
transport_recalculate_total_bars(Transport * self,ArrangerSelections * sel)1075 transport_recalculate_total_bars (
1076   Transport *          self,
1077   ArrangerSelections * sel)
1078 {
1079   if (!ZRYTHM_HAVE_UI)
1080     return;
1081 
1082   int total_bars = self->total_bars;
1083   if (sel)
1084     {
1085       int num_objs;
1086       ArrangerObject ** objs =
1087         arranger_selections_get_all_objects (
1088           sel, &num_objs);
1089       for (int i = 0; i < num_objs; i++)
1090         {
1091           ArrangerObject * obj = objs[i];
1092           Position pos;
1093           if (arranger_object_type_has_length (
1094                 obj->type))
1095             {
1096               arranger_object_get_end_pos (
1097                 obj, &pos);
1098             }
1099           else
1100             {
1101               arranger_object_get_pos (obj, &pos);
1102             }
1103           int pos_bars =
1104             position_get_total_bars (&pos, true);
1105           if (pos_bars > total_bars - 3)
1106             {
1107               total_bars =
1108                 pos_bars + BARS_END_BUFFER;
1109             }
1110         }
1111     }
1112   /* else no selections, calculate total bars for
1113    * every object */
1114   else
1115     {
1116       total_bars = TRANSPORT_DEFAULT_TOTAL_BARS;
1117 
1118       tracklist_get_total_bars (
1119         TRACKLIST, &total_bars);
1120 
1121       total_bars += BARS_END_BUFFER;
1122     }
1123 
1124   transport_update_total_bars (
1125     self, total_bars, F_PUBLISH_EVENTS);
1126 }
1127 
1128 /**
1129  * Updates the total bars.
1130  */
1131 void
transport_update_total_bars(Transport * self,int total_bars,bool fire_events)1132 transport_update_total_bars (
1133   Transport * self,
1134   int         total_bars,
1135   bool        fire_events)
1136 {
1137   g_return_if_fail (
1138     self &&
1139     total_bars >= TRANSPORT_DEFAULT_TOTAL_BARS);
1140 
1141   if (self->total_bars == total_bars)
1142     return;
1143 
1144   self->total_bars = total_bars;
1145 
1146   if (fire_events)
1147     {
1148       EVENTS_PUSH (
1149         ET_TRANSPORT_TOTAL_BARS_CHANGED, NULL);
1150     }
1151 }
1152 
1153 /**
1154  * Move to the previous snap point on the timeline.
1155  */
1156 void
transport_move_backward(Transport * self)1157 transport_move_backward (
1158   Transport * self)
1159 {
1160   Position * pos =
1161     snap_grid_get_nearby_snap_point (
1162       SNAP_GRID_TIMELINE,
1163       &self->playhead_pos, true);
1164   transport_move_playhead (
1165     self, pos, F_PANIC, F_SET_CUE_POINT,
1166     F_PUBLISH_EVENTS);
1167 }
1168 
1169 /**
1170  * Move to the next snap point on the timeline.
1171  */
1172 void
transport_move_forward(Transport * self)1173 transport_move_forward (
1174   Transport * self)
1175 {
1176   Position * pos =
1177     snap_grid_get_nearby_snap_point (
1178       SNAP_GRID_TIMELINE,
1179       &self->playhead_pos, false);
1180   transport_move_playhead (
1181     self, pos, F_PANIC, F_SET_CUE_POINT,
1182     F_PUBLISH_EVENTS);
1183 }
1184 
1185 /**
1186  * Sets recording on/off.
1187  */
1188 void
transport_set_recording(Transport * self,bool record,bool fire_events)1189 transport_set_recording (
1190   Transport * self,
1191   bool        record,
1192   bool        fire_events)
1193 {
1194   self->recording = record;
1195 
1196   if (fire_events)
1197     {
1198       EVENTS_PUSH (
1199         ET_TRANSPORT_RECORDING_ON_OFF_CHANGED, NULL);
1200     }
1201 }
1202 
1203 void
transport_free(Transport * self)1204 transport_free (
1205   Transport * self)
1206 {
1207   zix_sem_destroy (&self->paused);
1208 
1209   object_zero_and_free (self);
1210 }
1211