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"
46 using namespace ARDOUR;
49 Change Region::FadeChanged = ARDOUR::new_change ();
50 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
51 Change Region::MuteChanged = ARDOUR::new_change ();
52 Change Region::OpacityChanged = ARDOUR::new_change ();
53 Change Region::LockChanged = ARDOUR::new_change ();
54 Change Region::LayerChanged = ARDOUR::new_change ();
55 Change Region::HiddenChanged = ARDOUR::new_change ();
57 sigc::signal<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
59 /* derived-from-derived constructor (no sources in constructor) */
60 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
61 : SessionObject(s, name)
68 , _positional_lock_style(AudioTime)
69 , _sync_position(_start)
71 , _first_edit(EditChangesNothing)
76 , _pending_changed(Change (0))
79 /* no sources at this point */
82 /** Basic Region constructor (single source) */
83 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
84 : SessionObject(src->session(), name)
91 , _positional_lock_style(AudioTime)
92 , _sync_position(_start)
94 , _first_edit(EditChangesNothing)
96 , _ancestral_start (0)
97 , _ancestral_length (0)
100 , _valid_transients(false)
101 , _read_data_count(0)
102 , _pending_changed(Change (0))
106 _sources.push_back (src);
107 _master_sources.push_back (src);
109 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
111 assert(_sources.size() > 0);
112 _positional_lock_style = AudioTime;
115 /** Basic Region constructor (many sources) */
116 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
117 : SessionObject(srcs.front()->session(), name)
124 , _positional_lock_style(AudioTime)
125 , _sync_position(_start)
127 , _first_edit(EditChangesNothing)
131 , _read_data_count(0)
132 , _pending_changed(Change (0))
136 set<boost::shared_ptr<Source> > unique_srcs;
138 for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
139 _sources.push_back (*i);
140 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
141 unique_srcs.insert (*i);
144 for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
145 _master_sources.push_back (*i);
146 if (unique_srcs.find (*i) == unique_srcs.end()) {
147 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
151 assert(_sources.size() > 0);
154 /** Create a new Region from part of an existing one */
155 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
156 : SessionObject(other->session(), name)
157 , _type (other->data_type())
160 _start = other->_start + offset;
161 copy_stuff (other, offset, length, name, layer, flags);
163 /* if the other region had a distinct sync point
164 set, then continue to use it as best we can.
165 otherwise, reset sync point back to start.
168 if (other->flags() & SyncMarked) {
169 if (other->_sync_position < _start) {
170 _flags = Flag (_flags & ~SyncMarked);
171 _sync_position = _start;
173 _sync_position = other->_sync_position;
176 _flags = Flag (_flags & ~SyncMarked);
177 _sync_position = _start;
180 if (Profile->get_sae()) {
181 /* reset sync point to start if its ended up
182 outside region bounds.
185 if (_sync_position < _start || _sync_position >= _start + _length) {
186 _flags = Flag (_flags & ~SyncMarked);
187 _sync_position = _start;
192 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
193 : SessionObject(other->session(), name)
194 , _type (other->data_type())
196 /* create a new Region exactly like another but starting at 0 in its sources */
199 copy_stuff (other, 0, length, name, layer, flags);
201 /* sync pos is relative to start of file. our start-in-file is now zero,
202 so set our sync position to whatever the the difference between
203 _start and _sync_pos was in the other region.
205 result is that our new sync pos points to the same point in our source(s)
206 as the sync in the other region did in its source(s).
208 since we start at zero in our source(s), it is not possible to use a sync point that
209 is before the start. reset it to _start if that was true in the other region.
212 if (other->flags() & SyncMarked) {
213 if (other->_start < other->_sync_position) {
214 /* sync pos was after the start point of the other region */
215 _sync_position = other->_sync_position - other->_start;
217 /* sync pos was before the start point of the other region. not possible here. */
218 _flags = Flag (_flags & ~SyncMarked);
219 _sync_position = _start;
222 _flags = Flag (_flags & ~SyncMarked);
223 _sync_position = _start;
226 if (Profile->get_sae()) {
227 /* reset sync point to start if its ended up
228 outside region bounds.
231 if (_sync_position < _start || _sync_position >= _start + _length) {
232 _flags = Flag (_flags & ~SyncMarked);
233 _sync_position = _start;
237 /* reset a couple of things that copy_stuff() gets wrong in this particular case */
239 _positional_lock_style = other->_positional_lock_style;
240 _first_edit = other->_first_edit;
243 /** Pure copy constructor */
244 Region::Region (boost::shared_ptr<const Region> other)
245 : SessionObject(other->session(), other->name())
246 , _type(other->data_type())
247 , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
248 , _start(other->_start)
249 , _length(other->_length)
250 , _position(other->_position)
251 , _last_position(other->_last_position)
252 , _positional_lock_style(other->_positional_lock_style)
253 , _sync_position(other->_sync_position)
254 , _layer(other->_layer)
255 , _first_edit(EditChangesID)
257 , _ancestral_start (other->_ancestral_start)
258 , _ancestral_length (other->_ancestral_length)
259 , _stretch (other->_stretch)
260 , _shift (other->_shift)
261 , _valid_transients(false)
262 , _read_data_count(0)
263 , _pending_changed(Change(0))
264 , _last_layer_op(other->_last_layer_op)
266 other->_first_edit = EditChangesName;
268 if (other->_extra_xml) {
269 _extra_xml = new XMLNode (*other->_extra_xml);
274 set<boost::shared_ptr<Source> > unique_srcs;
276 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
277 _sources.push_back (*i);
278 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
279 unique_srcs.insert (*i);
282 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
283 _master_sources.push_back (*i);
284 if (unique_srcs.find (*i) == unique_srcs.end()) {
285 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
289 assert(_sources.size() > 0);
292 Region::Region (const SourceList& srcs, const XMLNode& node)
293 : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
294 , _type(DataType::NIL) // to be loaded from XML
300 , _positional_lock_style(AudioTime)
301 , _sync_position(_start)
303 , _first_edit(EditChangesNothing)
307 , _read_data_count(0)
308 , _pending_changed(Change(0))
311 set<boost::shared_ptr<Source> > unique_srcs;
313 for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
314 _sources.push_back (*i);
315 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
316 unique_srcs.insert (*i);
319 for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
320 _master_sources.push_back (*i);
321 if (unique_srcs.find (*i) == unique_srcs.end()) {
322 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
326 if (set_state (node)) {
327 throw failed_constructor();
330 assert(_type != DataType::NIL);
331 assert(_sources.size() > 0);
334 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
335 : SessionObject(src->session(), X_("error: XML did not reset this"))
336 , _type(DataType::NIL)
342 , _positional_lock_style(AudioTime)
343 , _sync_position(_start)
345 , _first_edit(EditChangesNothing)
349 , _read_data_count(0)
350 , _pending_changed(Change(0))
353 _sources.push_back (src);
355 if (set_state (node)) {
356 throw failed_constructor();
359 assert(_type != DataType::NIL);
360 assert(_sources.size() > 0);
365 boost::shared_ptr<Playlist> pl (playlist());
368 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
369 (*i)->remove_playlist (pl);
371 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
372 (*i)->remove_playlist (pl);
377 GoingAway (); /* EMIT SIGNAL */
381 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
384 _pending_changed = Change (0);
385 _read_data_count = 0;
386 _valid_transients = false;
389 _last_length = length;
390 _sync_position = other->_sync_position;
391 _ancestral_start = other->_ancestral_start;
392 _ancestral_length = other->_ancestral_length;
393 _stretch = other->_stretch;
394 _shift = other->_shift;
399 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
400 _first_edit = EditChangesNothing;
402 _positional_lock_style = AudioTime;
406 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
408 boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
410 boost::shared_ptr<Playlist> pl (wpl.lock());
412 if (old_playlist == pl) {
420 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
421 (*i)->remove_playlist (_playlist);
422 (*i)->add_playlist (pl);
424 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
425 (*i)->remove_playlist (_playlist);
426 (*i)->add_playlist (pl);
429 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
430 (*i)->add_playlist (pl);
432 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
433 (*i)->add_playlist (pl);
438 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
439 (*i)->remove_playlist (old_playlist);
441 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
442 (*i)->remove_playlist (old_playlist);
449 Region::set_name (const std::string& str)
452 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
453 assert(_name == str);
454 send_change (ARDOUR::NameChanged);
461 Region::set_length (nframes_t len, void *src)
463 //cerr << "Region::set_length() len = " << len << endl;
464 if (_flags & Locked) {
468 if (_length != len && len != 0) {
470 /* check that the current _position wouldn't make the new
474 if (max_frames - len < _position) {
478 if (!verify_length (len)) {
483 _last_length = _length;
486 _flags = Region::Flag (_flags & ~WholeFile);
490 invalidate_transients ();
496 send_change (LengthChanged);
501 Region::maybe_uncopy ()
506 Region::first_edit ()
508 boost::shared_ptr<Playlist> pl (playlist());
510 if (_first_edit != EditChangesNothing && pl) {
512 _name = pl->session().new_region_name (_name);
513 _first_edit = EditChangesNothing;
515 send_change (ARDOUR::NameChanged);
516 RegionFactory::CheckNewRegion (shared_from_this());
521 Region::at_natural_position () const
523 boost::shared_ptr<Playlist> pl (playlist());
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 if (_position == whole_file_region->position() + _start) {
541 Region::move_to_natural_position (void *src)
543 boost::shared_ptr<Playlist> pl (playlist());
549 boost::shared_ptr<Region> whole_file_region = get_parent();
551 if (whole_file_region) {
552 set_position (whole_file_region->position() + _start, src);
557 Region::special_set_position (nframes_t pos)
559 /* this is used when creating a whole file region as
560 a way to store its "natural" or "captured" position.
563 _position = _position;
568 Region::set_position_lock_style (PositionLockStyle ps)
570 boost::shared_ptr<Playlist> pl (playlist());
576 _positional_lock_style = ps;
578 if (_positional_lock_style == MusicTime) {
579 pl->session().tempo_map().bbt_time (_position, _bbt_time);
585 Region::update_position_after_tempo_map_change ()
587 boost::shared_ptr<Playlist> pl (playlist());
589 if (!pl || _positional_lock_style != MusicTime) {
593 TempoMap& map (pl->session().tempo_map());
594 nframes_t pos = map.frame_time (_bbt_time);
595 set_position_internal (pos, false);
599 Region::set_position (nframes_t pos, void *src)
605 set_position_internal (pos, true);
609 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
611 if (_position != pos) {
612 _last_position = _position;
615 /* check that the new _position wouldn't make the current
616 length impossible - if so, change the length.
618 XXX is this the right thing to do?
621 if (max_frames - _length < _position) {
622 _last_length = _length;
623 _length = max_frames - _position;
626 if (allow_bbt_recompute) {
627 recompute_position_from_lock_style ();
630 invalidate_transients ();
633 /* do this even if the position is the same. this helps out
634 a GUI that has moved its representation already.
637 send_change (PositionChanged);
641 Region::set_position_on_top (nframes_t pos, void *src)
643 if (_flags & Locked) {
647 if (_position != pos) {
648 _last_position = _position;
652 boost::shared_ptr<Playlist> pl (playlist());
655 pl->raise_region_to_top (shared_from_this ());
658 /* do this even if the position is the same. this helps out
659 a GUI that has moved its representation already.
662 send_change (PositionChanged);
666 Region::recompute_position_from_lock_style ()
668 if (_positional_lock_style == MusicTime) {
669 boost::shared_ptr<Playlist> pl (playlist());
671 pl->session().tempo_map().bbt_time (_position, _bbt_time);
677 Region::nudge_position (nframes64_t n, void *src)
679 if (_flags & Locked) {
687 _last_position = _position;
690 if (_position > max_frames - n) {
691 _position = max_frames;
696 if (_position < (nframes_t) -n) {
703 send_change (PositionChanged);
707 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
709 _ancestral_length = l;
710 _ancestral_start = s;
716 Region::set_start (nframes_t pos, void *src)
718 if (_flags & (Locked|PositionLocked)) {
721 /* This just sets the start, nothing else. It effectively shifts
722 the contents of the Region within the overall extent of the Source,
723 without changing the Region's position or length
728 if (!verify_start (pos)) {
733 _flags = Region::Flag (_flags & ~WholeFile);
735 invalidate_transients ();
737 send_change (StartChanged);
742 Region::trim_start (nframes_t new_position, void *src)
744 if (_flags & (Locked|PositionLocked)) {
750 if (new_position > _position) {
751 start_shift = new_position - _position;
753 start_shift = -(_position - new_position);
756 if (start_shift > 0) {
758 if (_start > max_frames - start_shift) {
759 new_start = max_frames;
761 new_start = _start + start_shift;
764 if (!verify_start (new_start)) {
768 } else if (start_shift < 0) {
770 if (_start < (nframes_t) -start_shift) {
773 new_start = _start + start_shift;
779 if (new_start == _start) {
784 _flags = Region::Flag (_flags & ~WholeFile);
787 send_change (StartChanged);
791 Region::trim_front (nframes_t new_position, void *src)
793 if (_flags & Locked) {
797 nframes_t end = last_frame();
798 nframes_t source_zero;
800 if (_position > _start) {
801 source_zero = _position - _start;
803 source_zero = 0; // its actually negative, but this will work for us
806 if (new_position < end) { /* can't trim it zero or negative length */
810 /* can't trim it back passed where source position zero is located */
812 new_position = max (new_position, source_zero);
815 if (new_position > _position) {
816 newlen = _length - (new_position - _position);
818 newlen = _length + (_position - new_position);
821 trim_to_internal (new_position, newlen, src);
823 recompute_at_start ();
829 Region::trim_end (nframes_t new_endpoint, void *src)
831 if (_flags & Locked) {
835 if (new_endpoint > _position) {
836 trim_to_internal (_position, new_endpoint - _position, this);
844 Region::trim_to (nframes_t position, nframes_t length, void *src)
846 if (_flags & Locked) {
850 trim_to_internal (position, length, src);
853 recompute_at_start ();
859 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
864 if (_flags & Locked) {
868 if (position > _position) {
869 start_shift = position - _position;
871 start_shift = -(_position - position);
874 if (start_shift > 0) {
876 if (_start > max_frames - start_shift) {
877 new_start = max_frames;
879 new_start = _start + start_shift;
883 } else if (start_shift < 0) {
885 if (_start < (nframes_t) -start_shift) {
888 new_start = _start + start_shift;
894 if (!verify_start_and_length (new_start, length)) {
898 Change what_changed = Change (0);
900 if (_start != new_start) {
902 what_changed = Change (what_changed|StartChanged);
904 if (_length != length) {
906 _last_length = _length;
909 what_changed = Change (what_changed|LengthChanged);
911 if (_position != position) {
913 _last_position = _position;
915 _position = position;
916 what_changed = Change (what_changed|PositionChanged);
919 _flags = Region::Flag (_flags & ~WholeFile);
921 if (what_changed & (StartChanged|LengthChanged)) {
926 send_change (what_changed);
931 Region::set_hidden (bool yn)
933 if (hidden() != yn) {
936 _flags = Flag (_flags|Hidden);
938 _flags = Flag (_flags & ~Hidden);
941 send_change (HiddenChanged);
946 Region::set_muted (bool yn)
951 _flags = Flag (_flags|Muted);
953 _flags = Flag (_flags & ~Muted);
956 send_change (MuteChanged);
961 Region::set_opaque (bool yn)
963 if (opaque() != yn) {
965 _flags = Flag (_flags|Opaque);
967 _flags = Flag (_flags & ~Opaque);
969 send_change (OpacityChanged);
974 Region::set_locked (bool yn)
976 if (locked() != yn) {
978 _flags = Flag (_flags|Locked);
980 _flags = Flag (_flags & ~Locked);
982 send_change (LockChanged);
987 Region::set_position_locked (bool yn)
989 if (position_locked() != yn) {
991 _flags = Flag (_flags|PositionLocked);
993 _flags = Flag (_flags & ~PositionLocked);
995 send_change (LockChanged);
1000 Region::set_sync_position (nframes_t absolute_pos)
1004 file_pos = _start + (absolute_pos - _position);
1006 if (file_pos != _sync_position) {
1008 _sync_position = file_pos;
1009 _flags = Flag (_flags|SyncMarked);
1014 send_change (SyncOffsetChanged);
1019 Region::clear_sync_position ()
1021 if (_flags & SyncMarked) {
1022 _flags = Flag (_flags & ~SyncMarked);
1027 send_change (SyncOffsetChanged);
1032 Region::sync_offset (int& dir) const
1034 /* returns the sync point relative the first frame of the region */
1036 if (_flags & SyncMarked) {
1037 if (_sync_position > _start) {
1039 return _sync_position - _start;
1042 return _start - _sync_position;
1051 Region::adjust_to_sync (nframes_t pos) const
1054 nframes_t offset = sync_offset (sync_dir);
1056 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1065 if (max_frames - pos > offset) {
1074 Region::sync_position() const
1076 if (_flags & SyncMarked) {
1077 return _sync_position;
1086 boost::shared_ptr<Playlist> pl (playlist());
1088 pl->raise_region (shared_from_this ());
1095 boost::shared_ptr<Playlist> pl (playlist());
1097 pl->lower_region (shared_from_this ());
1103 Region::raise_to_top ()
1105 boost::shared_ptr<Playlist> pl (playlist());
1107 pl->raise_region_to_top (shared_from_this());
1112 Region::lower_to_bottom ()
1114 boost::shared_ptr<Playlist> pl (playlist());
1116 pl->lower_region_to_bottom (shared_from_this());
1121 Region::set_layer (layer_t l)
1126 send_change (LayerChanged);
1131 Region::state (bool full_state)
1133 XMLNode *node = new XMLNode ("Region");
1135 const char* fe = NULL;
1137 _id.print (buf, sizeof (buf));
1138 node->add_property ("id", buf);
1139 node->add_property ("name", _name);
1140 node->add_property ("type", _type.to_string());
1141 snprintf (buf, sizeof (buf), "%u", _start);
1142 node->add_property ("start", buf);
1143 snprintf (buf, sizeof (buf), "%u", _length);
1144 node->add_property ("length", buf);
1145 snprintf (buf, sizeof (buf), "%u", _position);
1146 node->add_property ("position", buf);
1147 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1148 node->add_property ("ancestral-start", buf);
1149 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1150 node->add_property ("ancestral-length", buf);
1151 snprintf (buf, sizeof (buf), "%.12g", _stretch);
1152 node->add_property ("stretch", buf);
1153 snprintf (buf, sizeof (buf), "%.12g", _shift);
1154 node->add_property ("shift", buf);
1156 switch (_first_edit) {
1157 case EditChangesNothing:
1160 case EditChangesName:
1166 default: /* should be unreachable but makes g++ happy */
1171 node->add_property ("first-edit", fe);
1173 /* note: flags are stored by derived classes */
1175 snprintf (buf, sizeof (buf), "%d", (int) _layer);
1176 node->add_property ("layer", buf);
1177 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1178 node->add_property ("sync-position", buf);
1180 if (_positional_lock_style != AudioTime) {
1181 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1184 node->add_property ("bbt-position", str.str());
1191 Region::get_state ()
1193 return state (true);
1197 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
1199 const XMLNodeList& nlist = node.children();
1200 const XMLProperty *prop;
1203 /* this is responsible for setting those aspects of Region state
1204 that are mutable after construction.
1207 if ((prop = node.property ("name")) == 0) {
1208 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1212 _name = prop->value();
1214 if ((prop = node.property ("type")) == 0) {
1215 _type = DataType::AUDIO;
1217 _type = DataType(prop->value());
1220 if ((prop = node.property ("start")) != 0) {
1221 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1222 if (val != _start) {
1223 what_changed = Change (what_changed|StartChanged);
1230 if ((prop = node.property ("length")) != 0) {
1231 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1232 if (val != _length) {
1233 what_changed = Change (what_changed|LengthChanged);
1234 _last_length = _length;
1238 _last_length = _length;
1242 if ((prop = node.property ("position")) != 0) {
1243 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1244 if (val != _position) {
1245 what_changed = Change (what_changed|PositionChanged);
1246 _last_position = _position;
1250 _last_position = _position;
1254 if ((prop = node.property ("layer")) != 0) {
1256 x = (layer_t) atoi (prop->value().c_str());
1258 what_changed = Change (what_changed|LayerChanged);
1265 if ((prop = node.property ("sync-position")) != 0) {
1266 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1267 if (val != _sync_position) {
1268 what_changed = Change (what_changed|SyncOffsetChanged);
1269 _sync_position = val;
1272 _sync_position = _start;
1275 if ((prop = node.property ("positional-lock-style")) != 0) {
1276 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1278 if (_positional_lock_style == MusicTime) {
1279 if ((prop = node.property ("bbt-position")) == 0) {
1280 /* missing BBT info, revert to audio time locking */
1281 _positional_lock_style = AudioTime;
1283 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1286 &_bbt_time.ticks) != 3) {
1287 _positional_lock_style = AudioTime;
1293 _positional_lock_style = AudioTime;
1296 /* XXX FIRST EDIT !!! */
1298 /* these 3 properties never change as a result of any editing */
1300 if ((prop = node.property ("ancestral-start")) != 0) {
1301 _ancestral_start = atoi (prop->value());
1303 _ancestral_start = _start;
1306 if ((prop = node.property ("ancestral-length")) != 0) {
1307 _ancestral_length = atoi (prop->value());
1309 _ancestral_length = _length;
1312 if ((prop = node.property ("stretch")) != 0) {
1313 _stretch = atof (prop->value());
1315 /* fix problem with old sessions corrupted by an impossible
1318 if (_stretch == 0.0) {
1325 if ((prop = node.property ("shift")) != 0) {
1326 _shift = atof (prop->value());
1328 /* fix problem with old sessions corrupted by an impossible
1331 if (_shift == 0.0) {
1339 /* note: derived classes set flags */
1344 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1350 if (child->name () == "Extra") {
1351 _extra_xml = new XMLNode (*child);
1357 send_change (what_changed);
1364 Region::set_state (const XMLNode& node)
1366 const XMLProperty *prop;
1367 Change what_changed = Change (0);
1369 /* ID is not allowed to change, ever */
1371 if ((prop = node.property ("id")) == 0) {
1372 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1376 _id = prop->value();
1378 _first_edit = EditChangesNothing;
1380 set_live_state (node, what_changed, true);
1389 _last_length = _length;
1390 _last_position = _position;
1394 Region::thaw (const string& why)
1396 Change what_changed = Change (0);
1399 Glib::Mutex::Lock lm (_lock);
1401 if (_frozen && --_frozen > 0) {
1405 if (_pending_changed) {
1406 what_changed = _pending_changed;
1407 _pending_changed = Change (0);
1411 if (what_changed == Change (0)) {
1415 if (what_changed & LengthChanged) {
1416 if (what_changed & PositionChanged) {
1417 recompute_at_start ();
1419 recompute_at_end ();
1422 StateChanged (what_changed);
1426 Region::send_change (Change what_changed)
1429 Glib::Mutex::Lock lm (_lock);
1431 _pending_changed = Change (_pending_changed|what_changed);
1436 StateChanged (what_changed);
1438 if (!(_flags & DoNotSaveState)) {
1440 /* Try and send a shared_pointer unless this is part of the constructor.
1445 boost::shared_ptr<Region> rptr = shared_from_this();
1446 RegionPropertyChanged (rptr);
1448 /* no shared_ptr available, relax; */
1455 Region::set_last_layer_op (uint64_t when)
1457 _last_layer_op = when;
1461 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1463 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1467 Region::equivalent (boost::shared_ptr<const Region> other) const
1469 return _start == other->_start &&
1470 _position == other->_position &&
1471 _length == other->_length;
1475 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1477 return _start == other->_start &&
1478 _length == other->_length;
1482 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1484 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1488 Region::source_deleted (boost::shared_ptr<Source>)
1495 Region::master_source_names ()
1497 SourceList::iterator i;
1499 vector<string> names;
1500 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1501 names.push_back((*i)->name());
1508 Region::set_master_sources (const SourceList& srcs)
1510 _master_sources = srcs;
1514 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1519 SourceList::const_iterator i;
1520 SourceList::const_iterator io;
1522 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1523 if ((*i)->id() != (*io)->id()) {
1528 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1529 if ((*i)->id() != (*io)->id()) {
1538 Region::source_length(uint32_t n) const
1540 return _sources[n]->length(_position - _start);
1544 Region::verify_length (nframes_t len)
1546 if (source() && (source()->destructive() || source()->length_mutable())) {
1550 nframes_t maxlen = 0;
1552 for (uint32_t n=0; n < _sources.size(); ++n) {
1553 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1556 len = min (len, maxlen);
1562 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1564 if (source() && (source()->destructive() || source()->length_mutable())) {
1568 nframes_t maxlen = 0;
1570 for (uint32_t n=0; n < _sources.size(); ++n) {
1571 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1574 new_length = min (new_length, maxlen);
1580 Region::verify_start (nframes_t pos)
1582 if (source() && (source()->destructive() || source()->length_mutable())) {
1586 for (uint32_t n=0; n < _sources.size(); ++n) {
1587 if (pos > source_length(n) - _length) {
1595 Region::verify_start_mutable (nframes_t& new_start)
1597 if (source() && (source()->destructive() || source()->length_mutable())) {
1601 for (uint32_t n=0; n < _sources.size(); ++n) {
1602 if (new_start > source_length(n) - _length) {
1603 new_start = source_length(n) - _length;
1609 boost::shared_ptr<Region>
1610 Region::get_parent() const
1612 boost::shared_ptr<Playlist> pl (playlist());
1615 boost::shared_ptr<Region> r;
1616 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1618 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1619 return boost::static_pointer_cast<Region> (r);
1623 return boost::shared_ptr<Region>();
1627 Region::apply (Filter& filter)
1629 return filter.run (shared_from_this());
1634 Region::invalidate_transients ()
1636 _valid_transients = false;
1637 _transients.clear ();