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"
41 #include "ardour/profile.h"
42 #include "ardour/utils.h"
47 using namespace ARDOUR;
50 Change Region::FadeChanged = ARDOUR::new_change ();
51 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
52 Change Region::MuteChanged = ARDOUR::new_change ();
53 Change Region::OpacityChanged = ARDOUR::new_change ();
54 Change Region::LockChanged = ARDOUR::new_change ();
55 Change Region::LayerChanged = ARDOUR::new_change ();
56 Change Region::HiddenChanged = ARDOUR::new_change ();
58 sigc::signal<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
60 /* derived-from-derived constructor (no sources in constructor) */
61 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
62 : SessionObject(s, name)
64 , _flags(Flag (flags|DoNotSendPropertyChanges))
69 , _positional_lock_style(AudioTime)
70 , _sync_position(_start)
72 , _first_edit(EditChangesNothing)
74 , _ancestral_start (0)
75 , _ancestral_length (0)
79 , _pending_changed(Change (0))
81 , _pending_explicit_relayer (false)
83 /* no sources at this point */
86 /** Basic Region constructor (single source) */
87 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
88 : SessionObject(src->session(), name)
90 , _flags(Flag (flags|DoNotSendPropertyChanges))
95 , _positional_lock_style(AudioTime)
96 , _sync_position(_start)
98 , _first_edit(EditChangesNothing)
100 , _ancestral_start (0)
101 , _ancestral_length (0)
104 , _valid_transients(false)
105 , _read_data_count(0)
106 , _pending_changed(Change (0))
108 , _pending_explicit_relayer (false)
110 _sources.push_back (src);
111 _master_sources.push_back (src);
113 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
115 assert(_sources.size() > 0);
116 _positional_lock_style = AudioTime;
119 /** Basic Region constructor (many sources) */
120 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
121 : SessionObject(srcs.front()->session(), name)
123 , _flags(Flag (flags|DoNotSendPropertyChanges))
128 , _positional_lock_style(AudioTime)
129 , _sync_position(_start)
131 , _first_edit(EditChangesNothing)
133 , _ancestral_start (0)
134 , _ancestral_length (0)
137 , _read_data_count(0)
138 , _pending_changed(Change (0))
140 , _pending_explicit_relayer (false)
143 assert(_sources.size() > 0);
146 /** Create a new Region from part of an existing one */
147 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
148 : SessionObject(other->session(), name)
149 , _type (other->data_type())
150 , _pending_explicit_relayer (false)
153 _start = other->_start + offset;
154 copy_stuff (other, offset, length, name, layer, flags);
156 _flags = Flag (_flags | DoNotSendPropertyChanges);
158 /* if the other region had a distinct sync point
159 set, then continue to use it as best we can.
160 otherwise, reset sync point back to start.
163 if (other->flags() & SyncMarked) {
164 if (other->_sync_position < _start) {
165 _flags = Flag (_flags & ~SyncMarked);
166 _sync_position = _start;
168 _sync_position = other->_sync_position;
171 _flags = Flag (_flags & ~SyncMarked);
172 _sync_position = _start;
175 if (Profile->get_sae()) {
176 /* reset sync point to start if its ended up
177 outside region bounds.
180 if (_sync_position < _start || _sync_position >= _start + _length) {
181 _flags = Flag (_flags & ~SyncMarked);
182 _sync_position = _start;
187 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
188 : SessionObject(other->session(), name)
189 , _type (other->data_type())
190 , _pending_explicit_relayer (false)
192 /* create a new Region exactly like another but starting at 0 in its sources */
195 copy_stuff (other, 0, length, name, layer, flags);
197 _flags = Flag (_flags | DoNotSendPropertyChanges);
199 /* sync pos is relative to start of file. our start-in-file is now zero,
200 so set our sync position to whatever the the difference between
201 _start and _sync_pos was in the other region.
203 result is that our new sync pos points to the same point in our source(s)
204 as the sync in the other region did in its source(s).
206 since we start at zero in our source(s), it is not possible to use a sync point that
207 is before the start. reset it to _start if that was true in the other region.
210 if (other->flags() & SyncMarked) {
211 if (other->_start < other->_sync_position) {
212 /* sync pos was after the start point of the other region */
213 _sync_position = other->_sync_position - other->_start;
215 /* sync pos was before the start point of the other region. not possible here. */
216 _flags = Flag (_flags & ~SyncMarked);
217 _sync_position = _start;
220 _flags = Flag (_flags & ~SyncMarked);
221 _sync_position = _start;
224 if (Profile->get_sae()) {
225 /* reset sync point to start if its ended up
226 outside region bounds.
229 if (_sync_position < _start || _sync_position >= _start + _length) {
230 _flags = Flag (_flags & ~SyncMarked);
231 _sync_position = _start;
235 /* reset a couple of things that copy_stuff() gets wrong in this particular case */
237 _positional_lock_style = other->_positional_lock_style;
238 _first_edit = other->_first_edit;
241 /** Pure copy constructor */
242 Region::Region (boost::shared_ptr<const Region> other)
243 : SessionObject(other->session(), other->name())
244 , _type(other->data_type())
245 , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
246 , _start(other->_start)
247 , _length(other->_length)
248 , _position(other->_position)
249 , _last_position(other->_last_position)
250 , _positional_lock_style(other->_positional_lock_style)
251 , _sync_position(other->_sync_position)
252 , _layer(other->_layer)
253 , _first_edit(EditChangesID)
255 , _ancestral_start (other->_ancestral_start)
256 , _ancestral_length (other->_ancestral_length)
257 , _stretch (other->_stretch)
258 , _shift (other->_shift)
259 , _valid_transients(false)
260 , _read_data_count(0)
261 , _pending_changed(Change(0))
262 , _last_layer_op(other->_last_layer_op)
263 , _pending_explicit_relayer (false)
265 _flags = Flag (_flags | DoNotSendPropertyChanges);
267 other->_first_edit = EditChangesName;
269 if (other->_extra_xml) {
270 _extra_xml = new XMLNode (*other->_extra_xml);
275 use_sources (other->_sources);
276 assert(_sources.size() > 0);
279 Region::Region (const SourceList& srcs, const XMLNode& node)
280 : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
281 , _type(DataType::NIL) // to be loaded from XML
282 , _flags(DoNotSendPropertyChanges)
287 , _positional_lock_style(AudioTime)
288 , _sync_position(_start)
290 , _first_edit(EditChangesNothing)
294 , _read_data_count(0)
295 , _pending_changed(Change(0))
297 , _pending_explicit_relayer (false)
301 if (set_state (node, Stateful::loading_state_version)) {
302 throw failed_constructor();
305 assert(_type != DataType::NIL);
306 assert(_sources.size() > 0);
309 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
310 : SessionObject(src->session(), X_("error: XML did not reset this"))
311 , _type(DataType::NIL)
312 , _flags(DoNotSendPropertyChanges)
317 , _positional_lock_style(AudioTime)
318 , _sync_position(_start)
320 , _first_edit(EditChangesNothing)
324 , _read_data_count(0)
325 , _pending_changed(Change(0))
327 , _pending_explicit_relayer (false)
329 _sources.push_back (src);
331 if (set_state (node, Stateful::loading_state_version)) {
332 throw failed_constructor();
335 assert(_type != DataType::NIL);
336 assert(_sources.size() > 0);
341 boost::shared_ptr<Playlist> pl (playlist());
344 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
345 (*i)->remove_playlist (pl);
347 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
348 (*i)->remove_playlist (pl);
353 GoingAway (); /* EMIT SIGNAL */
357 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t /*offset*/, nframes_t length, const string& name, layer_t layer, Flag flags)
360 _pending_changed = Change (0);
361 _read_data_count = 0;
362 _valid_transients = false;
365 _last_length = length;
366 _sync_position = other->_sync_position;
367 _ancestral_start = other->_ancestral_start;
368 _ancestral_length = other->_ancestral_length;
369 _stretch = other->_stretch;
370 _shift = other->_shift;
375 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
376 _first_edit = EditChangesNothing;
378 _positional_lock_style = AudioTime;
380 use_sources (other->_sources);
384 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
386 boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
388 boost::shared_ptr<Playlist> pl (wpl.lock());
390 if (old_playlist == pl) {
398 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
399 (*i)->remove_playlist (_playlist);
400 (*i)->add_playlist (pl);
402 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
403 (*i)->remove_playlist (_playlist);
404 (*i)->add_playlist (pl);
407 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
408 (*i)->add_playlist (pl);
410 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
411 (*i)->add_playlist (pl);
416 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
417 (*i)->remove_playlist (old_playlist);
419 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
420 (*i)->remove_playlist (old_playlist);
427 Region::set_name (const std::string& str)
430 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
431 assert(_name == str);
432 send_change (ARDOUR::NameChanged);
439 Region::set_length (nframes_t len, void */*src*/)
441 //cerr << "Region::set_length() len = " << len << endl;
442 if (_flags & Locked) {
446 if (_length != len && len != 0) {
448 /* check that the current _position wouldn't make the new
452 if (max_frames - len < _position) {
456 if (!verify_length (len)) {
461 _last_length = _length;
464 _flags = Region::Flag (_flags & ~WholeFile);
468 invalidate_transients ();
474 send_change (LengthChanged);
479 Region::maybe_uncopy ()
484 Region::first_edit ()
486 boost::shared_ptr<Playlist> pl (playlist());
488 if (_first_edit != EditChangesNothing && pl) {
490 _name = pl->session().new_region_name (_name);
491 _first_edit = EditChangesNothing;
493 send_change (ARDOUR::NameChanged);
494 RegionFactory::CheckNewRegion (shared_from_this());
499 Region::at_natural_position () const
501 boost::shared_ptr<Playlist> pl (playlist());
507 boost::shared_ptr<Region> whole_file_region = get_parent();
509 if (whole_file_region) {
510 if (_position == whole_file_region->position() + _start) {
519 Region::move_to_natural_position (void *src)
521 boost::shared_ptr<Playlist> pl (playlist());
527 boost::shared_ptr<Region> whole_file_region = get_parent();
529 if (whole_file_region) {
530 set_position (whole_file_region->position() + _start, src);
535 Region::special_set_position (nframes_t pos)
537 /* this is used when creating a whole file region as
538 a way to store its "natural" or "captured" position.
541 _position = _position;
546 Region::set_position_lock_style (PositionLockStyle ps)
548 boost::shared_ptr<Playlist> pl (playlist());
554 _positional_lock_style = ps;
556 if (_positional_lock_style == MusicTime) {
557 pl->session().tempo_map().bbt_time (_position, _bbt_time);
563 Region::update_position_after_tempo_map_change ()
565 boost::shared_ptr<Playlist> pl (playlist());
567 if (!pl || _positional_lock_style != MusicTime) {
571 TempoMap& map (pl->session().tempo_map());
572 nframes_t pos = map.frame_time (_bbt_time);
573 set_position_internal (pos, false);
577 Region::set_position (nframes_t pos, void */*src*/)
583 set_position_internal (pos, true);
587 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
589 if (_position != pos) {
590 _last_position = _position;
593 /* check that the new _position wouldn't make the current
594 length impossible - if so, change the length.
596 XXX is this the right thing to do?
599 if (max_frames - _length < _position) {
600 _last_length = _length;
601 _length = max_frames - _position;
604 if (allow_bbt_recompute) {
605 recompute_position_from_lock_style ();
608 invalidate_transients ();
611 /* do this even if the position is the same. this helps out
612 a GUI that has moved its representation already.
615 send_change (PositionChanged);
619 Region::set_position_on_top (nframes_t pos, void */*src*/)
621 if (_flags & Locked) {
625 if (_position != pos) {
626 _last_position = _position;
630 boost::shared_ptr<Playlist> pl (playlist());
633 pl->raise_region_to_top (shared_from_this ());
636 /* do this even if the position is the same. this helps out
637 a GUI that has moved its representation already.
640 send_change (PositionChanged);
644 Region::recompute_position_from_lock_style ()
646 if (_positional_lock_style == MusicTime) {
647 boost::shared_ptr<Playlist> pl (playlist());
649 pl->session().tempo_map().bbt_time (_position, _bbt_time);
655 Region::nudge_position (nframes64_t n, void */*src*/)
657 if (_flags & Locked) {
665 _last_position = _position;
668 if (_position > max_frames - n) {
669 _position = max_frames;
674 if (_position < (nframes_t) -n) {
681 send_change (PositionChanged);
685 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
687 _ancestral_length = l;
688 _ancestral_start = s;
694 Region::set_start (nframes_t pos, void */*src*/)
696 if (_flags & (Locked|PositionLocked)) {
699 /* This just sets the start, nothing else. It effectively shifts
700 the contents of the Region within the overall extent of the Source,
701 without changing the Region's position or length
706 if (!verify_start (pos)) {
711 _flags = Region::Flag (_flags & ~WholeFile);
713 invalidate_transients ();
715 send_change (StartChanged);
720 Region::trim_start (nframes_t new_position, void */*src*/)
722 if (_flags & (Locked|PositionLocked)) {
728 if (new_position > _position) {
729 start_shift = new_position - _position;
731 start_shift = -(_position - new_position);
734 if (start_shift > 0) {
736 if (_start > max_frames - start_shift) {
737 new_start = max_frames;
739 new_start = _start + start_shift;
742 if (!verify_start (new_start)) {
746 } else if (start_shift < 0) {
748 if (_start < (nframes_t) -start_shift) {
751 new_start = _start + start_shift;
757 if (new_start == _start) {
762 _flags = Region::Flag (_flags & ~WholeFile);
765 send_change (StartChanged);
769 Region::trim_front (nframes_t new_position, void *src)
771 if (_flags & Locked) {
775 nframes_t end = last_frame();
776 nframes_t source_zero;
778 if (_position > _start) {
779 source_zero = _position - _start;
781 source_zero = 0; // its actually negative, but this will work for us
784 if (new_position < end) { /* can't trim it zero or negative length */
788 /* can't trim it back passed where source position zero is located */
790 new_position = max (new_position, source_zero);
793 if (new_position > _position) {
794 newlen = _length - (new_position - _position);
796 newlen = _length + (_position - new_position);
799 trim_to_internal (new_position, newlen, src);
801 recompute_at_start ();
806 /** @param new_endpoint New region end point, such that, for example,
807 * a region at 0 of length 10 has an endpoint of 9.
811 Region::trim_end (nframes_t new_endpoint, void */*src*/)
813 if (_flags & Locked) {
817 if (new_endpoint > _position) {
818 trim_to_internal (_position, new_endpoint - _position + 1, this);
826 Region::trim_to (nframes_t position, nframes_t length, void *src)
828 if (_flags & Locked) {
832 trim_to_internal (position, length, src);
835 recompute_at_start ();
841 Region::trim_to_internal (nframes_t position, nframes_t length, void */*src*/)
846 if (_flags & Locked) {
850 if (position > _position) {
851 start_shift = position - _position;
853 start_shift = -(_position - position);
856 if (start_shift > 0) {
858 if (_start > max_frames - start_shift) {
859 new_start = max_frames;
861 new_start = _start + start_shift;
865 } else if (start_shift < 0) {
867 if (_start < (nframes_t) -start_shift) {
870 new_start = _start + start_shift;
876 if (!verify_start_and_length (new_start, length)) {
880 Change what_changed = Change (0);
882 if (_start != new_start) {
884 what_changed = Change (what_changed|StartChanged);
886 if (_length != length) {
888 _last_length = _length;
891 what_changed = Change (what_changed|LengthChanged);
893 if (_position != position) {
895 _last_position = _position;
897 _position = position;
898 what_changed = Change (what_changed|PositionChanged);
901 _flags = Region::Flag (_flags & ~WholeFile);
903 if (what_changed & (StartChanged|LengthChanged)) {
908 send_change (what_changed);
913 Region::set_hidden (bool yn)
915 if (hidden() != yn) {
918 _flags = Flag (_flags|Hidden);
920 _flags = Flag (_flags & ~Hidden);
923 send_change (HiddenChanged);
928 Region::set_muted (bool yn)
933 _flags = Flag (_flags|Muted);
935 _flags = Flag (_flags & ~Muted);
938 send_change (MuteChanged);
943 Region::set_opaque (bool yn)
945 if (opaque() != yn) {
947 _flags = Flag (_flags|Opaque);
949 _flags = Flag (_flags & ~Opaque);
951 send_change (OpacityChanged);
956 Region::set_locked (bool yn)
958 if (locked() != yn) {
960 _flags = Flag (_flags|Locked);
962 _flags = Flag (_flags & ~Locked);
964 send_change (LockChanged);
969 Region::set_position_locked (bool yn)
971 if (position_locked() != yn) {
973 _flags = Flag (_flags|PositionLocked);
975 _flags = Flag (_flags & ~PositionLocked);
977 send_change (LockChanged);
982 Region::set_sync_position (nframes_t absolute_pos)
984 nframes_t const file_pos = _start + (absolute_pos - _position);
986 if (file_pos != _sync_position) {
988 _sync_position = file_pos;
989 _flags = Flag (_flags|SyncMarked);
994 send_change (SyncOffsetChanged);
999 Region::clear_sync_position ()
1001 if (_flags & SyncMarked) {
1002 _flags = Flag (_flags & ~SyncMarked);
1007 send_change (SyncOffsetChanged);
1012 Region::sync_offset (int& dir) const
1014 /* returns the sync point relative the first frame of the region */
1016 if (_flags & SyncMarked) {
1017 if (_sync_position > _start) {
1019 return _sync_position - _start;
1022 return _start - _sync_position;
1031 Region::adjust_to_sync (nframes_t pos) const
1034 nframes_t offset = sync_offset (sync_dir);
1036 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1045 if (max_frames - pos > offset) {
1054 Region::sync_position() const
1056 if (_flags & SyncMarked) {
1057 return _sync_position;
1066 boost::shared_ptr<Playlist> pl (playlist());
1068 pl->raise_region (shared_from_this ());
1075 boost::shared_ptr<Playlist> pl (playlist());
1077 pl->lower_region (shared_from_this ());
1083 Region::raise_to_top ()
1085 boost::shared_ptr<Playlist> pl (playlist());
1087 pl->raise_region_to_top (shared_from_this());
1092 Region::lower_to_bottom ()
1094 boost::shared_ptr<Playlist> pl (playlist());
1096 pl->lower_region_to_bottom (shared_from_this());
1101 Region::set_layer (layer_t l)
1106 send_change (LayerChanged);
1111 Region::state (bool /*full_state*/)
1113 XMLNode *node = new XMLNode ("Region");
1115 const char* fe = NULL;
1117 _id.print (buf, sizeof (buf));
1118 node->add_property ("id", buf);
1119 node->add_property ("name", _name);
1120 node->add_property ("type", _type.to_string());
1121 snprintf (buf, sizeof (buf), "%u", _start);
1122 node->add_property ("start", buf);
1123 snprintf (buf, sizeof (buf), "%u", _length);
1124 node->add_property ("length", buf);
1125 snprintf (buf, sizeof (buf), "%u", _position);
1126 node->add_property ("position", buf);
1127 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1128 node->add_property ("ancestral-start", buf);
1129 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1130 node->add_property ("ancestral-length", buf);
1131 snprintf (buf, sizeof (buf), "%.12g", _stretch);
1132 node->add_property ("stretch", buf);
1133 snprintf (buf, sizeof (buf), "%.12g", _shift);
1134 node->add_property ("shift", buf);
1136 switch (_first_edit) {
1137 case EditChangesNothing:
1140 case EditChangesName:
1146 default: /* should be unreachable but makes g++ happy */
1151 node->add_property ("first-edit", fe);
1153 /* note: flags are stored by derived classes */
1155 snprintf (buf, sizeof (buf), "%d", (int) _layer);
1156 node->add_property ("layer", buf);
1157 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1158 node->add_property ("sync-position", buf);
1160 if (_positional_lock_style != AudioTime) {
1161 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1164 node->add_property ("bbt-position", str.str());
1171 Region::get_state ()
1173 return state (true);
1177 Region::set_live_state (const XMLNode& node, int version, Change& what_changed, bool send)
1179 const XMLNodeList& nlist = node.children();
1180 const XMLProperty *prop;
1183 /* this is responsible for setting those aspects of Region state
1184 that are mutable after construction.
1187 if ((prop = node.property ("name")) == 0) {
1188 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1192 _name = prop->value();
1194 if ((prop = node.property ("type")) == 0) {
1195 _type = DataType::AUDIO;
1197 _type = DataType(prop->value());
1200 if ((prop = node.property ("start")) != 0) {
1201 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1202 if (val != _start) {
1203 what_changed = Change (what_changed|StartChanged);
1210 if ((prop = node.property ("length")) != 0) {
1211 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1212 if (val != _length) {
1213 what_changed = Change (what_changed|LengthChanged);
1214 _last_length = _length;
1218 _last_length = _length;
1222 if ((prop = node.property ("position")) != 0) {
1223 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1224 if (val != _position) {
1225 what_changed = Change (what_changed|PositionChanged);
1226 _last_position = _position;
1230 _last_position = _position;
1234 if ((prop = node.property ("layer")) != 0) {
1236 x = (layer_t) atoi (prop->value().c_str());
1238 what_changed = Change (what_changed|LayerChanged);
1245 if ((prop = node.property ("sync-position")) != 0) {
1246 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1247 if (val != _sync_position) {
1248 what_changed = Change (what_changed|SyncOffsetChanged);
1249 _sync_position = val;
1252 _sync_position = _start;
1255 if ((prop = node.property ("positional-lock-style")) != 0) {
1256 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1258 if (_positional_lock_style == MusicTime) {
1259 if ((prop = node.property ("bbt-position")) == 0) {
1260 /* missing BBT info, revert to audio time locking */
1261 _positional_lock_style = AudioTime;
1263 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1266 &_bbt_time.ticks) != 3) {
1267 _positional_lock_style = AudioTime;
1273 _positional_lock_style = AudioTime;
1276 /* XXX FIRST EDIT !!! */
1278 /* these 3 properties never change as a result of any editing */
1280 if ((prop = node.property ("ancestral-start")) != 0) {
1281 _ancestral_start = strtoll (prop->value().c_str(), 0, 10);
1283 _ancestral_start = _start;
1286 if ((prop = node.property ("ancestral-length")) != 0) {
1287 _ancestral_length = strtoll (prop->value().c_str(), 0, 10);
1289 _ancestral_length = _length;
1292 if ((prop = node.property ("stretch")) != 0) {
1293 _stretch = atof (prop->value());
1295 /* fix problem with old sessions corrupted by an impossible
1298 if (_stretch == 0.0) {
1305 if ((prop = node.property ("shift")) != 0) {
1306 _shift = atof (prop->value());
1308 /* fix problem with old sessions corrupted by an impossible
1311 if (_shift == 0.0) {
1319 /* note: derived classes set flags */
1324 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1330 if (child->name () == "Extra") {
1331 _extra_xml = new XMLNode (*child);
1337 send_change (what_changed);
1344 Region::set_state (const XMLNode& node, int version)
1346 const XMLProperty *prop;
1347 Change what_changed = Change (0);
1349 /* ID is not allowed to change, ever */
1351 if ((prop = node.property ("id")) == 0) {
1352 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1356 _id = prop->value();
1358 _first_edit = EditChangesNothing;
1360 set_live_state (node, version, what_changed, true);
1369 _last_length = _length;
1370 _last_position = _position;
1374 Region::thaw (const string& /*why*/)
1376 Change what_changed = Change (0);
1379 Glib::Mutex::Lock lm (_lock);
1381 if (_frozen && --_frozen > 0) {
1385 if (_pending_changed) {
1386 what_changed = _pending_changed;
1387 _pending_changed = Change (0);
1391 if (what_changed == Change (0)) {
1395 if (what_changed & LengthChanged) {
1396 if (what_changed & PositionChanged) {
1397 recompute_at_start ();
1399 recompute_at_end ();
1402 StateChanged (what_changed);
1406 Region::send_change (Change what_changed)
1409 Glib::Mutex::Lock lm (_lock);
1411 _pending_changed = Change (_pending_changed|what_changed);
1416 StateChanged (what_changed);
1418 if (!(_flags & DoNotSendPropertyChanges)) {
1420 /* Try and send a shared_pointer unless this is part of the constructor.
1425 boost::shared_ptr<Region> rptr = shared_from_this();
1426 RegionPropertyChanged (rptr);
1428 /* no shared_ptr available, relax; */
1435 Region::set_last_layer_op (uint64_t when)
1437 _last_layer_op = when;
1441 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1443 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1447 Region::equivalent (boost::shared_ptr<const Region> other) const
1449 return _start == other->_start &&
1450 _position == other->_position &&
1451 _length == other->_length;
1455 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1457 return _start == other->_start &&
1458 _length == other->_length;
1462 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1464 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1468 Region::source_deleted (boost::shared_ptr<Source>)
1475 Region::master_source_names ()
1477 SourceList::iterator i;
1479 vector<string> names;
1480 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1481 names.push_back((*i)->name());
1488 Region::set_master_sources (const SourceList& srcs)
1490 _master_sources = srcs;
1491 assert (_sources.size() == _master_sources.size());
1495 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1500 SourceList::const_iterator i;
1501 SourceList::const_iterator io;
1503 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1504 if ((*i)->id() != (*io)->id()) {
1509 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1510 if ((*i)->id() != (*io)->id()) {
1519 Region::source_length(uint32_t n) const
1521 return _sources[n]->length(_position - _start);
1525 Region::verify_length (nframes_t len)
1527 if (source() && (source()->destructive() || source()->length_mutable())) {
1531 nframes_t maxlen = 0;
1533 for (uint32_t n=0; n < _sources.size(); ++n) {
1534 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1537 len = min (len, maxlen);
1543 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1545 if (source() && (source()->destructive() || source()->length_mutable())) {
1549 nframes_t maxlen = 0;
1551 for (uint32_t n=0; n < _sources.size(); ++n) {
1552 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1555 new_length = min (new_length, maxlen);
1561 Region::verify_start (nframes_t pos)
1563 if (source() && (source()->destructive() || source()->length_mutable())) {
1567 for (uint32_t n=0; n < _sources.size(); ++n) {
1568 if (pos > source_length(n) - _length) {
1576 Region::verify_start_mutable (nframes_t& new_start)
1578 if (source() && (source()->destructive() || source()->length_mutable())) {
1582 for (uint32_t n=0; n < _sources.size(); ++n) {
1583 if (new_start > source_length(n) - _length) {
1584 new_start = source_length(n) - _length;
1590 boost::shared_ptr<Region>
1591 Region::get_parent() const
1593 boost::shared_ptr<Playlist> pl (playlist());
1596 boost::shared_ptr<Region> r;
1597 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1599 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1600 return boost::static_pointer_cast<Region> (r);
1604 return boost::shared_ptr<Region>();
1608 Region::apply (Filter& filter)
1610 return filter.run (shared_from_this());
1615 Region::invalidate_transients ()
1617 _valid_transients = false;
1618 _transients.clear ();
1623 Region::use_sources (SourceList const & s)
1625 set<boost::shared_ptr<Source> > unique_srcs;
1627 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1628 _sources.push_back (*i);
1629 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), *i));
1630 unique_srcs.insert (*i);
1633 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1634 _master_sources.push_back (*i);
1635 if (unique_srcs.find (*i) == unique_srcs.end()) {
1636 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), *i));