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