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<double> beat;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75 PBD::PropertyDescriptor<uint64_t> layering_index;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
92 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
94 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
96 Properties::import.property_id = g_quark_from_static_string (X_("import"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
98 Properties::external.property_id = g_quark_from_static_string (X_("external"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
100 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
102 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
104 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
106 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
108 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
110 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
112 Properties::start.property_id = g_quark_from_static_string (X_("start"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
114 Properties::length.property_id = g_quark_from_static_string (X_("length"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
116 Properties::position.property_id = g_quark_from_static_string (X_("position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
118 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
139 Region::register_properties ()
141 _xml_node_name = X_("Region");
143 add_property (_muted);
144 add_property (_opaque);
145 add_property (_locked);
146 add_property (_video_locked);
147 add_property (_automatic);
148 add_property (_whole_file);
149 add_property (_import);
150 add_property (_external);
151 add_property (_sync_marked);
152 add_property (_left_of_split);
153 add_property (_right_of_split);
154 add_property (_hidden);
155 add_property (_position_locked);
156 add_property (_valid_transients);
157 add_property (_start);
158 add_property (_length);
159 add_property (_position);
160 add_property (_beat);
161 add_property (_sync_position);
162 add_property (_ancestral_start);
163 add_property (_ancestral_length);
164 add_property (_stretch);
165 add_property (_shift);
166 add_property (_position_lock_style);
167 add_property (_layering_index);
170 #define REGION_DEFAULT_STATE(s,l) \
171 _sync_marked (Properties::sync_marked, false) \
172 , _left_of_split (Properties::left_of_split, false) \
173 , _right_of_split (Properties::right_of_split, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _beat (Properties::beat, 0.0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _transient_user_start (0) \
181 , _transient_analysis_start (0) \
182 , _transient_analysis_end (0) \
183 , _muted (Properties::muted, false) \
184 , _opaque (Properties::opaque, true) \
185 , _locked (Properties::locked, false) \
186 , _video_locked (Properties::video_locked, false) \
187 , _automatic (Properties::automatic, false) \
188 , _whole_file (Properties::whole_file, false) \
189 , _import (Properties::import, false) \
190 , _external (Properties::external, false) \
191 , _hidden (Properties::hidden, false) \
192 , _position_locked (Properties::position_locked, false) \
193 , _ancestral_start (Properties::ancestral_start, (s)) \
194 , _ancestral_length (Properties::ancestral_length, (l)) \
195 , _stretch (Properties::stretch, 1.0) \
196 , _shift (Properties::shift, 1.0) \
197 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
198 , _layering_index (Properties::layering_index, 0)
200 #define REGION_COPY_STATE(other) \
201 _sync_marked (Properties::sync_marked, other->_sync_marked) \
202 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
203 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
204 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
205 , _start(Properties::start, other->_start) \
206 , _length(Properties::length, other->_length) \
207 , _position(Properties::position, other->_position) \
208 , _beat (Properties::beat, other->_beat) \
209 , _sync_position(Properties::sync_position, other->_sync_position) \
210 , _user_transients (other->_user_transients) \
211 , _transient_user_start (other->_transient_user_start) \
212 , _transients (other->_transients) \
213 , _transient_analysis_start (other->_transient_analysis_start) \
214 , _transient_analysis_end (other->_transient_analysis_end) \
215 , _muted (Properties::muted, other->_muted) \
216 , _opaque (Properties::opaque, other->_opaque) \
217 , _locked (Properties::locked, other->_locked) \
218 , _video_locked (Properties::video_locked, other->_video_locked) \
219 , _automatic (Properties::automatic, other->_automatic) \
220 , _whole_file (Properties::whole_file, other->_whole_file) \
221 , _import (Properties::import, other->_import) \
222 , _external (Properties::external, other->_external) \
223 , _hidden (Properties::hidden, other->_hidden) \
224 , _position_locked (Properties::position_locked, other->_position_locked) \
225 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
226 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
227 , _stretch (Properties::stretch, other->_stretch) \
228 , _shift (Properties::shift, other->_shift) \
229 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
230 , _layering_index (Properties::layering_index, other->_layering_index)
232 /* derived-from-derived constructor (no sources in constructor) */
233 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
234 : SessionObject(s, name)
236 , REGION_DEFAULT_STATE(start,length)
237 , _last_length (length)
239 , _first_edit (EditChangesNothing)
242 register_properties ();
244 /* no sources at this point */
247 /** Basic Region constructor (many sources) */
248 Region::Region (const SourceList& srcs)
249 : SessionObject(srcs.front()->session(), "toBeRenamed")
250 , _type (srcs.front()->type())
251 , REGION_DEFAULT_STATE(0,0)
254 , _first_edit (EditChangesNothing)
257 register_properties ();
259 _type = srcs.front()->type();
263 assert(_sources.size() > 0);
264 assert (_type == srcs.front()->type());
267 /** Create a new Region from an existing one */
268 Region::Region (boost::shared_ptr<const Region> other)
269 : SessionObject(other->session(), other->name())
270 , _type (other->data_type())
271 , REGION_COPY_STATE (other)
272 , _last_length (other->_last_length)
273 , _last_position(other->_last_position) \
274 , _first_edit (EditChangesNothing)
275 , _layer (other->_layer)
277 register_properties ();
279 /* override state that may have been incorrectly inherited from the other region
282 _position = other->_position;
287 use_sources (other->_sources);
288 set_master_sources (other->_master_sources);
290 _position_lock_style = other->_position_lock_style;
291 _first_edit = other->_first_edit;
293 _start = other->_start;
294 _beat = other->_beat;
296 /* sync pos is relative to start of file. our start-in-file is now zero,
297 so set our sync position to whatever the the difference between
298 _start and _sync_pos was in the other region.
300 result is that our new sync pos points to the same point in our source(s)
301 as the sync in the other region did in its source(s).
303 since we start at zero in our source(s), it is not possible to use a sync point that
304 is before the start. reset it to _start if that was true in the other region.
307 if (other->sync_marked()) {
308 if (other->_start < other->_sync_position) {
309 /* sync pos was after the start point of the other region */
310 _sync_position = other->_sync_position - other->_start;
312 /* sync pos was before the start point of the other region. not possible here. */
313 _sync_marked = false;
314 _sync_position = _start;
317 _sync_marked = false;
318 _sync_position = _start;
321 assert (_type == other->data_type());
324 /** Create a new Region from part of an existing one.
326 the start within \a other is given by \a offset
327 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
329 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
330 : SessionObject(other->session(), other->name())
331 , _type (other->data_type())
332 , REGION_COPY_STATE (other)
333 , _last_length (other->_last_length)
334 , _last_position(other->_last_position) \
335 , _first_edit (EditChangesNothing)
336 , _layer (other->_layer)
338 register_properties ();
340 /* override state that may have been incorrectly inherited from the other region
343 _position = other->_position + offset;
348 use_sources (other->_sources);
349 set_master_sources (other->_master_sources);
351 _start = other->_start + offset;
352 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
354 /* if the other region had a distinct sync point
355 set, then continue to use it as best we can.
356 otherwise, reset sync point back to start.
359 if (other->sync_marked()) {
360 if (other->_sync_position < _start) {
361 _sync_marked = false;
362 _sync_position = _start;
364 _sync_position = other->_sync_position;
367 _sync_marked = false;
368 _sync_position = _start;
371 assert (_type == other->data_type());
374 /** Create a copy of @param other but with different sources. Used by filters */
375 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
376 : SessionObject (other->session(), other->name())
377 , _type (srcs.front()->type())
378 , REGION_COPY_STATE (other)
379 , _last_length (other->_last_length)
380 , _last_position (other->_last_position)
381 , _first_edit (EditChangesID)
382 , _layer (other->_layer)
384 register_properties ();
387 _position_locked = false;
389 other->_first_edit = EditChangesName;
391 if (other->_extra_xml) {
392 _extra_xml = new XMLNode (*other->_extra_xml);
398 assert(_sources.size() > 0);
403 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
408 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
410 _playlist = wpl.lock();
414 Region::set_name (const std::string& str)
417 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
418 assert(_name == str);
420 send_change (Properties::name);
427 Region::set_length (framecnt_t len, const int32_t sub_num)
429 //cerr << "Region::set_length() len = " << len << endl;
434 if (_length != len && len != 0) {
436 /* check that the current _position wouldn't make the new
440 if (max_framepos - len < _position) {
444 if (!verify_length (len)) {
449 set_length_internal (len, sub_num);
453 maybe_invalidate_transients ();
455 if (!property_changes_suspended()) {
459 send_change (Properties::length);
464 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
466 _last_length = _length;
471 Region::maybe_uncopy ()
473 /* this does nothing but marked a semantic moment once upon a time */
477 Region::first_edit ()
479 boost::shared_ptr<Playlist> pl (playlist());
481 if (_first_edit != EditChangesNothing && pl) {
483 _name = RegionFactory::new_region_name (_name);
484 _first_edit = EditChangesNothing;
486 send_change (Properties::name);
488 RegionFactory::CheckNewRegion (shared_from_this());
493 Region::at_natural_position () const
495 boost::shared_ptr<Playlist> pl (playlist());
501 boost::shared_ptr<Region> whole_file_region = get_parent();
503 if (whole_file_region) {
504 if (_position == whole_file_region->position() + _start) {
513 Region::move_to_natural_position ()
515 boost::shared_ptr<Playlist> pl (playlist());
521 boost::shared_ptr<Region> whole_file_region = get_parent();
523 if (whole_file_region) {
524 set_position (whole_file_region->position() + _start);
529 Region::special_set_position (framepos_t pos)
531 /* this is used when creating a whole file region as
532 a way to store its "natural" or "captured" position.
535 _position = _position;
540 Region::set_position_lock_style (PositionLockStyle ps)
542 if (_position_lock_style != ps) {
544 boost::shared_ptr<Playlist> pl (playlist());
546 _position_lock_style = ps;
548 if (_position_lock_style == MusicTime) {
549 _beat = _session.tempo_map().beat_at_frame (_position);
552 send_change (Properties::position_lock_style);
557 Region::update_after_tempo_map_change (bool send)
559 boost::shared_ptr<Playlist> pl (playlist());
565 if (_position_lock_style == AudioTime) {
566 /* don't signal as the actual position has not chnged */
567 recompute_position_from_lock_style (0);
571 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
572 /* we have _beat. update frame position non-musically */
573 set_position_internal (pos, false, 0);
575 /* do this even if the position is the same. this helps out
576 a GUI that has moved its representation already.
580 send_change (Properties::position);
585 Region::set_position (framepos_t pos, int32_t sub_num)
592 set_position_internal (pos, true, 0);
594 double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
596 set_position_internal (pos, false, sub_num);
599 /* do this even if the position is the same. this helps out
600 a GUI that has moved its representation already.
602 PropertyChange p_and_l;
604 p_and_l.add (Properties::position);
605 /* Currently length change due to position change is only implemented
606 for MidiRegion (Region has no length in beats).
607 Notify a length change regardless (its more efficient for MidiRegions),
608 and when Region has a _length_beats we will need it here anyway).
610 p_and_l.add (Properties::length);
612 send_change (p_and_l);
616 /** A gui may need to create a region, then place it in an initial
617 * position determined by the user.
618 * When this takes place within one gui operation, we have to reset
619 * _last_position to prevent an implied move.
622 Region::set_initial_position (framepos_t pos)
628 if (_position != pos) {
631 /* check that the new _position wouldn't make the current
632 length impossible - if so, change the length.
634 XXX is this the right thing to do?
637 if (max_framepos - _length < _position) {
638 _last_length = _length;
639 _length = max_framepos - _position;
642 recompute_position_from_lock_style (0);
643 /* ensure that this move doesn't cause a range move */
644 _last_position = _position;
648 /* do this even if the position is the same. this helps out
649 a GUI that has moved its representation already.
651 send_change (Properties::position);
655 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
657 /* We emit a change of Properties::position even if the position hasn't changed
658 (see Region::set_position), so we must always set this up so that
659 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
661 _last_position = _position;
663 if (_position != pos) {
666 if (allow_bbt_recompute) {
667 recompute_position_from_lock_style (sub_num);
669 /* check that the new _position wouldn't make the current
670 length impossible - if so, change the length.
672 XXX is this the right thing to do?
674 if (max_framepos - _length < _position) {
675 _last_length = _length;
676 _length = max_framepos - _position;
682 Region::recompute_position_from_lock_style (const int32_t sub_num)
684 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
688 Region::nudge_position (frameoffset_t n)
690 if (locked() || video_locked()) {
698 framepos_t new_position = _position;
701 if (_position > max_framepos - n) {
702 new_position = max_framepos;
707 if (_position < -n) {
713 /* assumes non-musical nudge */
714 set_position_internal (new_position, true, 0);
716 send_change (Properties::position);
720 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
722 _ancestral_length = l;
723 _ancestral_start = s;
729 Region::set_start (framepos_t pos)
731 if (locked() || position_locked() || video_locked()) {
734 /* This just sets the start, nothing else. It effectively shifts
735 the contents of the Region within the overall extent of the Source,
736 without changing the Region's position or length
741 if (!verify_start (pos)) {
745 set_start_internal (pos);
748 maybe_invalidate_transients ();
750 send_change (Properties::start);
755 Region::move_start (frameoffset_t distance, const int32_t sub_num)
757 if (locked() || position_locked() || video_locked()) {
761 framepos_t new_start;
765 if (_start > max_framepos - distance) {
766 new_start = max_framepos; // makes no sense
768 new_start = _start + distance;
771 if (!verify_start (new_start)) {
775 } else if (distance < 0) {
777 if (_start < -distance) {
780 new_start = _start + distance;
787 if (new_start == _start) {
791 set_start_internal (new_start, sub_num);
796 send_change (Properties::start);
800 Region::trim_front (framepos_t new_position, const int32_t sub_num)
802 modify_front (new_position, false, sub_num);
806 Region::cut_front (framepos_t new_position, const int32_t sub_num)
808 modify_front (new_position, true, sub_num);
812 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
814 modify_end (new_endpoint, true, sub_num);
818 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
824 framepos_t end = last_frame();
825 framepos_t source_zero;
827 if (_position > _start) {
828 source_zero = _position - _start;
830 source_zero = 0; // its actually negative, but this will work for us
833 if (new_position < end) { /* can't trim it zero or negative length */
835 framecnt_t newlen = 0;
837 if (!can_trim_start_before_source_start ()) {
838 /* can't trim it back past where source position zero is located */
839 new_position = max (new_position, source_zero);
842 if (new_position > _position) {
843 newlen = _length - (new_position - _position);
845 newlen = _length + (_position - new_position);
848 trim_to_internal (new_position, newlen, sub_num);
851 _right_of_split = true;
854 if (!property_changes_suspended()) {
855 recompute_at_start ();
858 maybe_invalidate_transients ();
863 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
869 if (new_endpoint > _position) {
870 trim_to_internal (_position, new_endpoint - _position, sub_num);
872 _left_of_split = true;
874 if (!property_changes_suspended()) {
880 /** @param new_endpoint New region end point, such that, for example,
881 * a region at 0 of length 10 has an endpoint of 9.
885 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
887 modify_end (new_endpoint, false, sub_num);
891 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
897 trim_to_internal (position, length, sub_num);
899 if (!property_changes_suspended()) {
900 recompute_at_start ();
906 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
908 framepos_t new_start;
914 frameoffset_t const start_shift = position - _position;
916 if (start_shift > 0) {
918 if (_start > max_framepos - start_shift) {
919 new_start = max_framepos;
921 new_start = _start + start_shift;
924 } else if (start_shift < 0) {
926 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
929 new_start = _start + start_shift;
936 if (!verify_start_and_length (new_start, length)) {
940 PropertyChange what_changed;
942 if (_start != new_start) {
943 set_start_internal (new_start, sub_num);
944 what_changed.add (Properties::start);
948 /* Set position before length, otherwise for MIDI regions this bad thing happens:
949 * 1. we call set_length_internal; length in beats is computed using the region's current
950 * (soon-to-be old) position
951 * 2. we call set_position_internal; position is set and length in frames re-computed using
952 * length in beats from (1) but at the new position, which is wrong if the region
953 * straddles a tempo/meter change.
956 if (_position != position) {
957 if (!property_changes_suspended()) {
958 _last_position = _position;
960 set_position_internal (position, true, sub_num);
961 what_changed.add (Properties::position);
964 if (_length != length) {
965 if (!property_changes_suspended()) {
966 _last_length = _length;
968 set_length_internal (length, sub_num);
969 what_changed.add (Properties::length);
974 PropertyChange start_and_length;
976 start_and_length.add (Properties::start);
977 start_and_length.add (Properties::length);
979 if (what_changed.contains (start_and_length)) {
983 if (!what_changed.empty()) {
984 send_change (what_changed);
989 Region::set_hidden (bool yn)
991 if (hidden() != yn) {
993 send_change (Properties::hidden);
998 Region::set_whole_file (bool yn)
1001 /* no change signal */
1005 Region::set_automatic (bool yn)
1008 /* no change signal */
1012 Region::set_muted (bool yn)
1014 if (muted() != yn) {
1016 send_change (Properties::muted);
1021 Region::set_opaque (bool yn)
1023 if (opaque() != yn) {
1025 send_change (Properties::opaque);
1030 Region::set_locked (bool yn)
1032 if (locked() != yn) {
1034 send_change (Properties::locked);
1039 Region::set_video_locked (bool yn)
1041 if (video_locked() != yn) {
1043 send_change (Properties::video_locked);
1048 Region::set_position_locked (bool yn)
1050 if (position_locked() != yn) {
1051 _position_locked = yn;
1052 send_change (Properties::locked);
1056 /** Set the region's sync point.
1057 * @param absolute_pos Session time.
1060 Region::set_sync_position (framepos_t absolute_pos)
1062 /* position within our file */
1063 framepos_t const file_pos = _start + (absolute_pos - _position);
1065 if (file_pos != _sync_position) {
1066 _sync_marked = true;
1067 _sync_position = file_pos;
1068 if (!property_changes_suspended()) {
1072 send_change (Properties::sync_position);
1077 Region::clear_sync_position ()
1079 if (sync_marked()) {
1080 _sync_marked = false;
1081 if (!property_changes_suspended()) {
1085 send_change (Properties::sync_position);
1089 /* @return the sync point relative the first frame of the region */
1091 Region::sync_offset (int& dir) const
1093 if (sync_marked()) {
1094 if (_sync_position > _start) {
1096 return _sync_position - _start;
1099 return _start - _sync_position;
1108 Region::adjust_to_sync (framepos_t pos) const
1111 frameoffset_t offset = sync_offset (sync_dir);
1113 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1122 if (max_framepos - pos > offset) {
1130 /** @return Sync position in session time */
1132 Region::sync_position() const
1134 if (sync_marked()) {
1135 return _position - _start + _sync_position;
1137 /* if sync has not been marked, use the start of the region */
1145 boost::shared_ptr<Playlist> pl (playlist());
1147 pl->raise_region (shared_from_this ());
1154 boost::shared_ptr<Playlist> pl (playlist());
1156 pl->lower_region (shared_from_this ());
1162 Region::raise_to_top ()
1164 boost::shared_ptr<Playlist> pl (playlist());
1166 pl->raise_region_to_top (shared_from_this());
1171 Region::lower_to_bottom ()
1173 boost::shared_ptr<Playlist> pl (playlist());
1175 pl->lower_region_to_bottom (shared_from_this());
1180 Region::set_layer (layer_t l)
1188 XMLNode *node = new XMLNode ("Region");
1192 const char* fe = NULL;
1194 /* custom version of 'add_properties (*node);'
1195 * skip values that have have dedicated save functions
1196 * in AudioRegion::state()
1198 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1199 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1200 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1201 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1202 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1203 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1204 i->second->get_value (*node);
1207 id().print (buf, sizeof (buf));
1208 node->add_property ("id", buf);
1209 node->add_property ("type", _type.to_string());
1211 switch (_first_edit) {
1212 case EditChangesNothing:
1215 case EditChangesName:
1221 default: /* should be unreachable but makes g++ happy */
1226 node->add_property ("first-edit", fe);
1228 /* note: flags are stored by derived classes */
1230 for (uint32_t n=0; n < _sources.size(); ++n) {
1231 snprintf (buf2, sizeof(buf2), "source-%d", n);
1232 _sources[n]->id().print (buf, sizeof(buf));
1233 node->add_property (buf2, buf);
1236 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1237 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1238 _master_sources[n]->id().print (buf, sizeof (buf));
1239 node->add_property (buf2, buf);
1242 /* Only store nested sources for the whole-file region that acts
1243 as the parent/root of all regions using it.
1246 if (_whole_file && max_source_level() > 0) {
1248 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1250 /* region is compound - get its playlist and
1251 store that before we list the region that
1255 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1256 nested_node->add_child_nocopy ((*s)->get_state ());
1260 node->add_child_nocopy (*nested_node);
1265 node->add_child_copy (*_extra_xml);
1272 Region::get_state ()
1278 Region::set_state (const XMLNode& node, int version)
1280 PropertyChange what_changed;
1281 return _set_state (node, version, what_changed, true);
1285 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1287 XMLProperty const * prop;
1288 Timecode::BBT_Time bbt_time;
1290 Stateful::save_extra_xml (node);
1292 what_changed = set_values (node);
1296 if (_position_lock_style == MusicTime) {
1297 if ((prop = node.property ("bbt-position")) != 0) {
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::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1527 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1529 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1532 if (sources.find (ps) == sources.end()) {
1533 /* (Playlist)Source not currently in
1534 accumulating set, so recurse.
1536 ps->playlist()->deep_sources (sources);
1540 /* add this source */
1541 sources.insert (*i);
1544 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 (sources.find (ps) == sources.end()) {
1550 /* (Playlist)Source not currently in
1551 accumulating set, so recurse.
1553 ps->playlist()->deep_sources (sources);
1557 /* add this source */
1558 sources.insert (*i);
1563 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1565 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1571 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1574 if (ps->playlist()->uses_source (source)) {
1581 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1587 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1590 if (ps->playlist()->uses_source (source)) {
1602 Region::source_length(uint32_t n) const
1604 assert (n < _sources.size());
1605 return _sources[n]->length (_position - _start);
1609 Region::verify_length (framecnt_t& len)
1611 if (source() && (source()->destructive() || source()->length_mutable())) {
1615 framecnt_t maxlen = 0;
1617 for (uint32_t n = 0; n < _sources.size(); ++n) {
1618 maxlen = max (maxlen, source_length(n) - _start);
1621 len = min (len, maxlen);
1627 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1629 if (source() && (source()->destructive() || source()->length_mutable())) {
1633 framecnt_t maxlen = 0;
1635 for (uint32_t n = 0; n < _sources.size(); ++n) {
1636 maxlen = max (maxlen, source_length(n) - new_start);
1639 new_length = min (new_length, maxlen);
1645 Region::verify_start (framepos_t pos)
1647 if (source() && (source()->destructive() || source()->length_mutable())) {
1651 for (uint32_t n = 0; n < _sources.size(); ++n) {
1652 if (pos > source_length(n) - _length) {
1660 Region::verify_start_mutable (framepos_t& new_start)
1662 if (source() && (source()->destructive() || source()->length_mutable())) {
1666 for (uint32_t n = 0; n < _sources.size(); ++n) {
1667 if (new_start > source_length(n) - _length) {
1668 new_start = source_length(n) - _length;
1674 boost::shared_ptr<Region>
1675 Region::get_parent() const
1677 boost::shared_ptr<Playlist> pl (playlist());
1680 boost::shared_ptr<Region> r;
1681 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1683 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1684 return boost::static_pointer_cast<Region> (r);
1688 return boost::shared_ptr<Region>();
1692 Region::apply (Filter& filter, Progress* progress)
1694 return filter.run (shared_from_this(), progress);
1699 Region::maybe_invalidate_transients ()
1701 bool changed = !_onsets.empty();
1704 if (_valid_transients || changed) {
1705 send_change (PropertyChange (Properties::valid_transients));
1711 Region::transients (AnalysisFeatureList& afl)
1713 int cnt = afl.empty() ? 0 : 1;
1715 Region::merge_features (afl, _onsets, _position);
1716 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1717 if (!_onsets.empty ()) {
1720 if (!_user_transients.empty ()) {
1725 // remove exact duplicates
1726 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1731 Region::has_transients () const
1733 if (!_user_transients.empty ()) {
1734 assert (_valid_transients);
1737 if (!_onsets.empty ()) {
1744 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1746 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1747 const frameoffset_t p = (*x) + off;
1748 if (p < first_frame() || p > last_frame()) {
1751 result.push_back (p);
1756 Region::drop_sources ()
1758 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1759 (*i)->dec_use_count ();
1764 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1765 (*i)->dec_use_count ();
1768 _master_sources.clear ();
1772 Region::use_sources (SourceList const & s)
1774 set<boost::shared_ptr<Source> > unique_srcs;
1776 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1778 _sources.push_back (*i);
1779 (*i)->inc_use_count ();
1780 _master_sources.push_back (*i);
1781 (*i)->inc_use_count ();
1783 /* connect only once to DropReferences, even if sources are replicated
1786 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1787 unique_srcs.insert (*i);
1788 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1794 Region::can_trim () const
1796 CanTrim ct = CanTrim (0);
1802 /* if not locked, we can always move the front later, and the end earlier
1805 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1807 if (start() != 0 || can_trim_start_before_source_start ()) {
1808 ct = CanTrim (ct | FrontTrimEarlier);
1811 if (!_sources.empty()) {
1812 if ((start() + length()) < _sources.front()->length (0)) {
1813 ct = CanTrim (ct | EndTrimLater);
1821 Region::max_source_level () const
1825 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1826 lvl = max (lvl, (*i)->level());
1833 Region::is_compound () const
1835 return max_source_level() > 0;
1839 Region::post_set (const PropertyChange& pc)
1841 if (pc.contains (Properties::position)) {
1842 recompute_position_from_lock_style (0);
1847 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1853 Region::earliest_possible_position () const
1855 if (_start > _position) {
1858 return _position - _start;
1863 Region::latest_possible_frame () const
1865 framecnt_t minlen = max_framecnt;
1867 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1868 /* non-audio regions have a length that may vary based on their
1869 * position, so we have to pass it in the call.
1871 minlen = min (minlen, (*i)->length (_position));
1874 /* the latest possible last frame is determined by the current
1875 * position, plus the shortest source extent past _start.
1878 return _position + (minlen - _start) - 1;