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 ()
551 boost::shared_ptr<Playlist> pl (playlist());
553 if (!pl || _position_lock_style != MusicTime) {
556 TempoMap& map (_session.tempo_map());
557 framepos_t pos = 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.
563 send_change (Properties::position);
567 Region::set_position (framepos_t pos)
573 set_position_internal (pos, true);
575 /* do this even if the position is the same. this helps out
576 a GUI that has moved its representation already.
578 send_change (Properties::position);
582 /** A gui may need to create a region, then place it in an initial
583 * position determined by the user.
584 * When this takes place within one gui operation, we have to reset
585 * _last_position to prevent an implied move.
588 Region::set_initial_position (framepos_t pos)
594 if (_position != pos) {
597 /* check that the new _position wouldn't make the current
598 length impossible - if so, change the length.
600 XXX is this the right thing to do?
603 if (max_framepos - _length < _position) {
604 _last_length = _length;
605 _length = max_framepos - _position;
608 recompute_position_from_lock_style ();
609 /* ensure that this move doesn't cause a range move */
610 _last_position = _position;
614 /* do this even if the position is the same. this helps out
615 a GUI that has moved its representation already.
617 send_change (Properties::position);
621 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
623 /* We emit a change of Properties::position even if the position hasn't changed
624 (see Region::set_position), so we must always set this up so that
625 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
627 _last_position = _position;
629 if (_position != pos) {
632 /* check that the new _position wouldn't make the current
633 length impossible - if so, change the length.
635 XXX is this the right thing to do?
638 if (max_framepos - _length < _position) {
639 _last_length = _length;
640 _length = max_framepos - _position;
643 if (allow_bbt_recompute) {
644 recompute_position_from_lock_style ();
650 Region::recompute_position_from_lock_style ()
652 if (_position_lock_style == MusicTime) {
653 _beat = _session.tempo_map().beat_at_frame (_position);
658 Region::nudge_position (frameoffset_t n)
660 if (locked() || video_locked()) {
668 framepos_t new_position = _position;
671 if (_position > max_framepos - n) {
672 new_position = max_framepos;
677 if (_position < -n) {
684 set_position_internal (new_position, true);
686 send_change (Properties::position);
690 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
692 _ancestral_length = l;
693 _ancestral_start = s;
699 Region::set_start (framepos_t pos)
701 if (locked() || position_locked() || video_locked()) {
704 /* This just sets the start, nothing else. It effectively shifts
705 the contents of the Region within the overall extent of the Source,
706 without changing the Region's position or length
711 if (!verify_start (pos)) {
715 set_start_internal (pos);
718 maybe_invalidate_transients ();
720 send_change (Properties::start);
725 Region::move_start (frameoffset_t distance)
727 if (locked() || position_locked() || video_locked()) {
731 framepos_t new_start;
735 if (_start > max_framepos - distance) {
736 new_start = max_framepos; // makes no sense
738 new_start = _start + distance;
741 if (!verify_start (new_start)) {
745 } else if (distance < 0) {
747 if (_start < -distance) {
750 new_start = _start + distance;
757 if (new_start == _start) {
761 set_start_internal (new_start);
766 send_change (Properties::start);
770 Region::trim_front (framepos_t new_position)
772 modify_front (new_position, false);
776 Region::cut_front (framepos_t new_position)
778 modify_front (new_position, true);
782 Region::cut_end (framepos_t new_endpoint)
784 modify_end (new_endpoint, true);
788 Region::modify_front (framepos_t new_position, bool reset_fade)
794 framepos_t end = last_frame();
795 framepos_t source_zero;
797 if (_position > _start) {
798 source_zero = _position - _start;
800 source_zero = 0; // its actually negative, but this will work for us
803 if (new_position < end) { /* can't trim it zero or negative length */
805 framecnt_t newlen = 0;
807 if (!can_trim_start_before_source_start ()) {
808 /* can't trim it back past where source position zero is located */
809 new_position = max (new_position, source_zero);
812 if (new_position > _position) {
813 newlen = _length - (new_position - _position);
815 newlen = _length + (_position - new_position);
818 trim_to_internal (new_position, newlen);
821 _right_of_split = true;
824 if (!property_changes_suspended()) {
825 recompute_at_start ();
828 maybe_invalidate_transients ();
833 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
839 if (new_endpoint > _position) {
840 trim_to_internal (_position, new_endpoint - _position);
842 _left_of_split = true;
844 if (!property_changes_suspended()) {
850 /** @param new_endpoint New region end point, such that, for example,
851 * a region at 0 of length 10 has an endpoint of 9.
855 Region::trim_end (framepos_t new_endpoint)
857 modify_end (new_endpoint, false);
861 Region::trim_to (framepos_t position, framecnt_t length)
867 trim_to_internal (position, length);
869 if (!property_changes_suspended()) {
870 recompute_at_start ();
876 Region::trim_to_internal (framepos_t position, framecnt_t length)
878 framepos_t new_start;
884 frameoffset_t const start_shift = position - _position;
886 if (start_shift > 0) {
888 if (_start > max_framepos - start_shift) {
889 new_start = max_framepos;
891 new_start = _start + start_shift;
894 } else if (start_shift < 0) {
896 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
899 new_start = _start + start_shift;
906 if (!verify_start_and_length (new_start, length)) {
910 PropertyChange what_changed;
912 if (_start != new_start) {
913 set_start_internal (new_start);
914 what_changed.add (Properties::start);
917 /* Set position before length, otherwise for MIDI regions this bad thing happens:
918 * 1. we call set_length_internal; length in beats is computed using the region's current
919 * (soon-to-be old) position
920 * 2. we call set_position_internal; position is set and length in frames re-computed using
921 * length in beats from (1) but at the new position, which is wrong if the region
922 * straddles a tempo/meter change.
925 if (_position != position) {
926 if (!property_changes_suspended()) {
927 _last_position = _position;
929 set_position_internal (position, true);
930 what_changed.add (Properties::position);
933 if (_length != length) {
934 if (!property_changes_suspended()) {
935 _last_length = _length;
937 set_length_internal (length);
938 what_changed.add (Properties::length);
943 PropertyChange start_and_length;
945 start_and_length.add (Properties::start);
946 start_and_length.add (Properties::length);
948 if (what_changed.contains (start_and_length)) {
952 if (!what_changed.empty()) {
953 send_change (what_changed);
958 Region::set_hidden (bool yn)
960 if (hidden() != yn) {
962 send_change (Properties::hidden);
967 Region::set_whole_file (bool yn)
970 /* no change signal */
974 Region::set_automatic (bool yn)
977 /* no change signal */
981 Region::set_muted (bool yn)
985 send_change (Properties::muted);
990 Region::set_opaque (bool yn)
992 if (opaque() != yn) {
994 send_change (Properties::opaque);
999 Region::set_locked (bool yn)
1001 if (locked() != yn) {
1003 send_change (Properties::locked);
1008 Region::set_video_locked (bool yn)
1010 if (video_locked() != yn) {
1012 send_change (Properties::video_locked);
1017 Region::set_position_locked (bool yn)
1019 if (position_locked() != yn) {
1020 _position_locked = yn;
1021 send_change (Properties::locked);
1025 /** Set the region's sync point.
1026 * @param absolute_pos Session time.
1029 Region::set_sync_position (framepos_t absolute_pos)
1031 /* position within our file */
1032 framepos_t const file_pos = _start + (absolute_pos - _position);
1034 if (file_pos != _sync_position) {
1035 _sync_marked = true;
1036 _sync_position = file_pos;
1037 if (!property_changes_suspended()) {
1041 send_change (Properties::sync_position);
1046 Region::clear_sync_position ()
1048 if (sync_marked()) {
1049 _sync_marked = false;
1050 if (!property_changes_suspended()) {
1054 send_change (Properties::sync_position);
1058 /* @return the sync point relative the first frame of the region */
1060 Region::sync_offset (int& dir) const
1062 if (sync_marked()) {
1063 if (_sync_position > _start) {
1065 return _sync_position - _start;
1068 return _start - _sync_position;
1077 Region::adjust_to_sync (framepos_t pos) const
1080 frameoffset_t offset = sync_offset (sync_dir);
1082 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1091 if (max_framepos - pos > offset) {
1099 /** @return Sync position in session time */
1101 Region::sync_position() const
1103 if (sync_marked()) {
1104 return _position - _start + _sync_position;
1106 /* if sync has not been marked, use the start of the region */
1114 boost::shared_ptr<Playlist> pl (playlist());
1116 pl->raise_region (shared_from_this ());
1123 boost::shared_ptr<Playlist> pl (playlist());
1125 pl->lower_region (shared_from_this ());
1131 Region::raise_to_top ()
1133 boost::shared_ptr<Playlist> pl (playlist());
1135 pl->raise_region_to_top (shared_from_this());
1140 Region::lower_to_bottom ()
1142 boost::shared_ptr<Playlist> pl (playlist());
1144 pl->lower_region_to_bottom (shared_from_this());
1149 Region::set_layer (layer_t l)
1157 XMLNode *node = new XMLNode ("Region");
1161 const char* fe = NULL;
1163 /* custom version of 'add_properties (*node);'
1164 * skip values that have have dedicated save functions
1165 * in AudioRegion::state()
1167 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1168 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1169 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1170 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1171 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1172 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1173 i->second->get_value (*node);
1176 id().print (buf, sizeof (buf));
1177 node->add_property ("id", buf);
1178 node->add_property ("type", _type.to_string());
1180 switch (_first_edit) {
1181 case EditChangesNothing:
1184 case EditChangesName:
1190 default: /* should be unreachable but makes g++ happy */
1195 node->add_property ("first-edit", fe);
1197 /* note: flags are stored by derived classes */
1199 if (_position_lock_style != AudioTime) {
1200 snprintf (buf, sizeof(buf), "%lf", _beat);
1201 node->add_property ("beat", buf);
1204 for (uint32_t n=0; n < _sources.size(); ++n) {
1205 snprintf (buf2, sizeof(buf2), "source-%d", n);
1206 _sources[n]->id().print (buf, sizeof(buf));
1207 node->add_property (buf2, buf);
1210 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1211 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1212 _master_sources[n]->id().print (buf, sizeof (buf));
1213 node->add_property (buf2, buf);
1216 /* Only store nested sources for the whole-file region that acts
1217 as the parent/root of all regions using it.
1220 if (_whole_file && max_source_level() > 0) {
1222 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1224 /* region is compound - get its playlist and
1225 store that before we list the region that
1229 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1230 nested_node->add_child_nocopy ((*s)->get_state ());
1234 node->add_child_nocopy (*nested_node);
1239 node->add_child_copy (*_extra_xml);
1246 Region::get_state ()
1252 Region::set_state (const XMLNode& node, int version)
1254 PropertyChange what_changed;
1255 return _set_state (node, version, what_changed, true);
1259 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1261 XMLProperty const * prop;
1262 Timecode::BBT_Time bbt_time;
1264 Stateful::save_extra_xml (node);
1266 what_changed = set_values (node);
1270 if (_position_lock_style == MusicTime) {
1271 if ((prop = node.property ("bbt-position")) == 0) {
1272 if ((prop = node.property ("beat")) == 0) {
1273 /* missing BBT info, revert to audio time locking */
1274 _position_lock_style = AudioTime;
1276 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1277 _position_lock_style = AudioTime;
1282 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1285 &bbt_time.ticks) != 3) {
1286 _position_lock_style = AudioTime;
1288 _beat = _session.tempo_map().bbt_to_beats (bbt_time);
1293 /* fix problems with old sessions corrupted by impossible
1294 values for _stretch or _shift
1296 if (_stretch == 0.0f) {
1300 if (_shift == 0.0f) {
1305 send_change (what_changed);
1308 /* Quick fix for 2.x sessions when region is muted */
1309 if ((prop = node.property (X_("flags")))) {
1310 if (string::npos != prop->value().find("Muted")){
1315 // saved property is invalid, region-transients are not saved
1316 if (_user_transients.size() == 0){
1317 _valid_transients = false;
1324 Region::suspend_property_changes ()
1326 Stateful::suspend_property_changes ();
1327 _last_length = _length;
1328 _last_position = _position;
1332 Region::mid_thaw (const PropertyChange& what_changed)
1334 if (what_changed.contains (Properties::length)) {
1335 if (what_changed.contains (Properties::position)) {
1336 recompute_at_start ();
1338 recompute_at_end ();
1343 Region::send_change (const PropertyChange& what_changed)
1345 if (what_changed.empty()) {
1349 Stateful::send_change (what_changed);
1351 if (!Stateful::property_changes_suspended()) {
1353 /* Try and send a shared_pointer unless this is part of the constructor.
1358 boost::shared_ptr<Region> rptr = shared_from_this();
1359 RegionPropertyChanged (rptr, what_changed);
1361 /* no shared_ptr available, relax; */
1367 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1369 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1373 Region::equivalent (boost::shared_ptr<const Region> other) const
1375 return _start == other->_start &&
1376 _position == other->_position &&
1377 _length == other->_length;
1381 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1383 return _start == other->_start &&
1384 _length == other->_length;
1388 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1390 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1394 Region::source_deleted (boost::weak_ptr<Source>)
1398 if (!_session.deletion_in_progress()) {
1399 /* this is a very special case: at least one of the region's
1400 sources has bee deleted, so invalidate all references to
1401 ourselves. Do NOT do this during session deletion, because
1402 then we run the risk that this will actually result
1403 in this object being deleted (as refcnt goes to zero)
1404 while emitting DropReferences.
1412 Region::master_source_names ()
1414 SourceList::iterator i;
1416 vector<string> names;
1417 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1418 names.push_back((*i)->name());
1425 Region::set_master_sources (const SourceList& srcs)
1427 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1428 (*i)->dec_use_count ();
1431 _master_sources = srcs;
1432 assert (_sources.size() == _master_sources.size());
1434 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1435 (*i)->inc_use_count ();
1440 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1445 if ((_sources.size() != other->_sources.size()) ||
1446 (_master_sources.size() != other->_master_sources.size())) {
1450 SourceList::const_iterator i;
1451 SourceList::const_iterator io;
1453 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1454 if ((*i)->id() != (*io)->id()) {
1459 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1460 if ((*i)->id() != (*io)->id()) {
1469 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1475 SourceList::const_iterator i;
1476 SourceList::const_iterator io;
1478 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1479 if ((*i)->id() == (*io)->id()) {
1488 Region::source_string () const
1490 //string res = itos(_sources.size());
1493 res << _sources.size() << ":";
1495 SourceList::const_iterator i;
1497 for (i = _sources.begin(); i != _sources.end(); ++i) {
1498 res << (*i)->id() << ":";
1501 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1502 res << (*i)->id() << ":";
1509 Region::uses_source (boost::shared_ptr<const Source> source) const
1511 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1516 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1519 if (ps->playlist()->uses_source (source)) {
1525 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1530 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1533 if (ps->playlist()->uses_source (source)) {
1543 Region::source_length(uint32_t n) const
1545 assert (n < _sources.size());
1546 return _sources[n]->length (_position - _start);
1550 Region::verify_length (framecnt_t& len)
1552 if (source() && (source()->destructive() || source()->length_mutable())) {
1556 framecnt_t maxlen = 0;
1558 for (uint32_t n = 0; n < _sources.size(); ++n) {
1559 maxlen = max (maxlen, source_length(n) - _start);
1562 len = min (len, maxlen);
1568 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1570 if (source() && (source()->destructive() || source()->length_mutable())) {
1574 framecnt_t maxlen = 0;
1576 for (uint32_t n = 0; n < _sources.size(); ++n) {
1577 maxlen = max (maxlen, source_length(n) - new_start);
1580 new_length = min (new_length, maxlen);
1586 Region::verify_start (framepos_t pos)
1588 if (source() && (source()->destructive() || source()->length_mutable())) {
1592 for (uint32_t n = 0; n < _sources.size(); ++n) {
1593 if (pos > source_length(n) - _length) {
1601 Region::verify_start_mutable (framepos_t& new_start)
1603 if (source() && (source()->destructive() || source()->length_mutable())) {
1607 for (uint32_t n = 0; n < _sources.size(); ++n) {
1608 if (new_start > source_length(n) - _length) {
1609 new_start = source_length(n) - _length;
1615 boost::shared_ptr<Region>
1616 Region::get_parent() const
1618 boost::shared_ptr<Playlist> pl (playlist());
1621 boost::shared_ptr<Region> r;
1622 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1624 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1625 return boost::static_pointer_cast<Region> (r);
1629 return boost::shared_ptr<Region>();
1633 Region::apply (Filter& filter, Progress* progress)
1635 return filter.run (shared_from_this(), progress);
1640 Region::maybe_invalidate_transients ()
1642 bool changed = !_onsets.empty();
1645 if (_valid_transients || changed) {
1646 send_change (PropertyChange (Properties::valid_transients));
1652 Region::transients (AnalysisFeatureList& afl)
1654 int cnt = afl.empty() ? 0 : 1;
1656 Region::merge_features (afl, _onsets, _position);
1657 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1658 if (!_onsets.empty ()) {
1661 if (!_user_transients.empty ()) {
1666 // remove exact duplicates
1667 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1672 Region::has_transients () const
1674 if (!_user_transients.empty ()) {
1675 assert (_valid_transients);
1678 if (!_onsets.empty ()) {
1685 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1687 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1688 const frameoffset_t p = (*x) + off;
1689 if (p < first_frame() || p > last_frame()) {
1692 result.push_back (p);
1697 Region::drop_sources ()
1699 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1700 (*i)->dec_use_count ();
1705 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1706 (*i)->dec_use_count ();
1709 _master_sources.clear ();
1713 Region::use_sources (SourceList const & s)
1715 set<boost::shared_ptr<Source> > unique_srcs;
1717 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1719 _sources.push_back (*i);
1720 (*i)->inc_use_count ();
1721 _master_sources.push_back (*i);
1722 (*i)->inc_use_count ();
1724 /* connect only once to DropReferences, even if sources are replicated
1727 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1728 unique_srcs.insert (*i);
1729 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1735 Region::can_trim () const
1737 CanTrim ct = CanTrim (0);
1743 /* if not locked, we can always move the front later, and the end earlier
1746 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1748 if (start() != 0 || can_trim_start_before_source_start ()) {
1749 ct = CanTrim (ct | FrontTrimEarlier);
1752 if (!_sources.empty()) {
1753 if ((start() + length()) < _sources.front()->length (0)) {
1754 ct = CanTrim (ct | EndTrimLater);
1762 Region::max_source_level () const
1766 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1767 lvl = max (lvl, (*i)->level());
1774 Region::is_compound () const
1776 return max_source_level() > 0;
1780 Region::post_set (const PropertyChange& pc)
1782 if (pc.contains (Properties::position)) {
1783 recompute_position_from_lock_style ();
1788 Region::set_start_internal (framecnt_t s)
1794 Region::earliest_possible_position () const
1796 if (_start > _position) {
1799 return _position - _start;
1804 Region::latest_possible_frame () const
1806 framecnt_t minlen = max_framecnt;
1808 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1809 /* non-audio regions have a length that may vary based on their
1810 * position, so we have to pass it in the call.
1812 minlen = min (minlen, (*i)->length (_position));
1815 /* the latest possible last frame is determined by the current
1816 * position, plus the shortest source extent past _start.
1819 return _position + (minlen - _start) - 1;