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.
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/source.h>
38 #include <ardour/tempo.h>
39 #include <ardour/region_factory.h>
40 #include <ardour/filter.h>
45 using namespace ARDOUR;
48 Change Region::FadeChanged = ARDOUR::new_change ();
49 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
50 Change Region::MuteChanged = ARDOUR::new_change ();
51 Change Region::OpacityChanged = ARDOUR::new_change ();
52 Change Region::LockChanged = ARDOUR::new_change ();
53 Change Region::LayerChanged = ARDOUR::new_change ();
54 Change Region::HiddenChanged = ARDOUR::new_change ();
57 /* derived-from-derived constructor (no sources in constructor) */
58 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
59 : Automatable(s, name)
66 , _positional_lock_style(AudioTime)
67 , _sync_position(_start)
69 , _first_edit(EditChangesNothing)
72 , _pending_changed(Change (0))
75 /* no sources at this point */
78 /** Basic Region constructor (single source) */
79 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
80 : Automatable(src->session(), name)
87 , _positional_lock_style(AudioTime)
88 , _sync_position(_start)
90 , _first_edit(EditChangesNothing)
92 , _ancestral_start (start)
93 , _ancestral_length (length)
96 , _valid_transients(false)
98 , _pending_changed(Change (0))
101 _sources.push_back (src);
102 _master_sources.push_back (src);
104 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
106 assert(_sources.size() > 0);
107 _positional_lock_style = AudioTime;
110 /** Basic Region constructor (many sources) */
111 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
112 : Automatable(srcs.front()->session(), name)
119 , _positional_lock_style(AudioTime)
120 , _sync_position(_start)
122 , _first_edit(EditChangesNothing)
124 , _read_data_count(0)
125 , _pending_changed(Change (0))
129 set<boost::shared_ptr<Source> > unique_srcs;
131 for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
132 _sources.push_back (*i);
133 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
134 unique_srcs.insert (*i);
137 for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
138 _master_sources.push_back (*i);
139 if (unique_srcs.find (*i) == unique_srcs.end()) {
140 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
144 assert(_sources.size() > 0);
147 /** Create a new Region from part of an existing one */
148 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
149 : Automatable(other->session(), name)
150 , _type(other->data_type())
151 , _flags(Flag(flags & ~(Locked|PositionLocked|WholeFile|Hidden)))
152 , _start(other->_start + offset)
156 , _positional_lock_style(other->_positional_lock_style)
157 , _sync_position(_start)
159 , _first_edit(EditChangesNothing)
161 , _ancestral_start (other->_ancestral_start + offset)
162 , _ancestral_length (length)
165 , _valid_transients(false)
166 , _read_data_count(0)
167 , _pending_changed(Change (0))
170 if (other->_sync_position < offset)
171 _sync_position = other->_sync_position;
173 set<boost::shared_ptr<Source> > unique_srcs;
175 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
176 _sources.push_back (*i);
177 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
178 unique_srcs.insert (*i);
181 if (other->_sync_position < offset) {
182 _sync_position = other->_sync_position;
185 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
186 if (unique_srcs.find (*i) == unique_srcs.end()) {
187 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
189 _master_sources.push_back (*i);
192 assert(_sources.size() > 0);
195 /** Pure copy constructor */
196 Region::Region (boost::shared_ptr<const Region> other)
197 : Automatable(other->session(), other->name())
198 , _type(other->data_type())
199 , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
200 , _start(other->_start)
201 , _length(other->_length)
202 , _position(other->_position)
203 , _last_position(other->_last_position)
204 , _positional_lock_style(other->_positional_lock_style)
205 , _sync_position(other->_sync_position)
206 , _layer(other->_layer)
207 , _first_edit(EditChangesID)
209 , _ancestral_start (_start)
210 , _ancestral_length (_length)
213 , _valid_transients(false)
214 , _read_data_count(0)
215 , _pending_changed(Change(0))
216 , _last_layer_op(other->_last_layer_op)
218 other->_first_edit = EditChangesName;
220 if (other->_extra_xml) {
221 _extra_xml = new XMLNode (*other->_extra_xml);
226 set<boost::shared_ptr<Source> > unique_srcs;
228 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
229 _sources.push_back (*i);
230 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
231 unique_srcs.insert (*i);
234 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
235 _master_sources.push_back (*i);
236 if (unique_srcs.find (*i) == unique_srcs.end()) {
237 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
241 assert(_sources.size() > 0);
244 Region::Region (const SourceList& srcs, const XMLNode& node)
245 : Automatable(srcs.front()->session(), X_("error: XML did not reset this"))
246 , _type(DataType::NIL) // to be loaded from XML
252 , _positional_lock_style(AudioTime)
253 , _sync_position(_start)
255 , _first_edit(EditChangesNothing)
257 , _read_data_count(0)
258 , _pending_changed(Change(0))
261 set<boost::shared_ptr<Source> > unique_srcs;
263 for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
264 _sources.push_back (*i);
265 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
266 unique_srcs.insert (*i);
269 for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
270 _master_sources.push_back (*i);
271 if (unique_srcs.find (*i) == unique_srcs.end()) {
272 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
276 if (set_state (node)) {
277 throw failed_constructor();
280 assert(_type != DataType::NIL);
281 assert(_sources.size() > 0);
284 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
285 : Automatable(src->session(), X_("error: XML did not reset this"))
286 , _type(DataType::NIL)
292 , _positional_lock_style(AudioTime)
293 , _sync_position(_start)
295 , _first_edit(EditChangesNothing)
297 , _read_data_count(0)
298 , _pending_changed(Change(0))
301 _sources.push_back (src);
303 if (set_state (node)) {
304 throw failed_constructor();
307 assert(_type != DataType::NIL);
308 assert(_sources.size() > 0);
313 boost::shared_ptr<Playlist> pl (playlist());
316 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
317 (*i)->remove_playlist (pl);
319 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
320 (*i)->remove_playlist (pl);
325 GoingAway (); /* EMIT SIGNAL */
329 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
331 boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
333 boost::shared_ptr<Playlist> pl (wpl.lock());
335 if (old_playlist == pl) {
343 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
344 (*i)->remove_playlist (_playlist);
345 (*i)->add_playlist (pl);
347 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
348 (*i)->remove_playlist (_playlist);
349 (*i)->add_playlist (pl);
352 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
353 (*i)->add_playlist (pl);
355 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
356 (*i)->add_playlist (pl);
361 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
362 (*i)->remove_playlist (old_playlist);
364 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
365 (*i)->remove_playlist (old_playlist);
372 Region::set_name (const std::string& str)
375 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
376 assert(_name == str);
377 send_change (ARDOUR::NameChanged);
384 Region::set_length (nframes_t len, void *src)
386 if (_flags & Locked) {
390 if (_length != len && len != 0) {
392 /* check that the current _position wouldn't make the new
396 if (max_frames - len < _position) {
400 if (!verify_length (len)) {
405 _last_length = _length;
408 _flags = Region::Flag (_flags & ~WholeFile);
412 invalidate_transients ();
418 send_change (LengthChanged);
423 Region::maybe_uncopy ()
428 Region::first_edit ()
430 boost::shared_ptr<Playlist> pl (playlist());
432 if (_first_edit != EditChangesNothing && pl) {
434 _name = pl->session().new_region_name (_name);
435 _first_edit = EditChangesNothing;
437 send_change (ARDOUR::NameChanged);
438 RegionFactory::CheckNewRegion (shared_from_this());
443 Region::at_natural_position () const
445 boost::shared_ptr<Playlist> pl (playlist());
451 boost::shared_ptr<Region> whole_file_region = get_parent();
453 if (whole_file_region) {
454 if (_position == whole_file_region->position() + _start) {
463 Region::move_to_natural_position (void *src)
465 boost::shared_ptr<Playlist> pl (playlist());
471 boost::shared_ptr<Region> whole_file_region = get_parent();
473 if (whole_file_region) {
474 set_position (whole_file_region->position() + _start, src);
479 Region::special_set_position (nframes_t pos)
481 /* this is used when creating a whole file region as
482 a way to store its "natural" or "captured" position.
485 _position = _position;
490 Region::set_position_lock_style (PositionLockStyle ps)
492 boost::shared_ptr<Playlist> pl (playlist());
498 _positional_lock_style = ps;
500 if (_positional_lock_style == MusicTime) {
501 pl->session().tempo_map().bbt_time (_position, _bbt_time);
507 Region::update_position_after_tempo_map_change ()
509 boost::shared_ptr<Playlist> pl (playlist());
511 if (!pl || _positional_lock_style != MusicTime) {
515 TempoMap& map (pl->session().tempo_map());
516 nframes_t pos = map.frame_time (_bbt_time);
517 set_position_internal (pos, false);
521 Region::set_position (nframes_t pos, void *src)
527 set_position_internal (pos, true);
531 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
533 if (_position != pos) {
534 _last_position = _position;
537 /* check that the new _position wouldn't make the current
538 length impossible - if so, change the length.
540 XXX is this the right thing to do?
543 if (max_frames - _length < _position) {
544 _last_length = _length;
545 _length = max_frames - _position;
548 if (allow_bbt_recompute) {
549 recompute_position_from_lock_style ();
552 invalidate_transients ();
555 /* do this even if the position is the same. this helps out
556 a GUI that has moved its representation already.
559 send_change (PositionChanged);
563 Region::set_position_on_top (nframes_t pos, void *src)
565 if (_flags & Locked) {
569 if (_position != pos) {
570 _last_position = _position;
574 boost::shared_ptr<Playlist> pl (playlist());
577 pl->raise_region_to_top (shared_from_this ());
580 /* do this even if the position is the same. this helps out
581 a GUI that has moved its representation already.
584 send_change (PositionChanged);
588 Region::recompute_position_from_lock_style ()
590 if (_positional_lock_style == MusicTime) {
591 boost::shared_ptr<Playlist> pl (playlist());
593 pl->session().tempo_map().bbt_time (_position, _bbt_time);
599 Region::nudge_position (nframes64_t n, void *src)
601 if (_flags & Locked) {
609 _last_position = _position;
612 if (_position > max_frames - n) {
613 _position = max_frames;
618 if (_position < (nframes_t) -n) {
625 send_change (PositionChanged);
629 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
631 _ancestral_length = l;
632 _ancestral_start = s;
638 Region::set_start (nframes_t pos, void *src)
640 if (_flags & (Locked|PositionLocked)) {
643 /* This just sets the start, nothing else. It effectively shifts
644 the contents of the Region within the overall extent of the Source,
645 without changing the Region's position or length
650 if (!verify_start (pos)) {
655 _flags = Region::Flag (_flags & ~WholeFile);
657 invalidate_transients ();
659 send_change (StartChanged);
664 Region::trim_start (nframes_t new_position, void *src)
666 if (_flags & (Locked|PositionLocked)) {
672 if (new_position > _position) {
673 start_shift = new_position - _position;
675 start_shift = -(_position - new_position);
678 if (start_shift > 0) {
680 if (_start > max_frames - start_shift) {
681 new_start = max_frames;
683 new_start = _start + start_shift;
686 if (!verify_start (new_start)) {
690 } else if (start_shift < 0) {
692 if (_start < (nframes_t) -start_shift) {
695 new_start = _start + start_shift;
701 if (new_start == _start) {
706 _flags = Region::Flag (_flags & ~WholeFile);
709 send_change (StartChanged);
713 Region::trim_front (nframes_t new_position, void *src)
715 if (_flags & Locked) {
719 nframes_t end = last_frame();
720 nframes_t source_zero;
722 if (_position > _start) {
723 source_zero = _position - _start;
725 source_zero = 0; // its actually negative, but this will work for us
728 if (new_position < end) { /* can't trim it zero or negative length */
732 /* can't trim it back passed where source position zero is located */
734 new_position = max (new_position, source_zero);
737 if (new_position > _position) {
738 newlen = _length - (new_position - _position);
740 newlen = _length + (_position - new_position);
743 trim_to_internal (new_position, newlen, src);
745 recompute_at_start ();
751 Region::trim_end (nframes_t new_endpoint, void *src)
753 if (_flags & Locked) {
757 if (new_endpoint > _position) {
758 trim_to_internal (_position, new_endpoint - _position, this);
766 Region::trim_to (nframes_t position, nframes_t length, void *src)
768 if (_flags & Locked) {
772 trim_to_internal (position, length, src);
775 recompute_at_start ();
781 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
786 if (_flags & Locked) {
790 if (position > _position) {
791 start_shift = position - _position;
793 start_shift = -(_position - position);
796 if (start_shift > 0) {
798 if (_start > max_frames - start_shift) {
799 new_start = max_frames;
801 new_start = _start + start_shift;
805 } else if (start_shift < 0) {
807 if (_start < (nframes_t) -start_shift) {
810 new_start = _start + start_shift;
816 if (!verify_start_and_length (new_start, length)) {
820 Change what_changed = Change (0);
822 if (_start != new_start) {
824 what_changed = Change (what_changed|StartChanged);
826 if (_length != length) {
828 _last_length = _length;
831 what_changed = Change (what_changed|LengthChanged);
833 if (_position != position) {
835 _last_position = _position;
837 _position = position;
838 what_changed = Change (what_changed|PositionChanged);
841 _flags = Region::Flag (_flags & ~WholeFile);
843 if (what_changed & (StartChanged|LengthChanged)) {
848 send_change (what_changed);
853 Region::set_hidden (bool yn)
855 if (hidden() != yn) {
858 _flags = Flag (_flags|Hidden);
860 _flags = Flag (_flags & ~Hidden);
863 send_change (HiddenChanged);
868 Region::set_muted (bool yn)
873 _flags = Flag (_flags|Muted);
875 _flags = Flag (_flags & ~Muted);
878 send_change (MuteChanged);
883 Region::set_opaque (bool yn)
885 if (opaque() != yn) {
887 _flags = Flag (_flags|Opaque);
889 _flags = Flag (_flags & ~Opaque);
891 send_change (OpacityChanged);
896 Region::set_locked (bool yn)
898 if (locked() != yn) {
900 _flags = Flag (_flags|Locked);
902 _flags = Flag (_flags & ~Locked);
904 send_change (LockChanged);
909 Region::set_position_locked (bool yn)
911 if (position_locked() != yn) {
913 _flags = Flag (_flags|PositionLocked);
915 _flags = Flag (_flags & ~PositionLocked);
917 send_change (LockChanged);
922 Region::set_sync_position (nframes_t absolute_pos)
926 file_pos = _start + (absolute_pos - _position);
928 if (file_pos != _sync_position) {
930 _sync_position = file_pos;
931 _flags = Flag (_flags|SyncMarked);
936 send_change (SyncOffsetChanged);
941 Region::clear_sync_position ()
943 if (_flags & SyncMarked) {
944 _flags = Flag (_flags & ~SyncMarked);
949 send_change (SyncOffsetChanged);
954 Region::sync_offset (int& dir) const
956 /* returns the sync point relative the first frame of the region */
958 if (_flags & SyncMarked) {
959 if (_sync_position > _start) {
961 return _sync_position - _start;
964 return _start - _sync_position;
973 Region::adjust_to_sync (nframes_t pos)
976 nframes_t offset = sync_offset (sync_dir);
978 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
987 if (max_frames - pos > offset) {
996 Region::sync_position() const
998 if (_flags & SyncMarked) {
999 return _sync_position;
1008 boost::shared_ptr<Playlist> pl (playlist());
1010 pl->raise_region (shared_from_this ());
1017 boost::shared_ptr<Playlist> pl (playlist());
1019 pl->lower_region (shared_from_this ());
1025 Region::raise_to_top ()
1027 boost::shared_ptr<Playlist> pl (playlist());
1029 pl->raise_region_to_top (shared_from_this());
1034 Region::lower_to_bottom ()
1036 boost::shared_ptr<Playlist> pl (playlist());
1038 pl->lower_region_to_bottom (shared_from_this());
1043 Region::set_layer (layer_t l)
1048 send_change (LayerChanged);
1053 Region::state (bool full_state)
1055 XMLNode *node = new XMLNode ("Region");
1057 const char* fe = NULL;
1059 _id.print (buf, sizeof (buf));
1060 node->add_property ("id", buf);
1061 node->add_property ("name", _name);
1062 node->add_property ("type", _type.to_string());
1063 snprintf (buf, sizeof (buf), "%u", _start);
1064 node->add_property ("start", buf);
1065 snprintf (buf, sizeof (buf), "%u", _length);
1066 node->add_property ("length", buf);
1067 snprintf (buf, sizeof (buf), "%u", _position);
1068 node->add_property ("position", buf);
1069 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1070 node->add_property ("ancestral-start", buf);
1071 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1072 node->add_property ("ancestral-length", buf);
1073 snprintf (buf, sizeof (buf), "%.12g", _stretch);
1074 node->add_property ("stretch", buf);
1075 snprintf (buf, sizeof (buf), "%.12g", _shift);
1076 node->add_property ("shift", buf);
1078 switch (_first_edit) {
1079 case EditChangesNothing:
1082 case EditChangesName:
1088 default: /* should be unreachable but makes g++ happy */
1093 node->add_property ("first_edit", fe);
1095 /* note: flags are stored by derived classes */
1097 snprintf (buf, sizeof (buf), "%d", (int) _layer);
1098 node->add_property ("layer", buf);
1099 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1100 node->add_property ("sync-position", buf);
1102 if (_positional_lock_style != AudioTime) {
1103 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1106 node->add_property ("bbt-position", str.str());
1113 Region::get_state ()
1115 return state (true);
1119 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
1121 const XMLNodeList& nlist = node.children();
1122 const XMLProperty *prop;
1125 /* this is responsible for setting those aspects of Region state
1126 that are mutable after construction.
1129 if ((prop = node.property ("name")) == 0) {
1130 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1134 _name = prop->value();
1136 if ((prop = node.property ("type")) == 0) {
1137 _type = DataType::AUDIO;
1139 _type = DataType(prop->value());
1142 if ((prop = node.property ("start")) != 0) {
1143 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1144 if (val != _start) {
1145 what_changed = Change (what_changed|StartChanged);
1152 if ((prop = node.property ("length")) != 0) {
1153 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1154 if (val != _length) {
1155 what_changed = Change (what_changed|LengthChanged);
1156 _last_length = _length;
1160 _last_length = _length;
1164 if ((prop = node.property ("position")) != 0) {
1165 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1166 if (val != _position) {
1167 what_changed = Change (what_changed|PositionChanged);
1168 _last_position = _position;
1172 _last_position = _position;
1176 if ((prop = node.property ("layer")) != 0) {
1178 x = (layer_t) atoi (prop->value().c_str());
1180 what_changed = Change (what_changed|LayerChanged);
1187 if ((prop = node.property ("sync-position")) != 0) {
1188 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1189 if (val != _sync_position) {
1190 what_changed = Change (what_changed|SyncOffsetChanged);
1191 _sync_position = val;
1194 _sync_position = _start;
1197 if ((prop = node.property ("positional-lock-style")) != 0) {
1198 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1200 if (_positional_lock_style == MusicTime) {
1201 if ((prop = node.property ("bbt-position")) == 0) {
1202 /* missing BBT info, revert to audio time locking */
1203 _positional_lock_style = AudioTime;
1205 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1208 &_bbt_time.ticks) != 3) {
1209 _positional_lock_style = AudioTime;
1215 _positional_lock_style = AudioTime;
1218 /* XXX FIRST EDIT !!! */
1220 /* these 3 properties never change as a result of any editing */
1222 if ((prop = node.property ("ancestral-start")) != 0) {
1223 _ancestral_start = atoi (prop->value());
1225 _ancestral_start = _start;
1228 if ((prop = node.property ("ancestral-length")) != 0) {
1229 _ancestral_length = atoi (prop->value());
1231 _ancestral_length = _length;
1234 if ((prop = node.property ("stretch")) != 0) {
1235 _stretch = atof (prop->value());
1240 if ((prop = node.property ("shift")) != 0) {
1241 _shift = atof (prop->value());
1246 /* note: derived classes set flags */
1253 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1259 if (child->name () == "extra") {
1260 _extra_xml = new XMLNode (*child);
1266 send_change (what_changed);
1273 Region::set_state (const XMLNode& node)
1275 const XMLProperty *prop;
1276 Change what_changed = Change (0);
1278 /* ID is not allowed to change, ever */
1280 if ((prop = node.property ("id")) == 0) {
1281 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1285 _id = prop->value();
1287 _first_edit = EditChangesNothing;
1289 set_live_state (node, what_changed, true);
1298 _last_length = _length;
1299 _last_position = _position;
1303 Region::thaw (const string& why)
1305 Change what_changed = Change (0);
1308 Glib::Mutex::Lock lm (_lock);
1310 if (_frozen && --_frozen > 0) {
1314 if (_pending_changed) {
1315 what_changed = _pending_changed;
1316 _pending_changed = Change (0);
1320 if (what_changed == Change (0)) {
1324 if (what_changed & LengthChanged) {
1325 if (what_changed & PositionChanged) {
1326 recompute_at_start ();
1328 recompute_at_end ();
1331 StateChanged (what_changed);
1335 Region::send_change (Change what_changed)
1338 Glib::Mutex::Lock lm (_lock);
1340 _pending_changed = Change (_pending_changed|what_changed);
1345 StateChanged (what_changed);
1349 Region::set_last_layer_op (uint64_t when)
1351 _last_layer_op = when;
1355 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1357 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1361 Region::equivalent (boost::shared_ptr<const Region> other) const
1363 return _start == other->_start &&
1364 _position == other->_position &&
1365 _length == other->_length;
1369 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1371 return _start == other->_start &&
1372 _length == other->_length;
1376 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1378 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1382 Region::source_deleted (boost::shared_ptr<Source>)
1389 Region::master_source_names ()
1391 SourceList::iterator i;
1393 vector<string> names;
1394 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1395 names.push_back((*i)->name());
1402 Region::set_master_sources (SourceList& srcs)
1404 _master_sources = srcs;
1408 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1413 SourceList::const_iterator i;
1414 SourceList::const_iterator io;
1416 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1417 if ((*i)->id() != (*io)->id()) {
1422 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1423 if ((*i)->id() != (*io)->id()) {
1432 Region::verify_length (nframes_t len)
1434 if (source() && (source()->destructive() || source()->length_mutable())) {
1438 nframes_t maxlen = 0;
1440 for (uint32_t n=0; n < _sources.size(); ++n) {
1441 maxlen = max (maxlen, _sources[n]->length() - _start);
1444 len = min (len, maxlen);
1450 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1452 if (source() && (source()->destructive() || source()->length_mutable())) {
1456 nframes_t maxlen = 0;
1458 for (uint32_t n=0; n < _sources.size(); ++n) {
1459 maxlen = max (maxlen, _sources[n]->length() - new_start);
1462 new_length = min (new_length, maxlen);
1468 Region::verify_start (nframes_t pos)
1470 if (source() && (source()->destructive() || source()->length_mutable())) {
1474 for (uint32_t n=0; n < _sources.size(); ++n) {
1475 if (pos > _sources[n]->length() - _length) {
1483 Region::verify_start_mutable (nframes_t& new_start)
1485 if (source() && (source()->destructive() || source()->length_mutable())) {
1489 for (uint32_t n=0; n < _sources.size(); ++n) {
1490 if (new_start > _sources[n]->length() - _length) {
1491 new_start = _sources[n]->length() - _length;
1497 boost::shared_ptr<Region>
1498 Region::get_parent() const
1500 boost::shared_ptr<Playlist> pl (playlist());
1503 boost::shared_ptr<Region> r;
1504 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1506 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1507 return boost::static_pointer_cast<Region> (r);
1511 return boost::shared_ptr<Region>();
1515 Region::apply (Filter& filter)
1517 return filter.run (shared_from_this());
1522 Region::invalidate_transients ()
1524 _valid_transients = false;
1525 _transients.clear ();