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 assert (_type == other->data_type());
307 /** Create a new Region from part of an existing one.
309 the start within \a other is given by \a offset
310 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
312 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
313 : SessionObject(other->session(), other->name())
314 , _type (other->data_type())
315 , REGION_COPY_STATE (other)
316 , _last_length (other->_last_length)
317 , _last_position(other->_last_position) \
318 , _first_edit (EditChangesNothing)
319 , _layer (other->_layer)
321 register_properties ();
323 /* override state that may have been incorrectly inherited from the other region
331 use_sources (other->_sources);
333 _start = other->_start + offset;
335 /* if the other region had a distinct sync point
336 set, then continue to use it as best we can.
337 otherwise, reset sync point back to start.
340 if (other->sync_marked()) {
341 if (other->_sync_position < _start) {
342 _sync_marked = false;
343 _sync_position = _start;
345 _sync_position = other->_sync_position;
348 _sync_marked = false;
349 _sync_position = _start;
352 assert (_type == other->data_type());
355 /** Create a copy of @param other but with different sources. Used by filters */
356 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
357 : SessionObject (other->session(), other->name())
358 , _type (srcs.front()->type())
359 , REGION_COPY_STATE (other)
360 , _last_length (other->_last_length)
361 , _last_position (other->_last_position)
362 , _first_edit (EditChangesID)
363 , _layer (other->_layer)
365 register_properties ();
368 _position_locked = false;
370 other->_first_edit = EditChangesName;
372 if (other->_extra_xml) {
373 _extra_xml = new XMLNode (*other->_extra_xml);
379 assert(_sources.size() > 0);
384 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
389 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
391 _playlist = wpl.lock();
395 Region::set_name (const std::string& str)
398 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
399 assert(_name == str);
401 send_change (Properties::name);
408 Region::set_length (framecnt_t len)
410 //cerr << "Region::set_length() len = " << len << endl;
415 if (_length != len && len != 0) {
417 /* check that the current _position wouldn't make the new
421 if (max_framepos - len < _position) {
425 if (!verify_length (len)) {
430 _last_length = _length;
431 set_length_internal (len);
435 invalidate_transients ();
437 if (!property_changes_suspended()) {
441 send_change (Properties::length);
446 Region::set_length_internal (framecnt_t len)
452 Region::maybe_uncopy ()
454 /* this does nothing but marked a semantic moment once upon a time */
458 Region::first_edit ()
460 boost::shared_ptr<Playlist> pl (playlist());
462 if (_first_edit != EditChangesNothing && pl) {
464 _name = RegionFactory::new_region_name (_name);
465 _first_edit = EditChangesNothing;
467 send_change (Properties::name);
469 RegionFactory::CheckNewRegion (shared_from_this());
474 Region::at_natural_position () const
476 boost::shared_ptr<Playlist> pl (playlist());
482 boost::shared_ptr<Region> whole_file_region = get_parent();
484 if (whole_file_region) {
485 if (_position == whole_file_region->position() + _start) {
494 Region::move_to_natural_position ()
496 boost::shared_ptr<Playlist> pl (playlist());
502 boost::shared_ptr<Region> whole_file_region = get_parent();
504 if (whole_file_region) {
505 set_position (whole_file_region->position() + _start);
510 Region::special_set_position (framepos_t pos)
512 /* this is used when creating a whole file region as
513 a way to store its "natural" or "captured" position.
516 _position = _position;
521 Region::set_position_lock_style (PositionLockStyle ps)
523 if (_position_lock_style != ps) {
525 boost::shared_ptr<Playlist> pl (playlist());
527 _position_lock_style = ps;
529 if (_position_lock_style == MusicTime) {
530 _session.bbt_time (_position, _bbt_time);
533 send_change (Properties::position_lock_style);
538 Region::update_after_tempo_map_change ()
540 boost::shared_ptr<Playlist> pl (playlist());
542 if (!pl || _position_lock_style != MusicTime) {
546 TempoMap& map (_session.tempo_map());
547 framepos_t pos = map.frame_time (_bbt_time);
548 set_position_internal (pos, false);
550 /* do this even if the position is the same. this helps out
551 a GUI that has moved its representation already.
553 send_change (Properties::position);
557 Region::set_position (framepos_t pos)
563 set_position_internal (pos, true);
565 /* do this even if the position is the same. this helps out
566 a GUI that has moved its representation already.
568 send_change (Properties::position);
572 /** A gui may need to create a region, then place it in an initial
573 * position determined by the user.
574 * When this takes place within one gui operation, we have to reset
575 * _last_position to prevent an implied move.
578 Region::set_initial_position (framepos_t pos)
584 if (_position != pos) {
587 /* check that the new _position wouldn't make the current
588 length impossible - if so, change the length.
590 XXX is this the right thing to do?
593 if (max_framepos - _length < _position) {
594 _last_length = _length;
595 _length = max_framepos - _position;
598 recompute_position_from_lock_style ();
599 /* ensure that this move doesn't cause a range move */
600 _last_position = _position;
604 /* do this even if the position is the same. this helps out
605 a GUI that has moved its representation already.
607 send_change (Properties::position);
611 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
613 /* We emit a change of Properties::position even if the position hasn't changed
614 (see Region::set_position), so we must always set this up so that
615 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
617 _last_position = _position;
619 if (_position != pos) {
622 /* check that the new _position wouldn't make the current
623 length impossible - if so, change the length.
625 XXX is this the right thing to do?
628 if (max_framepos - _length < _position) {
629 _last_length = _length;
630 _length = max_framepos - _position;
633 if (allow_bbt_recompute) {
634 recompute_position_from_lock_style ();
637 //invalidate_transients ();
642 Region::recompute_position_from_lock_style ()
644 if (_position_lock_style == MusicTime) {
645 _session.bbt_time (_position, _bbt_time);
650 Region::nudge_position (frameoffset_t n)
652 if (locked() || video_locked()) {
660 framepos_t new_position = _position;
663 if (_position > max_framepos - n) {
664 new_position = max_framepos;
669 if (_position < -n) {
676 set_position_internal (new_position, true);
678 send_change (Properties::position);
682 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
684 _ancestral_length = l;
685 _ancestral_start = s;
691 Region::set_start (framepos_t pos)
693 if (locked() || position_locked() || video_locked()) {
696 /* This just sets the start, nothing else. It effectively shifts
697 the contents of the Region within the overall extent of the Source,
698 without changing the Region's position or length
703 if (!verify_start (pos)) {
707 set_start_internal (pos);
710 invalidate_transients ();
712 send_change (Properties::start);
717 Region::move_start (frameoffset_t distance)
719 if (locked() || position_locked() || video_locked()) {
723 framepos_t new_start;
727 if (_start > max_framepos - distance) {
728 new_start = max_framepos; // makes no sense
730 new_start = _start + distance;
733 if (!verify_start (new_start)) {
737 } else if (distance < 0) {
739 if (_start < -distance) {
742 new_start = _start + distance;
749 if (new_start == _start) {
753 set_start_internal (new_start);
758 send_change (Properties::start);
762 Region::trim_front (framepos_t new_position)
764 modify_front (new_position, false);
768 Region::cut_front (framepos_t new_position)
770 modify_front (new_position, true);
774 Region::cut_end (framepos_t new_endpoint)
776 modify_end (new_endpoint, true);
780 Region::modify_front (framepos_t new_position, bool reset_fade)
786 framepos_t end = last_frame();
787 framepos_t source_zero;
789 if (_position > _start) {
790 source_zero = _position - _start;
792 source_zero = 0; // its actually negative, but this will work for us
795 if (new_position < end) { /* can't trim it zero or negative length */
797 framecnt_t newlen = 0;
798 framepos_t delta = 0;
800 if (!can_trim_start_before_source_start ()) {
801 /* can't trim it back past where source position zero is located */
802 new_position = max (new_position, source_zero);
805 if (new_position > _position) {
806 newlen = _length - (new_position - _position);
807 delta = -1 * (new_position - _position);
809 newlen = _length + (_position - new_position);
810 delta = _position - new_position;
813 trim_to_internal (new_position, newlen);
816 _right_of_split = true;
819 if (!property_changes_suspended()) {
820 recompute_at_start ();
823 if (_transients.size() > 0){
824 adjust_transients(delta);
830 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
836 if (new_endpoint > _position) {
837 trim_to_internal (_position, new_endpoint - _position);
839 _left_of_split = true;
841 if (!property_changes_suspended()) {
847 /** @param new_endpoint New region end point, such that, for example,
848 * a region at 0 of length 10 has an endpoint of 9.
852 Region::trim_end (framepos_t new_endpoint)
854 modify_end (new_endpoint, false);
858 Region::trim_to (framepos_t position, framecnt_t length)
864 trim_to_internal (position, length);
866 if (!property_changes_suspended()) {
867 recompute_at_start ();
873 Region::trim_to_internal (framepos_t position, framecnt_t length)
875 framepos_t new_start;
881 frameoffset_t const start_shift = position - _position;
883 if (start_shift > 0) {
885 if (_start > max_framepos - start_shift) {
886 new_start = max_framepos;
888 new_start = _start + start_shift;
891 } else if (start_shift < 0) {
893 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
896 new_start = _start + start_shift;
903 if (!verify_start_and_length (new_start, length)) {
907 PropertyChange what_changed;
909 if (_start != new_start) {
910 set_start_internal (new_start);
911 what_changed.add (Properties::start);
914 /* Set position before length, otherwise for MIDI regions this bad thing happens:
915 * 1. we call set_length_internal; length in beats is computed using the region's current
916 * (soon-to-be old) position
917 * 2. we call set_position_internal; position is set and length in frames re-computed using
918 * length in beats from (1) but at the new position, which is wrong if the region
919 * straddles a tempo/meter change.
922 if (_position != position) {
923 if (!property_changes_suspended()) {
924 _last_position = _position;
926 set_position_internal (position, true);
927 what_changed.add (Properties::position);
930 if (_length != length) {
931 if (!property_changes_suspended()) {
932 _last_length = _length;
934 set_length_internal (length);
935 what_changed.add (Properties::length);
940 PropertyChange start_and_length;
942 start_and_length.add (Properties::start);
943 start_and_length.add (Properties::length);
945 if (what_changed.contains (start_and_length)) {
949 if (!what_changed.empty()) {
950 send_change (what_changed);
955 Region::set_hidden (bool yn)
957 if (hidden() != yn) {
959 send_change (Properties::hidden);
964 Region::set_whole_file (bool yn)
967 /* no change signal */
971 Region::set_automatic (bool yn)
974 /* no change signal */
978 Region::set_muted (bool yn)
982 send_change (Properties::muted);
987 Region::set_opaque (bool yn)
989 if (opaque() != yn) {
991 send_change (Properties::opaque);
996 Region::set_locked (bool yn)
998 if (locked() != yn) {
1000 send_change (Properties::locked);
1005 Region::set_video_locked (bool yn)
1007 if (video_locked() != yn) {
1009 send_change (Properties::video_locked);
1014 Region::set_position_locked (bool yn)
1016 if (position_locked() != yn) {
1017 _position_locked = yn;
1018 send_change (Properties::locked);
1022 /** Set the region's sync point.
1023 * @param absolute_pos Session time.
1026 Region::set_sync_position (framepos_t absolute_pos)
1028 /* position within our file */
1029 framepos_t const file_pos = _start + (absolute_pos - _position);
1031 if (file_pos != _sync_position) {
1032 _sync_marked = true;
1033 _sync_position = file_pos;
1034 if (!property_changes_suspended()) {
1038 send_change (Properties::sync_position);
1043 Region::clear_sync_position ()
1045 if (sync_marked()) {
1046 _sync_marked = false;
1047 if (!property_changes_suspended()) {
1051 send_change (Properties::sync_position);
1055 /* @return the sync point relative the first frame of the region */
1057 Region::sync_offset (int& dir) const
1059 if (sync_marked()) {
1060 if (_sync_position > _start) {
1062 return _sync_position - _start;
1065 return _start - _sync_position;
1074 Region::adjust_to_sync (framepos_t pos) const
1077 frameoffset_t offset = sync_offset (sync_dir);
1079 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1088 if (max_framepos - pos > offset) {
1096 /** @return Sync position in session time */
1098 Region::sync_position() const
1100 if (sync_marked()) {
1101 return _position - _start + _sync_position;
1103 /* if sync has not been marked, use the start of the region */
1111 boost::shared_ptr<Playlist> pl (playlist());
1113 pl->raise_region (shared_from_this ());
1120 boost::shared_ptr<Playlist> pl (playlist());
1122 pl->lower_region (shared_from_this ());
1128 Region::raise_to_top ()
1130 boost::shared_ptr<Playlist> pl (playlist());
1132 pl->raise_region_to_top (shared_from_this());
1137 Region::lower_to_bottom ()
1139 boost::shared_ptr<Playlist> pl (playlist());
1141 pl->lower_region_to_bottom (shared_from_this());
1146 Region::set_layer (layer_t l)
1154 XMLNode *node = new XMLNode ("Region");
1157 LocaleGuard lg (X_("C"));
1158 const char* fe = NULL;
1160 /* custom version of 'add_properties (*node);'
1161 * skip values that have have dedicated save functions
1162 * in AudioRegion::state()
1164 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1165 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1166 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1167 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1168 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1169 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1170 i->second->get_value (*node);
1173 id().print (buf, sizeof (buf));
1174 node->add_property ("id", buf);
1175 node->add_property ("type", _type.to_string());
1177 switch (_first_edit) {
1178 case EditChangesNothing:
1181 case EditChangesName:
1187 default: /* should be unreachable but makes g++ happy */
1192 node->add_property ("first-edit", fe);
1194 /* note: flags are stored by derived classes */
1196 if (_position_lock_style != AudioTime) {
1199 node->add_property ("bbt-position", str.str());
1202 for (uint32_t n=0; n < _sources.size(); ++n) {
1203 snprintf (buf2, sizeof(buf2), "source-%d", n);
1204 _sources[n]->id().print (buf, sizeof(buf));
1205 node->add_property (buf2, buf);
1208 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1209 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1210 _master_sources[n]->id().print (buf, sizeof (buf));
1211 node->add_property (buf2, buf);
1214 /* Only store nested sources for the whole-file region that acts
1215 as the parent/root of all regions using it.
1218 if (_whole_file && max_source_level() > 0) {
1220 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1222 /* region is compound - get its playlist and
1223 store that before we list the region that
1227 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1228 nested_node->add_child_nocopy ((*s)->get_state ());
1232 node->add_child_nocopy (*nested_node);
1237 node->add_child_copy (*_extra_xml);
1244 Region::get_state ()
1250 Region::set_state (const XMLNode& node, int version)
1252 PropertyChange what_changed;
1253 return _set_state (node, version, what_changed, true);
1257 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1259 const XMLProperty* prop;
1261 Stateful::save_extra_xml (node);
1263 what_changed = set_values (node);
1267 if (_position_lock_style == MusicTime) {
1268 if ((prop = node.property ("bbt-position")) == 0) {
1269 /* missing BBT info, revert to audio time locking */
1270 _position_lock_style = AudioTime;
1272 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1275 &_bbt_time.ticks) != 3) {
1276 _position_lock_style = AudioTime;
1281 /* fix problems with old sessions corrupted by impossible
1282 values for _stretch or _shift
1284 if (_stretch == 0.0f) {
1288 if (_shift == 0.0f) {
1293 send_change (what_changed);
1296 /* Quick fix for 2.x sessions when region is muted */
1297 if ((prop = node.property (X_("flags")))) {
1298 if (string::npos != prop->value().find("Muted")){
1303 // saved property is invalid, region-transients are not saved
1304 if (_transients.size() == 0){
1305 _valid_transients = false;
1312 Region::suspend_property_changes ()
1314 Stateful::suspend_property_changes ();
1315 _last_length = _length;
1316 _last_position = _position;
1320 Region::mid_thaw (const PropertyChange& what_changed)
1322 if (what_changed.contains (Properties::length)) {
1323 if (what_changed.contains (Properties::position)) {
1324 recompute_at_start ();
1326 recompute_at_end ();
1331 Region::send_change (const PropertyChange& what_changed)
1333 if (what_changed.empty()) {
1337 Stateful::send_change (what_changed);
1339 if (!Stateful::property_changes_suspended()) {
1341 /* Try and send a shared_pointer unless this is part of the constructor.
1346 boost::shared_ptr<Region> rptr = shared_from_this();
1347 RegionPropertyChanged (rptr, what_changed);
1349 /* no shared_ptr available, relax; */
1355 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1357 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1361 Region::equivalent (boost::shared_ptr<const Region> other) const
1363 return _start == other->_start &&
1364 _position == other->_position &&
1365 _length == other->_length;
1369 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1371 return _start == other->_start &&
1372 _length == other->_length;
1376 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1378 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1382 Region::source_deleted (boost::weak_ptr<Source>)
1386 if (!_session.deletion_in_progress()) {
1387 /* this is a very special case: at least one of the region's
1388 sources has bee deleted, so invalidate all references to
1389 ourselves. Do NOT do this during session deletion, because
1390 then we run the risk that this will actually result
1391 in this object being deleted (as refcnt goes to zero)
1392 while emitting DropReferences.
1400 Region::master_source_names ()
1402 SourceList::iterator i;
1404 vector<string> names;
1405 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1406 names.push_back((*i)->name());
1413 Region::set_master_sources (const SourceList& srcs)
1415 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1416 (*i)->dec_use_count ();
1419 _master_sources = srcs;
1420 assert (_sources.size() == _master_sources.size());
1422 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1423 (*i)->inc_use_count ();
1428 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1433 if ((_sources.size() != other->_sources.size()) ||
1434 (_master_sources.size() != other->_master_sources.size())) {
1438 SourceList::const_iterator i;
1439 SourceList::const_iterator io;
1441 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1442 if ((*i)->id() != (*io)->id()) {
1447 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1448 if ((*i)->id() != (*io)->id()) {
1457 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1463 SourceList::const_iterator i;
1464 SourceList::const_iterator io;
1466 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1467 if ((*i)->id() == (*io)->id()) {
1476 Region::source_string () const
1478 //string res = itos(_sources.size());
1481 res << _sources.size() << ":";
1483 SourceList::const_iterator i;
1485 for (i = _sources.begin(); i != _sources.end(); ++i) {
1486 res << (*i)->id() << ":";
1489 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1490 res << (*i)->id() << ":";
1497 Region::uses_source (boost::shared_ptr<const Source> source) const
1499 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1504 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1507 if (ps->playlist()->uses_source (source)) {
1513 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1518 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1521 if (ps->playlist()->uses_source (source)) {
1531 Region::source_length(uint32_t n) const
1533 assert (n < _sources.size());
1534 return _sources[n]->length (_position - _start);
1538 Region::verify_length (framecnt_t& len)
1540 if (source() && (source()->destructive() || source()->length_mutable())) {
1544 framecnt_t maxlen = 0;
1546 for (uint32_t n = 0; n < _sources.size(); ++n) {
1547 maxlen = max (maxlen, source_length(n) - _start);
1550 len = min (len, maxlen);
1556 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1558 if (source() && (source()->destructive() || source()->length_mutable())) {
1562 framecnt_t maxlen = 0;
1564 for (uint32_t n = 0; n < _sources.size(); ++n) {
1565 maxlen = max (maxlen, source_length(n) - new_start);
1568 new_length = min (new_length, maxlen);
1574 Region::verify_start (framepos_t pos)
1576 if (source() && (source()->destructive() || source()->length_mutable())) {
1580 for (uint32_t n = 0; n < _sources.size(); ++n) {
1581 if (pos > source_length(n) - _length) {
1589 Region::verify_start_mutable (framepos_t& new_start)
1591 if (source() && (source()->destructive() || source()->length_mutable())) {
1595 for (uint32_t n = 0; n < _sources.size(); ++n) {
1596 if (new_start > source_length(n) - _length) {
1597 new_start = source_length(n) - _length;
1603 boost::shared_ptr<Region>
1604 Region::get_parent() const
1606 boost::shared_ptr<Playlist> pl (playlist());
1609 boost::shared_ptr<Region> r;
1610 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1612 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1613 return boost::static_pointer_cast<Region> (r);
1617 return boost::shared_ptr<Region>();
1621 Region::apply (Filter& filter, Progress* progress)
1623 return filter.run (shared_from_this(), progress);
1628 Region::invalidate_transients ()
1630 _valid_transients = false;
1631 _transients.clear ();
1633 send_change (PropertyChange (Properties::valid_transients));
1637 Region::drop_sources ()
1639 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1640 (*i)->dec_use_count ();
1645 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1646 (*i)->dec_use_count ();
1649 _master_sources.clear ();
1653 Region::use_sources (SourceList const & s)
1655 set<boost::shared_ptr<Source> > unique_srcs;
1657 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1659 _sources.push_back (*i);
1660 (*i)->inc_use_count ();
1661 _master_sources.push_back (*i);
1662 (*i)->inc_use_count ();
1664 /* connect only once to DropReferences, even if sources are replicated
1667 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1668 unique_srcs.insert (*i);
1669 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1675 Region::can_trim () const
1677 CanTrim ct = CanTrim (0);
1683 /* if not locked, we can always move the front later, and the end earlier
1686 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1688 if (start() != 0 || can_trim_start_before_source_start ()) {
1689 ct = CanTrim (ct | FrontTrimEarlier);
1692 if (!_sources.empty()) {
1693 if ((start() + length()) < _sources.front()->length (0)) {
1694 ct = CanTrim (ct | EndTrimLater);
1702 Region::max_source_level () const
1706 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1707 lvl = max (lvl, (*i)->level());
1714 Region::is_compound () const
1716 return max_source_level() > 0;
1720 Region::post_set (const PropertyChange& pc)
1722 if (pc.contains (Properties::position)) {
1723 recompute_position_from_lock_style ();
1728 Region::set_start_internal (framecnt_t s)
1734 Region::earliest_possible_position () const
1736 if (_start > _position) {
1739 return _position - _start;
1744 Region::latest_possible_frame () const
1746 framecnt_t minlen = max_framecnt;
1748 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1749 /* non-audio regions have a length that may vary based on their
1750 * position, so we have to pass it in the call.
1752 minlen = min (minlen, (*i)->length (_position));
1755 /* the latest possible last frame is determined by the current
1756 * position, plus the shortest source extent past _start.
1759 return _position + (minlen - _start) - 1;