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 _last_length = _length;
445 set_length_internal (len);
449 maybe_invalidate_transients ();
451 if (!property_changes_suspended()) {
455 send_change (Properties::length);
460 Region::set_length_internal (framecnt_t len)
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)
579 set_position_internal (pos, true);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
584 PropertyChange p_and_l;
586 p_and_l.add (Properties::position);
587 /* Currently length change due to position change is only implemented
588 for MidiRegion (Region has no length in beats).
589 Notify a length change regardless (its more efficient for MidiRegions),
590 and when Region has a _length_beats we will need it here anyway).
592 if (position_lock_style() == MusicTime) {
593 p_and_l.add (Properties::length);
596 send_change (p_and_l);
600 /** A gui may need to create a region, then place it in an initial
601 * position determined by the user.
602 * When this takes place within one gui operation, we have to reset
603 * _last_position to prevent an implied move.
606 Region::set_initial_position (framepos_t pos)
612 if (_position != pos) {
615 /* check that the new _position wouldn't make the current
616 length impossible - if so, change the length.
618 XXX is this the right thing to do?
621 if (max_framepos - _length < _position) {
622 _last_length = _length;
623 _length = max_framepos - _position;
626 recompute_position_from_lock_style ();
627 /* ensure that this move doesn't cause a range move */
628 _last_position = _position;
632 /* do this even if the position is the same. this helps out
633 a GUI that has moved its representation already.
635 send_change (Properties::position);
639 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
641 /* We emit a change of Properties::position even if the position hasn't changed
642 (see Region::set_position), so we must always set this up so that
643 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
645 _last_position = _position;
647 if (_position != pos) {
650 if (allow_bbt_recompute) {
651 recompute_position_from_lock_style ();
653 /* check that the new _position wouldn't make the current
654 length impossible - if so, change the length.
656 XXX is this the right thing to do?
658 if (max_framepos - _length < _position) {
659 _last_length = _length;
660 _length = max_framepos - _position;
666 Region::recompute_position_from_lock_style ()
668 if (_position_lock_style == MusicTime) {
669 _beat = _session.tempo_map().beat_at_frame (_position);
674 Region::nudge_position (frameoffset_t n)
676 if (locked() || video_locked()) {
684 framepos_t new_position = _position;
687 if (_position > max_framepos - n) {
688 new_position = max_framepos;
693 if (_position < -n) {
700 set_position_internal (new_position, true);
702 send_change (Properties::position);
706 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
708 _ancestral_length = l;
709 _ancestral_start = s;
715 Region::set_start (framepos_t pos)
717 if (locked() || position_locked() || video_locked()) {
720 /* This just sets the start, nothing else. It effectively shifts
721 the contents of the Region within the overall extent of the Source,
722 without changing the Region's position or length
727 if (!verify_start (pos)) {
731 set_start_internal (pos);
734 maybe_invalidate_transients ();
736 send_change (Properties::start);
741 Region::move_start (frameoffset_t distance)
743 if (locked() || position_locked() || video_locked()) {
747 framepos_t new_start;
751 if (_start > max_framepos - distance) {
752 new_start = max_framepos; // makes no sense
754 new_start = _start + distance;
757 if (!verify_start (new_start)) {
761 } else if (distance < 0) {
763 if (_start < -distance) {
766 new_start = _start + distance;
773 if (new_start == _start) {
777 set_start_internal (new_start);
782 send_change (Properties::start);
786 Region::trim_front (framepos_t new_position)
788 modify_front (new_position, false);
792 Region::cut_front (framepos_t new_position)
794 modify_front (new_position, true);
798 Region::cut_end (framepos_t new_endpoint)
800 modify_end (new_endpoint, true);
804 Region::modify_front (framepos_t new_position, bool reset_fade)
810 framepos_t end = last_frame();
811 framepos_t source_zero;
813 if (_position > _start) {
814 source_zero = _position - _start;
816 source_zero = 0; // its actually negative, but this will work for us
819 if (new_position < end) { /* can't trim it zero or negative length */
821 framecnt_t newlen = 0;
823 if (!can_trim_start_before_source_start ()) {
824 /* can't trim it back past where source position zero is located */
825 new_position = max (new_position, source_zero);
828 if (new_position > _position) {
829 newlen = _length - (new_position - _position);
831 newlen = _length + (_position - new_position);
834 trim_to_internal (new_position, newlen);
837 _right_of_split = true;
840 if (!property_changes_suspended()) {
841 recompute_at_start ();
844 maybe_invalidate_transients ();
849 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
855 if (new_endpoint > _position) {
856 trim_to_internal (_position, new_endpoint - _position);
858 _left_of_split = true;
860 if (!property_changes_suspended()) {
866 /** @param new_endpoint New region end point, such that, for example,
867 * a region at 0 of length 10 has an endpoint of 9.
871 Region::trim_end (framepos_t new_endpoint)
873 modify_end (new_endpoint, false);
877 Region::trim_to (framepos_t position, framecnt_t length)
883 trim_to_internal (position, length);
885 if (!property_changes_suspended()) {
886 recompute_at_start ();
892 Region::trim_to_internal (framepos_t position, framecnt_t length)
894 framepos_t new_start;
900 frameoffset_t const start_shift = position - _position;
902 if (start_shift > 0) {
904 if (_start > max_framepos - start_shift) {
905 new_start = max_framepos;
907 new_start = _start + start_shift;
910 } else if (start_shift < 0) {
912 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
915 new_start = _start + start_shift;
922 if (!verify_start_and_length (new_start, length)) {
926 PropertyChange what_changed;
928 /* Set position before length, otherwise for MIDI regions this bad thing happens:
929 * 1. we call set_length_internal; length in beats is computed using the region's current
930 * (soon-to-be old) position
931 * 2. we call set_position_internal; position is set and length in frames re-computed using
932 * length in beats from (1) but at the new position, which is wrong if the region
933 * straddles a tempo/meter change.
936 if (_position != position) {
937 if (!property_changes_suspended()) {
938 _last_position = _position;
940 set_position_internal (position, true);
941 what_changed.add (Properties::position);
944 if (_start != new_start) {
945 set_start_internal (new_start);
946 what_changed.add (Properties::start);
949 if (_length != length) {
950 if (!property_changes_suspended()) {
951 _last_length = _length;
953 set_length_internal (length);
954 what_changed.add (Properties::length);
959 PropertyChange start_and_length;
961 start_and_length.add (Properties::start);
962 start_and_length.add (Properties::length);
964 if (what_changed.contains (start_and_length)) {
968 if (!what_changed.empty()) {
969 send_change (what_changed);
974 Region::set_hidden (bool yn)
976 if (hidden() != yn) {
978 send_change (Properties::hidden);
983 Region::set_whole_file (bool yn)
986 /* no change signal */
990 Region::set_automatic (bool yn)
993 /* no change signal */
997 Region::set_muted (bool yn)
1001 send_change (Properties::muted);
1006 Region::set_opaque (bool yn)
1008 if (opaque() != yn) {
1010 send_change (Properties::opaque);
1015 Region::set_locked (bool yn)
1017 if (locked() != yn) {
1019 send_change (Properties::locked);
1024 Region::set_video_locked (bool yn)
1026 if (video_locked() != yn) {
1028 send_change (Properties::video_locked);
1033 Region::set_position_locked (bool yn)
1035 if (position_locked() != yn) {
1036 _position_locked = yn;
1037 send_change (Properties::locked);
1041 /** Set the region's sync point.
1042 * @param absolute_pos Session time.
1045 Region::set_sync_position (framepos_t absolute_pos)
1047 /* position within our file */
1048 framepos_t const file_pos = _start + (absolute_pos - _position);
1050 if (file_pos != _sync_position) {
1051 _sync_marked = true;
1052 _sync_position = file_pos;
1053 if (!property_changes_suspended()) {
1057 send_change (Properties::sync_position);
1062 Region::clear_sync_position ()
1064 if (sync_marked()) {
1065 _sync_marked = false;
1066 if (!property_changes_suspended()) {
1070 send_change (Properties::sync_position);
1074 /* @return the sync point relative the first frame of the region */
1076 Region::sync_offset (int& dir) const
1078 if (sync_marked()) {
1079 if (_sync_position > _start) {
1081 return _sync_position - _start;
1084 return _start - _sync_position;
1093 Region::adjust_to_sync (framepos_t pos) const
1096 frameoffset_t offset = sync_offset (sync_dir);
1098 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1107 if (max_framepos - pos > offset) {
1115 /** @return Sync position in session time */
1117 Region::sync_position() const
1119 if (sync_marked()) {
1120 return _position - _start + _sync_position;
1122 /* if sync has not been marked, use the start of the region */
1130 boost::shared_ptr<Playlist> pl (playlist());
1132 pl->raise_region (shared_from_this ());
1139 boost::shared_ptr<Playlist> pl (playlist());
1141 pl->lower_region (shared_from_this ());
1147 Region::raise_to_top ()
1149 boost::shared_ptr<Playlist> pl (playlist());
1151 pl->raise_region_to_top (shared_from_this());
1156 Region::lower_to_bottom ()
1158 boost::shared_ptr<Playlist> pl (playlist());
1160 pl->lower_region_to_bottom (shared_from_this());
1165 Region::set_layer (layer_t l)
1173 XMLNode *node = new XMLNode ("Region");
1177 const char* fe = NULL;
1179 /* custom version of 'add_properties (*node);'
1180 * skip values that have have dedicated save functions
1181 * in AudioRegion::state()
1183 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1184 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1185 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1186 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1187 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1188 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1189 i->second->get_value (*node);
1192 id().print (buf, sizeof (buf));
1193 node->add_property ("id", buf);
1194 node->add_property ("type", _type.to_string());
1196 switch (_first_edit) {
1197 case EditChangesNothing:
1200 case EditChangesName:
1206 default: /* should be unreachable but makes g++ happy */
1211 node->add_property ("first-edit", fe);
1213 /* note: flags are stored by derived classes */
1215 if (_position_lock_style != AudioTime) {
1216 snprintf (buf, sizeof(buf), "%lf", _beat);
1217 node->add_property ("beat", buf);
1220 for (uint32_t n=0; n < _sources.size(); ++n) {
1221 snprintf (buf2, sizeof(buf2), "source-%d", n);
1222 _sources[n]->id().print (buf, sizeof(buf));
1223 node->add_property (buf2, buf);
1226 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1227 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1228 _master_sources[n]->id().print (buf, sizeof (buf));
1229 node->add_property (buf2, buf);
1232 /* Only store nested sources for the whole-file region that acts
1233 as the parent/root of all regions using it.
1236 if (_whole_file && max_source_level() > 0) {
1238 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1240 /* region is compound - get its playlist and
1241 store that before we list the region that
1245 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1246 nested_node->add_child_nocopy ((*s)->get_state ());
1250 node->add_child_nocopy (*nested_node);
1255 node->add_child_copy (*_extra_xml);
1262 Region::get_state ()
1268 Region::set_state (const XMLNode& node, int version)
1270 PropertyChange what_changed;
1271 return _set_state (node, version, what_changed, true);
1275 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1277 XMLProperty const * prop;
1278 Timecode::BBT_Time bbt_time;
1280 Stateful::save_extra_xml (node);
1282 what_changed = set_values (node);
1286 if (_position_lock_style == MusicTime) {
1287 if ((prop = node.property ("bbt-position")) == 0) {
1288 if ((prop = node.property ("beat")) == 0) {
1289 /* missing BBT info, revert to audio time locking */
1290 _position_lock_style = AudioTime;
1292 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1293 _position_lock_style = AudioTime;
1298 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1301 &bbt_time.ticks) != 3) {
1302 _position_lock_style = AudioTime;
1304 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1309 /* fix problems with old sessions corrupted by impossible
1310 values for _stretch or _shift
1312 if (_stretch == 0.0f) {
1316 if (_shift == 0.0f) {
1321 send_change (what_changed);
1324 /* Quick fix for 2.x sessions when region is muted */
1325 if ((prop = node.property (X_("flags")))) {
1326 if (string::npos != prop->value().find("Muted")){
1331 // saved property is invalid, region-transients are not saved
1332 if (_user_transients.size() == 0){
1333 _valid_transients = false;
1340 Region::suspend_property_changes ()
1342 Stateful::suspend_property_changes ();
1343 _last_length = _length;
1344 _last_position = _position;
1348 Region::mid_thaw (const PropertyChange& what_changed)
1350 if (what_changed.contains (Properties::length)) {
1351 if (what_changed.contains (Properties::position)) {
1352 recompute_at_start ();
1354 recompute_at_end ();
1359 Region::send_change (const PropertyChange& what_changed)
1361 if (what_changed.empty()) {
1365 Stateful::send_change (what_changed);
1367 if (!Stateful::property_changes_suspended()) {
1369 /* Try and send a shared_pointer unless this is part of the constructor.
1374 boost::shared_ptr<Region> rptr = shared_from_this();
1375 RegionPropertyChanged (rptr, what_changed);
1377 /* no shared_ptr available, relax; */
1383 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1385 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1389 Region::equivalent (boost::shared_ptr<const Region> other) const
1391 return _start == other->_start &&
1392 _position == other->_position &&
1393 _length == other->_length;
1397 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1399 return _start == other->_start &&
1400 _length == other->_length;
1404 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1406 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1410 Region::source_deleted (boost::weak_ptr<Source>)
1414 if (!_session.deletion_in_progress()) {
1415 /* this is a very special case: at least one of the region's
1416 sources has bee deleted, so invalidate all references to
1417 ourselves. Do NOT do this during session deletion, because
1418 then we run the risk that this will actually result
1419 in this object being deleted (as refcnt goes to zero)
1420 while emitting DropReferences.
1428 Region::master_source_names ()
1430 SourceList::iterator i;
1432 vector<string> names;
1433 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1434 names.push_back((*i)->name());
1441 Region::set_master_sources (const SourceList& srcs)
1443 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1444 (*i)->dec_use_count ();
1447 _master_sources = srcs;
1448 assert (_sources.size() == _master_sources.size());
1450 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1451 (*i)->inc_use_count ();
1456 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1461 if ((_sources.size() != other->_sources.size()) ||
1462 (_master_sources.size() != other->_master_sources.size())) {
1466 SourceList::const_iterator i;
1467 SourceList::const_iterator io;
1469 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1470 if ((*i)->id() != (*io)->id()) {
1475 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1476 if ((*i)->id() != (*io)->id()) {
1485 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1491 SourceList::const_iterator i;
1492 SourceList::const_iterator io;
1494 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1495 if ((*i)->id() == (*io)->id()) {
1504 Region::source_string () const
1506 //string res = itos(_sources.size());
1509 res << _sources.size() << ":";
1511 SourceList::const_iterator i;
1513 for (i = _sources.begin(); i != _sources.end(); ++i) {
1514 res << (*i)->id() << ":";
1517 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1518 res << (*i)->id() << ":";
1525 Region::uses_source (boost::shared_ptr<const Source> source) const
1527 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1532 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1535 if (ps->playlist()->uses_source (source)) {
1541 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1546 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1549 if (ps->playlist()->uses_source (source)) {
1559 Region::source_length(uint32_t n) const
1561 assert (n < _sources.size());
1562 return _sources[n]->length (_position - _start);
1566 Region::verify_length (framecnt_t& len)
1568 if (source() && (source()->destructive() || source()->length_mutable())) {
1572 framecnt_t maxlen = 0;
1574 for (uint32_t n = 0; n < _sources.size(); ++n) {
1575 maxlen = max (maxlen, source_length(n) - _start);
1578 len = min (len, maxlen);
1584 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1586 if (source() && (source()->destructive() || source()->length_mutable())) {
1590 framecnt_t maxlen = 0;
1592 for (uint32_t n = 0; n < _sources.size(); ++n) {
1593 maxlen = max (maxlen, source_length(n) - new_start);
1596 new_length = min (new_length, maxlen);
1602 Region::verify_start (framepos_t pos)
1604 if (source() && (source()->destructive() || source()->length_mutable())) {
1608 for (uint32_t n = 0; n < _sources.size(); ++n) {
1609 if (pos > source_length(n) - _length) {
1617 Region::verify_start_mutable (framepos_t& new_start)
1619 if (source() && (source()->destructive() || source()->length_mutable())) {
1623 for (uint32_t n = 0; n < _sources.size(); ++n) {
1624 if (new_start > source_length(n) - _length) {
1625 new_start = source_length(n) - _length;
1631 boost::shared_ptr<Region>
1632 Region::get_parent() const
1634 boost::shared_ptr<Playlist> pl (playlist());
1637 boost::shared_ptr<Region> r;
1638 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1640 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1641 return boost::static_pointer_cast<Region> (r);
1645 return boost::shared_ptr<Region>();
1649 Region::apply (Filter& filter, Progress* progress)
1651 return filter.run (shared_from_this(), progress);
1656 Region::maybe_invalidate_transients ()
1658 bool changed = !_onsets.empty();
1661 if (_valid_transients || changed) {
1662 send_change (PropertyChange (Properties::valid_transients));
1668 Region::transients (AnalysisFeatureList& afl)
1670 int cnt = afl.empty() ? 0 : 1;
1672 Region::merge_features (afl, _onsets, _position);
1673 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1674 if (!_onsets.empty ()) {
1677 if (!_user_transients.empty ()) {
1682 // remove exact duplicates
1683 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1688 Region::has_transients () const
1690 if (!_user_transients.empty ()) {
1691 assert (_valid_transients);
1694 if (!_onsets.empty ()) {
1701 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1703 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1704 const frameoffset_t p = (*x) + off;
1705 if (p < first_frame() || p > last_frame()) {
1708 result.push_back (p);
1713 Region::drop_sources ()
1715 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1716 (*i)->dec_use_count ();
1721 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1722 (*i)->dec_use_count ();
1725 _master_sources.clear ();
1729 Region::use_sources (SourceList const & s)
1731 set<boost::shared_ptr<Source> > unique_srcs;
1733 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1735 _sources.push_back (*i);
1736 (*i)->inc_use_count ();
1737 _master_sources.push_back (*i);
1738 (*i)->inc_use_count ();
1740 /* connect only once to DropReferences, even if sources are replicated
1743 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1744 unique_srcs.insert (*i);
1745 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1751 Region::can_trim () const
1753 CanTrim ct = CanTrim (0);
1759 /* if not locked, we can always move the front later, and the end earlier
1762 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1764 if (start() != 0 || can_trim_start_before_source_start ()) {
1765 ct = CanTrim (ct | FrontTrimEarlier);
1768 if (!_sources.empty()) {
1769 if ((start() + length()) < _sources.front()->length (0)) {
1770 ct = CanTrim (ct | EndTrimLater);
1778 Region::max_source_level () const
1782 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1783 lvl = max (lvl, (*i)->level());
1790 Region::is_compound () const
1792 return max_source_level() > 0;
1796 Region::post_set (const PropertyChange& pc)
1798 if (pc.contains (Properties::position)) {
1799 recompute_position_from_lock_style ();
1804 Region::set_start_internal (framecnt_t s)
1810 Region::earliest_possible_position () const
1812 if (_start > _position) {
1815 return _position - _start;
1820 Region::latest_possible_frame () const
1822 framecnt_t minlen = max_framecnt;
1824 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1825 /* non-audio regions have a length that may vary based on their
1826 * position, so we have to pass it in the call.
1828 minlen = min (minlen, (*i)->length (_position));
1831 /* the latest possible last frame is determined by the current
1832 * position, plus the shortest source extent past _start.
1835 return _position + (minlen - _start) - 1;