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 <math.h>
21
22 #include "audio/engine.h"
23 #include "audio/position.h"
24 #include "audio/snap_grid.h"
25 #include "audio/tempo_track.h"
26 #include "audio/transport.h"
27 #include "gui/widgets/arranger.h"
28 #include "gui/widgets/bot_dock_edge.h"
29 #include "gui/widgets/center_dock.h"
30 #include "gui/widgets/main_window.h"
31 #include "gui/widgets/midi_arranger.h"
32 #include "gui/widgets/editor_ruler.h"
33 #include "gui/widgets/ruler.h"
34 #include "gui/widgets/timeline_arranger.h"
35 #include "gui/widgets/timeline_ruler.h"
36 #include "gui/widgets/top_bar.h"
37 #include "project.h"
38 #include "utils/algorithms.h"
39 #include "utils/math.h"
40 #include "utils/objects.h"
41
42 #include <gtk/gtk.h>
43
44 PURE
45 static int
position_cmpfunc(const void * a,const void * b)46 position_cmpfunc (
47 const void * a,
48 const void * b)
49 {
50 const Position * posa =
51 (Position const *) a;
52 const Position * posb =
53 (Position const *) b;
54 return
55 (int)
56 CLAMP (
57 position_compare (posa, posb), -1, 1);
58 }
59
60 void
position_sort_array(Position * array,const size_t size)61 position_sort_array (
62 Position * array,
63 const size_t size)
64 {
65 qsort (
66 array, size, sizeof (Position),
67 position_cmpfunc);
68 }
69
70 /**
71 * Updates ticks.
72 */
73 void
position_update_ticks_from_frames(Position * self)74 position_update_ticks_from_frames (
75 Position * self)
76 {
77 g_return_if_fail (
78 AUDIO_ENGINE->ticks_per_frame > 0);
79 self->ticks =
80 (double)
81 self->frames * AUDIO_ENGINE->ticks_per_frame;
82 }
83
84 /**
85 * Converts ticks to frames.
86 */
87 long
position_get_frames_from_ticks(double ticks)88 position_get_frames_from_ticks (
89 double ticks)
90 {
91 g_return_val_if_fail (
92 AUDIO_ENGINE->frames_per_tick > 0, -1);
93 return
94 math_round_double_to_long (
95 (ticks * AUDIO_ENGINE->frames_per_tick));
96 }
97
98 /**
99 * Updates frames.
100 */
101 void
position_update_frames_from_ticks(Position * self)102 position_update_frames_from_ticks (
103 Position * self)
104 {
105 self->frames =
106 position_get_frames_from_ticks (self->ticks);
107 }
108
109 /**
110 * Sets position to given bar.
111 */
112 void
position_set_to_bar(Position * self,int bar)113 position_set_to_bar (
114 Position * self,
115 int bar)
116 {
117 g_return_if_fail (
118 TRANSPORT->ticks_per_bar > 0
119 /* don't use INT_MAX, it results in a negative
120 * position */
121 && bar <= POSITION_MAX_BAR);
122
123 position_init (self);
124
125 if (bar > 0)
126 {
127 bar--;
128 self->ticks = TRANSPORT->ticks_per_bar * bar;
129 position_from_ticks (self, self->ticks);
130 }
131 else if (bar < 0)
132 {
133 bar++;
134 self->ticks = TRANSPORT->ticks_per_bar * bar;
135 position_from_ticks (self, self->ticks);
136 }
137 else
138 {
139 g_return_if_reached ();
140 }
141 }
142
143 /**
144 * Adds the frames to the position and updates
145 * the rest of the fields, and makes sure the
146 * frames are still accurate.
147 */
148 void
position_add_frames(Position * pos,const long frames)149 position_add_frames (
150 Position * pos,
151 const long frames)
152 {
153 pos->frames += frames;
154 position_update_ticks_from_frames (pos);
155 }
156
157 /**
158 * Returns the Position in milliseconds.
159 */
160 long
position_to_ms(const Position * pos)161 position_to_ms (
162 const Position * pos)
163 {
164 if (position_to_frames (pos) == 0)
165 {
166 return 0;
167 }
168 return
169 math_round_double_to_long (
170 (1000.0 * (double) position_to_frames (pos)) /
171 ((double) AUDIO_ENGINE->sample_rate));
172 }
173
174 long
position_ms_to_frames(const long ms)175 position_ms_to_frames (
176 const long ms)
177 {
178 return
179 math_round_double_to_long (
180 ((double) ms / 1000) *
181 (double) AUDIO_ENGINE->sample_rate);
182 }
183
184 void
position_add_ms(Position * pos,long ms)185 position_add_ms (
186 Position * pos,
187 long ms)
188 {
189 long frames = position_ms_to_frames (ms);
190 position_add_frames (pos, frames);
191 }
192
193 void
position_add_minutes(Position * pos,int mins)194 position_add_minutes (
195 Position * pos,
196 int mins)
197 {
198 long frames =
199 position_ms_to_frames (mins * 60 * 1000);
200 position_add_frames (pos, frames);
201 }
202
203 void
position_add_seconds(Position * pos,long seconds)204 position_add_seconds (
205 Position * pos,
206 long seconds)
207 {
208 long frames =
209 position_ms_to_frames (seconds * 1000);
210 position_add_frames (pos, frames);
211 }
212
213 /**
214 * Sets the end position to be 1 snap point away
215 * from the start pos.
216 *
217 * FIXME rename to something more meaningful.
218 * @param start_pos Start Position.
219 * @param end_pos Position to set.
220 * @param snap The SnapGrid.
221 */
222 void
position_set_min_size(const Position * start_pos,Position * end_pos,SnapGrid * snap)223 position_set_min_size (
224 const Position * start_pos,
225 Position * end_pos,
226 SnapGrid * snap)
227 {
228 position_set_to_pos (end_pos, start_pos);
229 position_add_ticks (
230 end_pos,
231 snap_grid_get_default_ticks (snap));
232 }
233
234 /**
235 * Returns closest snap point.
236 *
237 * @param pos Position.
238 * @param p1 Snap point 1.
239 * @param p2 Snap point 2.
240 */
241 PURE
242 static inline Position *
closest_snap_point(const Position * const pos,Position * const p1,Position * const p2)243 closest_snap_point (
244 const Position * const pos,
245 Position * const p1,
246 Position * const p2)
247 {
248 if (pos->ticks - p1->ticks <=
249 p2->ticks - pos->ticks)
250 {
251 return p1;
252 }
253 else
254 {
255 return p2;
256 }
257 }
258
259 /**
260 * Gets the previous snap point.
261 *
262 * @param pos The position to reference. Must be
263 * positive.
264 * @param track Track, used when moving things in
265 * the timeline. If keep offset is on and this is
266 * passed, the objects in the track will be taken
267 * into account. If keep offset is on and this is
268 * NULL, all applicable objects will be taken into
269 * account. Not used if keep offset is off.
270 * @param region Region, used when moving
271 * things in the editor. Same behavior as @ref
272 * track.
273 * @param sg SnapGrid options.
274 * @param prev_snap_point The position to set.
275 *
276 * @return Whether a snap point was found or not.
277 */
278 HOT
279 static bool
get_prev_snap_point(const Position * pos,Track * track,ZRegion * region,const SnapGrid * sg,Position * prev_sp)280 get_prev_snap_point (
281 const Position * pos,
282 Track * track,
283 ZRegion * region,
284 const SnapGrid * sg,
285 Position * prev_sp)
286 {
287 if (pos->frames < 0 || pos->ticks < 0)
288 {
289 /* negative not supported, set to same
290 * position */
291 position_set_to_pos (prev_sp, pos);
292 return false;
293 }
294
295 bool snapped = false;
296 Position * snap_point = NULL;
297 if (sg->snap_to_grid)
298 {
299 snap_point =
300 (Position *)
301 algorithms_binary_search_nearby (
302 pos, sg->snap_points,
303 (size_t) sg->num_snap_points,
304 sizeof (Position), position_cmp_func,
305 true, true);
306 }
307 if (snap_point)
308 {
309 position_set_to_pos (prev_sp, snap_point);
310 snapped = true;
311 }
312
313 if (track)
314 {
315 for (int i = 0; i < track->num_lanes; i++)
316 {
317 TrackLane * lane = track->lanes[i];
318 for (int j = 0; j < lane->num_regions;
319 j++)
320 {
321 ZRegion * r = lane->regions[j];
322 ArrangerObject * r_obj =
323 (ArrangerObject *) r;
324 snap_point = &r_obj->pos;
325 if (position_is_before_or_equal (
326 snap_point, pos) &&
327 position_is_after (
328 snap_point, prev_sp))
329 {
330 position_set_to_pos (
331 prev_sp, snap_point);
332 snapped = true;
333 }
334 snap_point = &r_obj->end_pos;
335 if (position_is_before_or_equal (
336 snap_point, pos) &&
337 position_is_after (
338 snap_point, prev_sp))
339 {
340 position_set_to_pos (
341 prev_sp, snap_point);
342 snapped = true;
343 }
344 }
345 }
346 }
347 else if (region)
348 {
349 /* TODO */
350 }
351
352 /* if no point to snap to, set to same position */
353 if (!snapped)
354 {
355 position_set_to_pos (prev_sp, pos);
356 }
357
358 return snapped;
359 }
360
361 /**
362 * Get next snap point.
363 *
364 * @param pos The position to reference. Must be
365 * positive.
366 * @param track Track, used when moving things in
367 * the timeline. If keep offset is on and this is
368 * passed, the objects in the track will be taken
369 * into account. If keep offset is on and this is
370 * NULL, all applicable objects will be taken into
371 * account. Not used if keep offset is off.
372 * @param region Region, used when moving
373 * things in the editor. Same behavior as @ref
374 * track.
375 * @param sg SnapGrid options.
376 * @param next_snap_point Position to set.
377 *
378 * @return Whether a snap point was found or not.
379 */
380 HOT
381 static bool
get_next_snap_point(const Position * pos,Track * track,ZRegion * region,const SnapGrid * sg,Position * next_sp)382 get_next_snap_point (
383 const Position * pos,
384 Track * track,
385 ZRegion * region,
386 const SnapGrid * sg,
387 Position * next_sp)
388 {
389 if (pos->frames < 0 || pos->ticks < 0)
390 {
391 /* negative not supported, set to same
392 * position */
393 position_set_to_pos (next_sp, pos);
394 return false;
395 }
396
397 bool snapped = false;
398 Position * snap_point = NULL;
399 if (sg->snap_to_grid)
400 {
401 snap_point =
402 algorithms_binary_search_nearby (
403 pos, sg->snap_points,
404 (size_t) sg->num_snap_points,
405 sizeof (Position), position_cmp_func,
406 false, false);
407 }
408 if (snap_point)
409 {
410 position_set_to_pos (next_sp, snap_point);
411 snapped = true;
412 }
413
414 if (track)
415 {
416 for (int i = 0; i < track->num_lanes; i++)
417 {
418 TrackLane * lane = track->lanes[i];
419 for (int j = 0; j < lane->num_regions;
420 j++)
421 {
422 ZRegion * r = lane->regions[j];
423 ArrangerObject * r_obj =
424 (ArrangerObject *) r;
425 snap_point = &r_obj->pos;
426 if (position_is_after (
427 snap_point, pos) &&
428 position_is_before (
429 snap_point, next_sp))
430 {
431 position_set_to_pos (
432 next_sp, snap_point);
433 snapped = true;
434 }
435 snap_point = &r_obj->end_pos;
436 if (position_is_after (
437 snap_point, pos) &&
438 position_is_before (
439 snap_point, next_sp))
440 {
441 position_set_to_pos (
442 next_sp, snap_point);
443 snapped = true;
444 }
445 }
446 }
447 }
448 else if (region)
449 {
450 /* TODO */
451 }
452
453 /* if no point to snap to, set to same position */
454 if (!snapped)
455 {
456 position_set_to_pos (next_sp, pos);
457 }
458
459 return snapped;
460 }
461
462 /**
463 * Get closest snap point.
464 *
465 * @param pos Position to reference.
466 * @param track Track, used when moving things in
467 * the timeline. If keep offset is on and this is
468 * passed, the objects in the track will be taken
469 * into account. If keep offset is on and this is
470 * NULL, all applicable objects will be taken into
471 * account. Not used if keep offset is off.
472 * @param region Region, used when moving
473 * things in the editor. Same behavior as @ref
474 * track.
475 * @param sg SnapGrid options.
476 * @param closest_sp Position to set.
477 *
478 * @return Whether a snap point was found or not.
479 */
480 static inline bool
get_closest_snap_point(const Position * pos,Track * track,ZRegion * region,const SnapGrid * sg,Position * closest_sp)481 get_closest_snap_point (
482 const Position * pos,
483 Track * track,
484 ZRegion * region,
485 const SnapGrid * sg,
486 Position * closest_sp)
487 {
488 /* get closest snap point */
489 Position prev_sp, next_sp;
490 bool prev_snapped =
491 get_prev_snap_point (
492 pos, track, region, sg, &prev_sp);
493 bool next_snapped =
494 get_next_snap_point (
495 pos, track, region, sg, &next_sp);
496 if (prev_snapped && next_snapped)
497 {
498 Position * closest_sp_ptr =
499 closest_snap_point (
500 pos, &prev_sp, &next_sp);
501 *closest_sp = *closest_sp_ptr;
502 return true;
503 }
504 else if (prev_snapped)
505 {
506 *closest_sp = prev_sp;
507 return true;
508 }
509 else if (next_snapped)
510 {
511 *closest_sp = next_sp;
512 return true;
513 }
514 else
515 {
516 *closest_sp = *pos;
517 return false;
518 }
519 }
520
521 /**
522 * Snaps position using given options.
523 *
524 * @param start_pos The previous position (ie, the
525 * position the drag started at. This is only used
526 * when the "keep offset" setting is on.
527 * @param pos Position to edit.
528 * @param track Track, used when moving things in
529 * the timeline. If snap to events is on and this is
530 * passed, the objects in the track will be taken
531 * into account. If snap to events is on and this is
532 * NULL, all applicable objects will be taken into
533 * account. Not used if snap to events is off.
534 * @param region Region, used when moving
535 * things in the editor. Same behavior as @ref
536 * track.
537 * @param sg SnapGrid options.
538 */
539 void
position_snap(const Position * start_pos,Position * pos,Track * track,ZRegion * region,const SnapGrid * sg)540 position_snap (
541 const Position * start_pos,
542 Position * pos,
543 Track * track,
544 ZRegion * region,
545 const SnapGrid * sg)
546 {
547 g_return_if_fail (sg);
548
549 /* this should only be called if snap is on.
550 * the check should be done before calling */
551 g_warn_if_fail (SNAP_GRID_ANY_SNAP (sg));
552
553 /* position must be positive - only global
554 * positions allowed */
555 g_return_if_fail (
556 pos->frames >= 0 && pos->ticks >= 0);
557
558 if (!sg->snap_to_events)
559 {
560 region = NULL;
561 track = NULL;
562 }
563
564 /* snap to grid with offset */
565 if (sg->snap_to_grid_keep_offset)
566 {
567 /* get previous snap point from start pos */
568 g_return_if_fail (start_pos);
569 Position prev_sp_from_start_pos;
570 position_init (&prev_sp_from_start_pos);
571 get_prev_snap_point (
572 start_pos, track, region, sg,
573 &prev_sp_from_start_pos);
574
575 /* get diff from previous snap point */
576 double ticks_delta =
577 start_pos->ticks -
578 prev_sp_from_start_pos.ticks;
579
580 /* add ticks to current pos and check the
581 * closest snap point to the resulting
582 * pos */
583 position_add_ticks (pos, - ticks_delta);
584
585 /* get closest snap point */
586 Position closest_sp;
587 bool have_closest_sp =
588 get_closest_snap_point (
589 pos, track, region, sg, &closest_sp);
590 if (have_closest_sp)
591 {
592 /* move to closest snap point */
593 position_set_to_pos (pos, &closest_sp);
594 }
595
596 /* readd ticks */
597 position_add_ticks (pos, ticks_delta);
598 }
599 /* else if snap to grid without offset */
600 else
601 {
602 /* get closest snap point */
603 Position closest_sp;
604 get_closest_snap_point (
605 pos, track, region, sg, &closest_sp);
606
607 /* move to closest snap point */
608 position_set_to_pos (pos, &closest_sp);
609 }
610 }
611
612 /**
613 * Converts seconds to position and puts the result
614 * in the given Position.
615 */
616 void
position_from_seconds(Position * position,double secs)617 position_from_seconds (
618 Position * position,
619 double secs)
620 {
621 position_from_ticks (
622 position,
623 (secs * (double) AUDIO_ENGINE->sample_rate) /
624 (double) AUDIO_ENGINE->frames_per_tick);
625 }
626
627 /**
628 * Sets position to the given total tick count.
629 */
630 void
position_from_ticks(Position * pos,double ticks)631 position_from_ticks (
632 Position * pos,
633 double ticks)
634 {
635 pos->schema_version = POSITION_SCHEMA_VERSION;
636 pos->ticks = ticks;
637 position_update_frames_from_ticks (pos);
638 }
639
640 void
position_from_frames(Position * pos,long frames)641 position_from_frames (
642 Position * pos,
643 long frames)
644 {
645 pos->schema_version = POSITION_SCHEMA_VERSION;
646 pos->frames = frames;
647 position_update_ticks_from_frames (pos);
648 }
649
650 void
position_from_bars(Position * pos,int bars)651 position_from_bars (
652 Position * pos,
653 int bars)
654 {
655 position_init (pos);
656 position_add_bars (pos, bars);
657 }
658
659 void
position_add_ticks(Position * self,double ticks)660 position_add_ticks (
661 Position * self,
662 double ticks)
663 {
664 position_from_ticks (self, self->ticks + ticks);
665 }
666
667 /**
668 * Calculates the midway point between the two
669 * positions and sets it on pos.
670 *
671 * @param pos Position to set to.
672 */
673 inline void
position_get_midway_pos(Position * start_pos,Position * end_pos,Position * pos)674 position_get_midway_pos (
675 Position * start_pos,
676 Position * end_pos,
677 Position * pos)
678 {
679 double end_ticks, start_ticks, ticks_diff;
680 start_ticks = position_to_ticks (start_pos);
681 end_ticks = position_to_ticks (end_pos);
682 ticks_diff = end_ticks - start_ticks;
683 position_set_to_pos (pos, start_pos);
684 position_add_ticks (pos, ticks_diff / 2.0);
685 }
686
687 /**
688 * Returns the difference in ticks between the two
689 * Position's, snapped based on the given SnapGrid
690 * (if any).
691 *
692 * TODO, refactor, too confusing on what it's
693 * supposed to do.
694 *
695 * @param end_pos End position.
696 * @param start_pos Start Position.
697 * @param sg SnapGrid to snap with, or NULL to not
698 * snap.
699 */
700 double
position_get_ticks_diff(const Position * end_pos,const Position * start_pos,const SnapGrid * sg)701 position_get_ticks_diff (
702 const Position * end_pos,
703 const Position * start_pos,
704 const SnapGrid * sg)
705 {
706 double ticks_diff =
707 end_pos->ticks -
708 start_pos->ticks;
709 int is_negative = ticks_diff < 0.0;
710 POSITION_INIT_ON_STACK (diff_pos);
711 position_add_ticks (
712 &diff_pos, fabs (ticks_diff));
713 if (sg && SNAP_GRID_ANY_SNAP(sg))
714 {
715 position_snap (
716 NULL, &diff_pos, NULL, NULL, sg);
717 }
718 ticks_diff = diff_pos.ticks;
719 if (is_negative)
720 ticks_diff = - ticks_diff;
721
722 return ticks_diff;
723 }
724
725 /**
726 * Creates a string in the form of "0.0.0.0" from
727 * the given position.
728 *
729 * Must be free'd by caller.
730 */
731 char *
position_to_string_alloc(const Position * pos)732 position_to_string_alloc (
733 const Position * pos)
734 {
735 char buf[80];
736 position_to_string (pos, buf);
737 return g_strdup (buf);
738 }
739
740 void
position_to_string_full(const Position * pos,char * buf,int decimal_places)741 position_to_string_full (
742 const Position * pos,
743 char * buf,
744 int decimal_places)
745 {
746 int bars = position_get_bars (pos, true);
747 int beats = position_get_beats (pos, true);
748 int sixteenths =
749 position_get_sixteenths (pos, true);
750 double ticks = position_get_ticks (pos);
751 g_return_if_fail (bars > -80000);
752 char template[32];
753 sprintf (
754 template, "%%d.%%d.%%d.%%.%df", decimal_places);
755 sprintf (
756 buf, template,
757 bars, abs (beats), abs (sixteenths),
758 fabs (ticks));
759 }
760
761 /**
762 * Creates a string in the form of "0.0.0.0" from
763 * the given position.
764 */
765 NONNULL
766 void
position_to_string(const Position * pos,char * buf)767 position_to_string (
768 const Position * pos,
769 char * buf)
770 {
771 position_to_string_full (pos, buf, 4);
772 }
773
774 /**
775 * Prints the Position in the "0.0.0.0" form.
776 */
777 void
position_print(const Position * pos)778 position_print (
779 const Position * pos)
780 {
781 char buf[140];
782 position_to_string (pos, buf);
783 g_message ("%s (%ld)", buf, pos->frames);
784 }
785
786 void
position_print_range(const Position * pos,const Position * pos2)787 position_print_range (
788 const Position * pos,
789 const Position * pos2)
790 {
791 char buf[140];
792 position_to_string (pos, buf);
793 char buf2[140];
794 position_to_string (pos2, buf2);
795 g_message (
796 "%s (%ld) - %s (%ld) "
797 "<delta %ld frames %f ticks>",
798 buf, pos->frames, buf2, pos2->frames,
799 pos2->frames - pos->frames,
800 pos2->ticks - pos->ticks);
801 }
802
803 /**
804 * Returns the total number of beats.
805 *
806 * @param include_current Whether to count the
807 * current beat if it is at the beat start.
808 */
809 int
position_get_total_bars(const Position * pos,bool include_current)810 position_get_total_bars (
811 const Position * pos,
812 bool include_current)
813 {
814 int bars = position_get_bars (pos, false);
815 int cur_bars = position_get_bars (pos, true);
816
817 if (include_current || bars == 0)
818 {
819 return bars;
820 }
821
822 /* if we are at the start of the bar, don't
823 * count this bar */
824 Position pos_at_bar;
825 position_set_to_bar (&pos_at_bar, cur_bars);
826 if (pos_at_bar.frames == pos->frames)
827 {
828 bars--;
829 }
830
831 return bars;
832 }
833
834 /**
835 * Returns the total number of beats.
836 *
837 * @param include_current Whether to count the
838 * current beat if it is at the beat start.
839 */
840 int
position_get_total_beats(const Position * pos,bool include_current)841 position_get_total_beats (
842 const Position * pos,
843 bool include_current)
844 {
845 int beats = position_get_beats (pos, false);
846 int bars = position_get_bars (pos, false);
847
848 int beats_per_bar =
849 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
850 int ret = beats + bars * beats_per_bar;
851
852 if (include_current || ret == 0)
853 {
854 return ret;
855 }
856
857 Position tmp;
858 position_from_ticks (
859 &tmp, (double) ret * TRANSPORT->ticks_per_beat);
860 if (tmp.frames == pos->frames)
861 {
862 ret--;
863 }
864
865 return ret;
866 }
867
868 /**
869 * Returns the total number of sixteenths not
870 * including the current one.
871 */
872 int
position_get_total_sixteenths(const Position * pos,bool include_current)873 position_get_total_sixteenths (
874 const Position * pos,
875 bool include_current)
876 {
877 int ret;
878 if (pos->ticks >= 0)
879 {
880 ret =
881 math_round_double_to_int (
882 floor (
883 pos->ticks /
884 TICKS_PER_SIXTEENTH_NOTE_DBL));
885 }
886 else
887 {
888 ret =
889 math_round_double_to_int (
890 ceil (
891 pos->ticks /
892 TICKS_PER_SIXTEENTH_NOTE_DBL));
893 }
894
895 if (include_current || ret == 0)
896 {
897 return ret;
898 }
899
900 Position tmp;
901 position_from_ticks (
902 &tmp,
903 (double) ret * TICKS_PER_SIXTEENTH_NOTE_DBL);
904 if (tmp.frames == pos->frames)
905 {
906 ret--;
907 }
908
909 return ret;
910 }
911
912 /**
913 * Changes the sign of the position.
914 *
915 * For example, 4.2.1.21 would become -4.2.1.21.
916 */
917 void
position_change_sign(Position * pos)918 position_change_sign (
919 Position * pos)
920 {
921 pos->ticks = - pos->ticks;
922 pos->frames = - pos->frames;
923 }
924
925 /**
926 * Gets the bars of the position.
927 *
928 * Ie, if the position is equivalent to 4.1.2.42,
929 * this will return 4.
930 *
931 * @param start_at_one Start at 1 or -1 instead of
932 * 0.
933 */
934 int
position_get_bars(const Position * pos,bool start_at_one)935 position_get_bars (
936 const Position * pos,
937 bool start_at_one)
938 {
939 g_return_val_if_fail (
940 ZRYTHM && PROJECT && TRANSPORT &&
941 TRANSPORT->ticks_per_bar > 0, -1);
942
943 double total_bars =
944 pos->ticks / TRANSPORT->ticks_per_bar;
945 if (total_bars >= 0.0)
946 {
947 int ret = (int) floor (total_bars);
948 if (start_at_one)
949 {
950 ret++;
951 }
952 return ret;
953 }
954 else
955 {
956 int ret = (int) ceil (total_bars);
957 if (start_at_one)
958 {
959 ret--;
960 }
961 return ret;
962 }
963 }
964
965 /**
966 * Gets the beats of the position.
967 *
968 * Ie, if the position is equivalent to 4.1.2.42,
969 * this will return 1.
970 *
971 * @param start_at_one Start at 1 or -1 instead of
972 * 0.
973 */
974 int
position_get_beats(const Position * pos,bool start_at_one)975 position_get_beats (
976 const Position * pos,
977 bool start_at_one)
978 {
979 g_return_val_if_fail (
980 ZRYTHM && PROJECT && TRANSPORT &&
981 TRANSPORT->ticks_per_bar > 0 &&
982 TRANSPORT->ticks_per_beat > 0 &&
983 P_TEMPO_TRACK, -1);
984
985 int beats_per_bar =
986 tempo_track_get_beats_per_bar (P_TEMPO_TRACK);
987 g_return_val_if_fail (beats_per_bar > 0, -1);
988
989 double total_bars =
990 (double) position_get_bars (pos, false);
991 double total_beats =
992 pos->ticks / TRANSPORT->ticks_per_beat;
993 total_beats -=
994 (double) (total_bars * beats_per_bar);
995 if (total_beats >= 0.0)
996 {
997 int ret = (int) floor (total_beats);
998 if (start_at_one)
999 {
1000 ret++;
1001 }
1002 return ret;
1003 }
1004 else
1005 {
1006 int ret = (int) ceil (total_beats);
1007 if (start_at_one)
1008 {
1009 ret--;
1010 }
1011 return ret;
1012 }
1013 }
1014
1015 /**
1016 * Gets the sixteenths of the position.
1017 *
1018 * Ie, if the position is equivalent to 4.1.2.42,
1019 * this will return 2.
1020 *
1021 * @param start_at_one Start at 1 or -1 instead of
1022 * 0.
1023 */
1024 int
position_get_sixteenths(const Position * pos,bool start_at_one)1025 position_get_sixteenths (
1026 const Position * pos,
1027 bool start_at_one)
1028 {
1029 g_return_val_if_fail (
1030 ZRYTHM && PROJECT && TRANSPORT &&
1031 TRANSPORT->sixteenths_per_beat > 0, -1);
1032
1033 double total_beats =
1034 (double) position_get_total_beats (pos, true);
1035 /*g_message ("total beats %f", total_beats);*/
1036 double total_sixteenths =
1037 pos->ticks / TICKS_PER_SIXTEENTH_NOTE_DBL;
1038 /*g_message ("total sixteenths %f",*/
1039 /*total_sixteenths);*/
1040 total_sixteenths -=
1041 (double)
1042 (total_beats * TRANSPORT->sixteenths_per_beat);
1043 if (total_sixteenths >= 0.0)
1044 {
1045 int ret = (int) floor (total_sixteenths);
1046 if (start_at_one)
1047 {
1048 ret++;
1049 }
1050 return ret;
1051 }
1052 else
1053 {
1054 int ret = (int) ceil (total_sixteenths);
1055 if (start_at_one)
1056 {
1057 ret--;
1058 }
1059 return ret;
1060 }
1061 }
1062
1063 /**
1064 * Gets the ticks of the position.
1065 *
1066 * Ie, if the position is equivalent to
1067 * 4.1.2.42.40124, this will return 42.40124.
1068 */
1069 double
position_get_ticks(const Position * pos)1070 position_get_ticks (
1071 const Position * pos)
1072 {
1073 double total_sixteenths =
1074 position_get_total_sixteenths (pos, true);
1075 /*g_debug ("total sixteenths %f", total_sixteenths);*/
1076 return
1077 pos->ticks -
1078 (total_sixteenths *
1079 TICKS_PER_SIXTEENTH_NOTE_DBL);
1080 }
1081
1082 bool
position_validate(const Position * pos)1083 position_validate (
1084 const Position * pos)
1085 {
1086 if (!pos->schema_version)
1087 {
1088 return false;
1089 }
1090
1091 return true;
1092 }
1093