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/profile.h"
36 #include "ardour/region.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/session.h"
39 #include "ardour/source.h"
40 #include "ardour/source_factory.h"
41 #include "ardour/tempo.h"
42 #include "ardour/utils.h"
47 using namespace ARDOUR;
51 namespace Properties {
52 PBD::PropertyDescriptor<bool> muted;
53 PBD::PropertyDescriptor<bool> opaque;
54 PBD::PropertyDescriptor<bool> locked;
55 PBD::PropertyDescriptor<bool> automatic;
56 PBD::PropertyDescriptor<bool> whole_file;
57 PBD::PropertyDescriptor<bool> import;
58 PBD::PropertyDescriptor<bool> external;
59 PBD::PropertyDescriptor<bool> sync_marked;
60 PBD::PropertyDescriptor<bool> left_of_split;
61 PBD::PropertyDescriptor<bool> right_of_split;
62 PBD::PropertyDescriptor<bool> hidden;
63 PBD::PropertyDescriptor<bool> position_locked;
64 PBD::PropertyDescriptor<bool> valid_transients;
65 PBD::PropertyDescriptor<framepos_t> start;
66 PBD::PropertyDescriptor<framecnt_t> length;
67 PBD::PropertyDescriptor<framepos_t> position;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
91 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
93 Properties::import.property_id = g_quark_from_static_string (X_("import"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
95 Properties::external.property_id = g_quark_from_static_string (X_("external"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
97 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
99 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
101 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
103 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
105 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
107 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
109 Properties::start.property_id = g_quark_from_static_string (X_("start"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
111 Properties::length.property_id = g_quark_from_static_string (X_("length"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
113 Properties::position.property_id = g_quark_from_static_string (X_("position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
115 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
117 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
119 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
121 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
123 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
125 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
127 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
132 Region::register_properties ()
134 _xml_node_name = X_("Region");
136 add_property (_muted);
137 add_property (_opaque);
138 add_property (_locked);
139 add_property (_automatic);
140 add_property (_whole_file);
141 add_property (_import);
142 add_property (_external);
143 add_property (_sync_marked);
144 add_property (_left_of_split);
145 add_property (_right_of_split);
146 add_property (_hidden);
147 add_property (_position_locked);
148 add_property (_valid_transients);
149 add_property (_start);
150 add_property (_length);
151 add_property (_position);
152 add_property (_sync_position);
153 add_property (_layer);
154 add_property (_ancestral_start);
155 add_property (_ancestral_length);
156 add_property (_stretch);
157 add_property (_shift);
158 add_property (_position_lock_style);
161 #define REGION_DEFAULT_STATE(s,l) \
162 _muted (Properties::muted, false) \
163 , _opaque (Properties::opaque, true) \
164 , _locked (Properties::locked, false) \
165 , _automatic (Properties::automatic, false) \
166 , _whole_file (Properties::whole_file, false) \
167 , _import (Properties::import, false) \
168 , _external (Properties::external, false) \
169 , _sync_marked (Properties::sync_marked, false) \
170 , _left_of_split (Properties::left_of_split, false) \
171 , _right_of_split (Properties::right_of_split, false) \
172 , _hidden (Properties::hidden, false) \
173 , _position_locked (Properties::position_locked, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _sync_position (Properties::sync_position, (s)) \
179 , _layer (Properties::layer, 0) \
180 , _ancestral_start (Properties::ancestral_start, (s)) \
181 , _ancestral_length (Properties::ancestral_length, (l)) \
182 , _stretch (Properties::stretch, 1.0) \
183 , _shift (Properties::shift, 1.0) \
184 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
186 #define REGION_COPY_STATE(other) \
187 _muted (Properties::muted, other->_muted) \
188 , _opaque (Properties::opaque, other->_opaque) \
189 , _locked (Properties::locked, other->_locked) \
190 , _automatic (Properties::automatic, other->_automatic) \
191 , _whole_file (Properties::whole_file, other->_whole_file) \
192 , _import (Properties::import, other->_import) \
193 , _external (Properties::external, other->_external) \
194 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
195 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
196 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
197 , _hidden (Properties::hidden, other->_hidden) \
198 , _position_locked (Properties::position_locked, other->_position_locked) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _layer (Properties::layer, other->_layer) \
205 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
206 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
207 , _stretch (Properties::stretch, other->_stretch) \
208 , _shift (Properties::shift, other->_shift) \
209 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
211 /* derived-from-derived constructor (no sources in constructor) */
212 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
213 : SessionObject(s, name)
215 , REGION_DEFAULT_STATE(start,length)
216 , _last_length (length)
218 , _first_edit (EditChangesNothing)
219 , _read_data_count(0)
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)
236 , _read_data_count(0)
238 , _pending_explicit_relayer (false)
240 register_properties ();
242 _type = srcs.front()->type();
246 assert(_sources.size() > 0);
247 assert (_type == srcs.front()->type());
250 /** Create a new Region from an existing one */
251 Region::Region (boost::shared_ptr<const Region> other)
252 : SessionObject(other->session(), other->name())
253 , _type (other->data_type())
254 , REGION_COPY_STATE (other)
255 , _last_length (other->_last_length)
256 , _last_position(other->_last_position) \
257 , _first_edit (EditChangesNothing)
258 , _read_data_count(0)
260 , _pending_explicit_relayer (false)
263 register_properties ();
265 /* override state that may have been incorrectly inherited from the other region
273 use_sources (other->_sources);
275 _position_lock_style = other->_position_lock_style;
276 _first_edit = other->_first_edit;
278 _start = 0; // It seems strange _start is not inherited here?
280 /* sync pos is relative to start of file. our start-in-file is now zero,
281 so set our sync position to whatever the the difference between
282 _start and _sync_pos was in the other region.
284 result is that our new sync pos points to the same point in our source(s)
285 as the sync in the other region did in its source(s).
287 since we start at zero in our source(s), it is not possible to use a sync point that
288 is before the start. reset it to _start if that was true in the other region.
291 if (other->sync_marked()) {
292 if (other->_start < other->_sync_position) {
293 /* sync pos was after the start point of the other region */
294 _sync_position = other->_sync_position - other->_start;
296 /* sync pos was before the start point of the other region. not possible here. */
297 _sync_marked = false;
298 _sync_position = _start;
301 _sync_marked = false;
302 _sync_position = _start;
305 if (Profile->get_sae()) {
306 /* reset sync point to start if its ended up
307 outside region bounds.
310 if (_sync_position < _start || _sync_position >= _start + _length) {
311 _sync_marked = false;
312 _sync_position = _start;
316 assert (_type == other->data_type());
319 /** Create a new Region from part of an existing one.
321 the start within \a other is given by \a offset
322 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
324 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
325 : SessionObject(other->session(), other->name())
326 , _type (other->data_type())
327 , REGION_COPY_STATE (other)
328 , _last_length (other->_last_length)
329 , _last_position(other->_last_position) \
330 , _first_edit (EditChangesNothing)
331 , _read_data_count(0)
333 , _pending_explicit_relayer (false)
336 register_properties ();
338 /* override state that may have been incorrectly inherited from the other region
346 use_sources (other->_sources);
348 _start = other->_start + offset;
350 /* if the other region had a distinct sync point
351 set, then continue to use it as best we can.
352 otherwise, reset sync point back to start.
355 if (other->sync_marked()) {
356 if (other->_sync_position < _start) {
357 _sync_marked = false;
358 _sync_position = _start;
360 _sync_position = other->_sync_position;
363 _sync_marked = false;
364 _sync_position = _start;
367 if (Profile->get_sae()) {
368 /* reset sync point to start if its ended up
369 outside region bounds.
372 if (_sync_position < _start || _sync_position >= _start + _length) {
373 _sync_marked = false;
374 _sync_position = _start;
378 assert (_type == other->data_type());
381 /** Create a copy of @param other but with different sources. Used by filters */
382 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
383 : SessionObject (other->session(), other->name())
384 , _type (srcs.front()->type())
385 , REGION_COPY_STATE (other)
386 , _last_length (other->_last_length)
387 , _last_position (other->_last_position)
388 , _first_edit (EditChangesID)
389 , _read_data_count (0)
390 , _last_layer_op (other->_last_layer_op)
391 , _pending_explicit_relayer (false)
393 register_properties ();
396 _position_locked = false;
398 other->_first_edit = EditChangesName;
400 if (other->_extra_xml) {
401 _extra_xml = new XMLNode (*other->_extra_xml);
407 assert(_sources.size() > 0);
412 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
417 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
419 _playlist = wpl.lock();
423 Region::set_name (const std::string& str)
426 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
427 assert(_name == str);
429 send_change (Properties::name);
436 Region::set_length (framecnt_t len, void */*src*/)
438 //cerr << "Region::set_length() len = " << len << endl;
443 if (_length != len && len != 0) {
445 /* check that the current _position wouldn't make the new
449 if (max_framepos - len < _position) {
453 if (!verify_length (len)) {
458 _last_length = _length;
459 set_length_internal (len);
463 invalidate_transients ();
465 if (!property_changes_suspended()) {
469 send_change (Properties::length);
474 Region::set_length_internal (framecnt_t len)
480 Region::maybe_uncopy ()
482 /* this does nothing but marked a semantic moment once upon a time */
486 Region::first_edit ()
488 boost::shared_ptr<Playlist> pl (playlist());
490 if (_first_edit != EditChangesNothing && pl) {
492 _name = RegionFactory::new_region_name (_name);
493 _first_edit = EditChangesNothing;
495 send_change (Properties::name);
497 RegionFactory::CheckNewRegion (shared_from_this());
502 Region::at_natural_position () const
504 boost::shared_ptr<Playlist> pl (playlist());
510 boost::shared_ptr<Region> whole_file_region = get_parent();
512 if (whole_file_region) {
513 if (_position == whole_file_region->position() + _start) {
522 Region::move_to_natural_position (void *src)
524 boost::shared_ptr<Playlist> pl (playlist());
530 boost::shared_ptr<Region> whole_file_region = get_parent();
532 if (whole_file_region) {
533 set_position (whole_file_region->position() + _start, src);
538 Region::special_set_position (framepos_t pos)
540 /* this is used when creating a whole file region as
541 a way to store its "natural" or "captured" position.
544 _position = _position;
549 Region::set_position_lock_style (PositionLockStyle ps)
551 if (_position_lock_style != ps) {
553 boost::shared_ptr<Playlist> pl (playlist());
559 _position_lock_style = ps;
561 if (_position_lock_style == MusicTime) {
562 _session.tempo_map().bbt_time (_position, _bbt_time);
565 send_change (Properties::position_lock_style);
571 Region::update_position_after_tempo_map_change ()
573 boost::shared_ptr<Playlist> pl (playlist());
575 if (!pl || _position_lock_style != MusicTime) {
579 TempoMap& map (_session.tempo_map());
580 framepos_t pos = map.frame_time (_bbt_time);
581 set_position_internal (pos, false);
583 /* do this even if the position is the same. this helps out
584 a GUI that has moved its representation already.
586 send_change (Properties::position);
590 Region::set_position (framepos_t pos, void* /*src*/)
596 set_position_internal (pos, true);
598 /* do this even if the position is the same. this helps out
599 a GUI that has moved its representation already.
601 send_change (Properties::position);
606 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
608 if (_position != pos) {
609 _last_position = _position;
612 /* check that the new _position wouldn't make the current
613 length impossible - if so, change the length.
615 XXX is this the right thing to do?
618 if (max_framepos - _length < _position) {
619 _last_length = _length;
620 _length = max_framepos - _position;
623 if (allow_bbt_recompute) {
624 recompute_position_from_lock_style ();
627 //invalidate_transients ();
632 Region::set_position_on_top (framepos_t pos, void* /*src*/)
638 if (_position != pos) {
639 set_position_internal (pos, true);
642 boost::shared_ptr<Playlist> pl (playlist());
645 pl->raise_region_to_top (shared_from_this ());
648 /* do this even if the position is the same. this helps out
649 a GUI that has moved its representation already.
651 send_change (Properties::position);
655 Region::recompute_position_from_lock_style ()
657 if (_position_lock_style == MusicTime) {
658 _session.tempo_map().bbt_time (_position, _bbt_time);
663 Region::nudge_position (frameoffset_t n, void* /*src*/)
673 framepos_t new_position = _position;
676 if (_position > max_framepos - n) {
677 new_position = max_framepos;
682 if (_position < -n) {
689 set_position_internal (new_position, true);
691 send_change (Properties::position);
695 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
697 _ancestral_length = l;
698 _ancestral_start = s;
704 Region::set_start (framepos_t pos, void* /*src*/)
706 if (locked() || position_locked()) {
709 /* This just sets the start, nothing else. It effectively shifts
710 the contents of the Region within the overall extent of the Source,
711 without changing the Region's position or length
716 if (!verify_start (pos)) {
723 invalidate_transients ();
725 send_change (Properties::start);
730 Region::trim_start (framepos_t new_position, void */*src*/)
732 if (locked() || position_locked()) {
735 framepos_t new_start;
736 frameoffset_t const start_shift = new_position - _position;
738 if (start_shift > 0) {
740 if (_start > max_framepos - start_shift) {
741 new_start = max_framepos;
743 new_start = _start + start_shift;
746 if (!verify_start (new_start)) {
750 } else if (start_shift < 0) {
752 if (_start < -start_shift) {
755 new_start = _start + start_shift;
762 if (new_start == _start) {
770 send_change (Properties::start);
774 Region::trim_front (framepos_t new_position, void *src)
776 modify_front (new_position, false, src);
780 Region::cut_front (framepos_t new_position, void *src)
782 modify_front (new_position, true, src);
786 Region::cut_end (framepos_t new_endpoint, void *src)
788 modify_end (new_endpoint, true, src);
792 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
798 framepos_t end = last_frame();
799 framepos_t source_zero;
801 if (_position > _start) {
802 source_zero = _position - _start;
804 source_zero = 0; // its actually negative, but this will work for us
807 if (new_position < end) { /* can't trim it zero or negative length */
809 framecnt_t newlen = 0;
810 framepos_t delta = 0;
812 if (!can_trim_start_before_source_start ()) {
813 /* can't trim it back past where source position zero is located */
814 new_position = max (new_position, source_zero);
817 if (new_position > _position) {
818 newlen = _length - (new_position - _position);
819 delta = -1 * (new_position - _position);
821 newlen = _length + (_position - new_position);
822 delta = _position - new_position;
825 trim_to_internal (new_position, newlen, src);
828 _right_of_split = true;
831 if (!property_changes_suspended()) {
832 recompute_at_start ();
835 if (_transients.size() > 0){
836 adjust_transients(delta);
842 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
848 if (new_endpoint > _position) {
849 trim_to_internal (_position, new_endpoint - _position +1, this);
851 _left_of_split = true;
853 if (!property_changes_suspended()) {
859 /** @param new_endpoint New region end point, such that, for example,
860 * a region at 0 of length 10 has an endpoint of 9.
864 Region::trim_end (framepos_t new_endpoint, void* src)
866 modify_end (new_endpoint, false, src);
870 Region::trim_to (framepos_t position, framecnt_t length, void *src)
876 trim_to_internal (position, length, src);
878 if (!property_changes_suspended()) {
879 recompute_at_start ();
885 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
887 framepos_t new_start;
893 frameoffset_t const start_shift = position - _position;
895 if (start_shift > 0) {
897 if (_start > max_framepos - start_shift) {
898 new_start = max_framepos;
900 new_start = _start + start_shift;
903 } else if (start_shift < 0) {
905 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
908 new_start = _start + start_shift;
915 if (!verify_start_and_length (new_start, length)) {
919 PropertyChange what_changed;
921 if (_start != new_start) {
923 what_changed.add (Properties::start);
926 /* Set position before length, otherwise for MIDI regions this bad thing happens:
927 * 1. we call set_length_internal; length in beats is computed using the region's current
928 * (soon-to-be old) position
929 * 2. we call set_position_internal; position is set and length in frames re-computed using
930 * length in beats from (1) but at the new position, which is wrong if the region
931 * straddles a tempo/meter change.
934 if (_position != position) {
935 if (!property_changes_suspended()) {
936 _last_position = _position;
938 set_position_internal (position, true);
939 what_changed.add (Properties::position);
942 if (_length != length) {
943 if (!property_changes_suspended()) {
944 _last_length = _length;
946 set_length_internal (length);
947 what_changed.add (Properties::length);
952 PropertyChange start_and_length;
954 start_and_length.add (Properties::start);
955 start_and_length.add (Properties::length);
957 if (what_changed.contains (start_and_length)) {
961 if (!what_changed.empty()) {
962 send_change (what_changed);
967 Region::set_hidden (bool yn)
969 if (hidden() != yn) {
971 send_change (Properties::hidden);
976 Region::set_whole_file (bool yn)
979 /* no change signal */
983 Region::set_automatic (bool yn)
986 /* no change signal */
990 Region::set_muted (bool yn)
994 send_change (Properties::muted);
999 Region::set_opaque (bool yn)
1001 if (opaque() != yn) {
1003 send_change (Properties::opaque);
1008 Region::set_locked (bool yn)
1010 if (locked() != yn) {
1012 send_change (Properties::locked);
1017 Region::set_position_locked (bool yn)
1019 if (position_locked() != yn) {
1020 _position_locked = yn;
1021 send_change (Properties::locked);
1025 /** Set the region's sync point.
1026 * @param absolute_pos Session time.
1029 Region::set_sync_position (framepos_t absolute_pos)
1031 /* position within our file */
1032 framepos_t const file_pos = _start + (absolute_pos - _position);
1034 if (file_pos != _sync_position) {
1035 _sync_marked = true;
1036 _sync_position = file_pos;
1037 if (!property_changes_suspended()) {
1041 send_change (Properties::sync_position);
1046 Region::clear_sync_position ()
1048 if (sync_marked()) {
1049 _sync_marked = false;
1050 if (!property_changes_suspended()) {
1054 send_change (Properties::sync_position);
1058 /* @return the sync point relative the first frame of the region */
1060 Region::sync_offset (int& dir) const
1062 if (sync_marked()) {
1063 if (_sync_position > _start) {
1065 return _sync_position - _start;
1068 return _start - _sync_position;
1077 Region::adjust_to_sync (framepos_t pos) const
1080 frameoffset_t offset = sync_offset (sync_dir);
1082 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1091 if (max_framepos - pos > offset) {
1099 /** @return Sync position in session time */
1101 Region::sync_position() const
1103 if (sync_marked()) {
1104 return _position - _start + _sync_position;
1106 /* if sync has not been marked, use the start of the region */
1114 boost::shared_ptr<Playlist> pl (playlist());
1116 pl->raise_region (shared_from_this ());
1123 boost::shared_ptr<Playlist> pl (playlist());
1125 pl->lower_region (shared_from_this ());
1131 Region::raise_to_top ()
1133 boost::shared_ptr<Playlist> pl (playlist());
1135 pl->raise_region_to_top (shared_from_this());
1140 Region::lower_to_bottom ()
1142 boost::shared_ptr<Playlist> pl (playlist());
1144 pl->lower_region_to_bottom (shared_from_this());
1149 Region::set_layer (layer_t l)
1154 send_change (Properties::layer);
1161 XMLNode *node = new XMLNode ("Region");
1164 LocaleGuard lg (X_("POSIX"));
1165 const char* fe = NULL;
1167 add_properties (*node);
1169 _id.print (buf, sizeof (buf));
1170 node->add_property ("id", buf);
1171 node->add_property ("type", _type.to_string());
1173 switch (_first_edit) {
1174 case EditChangesNothing:
1177 case EditChangesName:
1183 default: /* should be unreachable but makes g++ happy */
1188 node->add_property ("first-edit", fe);
1190 /* note: flags are stored by derived classes */
1192 if (_position_lock_style != AudioTime) {
1195 node->add_property ("bbt-position", str.str());
1198 for (uint32_t n=0; n < _sources.size(); ++n) {
1199 snprintf (buf2, sizeof(buf2), "source-%d", n);
1200 _sources[n]->id().print (buf, sizeof(buf));
1201 node->add_property (buf2, buf);
1204 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1205 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1206 _master_sources[n]->id().print (buf, sizeof (buf));
1207 node->add_property (buf2, buf);
1210 if (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;
1252 const XMLNodeList& nlist = node.children();
1254 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1256 XMLNode *child = (*niter);
1258 if (child->name () == "Extra") {
1260 _extra_xml = new XMLNode (*child);
1264 what_changed = set_values (node);
1266 if ((prop = node.property (X_("id")))) {
1267 _id = prop->value();
1270 if (_position_lock_style == MusicTime) {
1271 if ((prop = node.property ("bbt-position")) == 0) {
1272 /* missing BBT info, revert to audio time locking */
1273 _position_lock_style = AudioTime;
1275 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1278 &_bbt_time.ticks) != 3) {
1279 _position_lock_style = AudioTime;
1284 /* fix problems with old sessions corrupted by impossible
1285 values for _stretch or _shift
1287 if (_stretch == 0.0f) {
1291 if (_shift == 0.0f) {
1296 send_change (what_changed);
1299 /* Quick fix for 2.x sessions when region is muted */
1300 if ((prop = node.property (X_("flags")))) {
1301 if (string::npos != prop->value().find("Muted")){
1311 Region::suspend_property_changes ()
1313 Stateful::suspend_property_changes ();
1314 _last_length = _length;
1315 _last_position = _position;
1319 Region::mid_thaw (const PropertyChange& what_changed)
1321 if (what_changed.contains (Properties::length)) {
1322 if (what_changed.contains (Properties::position)) {
1323 recompute_at_start ();
1325 recompute_at_end ();
1330 Region::send_change (const PropertyChange& what_changed)
1332 if (what_changed.empty()) {
1336 Stateful::send_change (what_changed);
1338 if (!Stateful::frozen()) {
1340 /* Try and send a shared_pointer unless this is part of the constructor.
1345 boost::shared_ptr<Region> rptr = shared_from_this();
1346 RegionPropertyChanged (rptr, what_changed);
1348 /* no shared_ptr available, relax; */
1354 Region::set_last_layer_op (uint64_t when)
1356 _last_layer_op = when;
1360 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1362 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1366 Region::equivalent (boost::shared_ptr<const Region> other) const
1368 return _start == other->_start &&
1369 _position == other->_position &&
1370 _length == other->_length;
1374 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1376 return _start == other->_start &&
1377 _length == other->_length;
1381 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1383 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1387 Region::source_deleted (boost::weak_ptr<Source>)
1391 if (!_session.deletion_in_progress()) {
1392 /* this is a very special case: at least one of the region's
1393 sources has bee deleted, so invalidate all references to
1394 ourselves. Do NOT do this during session deletion, because
1395 then we run the risk that this will actually result
1396 in this object being deleted (as refcnt goes to zero)
1397 while emitting DropReferences.
1405 Region::master_source_names ()
1407 SourceList::iterator i;
1409 vector<string> names;
1410 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1411 names.push_back((*i)->name());
1418 Region::set_master_sources (const SourceList& srcs)
1420 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1421 (*i)->dec_use_count ();
1424 _master_sources = srcs;
1425 assert (_sources.size() == _master_sources.size());
1427 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1428 (*i)->inc_use_count ();
1433 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1438 if ((_sources.size() != other->_sources.size()) ||
1439 (_master_sources.size() != other->_master_sources.size())) {
1443 SourceList::const_iterator i;
1444 SourceList::const_iterator io;
1446 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1447 if ((*i)->id() != (*io)->id()) {
1452 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1453 if ((*i)->id() != (*io)->id()) {
1462 Region::source_string () const
1464 //string res = itos(_sources.size());
1467 res << _sources.size() << ":";
1469 SourceList::const_iterator i;
1471 for (i = _sources.begin(); i != _sources.end(); ++i) {
1472 res << (*i)->id() << ":";
1475 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1476 res << (*i)->id() << ":";
1483 Region::uses_source (boost::shared_ptr<const Source> source) const
1485 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1494 Region::uses_source_path (const std::string& path) const
1496 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1497 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1499 if (fs->path() == path) {
1508 Region::source_length(uint32_t n) const
1510 assert (n < _sources.size());
1511 return _sources[n]->length (_position - _start);
1515 Region::verify_length (framecnt_t len)
1517 if (source() && (source()->destructive() || source()->length_mutable())) {
1521 framecnt_t maxlen = 0;
1523 for (uint32_t n = 0; n < _sources.size(); ++n) {
1524 maxlen = max (maxlen, source_length(n) - _start);
1527 len = min (len, maxlen);
1533 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1535 if (source() && (source()->destructive() || source()->length_mutable())) {
1539 framecnt_t maxlen = 0;
1541 for (uint32_t n = 0; n < _sources.size(); ++n) {
1542 maxlen = max (maxlen, source_length(n) - new_start);
1545 new_length = min (new_length, maxlen);
1551 Region::verify_start (framepos_t pos)
1553 if (source() && (source()->destructive() || source()->length_mutable())) {
1557 for (uint32_t n = 0; n < _sources.size(); ++n) {
1558 if (pos > source_length(n) - _length) {
1566 Region::verify_start_mutable (framepos_t& new_start)
1568 if (source() && (source()->destructive() || source()->length_mutable())) {
1572 for (uint32_t n = 0; n < _sources.size(); ++n) {
1573 if (new_start > source_length(n) - _length) {
1574 new_start = source_length(n) - _length;
1580 boost::shared_ptr<Region>
1581 Region::get_parent() const
1583 boost::shared_ptr<Playlist> pl (playlist());
1586 boost::shared_ptr<Region> r;
1587 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1589 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1590 return boost::static_pointer_cast<Region> (r);
1594 return boost::shared_ptr<Region>();
1598 Region::apply (Filter& filter, Progress* progress)
1600 return filter.run (shared_from_this(), progress);
1605 Region::invalidate_transients ()
1607 _valid_transients = false;
1608 _transients.clear ();
1610 send_change (PropertyChange (Properties::valid_transients));
1614 Region::drop_sources ()
1616 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1617 (*i)->dec_use_count ();
1622 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1623 (*i)->dec_use_count ();
1626 _master_sources.clear ();
1630 Region::use_sources (SourceList const & s)
1632 set<boost::shared_ptr<Source> > unique_srcs;
1634 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1636 _sources.push_back (*i);
1637 (*i)->inc_use_count ();
1638 _master_sources.push_back (*i);
1639 (*i)->inc_use_count ();
1641 /* connect only once to DropReferences, even if sources are replicated
1644 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1645 unique_srcs.insert (*i);
1646 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1652 Region::can_trim () const
1654 CanTrim ct = CanTrim (0);
1660 /* if not locked, we can always move the front later, and the end earlier
1663 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1665 if (start() != 0 || can_trim_start_before_source_start ()) {
1666 ct = CanTrim (ct | FrontTrimEarlier);
1669 if (!_sources.empty()) {
1670 if ((start() + length()) < _sources.front()->length (0)) {
1671 ct = CanTrim (ct | EndTrimLater);
1679 Region::max_source_level () const
1683 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1684 lvl = max (lvl, (*i)->level());
1691 Region::is_compound () const
1693 return max_source_level() > 0;