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 <glibmm/thread.h>
27 #include "pbd/xml++.h"
28 #include "pbd/stacktrace.h"
29 #include "pbd/enumwriter.h"
31 #include "ardour/debug.h"
32 #include "ardour/region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/source.h"
36 #include "ardour/tempo.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/filter.h"
39 #include "ardour/profile.h"
40 #include "ardour/utils.h"
45 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> automatic;
54 PBD::PropertyDescriptor<bool> whole_file;
55 PBD::PropertyDescriptor<bool> import;
56 PBD::PropertyDescriptor<bool> external;
57 PBD::PropertyDescriptor<bool> sync_marked;
58 PBD::PropertyDescriptor<bool> left_of_split;
59 PBD::PropertyDescriptor<bool> right_of_split;
60 PBD::PropertyDescriptor<bool> hidden;
61 PBD::PropertyDescriptor<bool> position_locked;
62 PBD::PropertyDescriptor<bool> valid_transients;
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;
72 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
79 Region::make_property_quarks ()
81 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
83 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
85 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
87 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
89 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
91 Properties::import.property_id = g_quark_from_static_string (X_("import"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
93 Properties::external.property_id = g_quark_from_static_string (X_("external"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
95 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
97 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
99 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
101 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
103 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
105 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
107 Properties::start.property_id = g_quark_from_static_string (X_("start"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
109 Properties::length.property_id = g_quark_from_static_string (X_("length"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
111 Properties::position.property_id = g_quark_from_static_string (X_("position"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
113 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
115 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
117 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
119 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
121 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
123 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
125 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
130 Region::register_properties ()
132 _xml_node_name = X_("Region");
134 add_property (_muted);
135 add_property (_opaque);
136 add_property (_locked);
137 add_property (_automatic);
138 add_property (_whole_file);
139 add_property (_import);
140 add_property (_external);
141 add_property (_sync_marked);
142 add_property (_left_of_split);
143 add_property (_right_of_split);
144 add_property (_hidden);
145 add_property (_position_locked);
146 add_property (_valid_transients);
147 add_property (_start);
148 add_property (_length);
149 add_property (_position);
150 add_property (_sync_position);
151 add_property (_layer);
152 add_property (_ancestral_start);
153 add_property (_ancestral_length);
154 add_property (_stretch);
155 add_property (_shift);
156 add_property (_position_lock_style);
159 #define REGION_DEFAULT_STATE(s,l) \
160 _muted (Properties::muted, false) \
161 , _opaque (Properties::opaque, true) \
162 , _locked (Properties::locked, false) \
163 , _automatic (Properties::automatic, false) \
164 , _whole_file (Properties::whole_file, false) \
165 , _import (Properties::import, false) \
166 , _external (Properties::external, false) \
167 , _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _hidden (Properties::hidden, false) \
171 , _position_locked (Properties::position_locked, false) \
172 , _valid_transients (Properties::valid_transients, false) \
173 , _start (Properties::start, (s)) \
174 , _length (Properties::length, (l)) \
175 , _position (Properties::position, 0) \
176 , _sync_position (Properties::sync_position, (s)) \
177 , _layer (Properties::layer, 0) \
178 , _ancestral_start (Properties::ancestral_start, (s)) \
179 , _ancestral_length (Properties::ancestral_length, (l)) \
180 , _stretch (Properties::stretch, 1.0) \
181 , _shift (Properties::shift, 1.0) \
182 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
184 #define REGION_COPY_STATE(other) \
185 _muted (Properties::muted, other->_muted) \
186 , _opaque (Properties::opaque, other->_opaque) \
187 , _locked (Properties::locked, other->_locked) \
188 , _automatic (Properties::automatic, other->_automatic) \
189 , _whole_file (Properties::whole_file, other->_whole_file) \
190 , _import (Properties::import, other->_import) \
191 , _external (Properties::external, other->_external) \
192 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
193 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
194 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
195 , _hidden (Properties::hidden, other->_hidden) \
196 , _position_locked (Properties::position_locked, other->_position_locked) \
197 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
198 , _start(Properties::start, other->_start) \
199 , _length(Properties::length, other->_length) \
200 , _position(Properties::position, other->_position) \
201 , _sync_position(Properties::sync_position, other->_sync_position) \
202 , _layer (Properties::layer, other->_layer) \
203 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
204 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
205 , _stretch (Properties::stretch, other->_stretch) \
206 , _shift (Properties::shift, other->_shift) \
207 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
209 /* derived-from-derived constructor (no sources in constructor) */
210 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
211 : SessionObject(s, name)
213 , REGION_DEFAULT_STATE(start,length)
214 , _last_length (length)
216 , _first_edit (EditChangesNothing)
217 , _read_data_count(0)
219 , _pending_explicit_relayer (false)
221 register_properties ();
223 /* no sources at this point */
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228 : SessionObject(srcs.front()->session(), "toBeRenamed")
229 , _type (srcs.front()->type())
230 , REGION_DEFAULT_STATE(0,0)
233 , _first_edit (EditChangesNothing)
234 , _read_data_count(0)
236 , _pending_explicit_relayer (false)
238 register_properties ();
240 _type = srcs.front()->type();
244 assert(_sources.size() > 0);
245 assert (_type == srcs.front()->type());
248 /** Create a new Region from part of an existing one, starting at one of two places:
250 if \a offset_relative is true, then the start within \a other is given by \a offset
251 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
253 if @param offset_relative is false, then the start within the source is given \a offset.
255 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
256 : SessionObject(other->session(), other->name())
257 , _type (other->data_type())
258 , REGION_COPY_STATE (other)
259 , _last_length (other->_last_length)
260 , _last_position(other->_last_position) \
261 , _first_edit (EditChangesNothing)
262 , _read_data_count(0)
264 , _pending_explicit_relayer (false)
267 register_properties ();
269 /* override state that may have been incorrectly inherited from the other region
277 use_sources (other->_sources);
279 if (!offset_relative) {
281 /* not sure why we do this, but its a hangover from ardour before
282 property lists. this would be nice to remove.
285 _position_lock_style = other->_position_lock_style;
286 _first_edit = other->_first_edit;
292 /* sync pos is relative to start of file. our start-in-file is now zero,
293 so set our sync position to whatever the the difference between
294 _start and _sync_pos was in the other region.
296 result is that our new sync pos points to the same point in our source(s)
297 as the sync in the other region did in its source(s).
299 since we start at zero in our source(s), it is not possible to use a sync point that
300 is before the start. reset it to _start if that was true in the other region.
303 if (other->sync_marked()) {
304 if (other->_start < other->_sync_position) {
305 /* sync pos was after the start point of the other region */
306 _sync_position = other->_sync_position - other->_start;
308 /* sync pos was before the start point of the other region. not possible here. */
309 _sync_marked = false;
310 _sync_position = _start;
313 _sync_marked = false;
314 _sync_position = _start;
317 /* XXX do something else ! */
318 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
325 _start = other->_start + offset;
327 /* if the other region had a distinct sync point
328 set, then continue to use it as best we can.
329 otherwise, reset sync point back to start.
332 if (other->sync_marked()) {
333 if (other->_sync_position < _start) {
334 _sync_marked = false;
335 _sync_position = _start;
337 _sync_position = other->_sync_position;
340 _sync_marked = false;
341 _sync_position = _start;
345 if (Profile->get_sae()) {
346 /* reset sync point to start if its ended up
347 outside region bounds.
350 if (_sync_position < _start || _sync_position >= _start + _length) {
351 _sync_marked = false;
352 _sync_position = _start;
356 assert (_type == other->data_type());
359 /** Create a copy of @param other but with different sources. Used by filters */
360 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
361 : SessionObject (other->session(), other->name())
362 , _type (srcs.front()->type())
363 , REGION_COPY_STATE (other)
364 , _last_length (other->_last_length)
365 , _last_position (other->_last_position)
366 , _first_edit (EditChangesID)
367 , _read_data_count (0)
368 , _last_layer_op (other->_last_layer_op)
369 , _pending_explicit_relayer (false)
371 register_properties ();
374 _position_locked = false;
376 other->_first_edit = EditChangesName;
378 if (other->_extra_xml) {
379 _extra_xml = new XMLNode (*other->_extra_xml);
385 assert(_sources.size() > 0);
388 /** Simple "copy" constructor */
389 Region::Region (boost::shared_ptr<const Region> other)
390 : SessionObject(other->session(), other->name())
391 , _type(other->data_type())
392 , REGION_COPY_STATE (other)
393 , _last_length (other->_last_length)
394 , _last_position (other->_last_position)
395 , _first_edit (EditChangesID)
396 , _read_data_count(0)
397 , _last_layer_op(other->_last_layer_op)
398 , _pending_explicit_relayer (false)
400 register_properties ();
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
413 use_sources (other->_sources);
414 assert(_sources.size() > 0);
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
430 Region::set_name (const std::string& str)
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
435 send_change (Properties::name);
442 Region::set_length (framecnt_t len, void */*src*/)
444 //cerr << "Region::set_length() len = " << len << endl;
449 if (_length != len && len != 0) {
451 /* check that the current _position wouldn't make the new
455 if (max_framepos - len < _position) {
459 if (!verify_length (len)) {
464 _last_length = _length;
465 set_length_internal (len);
469 invalidate_transients ();
471 if (!property_changes_suspended()) {
475 send_change (Properties::length);
480 Region::set_length_internal (framecnt_t len)
486 Region::maybe_uncopy ()
488 /* this does nothing but marked a semantic moment once upon a time */
492 Region::first_edit ()
494 boost::shared_ptr<Playlist> pl (playlist());
496 if (_first_edit != EditChangesNothing && pl) {
498 _name = RegionFactory::new_region_name (_name);
499 _first_edit = EditChangesNothing;
501 send_change (Properties::name);
502 RegionFactory::CheckNewRegion (shared_from_this());
507 Region::at_natural_position () const
509 boost::shared_ptr<Playlist> pl (playlist());
515 boost::shared_ptr<Region> whole_file_region = get_parent();
517 if (whole_file_region) {
518 if (_position == whole_file_region->position() + _start) {
527 Region::move_to_natural_position (void *src)
529 boost::shared_ptr<Playlist> pl (playlist());
535 boost::shared_ptr<Region> whole_file_region = get_parent();
537 if (whole_file_region) {
538 set_position (whole_file_region->position() + _start, src);
543 Region::special_set_position (framepos_t pos)
545 /* this is used when creating a whole file region as
546 a way to store its "natural" or "captured" position.
549 _position = _position;
554 Region::set_position_lock_style (PositionLockStyle ps)
556 if (_position_lock_style != ps) {
558 boost::shared_ptr<Playlist> pl (playlist());
564 _position_lock_style = ps;
566 if (_position_lock_style == MusicTime) {
567 _session.tempo_map().bbt_time (_position, _bbt_time);
570 send_change (Properties::position_lock_style);
575 Region::update_position_after_tempo_map_change ()
577 boost::shared_ptr<Playlist> pl (playlist());
579 if (!pl || _position_lock_style != MusicTime) {
583 TempoMap& map (_session.tempo_map());
584 framepos_t pos = map.frame_time (_bbt_time);
585 set_position_internal (pos, false);
587 /* do this even if the position is the same. this helps out
588 a GUI that has moved its representation already.
590 send_change (Properties::position);
594 Region::set_position (framepos_t pos, void* /*src*/)
600 set_position_internal (pos, true);
602 /* do this even if the position is the same. this helps out
603 a GUI that has moved its representation already.
605 send_change (Properties::position);
610 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
612 if (_position != pos) {
613 _last_position = _position;
616 /* check that the new _position wouldn't make the current
617 length impossible - if so, change the length.
619 XXX is this the right thing to do?
622 if (max_framepos - _length < _position) {
623 _last_length = _length;
624 _length = max_framepos - _position;
627 if (allow_bbt_recompute) {
628 recompute_position_from_lock_style ();
631 //invalidate_transients ();
636 Region::set_position_on_top (framepos_t pos, void* /*src*/)
642 if (_position != pos) {
643 set_position_internal (pos, true);
646 boost::shared_ptr<Playlist> pl (playlist());
649 pl->raise_region_to_top (shared_from_this ());
652 /* do this even if the position is the same. this helps out
653 a GUI that has moved its representation already.
656 send_change (Properties::position);
660 Region::recompute_position_from_lock_style ()
662 if (_position_lock_style == MusicTime) {
663 _session.tempo_map().bbt_time (_position, _bbt_time);
668 Region::nudge_position (frameoffset_t n, void* /*src*/)
678 framepos_t new_position = _position;
681 if (_position > max_framepos - n) {
682 new_position = max_framepos;
687 if (_position < -n) {
694 set_position_internal (new_position, true);
696 send_change (Properties::position);
700 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
702 _ancestral_length = l;
703 _ancestral_start = s;
709 Region::set_start (framepos_t pos, void* /*src*/)
711 if (locked() || position_locked()) {
714 /* This just sets the start, nothing else. It effectively shifts
715 the contents of the Region within the overall extent of the Source,
716 without changing the Region's position or length
721 if (!verify_start (pos)) {
728 invalidate_transients ();
730 send_change (Properties::start);
735 Region::trim_start (framepos_t new_position, void */*src*/)
737 if (locked() || position_locked()) {
740 framepos_t new_start;
741 frameoffset_t const start_shift = new_position - _position;
743 if (start_shift > 0) {
745 if (_start > max_framepos - start_shift) {
746 new_start = max_framepos;
748 new_start = _start + start_shift;
751 if (!verify_start (new_start)) {
755 } else if (start_shift < 0) {
757 if (_start < -start_shift) {
760 new_start = _start + start_shift;
767 if (new_start == _start) {
775 send_change (Properties::start);
779 Region::trim_front (framepos_t new_position, void *src)
781 modify_front (new_position, false, src);
785 Region::cut_front (framepos_t new_position, void *src)
787 modify_front (new_position, true, src);
791 Region::cut_end (framepos_t new_endpoint, void *src)
793 modify_end (new_endpoint, true, src);
797 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
803 framepos_t end = last_frame();
804 framepos_t source_zero;
806 if (_position > _start) {
807 source_zero = _position - _start;
809 source_zero = 0; // its actually negative, but this will work for us
812 if (new_position < end) { /* can't trim it zero or negative length */
814 framecnt_t newlen = 0;
815 framepos_t delta = 0;
817 if (!can_trim_start_before_source_start ()) {
818 /* can't trim it back past where source position zero is located */
819 new_position = max (new_position, source_zero);
822 if (new_position > _position) {
823 newlen = _length - (new_position - _position);
824 delta = -1 * (new_position - _position);
826 newlen = _length + (_position - new_position);
827 delta = _position - new_position;
830 trim_to_internal (new_position, newlen, src);
833 _right_of_split = true;
836 if (!property_changes_suspended()) {
837 recompute_at_start ();
840 if (_transients.size() > 0){
841 adjust_transients(delta);
847 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
853 if (new_endpoint > _position) {
854 trim_to_internal (_position, new_endpoint - _position +1, this);
856 _left_of_split = true;
858 if (!property_changes_suspended()) {
864 /** @param new_endpoint New region end point, such that, for example,
865 * a region at 0 of length 10 has an endpoint of 9.
869 Region::trim_end (framepos_t new_endpoint, void* src)
871 modify_end (new_endpoint, false, src);
875 Region::trim_to (framepos_t position, framecnt_t length, void *src)
881 trim_to_internal (position, length, src);
883 if (!property_changes_suspended()) {
884 recompute_at_start ();
890 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
892 framepos_t new_start;
898 frameoffset_t const start_shift = position - _position;
900 if (start_shift > 0) {
902 if (_start > max_framepos - start_shift) {
903 new_start = max_framepos;
905 new_start = _start + start_shift;
908 } else if (start_shift < 0) {
910 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
913 new_start = _start + start_shift;
920 if (!verify_start_and_length (new_start, length)) {
924 PropertyChange what_changed;
926 if (_start != new_start) {
928 what_changed.add (Properties::start);
930 if (_length != length) {
931 if (!property_changes_suspended()) {
932 _last_length = _length;
934 set_length_internal (length);
935 what_changed.add (Properties::length);
937 if (_position != position) {
938 if (!property_changes_suspended()) {
939 _last_position = _position;
941 set_position_internal (position, true);
942 what_changed.add (Properties::position);
947 PropertyChange start_and_length;
949 start_and_length.add (Properties::start);
950 start_and_length.add (Properties::length);
952 if (what_changed.contains (start_and_length)) {
956 if (!what_changed.empty()) {
957 send_change (what_changed);
962 Region::set_hidden (bool yn)
964 if (hidden() != yn) {
966 send_change (Properties::hidden);
971 Region::set_whole_file (bool yn)
974 /* no change signal */
978 Region::set_automatic (bool yn)
981 /* no change signal */
985 Region::set_muted (bool yn)
989 send_change (Properties::muted);
994 Region::set_opaque (bool yn)
996 if (opaque() != yn) {
998 send_change (Properties::opaque);
1003 Region::set_locked (bool yn)
1005 if (locked() != yn) {
1007 send_change (Properties::locked);
1012 Region::set_position_locked (bool yn)
1014 if (position_locked() != yn) {
1015 _position_locked = yn;
1016 send_change (Properties::locked);
1020 /** Set the region's sync point.
1021 * @param absolute_pos Session time.
1024 Region::set_sync_position (framepos_t absolute_pos)
1026 /* position within our file */
1027 framepos_t const file_pos = _start + (absolute_pos - _position);
1029 if (file_pos != _sync_position) {
1030 _sync_marked = true;
1031 _sync_position = file_pos;
1032 if (!property_changes_suspended()) {
1035 send_change (Properties::sync_position);
1040 Region::clear_sync_position ()
1042 if (sync_marked()) {
1043 _sync_marked = false;
1044 if (!property_changes_suspended()) {
1047 send_change (Properties::sync_position);
1051 /* @return the sync point relative the first frame of the region */
1053 Region::sync_offset (int& dir) const
1055 if (sync_marked()) {
1056 if (_sync_position > _start) {
1058 return _sync_position - _start;
1061 return _start - _sync_position;
1070 Region::adjust_to_sync (framepos_t pos) const
1073 frameoffset_t offset = sync_offset (sync_dir);
1075 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1084 if (max_framepos - pos > offset) {
1092 /** @return Sync position in session time */
1094 Region::sync_position() const
1096 if (sync_marked()) {
1097 return _position - _start + _sync_position;
1099 /* if sync has not been marked, use the start of the region */
1107 boost::shared_ptr<Playlist> pl (playlist());
1109 pl->raise_region (shared_from_this ());
1116 boost::shared_ptr<Playlist> pl (playlist());
1118 pl->lower_region (shared_from_this ());
1124 Region::raise_to_top ()
1126 boost::shared_ptr<Playlist> pl (playlist());
1128 pl->raise_region_to_top (shared_from_this());
1133 Region::lower_to_bottom ()
1135 boost::shared_ptr<Playlist> pl (playlist());
1137 pl->lower_region_to_bottom (shared_from_this());
1142 Region::set_layer (layer_t l)
1147 send_change (Properties::layer);
1154 XMLNode *node = new XMLNode ("Region");
1157 LocaleGuard lg (X_("POSIX"));
1158 const char* fe = NULL;
1160 add_properties (*node);
1162 _id.print (buf, sizeof (buf));
1163 node->add_property ("id", buf);
1164 node->add_property ("type", _type.to_string());
1166 switch (_first_edit) {
1167 case EditChangesNothing:
1170 case EditChangesName:
1176 default: /* should be unreachable but makes g++ happy */
1181 node->add_property ("first-edit", fe);
1183 /* note: flags are stored by derived classes */
1185 if (_position_lock_style != AudioTime) {
1188 node->add_property ("bbt-position", str.str());
1191 for (uint32_t n=0; n < _sources.size(); ++n) {
1192 snprintf (buf2, sizeof(buf2), "source-%d", n);
1193 _sources[n]->id().print (buf, sizeof(buf));
1194 node->add_property (buf2, buf);
1197 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1198 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1199 _master_sources[n]->id().print (buf, sizeof (buf));
1200 node->add_property (buf2, buf);
1204 node->add_child_copy (*_extra_xml);
1211 Region::get_state ()
1217 Region::set_state (const XMLNode& node, int version)
1219 PropertyChange what_changed;
1220 return _set_state (node, version, what_changed, true);
1224 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1226 const XMLProperty* prop;
1228 what_changed = set_values (node);
1230 if ((prop = node.property (X_("id")))) {
1231 _id = prop->value();
1234 if (_position_lock_style == MusicTime) {
1235 if ((prop = node.property ("bbt-position")) == 0) {
1236 /* missing BBT info, revert to audio time locking */
1237 _position_lock_style = AudioTime;
1239 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1242 &_bbt_time.ticks) != 3) {
1243 _position_lock_style = AudioTime;
1248 /* fix problems with old sessions corrupted by impossible
1249 values for _stretch or _shift
1251 if (_stretch == 0.0f) {
1255 if (_shift == 0.0f) {
1259 const XMLNodeList& nlist = node.children();
1261 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1267 if (child->name () == "Extra") {
1269 _extra_xml = new XMLNode (*child);
1275 send_change (what_changed);
1278 /* Quick fix for 2.x sessions when region is muted */
1279 if ((prop = node.property (X_("flags")))) {
1280 if (string::npos != prop->value().find("Muted")){
1290 Region::suspend_property_changes ()
1292 Stateful::suspend_property_changes ();
1293 _last_length = _length;
1294 _last_position = _position;
1298 Region::mid_thaw (const PropertyChange& what_changed)
1300 if (what_changed.contains (Properties::length)) {
1301 if (what_changed.contains (Properties::position)) {
1302 recompute_at_start ();
1304 recompute_at_end ();
1309 Region::send_change (const PropertyChange& what_changed)
1311 if (what_changed.empty()) {
1315 Stateful::send_change (what_changed);
1317 if (!Stateful::frozen()) {
1319 /* Try and send a shared_pointer unless this is part of the constructor.
1324 boost::shared_ptr<Region> rptr = shared_from_this();
1325 RegionPropertyChanged (rptr, what_changed);
1327 /* no shared_ptr available, relax; */
1333 Region::set_last_layer_op (uint64_t when)
1335 _last_layer_op = when;
1339 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1341 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1345 Region::equivalent (boost::shared_ptr<const Region> other) const
1347 return _start == other->_start &&
1348 _position == other->_position &&
1349 _length == other->_length;
1353 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1355 return _start == other->_start &&
1356 _length == other->_length;
1360 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1362 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1366 Region::source_deleted (boost::weak_ptr<Source>)
1370 if (!_session.deletion_in_progress()) {
1371 /* this is a very special case: at least one of the region's
1372 sources has bee deleted, so invalidate all references to
1373 ourselves. Do NOT do this during session deletion, because
1374 then we run the risk that this will actually result
1375 in this object being deleted (as refcnt goes to zero)
1376 while emitting DropReferences.
1384 Region::master_source_names ()
1386 SourceList::iterator i;
1388 vector<string> names;
1389 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1390 names.push_back((*i)->name());
1397 Region::set_master_sources (const SourceList& srcs)
1399 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1400 (*i)->dec_use_count ();
1403 _master_sources = srcs;
1404 assert (_sources.size() == _master_sources.size());
1406 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1407 (*i)->inc_use_count ();
1412 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1417 if ((_sources.size() != other->_sources.size()) ||
1418 (_master_sources.size() != other->_master_sources.size())) {
1422 SourceList::const_iterator i;
1423 SourceList::const_iterator io;
1425 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1426 if ((*i)->id() != (*io)->id()) {
1431 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1432 if ((*i)->id() != (*io)->id()) {
1441 Region::uses_source (boost::shared_ptr<const Source> source) const
1443 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1452 Region::source_length(uint32_t n) const
1454 assert (n < _sources.size());
1455 return _sources[n]->length (_position - _start);
1459 Region::verify_length (framecnt_t len)
1461 if (source() && (source()->destructive() || source()->length_mutable())) {
1465 framecnt_t maxlen = 0;
1467 for (uint32_t n = 0; n < _sources.size(); ++n) {
1468 maxlen = max (maxlen, source_length(n) - _start);
1471 len = min (len, maxlen);
1477 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1479 if (source() && (source()->destructive() || source()->length_mutable())) {
1483 framecnt_t maxlen = 0;
1485 for (uint32_t n = 0; n < _sources.size(); ++n) {
1486 maxlen = max (maxlen, source_length(n) - new_start);
1489 new_length = min (new_length, maxlen);
1495 Region::verify_start (framepos_t pos)
1497 if (source() && (source()->destructive() || source()->length_mutable())) {
1501 for (uint32_t n = 0; n < _sources.size(); ++n) {
1502 if (pos > source_length(n) - _length) {
1510 Region::verify_start_mutable (framepos_t& new_start)
1512 if (source() && (source()->destructive() || source()->length_mutable())) {
1516 for (uint32_t n = 0; n < _sources.size(); ++n) {
1517 if (new_start > source_length(n) - _length) {
1518 new_start = source_length(n) - _length;
1524 boost::shared_ptr<Region>
1525 Region::get_parent() const
1527 boost::shared_ptr<Playlist> pl (playlist());
1530 boost::shared_ptr<Region> r;
1531 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1533 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1534 return boost::static_pointer_cast<Region> (r);
1538 return boost::shared_ptr<Region>();
1542 Region::apply (Filter& filter, Progress* progress)
1544 return filter.run (shared_from_this(), progress);
1549 Region::invalidate_transients ()
1551 _valid_transients = false;
1552 _transients.clear ();
1554 send_change (PropertyChange (Properties::valid_transients));
1558 Region::drop_sources ()
1560 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1561 (*i)->dec_use_count ();
1566 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1567 (*i)->dec_use_count ();
1570 _master_sources.clear ();
1574 Region::use_sources (SourceList const & s)
1576 set<boost::shared_ptr<Source> > unique_srcs;
1578 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1580 _sources.push_back (*i);
1581 (*i)->inc_use_count ();
1582 _master_sources.push_back (*i);
1583 (*i)->inc_use_count ();
1585 /* connect only once to DropReferences, even if sources are replicated
1588 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1589 unique_srcs.insert (*i);
1590 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1596 Region::can_trim () const
1598 CanTrim ct = CanTrim (0);
1604 /* if not locked, we can always move the front later, and the end earlier
1607 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1609 if (start() != 0 || can_trim_start_before_source_start ()) {
1610 ct = CanTrim (ct | FrontTrimEarlier);
1613 if (!_sources.empty()) {
1614 if ((start() + length()) < _sources.front()->length (0)) {
1615 ct = CanTrim (ct | EndTrimLater);