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
276 _position = other->_position;
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 = other->_start;
288 _beat = other->_beat;
290 /* sync pos is relative to start of file. our start-in-file is now zero,
291 so set our sync position to whatever the the difference between
292 _start and _sync_pos was in the other region.
294 result is that our new sync pos points to the same point in our source(s)
295 as the sync in the other region did in its source(s).
297 since we start at zero in our source(s), it is not possible to use a sync point that
298 is before the start. reset it to _start if that was true in the other region.
301 if (other->sync_marked()) {
302 if (other->_start < other->_sync_position) {
303 /* sync pos was after the start point of the other region */
304 _sync_position = other->_sync_position - other->_start;
306 /* sync pos was before the start point of the other region. not possible here. */
307 _sync_marked = false;
308 _sync_position = _start;
311 _sync_marked = false;
312 _sync_position = _start;
315 assert (_type == other->data_type());
318 /** Create a new Region from part of an existing one.
320 the start within \a other is given by \a offset
321 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
323 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
324 : SessionObject(other->session(), other->name())
325 , _type (other->data_type())
326 , REGION_COPY_STATE (other)
327 , _last_length (other->_last_length)
328 , _last_position(other->_last_position) \
329 , _first_edit (EditChangesNothing)
331 , _layer (other->_layer)
333 register_properties ();
335 /* override state that may have been incorrectly inherited from the other region
338 _position = other->_position + offset;
343 use_sources (other->_sources);
344 set_master_sources (other->_master_sources);
346 _start = other->_start + offset;
347 _beat = _session.tempo_map().beat_at_frame (_position);
349 /* if the other region had a distinct sync point
350 set, then continue to use it as best we can.
351 otherwise, reset sync point back to start.
354 if (other->sync_marked()) {
355 if (other->_sync_position < _start) {
356 _sync_marked = false;
357 _sync_position = _start;
359 _sync_position = other->_sync_position;
362 _sync_marked = false;
363 _sync_position = _start;
366 assert (_type == other->data_type());
369 /** Create a copy of @param other but with different sources. Used by filters */
370 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
371 : SessionObject (other->session(), other->name())
372 , _type (srcs.front()->type())
373 , REGION_COPY_STATE (other)
374 , _last_length (other->_last_length)
375 , _last_position (other->_last_position)
376 , _first_edit (EditChangesID)
377 , _layer (other->_layer)
379 register_properties ();
382 _position_locked = false;
384 other->_first_edit = EditChangesName;
386 if (other->_extra_xml) {
387 _extra_xml = new XMLNode (*other->_extra_xml);
393 assert(_sources.size() > 0);
398 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
403 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
405 _playlist = wpl.lock();
409 Region::set_name (const std::string& str)
412 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
413 assert(_name == str);
415 send_change (Properties::name);
422 Region::set_length (framecnt_t len)
424 //cerr << "Region::set_length() len = " << len << endl;
429 if (_length != len && len != 0) {
431 /* check that the current _position wouldn't make the new
435 if (max_framepos - len < _position) {
439 if (!verify_length (len)) {
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)
461 _last_length = _length;
466 Region::maybe_uncopy ()
468 /* this does nothing but marked a semantic moment once upon a time */
472 Region::first_edit ()
474 boost::shared_ptr<Playlist> pl (playlist());
476 if (_first_edit != EditChangesNothing && pl) {
478 _name = RegionFactory::new_region_name (_name);
479 _first_edit = EditChangesNothing;
481 send_change (Properties::name);
483 RegionFactory::CheckNewRegion (shared_from_this());
488 Region::at_natural_position () const
490 boost::shared_ptr<Playlist> pl (playlist());
496 boost::shared_ptr<Region> whole_file_region = get_parent();
498 if (whole_file_region) {
499 if (_position == whole_file_region->position() + _start) {
508 Region::move_to_natural_position ()
510 boost::shared_ptr<Playlist> pl (playlist());
516 boost::shared_ptr<Region> whole_file_region = get_parent();
518 if (whole_file_region) {
519 set_position (whole_file_region->position() + _start);
524 Region::special_set_position (framepos_t pos)
526 /* this is used when creating a whole file region as
527 a way to store its "natural" or "captured" position.
530 _position = _position;
535 Region::set_position_lock_style (PositionLockStyle ps)
537 if (_position_lock_style != ps) {
539 boost::shared_ptr<Playlist> pl (playlist());
541 _position_lock_style = ps;
543 if (_position_lock_style == MusicTime) {
544 _beat = _session.tempo_map().beat_at_frame (_position);
547 send_change (Properties::position_lock_style);
552 Region::update_after_tempo_map_change (bool send)
554 boost::shared_ptr<Playlist> pl (playlist());
556 if (!pl || _position_lock_style != MusicTime) {
560 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
561 set_position_internal (pos, false);
563 /* do this even if the position is the same. this helps out
564 a GUI that has moved its representation already.
568 send_change (Properties::position);
573 Region::set_position (framepos_t pos)
579 set_position_internal (pos, true);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
584 PropertyChange p_and_l;
586 p_and_l.add (Properties::position);
587 /* Currently length change due to position change is only implemented
588 for MidiRegion (Region has no length in beats).
589 Notify a length change regardless (its more efficient for MidiRegions),
590 and when Region has a _length_beats we will need it here anyway).
592 if (position_lock_style() == MusicTime) {
593 p_and_l.add (Properties::length);
596 send_change (p_and_l);
600 /** A gui may need to create a region, then place it in an initial
601 * position determined by the user.
602 * When this takes place within one gui operation, we have to reset
603 * _last_position to prevent an implied move.
606 Region::set_initial_position (framepos_t pos)
612 if (_position != pos) {
615 /* check that the new _position wouldn't make the current
616 length impossible - if so, change the length.
618 XXX is this the right thing to do?
621 if (max_framepos - _length < _position) {
622 _last_length = _length;
623 _length = max_framepos - _position;
626 recompute_position_from_lock_style ();
627 /* ensure that this move doesn't cause a range move */
628 _last_position = _position;
632 /* do this even if the position is the same. this helps out
633 a GUI that has moved its representation already.
635 send_change (Properties::position);
639 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
641 /* We emit a change of Properties::position even if the position hasn't changed
642 (see Region::set_position), so we must always set this up so that
643 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
645 _last_position = _position;
647 if (_position != pos) {
650 if (allow_bbt_recompute) {
651 recompute_position_from_lock_style ();
653 /* check that the new _position wouldn't make the current
654 length impossible - if so, change the length.
656 XXX is this the right thing to do?
658 if (max_framepos - _length < _position) {
659 _last_length = _length;
660 _length = max_framepos - _position;
666 Region::recompute_position_from_lock_style ()
668 if (_position_lock_style == MusicTime) {
669 _beat = _session.tempo_map().beat_at_frame (_position);
674 Region::nudge_position (frameoffset_t n)
676 if (locked() || video_locked()) {
684 framepos_t new_position = _position;
687 if (_position > max_framepos - n) {
688 new_position = max_framepos;
693 if (_position < -n) {
700 set_position_internal (new_position, true);
702 send_change (Properties::position);
706 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
708 _ancestral_length = l;
709 _ancestral_start = s;
715 Region::set_start (framepos_t pos)
717 if (locked() || position_locked() || video_locked()) {
720 /* This just sets the start, nothing else. It effectively shifts
721 the contents of the Region within the overall extent of the Source,
722 without changing the Region's position or length
727 if (!verify_start (pos)) {
731 set_start_internal (pos);
734 maybe_invalidate_transients ();
736 send_change (Properties::start);
741 Region::move_start (frameoffset_t distance)
743 if (locked() || position_locked() || video_locked()) {
747 framepos_t new_start;
751 if (_start > max_framepos - distance) {
752 new_start = max_framepos; // makes no sense
754 new_start = _start + distance;
757 if (!verify_start (new_start)) {
761 } else if (distance < 0) {
763 if (_start < -distance) {
766 new_start = _start + distance;
773 if (new_start == _start) {
777 set_start_internal (new_start);
782 send_change (Properties::start);
786 Region::trim_front (framepos_t new_position)
788 modify_front (new_position, false);
792 Region::cut_front (framepos_t new_position)
794 modify_front (new_position, true);
798 Region::cut_end (framepos_t new_endpoint)
800 modify_end (new_endpoint, true);
804 Region::modify_front (framepos_t new_position, bool reset_fade)
810 framepos_t end = last_frame();
811 framepos_t source_zero;
813 if (_position > _start) {
814 source_zero = _position - _start;
816 source_zero = 0; // its actually negative, but this will work for us
819 if (new_position < end) { /* can't trim it zero or negative length */
821 framecnt_t newlen = 0;
823 if (!can_trim_start_before_source_start ()) {
824 /* can't trim it back past where source position zero is located */
825 new_position = max (new_position, source_zero);
828 if (new_position > _position) {
829 newlen = _length - (new_position - _position);
831 newlen = _length + (_position - new_position);
834 trim_to_internal (new_position, newlen);
837 _right_of_split = true;
840 if (!property_changes_suspended()) {
841 recompute_at_start ();
844 maybe_invalidate_transients ();
849 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
855 if (new_endpoint > _position) {
856 trim_to_internal (_position, new_endpoint - _position);
858 _left_of_split = true;
860 if (!property_changes_suspended()) {
866 /** @param new_endpoint New region end point, such that, for example,
867 * a region at 0 of length 10 has an endpoint of 9.
871 Region::trim_end (framepos_t new_endpoint)
873 modify_end (new_endpoint, false);
877 Region::trim_to (framepos_t position, framecnt_t length)
883 trim_to_internal (position, length);
885 if (!property_changes_suspended()) {
886 recompute_at_start ();
892 Region::trim_to_internal (framepos_t position, framecnt_t length)
894 framepos_t new_start;
900 frameoffset_t const start_shift = position - _position;
902 if (start_shift > 0) {
904 if (_start > max_framepos - start_shift) {
905 new_start = max_framepos;
907 new_start = _start + start_shift;
910 } else if (start_shift < 0) {
912 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
915 new_start = _start + start_shift;
922 if (!verify_start_and_length (new_start, length)) {
926 PropertyChange what_changed;
928 if (_start != new_start) {
929 set_start_internal (new_start);
930 what_changed.add (Properties::start);
934 /* Set position before length, otherwise for MIDI regions this bad thing happens:
935 * 1. we call set_length_internal; length in beats is computed using the region's current
936 * (soon-to-be old) position
937 * 2. we call set_position_internal; position is set and length in frames re-computed using
938 * length in beats from (1) but at the new position, which is wrong if the region
939 * straddles a tempo/meter change.
942 if (_position != position) {
943 if (!property_changes_suspended()) {
944 _last_position = _position;
946 set_position_internal (position, true);
947 what_changed.add (Properties::position);
950 if (_length != length) {
951 if (!property_changes_suspended()) {
952 _last_length = _length;
954 set_length_internal (length);
955 what_changed.add (Properties::length);
960 PropertyChange start_and_length;
962 start_and_length.add (Properties::start);
963 start_and_length.add (Properties::length);
965 if (what_changed.contains (start_and_length)) {
969 if (!what_changed.empty()) {
970 send_change (what_changed);
975 Region::set_hidden (bool yn)
977 if (hidden() != yn) {
979 send_change (Properties::hidden);
984 Region::set_whole_file (bool yn)
987 /* no change signal */
991 Region::set_automatic (bool yn)
994 /* no change signal */
998 Region::set_muted (bool yn)
1000 if (muted() != yn) {
1002 send_change (Properties::muted);
1007 Region::set_opaque (bool yn)
1009 if (opaque() != yn) {
1011 send_change (Properties::opaque);
1016 Region::set_locked (bool yn)
1018 if (locked() != yn) {
1020 send_change (Properties::locked);
1025 Region::set_video_locked (bool yn)
1027 if (video_locked() != yn) {
1029 send_change (Properties::video_locked);
1034 Region::set_position_locked (bool yn)
1036 if (position_locked() != yn) {
1037 _position_locked = yn;
1038 send_change (Properties::locked);
1042 /** Set the region's sync point.
1043 * @param absolute_pos Session time.
1046 Region::set_sync_position (framepos_t absolute_pos)
1048 /* position within our file */
1049 framepos_t const file_pos = _start + (absolute_pos - _position);
1051 if (file_pos != _sync_position) {
1052 _sync_marked = true;
1053 _sync_position = file_pos;
1054 if (!property_changes_suspended()) {
1058 send_change (Properties::sync_position);
1063 Region::clear_sync_position ()
1065 if (sync_marked()) {
1066 _sync_marked = false;
1067 if (!property_changes_suspended()) {
1071 send_change (Properties::sync_position);
1075 /* @return the sync point relative the first frame of the region */
1077 Region::sync_offset (int& dir) const
1079 if (sync_marked()) {
1080 if (_sync_position > _start) {
1082 return _sync_position - _start;
1085 return _start - _sync_position;
1094 Region::adjust_to_sync (framepos_t pos) const
1097 frameoffset_t offset = sync_offset (sync_dir);
1099 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1108 if (max_framepos - pos > offset) {
1116 /** @return Sync position in session time */
1118 Region::sync_position() const
1120 if (sync_marked()) {
1121 return _position - _start + _sync_position;
1123 /* if sync has not been marked, use the start of the region */
1131 boost::shared_ptr<Playlist> pl (playlist());
1133 pl->raise_region (shared_from_this ());
1140 boost::shared_ptr<Playlist> pl (playlist());
1142 pl->lower_region (shared_from_this ());
1148 Region::raise_to_top ()
1150 boost::shared_ptr<Playlist> pl (playlist());
1152 pl->raise_region_to_top (shared_from_this());
1157 Region::lower_to_bottom ()
1159 boost::shared_ptr<Playlist> pl (playlist());
1161 pl->lower_region_to_bottom (shared_from_this());
1166 Region::set_layer (layer_t l)
1174 XMLNode *node = new XMLNode ("Region");
1178 const char* fe = NULL;
1180 /* custom version of 'add_properties (*node);'
1181 * skip values that have have dedicated save functions
1182 * in AudioRegion::state()
1184 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1185 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1186 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1187 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1188 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1189 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1190 i->second->get_value (*node);
1193 id().print (buf, sizeof (buf));
1194 node->add_property ("id", buf);
1195 node->add_property ("type", _type.to_string());
1197 switch (_first_edit) {
1198 case EditChangesNothing:
1201 case EditChangesName:
1207 default: /* should be unreachable but makes g++ happy */
1212 node->add_property ("first-edit", fe);
1214 /* note: flags are stored by derived classes */
1216 if (_position_lock_style != AudioTime) {
1217 snprintf (buf, sizeof(buf), "%lf", _beat);
1218 node->add_property ("beat", buf);
1221 for (uint32_t n=0; n < _sources.size(); ++n) {
1222 snprintf (buf2, sizeof(buf2), "source-%d", n);
1223 _sources[n]->id().print (buf, sizeof(buf));
1224 node->add_property (buf2, buf);
1227 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1228 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1229 _master_sources[n]->id().print (buf, sizeof (buf));
1230 node->add_property (buf2, buf);
1233 /* Only store nested sources for the whole-file region that acts
1234 as the parent/root of all regions using it.
1237 if (_whole_file && max_source_level() > 0) {
1239 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1241 /* region is compound - get its playlist and
1242 store that before we list the region that
1246 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1247 nested_node->add_child_nocopy ((*s)->get_state ());
1251 node->add_child_nocopy (*nested_node);
1256 node->add_child_copy (*_extra_xml);
1263 Region::get_state ()
1269 Region::set_state (const XMLNode& node, int version)
1271 PropertyChange what_changed;
1272 return _set_state (node, version, what_changed, true);
1276 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1278 XMLProperty const * prop;
1279 Timecode::BBT_Time bbt_time;
1281 Stateful::save_extra_xml (node);
1283 what_changed = set_values (node);
1287 if (_position_lock_style == MusicTime) {
1288 if ((prop = node.property ("bbt-position")) == 0) {
1289 if ((prop = node.property ("beat")) == 0) {
1290 /* missing BBT info, revert to audio time locking */
1291 _position_lock_style = AudioTime;
1293 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1294 _position_lock_style = AudioTime;
1299 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1302 &bbt_time.ticks) != 3) {
1303 _position_lock_style = AudioTime;
1305 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1310 /* fix problems with old sessions corrupted by impossible
1311 values for _stretch or _shift
1313 if (_stretch == 0.0f) {
1317 if (_shift == 0.0f) {
1322 send_change (what_changed);
1325 /* Quick fix for 2.x sessions when region is muted */
1326 if ((prop = node.property (X_("flags")))) {
1327 if (string::npos != prop->value().find("Muted")){
1332 // saved property is invalid, region-transients are not saved
1333 if (_user_transients.size() == 0){
1334 _valid_transients = false;
1341 Region::suspend_property_changes ()
1343 Stateful::suspend_property_changes ();
1344 _last_length = _length;
1345 _last_position = _position;
1349 Region::mid_thaw (const PropertyChange& what_changed)
1351 if (what_changed.contains (Properties::length)) {
1352 if (what_changed.contains (Properties::position)) {
1353 recompute_at_start ();
1355 recompute_at_end ();
1360 Region::send_change (const PropertyChange& what_changed)
1362 if (what_changed.empty()) {
1366 Stateful::send_change (what_changed);
1368 if (!Stateful::property_changes_suspended()) {
1370 /* Try and send a shared_pointer unless this is part of the constructor.
1375 boost::shared_ptr<Region> rptr = shared_from_this();
1376 RegionPropertyChanged (rptr, what_changed);
1378 /* no shared_ptr available, relax; */
1384 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1386 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1390 Region::equivalent (boost::shared_ptr<const Region> other) const
1392 return _start == other->_start &&
1393 _position == other->_position &&
1394 _length == other->_length;
1398 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1400 return _start == other->_start &&
1401 _length == other->_length;
1405 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1407 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1411 Region::source_deleted (boost::weak_ptr<Source>)
1415 if (!_session.deletion_in_progress()) {
1416 /* this is a very special case: at least one of the region's
1417 sources has bee deleted, so invalidate all references to
1418 ourselves. Do NOT do this during session deletion, because
1419 then we run the risk that this will actually result
1420 in this object being deleted (as refcnt goes to zero)
1421 while emitting DropReferences.
1429 Region::master_source_names ()
1431 SourceList::iterator i;
1433 vector<string> names;
1434 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1435 names.push_back((*i)->name());
1442 Region::set_master_sources (const SourceList& srcs)
1444 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1445 (*i)->dec_use_count ();
1448 _master_sources = srcs;
1449 assert (_sources.size() == _master_sources.size());
1451 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1452 (*i)->inc_use_count ();
1457 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1462 if ((_sources.size() != other->_sources.size()) ||
1463 (_master_sources.size() != other->_master_sources.size())) {
1467 SourceList::const_iterator i;
1468 SourceList::const_iterator io;
1470 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1471 if ((*i)->id() != (*io)->id()) {
1476 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1477 if ((*i)->id() != (*io)->id()) {
1486 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1492 SourceList::const_iterator i;
1493 SourceList::const_iterator io;
1495 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1496 if ((*i)->id() == (*io)->id()) {
1505 Region::source_string () const
1507 //string res = itos(_sources.size());
1510 res << _sources.size() << ":";
1512 SourceList::const_iterator i;
1514 for (i = _sources.begin(); i != _sources.end(); ++i) {
1515 res << (*i)->id() << ":";
1518 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1519 res << (*i)->id() << ":";
1526 Region::uses_source (boost::shared_ptr<const Source> source) const
1528 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1533 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1536 if (ps->playlist()->uses_source (source)) {
1542 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1547 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1550 if (ps->playlist()->uses_source (source)) {
1560 Region::source_length(uint32_t n) const
1562 assert (n < _sources.size());
1563 return _sources[n]->length (_position - _start);
1567 Region::verify_length (framecnt_t& len)
1569 if (source() && (source()->destructive() || source()->length_mutable())) {
1573 framecnt_t maxlen = 0;
1575 for (uint32_t n = 0; n < _sources.size(); ++n) {
1576 maxlen = max (maxlen, source_length(n) - _start);
1579 len = min (len, maxlen);
1585 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1587 if (source() && (source()->destructive() || source()->length_mutable())) {
1591 framecnt_t maxlen = 0;
1593 for (uint32_t n = 0; n < _sources.size(); ++n) {
1594 maxlen = max (maxlen, source_length(n) - new_start);
1597 new_length = min (new_length, maxlen);
1603 Region::verify_start (framepos_t pos)
1605 if (source() && (source()->destructive() || source()->length_mutable())) {
1609 for (uint32_t n = 0; n < _sources.size(); ++n) {
1610 if (pos > source_length(n) - _length) {
1618 Region::verify_start_mutable (framepos_t& new_start)
1620 if (source() && (source()->destructive() || source()->length_mutable())) {
1624 for (uint32_t n = 0; n < _sources.size(); ++n) {
1625 if (new_start > source_length(n) - _length) {
1626 new_start = source_length(n) - _length;
1632 boost::shared_ptr<Region>
1633 Region::get_parent() const
1635 boost::shared_ptr<Playlist> pl (playlist());
1638 boost::shared_ptr<Region> r;
1639 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1641 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1642 return boost::static_pointer_cast<Region> (r);
1646 return boost::shared_ptr<Region>();
1650 Region::apply (Filter& filter, Progress* progress)
1652 return filter.run (shared_from_this(), progress);
1657 Region::maybe_invalidate_transients ()
1659 bool changed = !_onsets.empty();
1662 if (_valid_transients || changed) {
1663 send_change (PropertyChange (Properties::valid_transients));
1669 Region::transients (AnalysisFeatureList& afl)
1671 int cnt = afl.empty() ? 0 : 1;
1673 Region::merge_features (afl, _onsets, _position);
1674 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1675 if (!_onsets.empty ()) {
1678 if (!_user_transients.empty ()) {
1683 // remove exact duplicates
1684 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1689 Region::has_transients () const
1691 if (!_user_transients.empty ()) {
1692 assert (_valid_transients);
1695 if (!_onsets.empty ()) {
1702 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1704 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1705 const frameoffset_t p = (*x) + off;
1706 if (p < first_frame() || p > last_frame()) {
1709 result.push_back (p);
1714 Region::drop_sources ()
1716 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1717 (*i)->dec_use_count ();
1722 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1723 (*i)->dec_use_count ();
1726 _master_sources.clear ();
1730 Region::use_sources (SourceList const & s)
1732 set<boost::shared_ptr<Source> > unique_srcs;
1734 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1736 _sources.push_back (*i);
1737 (*i)->inc_use_count ();
1738 _master_sources.push_back (*i);
1739 (*i)->inc_use_count ();
1741 /* connect only once to DropReferences, even if sources are replicated
1744 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1745 unique_srcs.insert (*i);
1746 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1752 Region::can_trim () const
1754 CanTrim ct = CanTrim (0);
1760 /* if not locked, we can always move the front later, and the end earlier
1763 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1765 if (start() != 0 || can_trim_start_before_source_start ()) {
1766 ct = CanTrim (ct | FrontTrimEarlier);
1769 if (!_sources.empty()) {
1770 if ((start() + length()) < _sources.front()->length (0)) {
1771 ct = CanTrim (ct | EndTrimLater);
1779 Region::max_source_level () const
1783 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1784 lvl = max (lvl, (*i)->level());
1791 Region::is_compound () const
1793 return max_source_level() > 0;
1797 Region::post_set (const PropertyChange& pc)
1799 if (pc.contains (Properties::position)) {
1800 recompute_position_from_lock_style ();
1805 Region::set_start_internal (framecnt_t s)
1811 Region::earliest_possible_position () const
1813 if (_start > _position) {
1816 return _position - _start;
1821 Region::latest_possible_frame () const
1823 framecnt_t minlen = max_framecnt;
1825 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1826 /* non-audio regions have a length that may vary based on their
1827 * position, so we have to pass it in the call.
1829 minlen = min (minlen, (*i)->length (_position));
1832 /* the latest possible last frame is determined by the current
1833 * position, plus the shortest source extent past _start.
1836 return _position + (minlen - _start) - 1;