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 _session.bbt_time (_position, _bbt_time);
544 send_change (Properties::position_lock_style);
549 Region::update_after_tempo_map_change ()
551 boost::shared_ptr<Playlist> pl (playlist());
553 if (!pl || _position_lock_style != MusicTime) {
557 TempoMap& map (_session.tempo_map());
558 framepos_t pos = map.frame_time (_bbt_time);
559 set_position_internal (pos, false);
561 /* do this even if the position is the same. this helps out
562 a GUI that has moved its representation already.
564 send_change (Properties::position);
568 Region::set_position (framepos_t pos)
574 set_position_internal (pos, true);
576 /* do this even if the position is the same. this helps out
577 a GUI that has moved its representation already.
579 send_change (Properties::position);
583 /** A gui may need to create a region, then place it in an initial
584 * position determined by the user.
585 * When this takes place within one gui operation, we have to reset
586 * _last_position to prevent an implied move.
589 Region::set_initial_position (framepos_t pos)
595 if (_position != pos) {
598 /* check that the new _position wouldn't make the current
599 length impossible - if so, change the length.
601 XXX is this the right thing to do?
604 if (max_framepos - _length < _position) {
605 _last_length = _length;
606 _length = max_framepos - _position;
609 recompute_position_from_lock_style ();
610 /* ensure that this move doesn't cause a range move */
611 _last_position = _position;
615 /* do this even if the position is the same. this helps out
616 a GUI that has moved its representation already.
618 send_change (Properties::position);
622 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
624 /* We emit a change of Properties::position even if the position hasn't changed
625 (see Region::set_position), so we must always set this up so that
626 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
628 _last_position = _position;
630 if (_position != pos) {
633 /* check that the new _position wouldn't make the current
634 length impossible - if so, change the length.
636 XXX is this the right thing to do?
639 if (max_framepos - _length < _position) {
640 _last_length = _length;
641 _length = max_framepos - _position;
644 if (allow_bbt_recompute) {
645 recompute_position_from_lock_style ();
651 Region::recompute_position_from_lock_style ()
653 if (_position_lock_style == MusicTime) {
654 _session.bbt_time (_position, _bbt_time);
659 Region::nudge_position (frameoffset_t n)
661 if (locked() || video_locked()) {
669 framepos_t new_position = _position;
672 if (_position > max_framepos - n) {
673 new_position = max_framepos;
678 if (_position < -n) {
685 set_position_internal (new_position, true);
687 send_change (Properties::position);
691 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
693 _ancestral_length = l;
694 _ancestral_start = s;
700 Region::set_start (framepos_t pos)
702 if (locked() || position_locked() || video_locked()) {
705 /* This just sets the start, nothing else. It effectively shifts
706 the contents of the Region within the overall extent of the Source,
707 without changing the Region's position or length
712 if (!verify_start (pos)) {
716 set_start_internal (pos);
719 maybe_invalidate_transients ();
721 send_change (Properties::start);
726 Region::move_start (frameoffset_t distance)
728 if (locked() || position_locked() || video_locked()) {
732 framepos_t new_start;
736 if (_start > max_framepos - distance) {
737 new_start = max_framepos; // makes no sense
739 new_start = _start + distance;
742 if (!verify_start (new_start)) {
746 } else if (distance < 0) {
748 if (_start < -distance) {
751 new_start = _start + distance;
758 if (new_start == _start) {
762 set_start_internal (new_start);
767 send_change (Properties::start);
771 Region::trim_front (framepos_t new_position)
773 modify_front (new_position, false);
777 Region::cut_front (framepos_t new_position)
779 modify_front (new_position, true);
783 Region::cut_end (framepos_t new_endpoint)
785 modify_end (new_endpoint, true);
789 Region::modify_front (framepos_t new_position, bool reset_fade)
795 framepos_t end = last_frame();
796 framepos_t source_zero;
798 if (_position > _start) {
799 source_zero = _position - _start;
801 source_zero = 0; // its actually negative, but this will work for us
804 if (new_position < end) { /* can't trim it zero or negative length */
806 framecnt_t newlen = 0;
808 if (!can_trim_start_before_source_start ()) {
809 /* can't trim it back past where source position zero is located */
810 new_position = max (new_position, source_zero);
813 if (new_position > _position) {
814 newlen = _length - (new_position - _position);
816 newlen = _length + (_position - new_position);
819 trim_to_internal (new_position, newlen);
822 _right_of_split = true;
825 if (!property_changes_suspended()) {
826 recompute_at_start ();
829 maybe_invalidate_transients ();
834 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
840 if (new_endpoint > _position) {
841 trim_to_internal (_position, new_endpoint - _position);
843 _left_of_split = true;
845 if (!property_changes_suspended()) {
851 /** @param new_endpoint New region end point, such that, for example,
852 * a region at 0 of length 10 has an endpoint of 9.
856 Region::trim_end (framepos_t new_endpoint)
858 modify_end (new_endpoint, false);
862 Region::trim_to (framepos_t position, framecnt_t length)
868 trim_to_internal (position, length);
870 if (!property_changes_suspended()) {
871 recompute_at_start ();
877 Region::trim_to_internal (framepos_t position, framecnt_t length)
879 framepos_t new_start;
885 frameoffset_t const start_shift = position - _position;
887 if (start_shift > 0) {
889 if (_start > max_framepos - start_shift) {
890 new_start = max_framepos;
892 new_start = _start + start_shift;
895 } else if (start_shift < 0) {
897 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
900 new_start = _start + start_shift;
907 if (!verify_start_and_length (new_start, length)) {
911 PropertyChange what_changed;
913 if (_start != new_start) {
914 set_start_internal (new_start);
915 what_changed.add (Properties::start);
918 /* Set position before length, otherwise for MIDI regions this bad thing happens:
919 * 1. we call set_length_internal; length in beats is computed using the region's current
920 * (soon-to-be old) position
921 * 2. we call set_position_internal; position is set and length in frames re-computed using
922 * length in beats from (1) but at the new position, which is wrong if the region
923 * straddles a tempo/meter change.
926 if (_position != position) {
927 if (!property_changes_suspended()) {
928 _last_position = _position;
930 set_position_internal (position, true);
931 what_changed.add (Properties::position);
934 if (_length != length) {
935 if (!property_changes_suspended()) {
936 _last_length = _length;
938 set_length_internal (length);
939 what_changed.add (Properties::length);
944 PropertyChange start_and_length;
946 start_and_length.add (Properties::start);
947 start_and_length.add (Properties::length);
949 if (what_changed.contains (start_and_length)) {
953 if (!what_changed.empty()) {
954 send_change (what_changed);
959 Region::set_hidden (bool yn)
961 if (hidden() != yn) {
963 send_change (Properties::hidden);
968 Region::set_whole_file (bool yn)
971 /* no change signal */
975 Region::set_automatic (bool yn)
978 /* no change signal */
982 Region::set_muted (bool yn)
986 send_change (Properties::muted);
991 Region::set_opaque (bool yn)
993 if (opaque() != yn) {
995 send_change (Properties::opaque);
1000 Region::set_locked (bool yn)
1002 if (locked() != yn) {
1004 send_change (Properties::locked);
1009 Region::set_video_locked (bool yn)
1011 if (video_locked() != yn) {
1013 send_change (Properties::video_locked);
1018 Region::set_position_locked (bool yn)
1020 if (position_locked() != yn) {
1021 _position_locked = yn;
1022 send_change (Properties::locked);
1026 /** Set the region's sync point.
1027 * @param absolute_pos Session time.
1030 Region::set_sync_position (framepos_t absolute_pos)
1032 /* position within our file */
1033 framepos_t const file_pos = _start + (absolute_pos - _position);
1035 if (file_pos != _sync_position) {
1036 _sync_marked = true;
1037 _sync_position = file_pos;
1038 if (!property_changes_suspended()) {
1042 send_change (Properties::sync_position);
1047 Region::clear_sync_position ()
1049 if (sync_marked()) {
1050 _sync_marked = false;
1051 if (!property_changes_suspended()) {
1055 send_change (Properties::sync_position);
1059 /* @return the sync point relative the first frame of the region */
1061 Region::sync_offset (int& dir) const
1063 if (sync_marked()) {
1064 if (_sync_position > _start) {
1066 return _sync_position - _start;
1069 return _start - _sync_position;
1078 Region::adjust_to_sync (framepos_t pos) const
1081 frameoffset_t offset = sync_offset (sync_dir);
1083 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1092 if (max_framepos - pos > offset) {
1100 /** @return Sync position in session time */
1102 Region::sync_position() const
1104 if (sync_marked()) {
1105 return _position - _start + _sync_position;
1107 /* if sync has not been marked, use the start of the region */
1115 boost::shared_ptr<Playlist> pl (playlist());
1117 pl->raise_region (shared_from_this ());
1124 boost::shared_ptr<Playlist> pl (playlist());
1126 pl->lower_region (shared_from_this ());
1132 Region::raise_to_top ()
1134 boost::shared_ptr<Playlist> pl (playlist());
1136 pl->raise_region_to_top (shared_from_this());
1141 Region::lower_to_bottom ()
1143 boost::shared_ptr<Playlist> pl (playlist());
1145 pl->lower_region_to_bottom (shared_from_this());
1150 Region::set_layer (layer_t l)
1158 XMLNode *node = new XMLNode ("Region");
1161 LocaleGuard lg (X_("C"));
1162 const char* fe = NULL;
1164 /* custom version of 'add_properties (*node);'
1165 * skip values that have have dedicated save functions
1166 * in AudioRegion::state()
1168 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1169 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1170 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1171 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1172 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1173 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1174 i->second->get_value (*node);
1177 id().print (buf, sizeof (buf));
1178 node->add_property ("id", buf);
1179 node->add_property ("type", _type.to_string());
1181 switch (_first_edit) {
1182 case EditChangesNothing:
1185 case EditChangesName:
1191 default: /* should be unreachable but makes g++ happy */
1196 node->add_property ("first-edit", fe);
1198 /* note: flags are stored by derived classes */
1200 if (_position_lock_style != AudioTime) {
1203 node->add_property ("bbt-position", str.str());
1206 for (uint32_t n=0; n < _sources.size(); ++n) {
1207 snprintf (buf2, sizeof(buf2), "source-%d", n);
1208 _sources[n]->id().print (buf, sizeof(buf));
1209 node->add_property (buf2, buf);
1212 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1213 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1214 _master_sources[n]->id().print (buf, sizeof (buf));
1215 node->add_property (buf2, buf);
1218 /* Only store nested sources for the whole-file region that acts
1219 as the parent/root of all regions using it.
1222 if (_whole_file && max_source_level() > 0) {
1224 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1226 /* region is compound - get its playlist and
1227 store that before we list the region that
1231 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1232 nested_node->add_child_nocopy ((*s)->get_state ());
1236 node->add_child_nocopy (*nested_node);
1241 node->add_child_copy (*_extra_xml);
1248 Region::get_state ()
1254 Region::set_state (const XMLNode& node, int version)
1256 PropertyChange what_changed;
1257 return _set_state (node, version, what_changed, true);
1261 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1263 const XMLProperty* prop;
1265 Stateful::save_extra_xml (node);
1267 what_changed = set_values (node);
1271 if (_position_lock_style == MusicTime) {
1272 if ((prop = node.property ("bbt-position")) == 0) {
1273 /* missing BBT info, revert to audio time locking */
1274 _position_lock_style = AudioTime;
1276 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1279 &_bbt_time.ticks) != 3) {
1280 _position_lock_style = AudioTime;
1285 /* fix problems with old sessions corrupted by impossible
1286 values for _stretch or _shift
1288 if (_stretch == 0.0f) {
1292 if (_shift == 0.0f) {
1297 send_change (what_changed);
1300 /* Quick fix for 2.x sessions when region is muted */
1301 if ((prop = node.property (X_("flags")))) {
1302 if (string::npos != prop->value().find("Muted")){
1307 // saved property is invalid, region-transients are not saved
1308 if (_user_transients.size() == 0){
1309 _valid_transients = false;
1316 Region::suspend_property_changes ()
1318 Stateful::suspend_property_changes ();
1319 _last_length = _length;
1320 _last_position = _position;
1324 Region::mid_thaw (const PropertyChange& what_changed)
1326 if (what_changed.contains (Properties::length)) {
1327 if (what_changed.contains (Properties::position)) {
1328 recompute_at_start ();
1330 recompute_at_end ();
1335 Region::send_change (const PropertyChange& what_changed)
1337 if (what_changed.empty()) {
1341 Stateful::send_change (what_changed);
1343 if (!Stateful::property_changes_suspended()) {
1345 /* Try and send a shared_pointer unless this is part of the constructor.
1350 boost::shared_ptr<Region> rptr = shared_from_this();
1351 RegionPropertyChanged (rptr, what_changed);
1353 /* no shared_ptr available, relax; */
1359 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1361 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1365 Region::equivalent (boost::shared_ptr<const Region> other) const
1367 return _start == other->_start &&
1368 _position == other->_position &&
1369 _length == other->_length;
1373 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1375 return _start == other->_start &&
1376 _length == other->_length;
1380 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1382 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1386 Region::source_deleted (boost::weak_ptr<Source>)
1390 if (!_session.deletion_in_progress()) {
1391 /* this is a very special case: at least one of the region's
1392 sources has bee deleted, so invalidate all references to
1393 ourselves. Do NOT do this during session deletion, because
1394 then we run the risk that this will actually result
1395 in this object being deleted (as refcnt goes to zero)
1396 while emitting DropReferences.
1404 Region::master_source_names ()
1406 SourceList::iterator i;
1408 vector<string> names;
1409 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1410 names.push_back((*i)->name());
1417 Region::set_master_sources (const SourceList& srcs)
1419 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1420 (*i)->dec_use_count ();
1423 _master_sources = srcs;
1424 assert (_sources.size() == _master_sources.size());
1426 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1427 (*i)->inc_use_count ();
1432 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1437 if ((_sources.size() != other->_sources.size()) ||
1438 (_master_sources.size() != other->_master_sources.size())) {
1442 SourceList::const_iterator i;
1443 SourceList::const_iterator io;
1445 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1446 if ((*i)->id() != (*io)->id()) {
1451 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1452 if ((*i)->id() != (*io)->id()) {
1461 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
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()) {
1480 Region::source_string () const
1482 //string res = itos(_sources.size());
1485 res << _sources.size() << ":";
1487 SourceList::const_iterator i;
1489 for (i = _sources.begin(); i != _sources.end(); ++i) {
1490 res << (*i)->id() << ":";
1493 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1494 res << (*i)->id() << ":";
1501 Region::uses_source (boost::shared_ptr<const Source> source) const
1503 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1508 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1511 if (ps->playlist()->uses_source (source)) {
1517 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1522 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1525 if (ps->playlist()->uses_source (source)) {
1535 Region::source_length(uint32_t n) const
1537 assert (n < _sources.size());
1538 return _sources[n]->length (_position - _start);
1542 Region::verify_length (framecnt_t& len)
1544 if (source() && (source()->destructive() || source()->length_mutable())) {
1548 framecnt_t maxlen = 0;
1550 for (uint32_t n = 0; n < _sources.size(); ++n) {
1551 maxlen = max (maxlen, source_length(n) - _start);
1554 len = min (len, maxlen);
1560 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1562 if (source() && (source()->destructive() || source()->length_mutable())) {
1566 framecnt_t maxlen = 0;
1568 for (uint32_t n = 0; n < _sources.size(); ++n) {
1569 maxlen = max (maxlen, source_length(n) - new_start);
1572 new_length = min (new_length, maxlen);
1578 Region::verify_start (framepos_t pos)
1580 if (source() && (source()->destructive() || source()->length_mutable())) {
1584 for (uint32_t n = 0; n < _sources.size(); ++n) {
1585 if (pos > source_length(n) - _length) {
1593 Region::verify_start_mutable (framepos_t& new_start)
1595 if (source() && (source()->destructive() || source()->length_mutable())) {
1599 for (uint32_t n = 0; n < _sources.size(); ++n) {
1600 if (new_start > source_length(n) - _length) {
1601 new_start = source_length(n) - _length;
1607 boost::shared_ptr<Region>
1608 Region::get_parent() const
1610 boost::shared_ptr<Playlist> pl (playlist());
1613 boost::shared_ptr<Region> r;
1614 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1616 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1617 return boost::static_pointer_cast<Region> (r);
1621 return boost::shared_ptr<Region>();
1625 Region::apply (Filter& filter, Progress* progress)
1627 return filter.run (shared_from_this(), progress);
1632 Region::maybe_invalidate_transients ()
1634 bool changed = !_onsets.empty();
1637 if (_valid_transients || changed) {
1638 send_change (PropertyChange (Properties::valid_transients));
1644 Region::transients (AnalysisFeatureList& afl)
1646 int cnt = afl.empty() ? 0 : 1;
1648 Region::merge_features (afl, _onsets, _position);
1649 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1650 if (!_onsets.empty ()) {
1653 if (!_user_transients.empty ()) {
1658 // remove exact duplicates
1659 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1664 Region::has_transients () const
1666 if (!_user_transients.empty ()) {
1667 assert (_valid_transients);
1670 if (!_onsets.empty ()) {
1677 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1679 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1680 const frameoffset_t p = (*x) + off;
1681 if (p < first_frame() || p > last_frame()) {
1684 result.push_back (p);
1689 Region::drop_sources ()
1691 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1692 (*i)->dec_use_count ();
1697 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1698 (*i)->dec_use_count ();
1701 _master_sources.clear ();
1705 Region::use_sources (SourceList const & s)
1707 set<boost::shared_ptr<Source> > unique_srcs;
1709 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1711 _sources.push_back (*i);
1712 (*i)->inc_use_count ();
1713 _master_sources.push_back (*i);
1714 (*i)->inc_use_count ();
1716 /* connect only once to DropReferences, even if sources are replicated
1719 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1720 unique_srcs.insert (*i);
1721 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1727 Region::can_trim () const
1729 CanTrim ct = CanTrim (0);
1735 /* if not locked, we can always move the front later, and the end earlier
1738 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1740 if (start() != 0 || can_trim_start_before_source_start ()) {
1741 ct = CanTrim (ct | FrontTrimEarlier);
1744 if (!_sources.empty()) {
1745 if ((start() + length()) < _sources.front()->length (0)) {
1746 ct = CanTrim (ct | EndTrimLater);
1754 Region::max_source_level () const
1758 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1759 lvl = max (lvl, (*i)->level());
1766 Region::is_compound () const
1768 return max_source_level() > 0;
1772 Region::post_set (const PropertyChange& pc)
1774 if (pc.contains (Properties::position)) {
1775 recompute_position_from_lock_style ();
1780 Region::set_start_internal (framecnt_t s)
1786 Region::earliest_possible_position () const
1788 if (_start > _position) {
1791 return _position - _start;
1796 Region::latest_possible_frame () const
1798 framecnt_t minlen = max_framecnt;
1800 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1801 /* non-audio regions have a length that may vary based on their
1802 * position, so we have to pass it in the call.
1804 minlen = min (minlen, (*i)->length (_position));
1807 /* the latest possible last frame is determined by the current
1808 * position, plus the shortest source extent past _start.
1811 return _position + (minlen - _start) - 1;