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::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1528 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1530 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1533 if (sources.find (ps) == sources.end()) {
1534 /* (Playlist)Source not currently in
1535 accumulating set, so recurse.
1537 ps->playlist()->deep_sources (sources);
1541 /* add this source */
1542 sources.insert (*i);
1545 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 (sources.find (ps) == sources.end()) {
1551 /* (Playlist)Source not currently in
1552 accumulating set, so recurse.
1554 ps->playlist()->deep_sources (sources);
1558 /* add this source */
1559 sources.insert (*i);
1564 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1566 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1572 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1575 if (ps->playlist()->uses_source (source)) {
1582 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1588 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1591 if (ps->playlist()->uses_source (source)) {
1603 Region::source_length(uint32_t n) const
1605 assert (n < _sources.size());
1606 return _sources[n]->length (_position - _start);
1610 Region::verify_length (framecnt_t& len)
1612 if (source() && (source()->destructive() || source()->length_mutable())) {
1616 framecnt_t maxlen = 0;
1618 for (uint32_t n = 0; n < _sources.size(); ++n) {
1619 maxlen = max (maxlen, source_length(n) - _start);
1622 len = min (len, maxlen);
1628 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1630 if (source() && (source()->destructive() || source()->length_mutable())) {
1634 framecnt_t maxlen = 0;
1636 for (uint32_t n = 0; n < _sources.size(); ++n) {
1637 maxlen = max (maxlen, source_length(n) - new_start);
1640 new_length = min (new_length, maxlen);
1646 Region::verify_start (framepos_t pos)
1648 if (source() && (source()->destructive() || source()->length_mutable())) {
1652 for (uint32_t n = 0; n < _sources.size(); ++n) {
1653 if (pos > source_length(n) - _length) {
1661 Region::verify_start_mutable (framepos_t& new_start)
1663 if (source() && (source()->destructive() || source()->length_mutable())) {
1667 for (uint32_t n = 0; n < _sources.size(); ++n) {
1668 if (new_start > source_length(n) - _length) {
1669 new_start = source_length(n) - _length;
1675 boost::shared_ptr<Region>
1676 Region::get_parent() const
1678 boost::shared_ptr<Playlist> pl (playlist());
1681 boost::shared_ptr<Region> r;
1682 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1684 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1685 return boost::static_pointer_cast<Region> (r);
1689 return boost::shared_ptr<Region>();
1693 Region::apply (Filter& filter, Progress* progress)
1695 return filter.run (shared_from_this(), progress);
1700 Region::maybe_invalidate_transients ()
1702 bool changed = !_onsets.empty();
1705 if (_valid_transients || changed) {
1706 send_change (PropertyChange (Properties::valid_transients));
1712 Region::transients (AnalysisFeatureList& afl)
1714 int cnt = afl.empty() ? 0 : 1;
1716 Region::merge_features (afl, _onsets, _position);
1717 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1718 if (!_onsets.empty ()) {
1721 if (!_user_transients.empty ()) {
1726 // remove exact duplicates
1727 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1732 Region::has_transients () const
1734 if (!_user_transients.empty ()) {
1735 assert (_valid_transients);
1738 if (!_onsets.empty ()) {
1745 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1747 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1748 const frameoffset_t p = (*x) + off;
1749 if (p < first_frame() || p > last_frame()) {
1752 result.push_back (p);
1757 Region::drop_sources ()
1759 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1760 (*i)->dec_use_count ();
1765 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1766 (*i)->dec_use_count ();
1769 _master_sources.clear ();
1773 Region::use_sources (SourceList const & s)
1775 set<boost::shared_ptr<Source> > unique_srcs;
1777 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1779 _sources.push_back (*i);
1780 (*i)->inc_use_count ();
1781 _master_sources.push_back (*i);
1782 (*i)->inc_use_count ();
1784 /* connect only once to DropReferences, even if sources are replicated
1787 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1788 unique_srcs.insert (*i);
1789 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1795 Region::can_trim () const
1797 CanTrim ct = CanTrim (0);
1803 /* if not locked, we can always move the front later, and the end earlier
1806 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1808 if (start() != 0 || can_trim_start_before_source_start ()) {
1809 ct = CanTrim (ct | FrontTrimEarlier);
1812 if (!_sources.empty()) {
1813 if ((start() + length()) < _sources.front()->length (0)) {
1814 ct = CanTrim (ct | EndTrimLater);
1822 Region::max_source_level () const
1826 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1827 lvl = max (lvl, (*i)->level());
1834 Region::is_compound () const
1836 return max_source_level() > 0;
1840 Region::post_set (const PropertyChange& pc)
1842 if (pc.contains (Properties::position)) {
1843 recompute_position_from_lock_style ();
1848 Region::set_start_internal (framecnt_t s)
1854 Region::earliest_possible_position () const
1856 if (_start > _position) {
1859 return _position - _start;
1864 Region::latest_possible_frame () const
1866 framecnt_t minlen = max_framecnt;
1868 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1869 /* non-audio regions have a length that may vary based on their
1870 * position, so we have to pass it in the call.
1872 minlen = min (minlen, (*i)->length (_position));
1875 /* the latest possible last frame is determined by the current
1876 * position, plus the shortest source extent past _start.
1879 return _position + (minlen - _start) - 1;