2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<framecnt_t> sync_position;
68 PBD::PropertyDescriptor<layer_t> layer;
69 PBD::PropertyDescriptor<framepos_t> ancestral_start;
70 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
71 PBD::PropertyDescriptor<float> stretch;
72 PBD::PropertyDescriptor<float> shift;
73 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
74 PBD::PropertyDescriptor<uint64_t> layering_index;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
91 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
93 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
95 Properties::import.property_id = g_quark_from_static_string (X_("import"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
97 Properties::external.property_id = g_quark_from_static_string (X_("external"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
99 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
101 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
103 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
105 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
107 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
109 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
111 Properties::start.property_id = g_quark_from_static_string (X_("start"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
113 Properties::length.property_id = g_quark_from_static_string (X_("length"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
115 Properties::position.property_id = g_quark_from_static_string (X_("position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
117 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
119 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
121 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
123 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
125 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
127 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
129 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
136 Region::register_properties ()
138 _xml_node_name = X_("Region");
140 add_property (_muted);
141 add_property (_opaque);
142 add_property (_locked);
143 add_property (_video_locked);
144 add_property (_automatic);
145 add_property (_whole_file);
146 add_property (_import);
147 add_property (_external);
148 add_property (_sync_marked);
149 add_property (_left_of_split);
150 add_property (_right_of_split);
151 add_property (_hidden);
152 add_property (_position_locked);
153 add_property (_valid_transients);
154 add_property (_start);
155 add_property (_length);
156 add_property (_position);
157 add_property (_sync_position);
158 add_property (_ancestral_start);
159 add_property (_ancestral_length);
160 add_property (_stretch);
161 add_property (_shift);
162 add_property (_position_lock_style);
163 add_property (_layering_index);
166 #define REGION_DEFAULT_STATE(s,l) \
167 _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _valid_transients (Properties::valid_transients, false) \
171 , _start (Properties::start, (s)) \
172 , _length (Properties::length, (l)) \
173 , _position (Properties::position, 0) \
174 , _sync_position (Properties::sync_position, (s)) \
175 , _transient_user_start (0) \
176 , _transient_analysis_start (0) \
177 , _transient_analysis_end (0) \
178 , _muted (Properties::muted, false) \
179 , _opaque (Properties::opaque, true) \
180 , _locked (Properties::locked, false) \
181 , _video_locked (Properties::video_locked, false) \
182 , _automatic (Properties::automatic, false) \
183 , _whole_file (Properties::whole_file, false) \
184 , _import (Properties::import, false) \
185 , _external (Properties::external, false) \
186 , _hidden (Properties::hidden, false) \
187 , _position_locked (Properties::position_locked, false) \
188 , _ancestral_start (Properties::ancestral_start, (s)) \
189 , _ancestral_length (Properties::ancestral_length, (l)) \
190 , _stretch (Properties::stretch, 1.0) \
191 , _shift (Properties::shift, 1.0) \
192 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
193 , _layering_index (Properties::layering_index, 0)
195 #define REGION_COPY_STATE(other) \
196 _sync_marked (Properties::sync_marked, other->_sync_marked) \
197 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
198 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _user_transients (other->_user_transients) \
205 , _transient_user_start (other->_transient_user_start) \
206 , _transients (other->_transients) \
207 , _transient_analysis_start (other->_transient_analysis_start) \
208 , _transient_analysis_end (other->_transient_analysis_end) \
209 , _muted (Properties::muted, other->_muted) \
210 , _opaque (Properties::opaque, other->_opaque) \
211 , _locked (Properties::locked, other->_locked) \
212 , _video_locked (Properties::video_locked, other->_video_locked) \
213 , _automatic (Properties::automatic, other->_automatic) \
214 , _whole_file (Properties::whole_file, other->_whole_file) \
215 , _import (Properties::import, other->_import) \
216 , _external (Properties::external, other->_external) \
217 , _hidden (Properties::hidden, other->_hidden) \
218 , _position_locked (Properties::position_locked, other->_position_locked) \
219 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
220 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
221 , _stretch (Properties::stretch, other->_stretch) \
222 , _shift (Properties::shift, other->_shift) \
223 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
224 , _layering_index (Properties::layering_index, other->_layering_index)
226 /* derived-from-derived constructor (no sources in constructor) */
227 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
228 : SessionObject(s, name)
230 , REGION_DEFAULT_STATE(start,length)
231 , _last_length (length)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 /* no sources at this point */
241 /** Basic Region constructor (many sources) */
242 Region::Region (const SourceList& srcs)
243 : SessionObject(srcs.front()->session(), "toBeRenamed")
244 , _type (srcs.front()->type())
245 , REGION_DEFAULT_STATE(0,0)
248 , _first_edit (EditChangesNothing)
251 register_properties ();
253 _type = srcs.front()->type();
257 assert(_sources.size() > 0);
258 assert (_type == srcs.front()->type());
261 /** Create a new Region from an existing one */
262 Region::Region (boost::shared_ptr<const Region> other)
263 : SessionObject(other->session(), other->name())
264 , _type (other->data_type())
265 , REGION_COPY_STATE (other)
266 , _last_length (other->_last_length)
267 , _last_position(other->_last_position) \
268 , _first_edit (EditChangesNothing)
269 , _layer (other->_layer)
271 register_properties ();
273 /* override state that may have been incorrectly inherited from the other region
281 use_sources (other->_sources);
282 set_master_sources (other->_master_sources);
284 _position_lock_style = other->_position_lock_style;
285 _first_edit = other->_first_edit;
287 _start = 0; // It seems strange _start is not inherited here?
289 /* sync pos is relative to start of file. our start-in-file is now zero,
290 so set our sync position to whatever the the difference between
291 _start and _sync_pos was in the other region.
293 result is that our new sync pos points to the same point in our source(s)
294 as the sync in the other region did in its source(s).
296 since we start at zero in our source(s), it is not possible to use a sync point that
297 is before the start. reset it to _start if that was true in the other region.
300 if (other->sync_marked()) {
301 if (other->_start < other->_sync_position) {
302 /* sync pos was after the start point of the other region */
303 _sync_position = other->_sync_position - other->_start;
305 /* sync pos was before the start point of the other region. not possible here. */
306 _sync_marked = false;
307 _sync_position = _start;
310 _sync_marked = false;
311 _sync_position = _start;
314 assert (_type == other->data_type());
317 /** Create a new Region from part of an existing one.
319 the start within \a other is given by \a offset
320 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323 : SessionObject(other->session(), other->name())
324 , _type (other->data_type())
325 , REGION_COPY_STATE (other)
326 , _last_length (other->_last_length)
327 , _last_position(other->_last_position) \
328 , _first_edit (EditChangesNothing)
329 , _layer (other->_layer)
331 register_properties ();
333 /* override state that may have been incorrectly inherited from the other region
341 use_sources (other->_sources);
342 set_master_sources (other->_master_sources);
344 _start = other->_start + offset;
346 /* if the other region had a distinct sync point
347 set, then continue to use it as best we can.
348 otherwise, reset sync point back to start.
351 if (other->sync_marked()) {
352 if (other->_sync_position < _start) {
353 _sync_marked = false;
354 _sync_position = _start;
356 _sync_position = other->_sync_position;
359 _sync_marked = false;
360 _sync_position = _start;
363 assert (_type == other->data_type());
366 /** Create a copy of @param other but with different sources. Used by filters */
367 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
368 : SessionObject (other->session(), other->name())
369 , _type (srcs.front()->type())
370 , REGION_COPY_STATE (other)
371 , _last_length (other->_last_length)
372 , _last_position (other->_last_position)
373 , _first_edit (EditChangesID)
374 , _layer (other->_layer)
376 register_properties ();
379 _position_locked = false;
381 other->_first_edit = EditChangesName;
383 if (other->_extra_xml) {
384 _extra_xml = new XMLNode (*other->_extra_xml);
390 assert(_sources.size() > 0);
395 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
400 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
402 _playlist = wpl.lock();
406 Region::set_name (const std::string& str)
409 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
410 assert(_name == str);
412 send_change (Properties::name);
419 Region::set_length (framecnt_t len)
421 //cerr << "Region::set_length() len = " << len << endl;
426 if (_length != len && len != 0) {
428 /* check that the current _position wouldn't make the new
432 if (max_framepos - len < _position) {
436 if (!verify_length (len)) {
441 _last_length = _length;
442 set_length_internal (len);
446 maybe_invalidate_transients ();
448 if (!property_changes_suspended()) {
452 send_change (Properties::length);
457 Region::set_length_internal (framecnt_t len)
463 Region::maybe_uncopy ()
465 /* this does nothing but marked a semantic moment once upon a time */
469 Region::first_edit ()
471 boost::shared_ptr<Playlist> pl (playlist());
473 if (_first_edit != EditChangesNothing && pl) {
475 _name = RegionFactory::new_region_name (_name);
476 _first_edit = EditChangesNothing;
478 send_change (Properties::name);
480 RegionFactory::CheckNewRegion (shared_from_this());
485 Region::at_natural_position () const
487 boost::shared_ptr<Playlist> pl (playlist());
493 boost::shared_ptr<Region> whole_file_region = get_parent();
495 if (whole_file_region) {
496 if (_position == whole_file_region->position() + _start) {
505 Region::move_to_natural_position ()
507 boost::shared_ptr<Playlist> pl (playlist());
513 boost::shared_ptr<Region> whole_file_region = get_parent();
515 if (whole_file_region) {
516 set_position (whole_file_region->position() + _start);
521 Region::special_set_position (framepos_t pos)
523 /* this is used when creating a whole file region as
524 a way to store its "natural" or "captured" position.
527 _position = _position;
532 Region::set_position_lock_style (PositionLockStyle ps)
534 if (_position_lock_style != ps) {
536 boost::shared_ptr<Playlist> pl (playlist());
538 _position_lock_style = ps;
540 if (_position_lock_style == MusicTime) {
541 _beat = _session.tempo_map().beat_at_frame (_position);
544 send_change (Properties::position_lock_style);
549 Region::update_after_tempo_map_change (bool send)
551 boost::shared_ptr<Playlist> pl (playlist());
553 if (!pl || _position_lock_style != MusicTime) {
557 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
558 set_position_internal (pos, false);
560 /* do this even if the position is the same. this helps out
561 a GUI that has moved its representation already.
565 send_change (Properties::position);
570 Region::set_position (framepos_t pos)
576 set_position_internal (pos, true);
578 /* do this even if the position is the same. this helps out
579 a GUI that has moved its representation already.
581 send_change (Properties::position);
585 /** A gui may need to create a region, then place it in an initial
586 * position determined by the user.
587 * When this takes place within one gui operation, we have to reset
588 * _last_position to prevent an implied move.
591 Region::set_initial_position (framepos_t pos)
597 if (_position != pos) {
600 /* check that the new _position wouldn't make the current
601 length impossible - if so, change the length.
603 XXX is this the right thing to do?
606 if (max_framepos - _length < _position) {
607 _last_length = _length;
608 _length = max_framepos - _position;
611 recompute_position_from_lock_style ();
612 /* ensure that this move doesn't cause a range move */
613 _last_position = _position;
617 /* do this even if the position is the same. this helps out
618 a GUI that has moved its representation already.
620 send_change (Properties::position);
624 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
626 /* We emit a change of Properties::position even if the position hasn't changed
627 (see Region::set_position), so we must always set this up so that
628 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
630 _last_position = _position;
632 if (_position != pos) {
635 /* check that the new _position wouldn't make the current
636 length impossible - if so, change the length.
638 XXX is this the right thing to do?
641 if (max_framepos - _length < _position) {
642 _last_length = _length;
643 _length = max_framepos - _position;
646 if (allow_bbt_recompute) {
647 recompute_position_from_lock_style ();
653 Region::recompute_position_from_lock_style ()
655 if (_position_lock_style == MusicTime) {
656 _beat = _session.tempo_map().beat_at_frame (_position);
661 Region::nudge_position (frameoffset_t n)
663 if (locked() || video_locked()) {
671 framepos_t new_position = _position;
674 if (_position > max_framepos - n) {
675 new_position = max_framepos;
680 if (_position < -n) {
687 set_position_internal (new_position, true);
689 send_change (Properties::position);
693 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
695 _ancestral_length = l;
696 _ancestral_start = s;
702 Region::set_start (framepos_t pos)
704 if (locked() || position_locked() || video_locked()) {
707 /* This just sets the start, nothing else. It effectively shifts
708 the contents of the Region within the overall extent of the Source,
709 without changing the Region's position or length
714 if (!verify_start (pos)) {
718 set_start_internal (pos);
721 maybe_invalidate_transients ();
723 send_change (Properties::start);
728 Region::move_start (frameoffset_t distance)
730 if (locked() || position_locked() || video_locked()) {
734 framepos_t new_start;
738 if (_start > max_framepos - distance) {
739 new_start = max_framepos; // makes no sense
741 new_start = _start + distance;
744 if (!verify_start (new_start)) {
748 } else if (distance < 0) {
750 if (_start < -distance) {
753 new_start = _start + distance;
760 if (new_start == _start) {
764 set_start_internal (new_start);
769 send_change (Properties::start);
773 Region::trim_front (framepos_t new_position)
775 modify_front (new_position, false);
779 Region::cut_front (framepos_t new_position)
781 modify_front (new_position, true);
785 Region::cut_end (framepos_t new_endpoint)
787 modify_end (new_endpoint, true);
791 Region::modify_front (framepos_t new_position, bool reset_fade)
797 framepos_t end = last_frame();
798 framepos_t source_zero;
800 if (_position > _start) {
801 source_zero = _position - _start;
803 source_zero = 0; // its actually negative, but this will work for us
806 if (new_position < end) { /* can't trim it zero or negative length */
808 framecnt_t newlen = 0;
810 if (!can_trim_start_before_source_start ()) {
811 /* can't trim it back past where source position zero is located */
812 new_position = max (new_position, source_zero);
815 if (new_position > _position) {
816 newlen = _length - (new_position - _position);
818 newlen = _length + (_position - new_position);
821 trim_to_internal (new_position, newlen);
824 _right_of_split = true;
827 if (!property_changes_suspended()) {
828 recompute_at_start ();
831 maybe_invalidate_transients ();
836 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
842 if (new_endpoint > _position) {
843 trim_to_internal (_position, new_endpoint - _position);
845 _left_of_split = true;
847 if (!property_changes_suspended()) {
853 /** @param new_endpoint New region end point, such that, for example,
854 * a region at 0 of length 10 has an endpoint of 9.
858 Region::trim_end (framepos_t new_endpoint)
860 modify_end (new_endpoint, false);
864 Region::trim_to (framepos_t position, framecnt_t length)
870 trim_to_internal (position, length);
872 if (!property_changes_suspended()) {
873 recompute_at_start ();
879 Region::trim_to_internal (framepos_t position, framecnt_t length)
881 framepos_t new_start;
887 frameoffset_t const start_shift = position - _position;
889 if (start_shift > 0) {
891 if (_start > max_framepos - start_shift) {
892 new_start = max_framepos;
894 new_start = _start + start_shift;
897 } else if (start_shift < 0) {
899 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
902 new_start = _start + start_shift;
909 if (!verify_start_and_length (new_start, length)) {
913 PropertyChange what_changed;
915 if (_start != new_start) {
916 set_start_internal (new_start);
917 what_changed.add (Properties::start);
920 /* Set position before length, otherwise for MIDI regions this bad thing happens:
921 * 1. we call set_length_internal; length in beats is computed using the region's current
922 * (soon-to-be old) position
923 * 2. we call set_position_internal; position is set and length in frames re-computed using
924 * length in beats from (1) but at the new position, which is wrong if the region
925 * straddles a tempo/meter change.
928 if (_position != position) {
929 if (!property_changes_suspended()) {
930 _last_position = _position;
932 set_position_internal (position, true);
933 what_changed.add (Properties::position);
936 if (_length != length) {
937 if (!property_changes_suspended()) {
938 _last_length = _length;
940 set_length_internal (length);
941 what_changed.add (Properties::length);
946 PropertyChange start_and_length;
948 start_and_length.add (Properties::start);
949 start_and_length.add (Properties::length);
951 if (what_changed.contains (start_and_length)) {
955 if (!what_changed.empty()) {
956 send_change (what_changed);
961 Region::set_hidden (bool yn)
963 if (hidden() != yn) {
965 send_change (Properties::hidden);
970 Region::set_whole_file (bool yn)
973 /* no change signal */
977 Region::set_automatic (bool yn)
980 /* no change signal */
984 Region::set_muted (bool yn)
988 send_change (Properties::muted);
993 Region::set_opaque (bool yn)
995 if (opaque() != yn) {
997 send_change (Properties::opaque);
1002 Region::set_locked (bool yn)
1004 if (locked() != yn) {
1006 send_change (Properties::locked);
1011 Region::set_video_locked (bool yn)
1013 if (video_locked() != yn) {
1015 send_change (Properties::video_locked);
1020 Region::set_position_locked (bool yn)
1022 if (position_locked() != yn) {
1023 _position_locked = yn;
1024 send_change (Properties::locked);
1028 /** Set the region's sync point.
1029 * @param absolute_pos Session time.
1032 Region::set_sync_position (framepos_t absolute_pos)
1034 /* position within our file */
1035 framepos_t const file_pos = _start + (absolute_pos - _position);
1037 if (file_pos != _sync_position) {
1038 _sync_marked = true;
1039 _sync_position = file_pos;
1040 if (!property_changes_suspended()) {
1044 send_change (Properties::sync_position);
1049 Region::clear_sync_position ()
1051 if (sync_marked()) {
1052 _sync_marked = false;
1053 if (!property_changes_suspended()) {
1057 send_change (Properties::sync_position);
1061 /* @return the sync point relative the first frame of the region */
1063 Region::sync_offset (int& dir) const
1065 if (sync_marked()) {
1066 if (_sync_position > _start) {
1068 return _sync_position - _start;
1071 return _start - _sync_position;
1080 Region::adjust_to_sync (framepos_t pos) const
1083 frameoffset_t offset = sync_offset (sync_dir);
1085 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1094 if (max_framepos - pos > offset) {
1102 /** @return Sync position in session time */
1104 Region::sync_position() const
1106 if (sync_marked()) {
1107 return _position - _start + _sync_position;
1109 /* if sync has not been marked, use the start of the region */
1117 boost::shared_ptr<Playlist> pl (playlist());
1119 pl->raise_region (shared_from_this ());
1126 boost::shared_ptr<Playlist> pl (playlist());
1128 pl->lower_region (shared_from_this ());
1134 Region::raise_to_top ()
1136 boost::shared_ptr<Playlist> pl (playlist());
1138 pl->raise_region_to_top (shared_from_this());
1143 Region::lower_to_bottom ()
1145 boost::shared_ptr<Playlist> pl (playlist());
1147 pl->lower_region_to_bottom (shared_from_this());
1152 Region::set_layer (layer_t l)
1160 XMLNode *node = new XMLNode ("Region");
1164 const char* fe = NULL;
1166 /* custom version of 'add_properties (*node);'
1167 * skip values that have have dedicated save functions
1168 * in AudioRegion::state()
1170 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1171 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1172 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1173 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1174 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1175 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1176 i->second->get_value (*node);
1179 id().print (buf, sizeof (buf));
1180 node->add_property ("id", buf);
1181 node->add_property ("type", _type.to_string());
1183 switch (_first_edit) {
1184 case EditChangesNothing:
1187 case EditChangesName:
1193 default: /* should be unreachable but makes g++ happy */
1198 node->add_property ("first-edit", fe);
1200 /* note: flags are stored by derived classes */
1202 if (_position_lock_style != AudioTime) {
1203 snprintf (buf, sizeof(buf), "%lf", _beat);
1204 node->add_property ("beat", buf);
1207 for (uint32_t n=0; n < _sources.size(); ++n) {
1208 snprintf (buf2, sizeof(buf2), "source-%d", n);
1209 _sources[n]->id().print (buf, sizeof(buf));
1210 node->add_property (buf2, buf);
1213 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1214 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1215 _master_sources[n]->id().print (buf, sizeof (buf));
1216 node->add_property (buf2, buf);
1219 /* Only store nested sources for the whole-file region that acts
1220 as the parent/root of all regions using it.
1223 if (_whole_file && max_source_level() > 0) {
1225 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1227 /* region is compound - get its playlist and
1228 store that before we list the region that
1232 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1233 nested_node->add_child_nocopy ((*s)->get_state ());
1237 node->add_child_nocopy (*nested_node);
1242 node->add_child_copy (*_extra_xml);
1249 Region::get_state ()
1255 Region::set_state (const XMLNode& node, int version)
1257 PropertyChange what_changed;
1258 return _set_state (node, version, what_changed, true);
1262 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1264 XMLProperty const * prop;
1265 Timecode::BBT_Time bbt_time;
1267 Stateful::save_extra_xml (node);
1269 what_changed = set_values (node);
1273 if (_position_lock_style == MusicTime) {
1274 if ((prop = node.property ("bbt-position")) == 0) {
1275 if ((prop = node.property ("beat")) == 0) {
1276 /* missing BBT info, revert to audio time locking */
1277 _position_lock_style = AudioTime;
1279 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1280 _position_lock_style = AudioTime;
1285 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1288 &bbt_time.ticks) != 3) {
1289 _position_lock_style = AudioTime;
1291 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1296 /* fix problems with old sessions corrupted by impossible
1297 values for _stretch or _shift
1299 if (_stretch == 0.0f) {
1303 if (_shift == 0.0f) {
1308 send_change (what_changed);
1311 /* Quick fix for 2.x sessions when region is muted */
1312 if ((prop = node.property (X_("flags")))) {
1313 if (string::npos != prop->value().find("Muted")){
1318 // saved property is invalid, region-transients are not saved
1319 if (_user_transients.size() == 0){
1320 _valid_transients = false;
1327 Region::suspend_property_changes ()
1329 Stateful::suspend_property_changes ();
1330 _last_length = _length;
1331 _last_position = _position;
1335 Region::mid_thaw (const PropertyChange& what_changed)
1337 if (what_changed.contains (Properties::length)) {
1338 if (what_changed.contains (Properties::position)) {
1339 recompute_at_start ();
1341 recompute_at_end ();
1346 Region::send_change (const PropertyChange& what_changed)
1348 if (what_changed.empty()) {
1352 Stateful::send_change (what_changed);
1354 if (!Stateful::property_changes_suspended()) {
1356 /* Try and send a shared_pointer unless this is part of the constructor.
1361 boost::shared_ptr<Region> rptr = shared_from_this();
1362 RegionPropertyChanged (rptr, what_changed);
1364 /* no shared_ptr available, relax; */
1370 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1372 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1376 Region::equivalent (boost::shared_ptr<const Region> other) const
1378 return _start == other->_start &&
1379 _position == other->_position &&
1380 _length == other->_length;
1384 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1386 return _start == other->_start &&
1387 _length == other->_length;
1391 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1393 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1397 Region::source_deleted (boost::weak_ptr<Source>)
1401 if (!_session.deletion_in_progress()) {
1402 /* this is a very special case: at least one of the region's
1403 sources has bee deleted, so invalidate all references to
1404 ourselves. Do NOT do this during session deletion, because
1405 then we run the risk that this will actually result
1406 in this object being deleted (as refcnt goes to zero)
1407 while emitting DropReferences.
1415 Region::master_source_names ()
1417 SourceList::iterator i;
1419 vector<string> names;
1420 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1421 names.push_back((*i)->name());
1428 Region::set_master_sources (const SourceList& srcs)
1430 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1431 (*i)->dec_use_count ();
1434 _master_sources = srcs;
1435 assert (_sources.size() == _master_sources.size());
1437 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1438 (*i)->inc_use_count ();
1443 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1448 if ((_sources.size() != other->_sources.size()) ||
1449 (_master_sources.size() != other->_master_sources.size())) {
1453 SourceList::const_iterator i;
1454 SourceList::const_iterator io;
1456 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1457 if ((*i)->id() != (*io)->id()) {
1462 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1463 if ((*i)->id() != (*io)->id()) {
1472 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1478 SourceList::const_iterator i;
1479 SourceList::const_iterator io;
1481 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1482 if ((*i)->id() == (*io)->id()) {
1491 Region::source_string () const
1493 //string res = itos(_sources.size());
1496 res << _sources.size() << ":";
1498 SourceList::const_iterator i;
1500 for (i = _sources.begin(); i != _sources.end(); ++i) {
1501 res << (*i)->id() << ":";
1504 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1505 res << (*i)->id() << ":";
1512 Region::uses_source (boost::shared_ptr<const Source> source) const
1514 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1519 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1522 if (ps->playlist()->uses_source (source)) {
1528 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1533 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1536 if (ps->playlist()->uses_source (source)) {
1546 Region::source_length(uint32_t n) const
1548 assert (n < _sources.size());
1549 return _sources[n]->length (_position - _start);
1553 Region::verify_length (framecnt_t& len)
1555 if (source() && (source()->destructive() || source()->length_mutable())) {
1559 framecnt_t maxlen = 0;
1561 for (uint32_t n = 0; n < _sources.size(); ++n) {
1562 maxlen = max (maxlen, source_length(n) - _start);
1565 len = min (len, maxlen);
1571 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1573 if (source() && (source()->destructive() || source()->length_mutable())) {
1577 framecnt_t maxlen = 0;
1579 for (uint32_t n = 0; n < _sources.size(); ++n) {
1580 maxlen = max (maxlen, source_length(n) - new_start);
1583 new_length = min (new_length, maxlen);
1589 Region::verify_start (framepos_t pos)
1591 if (source() && (source()->destructive() || source()->length_mutable())) {
1595 for (uint32_t n = 0; n < _sources.size(); ++n) {
1596 if (pos > source_length(n) - _length) {
1604 Region::verify_start_mutable (framepos_t& new_start)
1606 if (source() && (source()->destructive() || source()->length_mutable())) {
1610 for (uint32_t n = 0; n < _sources.size(); ++n) {
1611 if (new_start > source_length(n) - _length) {
1612 new_start = source_length(n) - _length;
1618 boost::shared_ptr<Region>
1619 Region::get_parent() const
1621 boost::shared_ptr<Playlist> pl (playlist());
1624 boost::shared_ptr<Region> r;
1625 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1627 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1628 return boost::static_pointer_cast<Region> (r);
1632 return boost::shared_ptr<Region>();
1636 Region::apply (Filter& filter, Progress* progress)
1638 return filter.run (shared_from_this(), progress);
1643 Region::maybe_invalidate_transients ()
1645 bool changed = !_onsets.empty();
1648 if (_valid_transients || changed) {
1649 send_change (PropertyChange (Properties::valid_transients));
1655 Region::transients (AnalysisFeatureList& afl)
1657 int cnt = afl.empty() ? 0 : 1;
1659 Region::merge_features (afl, _onsets, _position);
1660 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1661 if (!_onsets.empty ()) {
1664 if (!_user_transients.empty ()) {
1669 // remove exact duplicates
1670 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1675 Region::has_transients () const
1677 if (!_user_transients.empty ()) {
1678 assert (_valid_transients);
1681 if (!_onsets.empty ()) {
1688 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1690 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1691 const frameoffset_t p = (*x) + off;
1692 if (p < first_frame() || p > last_frame()) {
1695 result.push_back (p);
1700 Region::drop_sources ()
1702 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1703 (*i)->dec_use_count ();
1708 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1709 (*i)->dec_use_count ();
1712 _master_sources.clear ();
1716 Region::use_sources (SourceList const & s)
1718 set<boost::shared_ptr<Source> > unique_srcs;
1720 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1722 _sources.push_back (*i);
1723 (*i)->inc_use_count ();
1724 _master_sources.push_back (*i);
1725 (*i)->inc_use_count ();
1727 /* connect only once to DropReferences, even if sources are replicated
1730 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1731 unique_srcs.insert (*i);
1732 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1738 Region::can_trim () const
1740 CanTrim ct = CanTrim (0);
1746 /* if not locked, we can always move the front later, and the end earlier
1749 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1751 if (start() != 0 || can_trim_start_before_source_start ()) {
1752 ct = CanTrim (ct | FrontTrimEarlier);
1755 if (!_sources.empty()) {
1756 if ((start() + length()) < _sources.front()->length (0)) {
1757 ct = CanTrim (ct | EndTrimLater);
1765 Region::max_source_level () const
1769 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1770 lvl = max (lvl, (*i)->level());
1777 Region::is_compound () const
1779 return max_source_level() > 0;
1783 Region::post_set (const PropertyChange& pc)
1785 if (pc.contains (Properties::position)) {
1786 recompute_position_from_lock_style ();
1791 Region::set_start_internal (framecnt_t s)
1797 Region::earliest_possible_position () const
1799 if (_start > _position) {
1802 return _position - _start;
1807 Region::latest_possible_frame () const
1809 framecnt_t minlen = max_framecnt;
1811 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1812 /* non-audio regions have a length that may vary based on their
1813 * position, so we have to pass it in the call.
1815 minlen = min (minlen, (*i)->length (_position));
1818 /* the latest possible last frame is determined by the current
1819 * position, plus the shortest source extent past _start.
1822 return _position + (minlen - _start) - 1;