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 an existing one */
249 Region::Region (boost::shared_ptr<const Region> other)
250 : SessionObject(other->session(), other->name())
251 , _type (other->data_type())
252 , REGION_COPY_STATE (other)
253 , _last_length (other->_last_length)
254 , _last_position(other->_last_position) \
255 , _first_edit (EditChangesNothing)
256 , _read_data_count(0)
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)
329 , _read_data_count(0)
331 , _pending_explicit_relayer (false)
334 register_properties ();
336 /* override state that may have been incorrectly inherited from the other region
344 use_sources (other->_sources);
346 _start = other->_start + offset;
348 /* if the other region had a distinct sync point
349 set, then continue to use it as best we can.
350 otherwise, reset sync point back to start.
353 if (other->sync_marked()) {
354 if (other->_sync_position < _start) {
355 _sync_marked = false;
356 _sync_position = _start;
358 _sync_position = other->_sync_position;
361 _sync_marked = false;
362 _sync_position = _start;
365 if (Profile->get_sae()) {
366 /* reset sync point to start if its ended up
367 outside region bounds.
370 if (_sync_position < _start || _sync_position >= _start + _length) {
371 _sync_marked = false;
372 _sync_position = _start;
376 assert (_type == other->data_type());
379 /** Create a copy of @param other but with different sources. Used by filters */
380 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
381 : SessionObject (other->session(), other->name())
382 , _type (srcs.front()->type())
383 , REGION_COPY_STATE (other)
384 , _last_length (other->_last_length)
385 , _last_position (other->_last_position)
386 , _first_edit (EditChangesID)
387 , _read_data_count (0)
388 , _last_layer_op (other->_last_layer_op)
389 , _pending_explicit_relayer (false)
391 register_properties ();
394 _position_locked = false;
396 other->_first_edit = EditChangesName;
398 if (other->_extra_xml) {
399 _extra_xml = new XMLNode (*other->_extra_xml);
405 assert(_sources.size() > 0);
410 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
415 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
417 _playlist = wpl.lock();
421 Region::set_name (const std::string& str)
424 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
425 assert(_name == str);
426 send_change (Properties::name);
433 Region::set_length (framecnt_t len, void */*src*/)
435 //cerr << "Region::set_length() len = " << len << endl;
440 if (_length != len && len != 0) {
442 /* check that the current _position wouldn't make the new
446 if (max_framepos - len < _position) {
450 if (!verify_length (len)) {
455 _last_length = _length;
456 set_length_internal (len);
460 invalidate_transients ();
462 if (!property_changes_suspended()) {
466 send_change (Properties::length);
471 Region::set_length_internal (framecnt_t len)
477 Region::maybe_uncopy ()
479 /* this does nothing but marked a semantic moment once upon a time */
483 Region::first_edit ()
485 boost::shared_ptr<Playlist> pl (playlist());
487 if (_first_edit != EditChangesNothing && pl) {
489 _name = RegionFactory::new_region_name (_name);
490 _first_edit = EditChangesNothing;
492 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 (void *src)
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, src);
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());
555 _position_lock_style = ps;
557 if (_position_lock_style == MusicTime) {
558 _session.tempo_map().bbt_time (_position, _bbt_time);
561 send_change (Properties::position_lock_style);
566 Region::update_position_after_tempo_map_change ()
568 boost::shared_ptr<Playlist> pl (playlist());
570 if (!pl || _position_lock_style != MusicTime) {
574 TempoMap& map (_session.tempo_map());
575 framepos_t pos = map.frame_time (_bbt_time);
576 set_position_internal (pos, false);
578 /* do this even if the position is the same. this helps out
579 a GUI that has moved its representation already.
581 send_change (Properties::position);
585 Region::set_position (framepos_t pos, void* /*src*/)
591 set_position_internal (pos, true);
593 /* do this even if the position is the same. this helps out
594 a GUI that has moved its representation already.
596 send_change (Properties::position);
601 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
603 if (_position != pos) {
604 _last_position = _position;
607 /* check that the new _position wouldn't make the current
608 length impossible - if so, change the length.
610 XXX is this the right thing to do?
613 if (max_framepos - _length < _position) {
614 _last_length = _length;
615 _length = max_framepos - _position;
618 if (allow_bbt_recompute) {
619 recompute_position_from_lock_style ();
622 //invalidate_transients ();
627 Region::set_position_on_top (framepos_t pos, void* /*src*/)
633 if (_position != pos) {
634 set_position_internal (pos, true);
637 boost::shared_ptr<Playlist> pl (playlist());
640 pl->raise_region_to_top (shared_from_this ());
643 /* do this even if the position is the same. this helps out
644 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, void* /*src*/)
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, void* /*src*/)
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, void */*src*/)
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, void *src)
772 modify_front (new_position, false, src);
776 Region::cut_front (framepos_t new_position, void *src)
778 modify_front (new_position, true, src);
782 Region::cut_end (framepos_t new_endpoint, void *src)
784 modify_end (new_endpoint, true, src);
788 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
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, src);
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, void* /*src*/)
844 if (new_endpoint > _position) {
845 trim_to_internal (_position, new_endpoint - _position +1, this);
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, void* src)
862 modify_end (new_endpoint, false, src);
866 Region::trim_to (framepos_t position, framecnt_t length, void *src)
872 trim_to_internal (position, length, src);
874 if (!property_changes_suspended()) {
875 recompute_at_start ();
881 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
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);
921 if (_length != length) {
922 if (!property_changes_suspended()) {
923 _last_length = _length;
925 set_length_internal (length);
926 what_changed.add (Properties::length);
928 if (_position != position) {
929 if (!property_changes_suspended()) {
930 _last_position = _position;
932 set_position_internal (position, true);
933 what_changed.add (Properties::position);
938 PropertyChange start_and_length;
940 start_and_length.add (Properties::start);
941 start_and_length.add (Properties::length);
943 if (what_changed.contains (start_and_length)) {
947 if (!what_changed.empty()) {
948 send_change (what_changed);
953 Region::set_hidden (bool yn)
955 if (hidden() != yn) {
957 send_change (Properties::hidden);
962 Region::set_whole_file (bool yn)
965 /* no change signal */
969 Region::set_automatic (bool yn)
972 /* no change signal */
976 Region::set_muted (bool yn)
980 send_change (Properties::muted);
985 Region::set_opaque (bool yn)
987 if (opaque() != yn) {
989 send_change (Properties::opaque);
994 Region::set_locked (bool yn)
996 if (locked() != yn) {
998 send_change (Properties::locked);
1003 Region::set_position_locked (bool yn)
1005 if (position_locked() != yn) {
1006 _position_locked = yn;
1007 send_change (Properties::locked);
1011 /** Set the region's sync point.
1012 * @param absolute_pos Session time.
1015 Region::set_sync_position (framepos_t absolute_pos)
1017 /* position within our file */
1018 framepos_t const file_pos = _start + (absolute_pos - _position);
1020 if (file_pos != _sync_position) {
1021 _sync_marked = true;
1022 _sync_position = file_pos;
1023 if (!property_changes_suspended()) {
1026 send_change (Properties::sync_position);
1031 Region::clear_sync_position ()
1033 if (sync_marked()) {
1034 _sync_marked = false;
1035 if (!property_changes_suspended()) {
1038 send_change (Properties::sync_position);
1042 /* @return the sync point relative the first frame of the region */
1044 Region::sync_offset (int& dir) const
1046 if (sync_marked()) {
1047 if (_sync_position > _start) {
1049 return _sync_position - _start;
1052 return _start - _sync_position;
1061 Region::adjust_to_sync (framepos_t pos) const
1064 frameoffset_t offset = sync_offset (sync_dir);
1066 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1075 if (max_framepos - pos > offset) {
1083 /** @return Sync position in session time */
1085 Region::sync_position() const
1087 if (sync_marked()) {
1088 return _position - _start + _sync_position;
1090 /* if sync has not been marked, use the start of the region */
1098 boost::shared_ptr<Playlist> pl (playlist());
1100 pl->raise_region (shared_from_this ());
1107 boost::shared_ptr<Playlist> pl (playlist());
1109 pl->lower_region (shared_from_this ());
1115 Region::raise_to_top ()
1117 boost::shared_ptr<Playlist> pl (playlist());
1119 pl->raise_region_to_top (shared_from_this());
1124 Region::lower_to_bottom ()
1126 boost::shared_ptr<Playlist> pl (playlist());
1128 pl->lower_region_to_bottom (shared_from_this());
1133 Region::set_layer (layer_t l)
1138 send_change (Properties::layer);
1145 XMLNode *node = new XMLNode ("Region");
1148 LocaleGuard lg (X_("POSIX"));
1149 const char* fe = NULL;
1151 add_properties (*node);
1153 _id.print (buf, sizeof (buf));
1154 node->add_property ("id", buf);
1155 node->add_property ("type", _type.to_string());
1157 switch (_first_edit) {
1158 case EditChangesNothing:
1161 case EditChangesName:
1167 default: /* should be unreachable but makes g++ happy */
1172 node->add_property ("first-edit", fe);
1174 /* note: flags are stored by derived classes */
1176 if (_position_lock_style != AudioTime) {
1179 node->add_property ("bbt-position", str.str());
1182 for (uint32_t n=0; n < _sources.size(); ++n) {
1183 snprintf (buf2, sizeof(buf2), "source-%d", n);
1184 _sources[n]->id().print (buf, sizeof(buf));
1185 node->add_property (buf2, buf);
1188 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1189 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1190 _master_sources[n]->id().print (buf, sizeof (buf));
1191 node->add_property (buf2, buf);
1195 node->add_child_copy (*_extra_xml);
1202 Region::get_state ()
1208 Region::set_state (const XMLNode& node, int version)
1210 PropertyChange what_changed;
1211 return _set_state (node, version, what_changed, true);
1215 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1217 const XMLProperty* prop;
1219 what_changed = set_values (node);
1221 if ((prop = node.property (X_("id")))) {
1222 _id = prop->value();
1225 if (_position_lock_style == MusicTime) {
1226 if ((prop = node.property ("bbt-position")) == 0) {
1227 /* missing BBT info, revert to audio time locking */
1228 _position_lock_style = AudioTime;
1230 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1233 &_bbt_time.ticks) != 3) {
1234 _position_lock_style = AudioTime;
1239 /* fix problems with old sessions corrupted by impossible
1240 values for _stretch or _shift
1242 if (_stretch == 0.0f) {
1246 if (_shift == 0.0f) {
1250 const XMLNodeList& nlist = node.children();
1252 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1258 if (child->name () == "Extra") {
1260 _extra_xml = new XMLNode (*child);
1266 send_change (what_changed);
1269 /* Quick fix for 2.x sessions when region is muted */
1270 if ((prop = node.property (X_("flags")))) {
1271 if (string::npos != prop->value().find("Muted")){
1281 Region::suspend_property_changes ()
1283 Stateful::suspend_property_changes ();
1284 _last_length = _length;
1285 _last_position = _position;
1289 Region::mid_thaw (const PropertyChange& what_changed)
1291 if (what_changed.contains (Properties::length)) {
1292 if (what_changed.contains (Properties::position)) {
1293 recompute_at_start ();
1295 recompute_at_end ();
1300 Region::send_change (const PropertyChange& what_changed)
1302 if (what_changed.empty()) {
1306 Stateful::send_change (what_changed);
1308 if (!Stateful::frozen()) {
1310 /* Try and send a shared_pointer unless this is part of the constructor.
1315 boost::shared_ptr<Region> rptr = shared_from_this();
1316 RegionPropertyChanged (rptr, what_changed);
1318 /* no shared_ptr available, relax; */
1324 Region::set_last_layer_op (uint64_t when)
1326 _last_layer_op = when;
1330 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1332 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1336 Region::equivalent (boost::shared_ptr<const Region> other) const
1338 return _start == other->_start &&
1339 _position == other->_position &&
1340 _length == other->_length;
1344 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1346 return _start == other->_start &&
1347 _length == other->_length;
1351 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1353 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1357 Region::source_deleted (boost::weak_ptr<Source>)
1361 if (!_session.deletion_in_progress()) {
1362 /* this is a very special case: at least one of the region's
1363 sources has bee deleted, so invalidate all references to
1364 ourselves. Do NOT do this during session deletion, because
1365 then we run the risk that this will actually result
1366 in this object being deleted (as refcnt goes to zero)
1367 while emitting DropReferences.
1375 Region::master_source_names ()
1377 SourceList::iterator i;
1379 vector<string> names;
1380 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1381 names.push_back((*i)->name());
1388 Region::set_master_sources (const SourceList& srcs)
1390 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1391 (*i)->dec_use_count ();
1394 _master_sources = srcs;
1395 assert (_sources.size() == _master_sources.size());
1397 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1398 (*i)->inc_use_count ();
1403 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1408 if ((_sources.size() != other->_sources.size()) ||
1409 (_master_sources.size() != other->_master_sources.size())) {
1413 SourceList::const_iterator i;
1414 SourceList::const_iterator io;
1416 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1417 if ((*i)->id() != (*io)->id()) {
1422 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1423 if ((*i)->id() != (*io)->id()) {
1432 Region::uses_source (boost::shared_ptr<const Source> source) const
1434 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1443 Region::source_length(uint32_t n) const
1445 assert (n < _sources.size());
1446 return _sources[n]->length (_position - _start);
1450 Region::verify_length (framecnt_t len)
1452 if (source() && (source()->destructive() || source()->length_mutable())) {
1456 framecnt_t maxlen = 0;
1458 for (uint32_t n = 0; n < _sources.size(); ++n) {
1459 maxlen = max (maxlen, source_length(n) - _start);
1462 len = min (len, maxlen);
1468 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1470 if (source() && (source()->destructive() || source()->length_mutable())) {
1474 framecnt_t maxlen = 0;
1476 for (uint32_t n = 0; n < _sources.size(); ++n) {
1477 maxlen = max (maxlen, source_length(n) - new_start);
1480 new_length = min (new_length, maxlen);
1486 Region::verify_start (framepos_t pos)
1488 if (source() && (source()->destructive() || source()->length_mutable())) {
1492 for (uint32_t n = 0; n < _sources.size(); ++n) {
1493 if (pos > source_length(n) - _length) {
1501 Region::verify_start_mutable (framepos_t& new_start)
1503 if (source() && (source()->destructive() || source()->length_mutable())) {
1507 for (uint32_t n = 0; n < _sources.size(); ++n) {
1508 if (new_start > source_length(n) - _length) {
1509 new_start = source_length(n) - _length;
1515 boost::shared_ptr<Region>
1516 Region::get_parent() const
1518 boost::shared_ptr<Playlist> pl (playlist());
1521 boost::shared_ptr<Region> r;
1522 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1524 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1525 return boost::static_pointer_cast<Region> (r);
1529 return boost::shared_ptr<Region>();
1533 Region::apply (Filter& filter, Progress* progress)
1535 return filter.run (shared_from_this(), progress);
1540 Region::invalidate_transients ()
1542 _valid_transients = false;
1543 _transients.clear ();
1545 send_change (PropertyChange (Properties::valid_transients));
1549 Region::drop_sources ()
1551 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1552 (*i)->dec_use_count ();
1557 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1558 (*i)->dec_use_count ();
1561 _master_sources.clear ();
1565 Region::use_sources (SourceList const & s)
1567 set<boost::shared_ptr<Source> > unique_srcs;
1569 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1571 _sources.push_back (*i);
1572 (*i)->inc_use_count ();
1573 _master_sources.push_back (*i);
1574 (*i)->inc_use_count ();
1576 /* connect only once to DropReferences, even if sources are replicated
1579 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1580 unique_srcs.insert (*i);
1581 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1587 Region::can_trim () const
1589 CanTrim ct = CanTrim (0);
1595 /* if not locked, we can always move the front later, and the end earlier
1598 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1600 if (start() != 0 || can_trim_start_before_source_start ()) {
1601 ct = CanTrim (ct | FrontTrimEarlier);
1604 if (!_sources.empty()) {
1605 if ((start() + length()) < _sources.front()->length (0)) {
1606 ct = CanTrim (ct | EndTrimLater);