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;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> muted;
52 PBD::PropertyDescriptor<bool> opaque;
53 PBD::PropertyDescriptor<bool> locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<framepos_t> start;
64 PBD::PropertyDescriptor<framecnt_t> length;
65 PBD::PropertyDescriptor<framepos_t> position;
66 PBD::PropertyDescriptor<framecnt_t> sync_position;
67 PBD::PropertyDescriptor<layer_t> layer;
68 PBD::PropertyDescriptor<framepos_t> ancestral_start;
69 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
70 PBD::PropertyDescriptor<float> stretch;
71 PBD::PropertyDescriptor<float> shift;
75 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
78 Region::make_property_quarks ()
80 Properties::muted.id = g_quark_from_static_string (X_("muted"));
81 Properties::opaque.id = g_quark_from_static_string (X_("opaque"));
82 Properties::locked.id = g_quark_from_static_string (X_("locked"));
83 Properties::automatic.id = g_quark_from_static_string (X_("automatic"));
84 Properties::whole_file.id = g_quark_from_static_string (X_("whole-file"));
85 Properties::import.id = g_quark_from_static_string (X_("import"));
86 Properties::external.id = g_quark_from_static_string (X_("external"));
87 Properties::sync_marked.id = g_quark_from_static_string (X_("sync-marked"));
88 Properties::left_of_split.id = g_quark_from_static_string (X_("left-of-split"));
89 Properties::right_of_split.id = g_quark_from_static_string (X_("right-of-split"));
90 Properties::hidden.id = g_quark_from_static_string (X_("hidden"));
91 Properties::position_locked.id = g_quark_from_static_string (X_("position-locked"));
92 Properties::start.id = g_quark_from_static_string (X_("start"));
93 Properties::length.id = g_quark_from_static_string (X_("length"));
94 Properties::position.id = g_quark_from_static_string (X_("position"));
95 Properties::sync_position.id = g_quark_from_static_string (X_("sync-position"));
96 Properties::layer.id = g_quark_from_static_string (X_("layer"));
97 Properties::ancestral_start.id = g_quark_from_static_string (X_("ancestral-start"));
98 Properties::ancestral_length.id = g_quark_from_static_string (X_("ancestral-length"));
99 Properties::stretch.id = g_quark_from_static_string (X_("stretch"));
100 Properties::shift.id = g_quark_from_static_string (X_("shift"));
104 Region::register_properties ()
106 _xml_node_name = X_("Region");
108 add_property (_muted);
109 add_property (_opaque);
110 add_property (_locked);
111 add_property (_automatic);
112 add_property (_whole_file);
113 add_property (_import);
114 add_property (_external);
115 add_property (_sync_marked);
116 add_property (_left_of_split);
117 add_property (_right_of_split);
118 add_property (_hidden);
119 add_property (_position_locked);
120 add_property (_start);
121 add_property (_length);
122 add_property (_position);
123 add_property (_sync_position);
124 add_property (_layer);
125 add_property (_ancestral_start);
126 add_property (_ancestral_length);
127 add_property (_stretch);
128 add_property (_shift);
131 #define REGION_DEFAULT_STATE(s,l) \
132 _muted (Properties::muted, false) \
133 , _opaque (Properties::opaque, true) \
134 , _locked (Properties::locked, false) \
135 , _automatic (Properties::automatic, false) \
136 , _whole_file (Properties::whole_file, false) \
137 , _import (Properties::import, false) \
138 , _external (Properties::external, false) \
139 , _sync_marked (Properties::sync_marked, false) \
140 , _left_of_split (Properties::left_of_split, false) \
141 , _right_of_split (Properties::right_of_split, false) \
142 , _hidden (Properties::hidden, false) \
143 , _position_locked (Properties::position_locked, false) \
144 , _start (Properties::start, (s)) \
145 , _length (Properties::length, (l)) \
146 , _position (Properties::position, 0) \
147 , _sync_position (Properties::sync_position, (s)) \
148 , _layer (Properties::layer, 0) \
149 , _ancestral_start (Properties::ancestral_start, (s)) \
150 , _ancestral_length (Properties::ancestral_length, (l)) \
151 , _stretch (Properties::stretch, 1.0) \
152 , _shift (Properties::shift, 1.0)
154 #define REGION_COPY_STATE(other) \
155 _muted (other->_muted) \
156 , _opaque (other->_opaque) \
157 , _locked (other->_locked) \
158 , _automatic (other->_automatic) \
159 , _whole_file (other->_whole_file) \
160 , _import (other->_import) \
161 , _external (other->_external) \
162 , _sync_marked (other->_sync_marked) \
163 , _left_of_split (other->_left_of_split) \
164 , _right_of_split (other->_right_of_split) \
165 , _hidden (other->_hidden) \
166 , _position_locked (other->_position_locked) \
167 , _start(other->_start) \
168 , _length(other->_length) \
169 , _position(other->_position) \
170 , _sync_position(other->_sync_position) \
171 , _layer (other->_layer) \
172 , _ancestral_start (other->_ancestral_start) \
173 , _ancestral_length (other->_ancestral_length) \
174 , _stretch (other->_stretch) \
175 , _shift (other->_shift)
177 /* derived-from-derived constructor (no sources in constructor) */
178 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
179 : SessionObject(s, name)
181 , _no_property_changes (true)
182 , REGION_DEFAULT_STATE(start,length)
183 , _last_length (length)
185 , _positional_lock_style(AudioTime)
186 , _first_edit (EditChangesNothing)
188 , _read_data_count(0)
190 , _pending_explicit_relayer (false)
192 register_properties ();
194 /* no sources at this point */
197 /** Basic Region constructor (many sources) */
198 Region::Region (const SourceList& srcs)
199 : SessionObject(srcs.front()->session(), "toBeRenamed")
200 , _type (srcs.front()->type())
201 , _no_property_changes (true)
202 , REGION_DEFAULT_STATE(0,0)
205 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
206 , _first_edit (EditChangesNothing)
208 , _valid_transients(false)
209 , _read_data_count(0)
211 , _pending_explicit_relayer (false)
213 register_properties ();
215 _type = srcs.front()->type();
219 assert(_sources.size() > 0);
220 assert (_type == srcs.front()->type());
223 /** Create a new Region from part of an existing one, starting at one of two places:
225 if @param offset_relative is true, then the start within @param other is given by @param offset
226 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
228 if @param offset_relative is false, then the start within the source is given @param offset.
230 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
231 : SessionObject(other->session(), "toBeRenamed")
232 , _type (other->data_type())
233 , _no_property_changes (true)
234 , REGION_COPY_STATE (other)
235 , _last_length (other->_last_length)
236 , _last_position(other->_last_position) \
237 , _positional_lock_style(other->_positional_lock_style) \
238 , _first_edit (EditChangesNothing)
240 , _valid_transients(false)
241 , _read_data_count(0)
243 , _pending_explicit_relayer (false)
246 register_properties ();
248 /* override state that may have been incorrectly inherited from the other region
256 use_sources (other->_sources);
258 if (!offset_relative) {
260 /* not sure why we do this, but its a hangover from ardour before
261 property lists. this would be nice to remove.
264 _positional_lock_style = other->_positional_lock_style;
265 _first_edit = other->_first_edit;
271 /* sync pos is relative to start of file. our start-in-file is now zero,
272 so set our sync position to whatever the the difference between
273 _start and _sync_pos was in the other region.
275 result is that our new sync pos points to the same point in our source(s)
276 as the sync in the other region did in its source(s).
278 since we start at zero in our source(s), it is not possible to use a sync point that
279 is before the start. reset it to _start if that was true in the other region.
282 if (other->sync_marked()) {
283 if (other->_start < other->_sync_position) {
284 /* sync pos was after the start point of the other region */
285 _sync_position = other->_sync_position - other->_start;
287 /* sync pos was before the start point of the other region. not possible here. */
288 _sync_marked = false;
289 _sync_position = _start;
292 _sync_marked = false;
293 _sync_position = _start;
296 /* XXX do something else ! */
297 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
304 _start = other->_start + offset;
306 /* if the other region had a distinct sync point
307 set, then continue to use it as best we can.
308 otherwise, reset sync point back to start.
311 if (other->sync_marked()) {
312 if (other->_sync_position < _start) {
313 _sync_marked = false;
314 _sync_position = _start;
316 _sync_position = other->_sync_position;
319 _sync_marked = false;
320 _sync_position = _start;
324 if (Profile->get_sae()) {
325 /* reset sync point to start if its ended up
326 outside region bounds.
329 if (_sync_position < _start || _sync_position >= _start + _length) {
330 _sync_marked = false;
331 _sync_position = _start;
335 assert (_type == other->data_type());
338 /** Create a copy of @param other but with different sources. Used by filters */
339 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
340 : SessionObject (other->session(), other->name())
341 , _type (srcs.front()->type())
342 , _no_property_changes (true)
343 , REGION_COPY_STATE (other)
344 , _last_length (other->_last_length)
345 , _last_position (other->_last_position)
346 , _positional_lock_style (other->_positional_lock_style)
347 , _first_edit (EditChangesID)
349 , _valid_transients (false)
350 , _read_data_count (0)
351 , _last_layer_op (other->_last_layer_op)
352 , _pending_explicit_relayer (false)
354 register_properties ();
357 _position_locked = false;
359 other->_first_edit = EditChangesName;
361 if (other->_extra_xml) {
362 _extra_xml = new XMLNode (*other->_extra_xml);
368 assert(_sources.size() > 0);
371 /** Simple "copy" constructor */
372 Region::Region (boost::shared_ptr<const Region> other)
373 : SessionObject(other->session(), other->name())
374 , _type(other->data_type())
375 , _no_property_changes (true)
376 , REGION_COPY_STATE (other)
377 , _last_length (other->_last_length)
378 , _last_position (other->_last_position)
379 , _positional_lock_style (other->_positional_lock_style)
380 , _first_edit (EditChangesID)
382 , _valid_transients(false)
383 , _read_data_count(0)
384 , _last_layer_op(other->_last_layer_op)
385 , _pending_explicit_relayer (false)
387 register_properties ();
390 _position_locked = false;
392 other->_first_edit = EditChangesName;
394 if (other->_extra_xml) {
395 _extra_xml = new XMLNode (*other->_extra_xml);
400 use_sources (other->_sources);
401 assert(_sources.size() > 0);
406 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
410 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
412 _playlist = wpl.lock();
416 Region::set_name (const std::string& str)
419 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
420 assert(_name == str);
421 send_change (Properties::name);
428 Region::set_length (framecnt_t len, void */*src*/)
430 //cerr << "Region::set_length() len = " << len << endl;
435 if (_length != len && len != 0) {
437 /* check that the current _position wouldn't make the new
441 if (max_frames - len < _position) {
445 if (!verify_length (len)) {
450 _last_length = _length;
455 invalidate_transients ();
461 send_change (Properties::length);
466 Region::maybe_uncopy ()
468 /* this does nothing but marked a semantic moment once upon a time */
472 Region::first_edit ()
474 boost::shared_ptr<Playlist> pl (playlist());
476 if (_first_edit != EditChangesNothing && pl) {
478 _name = _session.new_region_name (_name);
479 _first_edit = EditChangesNothing;
481 send_change (Properties::name);
482 RegionFactory::CheckNewRegion (shared_from_this());
487 Region::at_natural_position () const
489 boost::shared_ptr<Playlist> pl (playlist());
495 boost::shared_ptr<Region> whole_file_region = get_parent();
497 if (whole_file_region) {
498 if (_position == whole_file_region->position() + _start) {
507 Region::move_to_natural_position (void *src)
509 boost::shared_ptr<Playlist> pl (playlist());
515 boost::shared_ptr<Region> whole_file_region = get_parent();
517 if (whole_file_region) {
518 set_position (whole_file_region->position() + _start, src);
523 Region::special_set_position (framepos_t pos)
525 /* this is used when creating a whole file region as
526 a way to store its "natural" or "captured" position.
529 _position = _position;
534 Region::set_position_lock_style (PositionLockStyle ps)
536 boost::shared_ptr<Playlist> pl (playlist());
542 _positional_lock_style = ps;
544 if (_positional_lock_style == MusicTime) {
545 _session.tempo_map().bbt_time (_position, _bbt_time);
551 Region::update_position_after_tempo_map_change ()
553 boost::shared_ptr<Playlist> pl (playlist());
555 if (!pl || _positional_lock_style != MusicTime) {
559 TempoMap& map (_session.tempo_map());
560 framepos_t pos = map.frame_time (_bbt_time);
561 set_position_internal (pos, false);
565 Region::set_position (framepos_t pos, void* /*src*/)
571 set_position_internal (pos, true);
575 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
577 if (_position != pos) {
578 _last_position = _position;
581 /* check that the new _position wouldn't make the current
582 length impossible - if so, change the length.
584 XXX is this the right thing to do?
587 if (max_frames - _length < _position) {
588 _last_length = _length;
589 _length = max_frames - _position;
592 if (allow_bbt_recompute) {
593 recompute_position_from_lock_style ();
596 invalidate_transients ();
599 /* do this even if the position is the same. this helps out
600 a GUI that has moved its representation already.
603 send_change (Properties::position);
607 Region::set_position_on_top (framepos_t pos, void* /*src*/)
613 if (_position != pos) {
614 _last_position = _position;
618 boost::shared_ptr<Playlist> pl (playlist());
621 pl->raise_region_to_top (shared_from_this ());
624 /* do this even if the position is the same. this helps out
625 a GUI that has moved its representation already.
628 send_change (Properties::position);
632 Region::recompute_position_from_lock_style ()
634 if (_positional_lock_style == MusicTime) {
635 _session.tempo_map().bbt_time (_position, _bbt_time);
640 Region::nudge_position (frameoffset_t n, void* /*src*/)
650 _last_position = _position;
653 if (_position > max_frames - n) {
654 _position = max_frames;
659 if (_position < -n) {
666 send_change (Properties::position);
670 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
672 _ancestral_length = l;
673 _ancestral_start = s;
679 Region::set_start (framepos_t pos, void* /*src*/)
681 if (locked() || position_locked()) {
684 /* This just sets the start, nothing else. It effectively shifts
685 the contents of the Region within the overall extent of the Source,
686 without changing the Region's position or length
691 if (!verify_start (pos)) {
698 invalidate_transients ();
700 send_change (Properties::start);
705 Region::trim_start (framepos_t new_position, void */*src*/)
707 if (locked() || position_locked()) {
710 framepos_t new_start;
711 frameoffset_t start_shift;
713 if (new_position > _position) {
714 start_shift = new_position - _position;
716 start_shift = -(_position - new_position);
719 if (start_shift > 0) {
721 if (_start > max_frames - start_shift) {
722 new_start = max_frames;
724 new_start = _start + start_shift;
727 if (!verify_start (new_start)) {
731 } else if (start_shift < 0) {
733 if (_start < -start_shift) {
736 new_start = _start + start_shift;
742 if (new_start == _start) {
750 send_change (Properties::start);
754 Region::trim_front (framepos_t new_position, void *src)
760 framepos_t end = last_frame();
761 framepos_t source_zero;
763 if (_position > _start) {
764 source_zero = _position - _start;
766 source_zero = 0; // its actually negative, but this will work for us
769 if (new_position < end) { /* can't trim it zero or negative length */
773 /* can't trim it back passed where source position zero is located */
775 new_position = max (new_position, source_zero);
778 if (new_position > _position) {
779 newlen = _length - (new_position - _position);
781 newlen = _length + (_position - new_position);
784 trim_to_internal (new_position, newlen, src);
786 recompute_at_start ();
791 /** @param new_endpoint New region end point, such that, for example,
792 * a region at 0 of length 10 has an endpoint of 9.
796 Region::trim_end (framepos_t new_endpoint, void */*src*/)
802 if (new_endpoint > _position) {
803 trim_to_internal (_position, new_endpoint - _position + 1, this);
811 Region::trim_to (framepos_t position, framecnt_t length, void *src)
817 trim_to_internal (position, length, src);
820 recompute_at_start ();
826 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
828 frameoffset_t start_shift;
829 framepos_t new_start;
835 if (position > _position) {
836 start_shift = position - _position;
838 start_shift = -(_position - position);
841 if (start_shift > 0) {
843 if (_start > max_frames - start_shift) {
844 new_start = max_frames;
846 new_start = _start + start_shift;
850 } else if (start_shift < 0) {
852 if (_start < -start_shift) {
855 new_start = _start + start_shift;
861 if (!verify_start_and_length (new_start, length)) {
865 PropertyChange what_changed;
867 if (_start != new_start) {
869 what_changed.add (Properties::start);
871 if (_length != length) {
873 _last_length = _length;
876 what_changed.add (Properties::length);
878 if (_position != position) {
880 _last_position = _position;
882 _position = position;
883 what_changed.add (Properties::position);
888 PropertyChange start_and_length;
890 start_and_length.add (Properties::start);
891 start_and_length.add (Properties::length);
893 if (what_changed.contains (start_and_length)) {
897 if (!what_changed.empty()) {
898 send_change (what_changed);
903 Region::set_hidden (bool yn)
905 if (hidden() != yn) {
907 send_change (Properties::hidden);
912 Region::set_whole_file (bool yn)
915 /* no change signal */
919 Region::set_automatic (bool yn)
922 /* no change signal */
926 Region::set_muted (bool yn)
930 send_change (Properties::muted);
935 Region::set_opaque (bool yn)
937 if (opaque() != yn) {
939 send_change (Properties::opaque);
944 Region::set_locked (bool yn)
946 if (locked() != yn) {
948 send_change (Properties::locked);
953 Region::set_position_locked (bool yn)
955 if (position_locked() != yn) {
956 _position_locked = yn;
957 send_change (Properties::locked);
962 Region::set_sync_position (framepos_t absolute_pos)
964 framepos_t const file_pos = _start + (absolute_pos - _position);
966 if (file_pos != _sync_position) {
968 _sync_position = file_pos;
972 send_change (Properties::sync_position);
977 Region::clear_sync_position ()
980 _sync_marked = false;
984 send_change (Properties::sync_position);
989 Region::sync_offset (int& dir) const
991 /* returns the sync point relative the first frame of the region */
994 if (_sync_position > _start) {
996 return _sync_position - _start;
999 return _start - _sync_position;
1008 Region::adjust_to_sync (framepos_t pos) const
1011 frameoffset_t offset = sync_offset (sync_dir);
1013 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1022 if (max_frames - pos > offset) {
1031 Region::sync_position() const
1033 if (sync_marked()) {
1034 return _sync_position;
1043 boost::shared_ptr<Playlist> pl (playlist());
1045 pl->raise_region (shared_from_this ());
1052 boost::shared_ptr<Playlist> pl (playlist());
1054 pl->lower_region (shared_from_this ());
1060 Region::raise_to_top ()
1062 boost::shared_ptr<Playlist> pl (playlist());
1064 pl->raise_region_to_top (shared_from_this());
1069 Region::lower_to_bottom ()
1071 boost::shared_ptr<Playlist> pl (playlist());
1073 pl->lower_region_to_bottom (shared_from_this());
1078 Region::set_layer (layer_t l)
1083 send_change (Properties::layer);
1088 Region::state (bool /*full_state*/)
1090 XMLNode *node = new XMLNode ("Region");
1092 const char* fe = NULL;
1094 add_properties (*node);
1096 _id.print (buf, sizeof (buf));
1097 node->add_property ("id", buf);
1098 node->add_property ("type", _type.to_string());
1100 switch (_first_edit) {
1101 case EditChangesNothing:
1104 case EditChangesName:
1110 default: /* should be unreachable but makes g++ happy */
1115 node->add_property ("first-edit", fe);
1117 /* note: flags are stored by derived classes */
1119 if (_positional_lock_style != AudioTime) {
1120 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1123 node->add_property ("bbt-position", str.str());
1130 Region::get_state ()
1132 return state (true);
1136 Region::set_state (const XMLNode& node, int version)
1138 PropertyChange what_changed;
1139 return _set_state (node, version, what_changed, true);
1143 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1145 const XMLProperty* prop;
1147 what_changed = set_properties (node);
1149 if ((prop = node.property (X_("id")))) {
1150 _id = prop->value();
1153 if ((prop = node.property ("positional-lock-style")) != 0) {
1154 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1156 if (_positional_lock_style == MusicTime) {
1157 if ((prop = node.property ("bbt-position")) == 0) {
1158 /* missing BBT info, revert to audio time locking */
1159 _positional_lock_style = AudioTime;
1161 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1164 &_bbt_time.ticks) != 3) {
1165 _positional_lock_style = AudioTime;
1172 /* fix problems with old sessions corrupted by impossible
1173 values for _stretch or _shift
1175 if (_stretch == 0.0f) {
1179 if (_shift == 0.0f) {
1183 const XMLNodeList& nlist = node.children();
1185 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1191 if (child->name () == "Extra") {
1193 _extra_xml = new XMLNode (*child);
1199 cerr << _name << ": final change to be sent: ";
1200 for (PropertyChange::iterator i = what_changed.begin(); i != what_changed.end(); ++i) {
1201 cerr << g_quark_to_string ((GQuark) *i) << ' ';
1204 send_change (what_changed);
1214 _last_length = _length;
1215 _last_position = _position;
1221 PropertyChange what_changed;
1224 Glib::Mutex::Lock lm (_lock);
1226 if (_frozen && --_frozen > 0) {
1230 if (!_pending_changed.empty()) {
1231 what_changed = _pending_changed;
1232 _pending_changed.clear ();
1236 if (what_changed.empty()) {
1240 if (what_changed.contains (Properties::length)) {
1241 if (what_changed.contains (Properties::position)) {
1242 recompute_at_start ();
1244 recompute_at_end ();
1247 send_change (what_changed);
1251 Region::send_change (const PropertyChange& what_changed)
1253 if (what_changed.empty()) {
1258 Glib::Mutex::Lock lm (_lock);
1260 _pending_changed.add (what_changed);
1265 PropertyChanged (what_changed);
1267 if (!_no_property_changes) {
1269 /* Try and send a shared_pointer unless this is part of the constructor.
1274 boost::shared_ptr<Region> rptr = shared_from_this();
1275 RegionPropertyChanged (rptr, what_changed);
1278 /* no shared_ptr available, relax; */
1285 Region::set_last_layer_op (uint64_t when)
1287 _last_layer_op = when;
1291 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1293 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1297 Region::equivalent (boost::shared_ptr<const Region> other) const
1299 return _start == other->_start &&
1300 _position == other->_position &&
1301 _length == other->_length;
1305 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1307 return _start == other->_start &&
1308 _length == other->_length;
1312 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1314 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1318 Region::source_deleted (boost::weak_ptr<Source>)
1322 if (!_session.deletion_in_progress()) {
1323 /* this is a very special case: at least one of the region's
1324 sources has bee deleted, so invalidate all references to
1325 ourselves. Do NOT do this during session deletion, because
1326 then we run the risk that this will actually result
1327 in this object being deleted (as refcnt goes to zero)
1328 while emitting DropReferences.
1336 Region::master_source_names ()
1338 SourceList::iterator i;
1340 vector<string> names;
1341 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1342 names.push_back((*i)->name());
1349 Region::set_master_sources (const SourceList& srcs)
1351 _master_sources = srcs;
1352 assert (_sources.size() == _master_sources.size());
1356 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1361 SourceList::const_iterator i;
1362 SourceList::const_iterator io;
1364 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1365 if ((*i)->id() != (*io)->id()) {
1370 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1371 if ((*i)->id() != (*io)->id()) {
1380 Region::uses_source (boost::shared_ptr<const Source> source) const
1382 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1391 Region::source_length(uint32_t n) const
1393 return _sources[n]->length(_position - _start);
1397 Region::verify_length (framecnt_t len)
1399 if (source() && (source()->destructive() || source()->length_mutable())) {
1403 framecnt_t maxlen = 0;
1405 for (uint32_t n=0; n < _sources.size(); ++n) {
1406 maxlen = max (maxlen, source_length(n) - _start);
1409 len = min (len, maxlen);
1415 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1417 if (source() && (source()->destructive() || source()->length_mutable())) {
1421 framecnt_t maxlen = 0;
1423 for (uint32_t n=0; n < _sources.size(); ++n) {
1424 maxlen = max (maxlen, source_length(n) - new_start);
1427 new_length = min (new_length, maxlen);
1433 Region::verify_start (framepos_t pos)
1435 if (source() && (source()->destructive() || source()->length_mutable())) {
1439 for (uint32_t n=0; n < _sources.size(); ++n) {
1440 if (pos > source_length(n) - _length) {
1448 Region::verify_start_mutable (framepos_t& new_start)
1450 if (source() && (source()->destructive() || source()->length_mutable())) {
1454 for (uint32_t n=0; n < _sources.size(); ++n) {
1455 if (new_start > source_length(n) - _length) {
1456 new_start = source_length(n) - _length;
1462 boost::shared_ptr<Region>
1463 Region::get_parent() const
1465 boost::shared_ptr<Playlist> pl (playlist());
1468 boost::shared_ptr<Region> r;
1469 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1471 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1472 return boost::static_pointer_cast<Region> (r);
1476 return boost::shared_ptr<Region>();
1480 Region::apply (Filter& filter)
1482 return filter.run (shared_from_this());
1487 Region::invalidate_transients ()
1489 _valid_transients = false;
1490 _transients.clear ();
1495 Region::use_sources (SourceList const & s)
1497 set<boost::shared_ptr<Source> > unique_srcs;
1499 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1500 _sources.push_back (*i);
1501 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1502 unique_srcs.insert (*i);
1505 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1506 _master_sources.push_back (*i);
1507 if (unique_srcs.find (*i) == unique_srcs.end()) {
1508 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1515 Region::set_property (const PropertyBase& prop)
1517 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 set property %2\n", _name.val(), prop.property_name()));
1519 if (prop == Properties::muted.id) {
1520 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1521 if (val != _muted) {
1522 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 muted changed from %2 to %3",
1523 _name.val(), _muted.val(), val));
1527 } else if (prop == Properties::opaque.id) {
1528 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1529 if (val != _opaque) {
1530 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 opaque changed from %2 to %3",
1531 _name.val(), _opaque.val(), val));
1535 } else if (prop == Properties::locked.id) {
1536 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1537 if (val != _locked) {
1538 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 locked changed from %2 to %3",
1539 _name.val(), _locked.val(), val));
1543 } else if (prop == Properties::automatic.id) {
1544 _automatic = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1545 } else if (prop == Properties::whole_file.id) {
1546 _whole_file = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1547 } else if (prop == Properties::import.id) {
1548 _import = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1549 } else if (prop == Properties::external.id) {
1550 _external = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1551 } else if (prop == Properties::sync_marked.id) {
1552 _sync_marked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1553 } else if (prop == Properties::left_of_split.id) {
1554 _left_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1555 } else if (prop == Properties::right_of_split.id) {
1556 _right_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1557 } else if (prop == Properties::hidden.id) {
1558 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1559 if (val != _hidden) {
1563 } else if (prop == Properties::position_locked.id) {
1564 _position_locked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1565 } else if (prop == Properties::start.id) {
1566 _start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1567 } else if (prop == Properties::length.id) {
1568 framecnt_t val = dynamic_cast<const PropertyTemplate<framecnt_t>* > (&prop)->val();
1569 if (val != _length) {
1573 } else if (prop == Properties::position.id) {
1574 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1575 if (val != _position) {
1579 } else if (prop == Properties::sync_position.id) {
1580 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1581 if (val != _sync_position) {
1582 _sync_position = val;
1585 } else if (prop == Properties::layer.id) {
1586 layer_t val = dynamic_cast<const PropertyTemplate<layer_t>*>(&prop)->val();
1587 if (val != _layer) {
1591 } else if (prop == Properties::ancestral_start.id) {
1592 _ancestral_start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1593 } else if (prop == Properties::ancestral_length.id) {
1594 _ancestral_length = dynamic_cast<const PropertyTemplate<framecnt_t>*>(&prop)->val();
1595 } else if (prop == Properties::stretch.id) {
1596 _stretch = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1597 } else if (prop == Properties::shift.id) {
1598 _shift = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1600 return SessionObject::set_property (prop);