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