various fixes to named selection mgmt and display, plus fixes for xfade mgmt and...
[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;
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         }
764
765         node->add_property ("first_edit", fe);
766
767         /* note: flags are stored by derived classes */
768
769         snprintf (buf, sizeof (buf), "%d", (int) _layer);
770         node->add_property ("layer", buf);
771         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
772         node->add_property ("sync-position", buf);
773
774         return *node;
775 }
776
777 XMLNode&
778 Region::get_state ()
779 {
780         return state (true);
781 }
782
783 int
784 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
785 {
786         const XMLNodeList& nlist = node.children();
787         const XMLProperty *prop;
788         nframes_t val;
789
790         /* this is responsible for setting those aspects of Region state 
791            that are mutable after construction.
792         */
793
794         if ((prop = node.property ("name")) == 0) {
795                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
796                 return -1;
797         }
798
799         _name = prop->value();
800
801         if ((prop = node.property ("start")) != 0) {
802                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
803                 if (val != _start) {
804                         what_changed = Change (what_changed|StartChanged);      
805                         _start = val;
806                 }
807         } else {
808                 _start = 0;
809         }
810
811         if ((prop = node.property ("length")) != 0) {
812                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
813                 if (val != _length) {
814                         what_changed = Change (what_changed|LengthChanged);
815                         _length = val;
816                 }
817         } else {
818                 _length = 1;
819         }
820
821         if ((prop = node.property ("position")) != 0) {
822                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
823                 if (val != _position) {
824                         what_changed = Change (what_changed|PositionChanged);
825                         _position = val;
826                 }
827         } else {
828                 _position = 0;
829         }
830
831         if ((prop = node.property ("layer")) != 0) {
832                 layer_t x;
833                 x = (layer_t) atoi (prop->value().c_str());
834                 if (x != _layer) {
835                         what_changed = Change (what_changed|LayerChanged);
836                         _layer = x;
837                 }
838         } else {
839                 _layer = 0;
840         }
841
842         if ((prop = node.property ("sync-position")) != 0) {
843                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
844                 if (val != _sync_position) {
845                         what_changed = Change (what_changed|SyncOffsetChanged);
846                         _sync_position = val;
847                 }
848         } else {
849                 _sync_position = _start;
850         }
851
852         /* XXX FIRST EDIT !!! */
853         
854         /* note: derived classes set flags */
855
856         if (_extra_xml) {
857                 delete _extra_xml;
858                 _extra_xml = 0;
859         }
860
861         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
862                 
863                 XMLNode *child;
864                 
865                 child = (*niter);
866                 
867                 if (child->name () == "extra") {
868                         _extra_xml = new XMLNode (*child);
869                         break;
870                 }
871         }
872
873         if (send) {
874                 send_change (what_changed);
875         }
876
877         return 0;
878 }
879
880 int
881 Region::set_state (const XMLNode& node)
882 {
883         const XMLProperty *prop;
884         Change what_changed = Change (0);
885
886         /* ID is not allowed to change, ever */
887
888         if ((prop = node.property ("id")) == 0) {
889                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
890                 return -1;
891         }
892
893         _id = prop->value();
894         
895         _first_edit = EditChangesNothing;
896         
897         set_live_state (node, what_changed, true);
898
899         return 0;
900 }
901
902 void
903 Region::freeze ()
904 {
905         _frozen++;
906 }
907
908 void
909 Region::thaw (const string& why)
910 {
911         Change what_changed = Change (0);
912
913         {
914                 Glib::Mutex::Lock lm (lock);
915
916                 if (_frozen && --_frozen > 0) {
917                         return;
918                 }
919
920                 if (pending_changed) {
921                         what_changed = pending_changed;
922                         pending_changed = Change (0);
923                 }
924         }
925
926         if (what_changed == Change (0)) {
927                 return;
928         }
929
930         if (what_changed & LengthChanged) {
931                 if (what_changed & PositionChanged) {
932                         recompute_at_start ();
933                 } 
934                 recompute_at_end ();
935         }
936                 
937         StateChanged (what_changed);
938 }
939
940 void
941 Region::send_change (Change what_changed)
942 {
943         {
944                 Glib::Mutex::Lock lm (lock);
945                 if (_frozen) {
946                         pending_changed = Change (pending_changed|what_changed);
947                         return;
948                 } 
949         }
950
951         StateChanged (what_changed);
952 }
953
954 void
955 Region::set_last_layer_op (uint64_t when)
956 {
957         _last_layer_op = when;
958 }
959
960 bool
961 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
962 {
963         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
964 }
965
966 bool
967 Region::equivalent (boost::shared_ptr<const Region> other) const
968 {
969         return _start == other->_start &&
970                 _position == other->_position &&
971                 _length == other->_length;
972 }
973
974 bool
975 Region::size_equivalent (boost::shared_ptr<const Region> other) const
976 {
977         return _start == other->_start &&
978                 _length == other->_length;
979 }
980
981 bool
982 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
983 {
984         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
985 }