make LADSPA and LV2 plugins pay attention to "offset" in connect_and_run, again ...
[ardour2.git] / libs / ardour / region.cc
blob37b10d6752e32c231eff081c9c00a8eb7a140eae
1 /*
2 Copyright (C) 2000-2003 Paul Davis
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.
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.
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.
20 #include <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24 #include <sstream>
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31 #include <pbd/stacktrace.h>
32 #include <pbd/enumwriter.h>
34 #include <ardour/region.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/tempo.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/profile.h>
41 #include "i18n.h"
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
47 Change Region::FadeChanged = ARDOUR::new_change ();
48 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
49 Change Region::MuteChanged = ARDOUR::new_change ();
50 Change Region::OpacityChanged = ARDOUR::new_change ();
51 Change Region::LockChanged = ARDOUR::new_change ();
52 Change Region::LayerChanged = ARDOUR::new_change ();
53 Change Region::HiddenChanged = ARDOUR::new_change ();
55 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
57 /* basic Region constructor */
59 _flags = flags;
60 _read_data_count = 0;
61 _frozen = 0;
62 pending_changed = Change (0);
63 valid_transients = false;
64 _name = name;
65 _start = start;
66 _sync_position = _start;
67 _length = length;
68 _last_length = length;
69 _ancestral_start = 0; // see comments in various libs/ardour/*_effect.cc
70 _ancestral_length = 0; // ditto
71 _stretch = 1.0;
72 _shift = 1.0;
73 _last_position = 0;
74 _position = 0;
75 _layer = layer;
76 _read_data_count = 0;
77 _first_edit = EditChangesNothing;
78 _last_layer_op = 0;
79 _positional_lock_style = AudioTime;
82 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
84 /* create a new Region from part of an existing one */
86 _start = other->_start + offset;
88 copy_stuff (other, offset, length, name, layer, flags);
90 /* if the other region had a distinct sync point
91 set, then continue to use it as best we can.
92 otherwise, reset sync point back to start.
95 if (other->flags() & SyncMarked) {
96 if (other->_sync_position < _start) {
97 _flags = Flag (_flags & ~SyncMarked);
98 _sync_position = _start;
99 } else {
100 _sync_position = other->_sync_position;
102 } else {
103 _flags = Flag (_flags & ~SyncMarked);
104 _sync_position = _start;
107 if (Profile->get_sae()) {
108 /* reset sync point to start if its ended up
109 outside region bounds.
112 if (_sync_position < _start || _sync_position >= _start + _length) {
113 _flags = Flag (_flags & ~SyncMarked);
114 _sync_position = _start;
120 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
122 /* create a new Region exactly like another but starting at 0 in its sources */
124 _start = 0;
125 copy_stuff (other, 0, length, name, layer, flags);
127 /* sync pos is relative to start of file. our start-in-file is now zero,
128 so set our sync position to whatever the the difference between
129 _start and _sync_pos was in the other region.
131 result is that our new sync pos points to the same point in our source(s)
132 as the sync in the other region did in its source(s).
134 since we start at zero in our source(s), it is not possible to use a sync point that
135 is before the start. reset it to _start if that was true in the other region.
138 if (other->flags() & SyncMarked) {
139 if (other->_start < other->_sync_position) {
140 /* sync pos was after the start point of the other region */
141 _sync_position = other->_sync_position - other->_start;
142 } else {
143 /* sync pos was before the start point of the other region. not possible here. */
144 _flags = Flag (_flags & ~SyncMarked);
145 _sync_position = _start;
147 } else {
148 _flags = Flag (_flags & ~SyncMarked);
149 _sync_position = _start;
152 if (Profile->get_sae()) {
153 /* reset sync point to start if its ended up
154 outside region bounds.
157 if (_sync_position < _start || _sync_position >= _start + _length) {
158 _flags = Flag (_flags & ~SyncMarked);
159 _sync_position = _start;
163 /* reset a couple of things that copy_stuff() gets wrong in this particular case */
165 _positional_lock_style = other->_positional_lock_style;
166 _first_edit = other->_first_edit;
169 void
170 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
172 _frozen = 0;
173 pending_changed = Change (0);
174 _read_data_count = 0;
175 valid_transients = false;
177 _length = length;
178 _last_length = length;
179 _sync_position = other->_sync_position;
180 _ancestral_start = other->_ancestral_start;
181 _ancestral_length = other->_ancestral_length;
182 _stretch = other->_stretch;
183 _shift = other->_shift;
184 _name = name;
185 _last_position = 0;
186 _position = 0;
187 _layer = layer;
188 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
189 _first_edit = EditChangesNothing;
190 _last_layer_op = 0;
191 _positional_lock_style = AudioTime;
194 Region::Region (boost::shared_ptr<const Region> other)
196 /* Pure copy constructor */
198 _frozen = 0;
199 pending_changed = Change (0);
200 _read_data_count = 0;
201 valid_transients = false;
203 _first_edit = EditChangesID;
204 other->_first_edit = EditChangesName;
206 if (other->_extra_xml) {
207 _extra_xml = new XMLNode (*other->_extra_xml);
208 } else {
209 _extra_xml = 0;
212 _start = other->_start;
213 _sync_position = other->_sync_position;
214 _length = other->_length;
215 _last_length = other->_length;
216 _ancestral_start = other->_ancestral_start;
217 _ancestral_length = other->_ancestral_length;
218 _stretch = other->_stretch;
219 _shift = other->_shift;
220 _name = other->_name;
221 _last_position = other->_position;
222 _position = other->_position;
223 _layer = other->_layer;
224 _flags = Flag (other->_flags & ~Locked);
225 _last_layer_op = other->_last_layer_op;
226 _positional_lock_style = AudioTime;
229 Region::Region (const XMLNode& node)
231 _frozen = 0;
232 pending_changed = Change (0);
233 valid_transients = false;
234 _read_data_count = 0;
235 _start = 0;
236 _sync_position = _start;
237 _length = 0;
238 _last_length = 0;
239 _name = X_("error: XML did not reset this");
240 _last_position = 0;
241 _position = 0;
242 _layer = 0;
243 _flags = Flag (0);
244 _first_edit = EditChangesNothing;
245 _positional_lock_style = AudioTime;
247 if (set_state (node)) {
248 throw failed_constructor();
252 Region::~Region ()
254 /* derived classes must call notify_callbacks() and then emit GoingAway */
257 void
258 Region::set_playlist (boost::weak_ptr<Playlist> pl)
260 _playlist = pl;
263 void
264 Region::set_name (string str)
266 if (_name != str) {
267 _name = str;
268 send_change (NameChanged);
272 void
273 Region::set_length (nframes_t len, void *src)
275 //cerr << "Region::set_length() len = " << len << endl;
276 if (_flags & Locked) {
277 return;
280 if (_length != len && len != 0) {
282 /* check that the current _position wouldn't make the new
283 length impossible.
286 if (max_frames - len < _position) {
287 return;
290 if (!verify_length (len)) {
291 return;
295 _last_length = _length;
296 _length = len;
298 _flags = Region::Flag (_flags & ~WholeFile);
300 first_edit ();
301 maybe_uncopy ();
302 invalidate_transients ();
304 if (!_frozen) {
305 recompute_at_end ();
308 send_change (LengthChanged);
312 void
313 Region::maybe_uncopy ()
317 void
318 Region::first_edit ()
320 boost::shared_ptr<Playlist> pl (playlist());
322 if (_first_edit != EditChangesNothing && pl) {
324 _name = pl->session().new_region_name (_name);
325 _first_edit = EditChangesNothing;
327 send_change (NameChanged);
328 RegionFactory::CheckNewRegion (shared_from_this());
332 bool
333 Region::at_natural_position () const
335 boost::shared_ptr<Playlist> pl (playlist());
337 if (!pl) {
338 return false;
341 boost::shared_ptr<Region> whole_file_region = get_parent();
343 if (whole_file_region) {
344 if (_position == whole_file_region->position() + _start) {
345 return true;
349 return false;
352 void
353 Region::move_to_natural_position (void *src)
355 boost::shared_ptr<Playlist> pl (playlist());
357 if (!pl) {
358 return;
361 boost::shared_ptr<Region> whole_file_region = get_parent();
363 if (whole_file_region) {
364 set_position (whole_file_region->position() + _start, src);
368 void
369 Region::special_set_position (nframes_t pos)
371 /* this is used when creating a whole file region as
372 a way to store its "natural" or "captured" position.
375 _position = _position;
376 _position = pos;
379 void
380 Region::set_position_lock_style (PositionLockStyle ps)
382 boost::shared_ptr<Playlist> pl (playlist());
384 if (!pl) {
385 return;
388 _positional_lock_style = ps;
390 if (_positional_lock_style == MusicTime) {
391 pl->session().tempo_map().bbt_time (_position, _bbt_time);
396 void
397 Region::update_position_after_tempo_map_change ()
399 boost::shared_ptr<Playlist> pl (playlist());
401 if (!pl || _positional_lock_style != MusicTime) {
402 return;
405 TempoMap& map (pl->session().tempo_map());
406 nframes_t pos = map.frame_time (_bbt_time);
407 set_position_internal (pos, false);
410 void
411 Region::set_position (nframes_t pos, void *src)
413 if (_flags & Locked) {
414 return;
417 set_position_internal (pos, true);
420 void
421 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
423 if (_position != pos) {
424 _last_position = _position;
425 _position = pos;
427 /* check that the new _position wouldn't make the current
428 length impossible - if so, change the length.
430 XXX is this the right thing to do?
433 if (max_frames - _length < _position) {
434 _last_length = _length;
435 _length = max_frames - _position;
438 if (allow_bbt_recompute) {
439 recompute_position_from_lock_style ();
442 invalidate_transients ();
445 /* do this even if the position is the same. this helps out
446 a GUI that has moved its representation already.
449 send_change (PositionChanged);
452 void
453 Region::set_position_on_top (nframes_t pos, void *src)
455 if (_flags & Locked) {
456 return;
459 if (_position != pos) {
460 _last_position = _position;
461 _position = pos;
464 boost::shared_ptr<Playlist> pl (playlist());
466 if (pl) {
467 pl->raise_region_to_top (shared_from_this ());
470 /* do this even if the position is the same. this helps out
471 a GUI that has moved its representation already.
474 send_change (PositionChanged);
477 void
478 Region::recompute_position_from_lock_style ()
480 if (_positional_lock_style == MusicTime) {
481 boost::shared_ptr<Playlist> pl (playlist());
482 if (pl) {
483 pl->session().tempo_map().bbt_time (_position, _bbt_time);
488 void
489 Region::nudge_position (nframes64_t n, void *src)
491 if (_flags & Locked) {
492 return;
495 if (n == 0) {
496 return;
499 _last_position = _position;
501 if (n > 0) {
502 if (_position > max_frames - n) {
503 _position = max_frames;
504 } else {
505 _position += n;
507 } else {
508 if (_position < (nframes_t) -n) {
509 _position = 0;
510 } else {
511 _position += n;
515 send_change (PositionChanged);
518 void
519 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
521 _ancestral_length = l;
522 _ancestral_start = s;
523 _stretch = st;
524 _shift = sh;
527 void
528 Region::set_start (nframes_t pos, void *src)
530 if (_flags & Locked) {
531 return;
533 /* This just sets the start, nothing else. It effectively shifts
534 the contents of the Region within the overall extent of the Source,
535 without changing the Region's position or length
538 if (_start != pos) {
540 if (!verify_start (pos)) {
541 return;
544 _start = pos;
545 _flags = Region::Flag (_flags & ~WholeFile);
546 first_edit ();
547 invalidate_transients ();
549 send_change (StartChanged);
553 void
554 Region::trim_start (nframes_t new_position, void *src)
556 if (_flags & Locked) {
557 return;
559 nframes_t new_start;
560 int32_t start_shift;
562 if (new_position > _position) {
563 start_shift = new_position - _position;
564 } else {
565 start_shift = -(_position - new_position);
568 if (start_shift > 0) {
570 if (_start > max_frames - start_shift) {
571 new_start = max_frames;
572 } else {
573 new_start = _start + start_shift;
576 if (!verify_start (new_start)) {
577 return;
580 } else if (start_shift < 0) {
582 if (_start < (nframes_t) -start_shift) {
583 new_start = 0;
584 } else {
585 new_start = _start + start_shift;
587 } else {
588 return;
591 if (new_start == _start) {
592 return;
595 _start = new_start;
596 _flags = Region::Flag (_flags & ~WholeFile);
597 first_edit ();
599 send_change (StartChanged);
602 void
603 Region::trim_front (nframes_t new_position, void *src)
605 if (_flags & Locked) {
606 return;
609 nframes_t end = last_frame();
610 nframes_t source_zero;
612 if (_position > _start) {
613 source_zero = _position - _start;
614 } else {
615 source_zero = 0; // its actually negative, but this will work for us
618 if (new_position < end) { /* can't trim it zero or negative length */
620 nframes_t newlen;
622 /* can't trim it back passed where source position zero is located */
624 new_position = max (new_position, source_zero);
627 if (new_position > _position) {
628 newlen = _length - (new_position - _position);
629 } else {
630 newlen = _length + (_position - new_position);
633 trim_to_internal (new_position, newlen, src);
634 if (!_frozen) {
635 recompute_at_start ();
640 void
641 Region::trim_end (nframes_t new_endpoint, void *src)
643 if (_flags & Locked) {
644 return;
647 if (new_endpoint > _position) {
648 trim_to_internal (_position, new_endpoint - _position, this);
649 if (!_frozen) {
650 recompute_at_end ();
655 void
656 Region::trim_to (nframes_t position, nframes_t length, void *src)
658 if (_flags & Locked) {
659 return;
662 trim_to_internal (position, length, src);
664 if (!_frozen) {
665 recompute_at_start ();
666 recompute_at_end ();
670 void
671 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
673 int32_t start_shift;
674 nframes_t new_start;
676 if (_flags & Locked) {
677 return;
680 if (position > _position) {
681 start_shift = position - _position;
682 } else {
683 start_shift = -(_position - position);
686 if (start_shift > 0) {
688 if (_start > max_frames - start_shift) {
689 new_start = max_frames;
690 } else {
691 new_start = _start + start_shift;
695 } else if (start_shift < 0) {
697 if (_start < (nframes_t) -start_shift) {
698 new_start = 0;
699 } else {
700 new_start = _start + start_shift;
702 } else {
703 new_start = _start;
706 if (!verify_start_and_length (new_start, length)) {
707 return;
710 Change what_changed = Change (0);
712 if (_start != new_start) {
713 _start = new_start;
714 what_changed = Change (what_changed|StartChanged);
716 if (_length != length) {
717 if (!_frozen) {
718 _last_length = _length;
720 _length = length;
721 what_changed = Change (what_changed|LengthChanged);
723 if (_position != position) {
724 if (!_frozen) {
725 _last_position = _position;
727 _position = position;
728 what_changed = Change (what_changed|PositionChanged);
731 _flags = Region::Flag (_flags & ~WholeFile);
733 if (what_changed & (StartChanged|LengthChanged)) {
734 first_edit ();
737 if (what_changed) {
738 send_change (what_changed);
742 void
743 Region::set_hidden (bool yn)
745 if (hidden() != yn) {
747 if (yn) {
748 _flags = Flag (_flags|Hidden);
749 } else {
750 _flags = Flag (_flags & ~Hidden);
753 send_change (HiddenChanged);
757 void
758 Region::set_muted (bool yn)
760 if (muted() != yn) {
762 if (yn) {
763 _flags = Flag (_flags|Muted);
764 } else {
765 _flags = Flag (_flags & ~Muted);
768 send_change (MuteChanged);
772 void
773 Region::set_opaque (bool yn)
775 if (opaque() != yn) {
776 if (yn) {
777 _flags = Flag (_flags|Opaque);
778 } else {
779 _flags = Flag (_flags & ~Opaque);
781 send_change (OpacityChanged);
785 void
786 Region::set_locked (bool yn)
788 if (locked() != yn) {
789 if (yn) {
790 _flags = Flag (_flags|Locked);
791 } else {
792 _flags = Flag (_flags & ~Locked);
794 send_change (LockChanged);
798 void
799 Region::set_sync_position (nframes_t absolute_pos)
801 nframes_t file_pos;
803 file_pos = _start + (absolute_pos - _position);
805 if (file_pos != _sync_position) {
807 _sync_position = file_pos;
808 _flags = Flag (_flags|SyncMarked);
810 if (!_frozen) {
811 maybe_uncopy ();
813 send_change (SyncOffsetChanged);
817 void
818 Region::clear_sync_position ()
820 if (_flags & SyncMarked) {
821 _flags = Flag (_flags & ~SyncMarked);
823 if (!_frozen) {
824 maybe_uncopy ();
826 send_change (SyncOffsetChanged);
830 nframes_t
831 Region::sync_offset (int& dir) const
833 /* returns the sync point relative the first frame of the region */
835 if (_flags & SyncMarked) {
836 if (_sync_position > _start) {
837 dir = 1;
838 return _sync_position - _start;
839 } else {
840 dir = -1;
841 return _start - _sync_position;
843 } else {
844 dir = 0;
845 return 0;
849 nframes_t
850 Region::adjust_to_sync (nframes_t pos) const
852 int sync_dir;
853 nframes_t offset = sync_offset (sync_dir);
855 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
857 if (sync_dir > 0) {
858 if (pos > offset) {
859 pos -= offset;
860 } else {
861 pos = 0;
863 } else {
864 if (max_frames - pos > offset) {
865 pos += offset;
869 return pos;
872 nframes_t
873 Region::sync_position() const
875 if (_flags & SyncMarked) {
876 return _sync_position;
877 } else {
878 return _start;
883 void
884 Region::raise ()
886 boost::shared_ptr<Playlist> pl (playlist());
887 if (pl) {
888 pl->raise_region (shared_from_this ());
892 void
893 Region::lower ()
895 boost::shared_ptr<Playlist> pl (playlist());
896 if (pl) {
897 pl->lower_region (shared_from_this ());
901 void
902 Region::raise_to_top ()
904 boost::shared_ptr<Playlist> pl (playlist());
905 if (pl) {
906 pl->raise_region_to_top (shared_from_this());
910 void
911 Region::lower_to_bottom ()
913 boost::shared_ptr<Playlist> pl (playlist());
914 if (pl) {
915 pl->lower_region_to_bottom (shared_from_this());
919 void
920 Region::set_layer (layer_t l)
922 if (_layer != l) {
923 _layer = l;
925 send_change (LayerChanged);
929 XMLNode&
930 Region::state (bool full_state)
932 XMLNode *node = new XMLNode ("Region");
933 char buf[64];
934 const char* fe = NULL;
936 _id.print (buf, sizeof (buf));
937 node->add_property ("id", buf);
938 node->add_property ("name", _name);
939 snprintf (buf, sizeof (buf), "%u", _start);
940 node->add_property ("start", buf);
941 snprintf (buf, sizeof (buf), "%u", _length);
942 node->add_property ("length", buf);
943 snprintf (buf, sizeof (buf), "%u", _position);
944 node->add_property ("position", buf);
945 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
946 node->add_property ("ancestral-start", buf);
947 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
948 node->add_property ("ancestral-length", buf);
949 snprintf (buf, sizeof (buf), "%.12g", _stretch);
950 node->add_property ("stretch", buf);
951 snprintf (buf, sizeof (buf), "%.12g", _shift);
952 node->add_property ("shift", buf);
954 switch (_first_edit) {
955 case EditChangesNothing:
956 fe = X_("nothing");
957 break;
958 case EditChangesName:
959 fe = X_("name");
960 break;
961 case EditChangesID:
962 fe = X_("id");
963 break;
964 default: /* should be unreachable but makes g++ happy */
965 fe = X_("nothing");
966 break;
969 node->add_property ("first_edit", fe);
971 /* note: flags are stored by derived classes */
973 snprintf (buf, sizeof (buf), "%d", (int) _layer);
974 node->add_property ("layer", buf);
975 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
976 node->add_property ("sync-position", buf);
978 if (_positional_lock_style != AudioTime) {
979 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
980 stringstream str;
981 str << _bbt_time;
982 node->add_property ("bbt-position", str.str());
985 return *node;
988 XMLNode&
989 Region::get_state ()
991 return state (true);
995 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
997 const XMLNodeList& nlist = node.children();
998 const XMLProperty *prop;
999 nframes_t val;
1001 /* this is responsible for setting those aspects of Region state
1002 that are mutable after construction.
1005 if ((prop = node.property ("name")) == 0) {
1006 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1007 return -1;
1010 _name = prop->value();
1012 if ((prop = node.property ("start")) != 0) {
1013 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1014 if (val != _start) {
1015 what_changed = Change (what_changed|StartChanged);
1016 _start = val;
1018 } else {
1019 _start = 0;
1022 if ((prop = node.property ("length")) != 0) {
1023 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1024 if (val != _length) {
1025 what_changed = Change (what_changed|LengthChanged);
1026 _last_length = _length;
1027 _length = val;
1029 } else {
1030 _last_length = _length;
1031 _length = 1;
1034 if ((prop = node.property ("position")) != 0) {
1035 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1036 if (val != _position) {
1037 what_changed = Change (what_changed|PositionChanged);
1038 _last_position = _position;
1039 _position = val;
1041 } else {
1042 _last_position = _position;
1043 _position = 0;
1046 if ((prop = node.property ("layer")) != 0) {
1047 layer_t x;
1048 x = (layer_t) atoi (prop->value().c_str());
1049 if (x != _layer) {
1050 what_changed = Change (what_changed|LayerChanged);
1051 _layer = x;
1053 } else {
1054 _layer = 0;
1057 if ((prop = node.property ("sync-position")) != 0) {
1058 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1059 if (val != _sync_position) {
1060 what_changed = Change (what_changed|SyncOffsetChanged);
1061 _sync_position = val;
1063 } else {
1064 _sync_position = _start;
1067 if ((prop = node.property ("positional-lock-style")) != 0) {
1068 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1070 if (_positional_lock_style == MusicTime) {
1071 if ((prop = node.property ("bbt-position")) == 0) {
1072 /* missing BBT info, revert to audio time locking */
1073 _positional_lock_style = AudioTime;
1074 } else {
1075 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1076 &_bbt_time.bars,
1077 &_bbt_time.beats,
1078 &_bbt_time.ticks) != 3) {
1079 _positional_lock_style = AudioTime;
1084 } else {
1085 _positional_lock_style = AudioTime;
1088 /* XXX FIRST EDIT !!! */
1090 /* these 3 properties never change as a result of any editing */
1092 if ((prop = node.property ("ancestral-start")) != 0) {
1093 _ancestral_start = atoi (prop->value());
1094 } else {
1095 _ancestral_start = _start;
1098 if ((prop = node.property ("ancestral-length")) != 0) {
1099 _ancestral_length = atoi (prop->value());
1100 } else {
1101 _ancestral_length = _length;
1104 if ((prop = node.property ("stretch")) != 0) {
1105 _stretch = atof (prop->value());
1106 /* fix problem with old sessions corrupted by an impossible
1107 value for _stretch
1109 if (_stretch == 0.0) {
1110 _stretch = 1.0;
1112 } else {
1113 _stretch = 1.0;
1116 if ((prop = node.property ("shift")) != 0) {
1117 _shift = atof (prop->value());
1118 /* fix problem with old sessions corrupted by an impossible
1119 value for _shift
1121 if (_shift == 0.0) {
1122 _shift = 1.0;
1124 } else {
1125 _shift = 1.0;
1128 /* note: derived classes set flags */
1130 if (_extra_xml) {
1131 delete _extra_xml;
1132 _extra_xml = 0;
1135 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1137 XMLNode *child;
1139 child = (*niter);
1141 if (child->name () == "extra") {
1142 _extra_xml = new XMLNode (*child);
1143 break;
1147 if (send) {
1148 send_change (what_changed);
1151 return 0;
1155 Region::set_state (const XMLNode& node)
1157 const XMLProperty *prop;
1158 Change what_changed = Change (0);
1160 /* ID is not allowed to change, ever */
1162 if ((prop = node.property ("id")) == 0) {
1163 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1164 return -1;
1167 _id = prop->value();
1169 _first_edit = EditChangesNothing;
1171 set_live_state (node, what_changed, true);
1173 return 0;
1176 void
1177 Region::freeze ()
1179 _frozen++;
1180 _last_length = _length;
1181 _last_position = _position;
1184 void
1185 Region::thaw (const string& why)
1187 Change what_changed = Change (0);
1190 Glib::Mutex::Lock lm (lock);
1192 if (_frozen && --_frozen > 0) {
1193 return;
1196 if (pending_changed) {
1197 what_changed = pending_changed;
1198 pending_changed = Change (0);
1202 if (what_changed == Change (0)) {
1203 return;
1206 if (what_changed & LengthChanged) {
1207 if (what_changed & PositionChanged) {
1208 recompute_at_start ();
1210 recompute_at_end ();
1213 StateChanged (what_changed);
1216 void
1217 Region::send_change (Change what_changed)
1220 Glib::Mutex::Lock lm (lock);
1221 if (_frozen) {
1222 pending_changed = Change (pending_changed|what_changed);
1223 return;
1227 StateChanged (what_changed);
1230 void
1231 Region::set_last_layer_op (uint64_t when)
1233 _last_layer_op = when;
1236 bool
1237 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1239 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1242 bool
1243 Region::equivalent (boost::shared_ptr<const Region> other) const
1245 return _start == other->_start &&
1246 _position == other->_position &&
1247 _length == other->_length;
1250 bool
1251 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1253 return _start == other->_start &&
1254 _length == other->_length;
1257 bool
1258 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1260 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1263 void
1264 Region::invalidate_transients ()
1266 valid_transients = false;
1267 _transients.clear ();