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)
329 , _layer (other->_layer)
331 register_properties ();
333 /* override state that may have been incorrectly inherited from the other region
341 use_sources (other->_sources);
342 set_master_sources (other->_master_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 assert (_type == other->data_type());
366 /** Create a copy of @param other but with different sources. Used by filters */
367 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
368 : SessionObject (other->session(), other->name())
369 , _type (srcs.front()->type())
370 , REGION_COPY_STATE (other)
371 , _last_length (other->_last_length)
372 , _last_position (other->_last_position)
373 , _first_edit (EditChangesID)
374 , _layer (other->_layer)
376 register_properties ();
379 _position_locked = false;
381 other->_first_edit = EditChangesName;
383 if (other->_extra_xml) {
384 _extra_xml = new XMLNode (*other->_extra_xml);
390 assert(_sources.size() > 0);
395 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
400 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
402 _playlist = wpl.lock();
406 Region::set_name (const std::string& str)
409 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
410 assert(_name == str);
412 send_change (Properties::name);
419 Region::set_length (framecnt_t len)
421 //cerr << "Region::set_length() len = " << len << endl;
426 if (_length != len && len != 0) {
428 /* check that the current _position wouldn't make the new
432 if (max_framepos - len < _position) {
436 if (!verify_length (len)) {
441 _last_length = _length;
442 set_length_internal (len);
446 maybe_invalidate_transients ();
448 if (!property_changes_suspended()) {
452 send_change (Properties::length);
457 Region::set_length_internal (framecnt_t len)
463 Region::maybe_uncopy ()
465 /* this does nothing but marked a semantic moment once upon a time */
469 Region::first_edit ()
471 boost::shared_ptr<Playlist> pl (playlist());
473 if (_first_edit != EditChangesNothing && pl) {
475 _name = RegionFactory::new_region_name (_name);
476 _first_edit = EditChangesNothing;
478 send_change (Properties::name);
480 RegionFactory::CheckNewRegion (shared_from_this());
485 Region::at_natural_position () const
487 boost::shared_ptr<Playlist> pl (playlist());
493 boost::shared_ptr<Region> whole_file_region = get_parent();
495 if (whole_file_region) {
496 if (_position == whole_file_region->position() + _start) {
505 Region::move_to_natural_position ()
507 boost::shared_ptr<Playlist> pl (playlist());
513 boost::shared_ptr<Region> whole_file_region = get_parent();
515 if (whole_file_region) {
516 set_position (whole_file_region->position() + _start);
521 Region::special_set_position (framepos_t pos)
523 /* this is used when creating a whole file region as
524 a way to store its "natural" or "captured" position.
527 _position = _position;
532 Region::set_position_lock_style (PositionLockStyle ps)
534 if (_position_lock_style != ps) {
536 boost::shared_ptr<Playlist> pl (playlist());
538 _position_lock_style = ps;
540 if (_position_lock_style == MusicTime) {
541 _beat = _session.tempo_map().beat_at_frame (_position);
544 send_change (Properties::position_lock_style);
549 Region::update_after_tempo_map_change (bool send)
551 boost::shared_ptr<Playlist> pl (playlist());
553 if (!pl || _position_lock_style != MusicTime) {
557 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
558 set_position_internal (pos, false);
560 /* do this even if the position is the same. this helps out
561 a GUI that has moved its representation already.
565 send_change (Properties::position);
570 Region::set_position (framepos_t pos)
576 set_position_internal (pos, true);
578 /* do this even if the position is the same. this helps out
579 a GUI that has moved its representation already.
581 PropertyChange p_and_l;
583 p_and_l.add (Properties::position);
584 /* Currently length change due to position change is only implemented
585 for MidiRegion (Region has no length in beats).
586 Notify a length change regardless (its more efficient for MidiRegions),
587 and when Region has a _length_beats we will need it here anyway).
589 if (position_lock_style() == MusicTime) {
590 p_and_l.add (Properties::length);
593 send_change (p_and_l);
597 /** A gui may need to create a region, then place it in an initial
598 * position determined by the user.
599 * When this takes place within one gui operation, we have to reset
600 * _last_position to prevent an implied move.
603 Region::set_initial_position (framepos_t pos)
609 if (_position != pos) {
612 /* check that the new _position wouldn't make the current
613 length impossible - if so, change the length.
615 XXX is this the right thing to do?
618 if (max_framepos - _length < _position) {
619 _last_length = _length;
620 _length = max_framepos - _position;
623 recompute_position_from_lock_style ();
624 /* ensure that this move doesn't cause a range move */
625 _last_position = _position;
629 /* do this even if the position is the same. this helps out
630 a GUI that has moved its representation already.
632 send_change (Properties::position);
636 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
638 /* We emit a change of Properties::position even if the position hasn't changed
639 (see Region::set_position), so we must always set this up so that
640 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
642 _last_position = _position;
644 if (_position != pos) {
647 /* check that the new _position wouldn't make the current
648 length impossible - if so, change the length.
650 XXX is this the right thing to do?
653 if (max_framepos - _length < _position) {
654 _last_length = _length;
655 _length = max_framepos - _position;
658 if (allow_bbt_recompute) {
659 recompute_position_from_lock_style ();
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 if (_start != new_start) {
928 set_start_internal (new_start);
929 what_changed.add (Properties::start);
932 /* Set position before length, otherwise for MIDI regions this bad thing happens:
933 * 1. we call set_length_internal; length in beats is computed using the region's current
934 * (soon-to-be old) position
935 * 2. we call set_position_internal; position is set and length in frames re-computed using
936 * length in beats from (1) but at the new position, which is wrong if the region
937 * straddles a tempo/meter change.
940 if (_position != position) {
941 if (!property_changes_suspended()) {
942 _last_position = _position;
944 set_position_internal (position, true);
945 what_changed.add (Properties::position);
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;