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/automation_track.h"
23 #include "audio/automation_point.h"
24 #include "audio/automation_region.h"
25 #include "audio/control_port.h"
26 #include "audio/instrument_track.h"
27 #include "audio/track.h"
28 #include "gui/backend/event_manager.h"
29 #include "gui/widgets/arranger.h"
30 #include "gui/widgets/center_dock.h"
31 #include "gui/widgets/custom_button.h"
32 #include "gui/widgets/region.h"
33 #include "gui/widgets/timeline_arranger.h"
34 #include "gui/widgets/track.h"
35 #include "project.h"
36 #include "utils/arrays.h"
37 #include "utils/flags.h"
38 #include "utils/math.h"
39 #include "utils/mem.h"
40 #include "utils/objects.h"
41 #include "utils/string.h"
42 #include "zrythm_app.h"
43
44 #include <glib/gi18n.h>
45
46 static AutomationTrack *
_at_create(void)47 _at_create (void)
48 {
49 AutomationTrack * self =
50 object_new (AutomationTrack);
51
52 port_identifier_init (&self->port_id);
53
54 self->schema_version =
55 AUTOMATION_TRACK_SCHEMA_VERSION;
56
57 return self;
58 }
59
60 void
automation_track_init_loaded(AutomationTrack * self,AutomationTracklist * atl)61 automation_track_init_loaded (
62 AutomationTrack * self,
63 AutomationTracklist * atl)
64 {
65 self->atl = atl;
66
67 /* init regions */
68 self->regions_size =
69 (size_t) self->num_regions;
70 int i;
71 ZRegion * region;
72 for (i = 0; i < self->num_regions; i++)
73 {
74 region = self->regions[i];
75 arranger_object_init_loaded (
76 (ArrangerObject *) region);
77 }
78 }
79
80 AutomationTrack *
automation_track_new(Port * port)81 automation_track_new (
82 Port * port)
83 {
84 AutomationTrack * self = _at_create ();
85
86 self->regions_size = 1;
87 self->regions =
88 object_new_n (
89 self->regions_size, ZRegion *);
90
91 self->height = TRACK_DEF_HEIGHT;
92
93 g_return_val_if_fail (
94 port_identifier_validate (&port->id), NULL);
95 port_identifier_copy (
96 &self->port_id, &port->id);
97
98 port->at = self;
99
100 if (port->id.flags & PORT_FLAG_MIDI_AUTOMATABLE)
101 {
102 self->automation_mode =
103 AUTOMATION_MODE_RECORD;
104 self->record_mode =
105 AUTOMATION_RECORD_MODE_TOUCH;
106 }
107 else
108 self->automation_mode = AUTOMATION_MODE_READ;
109
110 return self;
111 }
112
113 NONNULL
114 bool
automation_track_validate(AutomationTrack * self)115 automation_track_validate (
116 AutomationTrack * self)
117 {
118 g_return_val_if_fail (
119 self->schema_version ==
120 AUTOMATION_TRACK_SCHEMA_VERSION &&
121 port_identifier_validate (&self->port_id),
122 false);
123
124 unsigned int track_name_hash =
125 self->port_id.track_name_hash;
126 if (self->port_id.owner_type ==
127 PORT_OWNER_TYPE_PLUGIN)
128 {
129 g_return_val_if_fail (
130 self->port_id.plugin_id.track_name_hash ==
131 track_name_hash, false);
132 }
133 AutomationTrack * found_at =
134 automation_track_find_from_port_id (
135 &self->port_id, false);
136 if (found_at != self)
137 {
138 g_message (
139 "The automation track for the following "
140 "port identifier was not found");
141 port_identifier_print (&self->port_id);
142 g_message (
143 "automation tracks:");
144 AutomationTracklist * atl =
145 automation_track_get_automation_tracklist (
146 self);
147 automation_tracklist_print_ats (atl);
148 g_return_val_if_reached (false);
149 }
150 for (int j = 0; j < self->num_regions; j++)
151 {
152 ZRegion * r = self->regions[j];
153 g_return_val_if_fail (
154 r->id.track_name_hash == track_name_hash &&
155 r->id.at_idx == self->index &&
156 r->id.idx ==j, false);
157 for (int k = 0; k < r->num_aps; k++)
158 {
159 AutomationPoint * ap = r->aps[k];
160 ArrangerObject * obj =
161 (ArrangerObject *) ap;
162 g_return_val_if_fail (
163 obj->region_id.track_name_hash ==
164 track_name_hash, false);
165 }
166 for (int k = 0; k < r->num_midi_notes; k++)
167 {
168 MidiNote * mn = r->midi_notes[k];
169 ArrangerObject * obj =
170 (ArrangerObject *) mn;
171 g_return_val_if_fail (
172 obj->region_id.track_name_hash ==
173 track_name_hash, false);
174 }
175 for (int k = 0; k < r->num_chord_objects;
176 k++)
177 {
178 ChordObject * co = r->chord_objects[k];
179 ArrangerObject * obj =
180 (ArrangerObject *) co;
181 g_return_val_if_fail (
182 obj->region_id.track_name_hash ==
183 track_name_hash, false);
184 }
185 }
186
187 return true;
188 }
189
190 /**
191 * Gets the automation mode as a localized string.
192 */
193 void
automation_mode_get_localized(AutomationMode mode,char * buf)194 automation_mode_get_localized (
195 AutomationMode mode,
196 char * buf)
197 {
198 switch (mode)
199 {
200 case AUTOMATION_MODE_READ:
201 strcpy (buf, _("On"));
202 break;
203 case AUTOMATION_MODE_RECORD:
204 strcpy (buf, _("Rec"));
205 break;
206 case AUTOMATION_MODE_OFF:
207 strcpy (buf, _("Off"));
208 break;
209 default:
210 g_return_if_reached ();
211 }
212 }
213
214 /**
215 * Gets the automation mode as a localized string.
216 */
217 void
automation_record_mode_get_localized(AutomationRecordMode mode,char * buf)218 automation_record_mode_get_localized (
219 AutomationRecordMode mode,
220 char * buf)
221 {
222 switch (mode)
223 {
224 case AUTOMATION_RECORD_MODE_TOUCH:
225 strcpy (buf, _("Touch"));
226 break;
227 case AUTOMATION_RECORD_MODE_LATCH:
228 strcpy (buf, _("Latch"));
229 break;
230 default:
231 g_return_if_reached ();
232 }
233 }
234
235 /**
236 * Adds an automation ZRegion to the AutomationTrack.
237 *
238 * @note This must not be used directly. Use
239 * track_add_region() instead.
240 */
241 void
automation_track_add_region(AutomationTrack * self,ZRegion * region)242 automation_track_add_region (
243 AutomationTrack * self,
244 ZRegion * region)
245 {
246 automation_track_insert_region (
247 self, region, self->num_regions);
248 }
249
250 /**
251 * Inserts an automation ZRegion to the
252 * AutomationTrack at the given index.
253 *
254 * @note This must not be used directly. Use
255 * track_insert_region() instead.
256 */
257 void
automation_track_insert_region(AutomationTrack * self,ZRegion * region,int idx)258 automation_track_insert_region (
259 AutomationTrack * self,
260 ZRegion * region,
261 int idx)
262 {
263 g_return_if_fail (idx >= 0);
264 g_return_if_fail (
265 region->name &&
266 region->id.type == REGION_TYPE_AUTOMATION);
267
268 array_double_size_if_full (
269 self->regions, self->num_regions,
270 self->regions_size, ZRegion *);
271 for (int i = self->num_regions; i > idx; i--)
272 {
273 self->regions[i] = self->regions[i - 1];
274 self->regions[i]->id.idx = i;
275 region_update_identifier (self->regions[i]);
276 }
277 self->num_regions++;
278
279 self->regions[idx] = region;
280 region_set_automation_track (region, self);
281 region->id.idx = idx;
282 region_update_identifier (region);
283 }
284
285 AutomationTracklist *
automation_track_get_automation_tracklist(AutomationTrack * self)286 automation_track_get_automation_tracklist (
287 AutomationTrack * self)
288 {
289 Track * track =
290 automation_track_get_track (self);
291 return
292 track_get_automation_tracklist (track);
293 }
294
295 /**
296 * Returns the ZRegion that starts before
297 * given Position, if any.
298 *
299 * @param ends_after Whether to only check for
300 * regions that also end after \ref pos (ie,
301 * the region surrounds \ref pos), otherwise
302 * get the region that ends last.
303 */
304 ZRegion *
automation_track_get_region_before_pos(const AutomationTrack * self,const Position * pos,bool ends_after)305 automation_track_get_region_before_pos (
306 const AutomationTrack * self,
307 const Position * pos,
308 bool ends_after)
309 {
310 if (ends_after)
311 {
312 for (int i = self->num_regions - 1; i >= 0;
313 i--)
314 {
315 ZRegion * region = self->regions[i];
316 ArrangerObject * r_obj =
317 (ArrangerObject *) region;
318 if (position_is_before_or_equal (
319 &r_obj->pos, pos) &&
320 position_is_after_or_equal (
321 &r_obj->end_pos, pos))
322 return region;
323 }
324 }
325 else
326 {
327 /* find latest region */
328 ZRegion * latest_r = NULL;
329 long latest_distance = LONG_MIN;
330 for (int i = self->num_regions - 1; i >= 0;
331 i--)
332 {
333 ZRegion * region = self->regions[i];
334 ArrangerObject * r_obj =
335 (ArrangerObject *) region;
336 long distance_from_r_end =
337 r_obj->end_pos.frames - pos->frames;
338 if (position_is_before_or_equal (
339 &r_obj->pos, pos) &&
340 distance_from_r_end > latest_distance)
341 {
342 latest_distance = distance_from_r_end;
343 latest_r = region;
344 }
345 }
346 return latest_r;
347 }
348 return NULL;
349 }
350
351 /**
352 * Returns the automation point before the Position
353 * on the timeline.
354 *
355 * @param ends_after Whether to only check in
356 * regions that also end after \ref pos (ie,
357 * the region surrounds \ref pos), otherwise
358 * check in the region that ends last.
359 */
360 AutomationPoint *
automation_track_get_ap_before_pos(const AutomationTrack * self,const Position * pos,bool ends_after)361 automation_track_get_ap_before_pos (
362 const AutomationTrack * self,
363 const Position * pos,
364 bool ends_after)
365 {
366 ZRegion * r =
367 automation_track_get_region_before_pos (
368 self, pos, ends_after);
369 ArrangerObject * r_obj = (ArrangerObject *) r;
370
371 if (!r || arranger_object_get_muted (r_obj))
372 {
373 return NULL;
374 }
375
376 /* if region ends before pos, assume pos is the
377 * region's end pos */
378 long local_pos =
379 region_timeline_frames_to_local (
380 r,
381 !ends_after &&
382 (r_obj->end_pos.frames < pos->frames) ?
383 r_obj->end_pos.frames - 1 :
384 pos->frames,
385 F_NORMALIZE);
386 /*g_debug ("local pos %ld", local_pos);*/
387
388 AutomationPoint * ap;
389 ArrangerObject * obj;
390 for (int i = r->num_aps - 1; i >= 0; i--)
391 {
392 ap = r->aps[i];
393 obj = (ArrangerObject *) ap;
394 if (obj->pos.frames <= local_pos)
395 return ap;
396 }
397
398 return NULL;
399 }
400
401 /**
402 * Finds the AutomationTrack associated with
403 * `port`.
404 *
405 * FIXME use hashtable
406 *
407 * @param track The track that owns the port, if
408 * known.
409 */
410 AutomationTrack *
automation_track_find_from_port(Port * port,Track * track,bool basic_search)411 automation_track_find_from_port (
412 Port * port,
413 Track * track,
414 bool basic_search)
415 {
416 if (!track)
417 {
418 track = port_get_track (port, 1);
419 }
420 g_return_val_if_fail (track, NULL);
421
422 AutomationTracklist * atl =
423 track_get_automation_tracklist (track);
424 g_return_val_if_fail (atl, NULL);
425 for (int i = 0; i < atl->num_ats; i++)
426 {
427 AutomationTrack * at = atl->ats[i];
428 if (basic_search)
429 {
430 PortIdentifier * src = &port->id;
431 PortIdentifier * dest = &at->port_id;
432 if (
433 string_is_equal (
434 dest->label, src->label) &&
435 dest->owner_type == src->owner_type &&
436 dest->type == src->type &&
437 dest->flow == src->flow &&
438 dest->flags == src->flags &&
439 dest->track_name_hash ==
440 src->track_name_hash)
441 {
442 if (dest->owner_type ==
443 PORT_OWNER_TYPE_PLUGIN)
444 {
445 if (!plugin_identifier_is_equal (
446 &dest->plugin_id,
447 &src->plugin_id))
448 {
449 continue;
450 }
451
452 Plugin * pl =
453 port_get_plugin (port, true);
454 g_return_val_if_fail (
455 IS_PLUGIN_AND_NONNULL (pl),
456 NULL);
457
458 if (pl->setting->descr->protocol ==
459 PROT_LV2)
460 {
461 /* if lv2 plugin port (not
462 * standard zrythm-provided
463 * port), make sure the symbol
464 * matches (some plugins have
465 * multiple ports with the same
466 * label but different
467 * symbol) */
468 if (src->flags ^
469 PORT_FLAG_GENERIC_PLUGIN_PORT &&
470 !string_is_equal (
471 dest->sym, src->sym))
472 {
473 continue;
474 }
475 return at;
476 }
477 /* if not lv2, also search by
478 * index */
479 else if (dest->port_index ==
480 src->port_index)
481 {
482 return at;
483 }
484 }
485 else
486 {
487 if (dest->port_index ==
488 src->port_index)
489 {
490 return at;
491 }
492 }
493 }
494 }
495 /* not basic search */
496 else
497 {
498 if (port_identifier_is_equal (
499 &port->id, &at->port_id))
500 {
501 return at;
502 }
503 }
504 }
505
506 return NULL;
507 }
508
509 /**
510 * @note This is expensive and should only be used
511 * if \ref PortIdentifier.at_idx is not set. Use
512 * port_get_automation_track() instead.
513 *
514 * @param basic_search If true, only basic port
515 * identifier members are checked.
516 */
517 AutomationTrack *
automation_track_find_from_port_id(PortIdentifier * id,bool basic_search)518 automation_track_find_from_port_id (
519 PortIdentifier * id,
520 bool basic_search)
521 {
522 Port * port = port_find_from_identifier (id);
523 g_return_val_if_fail (
524 port &&
525 port_identifier_is_equal (id, &port->id),
526 NULL);
527
528 return
529 automation_track_find_from_port (
530 port, NULL, basic_search);
531 }
532
533 /**
534 * Returns whether the automation in the automation
535 * track should be read.
536 *
537 * @param cur_time Current time from
538 * g_get_monotonic_time() passed here to ensure
539 * the same timestamp is used in sequential calls.
540 */
541 bool
automation_track_should_read_automation(AutomationTrack * at,gint64 cur_time)542 automation_track_should_read_automation (
543 AutomationTrack * at,
544 gint64 cur_time)
545 {
546 if (at->automation_mode == AUTOMATION_MODE_OFF)
547 return false;
548
549 /* TODO last argument should be true but doesnt
550 * work properly atm */
551 if (automation_track_should_be_recording (
552 at, cur_time, false))
553 return false;
554
555 return true;
556 }
557
558 /**
559 * Returns if the automation track should currently
560 * be recording data.
561 *
562 * Returns false if in touch mode after the release
563 * time has passed.
564 *
565 * This function assumes that the transport will
566 * be rolling.
567 *
568 * @param cur_time Current time from
569 * g_get_monotonic_time() passed here to ensure
570 * the same timestamp is used in sequential calls.
571 * @param record_aps If set to true, this function
572 * will return whether we should be recording
573 * automation point data. If set to false, this
574 * function will return whether we should be
575 * recording a region (eg, if an automation point
576 * was already created before and we are still
577 * recording inside a region regardless of whether
578 * we should create/edit automation points or not.
579 */
580 bool
automation_track_should_be_recording(AutomationTrack * at,gint64 cur_time,bool record_aps)581 automation_track_should_be_recording (
582 AutomationTrack * at,
583 gint64 cur_time,
584 bool record_aps)
585 {
586 if (at->automation_mode == AUTOMATION_MODE_RECORD)
587 {
588 if (at->record_mode ==
589 AUTOMATION_RECORD_MODE_LATCH)
590 {
591 /* in latch mode, we are always recording,
592 * even if the value doesn't change
593 * (an automation point will be created
594 * as soon as latch mode is armed) and
595 * then only when changes are made) */
596 return true;
597 }
598 if (at->record_mode ==
599 AUTOMATION_RECORD_MODE_TOUCH)
600 {
601 Port * port = at->port;
602 g_return_val_if_fail (
603 IS_PORT_AND_NONNULL (port), false);
604 gint64 diff =
605 cur_time - port->last_change;
606 if (diff <
607 AUTOMATION_RECORDING_TOUCH_REL_MS * 1000)
608 {
609 /* still recording */
610 return true;
611 }
612 else if (!record_aps)
613 return at->recording_started;
614 }
615 }
616
617 return false;
618 }
619
620 /**
621 * Unselects all arranger objects.
622 */
623 void
automation_track_unselect_all(AutomationTrack * self)624 automation_track_unselect_all (
625 AutomationTrack * self)
626 {
627 for (int i = 0; i < self->num_regions; i++)
628 {
629 ZRegion * region = self->regions[i];
630 arranger_object_select (
631 (ArrangerObject *) region, false, false,
632 F_NO_PUBLISH_EVENTS);
633 }
634 }
635
636 /**
637 * Removes a region from the automation track.
638 */
639 void
automation_track_remove_region(AutomationTrack * self,ZRegion * region)640 automation_track_remove_region (
641 AutomationTrack * self,
642 ZRegion * region)
643 {
644 g_return_if_fail (IS_REGION (region));
645
646 array_delete (
647 self->regions, self->num_regions, region);
648
649 for (int i = region->id.idx;
650 i < self->num_regions; i++)
651 {
652 ZRegion * r = self->regions[i];
653 r->id.idx = i;
654 region_update_identifier (r);
655 }
656 }
657
658 /**
659 * Removes and frees all arranger objects
660 * recursively.
661 */
662 void
automation_track_clear(AutomationTrack * self)663 automation_track_clear (
664 AutomationTrack * self)
665 {
666 for (int i = self->num_regions - 1; i >= 0; i--)
667 {
668 ZRegion * region = self->regions[i];
669 Track * track =
670 automation_track_get_track (self);
671 g_return_if_fail (
672 IS_TRACK_AND_NONNULL (track));
673 track_remove_region (
674 track, region, F_NO_PUBLISH_EVENTS, F_FREE);
675 }
676 self->num_regions = 0;
677 }
678
679 Track *
automation_track_get_track(AutomationTrack * self)680 automation_track_get_track (
681 AutomationTrack * self)
682 {
683 Track * track =
684 tracklist_find_track_by_name_hash (
685 TRACKLIST, self->port_id.track_name_hash);
686 g_return_val_if_fail (track, NULL);
687
688 return track;
689 }
690
691 /**
692 * Sets the index of the AutomationTrack in the
693 * AutomationTracklist.
694 */
695 void
automation_track_set_index(AutomationTrack * self,int index)696 automation_track_set_index (
697 AutomationTrack * self,
698 int index)
699 {
700 self->index = index;
701
702 for (int i = 0; i < self->num_regions; i++)
703 {
704 ZRegion * region = self->regions[i];
705 g_return_if_fail (region);
706 region->id.at_idx = index;
707 region_update_identifier (region);
708 }
709 }
710
711 /**
712 * Returns the actual parameter value at the given
713 * position.
714 *
715 * If there is no automation point/curve during
716 * the position, it returns the current value
717 * of the parameter it is automating.
718 *
719 * @param normalized Whether to return the value
720 * normalized.
721 * @param ends_after Whether to only check in
722 * regions that also end after \ref pos (ie,
723 * the region surrounds \ref pos), otherwise
724 * check in the region that ends last.
725 */
726 float
automation_track_get_val_at_pos(AutomationTrack * self,Position * pos,bool normalized,bool ends_after)727 automation_track_get_val_at_pos (
728 AutomationTrack * self,
729 Position * pos,
730 bool normalized,
731 bool ends_after)
732 {
733 AutomationPoint * ap =
734 automation_track_get_ap_before_pos (
735 self, pos, ends_after);
736 ArrangerObject * ap_obj =
737 (ArrangerObject *) ap;
738
739 Port * port =
740 port_find_from_identifier (&self->port_id);
741 g_return_val_if_fail (port, 0.f);
742
743 /* no automation points yet, return negative
744 * (no change) */
745 if (!ap)
746 {
747 return
748 port_get_control_value (port, normalized);
749 }
750
751 ZRegion * region =
752 arranger_object_get_region (ap_obj);
753 ArrangerObject * r_obj =
754 (ArrangerObject *) region;
755
756 /* if region ends before pos, assume pos is the
757 * region's end pos */
758 long localp =
759 region_timeline_frames_to_local (
760 region,
761 !ends_after &&
762 (r_obj->end_pos.frames < pos->frames) ?
763 r_obj->end_pos.frames - 1 :
764 pos->frames,
765 F_NORMALIZE);
766 /*g_debug ("local pos %ld", localp);*/
767
768 AutomationPoint * next_ap =
769 automation_region_get_next_ap (
770 region, ap, false, false);
771 ArrangerObject * next_ap_obj =
772 (ArrangerObject *) next_ap;
773
774 /* return value at last ap */
775 if (!next_ap)
776 {
777 if (normalized)
778 {
779 return ap->normalized_val;
780 }
781 else
782 {
783 return ap->fvalue;
784 }
785 }
786
787 int prev_ap_lower =
788 ap->normalized_val <= next_ap->normalized_val;
789 float cur_next_diff =
790 (float)
791 fabsf (
792 ap->normalized_val - next_ap->normalized_val);
793
794 /* ratio of how far in we are in the curve */
795 long ap_frames =
796 position_to_frames (&ap_obj->pos);
797 long next_ap_frames =
798 position_to_frames (&next_ap_obj->pos);
799 double ratio =
800 (double) (localp - ap_frames) /
801 (double) (next_ap_frames - ap_frames);
802 g_return_val_if_fail (ratio >= 0, 0.f);
803
804 float result =
805 (float)
806 automation_point_get_normalized_value_in_curve (
807 ap, ratio);
808 result = result * cur_next_diff;
809 if (prev_ap_lower)
810 result +=
811 ap->normalized_val;
812 else
813 result +=
814 next_ap->normalized_val;
815
816 if (normalized)
817 {
818 return result;
819 }
820 else
821 {
822 return
823 control_port_normalized_val_to_real (
824 port, result);
825 }
826 }
827
828 /**
829 * Updates each position in each child of the
830 * automation track recursively.
831 *
832 * @param from_ticks Whether to update the
833 * positions based on ticks (true) or frames
834 * (false).
835 */
836 void
automation_track_update_positions(AutomationTrack * self,bool from_ticks)837 automation_track_update_positions (
838 AutomationTrack * self,
839 bool from_ticks)
840 {
841 for (int i = 0; i < self->num_regions; i++)
842 {
843 arranger_object_update_positions (
844 (ArrangerObject *) self->regions[i],
845 from_ticks);
846 }
847 }
848
849 CONST
850 static int
get_y_px_from_height_and_normalized_val(const float height,const float normalized_val)851 get_y_px_from_height_and_normalized_val (
852 const float height,
853 const float normalized_val)
854 {
855 return
856 (int) (height - normalized_val * height);
857 }
858
859 /**
860 * Returns the y pixels from the value based on the
861 * allocation of the automation track.
862 */
863 int
automation_track_get_y_px_from_normalized_val(AutomationTrack * self,float normalized_val)864 automation_track_get_y_px_from_normalized_val (
865 AutomationTrack * self,
866 float normalized_val)
867 {
868 return
869 get_y_px_from_height_and_normalized_val (
870 (float) self->height, normalized_val);
871 }
872
873 /**
874 * Gets the last ZRegion in the AutomationTrack.
875 *
876 * FIXME cache.
877 */
878 ZRegion *
automation_track_get_last_region(AutomationTrack * self)879 automation_track_get_last_region (
880 AutomationTrack * self)
881 {
882 Position pos;
883 position_init (&pos);
884 ZRegion * region, * last_region = NULL;
885 ArrangerObject * r_obj;
886 for (int i = 0; i < self->num_regions; i++)
887 {
888 region = self->regions[i];
889 r_obj = (ArrangerObject *) region;
890 if (position_is_after (
891 &r_obj->end_pos, &pos))
892 {
893 last_region = region;
894 position_set_to_pos (
895 &pos, &r_obj->end_pos);
896 }
897 }
898 return last_region;
899 }
900
901 bool
automation_track_verify(AutomationTrack * self)902 automation_track_verify (
903 AutomationTrack * self)
904 {
905 for (int i = 0; i < self->num_regions; i++)
906 {
907 ZRegion * r = self->regions[i];
908
909 for (int j = 0; j < r->num_aps; j++)
910 {
911 AutomationPoint * ap = r->aps[j];
912
913 if (ZRYTHM_TESTING)
914 {
915 if (!math_assert_nonnann (
916 ap->fvalue) ||
917 !math_assert_nonnann (
918 ap->normalized_val))
919 {
920 return false;
921 }
922 }
923 }
924 }
925 return true;
926 }
927
928 void
automation_track_set_caches(AutomationTrack * self)929 automation_track_set_caches (
930 AutomationTrack * self)
931 {
932 self->port =
933 port_find_from_identifier (&self->port_id);
934 }
935
936 /**
937 * Clones the AutomationTrack.
938 */
939 AutomationTrack *
automation_track_clone(AutomationTrack * src)940 automation_track_clone (
941 AutomationTrack * src)
942 {
943 AutomationTrack * dest = _at_create ();
944
945 dest->regions_size = (size_t) src->num_regions;
946 dest->num_regions = src->num_regions;
947 dest->regions =
948 object_new_n (dest->regions_size, ZRegion *);
949 dest->visible = src->visible;
950 dest->created = src->created;
951 dest->index = src->index;
952 dest->y = src->y;
953 dest->automation_mode = src->automation_mode;
954 dest->record_mode = src->record_mode;
955 dest->height = src->height;
956 g_warn_if_fail (dest->height >= TRACK_MIN_HEIGHT);
957
958 port_identifier_copy (
959 &dest->port_id, &src->port_id);
960
961 ZRegion * src_region;
962 for (int j = 0; j < src->num_regions; j++)
963 {
964 src_region = src->regions[j];
965 dest->regions[j] =
966 (ZRegion *)
967 arranger_object_clone (
968 (ArrangerObject *) src_region);
969 }
970
971 return dest;
972 }
973
974 void
automation_track_free(AutomationTrack * self)975 automation_track_free (AutomationTrack * self)
976 {
977 for (int i = 0; i < self->num_regions; i++)
978 {
979 object_free_w_func_and_null_cast (
980 arranger_object_free, ArrangerObject *,
981 self->regions[i]);
982 }
983 object_zero_and_free (self->regions);
984
985 object_zero_and_free (self);
986 }
987