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/tempo.h"
41 #include "ardour/utils.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> muted;
52 PBD::PropertyDescriptor<bool> opaque;
53 PBD::PropertyDescriptor<bool> locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<framecnt_t> sync_position;
68 PBD::PropertyDescriptor<layer_t> layer;
69 PBD::PropertyDescriptor<framepos_t> ancestral_start;
70 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
71 PBD::PropertyDescriptor<float> stretch;
72 PBD::PropertyDescriptor<float> shift;
73 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
77 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
80 Region::make_property_quarks ()
82 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
83 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
84 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
86 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
88 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
90 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
92 Properties::import.property_id = g_quark_from_static_string (X_("import"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
94 Properties::external.property_id = g_quark_from_static_string (X_("external"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
96 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
98 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
100 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
102 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
104 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
106 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
108 Properties::start.property_id = g_quark_from_static_string (X_("start"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
110 Properties::length.property_id = g_quark_from_static_string (X_("length"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
112 Properties::position.property_id = g_quark_from_static_string (X_("position"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
114 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
116 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
118 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
120 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
122 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
124 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
126 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Region::register_properties ()
133 _xml_node_name = X_("Region");
135 add_property (_muted);
136 add_property (_opaque);
137 add_property (_locked);
138 add_property (_automatic);
139 add_property (_whole_file);
140 add_property (_import);
141 add_property (_external);
142 add_property (_sync_marked);
143 add_property (_left_of_split);
144 add_property (_right_of_split);
145 add_property (_hidden);
146 add_property (_position_locked);
147 add_property (_valid_transients);
148 add_property (_start);
149 add_property (_length);
150 add_property (_position);
151 add_property (_sync_position);
152 add_property (_layer);
153 add_property (_ancestral_start);
154 add_property (_ancestral_length);
155 add_property (_stretch);
156 add_property (_shift);
157 add_property (_position_lock_style);
160 #define REGION_DEFAULT_STATE(s,l) \
161 _muted (Properties::muted, false) \
162 , _opaque (Properties::opaque, true) \
163 , _locked (Properties::locked, false) \
164 , _automatic (Properties::automatic, false) \
165 , _whole_file (Properties::whole_file, false) \
166 , _import (Properties::import, false) \
167 , _external (Properties::external, false) \
168 , _sync_marked (Properties::sync_marked, false) \
169 , _left_of_split (Properties::left_of_split, false) \
170 , _right_of_split (Properties::right_of_split, false) \
171 , _hidden (Properties::hidden, false) \
172 , _position_locked (Properties::position_locked, false) \
173 , _valid_transients (Properties::valid_transients, false) \
174 , _start (Properties::start, (s)) \
175 , _length (Properties::length, (l)) \
176 , _position (Properties::position, 0) \
177 , _sync_position (Properties::sync_position, (s)) \
178 , _layer (Properties::layer, 0) \
179 , _ancestral_start (Properties::ancestral_start, (s)) \
180 , _ancestral_length (Properties::ancestral_length, (l)) \
181 , _stretch (Properties::stretch, 1.0) \
182 , _shift (Properties::shift, 1.0) \
183 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
185 #define REGION_COPY_STATE(other) \
186 _muted (Properties::muted, other->_muted) \
187 , _opaque (Properties::opaque, other->_opaque) \
188 , _locked (Properties::locked, other->_locked) \
189 , _automatic (Properties::automatic, other->_automatic) \
190 , _whole_file (Properties::whole_file, other->_whole_file) \
191 , _import (Properties::import, other->_import) \
192 , _external (Properties::external, other->_external) \
193 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
194 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
195 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
196 , _hidden (Properties::hidden, other->_hidden) \
197 , _position_locked (Properties::position_locked, other->_position_locked) \
198 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
199 , _start(Properties::start, other->_start) \
200 , _length(Properties::length, other->_length) \
201 , _position(Properties::position, other->_position) \
202 , _sync_position(Properties::sync_position, other->_sync_position) \
203 , _layer (Properties::layer, other->_layer) \
204 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
205 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
206 , _stretch (Properties::stretch, other->_stretch) \
207 , _shift (Properties::shift, other->_shift) \
208 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
210 /* derived-from-derived constructor (no sources in constructor) */
211 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
212 : SessionObject(s, name)
214 , REGION_DEFAULT_STATE(start,length)
215 , _last_length (length)
217 , _first_edit (EditChangesNothing)
218 , _read_data_count(0)
220 , _pending_explicit_relayer (false)
222 register_properties ();
224 /* no sources at this point */
227 /** Basic Region constructor (many sources) */
228 Region::Region (const SourceList& srcs)
229 : SessionObject(srcs.front()->session(), "toBeRenamed")
230 , _type (srcs.front()->type())
231 , REGION_DEFAULT_STATE(0,0)
234 , _first_edit (EditChangesNothing)
235 , _read_data_count(0)
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)
257 , _read_data_count(0)
259 , _pending_explicit_relayer (false)
262 register_properties ();
264 /* override state that may have been incorrectly inherited from the other region
272 use_sources (other->_sources);
274 _position_lock_style = other->_position_lock_style;
275 _first_edit = other->_first_edit;
277 _start = 0; // It seems strange _start is not inherited here?
279 /* sync pos is relative to start of file. our start-in-file is now zero,
280 so set our sync position to whatever the the difference between
281 _start and _sync_pos was in the other region.
283 result is that our new sync pos points to the same point in our source(s)
284 as the sync in the other region did in its source(s).
286 since we start at zero in our source(s), it is not possible to use a sync point that
287 is before the start. reset it to _start if that was true in the other region.
290 if (other->sync_marked()) {
291 if (other->_start < other->_sync_position) {
292 /* sync pos was after the start point of the other region */
293 _sync_position = other->_sync_position - other->_start;
295 /* sync pos was before the start point of the other region. not possible here. */
296 _sync_marked = false;
297 _sync_position = _start;
300 _sync_marked = false;
301 _sync_position = _start;
304 if (Profile->get_sae()) {
305 /* reset sync point to start if its ended up
306 outside region bounds.
309 if (_sync_position < _start || _sync_position >= _start + _length) {
310 _sync_marked = false;
311 _sync_position = _start;
315 assert (_type == other->data_type());
318 /** Create a new Region from part of an existing one.
320 the start within \a other is given by \a offset
321 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
323 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
324 : SessionObject(other->session(), other->name())
325 , _type (other->data_type())
326 , REGION_COPY_STATE (other)
327 , _last_length (other->_last_length)
328 , _last_position(other->_last_position) \
329 , _first_edit (EditChangesNothing)
330 , _read_data_count(0)
332 , _pending_explicit_relayer (false)
335 register_properties ();
337 /* override state that may have been incorrectly inherited from the other region
345 use_sources (other->_sources);
347 _start = other->_start + offset;
349 /* if the other region had a distinct sync point
350 set, then continue to use it as best we can.
351 otherwise, reset sync point back to start.
354 if (other->sync_marked()) {
355 if (other->_sync_position < _start) {
356 _sync_marked = false;
357 _sync_position = _start;
359 _sync_position = other->_sync_position;
362 _sync_marked = false;
363 _sync_position = _start;
366 if (Profile->get_sae()) {
367 /* reset sync point to start if its ended up
368 outside region bounds.
371 if (_sync_position < _start || _sync_position >= _start + _length) {
372 _sync_marked = false;
373 _sync_position = _start;
377 assert (_type == other->data_type());
380 /** Create a copy of @param other but with different sources. Used by filters */
381 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
382 : SessionObject (other->session(), other->name())
383 , _type (srcs.front()->type())
384 , REGION_COPY_STATE (other)
385 , _last_length (other->_last_length)
386 , _last_position (other->_last_position)
387 , _first_edit (EditChangesID)
388 , _read_data_count (0)
389 , _last_layer_op (other->_last_layer_op)
390 , _pending_explicit_relayer (false)
392 register_properties ();
395 _position_locked = false;
397 other->_first_edit = EditChangesName;
399 if (other->_extra_xml) {
400 _extra_xml = new XMLNode (*other->_extra_xml);
406 assert(_sources.size() > 0);
411 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
416 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
418 _playlist = wpl.lock();
422 Region::set_name (const std::string& str)
425 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
426 assert(_name == str);
428 send_change (Properties::name);
435 Region::set_length (framecnt_t len, void */*src*/)
437 //cerr << "Region::set_length() len = " << len << endl;
442 if (_length != len && len != 0) {
444 /* check that the current _position wouldn't make the new
448 if (max_framepos - len < _position) {
452 if (!verify_length (len)) {
457 _last_length = _length;
458 set_length_internal (len);
462 invalidate_transients ();
464 if (!property_changes_suspended()) {
468 send_change (Properties::length);
473 Region::set_length_internal (framecnt_t len)
479 Region::maybe_uncopy ()
481 /* this does nothing but marked a semantic moment once upon a time */
485 Region::first_edit ()
487 boost::shared_ptr<Playlist> pl (playlist());
489 if (_first_edit != EditChangesNothing && pl) {
491 _name = RegionFactory::new_region_name (_name);
492 _first_edit = EditChangesNothing;
494 send_change (Properties::name);
496 RegionFactory::CheckNewRegion (shared_from_this());
501 Region::at_natural_position () const
503 boost::shared_ptr<Playlist> pl (playlist());
509 boost::shared_ptr<Region> whole_file_region = get_parent();
511 if (whole_file_region) {
512 if (_position == whole_file_region->position() + _start) {
521 Region::move_to_natural_position (void *src)
523 boost::shared_ptr<Playlist> pl (playlist());
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 set_position (whole_file_region->position() + _start, src);
537 Region::special_set_position (framepos_t pos)
539 /* this is used when creating a whole file region as
540 a way to store its "natural" or "captured" position.
543 _position = _position;
548 Region::set_position_lock_style (PositionLockStyle ps)
550 if (_position_lock_style != ps) {
552 boost::shared_ptr<Playlist> pl (playlist());
558 _position_lock_style = ps;
560 if (_position_lock_style == MusicTime) {
561 _session.tempo_map().bbt_time (_position, _bbt_time);
564 send_change (Properties::position_lock_style);
570 Region::update_position_after_tempo_map_change ()
572 boost::shared_ptr<Playlist> pl (playlist());
574 if (!pl || _position_lock_style != MusicTime) {
578 TempoMap& map (_session.tempo_map());
579 framepos_t pos = map.frame_time (_bbt_time);
580 set_position_internal (pos, false);
582 /* do this even if the position is the same. this helps out
583 a GUI that has moved its representation already.
585 send_change (Properties::position);
589 Region::set_position (framepos_t pos, void* /*src*/)
595 set_position_internal (pos, true);
597 /* do this even if the position is the same. this helps out
598 a GUI that has moved its representation already.
600 send_change (Properties::position);
605 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
607 if (_position != pos) {
608 _last_position = _position;
611 /* check that the new _position wouldn't make the current
612 length impossible - if so, change the length.
614 XXX is this the right thing to do?
617 if (max_framepos - _length < _position) {
618 _last_length = _length;
619 _length = max_framepos - _position;
622 if (allow_bbt_recompute) {
623 recompute_position_from_lock_style ();
626 //invalidate_transients ();
631 Region::set_position_on_top (framepos_t pos, void* /*src*/)
637 if (_position != pos) {
638 set_position_internal (pos, true);
641 boost::shared_ptr<Playlist> pl (playlist());
644 pl->raise_region_to_top (shared_from_this ());
647 /* do this even if the position is the same. this helps out
648 a GUI that has moved its representation already.
650 send_change (Properties::position);
654 Region::recompute_position_from_lock_style ()
656 if (_position_lock_style == MusicTime) {
657 _session.tempo_map().bbt_time (_position, _bbt_time);
662 Region::nudge_position (frameoffset_t n, void* /*src*/)
672 framepos_t new_position = _position;
675 if (_position > max_framepos - n) {
676 new_position = max_framepos;
681 if (_position < -n) {
688 set_position_internal (new_position, true);
690 send_change (Properties::position);
694 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
696 _ancestral_length = l;
697 _ancestral_start = s;
703 Region::set_start (framepos_t pos, void* /*src*/)
705 if (locked() || position_locked()) {
708 /* This just sets the start, nothing else. It effectively shifts
709 the contents of the Region within the overall extent of the Source,
710 without changing the Region's position or length
715 if (!verify_start (pos)) {
722 invalidate_transients ();
724 send_change (Properties::start);
729 Region::trim_start (framepos_t new_position, void */*src*/)
731 if (locked() || position_locked()) {
734 framepos_t new_start;
735 frameoffset_t const start_shift = new_position - _position;
737 if (start_shift > 0) {
739 if (_start > max_framepos - start_shift) {
740 new_start = max_framepos;
742 new_start = _start + start_shift;
745 if (!verify_start (new_start)) {
749 } else if (start_shift < 0) {
751 if (_start < -start_shift) {
754 new_start = _start + start_shift;
761 if (new_start == _start) {
769 send_change (Properties::start);
773 Region::trim_front (framepos_t new_position, void *src)
775 modify_front (new_position, false, src);
779 Region::cut_front (framepos_t new_position, void *src)
781 modify_front (new_position, true, src);
785 Region::cut_end (framepos_t new_endpoint, void *src)
787 modify_end (new_endpoint, true, src);
791 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
797 framepos_t end = last_frame();
798 framepos_t source_zero;
800 if (_position > _start) {
801 source_zero = _position - _start;
803 source_zero = 0; // its actually negative, but this will work for us
806 if (new_position < end) { /* can't trim it zero or negative length */
808 framecnt_t newlen = 0;
809 framepos_t delta = 0;
811 if (!can_trim_start_before_source_start ()) {
812 /* can't trim it back past where source position zero is located */
813 new_position = max (new_position, source_zero);
816 if (new_position > _position) {
817 newlen = _length - (new_position - _position);
818 delta = -1 * (new_position - _position);
820 newlen = _length + (_position - new_position);
821 delta = _position - new_position;
824 trim_to_internal (new_position, newlen, src);
827 _right_of_split = true;
830 if (!property_changes_suspended()) {
831 recompute_at_start ();
834 if (_transients.size() > 0){
835 adjust_transients(delta);
841 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
847 if (new_endpoint > _position) {
848 trim_to_internal (_position, new_endpoint - _position +1, this);
850 _left_of_split = true;
852 if (!property_changes_suspended()) {
858 /** @param new_endpoint New region end point, such that, for example,
859 * a region at 0 of length 10 has an endpoint of 9.
863 Region::trim_end (framepos_t new_endpoint, void* src)
865 modify_end (new_endpoint, false, src);
869 Region::trim_to (framepos_t position, framecnt_t length, void *src)
875 trim_to_internal (position, length, src);
877 if (!property_changes_suspended()) {
878 recompute_at_start ();
884 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
886 framepos_t new_start;
892 frameoffset_t const start_shift = position - _position;
894 if (start_shift > 0) {
896 if (_start > max_framepos - start_shift) {
897 new_start = max_framepos;
899 new_start = _start + start_shift;
902 } else if (start_shift < 0) {
904 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
907 new_start = _start + start_shift;
914 if (!verify_start_and_length (new_start, length)) {
918 PropertyChange what_changed;
920 if (_start != new_start) {
922 what_changed.add (Properties::start);
925 /* Set position before length, otherwise for MIDI regions this bad thing happens:
926 * 1. we call set_length_internal; length in beats is computed using the region's current
927 * (soon-to-be old) position
928 * 2. we call set_position_internal; position is set and length in frames re-computed using
929 * length in beats from (1) but at the new position, which is wrong if the region
930 * straddles a tempo/meter change.
933 if (_position != position) {
934 if (!property_changes_suspended()) {
935 _last_position = _position;
937 set_position_internal (position, true);
938 what_changed.add (Properties::position);
941 if (_length != length) {
942 if (!property_changes_suspended()) {
943 _last_length = _length;
945 set_length_internal (length);
946 what_changed.add (Properties::length);
951 PropertyChange start_and_length;
953 start_and_length.add (Properties::start);
954 start_and_length.add (Properties::length);
956 if (what_changed.contains (start_and_length)) {
960 if (!what_changed.empty()) {
961 send_change (what_changed);
966 Region::set_hidden (bool yn)
968 if (hidden() != yn) {
970 send_change (Properties::hidden);
975 Region::set_whole_file (bool yn)
978 /* no change signal */
982 Region::set_automatic (bool yn)
985 /* no change signal */
989 Region::set_muted (bool yn)
993 send_change (Properties::muted);
998 Region::set_opaque (bool yn)
1000 if (opaque() != yn) {
1002 send_change (Properties::opaque);
1007 Region::set_locked (bool yn)
1009 if (locked() != yn) {
1011 send_change (Properties::locked);
1016 Region::set_position_locked (bool yn)
1018 if (position_locked() != yn) {
1019 _position_locked = yn;
1020 send_change (Properties::locked);
1024 /** Set the region's sync point.
1025 * @param absolute_pos Session time.
1028 Region::set_sync_position (framepos_t absolute_pos)
1030 /* position within our file */
1031 framepos_t const file_pos = _start + (absolute_pos - _position);
1033 if (file_pos != _sync_position) {
1034 _sync_marked = true;
1035 _sync_position = file_pos;
1036 if (!property_changes_suspended()) {
1040 send_change (Properties::sync_position);
1045 Region::clear_sync_position ()
1047 if (sync_marked()) {
1048 _sync_marked = false;
1049 if (!property_changes_suspended()) {
1053 send_change (Properties::sync_position);
1057 /* @return the sync point relative the first frame of the region */
1059 Region::sync_offset (int& dir) const
1061 if (sync_marked()) {
1062 if (_sync_position > _start) {
1064 return _sync_position - _start;
1067 return _start - _sync_position;
1076 Region::adjust_to_sync (framepos_t pos) const
1079 frameoffset_t offset = sync_offset (sync_dir);
1081 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1090 if (max_framepos - pos > offset) {
1098 /** @return Sync position in session time */
1100 Region::sync_position() const
1102 if (sync_marked()) {
1103 return _position - _start + _sync_position;
1105 /* if sync has not been marked, use the start of the region */
1113 boost::shared_ptr<Playlist> pl (playlist());
1115 pl->raise_region (shared_from_this ());
1122 boost::shared_ptr<Playlist> pl (playlist());
1124 pl->lower_region (shared_from_this ());
1130 Region::raise_to_top ()
1132 boost::shared_ptr<Playlist> pl (playlist());
1134 pl->raise_region_to_top (shared_from_this());
1139 Region::lower_to_bottom ()
1141 boost::shared_ptr<Playlist> pl (playlist());
1143 pl->lower_region_to_bottom (shared_from_this());
1148 Region::set_layer (layer_t l)
1153 send_change (Properties::layer);
1160 XMLNode *node = new XMLNode ("Region");
1163 LocaleGuard lg (X_("POSIX"));
1164 const char* fe = NULL;
1166 add_properties (*node);
1168 _id.print (buf, sizeof (buf));
1169 node->add_property ("id", buf);
1170 node->add_property ("type", _type.to_string());
1172 switch (_first_edit) {
1173 case EditChangesNothing:
1176 case EditChangesName:
1182 default: /* should be unreachable but makes g++ happy */
1187 node->add_property ("first-edit", fe);
1189 /* note: flags are stored by derived classes */
1191 if (_position_lock_style != AudioTime) {
1194 node->add_property ("bbt-position", str.str());
1197 for (uint32_t n=0; n < _sources.size(); ++n) {
1198 snprintf (buf2, sizeof(buf2), "source-%d", n);
1199 _sources[n]->id().print (buf, sizeof(buf));
1200 node->add_property (buf2, buf);
1203 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1204 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1205 _master_sources[n]->id().print (buf, sizeof (buf));
1206 node->add_property (buf2, buf);
1210 node->add_child_copy (*_extra_xml);
1217 Region::get_state ()
1223 Region::set_state (const XMLNode& node, int version)
1225 PropertyChange what_changed;
1226 return _set_state (node, version, what_changed, true);
1230 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1232 const XMLProperty* prop;
1234 what_changed = set_values (node);
1236 if ((prop = node.property (X_("id")))) {
1237 _id = prop->value();
1240 if (_position_lock_style == MusicTime) {
1241 if ((prop = node.property ("bbt-position")) == 0) {
1242 /* missing BBT info, revert to audio time locking */
1243 _position_lock_style = AudioTime;
1245 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1248 &_bbt_time.ticks) != 3) {
1249 _position_lock_style = AudioTime;
1254 /* fix problems with old sessions corrupted by impossible
1255 values for _stretch or _shift
1257 if (_stretch == 0.0f) {
1261 if (_shift == 0.0f) {
1265 const XMLNodeList& nlist = node.children();
1267 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1273 if (child->name () == "Extra") {
1275 _extra_xml = new XMLNode (*child);
1281 send_change (what_changed);
1284 /* Quick fix for 2.x sessions when region is muted */
1285 if ((prop = node.property (X_("flags")))) {
1286 if (string::npos != prop->value().find("Muted")){
1296 Region::suspend_property_changes ()
1298 Stateful::suspend_property_changes ();
1299 _last_length = _length;
1300 _last_position = _position;
1304 Region::mid_thaw (const PropertyChange& what_changed)
1306 if (what_changed.contains (Properties::length)) {
1307 if (what_changed.contains (Properties::position)) {
1308 recompute_at_start ();
1310 recompute_at_end ();
1315 Region::send_change (const PropertyChange& what_changed)
1317 if (what_changed.empty()) {
1321 Stateful::send_change (what_changed);
1323 if (!Stateful::frozen()) {
1325 /* Try and send a shared_pointer unless this is part of the constructor.
1330 boost::shared_ptr<Region> rptr = shared_from_this();
1331 RegionPropertyChanged (rptr, what_changed);
1333 /* no shared_ptr available, relax; */
1339 Region::set_last_layer_op (uint64_t when)
1341 _last_layer_op = when;
1345 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1347 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1351 Region::equivalent (boost::shared_ptr<const Region> other) const
1353 return _start == other->_start &&
1354 _position == other->_position &&
1355 _length == other->_length;
1359 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1361 return _start == other->_start &&
1362 _length == other->_length;
1366 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1368 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1372 Region::source_deleted (boost::weak_ptr<Source>)
1376 if (!_session.deletion_in_progress()) {
1377 /* this is a very special case: at least one of the region's
1378 sources has bee deleted, so invalidate all references to
1379 ourselves. Do NOT do this during session deletion, because
1380 then we run the risk that this will actually result
1381 in this object being deleted (as refcnt goes to zero)
1382 while emitting DropReferences.
1390 Region::master_source_names ()
1392 SourceList::iterator i;
1394 vector<string> names;
1395 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1396 names.push_back((*i)->name());
1403 Region::set_master_sources (const SourceList& srcs)
1405 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1406 (*i)->dec_use_count ();
1409 _master_sources = srcs;
1410 assert (_sources.size() == _master_sources.size());
1412 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1413 (*i)->inc_use_count ();
1418 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1423 if ((_sources.size() != other->_sources.size()) ||
1424 (_master_sources.size() != other->_master_sources.size())) {
1428 SourceList::const_iterator i;
1429 SourceList::const_iterator io;
1431 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1432 if ((*i)->id() != (*io)->id()) {
1437 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1438 if ((*i)->id() != (*io)->id()) {
1447 Region::source_string () const
1449 //string res = itos(_sources.size());
1452 res << _sources.size() << ":";
1454 SourceList::const_iterator i;
1456 for (i = _sources.begin(); i != _sources.end(); ++i) {
1457 res << (*i)->id() << ":";
1460 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1461 res << (*i)->id() << ":";
1468 Region::uses_source (boost::shared_ptr<const Source> source) const
1470 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1479 Region::uses_source_path (const std::string& path) const
1481 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1482 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1484 if (fs->path() == path) {
1493 Region::source_length(uint32_t n) const
1495 assert (n < _sources.size());
1496 return _sources[n]->length (_position - _start);
1500 Region::verify_length (framecnt_t len)
1502 if (source() && (source()->destructive() || source()->length_mutable())) {
1506 framecnt_t maxlen = 0;
1508 for (uint32_t n = 0; n < _sources.size(); ++n) {
1509 maxlen = max (maxlen, source_length(n) - _start);
1512 len = min (len, maxlen);
1518 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1520 if (source() && (source()->destructive() || source()->length_mutable())) {
1524 framecnt_t maxlen = 0;
1526 for (uint32_t n = 0; n < _sources.size(); ++n) {
1527 maxlen = max (maxlen, source_length(n) - new_start);
1530 new_length = min (new_length, maxlen);
1536 Region::verify_start (framepos_t pos)
1538 if (source() && (source()->destructive() || source()->length_mutable())) {
1542 for (uint32_t n = 0; n < _sources.size(); ++n) {
1543 if (pos > source_length(n) - _length) {
1551 Region::verify_start_mutable (framepos_t& new_start)
1553 if (source() && (source()->destructive() || source()->length_mutable())) {
1557 for (uint32_t n = 0; n < _sources.size(); ++n) {
1558 if (new_start > source_length(n) - _length) {
1559 new_start = source_length(n) - _length;
1565 boost::shared_ptr<Region>
1566 Region::get_parent() const
1568 boost::shared_ptr<Playlist> pl (playlist());
1571 boost::shared_ptr<Region> r;
1572 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1574 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1575 return boost::static_pointer_cast<Region> (r);
1579 return boost::shared_ptr<Region>();
1583 Region::apply (Filter& filter, Progress* progress)
1585 return filter.run (shared_from_this(), progress);
1590 Region::invalidate_transients ()
1592 _valid_transients = false;
1593 _transients.clear ();
1595 send_change (PropertyChange (Properties::valid_transients));
1599 Region::drop_sources ()
1601 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1602 (*i)->dec_use_count ();
1607 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1608 (*i)->dec_use_count ();
1611 _master_sources.clear ();
1615 Region::use_sources (SourceList const & s)
1617 set<boost::shared_ptr<Source> > unique_srcs;
1619 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1621 _sources.push_back (*i);
1622 (*i)->inc_use_count ();
1623 _master_sources.push_back (*i);
1624 (*i)->inc_use_count ();
1626 /* connect only once to DropReferences, even if sources are replicated
1629 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1630 unique_srcs.insert (*i);
1631 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1637 Region::can_trim () const
1639 CanTrim ct = CanTrim (0);
1645 /* if not locked, we can always move the front later, and the end earlier
1648 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1650 if (start() != 0 || can_trim_start_before_source_start ()) {
1651 ct = CanTrim (ct | FrontTrimEarlier);
1654 if (!_sources.empty()) {
1655 if ((start() + length()) < _sources.front()->length (0)) {
1656 ct = CanTrim (ct | EndTrimLater);
1664 Region::max_source_level () const
1668 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1669 lvl = max (lvl, (*i)->level());