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) {
1202 node->add_property ("beat", str.str());
1205 for (uint32_t n=0; n < _sources.size(); ++n) {
1206 snprintf (buf2, sizeof(buf2), "source-%d", n);
1207 _sources[n]->id().print (buf, sizeof(buf));
1208 node->add_property (buf2, buf);
1211 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1212 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1213 _master_sources[n]->id().print (buf, sizeof (buf));
1214 node->add_property (buf2, buf);
1217 /* Only store nested sources for the whole-file region that acts
1218 as the parent/root of all regions using it.
1221 if (_whole_file && max_source_level() > 0) {
1223 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1225 /* region is compound - get its playlist and
1226 store that before we list the region that
1230 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1231 nested_node->add_child_nocopy ((*s)->get_state ());
1235 node->add_child_nocopy (*nested_node);
1240 node->add_child_copy (*_extra_xml);
1247 Region::get_state ()
1253 Region::set_state (const XMLNode& node, int version)
1255 PropertyChange what_changed;
1256 return _set_state (node, version, what_changed, true);
1260 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1262 XMLProperty const * prop;
1263 Timecode::BBT_Time bbt_time;
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;
1282 _beat = _session.tempo_map().bbt_to_beats (bbt_time);
1286 if (_position_lock_style == MusicTime) {
1287 if ((prop = node.property ("beat")) == 0) {
1288 /* missing BBT info, revert to audio time locking */
1289 _position_lock_style = AudioTime;
1291 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1292 _position_lock_style = AudioTime;
1297 /* fix problems with old sessions corrupted by impossible
1298 values for _stretch or _shift
1300 if (_stretch == 0.0f) {
1304 if (_shift == 0.0f) {
1309 send_change (what_changed);
1312 /* Quick fix for 2.x sessions when region is muted */
1313 if ((prop = node.property (X_("flags")))) {
1314 if (string::npos != prop->value().find("Muted")){
1319 // saved property is invalid, region-transients are not saved
1320 if (_user_transients.size() == 0){
1321 _valid_transients = false;
1328 Region::suspend_property_changes ()
1330 Stateful::suspend_property_changes ();
1331 _last_length = _length;
1332 _last_position = _position;
1336 Region::mid_thaw (const PropertyChange& what_changed)
1338 if (what_changed.contains (Properties::length)) {
1339 if (what_changed.contains (Properties::position)) {
1340 recompute_at_start ();
1342 recompute_at_end ();
1347 Region::send_change (const PropertyChange& what_changed)
1349 if (what_changed.empty()) {
1353 Stateful::send_change (what_changed);
1355 if (!Stateful::property_changes_suspended()) {
1357 /* Try and send a shared_pointer unless this is part of the constructor.
1362 boost::shared_ptr<Region> rptr = shared_from_this();
1363 RegionPropertyChanged (rptr, what_changed);
1365 /* no shared_ptr available, relax; */
1371 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1373 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1377 Region::equivalent (boost::shared_ptr<const Region> other) const
1379 return _start == other->_start &&
1380 _position == other->_position &&
1381 _length == other->_length;
1385 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1387 return _start == other->_start &&
1388 _length == other->_length;
1392 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1394 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1398 Region::source_deleted (boost::weak_ptr<Source>)
1402 if (!_session.deletion_in_progress()) {
1403 /* this is a very special case: at least one of the region's
1404 sources has bee deleted, so invalidate all references to
1405 ourselves. Do NOT do this during session deletion, because
1406 then we run the risk that this will actually result
1407 in this object being deleted (as refcnt goes to zero)
1408 while emitting DropReferences.
1416 Region::master_source_names ()
1418 SourceList::iterator i;
1420 vector<string> names;
1421 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1422 names.push_back((*i)->name());
1429 Region::set_master_sources (const SourceList& srcs)
1431 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1432 (*i)->dec_use_count ();
1435 _master_sources = srcs;
1436 assert (_sources.size() == _master_sources.size());
1438 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1439 (*i)->inc_use_count ();
1444 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1449 if ((_sources.size() != other->_sources.size()) ||
1450 (_master_sources.size() != other->_master_sources.size())) {
1454 SourceList::const_iterator i;
1455 SourceList::const_iterator io;
1457 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1458 if ((*i)->id() != (*io)->id()) {
1463 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1464 if ((*i)->id() != (*io)->id()) {
1473 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1479 SourceList::const_iterator i;
1480 SourceList::const_iterator io;
1482 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1483 if ((*i)->id() == (*io)->id()) {
1492 Region::source_string () const
1494 //string res = itos(_sources.size());
1497 res << _sources.size() << ":";
1499 SourceList::const_iterator i;
1501 for (i = _sources.begin(); i != _sources.end(); ++i) {
1502 res << (*i)->id() << ":";
1505 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1506 res << (*i)->id() << ":";
1513 Region::uses_source (boost::shared_ptr<const Source> source) const
1515 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1520 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1523 if (ps->playlist()->uses_source (source)) {
1529 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1534 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1537 if (ps->playlist()->uses_source (source)) {
1547 Region::source_length(uint32_t n) const
1549 assert (n < _sources.size());
1550 return _sources[n]->length (_position - _start);
1554 Region::verify_length (framecnt_t& len)
1556 if (source() && (source()->destructive() || source()->length_mutable())) {
1560 framecnt_t maxlen = 0;
1562 for (uint32_t n = 0; n < _sources.size(); ++n) {
1563 maxlen = max (maxlen, source_length(n) - _start);
1566 len = min (len, maxlen);
1572 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1574 if (source() && (source()->destructive() || source()->length_mutable())) {
1578 framecnt_t maxlen = 0;
1580 for (uint32_t n = 0; n < _sources.size(); ++n) {
1581 maxlen = max (maxlen, source_length(n) - new_start);
1584 new_length = min (new_length, maxlen);
1590 Region::verify_start (framepos_t pos)
1592 if (source() && (source()->destructive() || source()->length_mutable())) {
1596 for (uint32_t n = 0; n < _sources.size(); ++n) {
1597 if (pos > source_length(n) - _length) {
1605 Region::verify_start_mutable (framepos_t& new_start)
1607 if (source() && (source()->destructive() || source()->length_mutable())) {
1611 for (uint32_t n = 0; n < _sources.size(); ++n) {
1612 if (new_start > source_length(n) - _length) {
1613 new_start = source_length(n) - _length;
1619 boost::shared_ptr<Region>
1620 Region::get_parent() const
1622 boost::shared_ptr<Playlist> pl (playlist());
1625 boost::shared_ptr<Region> r;
1626 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1628 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1629 return boost::static_pointer_cast<Region> (r);
1633 return boost::shared_ptr<Region>();
1637 Region::apply (Filter& filter, Progress* progress)
1639 return filter.run (shared_from_this(), progress);
1644 Region::maybe_invalidate_transients ()
1646 bool changed = !_onsets.empty();
1649 if (_valid_transients || changed) {
1650 send_change (PropertyChange (Properties::valid_transients));
1656 Region::transients (AnalysisFeatureList& afl)
1658 int cnt = afl.empty() ? 0 : 1;
1660 Region::merge_features (afl, _onsets, _position);
1661 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1662 if (!_onsets.empty ()) {
1665 if (!_user_transients.empty ()) {
1670 // remove exact duplicates
1671 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1676 Region::has_transients () const
1678 if (!_user_transients.empty ()) {
1679 assert (_valid_transients);
1682 if (!_onsets.empty ()) {
1689 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1691 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1692 const frameoffset_t p = (*x) + off;
1693 if (p < first_frame() || p > last_frame()) {
1696 result.push_back (p);
1701 Region::drop_sources ()
1703 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1704 (*i)->dec_use_count ();
1709 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1710 (*i)->dec_use_count ();
1713 _master_sources.clear ();
1717 Region::use_sources (SourceList const & s)
1719 set<boost::shared_ptr<Source> > unique_srcs;
1721 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1723 _sources.push_back (*i);
1724 (*i)->inc_use_count ();
1725 _master_sources.push_back (*i);
1726 (*i)->inc_use_count ();
1728 /* connect only once to DropReferences, even if sources are replicated
1731 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1732 unique_srcs.insert (*i);
1733 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1739 Region::can_trim () const
1741 CanTrim ct = CanTrim (0);
1747 /* if not locked, we can always move the front later, and the end earlier
1750 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1752 if (start() != 0 || can_trim_start_before_source_start ()) {
1753 ct = CanTrim (ct | FrontTrimEarlier);
1756 if (!_sources.empty()) {
1757 if ((start() + length()) < _sources.front()->length (0)) {
1758 ct = CanTrim (ct | EndTrimLater);
1766 Region::max_source_level () const
1770 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1771 lvl = max (lvl, (*i)->level());
1778 Region::is_compound () const
1780 return max_source_level() > 0;
1784 Region::post_set (const PropertyChange& pc)
1786 if (pc.contains (Properties::position)) {
1787 recompute_position_from_lock_style ();
1792 Region::set_start_internal (framecnt_t s)
1798 Region::earliest_possible_position () const
1800 if (_start > _position) {
1803 return _position - _start;
1808 Region::latest_possible_frame () const
1810 framecnt_t minlen = max_framecnt;
1812 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1813 /* non-audio regions have a length that may vary based on their
1814 * position, so we have to pass it in the call.
1816 minlen = min (minlen, (*i)->length (_position));
1819 /* the latest possible last frame is determined by the current
1820 * position, plus the shortest source extent past _start.
1823 return _position + (minlen - _start) - 1;