2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<framecnt_t> sync_position;
68 PBD::PropertyDescriptor<layer_t> layer;
69 PBD::PropertyDescriptor<framepos_t> ancestral_start;
70 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
71 PBD::PropertyDescriptor<float> stretch;
72 PBD::PropertyDescriptor<float> shift;
73 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
74 PBD::PropertyDescriptor<uint64_t> layering_index;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
91 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
93 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
95 Properties::import.property_id = g_quark_from_static_string (X_("import"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
97 Properties::external.property_id = g_quark_from_static_string (X_("external"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
99 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
101 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
103 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
105 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
107 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
109 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
111 Properties::start.property_id = g_quark_from_static_string (X_("start"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
113 Properties::length.property_id = g_quark_from_static_string (X_("length"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
115 Properties::position.property_id = g_quark_from_static_string (X_("position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
117 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
119 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
121 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
123 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
125 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
127 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
129 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
136 Region::register_properties ()
138 _xml_node_name = X_("Region");
140 add_property (_muted);
141 add_property (_opaque);
142 add_property (_locked);
143 add_property (_video_locked);
144 add_property (_automatic);
145 add_property (_whole_file);
146 add_property (_import);
147 add_property (_external);
148 add_property (_sync_marked);
149 add_property (_left_of_split);
150 add_property (_right_of_split);
151 add_property (_hidden);
152 add_property (_position_locked);
153 add_property (_valid_transients);
154 add_property (_start);
155 add_property (_length);
156 add_property (_position);
157 add_property (_sync_position);
158 add_property (_ancestral_start);
159 add_property (_ancestral_length);
160 add_property (_stretch);
161 add_property (_shift);
162 add_property (_position_lock_style);
163 add_property (_layering_index);
166 #define REGION_DEFAULT_STATE(s,l) \
167 _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _valid_transients (Properties::valid_transients, false) \
171 , _start (Properties::start, (s)) \
172 , _length (Properties::length, (l)) \
173 , _position (Properties::position, 0) \
174 , _sync_position (Properties::sync_position, (s)) \
175 , _transient_user_start (0) \
176 , _transient_analysis_start (0) \
177 , _transient_analysis_end (0) \
178 , _muted (Properties::muted, false) \
179 , _opaque (Properties::opaque, true) \
180 , _locked (Properties::locked, false) \
181 , _video_locked (Properties::video_locked, false) \
182 , _automatic (Properties::automatic, false) \
183 , _whole_file (Properties::whole_file, false) \
184 , _import (Properties::import, false) \
185 , _external (Properties::external, false) \
186 , _hidden (Properties::hidden, false) \
187 , _position_locked (Properties::position_locked, false) \
188 , _ancestral_start (Properties::ancestral_start, (s)) \
189 , _ancestral_length (Properties::ancestral_length, (l)) \
190 , _stretch (Properties::stretch, 1.0) \
191 , _shift (Properties::shift, 1.0) \
192 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
193 , _layering_index (Properties::layering_index, 0)
195 #define REGION_COPY_STATE(other) \
196 _sync_marked (Properties::sync_marked, other->_sync_marked) \
197 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
198 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _user_transients (other->_user_transients) \
205 , _transient_user_start (other->_transient_user_start) \
206 , _transients (other->_transients) \
207 , _transient_analysis_start (other->_transient_analysis_start) \
208 , _transient_analysis_end (other->_transient_analysis_end) \
209 , _muted (Properties::muted, other->_muted) \
210 , _opaque (Properties::opaque, other->_opaque) \
211 , _locked (Properties::locked, other->_locked) \
212 , _video_locked (Properties::video_locked, other->_video_locked) \
213 , _automatic (Properties::automatic, other->_automatic) \
214 , _whole_file (Properties::whole_file, other->_whole_file) \
215 , _import (Properties::import, other->_import) \
216 , _external (Properties::external, other->_external) \
217 , _hidden (Properties::hidden, other->_hidden) \
218 , _position_locked (Properties::position_locked, other->_position_locked) \
219 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
220 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
221 , _stretch (Properties::stretch, other->_stretch) \
222 , _shift (Properties::shift, other->_shift) \
223 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
224 , _layering_index (Properties::layering_index, other->_layering_index)
226 /* derived-from-derived constructor (no sources in constructor) */
227 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
228 : SessionObject(s, name)
230 , REGION_DEFAULT_STATE(start,length)
231 , _last_length (length)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 /* no sources at this point */
241 /** Basic Region constructor (many sources) */
242 Region::Region (const SourceList& srcs)
243 : SessionObject(srcs.front()->session(), "toBeRenamed")
244 , _type (srcs.front()->type())
245 , REGION_DEFAULT_STATE(0,0)
248 , _first_edit (EditChangesNothing)
251 register_properties ();
253 _type = srcs.front()->type();
257 assert(_sources.size() > 0);
258 assert (_type == srcs.front()->type());
261 /** Create a new Region from an existing one */
262 Region::Region (boost::shared_ptr<const Region> other)
263 : SessionObject(other->session(), other->name())
264 , _type (other->data_type())
265 , REGION_COPY_STATE (other)
266 , _last_length (other->_last_length)
267 , _last_position(other->_last_position) \
268 , _first_edit (EditChangesNothing)
269 , _layer (other->_layer)
271 register_properties ();
273 /* override state that may have been incorrectly inherited from the other region
276 _position = other->_position;
281 use_sources (other->_sources);
282 set_master_sources (other->_master_sources);
284 _position_lock_style = other->_position_lock_style;
285 _first_edit = other->_first_edit;
287 _start = other->_start;
288 _beat = other->_beat;
290 /* sync pos is relative to start of file. our start-in-file is now zero,
291 so set our sync position to whatever the the difference between
292 _start and _sync_pos was in the other region.
294 result is that our new sync pos points to the same point in our source(s)
295 as the sync in the other region did in its source(s).
297 since we start at zero in our source(s), it is not possible to use a sync point that
298 is before the start. reset it to _start if that was true in the other region.
301 if (other->sync_marked()) {
302 if (other->_start < other->_sync_position) {
303 /* sync pos was after the start point of the other region */
304 _sync_position = other->_sync_position - other->_start;
306 /* sync pos was before the start point of the other region. not possible here. */
307 _sync_marked = false;
308 _sync_position = _start;
311 _sync_marked = false;
312 _sync_position = _start;
315 assert (_type == other->data_type());
318 /** Create a new Region from part of an existing one.
320 the start within \a other is given by \a offset
321 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
323 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
324 : SessionObject(other->session(), other->name())
325 , _type (other->data_type())
326 , REGION_COPY_STATE (other)
327 , _last_length (other->_last_length)
328 , _last_position(other->_last_position) \
329 , _first_edit (EditChangesNothing)
331 , _layer (other->_layer)
333 register_properties ();
335 /* override state that may have been incorrectly inherited from the other region
338 _position = other->_position + offset;
343 use_sources (other->_sources);
344 set_master_sources (other->_master_sources);
346 _start = other->_start + offset;
347 _beat = _session.tempo_map().beat_at_frame (_position);
349 /* if the other region had a distinct sync point
350 set, then continue to use it as best we can.
351 otherwise, reset sync point back to start.
354 if (other->sync_marked()) {
355 if (other->_sync_position < _start) {
356 _sync_marked = false;
357 _sync_position = _start;
359 _sync_position = other->_sync_position;
362 _sync_marked = false;
363 _sync_position = _start;
366 assert (_type == other->data_type());
369 /** Create a copy of @param other but with different sources. Used by filters */
370 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
371 : SessionObject (other->session(), other->name())
372 , _type (srcs.front()->type())
373 , REGION_COPY_STATE (other)
374 , _last_length (other->_last_length)
375 , _last_position (other->_last_position)
376 , _first_edit (EditChangesID)
377 , _layer (other->_layer)
379 register_properties ();
382 _position_locked = false;
384 other->_first_edit = EditChangesName;
386 if (other->_extra_xml) {
387 _extra_xml = new XMLNode (*other->_extra_xml);
393 assert(_sources.size() > 0);
398 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
403 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
405 _playlist = wpl.lock();
409 Region::set_name (const std::string& str)
412 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
413 assert(_name == str);
415 send_change (Properties::name);
422 Region::set_length (framecnt_t len)
424 //cerr << "Region::set_length() len = " << len << endl;
429 if (_length != len && len != 0) {
431 /* check that the current _position wouldn't make the new
435 if (max_framepos - len < _position) {
439 if (!verify_length (len)) {
444 set_length_internal (len);
448 maybe_invalidate_transients ();
450 if (!property_changes_suspended()) {
454 send_change (Properties::length);
459 Region::set_length_internal (framecnt_t len)
461 _last_length = _length;
466 Region::maybe_uncopy ()
468 /* this does nothing but marked a semantic moment once upon a time */
472 Region::first_edit ()
474 boost::shared_ptr<Playlist> pl (playlist());
476 if (_first_edit != EditChangesNothing && pl) {
478 _name = RegionFactory::new_region_name (_name);
479 _first_edit = EditChangesNothing;
481 send_change (Properties::name);
483 RegionFactory::CheckNewRegion (shared_from_this());
488 Region::at_natural_position () const
490 boost::shared_ptr<Playlist> pl (playlist());
496 boost::shared_ptr<Region> whole_file_region = get_parent();
498 if (whole_file_region) {
499 if (_position == whole_file_region->position() + _start) {
508 Region::move_to_natural_position ()
510 boost::shared_ptr<Playlist> pl (playlist());
516 boost::shared_ptr<Region> whole_file_region = get_parent();
518 if (whole_file_region) {
519 set_position (whole_file_region->position() + _start);
524 Region::special_set_position (framepos_t pos)
526 /* this is used when creating a whole file region as
527 a way to store its "natural" or "captured" position.
530 _position = _position;
535 Region::set_position_lock_style (PositionLockStyle ps)
537 if (_position_lock_style != ps) {
539 boost::shared_ptr<Playlist> pl (playlist());
541 _position_lock_style = ps;
543 if (_position_lock_style == MusicTime) {
544 _beat = _session.tempo_map().beat_at_frame (_position);
547 send_change (Properties::position_lock_style);
552 Region::update_after_tempo_map_change (bool send)
554 boost::shared_ptr<Playlist> pl (playlist());
556 if (!pl || _position_lock_style != MusicTime) {
560 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
561 set_position_internal (pos, false);
563 /* do this even if the position is the same. this helps out
564 a GUI that has moved its representation already.
568 send_change (Properties::position);
573 Region::set_position (framepos_t pos, int32_t sub_num)
580 set_position_internal (pos, true);
582 double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
584 set_position_internal (pos, false);
587 /* do this even if the position is the same. this helps out
588 a GUI that has moved its representation already.
590 PropertyChange p_and_l;
592 p_and_l.add (Properties::position);
593 /* Currently length change due to position change is only implemented
594 for MidiRegion (Region has no length in beats).
595 Notify a length change regardless (its more efficient for MidiRegions),
596 and when Region has a _length_beats we will need it here anyway).
598 if (position_lock_style() == MusicTime) {
599 p_and_l.add (Properties::length);
602 send_change (p_and_l);
606 /** A gui may need to create a region, then place it in an initial
607 * position determined by the user.
608 * When this takes place within one gui operation, we have to reset
609 * _last_position to prevent an implied move.
612 Region::set_initial_position (framepos_t pos)
618 if (_position != pos) {
621 /* check that the new _position wouldn't make the current
622 length impossible - if so, change the length.
624 XXX is this the right thing to do?
627 if (max_framepos - _length < _position) {
628 _last_length = _length;
629 _length = max_framepos - _position;
632 recompute_position_from_lock_style ();
633 /* ensure that this move doesn't cause a range move */
634 _last_position = _position;
638 /* do this even if the position is the same. this helps out
639 a GUI that has moved its representation already.
641 send_change (Properties::position);
645 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
647 /* We emit a change of Properties::position even if the position hasn't changed
648 (see Region::set_position), so we must always set this up so that
649 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
651 _last_position = _position;
653 if (_position != pos) {
656 if (allow_bbt_recompute) {
657 recompute_position_from_lock_style ();
659 /* check that the new _position wouldn't make the current
660 length impossible - if so, change the length.
662 XXX is this the right thing to do?
664 if (max_framepos - _length < _position) {
665 _last_length = _length;
666 _length = max_framepos - _position;
672 Region::recompute_position_from_lock_style ()
674 if (_position_lock_style == MusicTime) {
675 _beat = _session.tempo_map().beat_at_frame (_position);
680 Region::nudge_position (frameoffset_t n)
682 if (locked() || video_locked()) {
690 framepos_t new_position = _position;
693 if (_position > max_framepos - n) {
694 new_position = max_framepos;
699 if (_position < -n) {
706 set_position_internal (new_position, true);
708 send_change (Properties::position);
712 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
714 _ancestral_length = l;
715 _ancestral_start = s;
721 Region::set_start (framepos_t pos)
723 if (locked() || position_locked() || video_locked()) {
726 /* This just sets the start, nothing else. It effectively shifts
727 the contents of the Region within the overall extent of the Source,
728 without changing the Region's position or length
733 if (!verify_start (pos)) {
737 set_start_internal (pos);
740 maybe_invalidate_transients ();
742 send_change (Properties::start);
747 Region::move_start (frameoffset_t distance, const int32_t& sub_num)
749 if (locked() || position_locked() || video_locked()) {
753 framepos_t new_start;
757 if (_start > max_framepos - distance) {
758 new_start = max_framepos; // makes no sense
760 new_start = _start + distance;
763 if (!verify_start (new_start)) {
767 } else if (distance < 0) {
769 if (_start < -distance) {
772 new_start = _start + distance;
779 if (new_start == _start) {
783 set_start_internal (new_start, sub_num);
788 send_change (Properties::start);
792 Region::trim_front (framepos_t new_position, const int32_t& sub_num)
794 modify_front (new_position, false, sub_num);
798 Region::cut_front (framepos_t new_position, const int32_t& sub_num)
800 modify_front (new_position, true, sub_num);
804 Region::cut_end (framepos_t new_endpoint, const int32_t& sub_num)
806 modify_end (new_endpoint, true, sub_num);
810 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t& sub_num)
816 framepos_t end = last_frame();
817 framepos_t source_zero;
819 if (_position > _start) {
820 source_zero = _position - _start;
822 source_zero = 0; // its actually negative, but this will work for us
825 if (new_position < end) { /* can't trim it zero or negative length */
827 framecnt_t newlen = 0;
829 if (!can_trim_start_before_source_start ()) {
830 /* can't trim it back past where source position zero is located */
831 new_position = max (new_position, source_zero);
834 if (new_position > _position) {
835 newlen = _length - (new_position - _position);
837 newlen = _length + (_position - new_position);
840 trim_to_internal (new_position, newlen, sub_num);
843 _right_of_split = true;
846 if (!property_changes_suspended()) {
847 recompute_at_start ();
850 maybe_invalidate_transients ();
855 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t& sub_num)
861 if (new_endpoint > _position) {
862 trim_to_internal (_position, new_endpoint - _position, sub_num);
864 _left_of_split = true;
866 if (!property_changes_suspended()) {
872 /** @param new_endpoint New region end point, such that, for example,
873 * a region at 0 of length 10 has an endpoint of 9.
877 Region::trim_end (framepos_t new_endpoint, const int32_t& sub_num)
879 modify_end (new_endpoint, false, sub_num);
883 Region::trim_to (framepos_t position, framecnt_t length, const int32_t& sub_num)
889 trim_to_internal (position, length, sub_num);
891 if (!property_changes_suspended()) {
892 recompute_at_start ();
898 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t& sub_num)
900 framepos_t new_start;
906 frameoffset_t const start_shift = position - _position;
908 if (start_shift > 0) {
910 if (_start > max_framepos - start_shift) {
911 new_start = max_framepos;
913 new_start = _start + start_shift;
916 } else if (start_shift < 0) {
918 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
921 new_start = _start + start_shift;
928 if (!verify_start_and_length (new_start, length)) {
932 PropertyChange what_changed;
934 if (_start != new_start) {
935 set_start_internal (new_start, sub_num);
936 what_changed.add (Properties::start);
940 /* Set position before length, otherwise for MIDI regions this bad thing happens:
941 * 1. we call set_length_internal; length in beats is computed using the region's current
942 * (soon-to-be old) position
943 * 2. we call set_position_internal; position is set and length in frames re-computed using
944 * length in beats from (1) but at the new position, which is wrong if the region
945 * straddles a tempo/meter change.
948 if (_position != position) {
949 if (!property_changes_suspended()) {
950 _last_position = _position;
952 set_position_internal (position, true);
953 what_changed.add (Properties::position);
956 if (_length != length) {
957 if (!property_changes_suspended()) {
958 _last_length = _length;
960 set_length_internal (length);
961 what_changed.add (Properties::length);
966 PropertyChange start_and_length;
968 start_and_length.add (Properties::start);
969 start_and_length.add (Properties::length);
971 if (what_changed.contains (start_and_length)) {
975 if (!what_changed.empty()) {
976 send_change (what_changed);
981 Region::set_hidden (bool yn)
983 if (hidden() != yn) {
985 send_change (Properties::hidden);
990 Region::set_whole_file (bool yn)
993 /* no change signal */
997 Region::set_automatic (bool yn)
1000 /* no change signal */
1004 Region::set_muted (bool yn)
1006 if (muted() != yn) {
1008 send_change (Properties::muted);
1013 Region::set_opaque (bool yn)
1015 if (opaque() != yn) {
1017 send_change (Properties::opaque);
1022 Region::set_locked (bool yn)
1024 if (locked() != yn) {
1026 send_change (Properties::locked);
1031 Region::set_video_locked (bool yn)
1033 if (video_locked() != yn) {
1035 send_change (Properties::video_locked);
1040 Region::set_position_locked (bool yn)
1042 if (position_locked() != yn) {
1043 _position_locked = yn;
1044 send_change (Properties::locked);
1048 /** Set the region's sync point.
1049 * @param absolute_pos Session time.
1052 Region::set_sync_position (framepos_t absolute_pos)
1054 /* position within our file */
1055 framepos_t const file_pos = _start + (absolute_pos - _position);
1057 if (file_pos != _sync_position) {
1058 _sync_marked = true;
1059 _sync_position = file_pos;
1060 if (!property_changes_suspended()) {
1064 send_change (Properties::sync_position);
1069 Region::clear_sync_position ()
1071 if (sync_marked()) {
1072 _sync_marked = false;
1073 if (!property_changes_suspended()) {
1077 send_change (Properties::sync_position);
1081 /* @return the sync point relative the first frame of the region */
1083 Region::sync_offset (int& dir) const
1085 if (sync_marked()) {
1086 if (_sync_position > _start) {
1088 return _sync_position - _start;
1091 return _start - _sync_position;
1100 Region::adjust_to_sync (framepos_t pos) const
1103 frameoffset_t offset = sync_offset (sync_dir);
1105 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1114 if (max_framepos - pos > offset) {
1122 /** @return Sync position in session time */
1124 Region::sync_position() const
1126 if (sync_marked()) {
1127 return _position - _start + _sync_position;
1129 /* if sync has not been marked, use the start of the region */
1137 boost::shared_ptr<Playlist> pl (playlist());
1139 pl->raise_region (shared_from_this ());
1146 boost::shared_ptr<Playlist> pl (playlist());
1148 pl->lower_region (shared_from_this ());
1154 Region::raise_to_top ()
1156 boost::shared_ptr<Playlist> pl (playlist());
1158 pl->raise_region_to_top (shared_from_this());
1163 Region::lower_to_bottom ()
1165 boost::shared_ptr<Playlist> pl (playlist());
1167 pl->lower_region_to_bottom (shared_from_this());
1172 Region::set_layer (layer_t l)
1180 XMLNode *node = new XMLNode ("Region");
1184 const char* fe = NULL;
1186 /* custom version of 'add_properties (*node);'
1187 * skip values that have have dedicated save functions
1188 * in AudioRegion::state()
1190 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1191 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1192 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1193 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1194 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1195 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1196 i->second->get_value (*node);
1199 id().print (buf, sizeof (buf));
1200 node->add_property ("id", buf);
1201 node->add_property ("type", _type.to_string());
1203 switch (_first_edit) {
1204 case EditChangesNothing:
1207 case EditChangesName:
1213 default: /* should be unreachable but makes g++ happy */
1218 node->add_property ("first-edit", fe);
1220 /* note: flags are stored by derived classes */
1222 if (_position_lock_style != AudioTime) {
1223 snprintf (buf, sizeof(buf), "%lf", _beat);
1224 node->add_property ("beat", buf);
1227 for (uint32_t n=0; n < _sources.size(); ++n) {
1228 snprintf (buf2, sizeof(buf2), "source-%d", n);
1229 _sources[n]->id().print (buf, sizeof(buf));
1230 node->add_property (buf2, buf);
1233 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1234 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1235 _master_sources[n]->id().print (buf, sizeof (buf));
1236 node->add_property (buf2, buf);
1239 /* Only store nested sources for the whole-file region that acts
1240 as the parent/root of all regions using it.
1243 if (_whole_file && max_source_level() > 0) {
1245 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1247 /* region is compound - get its playlist and
1248 store that before we list the region that
1252 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1253 nested_node->add_child_nocopy ((*s)->get_state ());
1257 node->add_child_nocopy (*nested_node);
1262 node->add_child_copy (*_extra_xml);
1269 Region::get_state ()
1275 Region::set_state (const XMLNode& node, int version)
1277 PropertyChange what_changed;
1278 return _set_state (node, version, what_changed, true);
1282 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1284 XMLProperty const * prop;
1285 Timecode::BBT_Time bbt_time;
1287 Stateful::save_extra_xml (node);
1289 what_changed = set_values (node);
1293 if (_position_lock_style == MusicTime) {
1294 if ((prop = node.property ("bbt-position")) == 0) {
1295 if ((prop = node.property ("beat")) == 0) {
1296 /* missing BBT info, revert to audio time locking */
1297 _position_lock_style = AudioTime;
1299 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1300 _position_lock_style = AudioTime;
1305 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1308 &bbt_time.ticks) != 3) {
1309 _position_lock_style = AudioTime;
1311 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1316 /* fix problems with old sessions corrupted by impossible
1317 values for _stretch or _shift
1319 if (_stretch == 0.0f) {
1323 if (_shift == 0.0f) {
1328 send_change (what_changed);
1331 /* Quick fix for 2.x sessions when region is muted */
1332 if ((prop = node.property (X_("flags")))) {
1333 if (string::npos != prop->value().find("Muted")){
1338 // saved property is invalid, region-transients are not saved
1339 if (_user_transients.size() == 0){
1340 _valid_transients = false;
1347 Region::suspend_property_changes ()
1349 Stateful::suspend_property_changes ();
1350 _last_length = _length;
1351 _last_position = _position;
1355 Region::mid_thaw (const PropertyChange& what_changed)
1357 if (what_changed.contains (Properties::length)) {
1358 if (what_changed.contains (Properties::position)) {
1359 recompute_at_start ();
1361 recompute_at_end ();
1366 Region::send_change (const PropertyChange& what_changed)
1368 if (what_changed.empty()) {
1372 Stateful::send_change (what_changed);
1374 if (!Stateful::property_changes_suspended()) {
1376 /* Try and send a shared_pointer unless this is part of the constructor.
1381 boost::shared_ptr<Region> rptr = shared_from_this();
1382 RegionPropertyChanged (rptr, what_changed);
1384 /* no shared_ptr available, relax; */
1390 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1392 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1396 Region::equivalent (boost::shared_ptr<const Region> other) const
1398 return _start == other->_start &&
1399 _position == other->_position &&
1400 _length == other->_length;
1404 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1406 return _start == other->_start &&
1407 _length == other->_length;
1411 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1413 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1417 Region::source_deleted (boost::weak_ptr<Source>)
1421 if (!_session.deletion_in_progress()) {
1422 /* this is a very special case: at least one of the region's
1423 sources has bee deleted, so invalidate all references to
1424 ourselves. Do NOT do this during session deletion, because
1425 then we run the risk that this will actually result
1426 in this object being deleted (as refcnt goes to zero)
1427 while emitting DropReferences.
1435 Region::master_source_names ()
1437 SourceList::iterator i;
1439 vector<string> names;
1440 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1441 names.push_back((*i)->name());
1448 Region::set_master_sources (const SourceList& srcs)
1450 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1451 (*i)->dec_use_count ();
1454 _master_sources = srcs;
1455 assert (_sources.size() == _master_sources.size());
1457 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1458 (*i)->inc_use_count ();
1463 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1468 if ((_sources.size() != other->_sources.size()) ||
1469 (_master_sources.size() != other->_master_sources.size())) {
1473 SourceList::const_iterator i;
1474 SourceList::const_iterator io;
1476 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1477 if ((*i)->id() != (*io)->id()) {
1482 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1483 if ((*i)->id() != (*io)->id()) {
1492 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1498 SourceList::const_iterator i;
1499 SourceList::const_iterator io;
1501 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1502 if ((*i)->id() == (*io)->id()) {
1511 Region::source_string () const
1513 //string res = itos(_sources.size());
1516 res << _sources.size() << ":";
1518 SourceList::const_iterator i;
1520 for (i = _sources.begin(); i != _sources.end(); ++i) {
1521 res << (*i)->id() << ":";
1524 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1525 res << (*i)->id() << ":";
1532 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1534 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1536 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1539 if (sources.find (ps) == sources.end()) {
1540 /* (Playlist)Source not currently in
1541 accumulating set, so recurse.
1543 ps->playlist()->deep_sources (sources);
1547 /* add this source */
1548 sources.insert (*i);
1551 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1553 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1556 if (sources.find (ps) == sources.end()) {
1557 /* (Playlist)Source not currently in
1558 accumulating set, so recurse.
1560 ps->playlist()->deep_sources (sources);
1564 /* add this source */
1565 sources.insert (*i);
1570 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1572 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1578 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1581 if (ps->playlist()->uses_source (source)) {
1588 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1594 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1597 if (ps->playlist()->uses_source (source)) {
1609 Region::source_length(uint32_t n) const
1611 assert (n < _sources.size());
1612 return _sources[n]->length (_position - _start);
1616 Region::verify_length (framecnt_t& len)
1618 if (source() && (source()->destructive() || source()->length_mutable())) {
1622 framecnt_t maxlen = 0;
1624 for (uint32_t n = 0; n < _sources.size(); ++n) {
1625 maxlen = max (maxlen, source_length(n) - _start);
1628 len = min (len, maxlen);
1634 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1636 if (source() && (source()->destructive() || source()->length_mutable())) {
1640 framecnt_t maxlen = 0;
1642 for (uint32_t n = 0; n < _sources.size(); ++n) {
1643 maxlen = max (maxlen, source_length(n) - new_start);
1646 new_length = min (new_length, maxlen);
1652 Region::verify_start (framepos_t pos)
1654 if (source() && (source()->destructive() || source()->length_mutable())) {
1658 for (uint32_t n = 0; n < _sources.size(); ++n) {
1659 if (pos > source_length(n) - _length) {
1667 Region::verify_start_mutable (framepos_t& new_start)
1669 if (source() && (source()->destructive() || source()->length_mutable())) {
1673 for (uint32_t n = 0; n < _sources.size(); ++n) {
1674 if (new_start > source_length(n) - _length) {
1675 new_start = source_length(n) - _length;
1681 boost::shared_ptr<Region>
1682 Region::get_parent() const
1684 boost::shared_ptr<Playlist> pl (playlist());
1687 boost::shared_ptr<Region> r;
1688 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1690 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1691 return boost::static_pointer_cast<Region> (r);
1695 return boost::shared_ptr<Region>();
1699 Region::apply (Filter& filter, Progress* progress)
1701 return filter.run (shared_from_this(), progress);
1706 Region::maybe_invalidate_transients ()
1708 bool changed = !_onsets.empty();
1711 if (_valid_transients || changed) {
1712 send_change (PropertyChange (Properties::valid_transients));
1718 Region::transients (AnalysisFeatureList& afl)
1720 int cnt = afl.empty() ? 0 : 1;
1722 Region::merge_features (afl, _onsets, _position);
1723 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1724 if (!_onsets.empty ()) {
1727 if (!_user_transients.empty ()) {
1732 // remove exact duplicates
1733 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1738 Region::has_transients () const
1740 if (!_user_transients.empty ()) {
1741 assert (_valid_transients);
1744 if (!_onsets.empty ()) {
1751 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1753 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1754 const frameoffset_t p = (*x) + off;
1755 if (p < first_frame() || p > last_frame()) {
1758 result.push_back (p);
1763 Region::drop_sources ()
1765 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1766 (*i)->dec_use_count ();
1771 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1772 (*i)->dec_use_count ();
1775 _master_sources.clear ();
1779 Region::use_sources (SourceList const & s)
1781 set<boost::shared_ptr<Source> > unique_srcs;
1783 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1785 _sources.push_back (*i);
1786 (*i)->inc_use_count ();
1787 _master_sources.push_back (*i);
1788 (*i)->inc_use_count ();
1790 /* connect only once to DropReferences, even if sources are replicated
1793 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1794 unique_srcs.insert (*i);
1795 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1801 Region::can_trim () const
1803 CanTrim ct = CanTrim (0);
1809 /* if not locked, we can always move the front later, and the end earlier
1812 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1814 if (start() != 0 || can_trim_start_before_source_start ()) {
1815 ct = CanTrim (ct | FrontTrimEarlier);
1818 if (!_sources.empty()) {
1819 if ((start() + length()) < _sources.front()->length (0)) {
1820 ct = CanTrim (ct | EndTrimLater);
1828 Region::max_source_level () const
1832 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1833 lvl = max (lvl, (*i)->level());
1840 Region::is_compound () const
1842 return max_source_level() > 0;
1846 Region::post_set (const PropertyChange& pc)
1848 if (pc.contains (Properties::position)) {
1849 recompute_position_from_lock_style ();
1854 Region::set_start_internal (framecnt_t s, const int32_t& sub_num)
1860 Region::earliest_possible_position () const
1862 if (_start > _position) {
1865 return _position - _start;
1870 Region::latest_possible_frame () const
1872 framecnt_t minlen = max_framecnt;
1874 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1875 /* non-audio regions have a length that may vary based on their
1876 * position, so we have to pass it in the call.
1878 minlen = min (minlen, (*i)->length (_position));
1881 /* the latest possible last frame is determined by the current
1882 * position, plus the shortest source extent past _start.
1885 return _position + (minlen - _start) - 1;