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);
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 , _layer (other->_layer)
332 register_properties ();
334 /* override state that may have been incorrectly inherited from the other region
342 use_sources (other->_sources);
344 _start = other->_start + offset;
346 /* if the other region had a distinct sync point
347 set, then continue to use it as best we can.
348 otherwise, reset sync point back to start.
351 if (other->sync_marked()) {
352 if (other->_sync_position < _start) {
353 _sync_marked = false;
354 _sync_position = _start;
356 _sync_position = other->_sync_position;
359 _sync_marked = false;
360 _sync_position = _start;
363 if (Profile->get_sae()) {
364 /* reset sync point to start if its ended up
365 outside region bounds.
368 if (_sync_position < _start || _sync_position >= _start + _length) {
369 _sync_marked = false;
370 _sync_position = _start;
374 assert (_type == other->data_type());
377 /** Create a copy of @param other but with different sources. Used by filters */
378 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
379 : SessionObject (other->session(), other->name())
380 , _type (srcs.front()->type())
381 , REGION_COPY_STATE (other)
382 , _last_length (other->_last_length)
383 , _last_position (other->_last_position)
384 , _first_edit (EditChangesID)
385 , _layer (other->_layer)
387 register_properties ();
390 _position_locked = false;
392 other->_first_edit = EditChangesName;
394 if (other->_extra_xml) {
395 _extra_xml = new XMLNode (*other->_extra_xml);
401 assert(_sources.size() > 0);
406 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
411 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
413 _playlist = wpl.lock();
417 Region::set_name (const std::string& str)
420 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
421 assert(_name == str);
423 send_change (Properties::name);
430 Region::set_length (framecnt_t len)
432 //cerr << "Region::set_length() len = " << len << endl;
437 if (_length != len && len != 0) {
439 /* check that the current _position wouldn't make the new
443 if (max_framepos - len < _position) {
447 if (!verify_length (len)) {
452 _last_length = _length;
453 set_length_internal (len);
457 invalidate_transients ();
459 if (!property_changes_suspended()) {
463 send_change (Properties::length);
468 Region::set_length_internal (framecnt_t len)
474 Region::maybe_uncopy ()
476 /* this does nothing but marked a semantic moment once upon a time */
480 Region::first_edit ()
482 boost::shared_ptr<Playlist> pl (playlist());
484 if (_first_edit != EditChangesNothing && pl) {
486 _name = RegionFactory::new_region_name (_name);
487 _first_edit = EditChangesNothing;
489 send_change (Properties::name);
491 RegionFactory::CheckNewRegion (shared_from_this());
496 Region::at_natural_position () const
498 boost::shared_ptr<Playlist> pl (playlist());
504 boost::shared_ptr<Region> whole_file_region = get_parent();
506 if (whole_file_region) {
507 if (_position == whole_file_region->position() + _start) {
516 Region::move_to_natural_position ()
518 boost::shared_ptr<Playlist> pl (playlist());
524 boost::shared_ptr<Region> whole_file_region = get_parent();
526 if (whole_file_region) {
527 set_position (whole_file_region->position() + _start);
532 Region::special_set_position (framepos_t pos)
534 /* this is used when creating a whole file region as
535 a way to store its "natural" or "captured" position.
538 _position = _position;
543 Region::set_position_lock_style (PositionLockStyle ps)
545 if (_position_lock_style != ps) {
547 boost::shared_ptr<Playlist> pl (playlist());
549 _position_lock_style = ps;
551 if (_position_lock_style == MusicTime) {
552 _session.bbt_time (_position, _bbt_time);
555 send_change (Properties::position_lock_style);
560 Region::update_after_tempo_map_change ()
562 boost::shared_ptr<Playlist> pl (playlist());
564 if (!pl || _position_lock_style != MusicTime) {
568 TempoMap& map (_session.tempo_map());
569 framepos_t pos = map.frame_time (_bbt_time);
570 set_position_internal (pos, false);
572 /* do this even if the position is the same. this helps out
573 a GUI that has moved its representation already.
575 send_change (Properties::position);
579 Region::set_position (framepos_t pos)
585 set_position_internal (pos, true);
587 /* do this even if the position is the same. this helps out
588 a GUI that has moved its representation already.
590 send_change (Properties::position);
594 /** A gui may need to create a region, then place it in an initial
595 * position determined by the user.
596 * When this takes place within one gui operation, we have to reset
597 * _last_position to prevent an implied move.
600 Region::set_initial_position (framepos_t pos)
606 if (_position != pos) {
609 /* check that the new _position wouldn't make the current
610 length impossible - if so, change the length.
612 XXX is this the right thing to do?
615 if (max_framepos - _length < _position) {
616 _last_length = _length;
617 _length = max_framepos - _position;
620 recompute_position_from_lock_style ();
621 /* ensure that this move doesn't cause a range move */
622 _last_position = _position;
626 /* do this even if the position is the same. this helps out
627 a GUI that has moved its representation already.
629 send_change (Properties::position);
633 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
635 /* We emit a change of Properties::position even if the position hasn't changed
636 (see Region::set_position), so we must always set this up so that
637 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
639 _last_position = _position;
641 if (_position != pos) {
644 /* check that the new _position wouldn't make the current
645 length impossible - if so, change the length.
647 XXX is this the right thing to do?
650 if (max_framepos - _length < _position) {
651 _last_length = _length;
652 _length = max_framepos - _position;
655 if (allow_bbt_recompute) {
656 recompute_position_from_lock_style ();
659 //invalidate_transients ();
664 Region::recompute_position_from_lock_style ()
666 if (_position_lock_style == MusicTime) {
667 _session.bbt_time (_position, _bbt_time);
672 Region::nudge_position (frameoffset_t n)
674 if (locked() || video_locked()) {
682 framepos_t new_position = _position;
685 if (_position > max_framepos - n) {
686 new_position = max_framepos;
691 if (_position < -n) {
698 set_position_internal (new_position, true);
700 send_change (Properties::position);
704 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
706 _ancestral_length = l;
707 _ancestral_start = s;
713 Region::set_start (framepos_t pos)
715 if (locked() || position_locked() || video_locked()) {
718 /* This just sets the start, nothing else. It effectively shifts
719 the contents of the Region within the overall extent of the Source,
720 without changing the Region's position or length
725 if (!verify_start (pos)) {
729 set_start_internal (pos);
732 invalidate_transients ();
734 send_change (Properties::start);
739 Region::move_start (frameoffset_t distance)
741 if (locked() || position_locked() || video_locked()) {
745 framepos_t new_start;
749 if (_start > max_framepos - distance) {
750 new_start = max_framepos; // makes no sense
752 new_start = _start + distance;
755 if (!verify_start (new_start)) {
759 } else if (distance < 0) {
761 if (_start < -distance) {
764 new_start = _start + distance;
771 if (new_start == _start) {
775 set_start_internal (new_start);
780 send_change (Properties::start);
784 Region::trim_front (framepos_t new_position)
786 modify_front (new_position, false);
790 Region::cut_front (framepos_t new_position)
792 modify_front (new_position, true);
796 Region::cut_end (framepos_t new_endpoint)
798 modify_end (new_endpoint, true);
802 Region::modify_front (framepos_t new_position, bool reset_fade)
808 framepos_t end = last_frame();
809 framepos_t source_zero;
811 if (_position > _start) {
812 source_zero = _position - _start;
814 source_zero = 0; // its actually negative, but this will work for us
817 if (new_position < end) { /* can't trim it zero or negative length */
819 framecnt_t newlen = 0;
820 framepos_t delta = 0;
822 if (!can_trim_start_before_source_start ()) {
823 /* can't trim it back past where source position zero is located */
824 new_position = max (new_position, source_zero);
827 if (new_position > _position) {
828 newlen = _length - (new_position - _position);
829 delta = -1 * (new_position - _position);
831 newlen = _length + (_position - new_position);
832 delta = _position - new_position;
835 trim_to_internal (new_position, newlen);
838 _right_of_split = true;
841 if (!property_changes_suspended()) {
842 recompute_at_start ();
845 if (_transients.size() > 0){
846 adjust_transients(delta);
852 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
858 if (new_endpoint > _position) {
859 trim_to_internal (_position, new_endpoint - _position);
861 _left_of_split = true;
863 if (!property_changes_suspended()) {
869 /** @param new_endpoint New region end point, such that, for example,
870 * a region at 0 of length 10 has an endpoint of 9.
874 Region::trim_end (framepos_t new_endpoint)
876 modify_end (new_endpoint, false);
880 Region::trim_to (framepos_t position, framecnt_t length)
886 trim_to_internal (position, length);
888 if (!property_changes_suspended()) {
889 recompute_at_start ();
895 Region::trim_to_internal (framepos_t position, framecnt_t length)
897 framepos_t new_start;
903 frameoffset_t const start_shift = position - _position;
905 if (start_shift > 0) {
907 if (_start > max_framepos - start_shift) {
908 new_start = max_framepos;
910 new_start = _start + start_shift;
913 } else if (start_shift < 0) {
915 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
918 new_start = _start + start_shift;
925 if (!verify_start_and_length (new_start, length)) {
929 PropertyChange what_changed;
931 if (_start != new_start) {
932 set_start_internal (new_start);
933 what_changed.add (Properties::start);
936 /* Set position before length, otherwise for MIDI regions this bad thing happens:
937 * 1. we call set_length_internal; length in beats is computed using the region's current
938 * (soon-to-be old) position
939 * 2. we call set_position_internal; position is set and length in frames re-computed using
940 * length in beats from (1) but at the new position, which is wrong if the region
941 * straddles a tempo/meter change.
944 if (_position != position) {
945 if (!property_changes_suspended()) {
946 _last_position = _position;
948 set_position_internal (position, true);
949 what_changed.add (Properties::position);
952 if (_length != length) {
953 if (!property_changes_suspended()) {
954 _last_length = _length;
956 set_length_internal (length);
957 what_changed.add (Properties::length);
962 PropertyChange start_and_length;
964 start_and_length.add (Properties::start);
965 start_and_length.add (Properties::length);
967 if (what_changed.contains (start_and_length)) {
971 if (!what_changed.empty()) {
972 send_change (what_changed);
977 Region::set_hidden (bool yn)
979 if (hidden() != yn) {
981 send_change (Properties::hidden);
986 Region::set_whole_file (bool yn)
989 /* no change signal */
993 Region::set_automatic (bool yn)
996 /* no change signal */
1000 Region::set_muted (bool yn)
1002 if (muted() != yn) {
1004 send_change (Properties::muted);
1009 Region::set_opaque (bool yn)
1011 if (opaque() != yn) {
1013 send_change (Properties::opaque);
1018 Region::set_locked (bool yn)
1020 if (locked() != yn) {
1022 send_change (Properties::locked);
1027 Region::set_video_locked (bool yn)
1029 if (video_locked() != yn) {
1031 send_change (Properties::video_locked);
1036 Region::set_position_locked (bool yn)
1038 if (position_locked() != yn) {
1039 _position_locked = yn;
1040 send_change (Properties::locked);
1044 /** Set the region's sync point.
1045 * @param absolute_pos Session time.
1048 Region::set_sync_position (framepos_t absolute_pos)
1050 /* position within our file */
1051 framepos_t const file_pos = _start + (absolute_pos - _position);
1053 if (file_pos != _sync_position) {
1054 _sync_marked = true;
1055 _sync_position = file_pos;
1056 if (!property_changes_suspended()) {
1060 send_change (Properties::sync_position);
1065 Region::clear_sync_position ()
1067 if (sync_marked()) {
1068 _sync_marked = false;
1069 if (!property_changes_suspended()) {
1073 send_change (Properties::sync_position);
1077 /* @return the sync point relative the first frame of the region */
1079 Region::sync_offset (int& dir) const
1081 if (sync_marked()) {
1082 if (_sync_position > _start) {
1084 return _sync_position - _start;
1087 return _start - _sync_position;
1096 Region::adjust_to_sync (framepos_t pos) const
1099 frameoffset_t offset = sync_offset (sync_dir);
1101 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1110 if (max_framepos - pos > offset) {
1118 /** @return Sync position in session time */
1120 Region::sync_position() const
1122 if (sync_marked()) {
1123 return _position - _start + _sync_position;
1125 /* if sync has not been marked, use the start of the region */
1133 boost::shared_ptr<Playlist> pl (playlist());
1135 pl->raise_region (shared_from_this ());
1142 boost::shared_ptr<Playlist> pl (playlist());
1144 pl->lower_region (shared_from_this ());
1150 Region::raise_to_top ()
1152 boost::shared_ptr<Playlist> pl (playlist());
1154 pl->raise_region_to_top (shared_from_this());
1159 Region::lower_to_bottom ()
1161 boost::shared_ptr<Playlist> pl (playlist());
1163 pl->lower_region_to_bottom (shared_from_this());
1168 Region::set_layer (layer_t l)
1176 XMLNode *node = new XMLNode ("Region");
1179 LocaleGuard lg (X_("C"));
1180 const char* fe = NULL;
1182 /* custom version of 'add_properties (*node);'
1183 * skip values that have have dedicated save functions
1184 * in AudioRegion::state()
1186 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1187 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1188 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1189 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1190 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1191 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1192 i->second->get_value (*node);
1195 id().print (buf, sizeof (buf));
1196 node->add_property ("id", buf);
1197 node->add_property ("type", _type.to_string());
1199 switch (_first_edit) {
1200 case EditChangesNothing:
1203 case EditChangesName:
1209 default: /* should be unreachable but makes g++ happy */
1214 node->add_property ("first-edit", fe);
1216 /* note: flags are stored by derived classes */
1218 if (_position_lock_style != AudioTime) {
1221 node->add_property ("bbt-position", str.str());
1224 for (uint32_t n=0; n < _sources.size(); ++n) {
1225 snprintf (buf2, sizeof(buf2), "source-%d", n);
1226 _sources[n]->id().print (buf, sizeof(buf));
1227 node->add_property (buf2, buf);
1230 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1231 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1232 _master_sources[n]->id().print (buf, sizeof (buf));
1233 node->add_property (buf2, buf);
1236 /* Only store nested sources for the whole-file region that acts
1237 as the parent/root of all regions using it.
1240 if (_whole_file && max_source_level() > 0) {
1242 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1244 /* region is compound - get its playlist and
1245 store that before we list the region that
1249 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1250 nested_node->add_child_nocopy ((*s)->get_state ());
1254 node->add_child_nocopy (*nested_node);
1259 node->add_child_copy (*_extra_xml);
1266 Region::get_state ()
1272 Region::set_state (const XMLNode& node, int version)
1274 PropertyChange what_changed;
1275 return _set_state (node, version, what_changed, true);
1279 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1281 const XMLProperty* prop;
1283 Stateful::save_extra_xml (node);
1285 what_changed = set_values (node);
1289 if (_position_lock_style == MusicTime) {
1290 if ((prop = node.property ("bbt-position")) == 0) {
1291 /* missing BBT info, revert to audio time locking */
1292 _position_lock_style = AudioTime;
1294 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1297 &_bbt_time.ticks) != 3) {
1298 _position_lock_style = AudioTime;
1303 /* fix problems with old sessions corrupted by impossible
1304 values for _stretch or _shift
1306 if (_stretch == 0.0f) {
1310 if (_shift == 0.0f) {
1315 send_change (what_changed);
1318 /* Quick fix for 2.x sessions when region is muted */
1319 if ((prop = node.property (X_("flags")))) {
1320 if (string::npos != prop->value().find("Muted")){
1325 // saved property is invalid, region-transients are not saved
1326 if (_transients.size() == 0){
1327 _valid_transients = false;
1334 Region::suspend_property_changes ()
1336 Stateful::suspend_property_changes ();
1337 _last_length = _length;
1338 _last_position = _position;
1342 Region::mid_thaw (const PropertyChange& what_changed)
1344 if (what_changed.contains (Properties::length)) {
1345 if (what_changed.contains (Properties::position)) {
1346 recompute_at_start ();
1348 recompute_at_end ();
1353 Region::send_change (const PropertyChange& what_changed)
1355 if (what_changed.empty()) {
1359 Stateful::send_change (what_changed);
1361 if (!Stateful::property_changes_suspended()) {
1363 /* Try and send a shared_pointer unless this is part of the constructor.
1368 boost::shared_ptr<Region> rptr = shared_from_this();
1369 RegionPropertyChanged (rptr, what_changed);
1371 /* no shared_ptr available, relax; */
1377 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1379 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1383 Region::equivalent (boost::shared_ptr<const Region> other) const
1385 return _start == other->_start &&
1386 _position == other->_position &&
1387 _length == other->_length;
1391 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1393 return _start == other->_start &&
1394 _length == other->_length;
1398 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1400 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1404 Region::source_deleted (boost::weak_ptr<Source>)
1408 if (!_session.deletion_in_progress()) {
1409 /* this is a very special case: at least one of the region's
1410 sources has bee deleted, so invalidate all references to
1411 ourselves. Do NOT do this during session deletion, because
1412 then we run the risk that this will actually result
1413 in this object being deleted (as refcnt goes to zero)
1414 while emitting DropReferences.
1422 Region::master_source_names ()
1424 SourceList::iterator i;
1426 vector<string> names;
1427 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1428 names.push_back((*i)->name());
1435 Region::set_master_sources (const SourceList& srcs)
1437 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1438 (*i)->dec_use_count ();
1441 _master_sources = srcs;
1442 assert (_sources.size() == _master_sources.size());
1444 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1445 (*i)->inc_use_count ();
1450 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1455 if ((_sources.size() != other->_sources.size()) ||
1456 (_master_sources.size() != other->_master_sources.size())) {
1460 SourceList::const_iterator i;
1461 SourceList::const_iterator io;
1463 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1464 if ((*i)->id() != (*io)->id()) {
1469 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1470 if ((*i)->id() != (*io)->id()) {
1479 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1485 SourceList::const_iterator i;
1486 SourceList::const_iterator io;
1488 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1489 if ((*i)->id() == (*io)->id()) {
1498 Region::source_string () const
1500 //string res = itos(_sources.size());
1503 res << _sources.size() << ":";
1505 SourceList::const_iterator i;
1507 for (i = _sources.begin(); i != _sources.end(); ++i) {
1508 res << (*i)->id() << ":";
1511 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1512 res << (*i)->id() << ":";
1519 Region::uses_source (boost::shared_ptr<const Source> source) const
1521 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1526 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1529 if (ps->playlist()->uses_source (source)) {
1535 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1540 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1543 if (ps->playlist()->uses_source (source)) {
1553 Region::source_length(uint32_t n) const
1555 assert (n < _sources.size());
1556 return _sources[n]->length (_position - _start);
1560 Region::verify_length (framecnt_t& len)
1562 if (source() && (source()->destructive() || source()->length_mutable())) {
1566 framecnt_t maxlen = 0;
1568 for (uint32_t n = 0; n < _sources.size(); ++n) {
1569 maxlen = max (maxlen, source_length(n) - _start);
1572 len = min (len, maxlen);
1578 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1580 if (source() && (source()->destructive() || source()->length_mutable())) {
1584 framecnt_t maxlen = 0;
1586 for (uint32_t n = 0; n < _sources.size(); ++n) {
1587 maxlen = max (maxlen, source_length(n) - new_start);
1590 new_length = min (new_length, maxlen);
1596 Region::verify_start (framepos_t pos)
1598 if (source() && (source()->destructive() || source()->length_mutable())) {
1602 for (uint32_t n = 0; n < _sources.size(); ++n) {
1603 if (pos > source_length(n) - _length) {
1611 Region::verify_start_mutable (framepos_t& new_start)
1613 if (source() && (source()->destructive() || source()->length_mutable())) {
1617 for (uint32_t n = 0; n < _sources.size(); ++n) {
1618 if (new_start > source_length(n) - _length) {
1619 new_start = source_length(n) - _length;
1625 boost::shared_ptr<Region>
1626 Region::get_parent() const
1628 boost::shared_ptr<Playlist> pl (playlist());
1631 boost::shared_ptr<Region> r;
1632 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1634 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1635 return boost::static_pointer_cast<Region> (r);
1639 return boost::shared_ptr<Region>();
1643 Region::apply (Filter& filter, Progress* progress)
1645 return filter.run (shared_from_this(), progress);
1650 Region::invalidate_transients ()
1652 _valid_transients = false;
1653 _transients.clear ();
1655 send_change (PropertyChange (Properties::valid_transients));
1659 Region::drop_sources ()
1661 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1662 (*i)->dec_use_count ();
1667 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1668 (*i)->dec_use_count ();
1671 _master_sources.clear ();
1675 Region::use_sources (SourceList const & s)
1677 set<boost::shared_ptr<Source> > unique_srcs;
1679 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1681 _sources.push_back (*i);
1682 (*i)->inc_use_count ();
1683 _master_sources.push_back (*i);
1684 (*i)->inc_use_count ();
1686 /* connect only once to DropReferences, even if sources are replicated
1689 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1690 unique_srcs.insert (*i);
1691 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1697 Region::can_trim () const
1699 CanTrim ct = CanTrim (0);
1705 /* if not locked, we can always move the front later, and the end earlier
1708 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1710 if (start() != 0 || can_trim_start_before_source_start ()) {
1711 ct = CanTrim (ct | FrontTrimEarlier);
1714 if (!_sources.empty()) {
1715 if ((start() + length()) < _sources.front()->length (0)) {
1716 ct = CanTrim (ct | EndTrimLater);
1724 Region::max_source_level () const
1728 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1729 lvl = max (lvl, (*i)->level());
1736 Region::is_compound () const
1738 return max_source_level() > 0;
1742 Region::post_set (const PropertyChange& pc)
1744 if (pc.contains (Properties::position)) {
1745 recompute_position_from_lock_style ();
1750 Region::set_start_internal (framecnt_t s)
1756 Region::earliest_possible_position () const
1758 if (_start > _position) {
1761 return _position - _start;
1766 Region::latest_possible_frame () const
1768 framecnt_t minlen = max_framecnt;
1770 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1771 /* non-audio regions have a length that may vary based on their
1772 * position, so we have to pass it in the call.
1774 minlen = min (minlen, (*i)->length (_position));
1777 /* the latest possible last frame is determined by the current
1778 * position, plus the shortest source extent past _start.
1781 return _position + (minlen - _start) - 1;