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/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
43 using namespace ARDOUR;
48 namespace Properties {
49 PBD::PropertyDescriptor<bool> muted;
50 PBD::PropertyDescriptor<bool> opaque;
51 PBD::PropertyDescriptor<bool> locked;
52 PBD::PropertyDescriptor<bool> video_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;
73 PBD::PropertyDescriptor<uint64_t> layering_index;
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::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
90 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
92 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
94 Properties::import.property_id = g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
96 Properties::external.property_id = g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
98 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
100 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
102 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
104 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
106 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
108 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
110 Properties::start.property_id = g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
112 Properties::length.property_id = g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
114 Properties::position.property_id = g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
116 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
118 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
120 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
122 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
124 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
126 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
128 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
130 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
135 Region::register_properties ()
137 _xml_node_name = X_("Region");
139 add_property (_muted);
140 add_property (_opaque);
141 add_property (_locked);
142 add_property (_video_locked);
143 add_property (_automatic);
144 add_property (_whole_file);
145 add_property (_import);
146 add_property (_external);
147 add_property (_sync_marked);
148 add_property (_left_of_split);
149 add_property (_right_of_split);
150 add_property (_hidden);
151 add_property (_position_locked);
152 add_property (_valid_transients);
153 add_property (_start);
154 add_property (_length);
155 add_property (_position);
156 add_property (_sync_position);
157 add_property (_ancestral_start);
158 add_property (_ancestral_length);
159 add_property (_stretch);
160 add_property (_shift);
161 add_property (_position_lock_style);
162 add_property (_layering_index);
165 #define REGION_DEFAULT_STATE(s,l) \
166 _sync_marked (Properties::sync_marked, false) \
167 , _left_of_split (Properties::left_of_split, false) \
168 , _right_of_split (Properties::right_of_split, false) \
169 , _valid_transients (Properties::valid_transients, false) \
170 , _start (Properties::start, (s)) \
171 , _length (Properties::length, (l)) \
172 , _position (Properties::position, 0) \
173 , _sync_position (Properties::sync_position, (s)) \
174 , _muted (Properties::muted, false) \
175 , _opaque (Properties::opaque, true) \
176 , _locked (Properties::locked, false) \
177 , _video_locked (Properties::video_locked, false) \
178 , _automatic (Properties::automatic, false) \
179 , _whole_file (Properties::whole_file, false) \
180 , _import (Properties::import, false) \
181 , _external (Properties::external, false) \
182 , _hidden (Properties::hidden, false) \
183 , _position_locked (Properties::position_locked, false) \
184 , _ancestral_start (Properties::ancestral_start, (s)) \
185 , _ancestral_length (Properties::ancestral_length, (l)) \
186 , _stretch (Properties::stretch, 1.0) \
187 , _shift (Properties::shift, 1.0) \
188 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
189 , _layering_index (Properties::layering_index, 0)
191 #define REGION_COPY_STATE(other) \
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 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
196 , _start(Properties::start, other->_start) \
197 , _length(Properties::length, other->_length) \
198 , _position(Properties::position, other->_position) \
199 , _sync_position(Properties::sync_position, other->_sync_position) \
200 , _muted (Properties::muted, other->_muted) \
201 , _opaque (Properties::opaque, other->_opaque) \
202 , _locked (Properties::locked, other->_locked) \
203 , _video_locked (Properties::video_locked, other->_video_locked) \
204 , _automatic (Properties::automatic, other->_automatic) \
205 , _whole_file (Properties::whole_file, other->_whole_file) \
206 , _import (Properties::import, other->_import) \
207 , _external (Properties::external, other->_external) \
208 , _hidden (Properties::hidden, other->_hidden) \
209 , _position_locked (Properties::position_locked, other->_position_locked) \
210 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
211 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
212 , _stretch (Properties::stretch, other->_stretch) \
213 , _shift (Properties::shift, other->_shift) \
214 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
215 , _layering_index (Properties::layering_index, other->_layering_index)
217 /* derived-from-derived constructor (no sources in constructor) */
218 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
219 : SessionObject(s, name)
221 , REGION_DEFAULT_STATE(start,length)
222 , _last_length (length)
224 , _first_edit (EditChangesNothing)
227 register_properties ();
229 /* no sources at this point */
232 /** Basic Region constructor (many sources) */
233 Region::Region (const SourceList& srcs)
234 : SessionObject(srcs.front()->session(), "toBeRenamed")
235 , _type (srcs.front()->type())
236 , REGION_DEFAULT_STATE(0,0)
239 , _first_edit (EditChangesNothing)
242 register_properties ();
244 _type = srcs.front()->type();
248 assert(_sources.size() > 0);
249 assert (_type == srcs.front()->type());
252 /** Create a new Region from an existing one */
253 Region::Region (boost::shared_ptr<const Region> other)
254 : SessionObject(other->session(), other->name())
255 , _type (other->data_type())
256 , REGION_COPY_STATE (other)
257 , _last_length (other->_last_length)
258 , _last_position(other->_last_position) \
259 , _first_edit (EditChangesNothing)
260 , _layer (other->_layer)
262 register_properties ();
264 /* override state that may have been incorrectly inherited from the other region
272 use_sources (other->_sources);
273 set_master_sources (other->_master_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 assert (_type == other->data_type());
308 /** Create a new Region from part of an existing one.
310 the start within \a other is given by \a offset
311 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
313 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
314 : SessionObject(other->session(), other->name())
315 , _type (other->data_type())
316 , REGION_COPY_STATE (other)
317 , _last_length (other->_last_length)
318 , _last_position(other->_last_position) \
319 , _first_edit (EditChangesNothing)
320 , _layer (other->_layer)
322 register_properties ();
324 /* override state that may have been incorrectly inherited from the other region
332 use_sources (other->_sources);
333 set_master_sources (other->_master_sources);
335 _start = other->_start + offset;
337 /* if the other region had a distinct sync point
338 set, then continue to use it as best we can.
339 otherwise, reset sync point back to start.
342 if (other->sync_marked()) {
343 if (other->_sync_position < _start) {
344 _sync_marked = false;
345 _sync_position = _start;
347 _sync_position = other->_sync_position;
350 _sync_marked = false;
351 _sync_position = _start;
354 assert (_type == other->data_type());
357 /** Create a copy of @param other but with different sources. Used by filters */
358 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
359 : SessionObject (other->session(), other->name())
360 , _type (srcs.front()->type())
361 , REGION_COPY_STATE (other)
362 , _last_length (other->_last_length)
363 , _last_position (other->_last_position)
364 , _first_edit (EditChangesID)
365 , _layer (other->_layer)
367 register_properties ();
370 _position_locked = false;
372 other->_first_edit = EditChangesName;
374 if (other->_extra_xml) {
375 _extra_xml = new XMLNode (*other->_extra_xml);
381 assert(_sources.size() > 0);
386 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
391 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
393 _playlist = wpl.lock();
397 Region::set_name (const std::string& str)
400 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
401 assert(_name == str);
403 send_change (Properties::name);
410 Region::set_length (framecnt_t len)
412 //cerr << "Region::set_length() len = " << len << endl;
417 if (_length != len && len != 0) {
419 /* check that the current _position wouldn't make the new
423 if (max_framepos - len < _position) {
427 if (!verify_length (len)) {
432 _last_length = _length;
433 set_length_internal (len);
437 invalidate_transients ();
439 if (!property_changes_suspended()) {
443 send_change (Properties::length);
448 Region::set_length_internal (framecnt_t len)
454 Region::maybe_uncopy ()
456 /* this does nothing but marked a semantic moment once upon a time */
460 Region::first_edit ()
462 boost::shared_ptr<Playlist> pl (playlist());
464 if (_first_edit != EditChangesNothing && pl) {
466 _name = RegionFactory::new_region_name (_name);
467 _first_edit = EditChangesNothing;
469 send_change (Properties::name);
471 RegionFactory::CheckNewRegion (shared_from_this());
476 Region::at_natural_position () const
478 boost::shared_ptr<Playlist> pl (playlist());
484 boost::shared_ptr<Region> whole_file_region = get_parent();
486 if (whole_file_region) {
487 if (_position == whole_file_region->position() + _start) {
496 Region::move_to_natural_position ()
498 boost::shared_ptr<Playlist> pl (playlist());
504 boost::shared_ptr<Region> whole_file_region = get_parent();
506 if (whole_file_region) {
507 set_position (whole_file_region->position() + _start);
512 Region::special_set_position (framepos_t pos)
514 /* this is used when creating a whole file region as
515 a way to store its "natural" or "captured" position.
518 _position = _position;
523 Region::set_position_lock_style (PositionLockStyle ps)
525 if (_position_lock_style != ps) {
527 boost::shared_ptr<Playlist> pl (playlist());
529 _position_lock_style = ps;
531 if (_position_lock_style == MusicTime) {
532 _session.bbt_time (_position, _bbt_time);
535 send_change (Properties::position_lock_style);
540 Region::update_after_tempo_map_change ()
542 boost::shared_ptr<Playlist> pl (playlist());
544 if (!pl || _position_lock_style != MusicTime) {
548 TempoMap& map (_session.tempo_map());
549 framepos_t pos = map.frame_time (_bbt_time);
550 set_position_internal (pos, false);
552 /* do this even if the position is the same. this helps out
553 a GUI that has moved its representation already.
555 send_change (Properties::position);
559 Region::set_position (framepos_t pos)
565 set_position_internal (pos, true);
567 /* do this even if the position is the same. this helps out
568 a GUI that has moved its representation already.
570 send_change (Properties::position);
574 /** A gui may need to create a region, then place it in an initial
575 * position determined by the user.
576 * When this takes place within one gui operation, we have to reset
577 * _last_position to prevent an implied move.
580 Region::set_initial_position (framepos_t pos)
586 if (_position != pos) {
589 /* check that the new _position wouldn't make the current
590 length impossible - if so, change the length.
592 XXX is this the right thing to do?
595 if (max_framepos - _length < _position) {
596 _last_length = _length;
597 _length = max_framepos - _position;
600 recompute_position_from_lock_style ();
601 /* ensure that this move doesn't cause a range move */
602 _last_position = _position;
606 /* do this even if the position is the same. this helps out
607 a GUI that has moved its representation already.
609 send_change (Properties::position);
613 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
615 /* We emit a change of Properties::position even if the position hasn't changed
616 (see Region::set_position), so we must always set this up so that
617 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
619 _last_position = _position;
621 if (_position != pos) {
624 /* check that the new _position wouldn't make the current
625 length impossible - if so, change the length.
627 XXX is this the right thing to do?
630 if (max_framepos - _length < _position) {
631 _last_length = _length;
632 _length = max_framepos - _position;
635 if (allow_bbt_recompute) {
636 recompute_position_from_lock_style ();
639 //invalidate_transients ();
644 Region::recompute_position_from_lock_style ()
646 if (_position_lock_style == MusicTime) {
647 _session.bbt_time (_position, _bbt_time);
652 Region::nudge_position (frameoffset_t n)
654 if (locked() || video_locked()) {
662 framepos_t new_position = _position;
665 if (_position > max_framepos - n) {
666 new_position = max_framepos;
671 if (_position < -n) {
678 set_position_internal (new_position, true);
680 send_change (Properties::position);
684 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
686 _ancestral_length = l;
687 _ancestral_start = s;
693 Region::set_start (framepos_t pos)
695 if (locked() || position_locked() || video_locked()) {
698 /* This just sets the start, nothing else. It effectively shifts
699 the contents of the Region within the overall extent of the Source,
700 without changing the Region's position or length
705 if (!verify_start (pos)) {
709 set_start_internal (pos);
712 invalidate_transients ();
714 send_change (Properties::start);
719 Region::move_start (frameoffset_t distance)
721 if (locked() || position_locked() || video_locked()) {
725 framepos_t new_start;
729 if (_start > max_framepos - distance) {
730 new_start = max_framepos; // makes no sense
732 new_start = _start + distance;
735 if (!verify_start (new_start)) {
739 } else if (distance < 0) {
741 if (_start < -distance) {
744 new_start = _start + distance;
751 if (new_start == _start) {
755 set_start_internal (new_start);
760 send_change (Properties::start);
764 Region::trim_front (framepos_t new_position)
766 modify_front (new_position, false);
770 Region::cut_front (framepos_t new_position)
772 modify_front (new_position, true);
776 Region::cut_end (framepos_t new_endpoint)
778 modify_end (new_endpoint, true);
782 Region::modify_front (framepos_t new_position, bool reset_fade)
788 framepos_t end = last_frame();
789 framepos_t source_zero;
791 if (_position > _start) {
792 source_zero = _position - _start;
794 source_zero = 0; // its actually negative, but this will work for us
797 if (new_position < end) { /* can't trim it zero or negative length */
799 framecnt_t newlen = 0;
800 framepos_t delta = 0;
802 if (!can_trim_start_before_source_start ()) {
803 /* can't trim it back past where source position zero is located */
804 new_position = max (new_position, source_zero);
807 if (new_position > _position) {
808 newlen = _length - (new_position - _position);
809 delta = -1 * (new_position - _position);
811 newlen = _length + (_position - new_position);
812 delta = _position - new_position;
815 trim_to_internal (new_position, newlen);
818 _right_of_split = true;
821 if (!property_changes_suspended()) {
822 recompute_at_start ();
825 if (_transients.size() > 0){
826 adjust_transients(delta);
832 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
838 if (new_endpoint > _position) {
839 trim_to_internal (_position, new_endpoint - _position);
841 _left_of_split = true;
843 if (!property_changes_suspended()) {
849 /** @param new_endpoint New region end point, such that, for example,
850 * a region at 0 of length 10 has an endpoint of 9.
854 Region::trim_end (framepos_t new_endpoint)
856 modify_end (new_endpoint, false);
860 Region::trim_to (framepos_t position, framecnt_t length)
866 trim_to_internal (position, length);
868 if (!property_changes_suspended()) {
869 recompute_at_start ();
875 Region::trim_to_internal (framepos_t position, framecnt_t length)
877 framepos_t new_start;
883 frameoffset_t const start_shift = position - _position;
885 if (start_shift > 0) {
887 if (_start > max_framepos - start_shift) {
888 new_start = max_framepos;
890 new_start = _start + start_shift;
893 } else if (start_shift < 0) {
895 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
898 new_start = _start + start_shift;
905 if (!verify_start_and_length (new_start, length)) {
909 PropertyChange what_changed;
911 if (_start != new_start) {
912 set_start_internal (new_start);
913 what_changed.add (Properties::start);
916 /* Set position before length, otherwise for MIDI regions this bad thing happens:
917 * 1. we call set_length_internal; length in beats is computed using the region's current
918 * (soon-to-be old) position
919 * 2. we call set_position_internal; position is set and length in frames re-computed using
920 * length in beats from (1) but at the new position, which is wrong if the region
921 * straddles a tempo/meter change.
924 if (_position != position) {
925 if (!property_changes_suspended()) {
926 _last_position = _position;
928 set_position_internal (position, true);
929 what_changed.add (Properties::position);
932 if (_length != length) {
933 if (!property_changes_suspended()) {
934 _last_length = _length;
936 set_length_internal (length);
937 what_changed.add (Properties::length);
942 PropertyChange start_and_length;
944 start_and_length.add (Properties::start);
945 start_and_length.add (Properties::length);
947 if (what_changed.contains (start_and_length)) {
951 if (!what_changed.empty()) {
952 send_change (what_changed);
957 Region::set_hidden (bool yn)
959 if (hidden() != yn) {
961 send_change (Properties::hidden);
966 Region::set_whole_file (bool yn)
969 /* no change signal */
973 Region::set_automatic (bool yn)
976 /* no change signal */
980 Region::set_muted (bool yn)
984 send_change (Properties::muted);
989 Region::set_opaque (bool yn)
991 if (opaque() != yn) {
993 send_change (Properties::opaque);
998 Region::set_locked (bool yn)
1000 if (locked() != yn) {
1002 send_change (Properties::locked);
1007 Region::set_video_locked (bool yn)
1009 if (video_locked() != yn) {
1011 send_change (Properties::video_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)
1156 XMLNode *node = new XMLNode ("Region");
1159 LocaleGuard lg (X_("C"));
1160 const char* fe = NULL;
1162 /* custom version of 'add_properties (*node);'
1163 * skip values that have have dedicated save functions
1164 * in AudioRegion::state()
1166 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1167 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1168 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1169 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1170 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1171 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1172 i->second->get_value (*node);
1175 id().print (buf, sizeof (buf));
1176 node->add_property ("id", buf);
1177 node->add_property ("type", _type.to_string());
1179 switch (_first_edit) {
1180 case EditChangesNothing:
1183 case EditChangesName:
1189 default: /* should be unreachable but makes g++ happy */
1194 node->add_property ("first-edit", fe);
1196 /* note: flags are stored by derived classes */
1198 if (_position_lock_style != AudioTime) {
1201 node->add_property ("bbt-position", str.str());
1204 for (uint32_t n=0; n < _sources.size(); ++n) {
1205 snprintf (buf2, sizeof(buf2), "source-%d", n);
1206 _sources[n]->id().print (buf, sizeof(buf));
1207 node->add_property (buf2, buf);
1210 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1211 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1212 _master_sources[n]->id().print (buf, sizeof (buf));
1213 node->add_property (buf2, buf);
1216 /* Only store nested sources for the whole-file region that acts
1217 as the parent/root of all regions using it.
1220 if (_whole_file && max_source_level() > 0) {
1222 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1224 /* region is compound - get its playlist and
1225 store that before we list the region that
1229 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1230 nested_node->add_child_nocopy ((*s)->get_state ());
1234 node->add_child_nocopy (*nested_node);
1239 node->add_child_copy (*_extra_xml);
1246 Region::get_state ()
1252 Region::set_state (const XMLNode& node, int version)
1254 PropertyChange what_changed;
1255 return _set_state (node, version, what_changed, true);
1259 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1261 const XMLProperty* prop;
1263 Stateful::save_extra_xml (node);
1265 what_changed = set_values (node);
1269 if (_position_lock_style == MusicTime) {
1270 if ((prop = node.property ("bbt-position")) == 0) {
1271 /* missing BBT info, revert to audio time locking */
1272 _position_lock_style = AudioTime;
1274 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1277 &_bbt_time.ticks) != 3) {
1278 _position_lock_style = AudioTime;
1283 /* fix problems with old sessions corrupted by impossible
1284 values for _stretch or _shift
1286 if (_stretch == 0.0f) {
1290 if (_shift == 0.0f) {
1295 send_change (what_changed);
1298 /* Quick fix for 2.x sessions when region is muted */
1299 if ((prop = node.property (X_("flags")))) {
1300 if (string::npos != prop->value().find("Muted")){
1305 // saved property is invalid, region-transients are not saved
1306 if (_transients.size() == 0){
1307 _valid_transients = false;
1314 Region::suspend_property_changes ()
1316 Stateful::suspend_property_changes ();
1317 _last_length = _length;
1318 _last_position = _position;
1322 Region::mid_thaw (const PropertyChange& what_changed)
1324 if (what_changed.contains (Properties::length)) {
1325 if (what_changed.contains (Properties::position)) {
1326 recompute_at_start ();
1328 recompute_at_end ();
1333 Region::send_change (const PropertyChange& what_changed)
1335 if (what_changed.empty()) {
1339 Stateful::send_change (what_changed);
1341 if (!Stateful::property_changes_suspended()) {
1343 /* Try and send a shared_pointer unless this is part of the constructor.
1348 boost::shared_ptr<Region> rptr = shared_from_this();
1349 RegionPropertyChanged (rptr, what_changed);
1351 /* no shared_ptr available, relax; */
1357 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1359 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1363 Region::equivalent (boost::shared_ptr<const Region> other) const
1365 return _start == other->_start &&
1366 _position == other->_position &&
1367 _length == other->_length;
1371 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1373 return _start == other->_start &&
1374 _length == other->_length;
1378 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1380 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1384 Region::source_deleted (boost::weak_ptr<Source>)
1388 if (!_session.deletion_in_progress()) {
1389 /* this is a very special case: at least one of the region's
1390 sources has bee deleted, so invalidate all references to
1391 ourselves. Do NOT do this during session deletion, because
1392 then we run the risk that this will actually result
1393 in this object being deleted (as refcnt goes to zero)
1394 while emitting DropReferences.
1402 Region::master_source_names ()
1404 SourceList::iterator i;
1406 vector<string> names;
1407 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1408 names.push_back((*i)->name());
1415 Region::set_master_sources (const SourceList& srcs)
1417 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1418 (*i)->dec_use_count ();
1421 _master_sources = srcs;
1422 assert (_sources.size() == _master_sources.size());
1424 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1425 (*i)->inc_use_count ();
1430 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1435 if ((_sources.size() != other->_sources.size()) ||
1436 (_master_sources.size() != other->_master_sources.size())) {
1440 SourceList::const_iterator i;
1441 SourceList::const_iterator io;
1443 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1444 if ((*i)->id() != (*io)->id()) {
1449 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1450 if ((*i)->id() != (*io)->id()) {
1459 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1465 SourceList::const_iterator i;
1466 SourceList::const_iterator io;
1468 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1469 if ((*i)->id() == (*io)->id()) {
1478 Region::source_string () const
1480 //string res = itos(_sources.size());
1483 res << _sources.size() << ":";
1485 SourceList::const_iterator i;
1487 for (i = _sources.begin(); i != _sources.end(); ++i) {
1488 res << (*i)->id() << ":";
1491 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1492 res << (*i)->id() << ":";
1499 Region::uses_source (boost::shared_ptr<const Source> source) const
1501 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1506 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1509 if (ps->playlist()->uses_source (source)) {
1515 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1520 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1523 if (ps->playlist()->uses_source (source)) {
1533 Region::source_length(uint32_t n) const
1535 assert (n < _sources.size());
1536 return _sources[n]->length (_position - _start);
1540 Region::verify_length (framecnt_t& len)
1542 if (source() && (source()->destructive() || source()->length_mutable())) {
1546 framecnt_t maxlen = 0;
1548 for (uint32_t n = 0; n < _sources.size(); ++n) {
1549 maxlen = max (maxlen, source_length(n) - _start);
1552 len = min (len, maxlen);
1558 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1560 if (source() && (source()->destructive() || source()->length_mutable())) {
1564 framecnt_t maxlen = 0;
1566 for (uint32_t n = 0; n < _sources.size(); ++n) {
1567 maxlen = max (maxlen, source_length(n) - new_start);
1570 new_length = min (new_length, maxlen);
1576 Region::verify_start (framepos_t pos)
1578 if (source() && (source()->destructive() || source()->length_mutable())) {
1582 for (uint32_t n = 0; n < _sources.size(); ++n) {
1583 if (pos > source_length(n) - _length) {
1591 Region::verify_start_mutable (framepos_t& new_start)
1593 if (source() && (source()->destructive() || source()->length_mutable())) {
1597 for (uint32_t n = 0; n < _sources.size(); ++n) {
1598 if (new_start > source_length(n) - _length) {
1599 new_start = source_length(n) - _length;
1605 boost::shared_ptr<Region>
1606 Region::get_parent() const
1608 boost::shared_ptr<Playlist> pl (playlist());
1611 boost::shared_ptr<Region> r;
1612 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1614 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1615 return boost::static_pointer_cast<Region> (r);
1619 return boost::shared_ptr<Region>();
1623 Region::apply (Filter& filter, Progress* progress)
1625 return filter.run (shared_from_this(), progress);
1630 Region::invalidate_transients ()
1632 _valid_transients = false;
1633 _transients.clear ();
1635 send_change (PropertyChange (Properties::valid_transients));
1639 Region::drop_sources ()
1641 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1642 (*i)->dec_use_count ();
1647 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1648 (*i)->dec_use_count ();
1651 _master_sources.clear ();
1655 Region::use_sources (SourceList const & s)
1657 set<boost::shared_ptr<Source> > unique_srcs;
1659 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1661 _sources.push_back (*i);
1662 (*i)->inc_use_count ();
1663 _master_sources.push_back (*i);
1664 (*i)->inc_use_count ();
1666 /* connect only once to DropReferences, even if sources are replicated
1669 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1670 unique_srcs.insert (*i);
1671 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1677 Region::can_trim () const
1679 CanTrim ct = CanTrim (0);
1685 /* if not locked, we can always move the front later, and the end earlier
1688 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1690 if (start() != 0 || can_trim_start_before_source_start ()) {
1691 ct = CanTrim (ct | FrontTrimEarlier);
1694 if (!_sources.empty()) {
1695 if ((start() + length()) < _sources.front()->length (0)) {
1696 ct = CanTrim (ct | EndTrimLater);
1704 Region::max_source_level () const
1708 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1709 lvl = max (lvl, (*i)->level());
1716 Region::is_compound () const
1718 return max_source_level() > 0;
1722 Region::post_set (const PropertyChange& pc)
1724 if (pc.contains (Properties::position)) {
1725 recompute_position_from_lock_style ();
1730 Region::set_start_internal (framecnt_t s)
1736 Region::earliest_possible_position () const
1738 if (_start > _position) {
1741 return _position - _start;
1746 Region::latest_possible_frame () const
1748 framecnt_t minlen = max_framecnt;
1750 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1751 /* non-audio regions have a length that may vary based on their
1752 * position, so we have to pass it in the call.
1754 minlen = min (minlen, (*i)->length (_position));
1757 /* the latest possible last frame is determined by the current
1758 * position, plus the shortest source extent past _start.
1761 return _position + (minlen - _start) - 1;