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"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<framecnt_t> sync_position;
68 PBD::PropertyDescriptor<layer_t> layer;
69 PBD::PropertyDescriptor<framepos_t> ancestral_start;
70 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
71 PBD::PropertyDescriptor<float> stretch;
72 PBD::PropertyDescriptor<float> shift;
73 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
74 PBD::PropertyDescriptor<uint64_t> layering_index;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
91 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
93 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
95 Properties::import.property_id = g_quark_from_static_string (X_("import"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
97 Properties::external.property_id = g_quark_from_static_string (X_("external"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
99 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
101 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
103 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
105 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
107 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
109 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
111 Properties::start.property_id = g_quark_from_static_string (X_("start"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
113 Properties::length.property_id = g_quark_from_static_string (X_("length"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
115 Properties::position.property_id = g_quark_from_static_string (X_("position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
117 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
119 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
121 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
123 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
125 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
127 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
129 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
136 Region::register_properties ()
138 _xml_node_name = X_("Region");
140 add_property (_muted);
141 add_property (_opaque);
142 add_property (_locked);
143 add_property (_video_locked);
144 add_property (_automatic);
145 add_property (_whole_file);
146 add_property (_import);
147 add_property (_external);
148 add_property (_sync_marked);
149 add_property (_left_of_split);
150 add_property (_right_of_split);
151 add_property (_hidden);
152 add_property (_position_locked);
153 add_property (_valid_transients);
154 add_property (_start);
155 add_property (_length);
156 add_property (_position);
157 add_property (_sync_position);
158 add_property (_ancestral_start);
159 add_property (_ancestral_length);
160 add_property (_stretch);
161 add_property (_shift);
162 add_property (_position_lock_style);
163 add_property (_layering_index);
166 #define REGION_DEFAULT_STATE(s,l) \
167 _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _valid_transients (Properties::valid_transients, false) \
171 , _start (Properties::start, (s)) \
172 , _length (Properties::length, (l)) \
173 , _position (Properties::position, 0) \
174 , _sync_position (Properties::sync_position, (s)) \
175 , _transient_user_start (0) \
176 , _transient_analysis_start (0) \
177 , _transient_analysis_end (0) \
178 , _muted (Properties::muted, false) \
179 , _opaque (Properties::opaque, true) \
180 , _locked (Properties::locked, false) \
181 , _video_locked (Properties::video_locked, false) \
182 , _automatic (Properties::automatic, false) \
183 , _whole_file (Properties::whole_file, false) \
184 , _import (Properties::import, false) \
185 , _external (Properties::external, false) \
186 , _hidden (Properties::hidden, false) \
187 , _position_locked (Properties::position_locked, false) \
188 , _ancestral_start (Properties::ancestral_start, (s)) \
189 , _ancestral_length (Properties::ancestral_length, (l)) \
190 , _stretch (Properties::stretch, 1.0) \
191 , _shift (Properties::shift, 1.0) \
192 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
193 , _layering_index (Properties::layering_index, 0)
195 #define REGION_COPY_STATE(other) \
196 _sync_marked (Properties::sync_marked, other->_sync_marked) \
197 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
198 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _user_transients (other->_user_transients) \
205 , _transient_user_start (other->_transient_user_start) \
206 , _transients (other->_transients) \
207 , _transient_analysis_start (other->_transient_analysis_start) \
208 , _transient_analysis_end (other->_transient_analysis_end) \
209 , _muted (Properties::muted, other->_muted) \
210 , _opaque (Properties::opaque, other->_opaque) \
211 , _locked (Properties::locked, other->_locked) \
212 , _video_locked (Properties::video_locked, other->_video_locked) \
213 , _automatic (Properties::automatic, other->_automatic) \
214 , _whole_file (Properties::whole_file, other->_whole_file) \
215 , _import (Properties::import, other->_import) \
216 , _external (Properties::external, other->_external) \
217 , _hidden (Properties::hidden, other->_hidden) \
218 , _position_locked (Properties::position_locked, other->_position_locked) \
219 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
220 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
221 , _stretch (Properties::stretch, other->_stretch) \
222 , _shift (Properties::shift, other->_shift) \
223 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
224 , _layering_index (Properties::layering_index, other->_layering_index)
226 /* derived-from-derived constructor (no sources in constructor) */
227 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
228 : SessionObject(s, name)
230 , REGION_DEFAULT_STATE(start,length)
231 , _last_length (length)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 /* no sources at this point */
241 /** Basic Region constructor (many sources) */
242 Region::Region (const SourceList& srcs)
243 : SessionObject(srcs.front()->session(), "toBeRenamed")
244 , _type (srcs.front()->type())
245 , REGION_DEFAULT_STATE(0,0)
248 , _first_edit (EditChangesNothing)
251 register_properties ();
253 _type = srcs.front()->type();
257 assert(_sources.size() > 0);
258 assert (_type == srcs.front()->type());
261 /** Create a new Region from an existing one */
262 Region::Region (boost::shared_ptr<const Region> other)
263 : SessionObject(other->session(), other->name())
264 , _type (other->data_type())
265 , REGION_COPY_STATE (other)
266 , _last_length (other->_last_length)
267 , _last_position(other->_last_position) \
268 , _first_edit (EditChangesNothing)
269 , _layer (other->_layer)
271 register_properties ();
273 /* override state that may have been incorrectly inherited from the other region
281 use_sources (other->_sources);
282 set_master_sources (other->_master_sources);
284 _position_lock_style = other->_position_lock_style;
285 _first_edit = other->_first_edit;
287 _start = 0; // It seems strange _start is not inherited here?
289 /* sync pos is relative to start of file. our start-in-file is now zero,
290 so set our sync position to whatever the the difference between
291 _start and _sync_pos was in the other region.
293 result is that our new sync pos points to the same point in our source(s)
294 as the sync in the other region did in its source(s).
296 since we start at zero in our source(s), it is not possible to use a sync point that
297 is before the start. reset it to _start if that was true in the other region.
300 if (other->sync_marked()) {
301 if (other->_start < other->_sync_position) {
302 /* sync pos was after the start point of the other region */
303 _sync_position = other->_sync_position - other->_start;
305 /* sync pos was before the start point of the other region. not possible here. */
306 _sync_marked = false;
307 _sync_position = _start;
310 _sync_marked = false;
311 _sync_position = _start;
314 assert (_type == other->data_type());
317 /** Create a new Region from part of an existing one.
319 the start within \a other is given by \a offset
320 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323 : SessionObject(other->session(), other->name())
324 , _type (other->data_type())
325 , REGION_COPY_STATE (other)
326 , _last_length (other->_last_length)
327 , _last_position(other->_last_position) \
328 , _first_edit (EditChangesNothing)
330 , _layer (other->_layer)
332 register_properties ();
334 /* override state that may have been incorrectly inherited from the other region
337 _position = other->_position + offset;
342 use_sources (other->_sources);
343 set_master_sources (other->_master_sources);
345 _start = other->_start + offset;
346 _beat = _session.tempo_map().beat_at_frame (_position);
348 /* if the other region had a distinct sync point
349 set, then continue to use it as best we can.
350 otherwise, reset sync point back to start.
353 if (other->sync_marked()) {
354 if (other->_sync_position < _start) {
355 _sync_marked = false;
356 _sync_position = _start;
358 _sync_position = other->_sync_position;
361 _sync_marked = false;
362 _sync_position = _start;
365 assert (_type == other->data_type());
368 /** Create a copy of @param other but with different sources. Used by filters */
369 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
370 : SessionObject (other->session(), other->name())
371 , _type (srcs.front()->type())
372 , REGION_COPY_STATE (other)
373 , _last_length (other->_last_length)
374 , _last_position (other->_last_position)
375 , _first_edit (EditChangesID)
376 , _layer (other->_layer)
378 register_properties ();
381 _position_locked = false;
383 other->_first_edit = EditChangesName;
385 if (other->_extra_xml) {
386 _extra_xml = new XMLNode (*other->_extra_xml);
392 assert(_sources.size() > 0);
397 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
402 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
404 _playlist = wpl.lock();
408 Region::set_name (const std::string& str)
411 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
412 assert(_name == str);
414 send_change (Properties::name);
421 Region::set_length (framecnt_t len)
423 //cerr << "Region::set_length() len = " << len << endl;
428 if (_length != len && len != 0) {
430 /* check that the current _position wouldn't make the new
434 if (max_framepos - len < _position) {
438 if (!verify_length (len)) {
443 _last_length = _length;
444 set_length_internal (len);
448 maybe_invalidate_transients ();
450 if (!property_changes_suspended()) {
454 send_change (Properties::length);
459 Region::set_length_internal (framecnt_t len)
465 Region::maybe_uncopy ()
467 /* this does nothing but marked a semantic moment once upon a time */
471 Region::first_edit ()
473 boost::shared_ptr<Playlist> pl (playlist());
475 if (_first_edit != EditChangesNothing && pl) {
477 _name = RegionFactory::new_region_name (_name);
478 _first_edit = EditChangesNothing;
480 send_change (Properties::name);
482 RegionFactory::CheckNewRegion (shared_from_this());
487 Region::at_natural_position () const
489 boost::shared_ptr<Playlist> pl (playlist());
495 boost::shared_ptr<Region> whole_file_region = get_parent();
497 if (whole_file_region) {
498 if (_position == whole_file_region->position() + _start) {
507 Region::move_to_natural_position ()
509 boost::shared_ptr<Playlist> pl (playlist());
515 boost::shared_ptr<Region> whole_file_region = get_parent();
517 if (whole_file_region) {
518 set_position (whole_file_region->position() + _start);
523 Region::special_set_position (framepos_t pos)
525 /* this is used when creating a whole file region as
526 a way to store its "natural" or "captured" position.
529 _position = _position;
534 Region::set_position_lock_style (PositionLockStyle ps)
536 if (_position_lock_style != ps) {
538 boost::shared_ptr<Playlist> pl (playlist());
540 _position_lock_style = ps;
542 if (_position_lock_style == MusicTime) {
543 _beat = _session.tempo_map().beat_at_frame (_position);
546 send_change (Properties::position_lock_style);
551 Region::update_after_tempo_map_change (bool send)
553 boost::shared_ptr<Playlist> pl (playlist());
555 if (!pl || _position_lock_style != MusicTime) {
559 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
560 set_position_internal (pos, false);
562 /* do this even if the position is the same. this helps out
563 a GUI that has moved its representation already.
567 send_change (Properties::position);
572 Region::set_position (framepos_t pos)
578 set_position_internal (pos, true);
580 /* do this even if the position is the same. this helps out
581 a GUI that has moved its representation already.
583 PropertyChange p_and_l;
585 p_and_l.add (Properties::position);
586 /* Currently length change due to position change is only implemented
587 for MidiRegion (Region has no length in beats).
588 Notify a length change regardless (its more efficient for MidiRegions),
589 and when Region has a _length_beats we will need it here anyway).
591 if (position_lock_style() == MusicTime) {
592 p_and_l.add (Properties::length);
595 send_change (p_and_l);
599 /** A gui may need to create a region, then place it in an initial
600 * position determined by the user.
601 * When this takes place within one gui operation, we have to reset
602 * _last_position to prevent an implied move.
605 Region::set_initial_position (framepos_t pos)
611 if (_position != pos) {
614 /* check that the new _position wouldn't make the current
615 length impossible - if so, change the length.
617 XXX is this the right thing to do?
620 if (max_framepos - _length < _position) {
621 _last_length = _length;
622 _length = max_framepos - _position;
625 recompute_position_from_lock_style ();
626 /* ensure that this move doesn't cause a range move */
627 _last_position = _position;
631 /* do this even if the position is the same. this helps out
632 a GUI that has moved its representation already.
634 send_change (Properties::position);
638 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
640 /* We emit a change of Properties::position even if the position hasn't changed
641 (see Region::set_position), so we must always set this up so that
642 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
644 _last_position = _position;
646 if (_position != pos) {
649 if (allow_bbt_recompute) {
650 recompute_position_from_lock_style ();
652 /* check that the new _position wouldn't make the current
653 length impossible - if so, change the length.
655 XXX is this the right thing to do?
657 if (max_framepos - _length < _position) {
658 _last_length = _length;
659 _length = max_framepos - _position;
665 Region::recompute_position_from_lock_style ()
667 if (_position_lock_style == MusicTime) {
668 _beat = _session.tempo_map().beat_at_frame (_position);
673 Region::nudge_position (frameoffset_t n)
675 if (locked() || video_locked()) {
683 framepos_t new_position = _position;
686 if (_position > max_framepos - n) {
687 new_position = max_framepos;
692 if (_position < -n) {
699 set_position_internal (new_position, true);
701 send_change (Properties::position);
705 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
707 _ancestral_length = l;
708 _ancestral_start = s;
714 Region::set_start (framepos_t pos)
716 if (locked() || position_locked() || video_locked()) {
719 /* This just sets the start, nothing else. It effectively shifts
720 the contents of the Region within the overall extent of the Source,
721 without changing the Region's position or length
726 if (!verify_start (pos)) {
730 set_start_internal (pos);
733 maybe_invalidate_transients ();
735 send_change (Properties::start);
740 Region::move_start (frameoffset_t distance)
742 if (locked() || position_locked() || video_locked()) {
746 framepos_t new_start;
750 if (_start > max_framepos - distance) {
751 new_start = max_framepos; // makes no sense
753 new_start = _start + distance;
756 if (!verify_start (new_start)) {
760 } else if (distance < 0) {
762 if (_start < -distance) {
765 new_start = _start + distance;
772 if (new_start == _start) {
776 set_start_internal (new_start);
781 send_change (Properties::start);
785 Region::trim_front (framepos_t new_position)
787 modify_front (new_position, false);
791 Region::cut_front (framepos_t new_position)
793 modify_front (new_position, true);
797 Region::cut_end (framepos_t new_endpoint)
799 modify_end (new_endpoint, true);
803 Region::modify_front (framepos_t new_position, bool reset_fade)
809 framepos_t end = last_frame();
810 framepos_t source_zero;
812 if (_position > _start) {
813 source_zero = _position - _start;
815 source_zero = 0; // its actually negative, but this will work for us
818 if (new_position < end) { /* can't trim it zero or negative length */
820 framecnt_t newlen = 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);
830 newlen = _length + (_position - new_position);
833 trim_to_internal (new_position, newlen);
836 _right_of_split = true;
839 if (!property_changes_suspended()) {
840 recompute_at_start ();
843 maybe_invalidate_transients ();
848 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
854 if (new_endpoint > _position) {
855 trim_to_internal (_position, new_endpoint - _position);
857 _left_of_split = true;
859 if (!property_changes_suspended()) {
865 /** @param new_endpoint New region end point, such that, for example,
866 * a region at 0 of length 10 has an endpoint of 9.
870 Region::trim_end (framepos_t new_endpoint)
872 modify_end (new_endpoint, false);
876 Region::trim_to (framepos_t position, framecnt_t length)
882 trim_to_internal (position, length);
884 if (!property_changes_suspended()) {
885 recompute_at_start ();
891 Region::trim_to_internal (framepos_t position, framecnt_t length)
893 framepos_t new_start;
899 frameoffset_t const start_shift = position - _position;
901 if (start_shift > 0) {
903 if (_start > max_framepos - start_shift) {
904 new_start = max_framepos;
906 new_start = _start + start_shift;
909 } else if (start_shift < 0) {
911 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
914 new_start = _start + start_shift;
921 if (!verify_start_and_length (new_start, length)) {
925 PropertyChange what_changed;
927 /* Set position before length, otherwise for MIDI regions this bad thing happens:
928 * 1. we call set_length_internal; length in beats is computed using the region's current
929 * (soon-to-be old) position
930 * 2. we call set_position_internal; position is set and length in frames re-computed using
931 * length in beats from (1) but at the new position, which is wrong if the region
932 * straddles a tempo/meter change.
935 if (_position != position) {
936 if (!property_changes_suspended()) {
937 _last_position = _position;
939 set_position_internal (position, true);
940 what_changed.add (Properties::position);
943 if (_start != new_start) {
944 set_start_internal (new_start);
945 what_changed.add (Properties::start);
948 if (_length != length) {
949 if (!property_changes_suspended()) {
950 _last_length = _length;
952 set_length_internal (length);
953 what_changed.add (Properties::length);
958 PropertyChange start_and_length;
960 start_and_length.add (Properties::start);
961 start_and_length.add (Properties::length);
963 if (what_changed.contains (start_and_length)) {
967 if (!what_changed.empty()) {
968 send_change (what_changed);
973 Region::set_hidden (bool yn)
975 if (hidden() != yn) {
977 send_change (Properties::hidden);
982 Region::set_whole_file (bool yn)
985 /* no change signal */
989 Region::set_automatic (bool yn)
992 /* no change signal */
996 Region::set_muted (bool yn)
1000 send_change (Properties::muted);
1005 Region::set_opaque (bool yn)
1007 if (opaque() != yn) {
1009 send_change (Properties::opaque);
1014 Region::set_locked (bool yn)
1016 if (locked() != yn) {
1018 send_change (Properties::locked);
1023 Region::set_video_locked (bool yn)
1025 if (video_locked() != yn) {
1027 send_change (Properties::video_locked);
1032 Region::set_position_locked (bool yn)
1034 if (position_locked() != yn) {
1035 _position_locked = yn;
1036 send_change (Properties::locked);
1040 /** Set the region's sync point.
1041 * @param absolute_pos Session time.
1044 Region::set_sync_position (framepos_t absolute_pos)
1046 /* position within our file */
1047 framepos_t const file_pos = _start + (absolute_pos - _position);
1049 if (file_pos != _sync_position) {
1050 _sync_marked = true;
1051 _sync_position = file_pos;
1052 if (!property_changes_suspended()) {
1056 send_change (Properties::sync_position);
1061 Region::clear_sync_position ()
1063 if (sync_marked()) {
1064 _sync_marked = false;
1065 if (!property_changes_suspended()) {
1069 send_change (Properties::sync_position);
1073 /* @return the sync point relative the first frame of the region */
1075 Region::sync_offset (int& dir) const
1077 if (sync_marked()) {
1078 if (_sync_position > _start) {
1080 return _sync_position - _start;
1083 return _start - _sync_position;
1092 Region::adjust_to_sync (framepos_t pos) const
1095 frameoffset_t offset = sync_offset (sync_dir);
1097 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1106 if (max_framepos - pos > offset) {
1114 /** @return Sync position in session time */
1116 Region::sync_position() const
1118 if (sync_marked()) {
1119 return _position - _start + _sync_position;
1121 /* if sync has not been marked, use the start of the region */
1129 boost::shared_ptr<Playlist> pl (playlist());
1131 pl->raise_region (shared_from_this ());
1138 boost::shared_ptr<Playlist> pl (playlist());
1140 pl->lower_region (shared_from_this ());
1146 Region::raise_to_top ()
1148 boost::shared_ptr<Playlist> pl (playlist());
1150 pl->raise_region_to_top (shared_from_this());
1155 Region::lower_to_bottom ()
1157 boost::shared_ptr<Playlist> pl (playlist());
1159 pl->lower_region_to_bottom (shared_from_this());
1164 Region::set_layer (layer_t l)
1172 XMLNode *node = new XMLNode ("Region");
1176 const char* fe = NULL;
1178 /* custom version of 'add_properties (*node);'
1179 * skip values that have have dedicated save functions
1180 * in AudioRegion::state()
1182 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1183 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1184 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1185 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1186 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1187 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1188 i->second->get_value (*node);
1191 id().print (buf, sizeof (buf));
1192 node->add_property ("id", buf);
1193 node->add_property ("type", _type.to_string());
1195 switch (_first_edit) {
1196 case EditChangesNothing:
1199 case EditChangesName:
1205 default: /* should be unreachable but makes g++ happy */
1210 node->add_property ("first-edit", fe);
1212 /* note: flags are stored by derived classes */
1214 if (_position_lock_style != AudioTime) {
1215 snprintf (buf, sizeof(buf), "%lf", _beat);
1216 node->add_property ("beat", buf);
1219 for (uint32_t n=0; n < _sources.size(); ++n) {
1220 snprintf (buf2, sizeof(buf2), "source-%d", n);
1221 _sources[n]->id().print (buf, sizeof(buf));
1222 node->add_property (buf2, buf);
1225 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1226 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1227 _master_sources[n]->id().print (buf, sizeof (buf));
1228 node->add_property (buf2, buf);
1231 /* Only store nested sources for the whole-file region that acts
1232 as the parent/root of all regions using it.
1235 if (_whole_file && max_source_level() > 0) {
1237 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1239 /* region is compound - get its playlist and
1240 store that before we list the region that
1244 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1245 nested_node->add_child_nocopy ((*s)->get_state ());
1249 node->add_child_nocopy (*nested_node);
1254 node->add_child_copy (*_extra_xml);
1261 Region::get_state ()
1267 Region::set_state (const XMLNode& node, int version)
1269 PropertyChange what_changed;
1270 return _set_state (node, version, what_changed, true);
1274 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1276 XMLProperty const * prop;
1277 Timecode::BBT_Time bbt_time;
1279 Stateful::save_extra_xml (node);
1281 what_changed = set_values (node);
1285 if (_position_lock_style == MusicTime) {
1286 if ((prop = node.property ("bbt-position")) == 0) {
1287 if ((prop = node.property ("beat")) == 0) {
1288 /* missing BBT info, revert to audio time locking */
1289 _position_lock_style = AudioTime;
1291 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1292 _position_lock_style = AudioTime;
1297 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1300 &bbt_time.ticks) != 3) {
1301 _position_lock_style = AudioTime;
1303 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1308 /* fix problems with old sessions corrupted by impossible
1309 values for _stretch or _shift
1311 if (_stretch == 0.0f) {
1315 if (_shift == 0.0f) {
1320 send_change (what_changed);
1323 /* Quick fix for 2.x sessions when region is muted */
1324 if ((prop = node.property (X_("flags")))) {
1325 if (string::npos != prop->value().find("Muted")){
1330 // saved property is invalid, region-transients are not saved
1331 if (_user_transients.size() == 0){
1332 _valid_transients = false;
1339 Region::suspend_property_changes ()
1341 Stateful::suspend_property_changes ();
1342 _last_length = _length;
1343 _last_position = _position;
1347 Region::mid_thaw (const PropertyChange& what_changed)
1349 if (what_changed.contains (Properties::length)) {
1350 if (what_changed.contains (Properties::position)) {
1351 recompute_at_start ();
1353 recompute_at_end ();
1358 Region::send_change (const PropertyChange& what_changed)
1360 if (what_changed.empty()) {
1364 Stateful::send_change (what_changed);
1366 if (!Stateful::property_changes_suspended()) {
1368 /* Try and send a shared_pointer unless this is part of the constructor.
1373 boost::shared_ptr<Region> rptr = shared_from_this();
1374 RegionPropertyChanged (rptr, what_changed);
1376 /* no shared_ptr available, relax; */
1382 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1384 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1388 Region::equivalent (boost::shared_ptr<const Region> other) const
1390 return _start == other->_start &&
1391 _position == other->_position &&
1392 _length == other->_length;
1396 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1398 return _start == other->_start &&
1399 _length == other->_length;
1403 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1405 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1409 Region::source_deleted (boost::weak_ptr<Source>)
1413 if (!_session.deletion_in_progress()) {
1414 /* this is a very special case: at least one of the region's
1415 sources has bee deleted, so invalidate all references to
1416 ourselves. Do NOT do this during session deletion, because
1417 then we run the risk that this will actually result
1418 in this object being deleted (as refcnt goes to zero)
1419 while emitting DropReferences.
1427 Region::master_source_names ()
1429 SourceList::iterator i;
1431 vector<string> names;
1432 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1433 names.push_back((*i)->name());
1440 Region::set_master_sources (const SourceList& srcs)
1442 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1443 (*i)->dec_use_count ();
1446 _master_sources = srcs;
1447 assert (_sources.size() == _master_sources.size());
1449 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1450 (*i)->inc_use_count ();
1455 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1460 if ((_sources.size() != other->_sources.size()) ||
1461 (_master_sources.size() != other->_master_sources.size())) {
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()) {
1474 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1475 if ((*i)->id() != (*io)->id()) {
1484 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1490 SourceList::const_iterator i;
1491 SourceList::const_iterator io;
1493 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1494 if ((*i)->id() == (*io)->id()) {
1503 Region::source_string () const
1505 //string res = itos(_sources.size());
1508 res << _sources.size() << ":";
1510 SourceList::const_iterator i;
1512 for (i = _sources.begin(); i != _sources.end(); ++i) {
1513 res << (*i)->id() << ":";
1516 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1517 res << (*i)->id() << ":";
1524 Region::uses_source (boost::shared_ptr<const Source> source) const
1526 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1531 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1534 if (ps->playlist()->uses_source (source)) {
1540 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1545 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1548 if (ps->playlist()->uses_source (source)) {
1558 Region::source_length(uint32_t n) const
1560 assert (n < _sources.size());
1561 return _sources[n]->length (_position - _start);
1565 Region::verify_length (framecnt_t& len)
1567 if (source() && (source()->destructive() || source()->length_mutable())) {
1571 framecnt_t maxlen = 0;
1573 for (uint32_t n = 0; n < _sources.size(); ++n) {
1574 maxlen = max (maxlen, source_length(n) - _start);
1577 len = min (len, maxlen);
1583 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1585 if (source() && (source()->destructive() || source()->length_mutable())) {
1589 framecnt_t maxlen = 0;
1591 for (uint32_t n = 0; n < _sources.size(); ++n) {
1592 maxlen = max (maxlen, source_length(n) - new_start);
1595 new_length = min (new_length, maxlen);
1601 Region::verify_start (framepos_t pos)
1603 if (source() && (source()->destructive() || source()->length_mutable())) {
1607 for (uint32_t n = 0; n < _sources.size(); ++n) {
1608 if (pos > source_length(n) - _length) {
1616 Region::verify_start_mutable (framepos_t& new_start)
1618 if (source() && (source()->destructive() || source()->length_mutable())) {
1622 for (uint32_t n = 0; n < _sources.size(); ++n) {
1623 if (new_start > source_length(n) - _length) {
1624 new_start = source_length(n) - _length;
1630 boost::shared_ptr<Region>
1631 Region::get_parent() const
1633 boost::shared_ptr<Playlist> pl (playlist());
1636 boost::shared_ptr<Region> r;
1637 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1639 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1640 return boost::static_pointer_cast<Region> (r);
1644 return boost::shared_ptr<Region>();
1648 Region::apply (Filter& filter, Progress* progress)
1650 return filter.run (shared_from_this(), progress);
1655 Region::maybe_invalidate_transients ()
1657 bool changed = !_onsets.empty();
1660 if (_valid_transients || changed) {
1661 send_change (PropertyChange (Properties::valid_transients));
1667 Region::transients (AnalysisFeatureList& afl)
1669 int cnt = afl.empty() ? 0 : 1;
1671 Region::merge_features (afl, _onsets, _position);
1672 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1673 if (!_onsets.empty ()) {
1676 if (!_user_transients.empty ()) {
1681 // remove exact duplicates
1682 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1687 Region::has_transients () const
1689 if (!_user_transients.empty ()) {
1690 assert (_valid_transients);
1693 if (!_onsets.empty ()) {
1700 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1702 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1703 const frameoffset_t p = (*x) + off;
1704 if (p < first_frame() || p > last_frame()) {
1707 result.push_back (p);
1712 Region::drop_sources ()
1714 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1715 (*i)->dec_use_count ();
1720 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1721 (*i)->dec_use_count ();
1724 _master_sources.clear ();
1728 Region::use_sources (SourceList const & s)
1730 set<boost::shared_ptr<Source> > unique_srcs;
1732 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1734 _sources.push_back (*i);
1735 (*i)->inc_use_count ();
1736 _master_sources.push_back (*i);
1737 (*i)->inc_use_count ();
1739 /* connect only once to DropReferences, even if sources are replicated
1742 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1743 unique_srcs.insert (*i);
1744 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1750 Region::can_trim () const
1752 CanTrim ct = CanTrim (0);
1758 /* if not locked, we can always move the front later, and the end earlier
1761 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1763 if (start() != 0 || can_trim_start_before_source_start ()) {
1764 ct = CanTrim (ct | FrontTrimEarlier);
1767 if (!_sources.empty()) {
1768 if ((start() + length()) < _sources.front()->length (0)) {
1769 ct = CanTrim (ct | EndTrimLater);
1777 Region::max_source_level () const
1781 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1782 lvl = max (lvl, (*i)->level());
1789 Region::is_compound () const
1791 return max_source_level() > 0;
1795 Region::post_set (const PropertyChange& pc)
1797 if (pc.contains (Properties::position)) {
1798 recompute_position_from_lock_style ();
1803 Region::set_start_internal (framecnt_t s)
1809 Region::earliest_possible_position () const
1811 if (_start > _position) {
1814 return _position - _start;
1819 Region::latest_possible_frame () const
1821 framecnt_t minlen = max_framecnt;
1823 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1824 /* non-audio regions have a length that may vary based on their
1825 * position, so we have to pass it in the call.
1827 minlen = min (minlen, (*i)->length (_position));
1830 /* the latest possible last frame is determined by the current
1831 * position, plus the shortest source extent past _start.
1834 return _position + (minlen - _start) - 1;