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/file_source.h"
33 #include "ardour/filter.h"
34 #include "ardour/playlist.h"
35 #include "ardour/playlist_source.h"
36 #include "ardour/profile.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
40 #include "ardour/source.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/utils.h"
48 using namespace ARDOUR;
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> muted;
54 PBD::PropertyDescriptor<bool> opaque;
55 PBD::PropertyDescriptor<bool> locked;
56 PBD::PropertyDescriptor<bool> automatic;
57 PBD::PropertyDescriptor<bool> whole_file;
58 PBD::PropertyDescriptor<bool> import;
59 PBD::PropertyDescriptor<bool> external;
60 PBD::PropertyDescriptor<bool> sync_marked;
61 PBD::PropertyDescriptor<bool> left_of_split;
62 PBD::PropertyDescriptor<bool> right_of_split;
63 PBD::PropertyDescriptor<bool> hidden;
64 PBD::PropertyDescriptor<bool> position_locked;
65 PBD::PropertyDescriptor<bool> valid_transients;
66 PBD::PropertyDescriptor<framepos_t> start;
67 PBD::PropertyDescriptor<framecnt_t> length;
68 PBD::PropertyDescriptor<framepos_t> position;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
92 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
94 Properties::import.property_id = g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
96 Properties::external.property_id = g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
98 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
100 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
102 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
104 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
106 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
108 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
110 Properties::start.property_id = g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
112 Properties::length.property_id = g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
114 Properties::position.property_id = g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
116 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
118 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
120 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
122 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
124 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
126 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
128 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
133 Region::register_properties ()
135 _xml_node_name = X_("Region");
137 add_property (_muted);
138 add_property (_opaque);
139 add_property (_locked);
140 add_property (_automatic);
141 add_property (_whole_file);
142 add_property (_import);
143 add_property (_external);
144 add_property (_sync_marked);
145 add_property (_left_of_split);
146 add_property (_right_of_split);
147 add_property (_hidden);
148 add_property (_position_locked);
149 add_property (_valid_transients);
150 add_property (_start);
151 add_property (_length);
152 add_property (_position);
153 add_property (_sync_position);
154 add_property (_layer);
155 add_property (_ancestral_start);
156 add_property (_ancestral_length);
157 add_property (_stretch);
158 add_property (_shift);
159 add_property (_position_lock_style);
162 #define REGION_DEFAULT_STATE(s,l) \
163 _sync_marked (Properties::sync_marked, false) \
164 , _left_of_split (Properties::left_of_split, false) \
165 , _right_of_split (Properties::right_of_split, false) \
166 , _valid_transients (Properties::valid_transients, false) \
167 , _start (Properties::start, (s)) \
168 , _length (Properties::length, (l)) \
169 , _position (Properties::position, 0) \
170 , _sync_position (Properties::sync_position, (s)) \
171 , _layer (Properties::layer, 0) \
172 , _muted (Properties::muted, false) \
173 , _opaque (Properties::opaque, true) \
174 , _locked (Properties::locked, false) \
175 , _automatic (Properties::automatic, false) \
176 , _whole_file (Properties::whole_file, false) \
177 , _import (Properties::import, false) \
178 , _external (Properties::external, false) \
179 , _hidden (Properties::hidden, false) \
180 , _position_locked (Properties::position_locked, false) \
181 , _ancestral_start (Properties::ancestral_start, (s)) \
182 , _ancestral_length (Properties::ancestral_length, (l)) \
183 , _stretch (Properties::stretch, 1.0) \
184 , _shift (Properties::shift, 1.0) \
185 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
187 #define REGION_COPY_STATE(other) \
188 _sync_marked (Properties::sync_marked, other->_sync_marked) \
189 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
190 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
191 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
192 , _start(Properties::start, other->_start) \
193 , _length(Properties::length, other->_length) \
194 , _position(Properties::position, other->_position) \
195 , _sync_position(Properties::sync_position, other->_sync_position) \
196 , _layer (Properties::layer, other->_layer) \
197 , _muted (Properties::muted, other->_muted) \
198 , _opaque (Properties::opaque, other->_opaque) \
199 , _locked (Properties::locked, other->_locked) \
200 , _automatic (Properties::automatic, other->_automatic) \
201 , _whole_file (Properties::whole_file, other->_whole_file) \
202 , _import (Properties::import, other->_import) \
203 , _external (Properties::external, other->_external) \
204 , _hidden (Properties::hidden, other->_hidden) \
205 , _position_locked (Properties::position_locked, other->_position_locked) \
206 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
207 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
208 , _stretch (Properties::stretch, other->_stretch) \
209 , _shift (Properties::shift, other->_shift) \
210 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
212 /* derived-from-derived constructor (no sources in constructor) */
213 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
214 : SessionObject(s, name)
216 , REGION_DEFAULT_STATE(start,length)
217 , _last_length (length)
219 , _first_edit (EditChangesNothing)
221 , _pending_explicit_relayer (false)
223 register_properties ();
225 /* no sources at this point */
228 /** Basic Region constructor (many sources) */
229 Region::Region (const SourceList& srcs)
230 : SessionObject(srcs.front()->session(), "toBeRenamed")
231 , _type (srcs.front()->type())
232 , REGION_DEFAULT_STATE(0,0)
235 , _first_edit (EditChangesNothing)
237 , _pending_explicit_relayer (false)
239 register_properties ();
241 _type = srcs.front()->type();
245 assert(_sources.size() > 0);
246 assert (_type == srcs.front()->type());
249 /** Create a new Region from an existing one */
250 Region::Region (boost::shared_ptr<const Region> other)
251 : SessionObject(other->session(), other->name())
252 , _type (other->data_type())
253 , REGION_COPY_STATE (other)
254 , _last_length (other->_last_length)
255 , _last_position(other->_last_position) \
256 , _first_edit (EditChangesNothing)
258 , _pending_explicit_relayer (false)
261 register_properties ();
263 /* override state that may have been incorrectly inherited from the other region
271 use_sources (other->_sources);
273 _position_lock_style = other->_position_lock_style;
274 _first_edit = other->_first_edit;
276 _start = 0; // It seems strange _start is not inherited here?
278 /* sync pos is relative to start of file. our start-in-file is now zero,
279 so set our sync position to whatever the the difference between
280 _start and _sync_pos was in the other region.
282 result is that our new sync pos points to the same point in our source(s)
283 as the sync in the other region did in its source(s).
285 since we start at zero in our source(s), it is not possible to use a sync point that
286 is before the start. reset it to _start if that was true in the other region.
289 if (other->sync_marked()) {
290 if (other->_start < other->_sync_position) {
291 /* sync pos was after the start point of the other region */
292 _sync_position = other->_sync_position - other->_start;
294 /* sync pos was before the start point of the other region. not possible here. */
295 _sync_marked = false;
296 _sync_position = _start;
299 _sync_marked = false;
300 _sync_position = _start;
303 if (Profile->get_sae()) {
304 /* reset sync point to start if its ended up
305 outside region bounds.
308 if (_sync_position < _start || _sync_position >= _start + _length) {
309 _sync_marked = false;
310 _sync_position = _start;
314 assert (_type == other->data_type());
317 /** Create a new Region from part of an existing one.
319 the start within \a other is given by \a offset
320 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323 : SessionObject(other->session(), other->name())
324 , _type (other->data_type())
325 , REGION_COPY_STATE (other)
326 , _last_length (other->_last_length)
327 , _last_position(other->_last_position) \
328 , _first_edit (EditChangesNothing)
330 , _pending_explicit_relayer (false)
333 register_properties ();
335 /* override state that may have been incorrectly inherited from the other region
343 use_sources (other->_sources);
345 _start = other->_start + offset;
347 /* if the other region had a distinct sync point
348 set, then continue to use it as best we can.
349 otherwise, reset sync point back to start.
352 if (other->sync_marked()) {
353 if (other->_sync_position < _start) {
354 _sync_marked = false;
355 _sync_position = _start;
357 _sync_position = other->_sync_position;
360 _sync_marked = false;
361 _sync_position = _start;
364 if (Profile->get_sae()) {
365 /* reset sync point to start if its ended up
366 outside region bounds.
369 if (_sync_position < _start || _sync_position >= _start + _length) {
370 _sync_marked = false;
371 _sync_position = _start;
375 assert (_type == other->data_type());
378 /** Create a copy of @param other but with different sources. Used by filters */
379 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
380 : SessionObject (other->session(), other->name())
381 , _type (srcs.front()->type())
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _first_edit (EditChangesID)
386 , _last_layer_op (other->_last_layer_op)
387 , _pending_explicit_relayer (false)
389 register_properties ();
392 _position_locked = false;
394 other->_first_edit = EditChangesName;
396 if (other->_extra_xml) {
397 _extra_xml = new XMLNode (*other->_extra_xml);
403 assert(_sources.size() > 0);
408 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
413 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
415 _playlist = wpl.lock();
419 Region::set_name (const std::string& str)
422 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
423 assert(_name == str);
425 send_change (Properties::name);
432 Region::set_length (framecnt_t len)
434 //cerr << "Region::set_length() len = " << len << endl;
439 if (_length != len && len != 0) {
441 /* check that the current _position wouldn't make the new
445 if (max_framepos - len < _position) {
449 if (!verify_length (len)) {
454 _last_length = _length;
455 set_length_internal (len);
459 invalidate_transients ();
461 if (!property_changes_suspended()) {
465 send_change (Properties::length);
470 Region::set_length_internal (framecnt_t len)
476 Region::maybe_uncopy ()
478 /* this does nothing but marked a semantic moment once upon a time */
482 Region::first_edit ()
484 boost::shared_ptr<Playlist> pl (playlist());
486 if (_first_edit != EditChangesNothing && pl) {
488 _name = RegionFactory::new_region_name (_name);
489 _first_edit = EditChangesNothing;
491 send_change (Properties::name);
493 RegionFactory::CheckNewRegion (shared_from_this());
498 Region::at_natural_position () const
500 boost::shared_ptr<Playlist> pl (playlist());
506 boost::shared_ptr<Region> whole_file_region = get_parent();
508 if (whole_file_region) {
509 if (_position == whole_file_region->position() + _start) {
518 Region::move_to_natural_position ()
520 boost::shared_ptr<Playlist> pl (playlist());
526 boost::shared_ptr<Region> whole_file_region = get_parent();
528 if (whole_file_region) {
529 set_position (whole_file_region->position() + _start);
534 Region::special_set_position (framepos_t pos)
536 /* this is used when creating a whole file region as
537 a way to store its "natural" or "captured" position.
540 _position = _position;
545 Region::set_position_lock_style (PositionLockStyle ps)
547 if (_position_lock_style != ps) {
549 boost::shared_ptr<Playlist> pl (playlist());
551 _position_lock_style = ps;
553 if (_position_lock_style == MusicTime) {
554 _session.tempo_map().bbt_time (_position, _bbt_time);
557 send_change (Properties::position_lock_style);
562 Region::update_after_tempo_map_change ()
564 boost::shared_ptr<Playlist> pl (playlist());
566 if (!pl || _position_lock_style != MusicTime) {
570 TempoMap& map (_session.tempo_map());
571 framepos_t pos = map.frame_time (_bbt_time);
572 set_position_internal (pos, false);
574 /* do this even if the position is the same. this helps out
575 a GUI that has moved its representation already.
577 send_change (Properties::position);
581 Region::set_position (framepos_t pos)
587 set_position_internal (pos, true);
589 /* do this even if the position is the same. this helps out
590 a GUI that has moved its representation already.
592 send_change (Properties::position);
597 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
599 /* We emit a change of Properties::position even if the position hasn't changed
600 (see Region::set_position), so we must always set this up so that
601 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
603 _last_position = _position;
605 if (_position != pos) {
608 /* check that the new _position wouldn't make the current
609 length impossible - if so, change the length.
611 XXX is this the right thing to do?
614 if (max_framepos - _length < _position) {
615 _last_length = _length;
616 _length = max_framepos - _position;
619 if (allow_bbt_recompute) {
620 recompute_position_from_lock_style ();
623 //invalidate_transients ();
628 Region::set_position_on_top (framepos_t pos)
634 if (_position != pos) {
635 set_position_internal (pos, true);
638 boost::shared_ptr<Playlist> pl (playlist());
641 pl->raise_region_to_top (shared_from_this ());
644 /* do this even if the position is the same. this helps out
645 a GUI that has moved its representation already.
647 send_change (Properties::position);
651 Region::recompute_position_from_lock_style ()
653 if (_position_lock_style == MusicTime) {
654 _session.tempo_map().bbt_time (_position, _bbt_time);
659 Region::nudge_position (frameoffset_t n)
669 framepos_t new_position = _position;
672 if (_position > max_framepos - n) {
673 new_position = max_framepos;
678 if (_position < -n) {
685 set_position_internal (new_position, true);
687 send_change (Properties::position);
691 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
693 _ancestral_length = l;
694 _ancestral_start = s;
700 Region::set_start (framepos_t pos)
702 if (locked() || position_locked()) {
705 /* This just sets the start, nothing else. It effectively shifts
706 the contents of the Region within the overall extent of the Source,
707 without changing the Region's position or length
712 if (!verify_start (pos)) {
719 invalidate_transients ();
721 send_change (Properties::start);
726 Region::trim_start (framepos_t new_position)
728 if (locked() || position_locked()) {
731 framepos_t new_start;
732 frameoffset_t const start_shift = new_position - _position;
734 if (start_shift > 0) {
736 if (_start > max_framepos - start_shift) {
737 new_start = max_framepos;
739 new_start = _start + start_shift;
742 if (!verify_start (new_start)) {
746 } else if (start_shift < 0) {
748 if (_start < -start_shift) {
751 new_start = _start + start_shift;
758 if (new_start == _start) {
766 send_change (Properties::start);
770 Region::trim_front (framepos_t new_position)
772 modify_front (new_position, false);
776 Region::cut_front (framepos_t new_position)
778 modify_front (new_position, true);
782 Region::cut_end (framepos_t new_endpoint)
784 modify_end (new_endpoint, true);
788 Region::modify_front (framepos_t new_position, bool reset_fade)
794 framepos_t end = last_frame();
795 framepos_t source_zero;
797 if (_position > _start) {
798 source_zero = _position - _start;
800 source_zero = 0; // its actually negative, but this will work for us
803 if (new_position < end) { /* can't trim it zero or negative length */
805 framecnt_t newlen = 0;
806 framepos_t delta = 0;
808 if (!can_trim_start_before_source_start ()) {
809 /* can't trim it back past where source position zero is located */
810 new_position = max (new_position, source_zero);
813 if (new_position > _position) {
814 newlen = _length - (new_position - _position);
815 delta = -1 * (new_position - _position);
817 newlen = _length + (_position - new_position);
818 delta = _position - new_position;
821 trim_to_internal (new_position, newlen);
824 _right_of_split = true;
827 if (!property_changes_suspended()) {
828 recompute_at_start ();
831 if (_transients.size() > 0){
832 adjust_transients(delta);
838 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
844 if (new_endpoint > _position) {
845 trim_to_internal (_position, new_endpoint - _position);
847 _left_of_split = true;
849 if (!property_changes_suspended()) {
855 /** @param new_endpoint New region end point, such that, for example,
856 * a region at 0 of length 10 has an endpoint of 9.
860 Region::trim_end (framepos_t new_endpoint)
862 modify_end (new_endpoint, false);
866 Region::trim_to (framepos_t position, framecnt_t length)
872 trim_to_internal (position, length);
874 if (!property_changes_suspended()) {
875 recompute_at_start ();
881 Region::trim_to_internal (framepos_t position, framecnt_t length)
883 framepos_t new_start;
889 frameoffset_t const start_shift = position - _position;
891 if (start_shift > 0) {
893 if (_start > max_framepos - start_shift) {
894 new_start = max_framepos;
896 new_start = _start + start_shift;
899 } else if (start_shift < 0) {
901 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
904 new_start = _start + start_shift;
911 if (!verify_start_and_length (new_start, length)) {
915 PropertyChange what_changed;
917 if (_start != new_start) {
919 what_changed.add (Properties::start);
922 /* Set position before length, otherwise for MIDI regions this bad thing happens:
923 * 1. we call set_length_internal; length in beats is computed using the region's current
924 * (soon-to-be old) position
925 * 2. we call set_position_internal; position is set and length in frames re-computed using
926 * length in beats from (1) but at the new position, which is wrong if the region
927 * straddles a tempo/meter change.
930 if (_position != position) {
931 if (!property_changes_suspended()) {
932 _last_position = _position;
934 set_position_internal (position, true);
935 what_changed.add (Properties::position);
938 if (_length != length) {
939 if (!property_changes_suspended()) {
940 _last_length = _length;
942 set_length_internal (length);
943 what_changed.add (Properties::length);
948 PropertyChange start_and_length;
950 start_and_length.add (Properties::start);
951 start_and_length.add (Properties::length);
953 if (what_changed.contains (start_and_length)) {
957 if (!what_changed.empty()) {
958 send_change (what_changed);
963 Region::set_hidden (bool yn)
965 if (hidden() != yn) {
967 send_change (Properties::hidden);
972 Region::set_whole_file (bool yn)
975 /* no change signal */
979 Region::set_automatic (bool yn)
982 /* no change signal */
986 Region::set_muted (bool yn)
990 send_change (Properties::muted);
995 Region::set_opaque (bool yn)
997 if (opaque() != yn) {
999 send_change (Properties::opaque);
1004 Region::set_locked (bool yn)
1006 if (locked() != yn) {
1008 send_change (Properties::locked);
1013 Region::set_position_locked (bool yn)
1015 if (position_locked() != yn) {
1016 _position_locked = yn;
1017 send_change (Properties::locked);
1021 /** Set the region's sync point.
1022 * @param absolute_pos Session time.
1025 Region::set_sync_position (framepos_t absolute_pos)
1027 /* position within our file */
1028 framepos_t const file_pos = _start + (absolute_pos - _position);
1030 if (file_pos != _sync_position) {
1031 _sync_marked = true;
1032 _sync_position = file_pos;
1033 if (!property_changes_suspended()) {
1037 send_change (Properties::sync_position);
1042 Region::clear_sync_position ()
1044 if (sync_marked()) {
1045 _sync_marked = false;
1046 if (!property_changes_suspended()) {
1050 send_change (Properties::sync_position);
1054 /* @return the sync point relative the first frame of the region */
1056 Region::sync_offset (int& dir) const
1058 if (sync_marked()) {
1059 if (_sync_position > _start) {
1061 return _sync_position - _start;
1064 return _start - _sync_position;
1073 Region::adjust_to_sync (framepos_t pos) const
1076 frameoffset_t offset = sync_offset (sync_dir);
1078 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1087 if (max_framepos - pos > offset) {
1095 /** @return Sync position in session time */
1097 Region::sync_position() const
1099 if (sync_marked()) {
1100 return _position - _start + _sync_position;
1102 /* if sync has not been marked, use the start of the region */
1110 boost::shared_ptr<Playlist> pl (playlist());
1112 pl->raise_region (shared_from_this ());
1119 boost::shared_ptr<Playlist> pl (playlist());
1121 pl->lower_region (shared_from_this ());
1127 Region::raise_to_top ()
1129 boost::shared_ptr<Playlist> pl (playlist());
1131 pl->raise_region_to_top (shared_from_this());
1136 Region::lower_to_bottom ()
1138 boost::shared_ptr<Playlist> pl (playlist());
1140 pl->lower_region_to_bottom (shared_from_this());
1145 Region::set_layer (layer_t l)
1150 send_change (Properties::layer);
1157 XMLNode *node = new XMLNode ("Region");
1160 LocaleGuard lg (X_("POSIX"));
1161 const char* fe = NULL;
1163 add_properties (*node);
1165 id().print (buf, sizeof (buf));
1166 node->add_property ("id", buf);
1167 node->add_property ("type", _type.to_string());
1169 switch (_first_edit) {
1170 case EditChangesNothing:
1173 case EditChangesName:
1179 default: /* should be unreachable but makes g++ happy */
1184 node->add_property ("first-edit", fe);
1186 /* note: flags are stored by derived classes */
1188 if (_position_lock_style != AudioTime) {
1191 node->add_property ("bbt-position", str.str());
1194 for (uint32_t n=0; n < _sources.size(); ++n) {
1195 snprintf (buf2, sizeof(buf2), "source-%d", n);
1196 _sources[n]->id().print (buf, sizeof(buf));
1197 node->add_property (buf2, buf);
1200 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1201 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1202 _master_sources[n]->id().print (buf, sizeof (buf));
1203 node->add_property (buf2, buf);
1206 /* Only store nested sources for the whole-file region that acts
1207 as the parent/root of all regions using it.
1210 if (_whole_file && max_source_level() > 0) {
1212 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1214 /* region is compound - get its playlist and
1215 store that before we list the region that
1219 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1220 nested_node->add_child_nocopy ((*s)->get_state ());
1224 node->add_child_nocopy (*nested_node);
1229 node->add_child_copy (*_extra_xml);
1236 Region::get_state ()
1242 Region::set_state (const XMLNode& node, int version)
1244 PropertyChange what_changed;
1245 return _set_state (node, version, what_changed, true);
1249 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1251 const XMLProperty* prop;
1253 Stateful::save_extra_xml (node);
1255 what_changed = set_values (node);
1259 if (_position_lock_style == MusicTime) {
1260 if ((prop = node.property ("bbt-position")) == 0) {
1261 /* missing BBT info, revert to audio time locking */
1262 _position_lock_style = AudioTime;
1264 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1267 &_bbt_time.ticks) != 3) {
1268 _position_lock_style = AudioTime;
1273 /* fix problems with old sessions corrupted by impossible
1274 values for _stretch or _shift
1276 if (_stretch == 0.0f) {
1280 if (_shift == 0.0f) {
1285 send_change (what_changed);
1288 /* Quick fix for 2.x sessions when region is muted */
1289 if ((prop = node.property (X_("flags")))) {
1290 if (string::npos != prop->value().find("Muted")){
1300 Region::suspend_property_changes ()
1302 Stateful::suspend_property_changes ();
1303 _last_length = _length;
1304 _last_position = _position;
1308 Region::mid_thaw (const PropertyChange& what_changed)
1310 if (what_changed.contains (Properties::length)) {
1311 if (what_changed.contains (Properties::position)) {
1312 recompute_at_start ();
1314 recompute_at_end ();
1319 Region::send_change (const PropertyChange& what_changed)
1321 if (what_changed.empty()) {
1325 Stateful::send_change (what_changed);
1327 if (!Stateful::frozen()) {
1329 /* Try and send a shared_pointer unless this is part of the constructor.
1334 boost::shared_ptr<Region> rptr = shared_from_this();
1335 RegionPropertyChanged (rptr, what_changed);
1337 /* no shared_ptr available, relax; */
1343 Region::set_last_layer_op (uint64_t when)
1345 _last_layer_op = when;
1349 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1351 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1355 Region::equivalent (boost::shared_ptr<const Region> other) const
1357 return _start == other->_start &&
1358 _position == other->_position &&
1359 _length == other->_length;
1363 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1365 return _start == other->_start &&
1366 _length == other->_length;
1370 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1372 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1376 Region::source_deleted (boost::weak_ptr<Source>)
1380 if (!_session.deletion_in_progress()) {
1381 /* this is a very special case: at least one of the region's
1382 sources has bee deleted, so invalidate all references to
1383 ourselves. Do NOT do this during session deletion, because
1384 then we run the risk that this will actually result
1385 in this object being deleted (as refcnt goes to zero)
1386 while emitting DropReferences.
1394 Region::master_source_names ()
1396 SourceList::iterator i;
1398 vector<string> names;
1399 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1400 names.push_back((*i)->name());
1407 Region::set_master_sources (const SourceList& srcs)
1409 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1410 (*i)->dec_use_count ();
1413 _master_sources = srcs;
1414 assert (_sources.size() == _master_sources.size());
1416 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1417 (*i)->inc_use_count ();
1422 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1427 if ((_sources.size() != other->_sources.size()) ||
1428 (_master_sources.size() != other->_master_sources.size())) {
1432 SourceList::const_iterator i;
1433 SourceList::const_iterator io;
1435 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1436 if ((*i)->id() != (*io)->id()) {
1441 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1442 if ((*i)->id() != (*io)->id()) {
1451 Region::source_string () const
1453 //string res = itos(_sources.size());
1456 res << _sources.size() << ":";
1458 SourceList::const_iterator i;
1460 for (i = _sources.begin(); i != _sources.end(); ++i) {
1461 res << (*i)->id() << ":";
1464 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1465 res << (*i)->id() << ":";
1472 Region::uses_source (boost::shared_ptr<const Source> source) const
1474 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1479 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1482 if (ps->playlist()->uses_source (source)) {
1492 Region::uses_source_path (const std::string& path) const
1494 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1495 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1497 if (fs->path() == path) {
1506 Region::source_length(uint32_t n) const
1508 assert (n < _sources.size());
1509 return _sources[n]->length (_position - _start);
1513 Region::verify_length (framecnt_t len)
1515 if (source() && (source()->destructive() || source()->length_mutable())) {
1519 framecnt_t maxlen = 0;
1521 for (uint32_t n = 0; n < _sources.size(); ++n) {
1522 maxlen = max (maxlen, source_length(n) - _start);
1525 len = min (len, maxlen);
1531 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1533 if (source() && (source()->destructive() || source()->length_mutable())) {
1537 framecnt_t maxlen = 0;
1539 for (uint32_t n = 0; n < _sources.size(); ++n) {
1540 maxlen = max (maxlen, source_length(n) - new_start);
1543 new_length = min (new_length, maxlen);
1549 Region::verify_start (framepos_t pos)
1551 if (source() && (source()->destructive() || source()->length_mutable())) {
1555 for (uint32_t n = 0; n < _sources.size(); ++n) {
1556 if (pos > source_length(n) - _length) {
1564 Region::verify_start_mutable (framepos_t& new_start)
1566 if (source() && (source()->destructive() || source()->length_mutable())) {
1570 for (uint32_t n = 0; n < _sources.size(); ++n) {
1571 if (new_start > source_length(n) - _length) {
1572 new_start = source_length(n) - _length;
1578 boost::shared_ptr<Region>
1579 Region::get_parent() const
1581 boost::shared_ptr<Playlist> pl (playlist());
1584 boost::shared_ptr<Region> r;
1585 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1587 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1588 return boost::static_pointer_cast<Region> (r);
1592 return boost::shared_ptr<Region>();
1596 Region::apply (Filter& filter, Progress* progress)
1598 return filter.run (shared_from_this(), progress);
1603 Region::invalidate_transients ()
1605 _valid_transients = false;
1606 _transients.clear ();
1608 send_change (PropertyChange (Properties::valid_transients));
1612 Region::drop_sources ()
1614 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1615 (*i)->dec_use_count ();
1620 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1621 (*i)->dec_use_count ();
1624 _master_sources.clear ();
1628 Region::use_sources (SourceList const & s)
1630 set<boost::shared_ptr<Source> > unique_srcs;
1632 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1634 _sources.push_back (*i);
1635 (*i)->inc_use_count ();
1636 _master_sources.push_back (*i);
1637 (*i)->inc_use_count ();
1639 /* connect only once to DropReferences, even if sources are replicated
1642 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1643 unique_srcs.insert (*i);
1644 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1650 Region::can_trim () const
1652 CanTrim ct = CanTrim (0);
1658 /* if not locked, we can always move the front later, and the end earlier
1661 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1663 if (start() != 0 || can_trim_start_before_source_start ()) {
1664 ct = CanTrim (ct | FrontTrimEarlier);
1667 if (!_sources.empty()) {
1668 if ((start() + length()) < _sources.front()->length (0)) {
1669 ct = CanTrim (ct | EndTrimLater);
1677 Region::max_source_level () const
1681 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1682 lvl = max (lvl, (*i)->level());
1689 Region::is_compound () const
1691 return max_source_level() > 0;
1695 Region::post_set (const PropertyChange& pc)
1697 if (pc.contains (Properties::position)) {
1698 recompute_position_from_lock_style ();