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.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
46 using namespace ARDOUR;
49 Change Region::FadeChanged = PBD::new_change ();
50 Change Region::SyncOffsetChanged = PBD::new_change ();
51 Change Region::MuteChanged = PBD::new_change ();
52 Change Region::OpacityChanged = PBD::new_change ();
53 Change Region::LockChanged = PBD::new_change ();
54 Change Region::LayerChanged = PBD::new_change ();
55 Change Region::HiddenChanged = PBD::new_change ();
57 PBD::Signal1<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
60 Region::register_states ()
62 _xml_node_name = X_("Region");
64 add_state (_position);
68 /* derived-from-derived constructor (no sources in constructor) */
69 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
70 : SessionObject(s, name)
72 , _flags(Flag (flags|DoNotSendPropertyChanges))
73 , _start (X_("start"), StartChanged, start)
75 , _position (X_("position"), PositionChanged, 0)
77 , _positional_lock_style(AudioTime)
78 , _sync_position(_start)
79 , _layer (X_("layer"), LayerChanged, layer)
80 , _first_edit(EditChangesNothing)
82 , _ancestral_start (0)
83 , _ancestral_length (0)
87 , _pending_changed(Change (0))
89 , _pending_explicit_relayer (false)
93 /* no sources at this point */
96 /** Basic Region constructor (single source) */
97 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
98 : SessionObject(src->session(), name)
100 , _flags(Flag (flags|DoNotSendPropertyChanges))
101 , _start (X_("start"), StartChanged, start)
103 , _position (X_("position"), PositionChanged, 0)
105 , _positional_lock_style(AudioTime)
106 , _sync_position(_start)
107 , _layer (X_("layer"), LayerChanged, layer)
108 , _first_edit(EditChangesNothing)
110 , _ancestral_start (0)
111 , _ancestral_length (0)
114 , _valid_transients(false)
115 , _read_data_count(0)
116 , _pending_changed(Change (0))
118 , _pending_explicit_relayer (false)
122 _sources.push_back (src);
123 _master_sources.push_back (src);
125 src->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(src)));
127 assert(_sources.size() > 0);
128 _positional_lock_style = AudioTime;
131 /** Basic Region constructor (many sources) */
132 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
133 : SessionObject(srcs.front()->session(), name)
135 , _flags(Flag (flags|DoNotSendPropertyChanges))
136 , _start (X_("start"), StartChanged, start)
138 , _position (X_("position"), PositionChanged, 0)
140 , _positional_lock_style(AudioTime)
141 , _sync_position(_start)
142 , _layer (X_("layer"), LayerChanged, layer)
143 , _first_edit(EditChangesNothing)
145 , _ancestral_start (0)
146 , _ancestral_length (0)
149 , _read_data_count(0)
150 , _pending_changed(Change (0))
152 , _pending_explicit_relayer (false)
157 assert(_sources.size() > 0);
160 /** Create a new Region from part of an existing one */
161 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
162 : SessionObject(other->session(), name)
163 , _type (other->data_type())
164 , _start (X_("start"), StartChanged, 0)
165 , _position (X_("position"), PositionChanged, 0)
166 , _layer (X_("layer"), LayerChanged, 0)
167 , _pending_explicit_relayer (false)
172 _start = other->_start + offset;
173 copy_stuff (other, offset, length, name, layer, flags);
175 _flags = Flag (_flags | DoNotSendPropertyChanges);
177 /* if the other region had a distinct sync point
178 set, then continue to use it as best we can.
179 otherwise, reset sync point back to start.
182 if (other->flags() & SyncMarked) {
183 if (other->_sync_position < _start) {
184 _flags = Flag (_flags & ~SyncMarked);
185 _sync_position = _start;
187 _sync_position = other->_sync_position;
190 _flags = Flag (_flags & ~SyncMarked);
191 _sync_position = _start;
194 if (Profile->get_sae()) {
195 /* reset sync point to start if its ended up
196 outside region bounds.
199 if (_sync_position < _start || _sync_position >= _start + _length) {
200 _flags = Flag (_flags & ~SyncMarked);
201 _sync_position = _start;
206 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
207 : SessionObject(other->session(), name)
208 , _type (other->data_type())
209 , _start (X_("start"), StartChanged, 0)
210 , _position (X_("position"), PositionChanged, 0)
211 , _layer (X_("layer"), LayerChanged, 0)
212 , _pending_explicit_relayer (false)
216 /* create a new Region exactly like another but starting at 0 in its sources */
219 copy_stuff (other, 0, length, name, layer, flags);
221 _flags = Flag (_flags | DoNotSendPropertyChanges);
223 /* sync pos is relative to start of file. our start-in-file is now zero,
224 so set our sync position to whatever the the difference between
225 _start and _sync_pos was in the other region.
227 result is that our new sync pos points to the same point in our source(s)
228 as the sync in the other region did in its source(s).
230 since we start at zero in our source(s), it is not possible to use a sync point that
231 is before the start. reset it to _start if that was true in the other region.
234 if (other->flags() & SyncMarked) {
235 if (other->_start < other->_sync_position) {
236 /* sync pos was after the start point of the other region */
237 _sync_position = other->_sync_position - other->_start;
239 /* sync pos was before the start point of the other region. not possible here. */
240 _flags = Flag (_flags & ~SyncMarked);
241 _sync_position = _start;
244 _flags = Flag (_flags & ~SyncMarked);
245 _sync_position = _start;
248 if (Profile->get_sae()) {
249 /* reset sync point to start if its ended up
250 outside region bounds.
253 if (_sync_position < _start || _sync_position >= _start + _length) {
254 _flags = Flag (_flags & ~SyncMarked);
255 _sync_position = _start;
259 /* reset a couple of things that copy_stuff() gets wrong in this particular case */
261 _positional_lock_style = other->_positional_lock_style;
262 _first_edit = other->_first_edit;
265 /** Pure copy constructor */
266 Region::Region (boost::shared_ptr<const Region> other)
267 : SessionObject(other->session(), other->name())
268 , _type(other->data_type())
269 , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
270 , _start(other->_start)
271 , _length(other->_length)
272 , _position(other->_position)
273 , _last_position(other->_last_position)
274 , _positional_lock_style(other->_positional_lock_style)
275 , _sync_position(other->_sync_position)
276 , _layer(other->_layer)
277 , _first_edit(EditChangesID)
279 , _ancestral_start (other->_ancestral_start)
280 , _ancestral_length (other->_ancestral_length)
281 , _stretch (other->_stretch)
282 , _shift (other->_shift)
283 , _valid_transients(false)
284 , _read_data_count(0)
285 , _pending_changed(Change(0))
286 , _last_layer_op(other->_last_layer_op)
287 , _pending_explicit_relayer (false)
291 _flags = Flag (_flags | DoNotSendPropertyChanges);
293 other->_first_edit = EditChangesName;
295 if (other->_extra_xml) {
296 _extra_xml = new XMLNode (*other->_extra_xml);
301 use_sources (other->_sources);
302 assert(_sources.size() > 0);
305 Region::Region (const SourceList& srcs, const XMLNode& node)
306 : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
307 , _type(DataType::NIL) // to be loaded from XML
308 , _flags(DoNotSendPropertyChanges)
309 , _start (X_("start"), StartChanged, 0)
311 , _position (X_("position"), PositionChanged, 0)
313 , _positional_lock_style(AudioTime)
314 , _sync_position(_start)
315 , _layer (X_("layer"), LayerChanged, 0)
316 , _first_edit(EditChangesNothing)
320 , _read_data_count(0)
321 , _pending_changed(Change(0))
323 , _pending_explicit_relayer (false)
329 if (set_state (node, Stateful::loading_state_version)) {
330 throw failed_constructor();
333 assert(_type != DataType::NIL);
334 assert(_sources.size() > 0);
337 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
338 : SessionObject(src->session(), X_("error: XML did not reset this"))
339 , _type(DataType::NIL)
340 , _flags(DoNotSendPropertyChanges)
341 , _start (X_("start"), StartChanged, 0)
343 , _position (X_("position"), PositionChanged, 0)
345 , _positional_lock_style(AudioTime)
346 , _sync_position(_start)
347 , _layer (X_("layer"), LayerChanged, 0)
348 , _first_edit(EditChangesNothing)
352 , _read_data_count(0)
353 , _pending_changed(Change(0))
355 , _pending_explicit_relayer (false)
359 _sources.push_back (src);
361 if (set_state (node, Stateful::loading_state_version)) {
362 throw failed_constructor();
365 assert(_type != DataType::NIL);
366 assert(_sources.size() > 0);
371 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
375 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t /*offset*/, nframes_t length, const string& name, layer_t layer, Flag flags)
378 _pending_changed = Change (0);
379 _read_data_count = 0;
380 _valid_transients = false;
383 _last_length = length;
384 _sync_position = other->_sync_position;
385 _ancestral_start = other->_ancestral_start;
386 _ancestral_length = other->_ancestral_length;
387 _stretch = other->_stretch;
388 _shift = other->_shift;
393 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
394 _first_edit = EditChangesNothing;
396 _positional_lock_style = AudioTime;
398 use_sources (other->_sources);
402 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
404 _playlist = wpl.lock();
408 Region::set_name (const std::string& str)
411 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
412 assert(_name == str);
413 send_change (ARDOUR::NameChanged);
420 Region::set_length (nframes_t len, void */*src*/)
422 //cerr << "Region::set_length() len = " << len << endl;
423 if (_flags & Locked) {
427 if (_length != len && len != 0) {
429 /* check that the current _position wouldn't make the new
433 if (max_frames - len < _position) {
437 if (!verify_length (len)) {
442 _last_length = _length;
445 _flags = Region::Flag (_flags & ~WholeFile);
449 invalidate_transients ();
455 send_change (LengthChanged);
460 Region::maybe_uncopy ()
465 Region::first_edit ()
467 boost::shared_ptr<Playlist> pl (playlist());
469 if (_first_edit != EditChangesNothing && pl) {
471 _name = _session.new_region_name (_name);
472 _first_edit = EditChangesNothing;
474 send_change (ARDOUR::NameChanged);
475 RegionFactory::CheckNewRegion (shared_from_this());
480 Region::at_natural_position () const
482 boost::shared_ptr<Playlist> pl (playlist());
488 boost::shared_ptr<Region> whole_file_region = get_parent();
490 if (whole_file_region) {
491 if (_position == whole_file_region->position() + _start) {
500 Region::move_to_natural_position (void *src)
502 boost::shared_ptr<Playlist> pl (playlist());
508 boost::shared_ptr<Region> whole_file_region = get_parent();
510 if (whole_file_region) {
511 set_position (whole_file_region->position() + _start, src);
516 Region::special_set_position (nframes_t pos)
518 /* this is used when creating a whole file region as
519 a way to store its "natural" or "captured" position.
522 _position = _position;
527 Region::set_position_lock_style (PositionLockStyle ps)
529 boost::shared_ptr<Playlist> pl (playlist());
535 _positional_lock_style = ps;
537 if (_positional_lock_style == MusicTime) {
538 _session.tempo_map().bbt_time (_position, _bbt_time);
544 Region::update_position_after_tempo_map_change ()
546 boost::shared_ptr<Playlist> pl (playlist());
548 if (!pl || _positional_lock_style != MusicTime) {
552 TempoMap& map (_session.tempo_map());
553 nframes_t pos = map.frame_time (_bbt_time);
554 set_position_internal (pos, false);
558 Region::set_position (nframes_t pos, void* /*src*/)
564 set_position_internal (pos, true);
568 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
570 if (_position != pos) {
571 _last_position = _position;
574 /* check that the new _position wouldn't make the current
575 length impossible - if so, change the length.
577 XXX is this the right thing to do?
580 if (max_frames - _length < _position) {
581 _last_length = _length;
582 _length = max_frames - _position;
585 if (allow_bbt_recompute) {
586 recompute_position_from_lock_style ();
589 invalidate_transients ();
592 /* do this even if the position is the same. this helps out
593 a GUI that has moved its representation already.
596 send_change (PositionChanged);
600 Region::set_position_on_top (nframes_t pos, void* /*src*/)
602 if (_flags & Locked) {
606 if (_position != pos) {
607 _last_position = _position;
611 boost::shared_ptr<Playlist> pl (playlist());
614 pl->raise_region_to_top (shared_from_this ());
617 /* do this even if the position is the same. this helps out
618 a GUI that has moved its representation already.
621 send_change (PositionChanged);
625 Region::recompute_position_from_lock_style ()
627 if (_positional_lock_style == MusicTime) {
628 _session.tempo_map().bbt_time (_position, _bbt_time);
633 Region::nudge_position (nframes64_t n, void* /*src*/)
635 if (_flags & Locked) {
643 _last_position = _position;
646 if (_position > max_frames - n) {
647 _position = max_frames;
652 if (_position < (nframes_t) -n) {
659 send_change (PositionChanged);
663 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
665 _ancestral_length = l;
666 _ancestral_start = s;
672 Region::set_start (nframes_t pos, void* /*src*/)
674 if (_flags & (Locked|PositionLocked)) {
677 /* This just sets the start, nothing else. It effectively shifts
678 the contents of the Region within the overall extent of the Source,
679 without changing the Region's position or length
684 if (!verify_start (pos)) {
689 _flags = Region::Flag (_flags & ~WholeFile);
691 invalidate_transients ();
693 send_change (StartChanged);
698 Region::trim_start (nframes_t new_position, void */*src*/)
700 if (_flags & (Locked|PositionLocked)) {
706 if (new_position > _position) {
707 start_shift = new_position - _position;
709 start_shift = -(_position - new_position);
712 if (start_shift > 0) {
714 if (_start > max_frames - start_shift) {
715 new_start = max_frames;
717 new_start = _start + start_shift;
720 if (!verify_start (new_start)) {
724 } else if (start_shift < 0) {
726 if (_start < (nframes_t) -start_shift) {
729 new_start = _start + start_shift;
735 if (new_start == _start) {
740 _flags = Region::Flag (_flags & ~WholeFile);
743 send_change (StartChanged);
747 Region::trim_front (nframes_t new_position, void *src)
749 if (_flags & Locked) {
753 nframes_t end = last_frame();
754 nframes_t source_zero;
756 if (_position > _start) {
757 source_zero = _position - _start;
759 source_zero = 0; // its actually negative, but this will work for us
762 if (new_position < end) { /* can't trim it zero or negative length */
766 /* can't trim it back passed where source position zero is located */
768 new_position = max (new_position, source_zero);
771 if (new_position > _position) {
772 newlen = _length - (new_position - _position);
774 newlen = _length + (_position - new_position);
777 trim_to_internal (new_position, newlen, src);
779 recompute_at_start ();
784 /** @param new_endpoint New region end point, such that, for example,
785 * a region at 0 of length 10 has an endpoint of 9.
789 Region::trim_end (nframes_t new_endpoint, void */*src*/)
791 if (_flags & Locked) {
795 if (new_endpoint > _position) {
796 trim_to_internal (_position, new_endpoint - _position + 1, this);
804 Region::trim_to (nframes_t position, nframes_t length, void *src)
806 if (_flags & Locked) {
810 trim_to_internal (position, length, src);
813 recompute_at_start ();
819 Region::trim_to_internal (nframes_t position, nframes_t length, void */*src*/)
824 if (_flags & Locked) {
828 if (position > _position) {
829 start_shift = position - _position;
831 start_shift = -(_position - position);
834 if (start_shift > 0) {
836 if (_start > max_frames - start_shift) {
837 new_start = max_frames;
839 new_start = _start + start_shift;
843 } else if (start_shift < 0) {
845 if (_start < (nframes_t) -start_shift) {
848 new_start = _start + start_shift;
854 if (!verify_start_and_length (new_start, length)) {
858 Change what_changed = Change (0);
860 if (_start != new_start) {
862 what_changed = Change (what_changed|StartChanged);
864 if (_length != length) {
866 _last_length = _length;
869 what_changed = Change (what_changed|LengthChanged);
871 if (_position != position) {
873 _last_position = _position;
875 _position = position;
876 what_changed = Change (what_changed|PositionChanged);
879 _flags = Region::Flag (_flags & ~WholeFile);
881 if (what_changed & (StartChanged|LengthChanged)) {
886 send_change (what_changed);
891 Region::set_hidden (bool yn)
893 if (hidden() != yn) {
896 _flags = Flag (_flags|Hidden);
898 _flags = Flag (_flags & ~Hidden);
901 send_change (HiddenChanged);
906 Region::set_muted (bool yn)
911 _flags = Flag (_flags|Muted);
913 _flags = Flag (_flags & ~Muted);
916 send_change (MuteChanged);
921 Region::set_opaque (bool yn)
923 if (opaque() != yn) {
925 _flags = Flag (_flags|Opaque);
927 _flags = Flag (_flags & ~Opaque);
929 send_change (OpacityChanged);
934 Region::set_locked (bool yn)
936 if (locked() != yn) {
938 _flags = Flag (_flags|Locked);
940 _flags = Flag (_flags & ~Locked);
942 send_change (LockChanged);
947 Region::set_position_locked (bool yn)
949 if (position_locked() != yn) {
951 _flags = Flag (_flags|PositionLocked);
953 _flags = Flag (_flags & ~PositionLocked);
955 send_change (LockChanged);
960 Region::set_sync_position (nframes_t absolute_pos)
962 nframes_t const file_pos = _start + (absolute_pos - _position);
964 if (file_pos != _sync_position) {
966 _sync_position = file_pos;
967 _flags = Flag (_flags|SyncMarked);
972 send_change (SyncOffsetChanged);
977 Region::clear_sync_position ()
979 if (_flags & SyncMarked) {
980 _flags = Flag (_flags & ~SyncMarked);
985 send_change (SyncOffsetChanged);
990 Region::sync_offset (int& dir) const
992 /* returns the sync point relative the first frame of the region */
994 if (_flags & SyncMarked) {
995 if (_sync_position > _start) {
997 return _sync_position - _start;
1000 return _start - _sync_position;
1009 Region::adjust_to_sync (nframes_t pos) const
1012 nframes_t offset = sync_offset (sync_dir);
1014 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1023 if (max_frames - pos > offset) {
1032 Region::sync_position() const
1034 if (_flags & SyncMarked) {
1035 return _sync_position;
1044 boost::shared_ptr<Playlist> pl (playlist());
1046 pl->raise_region (shared_from_this ());
1053 boost::shared_ptr<Playlist> pl (playlist());
1055 pl->lower_region (shared_from_this ());
1061 Region::raise_to_top ()
1063 boost::shared_ptr<Playlist> pl (playlist());
1065 pl->raise_region_to_top (shared_from_this());
1070 Region::lower_to_bottom ()
1072 boost::shared_ptr<Playlist> pl (playlist());
1074 pl->lower_region_to_bottom (shared_from_this());
1079 Region::set_layer (layer_t l)
1084 send_change (LayerChanged);
1089 Region::state (bool /*full_state*/)
1091 XMLNode *node = new XMLNode ("Region");
1093 const char* fe = NULL;
1097 _id.print (buf, sizeof (buf));
1098 node->add_property ("id", buf);
1099 node->add_property ("name", _name);
1100 node->add_property ("type", _type.to_string());
1101 snprintf (buf, sizeof (buf), "%u", _length);
1102 node->add_property ("length", buf);
1103 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1104 node->add_property ("ancestral-start", buf);
1105 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1106 node->add_property ("ancestral-length", buf);
1107 snprintf (buf, sizeof (buf), "%.12g", _stretch);
1108 node->add_property ("stretch", buf);
1109 snprintf (buf, sizeof (buf), "%.12g", _shift);
1110 node->add_property ("shift", buf);
1112 switch (_first_edit) {
1113 case EditChangesNothing:
1116 case EditChangesName:
1122 default: /* should be unreachable but makes g++ happy */
1127 node->add_property ("first-edit", fe);
1129 /* note: flags are stored by derived classes */
1131 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1132 node->add_property ("sync-position", buf);
1134 if (_positional_lock_style != AudioTime) {
1135 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1138 node->add_property ("bbt-position", str.str());
1145 Region::get_state ()
1147 return state (true);
1151 Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_changed, bool send)
1153 const XMLNodeList& nlist = node.children();
1154 const XMLProperty *prop;
1157 /* this is responsible for setting those aspects of Region state
1158 that are mutable after construction.
1161 if ((prop = node.property ("name"))) {
1162 _name = prop->value();
1165 if ((prop = node.property ("type")) == 0) {
1166 _type = DataType::AUDIO;
1168 _type = DataType(prop->value());
1171 if ((prop = node.property ("length")) != 0) {
1172 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1173 if (val != _length) {
1174 what_changed = Change (what_changed|LengthChanged);
1175 cerr << _name << " length changed\n";
1176 _last_length = _length;
1181 if ((prop = node.property ("sync-position")) != 0) {
1182 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1183 if (val != _sync_position) {
1184 what_changed = Change (what_changed|SyncOffsetChanged);
1185 cerr << _name << " sync changed\n";
1186 _sync_position = val;
1190 if ((prop = node.property ("positional-lock-style")) != 0) {
1191 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1193 if (_positional_lock_style == MusicTime) {
1194 if ((prop = node.property ("bbt-position")) == 0) {
1195 /* missing BBT info, revert to audio time locking */
1196 _positional_lock_style = AudioTime;
1198 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1201 &_bbt_time.ticks) != 3) {
1202 _positional_lock_style = AudioTime;
1209 /* XXX FIRST EDIT !!! */
1211 /* these 3 properties never change as a result of any editing */
1213 if ((prop = node.property ("ancestral-start")) != 0) {
1214 _ancestral_start = strtoll (prop->value().c_str(), 0, 10);
1216 _ancestral_start = _start;
1219 if ((prop = node.property ("ancestral-length")) != 0) {
1220 _ancestral_length = strtoll (prop->value().c_str(), 0, 10);
1222 _ancestral_length = _length;
1225 if ((prop = node.property ("stretch")) != 0) {
1226 _stretch = atof (prop->value());
1228 /* fix problem with old sessions corrupted by an impossible
1231 if (_stretch == 0.0) {
1238 if ((prop = node.property ("shift")) != 0) {
1239 _shift = atof (prop->value());
1241 /* fix problem with old sessions corrupted by an impossible
1244 if (_shift == 0.0) {
1252 /* note: derived classes set flags */
1254 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1260 if (child->name () == "Extra") {
1262 _extra_xml = new XMLNode (*child);
1268 cerr << _name << ": final change to be sent: " << hex << what_changed << dec << endl;
1269 send_change (what_changed);
1276 Region::set_state (const XMLNode& node, int version)
1278 const XMLProperty *prop;
1280 /* ID is not allowed to change, ever */
1282 if ((prop = node.property ("id"))) {
1283 _id = prop->value();
1286 _first_edit = EditChangesNothing;
1288 Change what_changed = set_state_using_states (node);
1290 set_live_state (node, version, what_changed, true);
1299 _last_length = _length;
1300 _last_position = _position;
1306 Change what_changed = Change (0);
1309 Glib::Mutex::Lock lm (_lock);
1311 if (_frozen && --_frozen > 0) {
1315 if (_pending_changed) {
1316 what_changed = _pending_changed;
1317 _pending_changed = Change (0);
1321 if (what_changed == Change (0)) {
1325 if (what_changed & LengthChanged) {
1326 if (what_changed & PositionChanged) {
1327 recompute_at_start ();
1329 recompute_at_end ();
1332 send_change (what_changed);
1336 Region::send_change (Change what_changed)
1340 Glib::Mutex::Lock lm (_lock);
1342 _pending_changed = Change (_pending_changed|what_changed);
1347 cerr << _name << " actually sends " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1348 StateChanged (what_changed);
1349 cerr << _name << " done with " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1351 if (!(_flags & DoNotSendPropertyChanges)) {
1353 /* Try and send a shared_pointer unless this is part of the constructor.
1358 boost::shared_ptr<Region> rptr = shared_from_this();
1359 cerr << _name << " actually sends prop change " << hex << what_changed << dec << " @ " << get_microseconds() << endl;
1360 RegionPropertyChanged (rptr);
1361 cerr << _name << " done with prop change @ " << get_microseconds() << endl;
1364 /* no shared_ptr available, relax; */
1371 Region::set_last_layer_op (uint64_t when)
1373 _last_layer_op = when;
1377 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1379 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1383 Region::equivalent (boost::shared_ptr<const Region> other) const
1385 return _start == other->_start &&
1386 _position == other->_position &&
1387 _length == other->_length;
1391 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1393 return _start == other->_start &&
1394 _length == other->_length;
1398 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1400 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1404 Region::source_deleted (boost::weak_ptr<Source>)
1408 if (!_session.deletion_in_progress()) {
1409 /* this is a very special case: at least one of the region's
1410 sources has bee deleted, so invalidate all references to
1411 ourselves. Do NOT do this during session deletion, because
1412 then we run the risk that this will actually result
1413 in this object being deleted (as refcnt goes to zero)
1414 while emitting DropReferences.
1422 Region::master_source_names ()
1424 SourceList::iterator i;
1426 vector<string> names;
1427 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1428 names.push_back((*i)->name());
1435 Region::set_master_sources (const SourceList& srcs)
1437 _master_sources = srcs;
1438 assert (_sources.size() == _master_sources.size());
1442 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1447 SourceList::const_iterator i;
1448 SourceList::const_iterator io;
1450 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1451 if ((*i)->id() != (*io)->id()) {
1456 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1457 if ((*i)->id() != (*io)->id()) {
1466 Region::uses_source (boost::shared_ptr<const Source> source) const
1468 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1477 Region::source_length(uint32_t n) const
1479 return _sources[n]->length(_position - _start);
1483 Region::verify_length (nframes_t len)
1485 if (source() && (source()->destructive() || source()->length_mutable())) {
1489 nframes_t maxlen = 0;
1491 for (uint32_t n=0; n < _sources.size(); ++n) {
1492 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1495 len = min (len, maxlen);
1501 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1503 if (source() && (source()->destructive() || source()->length_mutable())) {
1507 nframes_t maxlen = 0;
1509 for (uint32_t n=0; n < _sources.size(); ++n) {
1510 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1513 new_length = min (new_length, maxlen);
1519 Region::verify_start (nframes_t pos)
1521 if (source() && (source()->destructive() || source()->length_mutable())) {
1525 for (uint32_t n=0; n < _sources.size(); ++n) {
1526 if (pos > source_length(n) - _length) {
1534 Region::verify_start_mutable (nframes_t& new_start)
1536 if (source() && (source()->destructive() || source()->length_mutable())) {
1540 for (uint32_t n=0; n < _sources.size(); ++n) {
1541 if (new_start > source_length(n) - _length) {
1542 new_start = source_length(n) - _length;
1548 boost::shared_ptr<Region>
1549 Region::get_parent() const
1551 boost::shared_ptr<Playlist> pl (playlist());
1554 boost::shared_ptr<Region> r;
1555 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1557 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1558 return boost::static_pointer_cast<Region> (r);
1562 return boost::shared_ptr<Region>();
1566 Region::apply (Filter& filter)
1568 return filter.run (shared_from_this());
1573 Region::invalidate_transients ()
1575 _valid_transients = false;
1576 _transients.clear ();
1581 Region::use_sources (SourceList const & s)
1583 set<boost::shared_ptr<Source> > unique_srcs;
1585 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1586 _sources.push_back (*i);
1587 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1588 unique_srcs.insert (*i);
1591 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1592 _master_sources.push_back (*i);
1593 if (unique_srcs.find (*i) == unique_srcs.end()) {
1594 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));