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