new automation state model, sort of working, but not really
[ardour.git] / libs / ardour / region.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <iostream>
22 #include <cmath>
23 #include <climits>
24 #include <algorithm>
25
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
28
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31 #include <pbd/stacktrace.h>
32
33 #include <ardour/region.h>
34 #include <ardour/playlist.h>
35 #include <ardour/session.h>
36 #include <ardour/region_factory.h>
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43
44 Change Region::FadeChanged       = ARDOUR::new_change ();
45 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
46 Change Region::MuteChanged       = ARDOUR::new_change ();
47 Change Region::OpacityChanged    = ARDOUR::new_change ();
48 Change Region::LockChanged       = ARDOUR::new_change ();
49 Change Region::LayerChanged      = ARDOUR::new_change ();
50 Change Region::HiddenChanged     = ARDOUR::new_change ();
51
52 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
53 {
54         /* basic Region constructor */
55
56         _flags = flags;
57         _playlist = 0;
58         _read_data_count = 0;
59         _frozen = 0;
60         pending_changed = Change (0);
61
62         _name = name;
63         _start = start; 
64         _sync_position = _start;
65         _length = length; 
66         _position = 0; 
67         _layer = layer;
68         _read_data_count = 0;
69         _first_edit = EditChangesNothing;
70         _last_layer_op = 0;
71 }
72
73 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
74 {
75         /* create a new Region from part of an existing one */
76
77         _frozen = 0;
78         pending_changed = Change (0);
79         _playlist = 0;
80         _read_data_count = 0;
81
82         _start = other->_start + offset; 
83         if (other->_sync_position < offset) {
84                 _sync_position = other->_sync_position;
85         } else {
86                 _sync_position = _start;
87         }
88         _length = length; 
89         _name = name;
90         _position = 0; 
91         _layer = layer; 
92         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
93         _first_edit = EditChangesNothing;
94         _last_layer_op = 0;
95 }
96
97 Region::Region (boost::shared_ptr<const Region> other)
98 {
99         /* Pure copy constructor */
100
101         _frozen = 0;
102         pending_changed = Change (0);
103         _playlist = 0;
104         _read_data_count = 0;
105
106         _first_edit = EditChangesID;
107         other->_first_edit = EditChangesName;
108
109         if (other->_extra_xml) {
110                 _extra_xml = new XMLNode (*other->_extra_xml);
111         } else {
112                 _extra_xml = 0;
113         }
114
115         _start = other->_start;
116         _sync_position = other->_sync_position;
117         _length = other->_length; 
118         _name = other->_name;
119         _position = other->_position; 
120         _layer = other->_layer; 
121         _flags = Flag (other->_flags & ~Locked);
122         _last_layer_op = other->_last_layer_op;
123 }
124
125 Region::Region (const XMLNode& node)
126 {
127         _frozen = 0;
128         pending_changed = Change (0);
129         _playlist = 0;
130         _read_data_count = 0;
131         _start = 0; 
132         _sync_position = _start;
133         _length = 0;
134         _name = X_("error: XML did not reset this");
135         _position = 0; 
136         _layer = 0;
137         _flags = Flag (0);
138         _first_edit = EditChangesNothing;
139
140         if (set_state (node)) {
141                 throw failed_constructor();
142         }
143 }
144
145 Region::~Region ()
146 {
147         /* derived classes must call notify_callbacks() and then emit GoingAway */
148 }
149
150 void
151 Region::set_playlist (Playlist* pl)
152 {
153         _playlist = pl;
154 }
155
156 void
157 Region::set_name (string str)
158 {
159         if (_name != str) {
160                 _name = str; 
161                 send_change (NameChanged);
162         }
163 }
164
165 void
166 Region::set_length (nframes_t len, void *src)
167 {
168         if (_flags & Locked) {
169                 return;
170         }
171
172         if (_length != len && len != 0) {
173
174                 /* check that the current _position wouldn't make the new 
175                    length impossible.
176                 */
177
178                 if (max_frames - len < _position) {
179                         return;
180                 }
181
182                 if (!verify_length (len)) {
183                         return;
184                 }
185                 
186                 _length = len;
187
188                 _flags = Region::Flag (_flags & ~WholeFile);
189
190                 first_edit ();
191                 maybe_uncopy ();
192
193                 if (!_frozen) {
194                         recompute_at_end ();
195                 }
196
197                 send_change (LengthChanged);
198         }
199 }
200
201 void
202 Region::maybe_uncopy ()
203 {
204 }
205
206 void
207 Region::first_edit ()
208 {
209         if (_first_edit != EditChangesNothing && _playlist) {
210
211                 _name = _playlist->session().new_region_name (_name);
212                 _first_edit = EditChangesNothing;
213
214                 send_change (NameChanged);
215                 RegionFactory::CheckNewRegion (shared_from_this());
216         }
217 }
218
219 void
220 Region::move_to_natural_position (void *src)
221 {
222         if (!_playlist) {
223                 return;
224         }
225         
226         boost::shared_ptr<Region> whole_file_region = get_parent();
227
228         if (whole_file_region) {
229                 set_position (whole_file_region->position() + _start, src);
230         }
231 }
232         
233 void
234 Region::special_set_position (nframes_t pos)
235 {
236         /* this is used when creating a whole file region as 
237            a way to store its "natural" or "captured" position.
238         */
239
240         _position = pos;
241 }
242
243 void
244 Region::set_position (nframes_t pos, void *src)
245 {
246         if (_flags & Locked) {
247                 return;
248         }
249
250         if (_position != pos) {
251                 _position = pos;
252
253                 /* check that the new _position wouldn't make the current
254                    length impossible - if so, change the length. 
255
256                    XXX is this the right thing to do?
257                 */
258
259                 if (max_frames - _length < _position) {
260                         _length = max_frames - _position;
261                 }
262         }
263
264         /* do this even if the position is the same. this helps out
265            a GUI that has moved its representation already.
266         */
267
268         send_change (PositionChanged);
269 }
270
271 void
272 Region::set_position_on_top (nframes_t pos, void *src)
273 {
274         if (_flags & Locked) {
275                 return;
276         }
277
278         if (_position != pos) {
279                 _position = pos;
280         }
281
282         _playlist->raise_region_to_top (shared_from_this ());
283
284         /* do this even if the position is the same. this helps out
285            a GUI that has moved its representation already.
286         */
287         
288         send_change (PositionChanged);
289 }
290
291 void
292 Region::nudge_position (long n, void *src)
293 {
294         if (_flags & Locked) {
295                 return;
296         }
297
298         if (n == 0) {
299                 return;
300         }
301         
302         if (n > 0) {
303                 if (_position > max_frames - n) {
304                         _position = max_frames;
305                 } else {
306                         _position += n;
307                 }
308         } else {
309                 if (_position < (nframes_t) -n) {
310                         _position = 0;
311                 } else {
312                         _position += n;
313                 }
314         }
315
316         send_change (PositionChanged);
317 }
318
319 void
320 Region::set_start (nframes_t pos, void *src)
321 {
322         if (_flags & Locked) {
323                 return;
324         }
325         /* This just sets the start, nothing else. It effectively shifts
326            the contents of the Region within the overall extent of the Source,
327            without changing the Region's position or length
328         */
329
330         if (_start != pos) {
331
332                 if (!verify_start (pos)) {
333                         return;
334                 }
335
336                 _start = pos;
337                 _flags = Region::Flag (_flags & ~WholeFile);
338                 first_edit ();
339
340                 send_change (StartChanged);
341         }
342 }
343
344 void
345 Region::trim_start (nframes_t new_position, void *src)
346 {
347         if (_flags & Locked) {
348                 return;
349         }
350         nframes_t new_start;
351         int32_t start_shift;
352         
353         if (new_position > _position) {
354                 start_shift = new_position - _position;
355         } else {
356                 start_shift = -(_position - new_position);
357         }
358
359         if (start_shift > 0) {
360
361                 if (_start > max_frames - start_shift) {
362                         new_start = max_frames;
363                 } else {
364                         new_start = _start + start_shift;
365                 }
366
367                 if (!verify_start (new_start)) {
368                         return;
369                 }
370
371         } else if (start_shift < 0) {
372
373                 if (_start < (nframes_t) -start_shift) {
374                         new_start = 0;
375                 } else {
376                         new_start = _start + start_shift;
377                 }
378         } else {
379                 return;
380         }
381
382         if (new_start == _start) {
383                 return;
384         }
385         
386         _start = new_start;
387         _flags = Region::Flag (_flags & ~WholeFile);
388         first_edit ();
389
390         send_change (StartChanged);
391 }
392
393 void
394 Region::trim_front (nframes_t new_position, void *src)
395 {
396         if (_flags & Locked) {
397                 return;
398         }
399
400         nframes_t end = last_frame();
401         nframes_t source_zero;
402
403         if (_position > _start) {
404                 source_zero = _position - _start;
405         } else {
406                 source_zero = 0; // its actually negative, but this will work for us
407         }
408
409         if (new_position < end) { /* can't trim it zero or negative length */
410                 
411                 nframes_t newlen;
412
413                 /* can't trim it back passed where source position zero is located */
414                 
415                 new_position = max (new_position, source_zero);
416                 
417                 
418                 if (new_position > _position) {
419                         newlen = _length - (new_position - _position);
420                 } else {
421                         newlen = _length + (_position - new_position);
422                 }
423                 
424                 trim_to_internal (new_position, newlen, src);
425                 if (!_frozen) {
426                         recompute_at_start ();
427                 }
428         }
429 }
430
431 void
432 Region::trim_end (nframes_t new_endpoint, void *src)
433 {
434         if (_flags & Locked) {
435                 return;
436         }
437
438         if (new_endpoint > _position) {
439                 trim_to_internal (_position, new_endpoint - _position, this);
440                 if (!_frozen) {
441                         recompute_at_end ();
442                 }
443         }
444 }
445
446 void
447 Region::trim_to (nframes_t position, nframes_t length, void *src)
448 {
449         if (_flags & Locked) {
450                 return;
451         }
452
453         trim_to_internal (position, length, src);
454
455         if (!_frozen) {
456                 recompute_at_start ();
457                 recompute_at_end ();
458         }
459 }
460
461 void
462 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
463 {
464         int32_t start_shift;
465         nframes_t new_start;
466
467         if (_flags & Locked) {
468                 return;
469         }
470
471         if (position > _position) {
472                 start_shift = position - _position;
473         } else {
474                 start_shift = -(_position - position);
475         }
476
477         if (start_shift > 0) {
478
479                 if (_start > max_frames - start_shift) {
480                         new_start = max_frames;
481                 } else {
482                         new_start = _start + start_shift;
483                 }
484
485
486         } else if (start_shift < 0) {
487
488                 if (_start < (nframes_t) -start_shift) {
489                         new_start = 0;
490                 } else {
491                         new_start = _start + start_shift;
492                 }
493         } else {
494                 new_start = _start;
495         }
496
497         if (!verify_start_and_length (new_start, length)) {
498                 return;
499         }
500
501         Change what_changed = Change (0);
502
503         if (_start != new_start) {
504                 _start = new_start;
505                 what_changed = Change (what_changed|StartChanged);
506         }
507         if (_length != length) {
508                 _length = length;
509                 what_changed = Change (what_changed|LengthChanged);
510         }
511         if (_position != position) {
512                 _position = position;
513                 what_changed = Change (what_changed|PositionChanged);
514         }
515         
516         _flags = Region::Flag (_flags & ~WholeFile);
517
518         if (what_changed & (StartChanged|LengthChanged)) {
519                 first_edit ();
520         } 
521
522         if (what_changed) {
523                 send_change (what_changed);
524         }
525 }       
526
527 void
528 Region::set_hidden (bool yn)
529 {
530         if (hidden() != yn) {
531
532                 if (yn) {
533                         _flags = Flag (_flags|Hidden);
534                 } else {
535                         _flags = Flag (_flags & ~Hidden);
536                 }
537
538                 send_change (HiddenChanged);
539         }
540 }
541
542 void
543 Region::set_muted (bool yn)
544 {
545         if (muted() != yn) {
546
547                 if (yn) {
548                         _flags = Flag (_flags|Muted);
549                 } else {
550                         _flags = Flag (_flags & ~Muted);
551                 }
552
553                 send_change (MuteChanged);
554         }
555 }
556
557 void
558 Region::set_opaque (bool yn)
559 {
560         if (opaque() != yn) {
561                 if (yn) {
562                         _flags = Flag (_flags|Opaque);
563                 } else {
564                         _flags = Flag (_flags & ~Opaque);
565                 }
566                 send_change (OpacityChanged);
567         }
568 }
569
570 void
571 Region::set_locked (bool yn)
572 {
573         if (locked() != yn) {
574                 if (yn) {
575                         _flags = Flag (_flags|Locked);
576                 } else {
577                         _flags = Flag (_flags & ~Locked);
578                 }
579                 send_change (LockChanged);
580         }
581 }
582
583 void
584 Region::set_sync_position (nframes_t absolute_pos)
585 {
586         nframes_t file_pos;
587
588         file_pos = _start + (absolute_pos - _position);
589
590         if (file_pos != _sync_position) {
591                 
592                 _sync_position = file_pos;
593                 _flags = Flag (_flags|SyncMarked);
594
595                 if (!_frozen) {
596                         maybe_uncopy ();
597                 }
598                 send_change (SyncOffsetChanged);
599         }
600 }
601
602 void
603 Region::clear_sync_position ()
604 {
605         if (_flags & SyncMarked) {
606                 _flags = Flag (_flags & ~SyncMarked);
607
608                 if (!_frozen) {
609                         maybe_uncopy ();
610                 }
611                 send_change (SyncOffsetChanged);
612         }
613 }
614
615 nframes_t
616 Region::sync_offset (int& dir) const
617 {
618         /* returns the sync point relative the first frame of the region */
619
620         if (_flags & SyncMarked) {
621                 if (_sync_position > _start) {
622                         dir = 1;
623                         return _sync_position - _start; 
624                 } else {
625                         dir = -1;
626                         return _start - _sync_position;
627                 }
628         } else {
629                 dir = 0;
630                 return 0;
631         }
632 }
633
634 nframes_t 
635 Region::adjust_to_sync (nframes_t pos)
636 {
637         int sync_dir;
638         nframes_t offset = sync_offset (sync_dir);
639         
640         if (sync_dir > 0) {
641                 if (max_frames - pos > offset) {
642                         pos += offset;
643                 }
644         } else {
645                 if (pos > offset) {
646                         pos -= offset;
647                 } else {
648                         pos = 0;
649                 }
650         }
651
652         return pos;
653 }
654
655 nframes_t
656 Region::sync_position() const
657 {
658         if (_flags & SyncMarked) {
659                 return _sync_position; 
660         } else {
661                 return _start;
662         }
663 }
664
665
666 void
667 Region::raise ()
668 {
669         if (_playlist == 0) {
670                 return;
671         }
672
673         _playlist->raise_region (shared_from_this ());
674 }
675
676 void
677 Region::lower ()
678 {
679         if (_playlist == 0) {
680                 return;
681         }
682
683         _playlist->lower_region (shared_from_this ());
684 }
685
686 void
687 Region::raise_to_top ()
688 {
689
690         if (_playlist == 0) {
691                 return;
692         }
693
694         _playlist->raise_region_to_top (shared_from_this());
695 }
696
697 void
698 Region::lower_to_bottom ()
699 {
700         if (_playlist == 0) {
701                 return;
702         }
703
704         _playlist->lower_region_to_bottom (shared_from_this());
705 }
706
707 void
708 Region::set_layer (layer_t l)
709 {
710         if (_layer != l) {
711                 _layer = l;
712                 
713                 send_change (LayerChanged);
714         }
715 }
716
717 XMLNode&
718 Region::state (bool full_state)
719 {
720         XMLNode *node = new XMLNode ("Region");
721         char buf[64];
722         char* fe;
723
724         _id.print (buf, sizeof (buf));
725         node->add_property ("id", buf);
726         node->add_property ("name", _name);
727         snprintf (buf, sizeof (buf), "%u", _start);
728         node->add_property ("start", buf);
729         snprintf (buf, sizeof (buf), "%u", _length);
730         node->add_property ("length", buf);
731         snprintf (buf, sizeof (buf), "%u", _position);
732         node->add_property ("position", buf);
733         
734         switch (_first_edit) {
735         case EditChangesNothing:
736                 fe = X_("nothing");
737                 break;
738         case EditChangesName:
739                 fe = X_("name");
740                 break;
741         case EditChangesID:
742                 fe = X_("id");
743                 break;
744         }
745
746         node->add_property ("first_edit", fe);
747
748         /* note: flags are stored by derived classes */
749
750         snprintf (buf, sizeof (buf), "%d", (int) _layer);
751         node->add_property ("layer", buf);
752         snprintf (buf, sizeof (buf), "%u", _sync_position);
753         node->add_property ("sync-position", buf);
754
755         return *node;
756 }
757
758 XMLNode&
759 Region::get_state ()
760 {
761         return state (true);
762 }
763
764 int
765 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
766 {
767         const XMLNodeList& nlist = node.children();
768         const XMLProperty *prop;
769         nframes_t val;
770
771         /* this is responsible for setting those aspects of Region state 
772            that are mutable after construction.
773         */
774
775         if ((prop = node.property ("name")) == 0) {
776                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
777                 return -1;
778         }
779
780         _name = prop->value();
781
782         if ((prop = node.property ("start")) != 0) {
783                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
784                 if (val != _start) {
785                         what_changed = Change (what_changed|StartChanged);      
786                         _start = val;
787                 }
788         } else {
789                 _start = 0;
790         }
791
792         if ((prop = node.property ("length")) != 0) {
793                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
794                 if (val != _length) {
795                         what_changed = Change (what_changed|LengthChanged);
796                         _length = val;
797                 }
798         } else {
799                 _length = 1;
800         }
801
802         if ((prop = node.property ("position")) != 0) {
803                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
804                 if (val != _position) {
805                         what_changed = Change (what_changed|PositionChanged);
806                         _position = val;
807                 }
808         } else {
809                 _position = 0;
810         }
811
812         if ((prop = node.property ("layer")) != 0) {
813                 layer_t x;
814                 x = (layer_t) atoi (prop->value().c_str());
815                 if (x != _layer) {
816                         what_changed = Change (what_changed|LayerChanged);
817                         _layer = x;
818                 }
819         } else {
820                 _layer = 0;
821         }
822
823         if ((prop = node.property ("sync-position")) != 0) {
824                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
825                 if (val != _sync_position) {
826                         what_changed = Change (what_changed|SyncOffsetChanged);
827                         _sync_position = val;
828                 }
829         } else {
830                 _sync_position = _start;
831         }
832
833         /* XXX FIRST EDIT !!! */
834         
835         /* note: derived classes set flags */
836
837         if (_extra_xml) {
838                 delete _extra_xml;
839                 _extra_xml = 0;
840         }
841
842         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
843                 
844                 XMLNode *child;
845                 
846                 child = (*niter);
847                 
848                 if (child->name () == "extra") {
849                         _extra_xml = new XMLNode (*child);
850                         break;
851                 }
852         }
853
854         if (send) {
855                 send_change (what_changed);
856         }
857
858         return 0;
859 }
860
861 int
862 Region::set_state (const XMLNode& node)
863 {
864         const XMLProperty *prop;
865         Change what_changed = Change (0);
866
867         /* ID is not allowed to change, ever */
868
869         if ((prop = node.property ("id")) == 0) {
870                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
871                 return -1;
872         }
873
874         _id = prop->value();
875         
876         _first_edit = EditChangesNothing;
877         
878         set_live_state (node, what_changed, true);
879
880         return 0;
881 }
882
883 void
884 Region::freeze ()
885 {
886         _frozen++;
887 }
888
889 void
890 Region::thaw (const string& why)
891 {
892         Change what_changed = Change (0);
893
894         {
895                 Glib::Mutex::Lock lm (lock);
896
897                 if (_frozen && --_frozen > 0) {
898                         return;
899                 }
900
901                 if (pending_changed) {
902                         what_changed = pending_changed;
903                         pending_changed = Change (0);
904                 }
905         }
906
907         if (what_changed == Change (0)) {
908                 return;
909         }
910
911         if (what_changed & LengthChanged) {
912                 if (what_changed & PositionChanged) {
913                         recompute_at_start ();
914                 } 
915                 recompute_at_end ();
916         }
917                 
918         StateChanged (what_changed);
919 }
920
921 void
922 Region::send_change (Change what_changed)
923 {
924         {
925                 Glib::Mutex::Lock lm (lock);
926                 if (_frozen) {
927                         pending_changed = Change (pending_changed|what_changed);
928                         return;
929                 } 
930         }
931
932         StateChanged (what_changed);
933 }
934
935 void
936 Region::set_last_layer_op (uint64_t when)
937 {
938         _last_layer_op = when;
939 }
940
941 bool
942 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
943 {
944         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
945 }
946
947 bool
948 Region::equivalent (boost::shared_ptr<const Region> other) const
949 {
950         return _start == other->_start &&
951                 _position == other->_position &&
952                 _length == other->_length;
953 }
954
955 bool
956 Region::size_equivalent (boost::shared_ptr<const Region> other) const
957 {
958         return _start == other->_start &&
959                 _length == other->_length;
960 }
961
962 bool
963 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
964 {
965         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
966 }