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)) \
181 , _transient_user_start (0) \
182 , _transient_analysis_start (0) \
183 , _transient_analysis_end (0) \
184 , _muted (Properties::muted, false) \
185 , _opaque (Properties::opaque, true) \
186 , _locked (Properties::locked, false) \
187 , _video_locked (Properties::video_locked, false) \
188 , _automatic (Properties::automatic, false) \
189 , _whole_file (Properties::whole_file, false) \
190 , _import (Properties::import, false) \
191 , _external (Properties::external, false) \
192 , _hidden (Properties::hidden, false) \
193 , _position_locked (Properties::position_locked, false) \
194 , _ancestral_start (Properties::ancestral_start, (s)) \
195 , _ancestral_length (Properties::ancestral_length, (l)) \
196 , _stretch (Properties::stretch, 1.0) \
197 , _shift (Properties::shift, 1.0) \
198 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
199 , _layering_index (Properties::layering_index, 0)
201 #define REGION_COPY_STATE(other) \
202 _sync_marked (Properties::sync_marked, other->_sync_marked) \
203 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
204 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
205 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
206 , _start(Properties::start, other->_start) \
207 , _length(Properties::length, other->_length) \
208 , _position(Properties::position, other->_position) \
209 , _beat (Properties::beat, other->_beat) \
210 , _sync_position(Properties::sync_position, other->_sync_position) \
211 , _pos_beats (other->_pos_beats) \
212 , _user_transients (other->_user_transients) \
213 , _transient_user_start (other->_transient_user_start) \
214 , _transients (other->_transients) \
215 , _transient_analysis_start (other->_transient_analysis_start) \
216 , _transient_analysis_end (other->_transient_analysis_end) \
217 , _muted (Properties::muted, other->_muted) \
218 , _opaque (Properties::opaque, other->_opaque) \
219 , _locked (Properties::locked, other->_locked) \
220 , _video_locked (Properties::video_locked, other->_video_locked) \
221 , _automatic (Properties::automatic, other->_automatic) \
222 , _whole_file (Properties::whole_file, other->_whole_file) \
223 , _import (Properties::import, other->_import) \
224 , _external (Properties::external, other->_external) \
225 , _hidden (Properties::hidden, other->_hidden) \
226 , _position_locked (Properties::position_locked, other->_position_locked) \
227 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
228 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
229 , _stretch (Properties::stretch, other->_stretch) \
230 , _shift (Properties::shift, other->_shift) \
231 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
232 , _layering_index (Properties::layering_index, other->_layering_index)
234 /* derived-from-derived constructor (no sources in constructor) */
235 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
236 : SessionObject(s, name)
238 , REGION_DEFAULT_STATE(start,length)
239 , _last_length (length)
241 , _first_edit (EditChangesNothing)
244 register_properties ();
246 /* no sources at this point */
249 /** Basic Region constructor (many sources) */
250 Region::Region (const SourceList& srcs)
251 : SessionObject(srcs.front()->session(), "toBeRenamed")
252 , _type (srcs.front()->type())
253 , REGION_DEFAULT_STATE(0,0)
256 , _first_edit (EditChangesNothing)
259 register_properties ();
261 _type = srcs.front()->type();
265 assert(_sources.size() > 0);
266 assert (_type == srcs.front()->type());
269 /** Create a new Region from an existing one */
270 Region::Region (boost::shared_ptr<const Region> other)
271 : SessionObject(other->session(), other->name())
272 , _type (other->data_type())
273 , REGION_COPY_STATE (other)
274 , _last_length (other->_last_length)
275 , _last_position(other->_last_position) \
276 , _first_edit (EditChangesNothing)
277 , _layer (other->_layer)
279 register_properties ();
281 /* override state that may have been incorrectly inherited from the other region
284 _position = other->_position;
289 use_sources (other->_sources);
290 set_master_sources (other->_master_sources);
292 _position_lock_style = other->_position_lock_style;
293 _first_edit = other->_first_edit;
295 _start = other->_start;
296 _beat = other->_beat;
297 _pos_beats = other->_pos_beats;
299 /* sync pos is relative to start of file. our start-in-file is now zero,
300 so set our sync position to whatever the the difference between
301 _start and _sync_pos was in the other region.
303 result is that our new sync pos points to the same point in our source(s)
304 as the sync in the other region did in its source(s).
306 since we start at zero in our source(s), it is not possible to use a sync point that
307 is before the start. reset it to _start if that was true in the other region.
310 if (other->sync_marked()) {
311 if (other->_start < other->_sync_position) {
312 /* sync pos was after the start point of the other region */
313 _sync_position = other->_sync_position - other->_start;
315 /* sync pos was before the start point of the other region. not possible here. */
316 _sync_marked = false;
317 _sync_position = _start;
320 _sync_marked = false;
321 _sync_position = _start;
324 assert (_type == other->data_type());
327 /** Create a new Region from part of an existing one.
329 the start within \a other is given by \a offset
330 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
332 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
333 : SessionObject(other->session(), other->name())
334 , _type (other->data_type())
335 , REGION_COPY_STATE (other)
336 , _last_length (other->_last_length)
337 , _last_position(other->_last_position) \
338 , _first_edit (EditChangesNothing)
339 , _layer (other->_layer)
341 register_properties ();
343 /* override state that may have been incorrectly inherited from the other region
346 _position = other->_position + offset;
351 use_sources (other->_sources);
352 set_master_sources (other->_master_sources);
354 _start = other->_start + offset;
355 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
356 _pos_beats = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
358 /* if the other region had a distinct sync point
359 set, then continue to use it as best we can.
360 otherwise, reset sync point back to start.
363 if (other->sync_marked()) {
364 if (other->_sync_position < _start) {
365 _sync_marked = false;
366 _sync_position = _start;
368 _sync_position = other->_sync_position;
371 _sync_marked = false;
372 _sync_position = _start;
375 assert (_type == other->data_type());
378 /** Create a copy of @param other but with different sources. Used by filters */
379 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
380 : SessionObject (other->session(), other->name())
381 , _type (srcs.front()->type())
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _first_edit (EditChangesID)
386 , _layer (other->_layer)
388 register_properties ();
391 _position_locked = false;
393 other->_first_edit = EditChangesName;
395 if (other->_extra_xml) {
396 _extra_xml = new XMLNode (*other->_extra_xml);
402 assert(_sources.size() > 0);
407 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
412 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
414 _playlist = wpl.lock();
418 Region::set_name (const std::string& str)
421 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
422 assert(_name == str);
424 send_change (Properties::name);
431 Region::set_length (framecnt_t len, const int32_t sub_num)
433 //cerr << "Region::set_length() len = " << len << endl;
438 if (_length != len && len != 0) {
440 /* check that the current _position wouldn't make the new
444 if (max_framepos - len < _position) {
448 if (!verify_length (len)) {
453 set_length_internal (len, sub_num);
457 maybe_invalidate_transients ();
459 if (!property_changes_suspended()) {
463 send_change (Properties::length);
468 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
470 _last_length = _length;
475 Region::maybe_uncopy ()
477 /* this does nothing but marked a semantic moment once upon a time */
481 Region::first_edit ()
483 boost::shared_ptr<Playlist> pl (playlist());
485 if (_first_edit != EditChangesNothing && pl) {
487 _name = RegionFactory::new_region_name (_name);
488 _first_edit = EditChangesNothing;
490 send_change (Properties::name);
492 RegionFactory::CheckNewRegion (shared_from_this());
497 Region::at_natural_position () const
499 boost::shared_ptr<Playlist> pl (playlist());
505 boost::shared_ptr<Region> whole_file_region = get_parent();
507 if (whole_file_region) {
508 if (_position == whole_file_region->position() + _start) {
517 Region::move_to_natural_position ()
519 boost::shared_ptr<Playlist> pl (playlist());
525 boost::shared_ptr<Region> whole_file_region = get_parent();
527 if (whole_file_region) {
528 set_position (whole_file_region->position() + _start);
533 Region::special_set_position (framepos_t pos)
535 /* this is used when creating a whole file region as
536 a way to store its "natural" or "captured" position.
539 _position = _position;
544 Region::set_position_lock_style (PositionLockStyle ps)
546 if (_position_lock_style != ps) {
548 boost::shared_ptr<Playlist> pl (playlist());
550 _position_lock_style = ps;
552 if (_position_lock_style == MusicTime) {
553 //_beat = _session.tempo_map().beat_at_frame (_position);
554 //_pos_beats = _session.tempo_map().quarter_note_at_frame (_position);
557 send_change (Properties::position_lock_style);
562 Region::update_after_tempo_map_change (bool send)
564 boost::shared_ptr<Playlist> pl (playlist());
570 if (_position_lock_style == AudioTime) {
571 /* don't signal as the actual position has not chnged */
572 recompute_position_from_lock_style (0);
576 /* prevent movement before 0 */
577 const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
578 /* we have _beat. update frame position non-musically */
579 set_position_internal (pos, false, 0);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
586 send_change (Properties::position);
591 Region::set_position (framepos_t pos, int32_t sub_num)
597 if (position_lock_style() == AudioTime) {
598 set_position_internal (pos, true, sub_num);
599 if (_session.tempo_map().frame_at_beat (_beat) != _position) {
601 << " Region::set_position AudioTime position error!!! FRAME AT BEAT : " <<_session.tempo_map().frame_at_beat (_beat)
602 << " position : " << _position << " has playlist : " << (playlist() == 0) << std::endl;
605 if (!_session.loading()) {
606 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
609 /* will set pulse accordingly */
610 set_position_internal (pos, false, sub_num);
612 if (_session.tempo_map().frame_at_beat (_beat) != _position) {
614 << " Region::set_position MusicTime position error!!! FRAME AT BEAT : " <<_session.tempo_map().frame_at_beat (_beat)
615 << " position : " << _position << " beat : " << _beat << " has playlist : " << (playlist() == 0) << std::endl;
619 /* do this even if the position is the same. this helps out
620 a GUI that has moved its representation already.
622 PropertyChange p_and_l;
624 p_and_l.add (Properties::position);
625 /* Currently length change due to position change is only implemented
626 for MidiRegion (Region has no length in beats).
627 Notify a length change regardless (its more efficient for MidiRegions),
628 and when Region has a _length_beats we will need it here anyway).
630 p_and_l.add (Properties::length);
632 send_change (p_and_l);
636 /** A gui may need to create a region, then place it in an initial
637 * position determined by the user.
638 * When this takes place within one gui operation, we have to reset
639 * _last_position to prevent an implied move.
642 Region::set_initial_position (framepos_t pos)
648 if (_position != pos) {
651 /* check that the new _position wouldn't make the current
652 length impossible - if so, change the length.
654 XXX is this the right thing to do?
657 if (max_framepos - _length < _position) {
658 _last_length = _length;
659 _length = max_framepos - _position;
662 recompute_position_from_lock_style (0);
663 /* ensure that this move doesn't cause a range move */
664 _last_position = _position;
668 /* do this even if the position is the same. this helps out
669 a GUI that has moved its representation already.
671 send_change (Properties::position);
675 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
677 /* We emit a change of Properties::position even if the position hasn't changed
678 (see Region::set_position), so we must always set this up so that
679 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
681 _last_position = _position;
683 if (_position != pos) {
686 if (allow_bbt_recompute) {
687 recompute_position_from_lock_style (sub_num);
689 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
690 _pos_beats = _session.tempo_map().quarter_note_at_beat (_beat);
693 /* check that the new _position wouldn't make the current
694 length impossible - if so, change the length.
696 XXX is this the right thing to do?
698 if (max_framepos - _length < _position) {
699 _last_length = _length;
700 _length = max_framepos - _position;
706 Region::recompute_position_from_lock_style (const int32_t sub_num)
708 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
709 _pos_beats = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
713 Region::nudge_position (frameoffset_t n)
715 if (locked() || video_locked()) {
723 framepos_t new_position = _position;
726 if (_position > max_framepos - n) {
727 new_position = max_framepos;
732 if (_position < -n) {
738 /* assumes non-musical nudge */
739 set_position_internal (new_position, true, 0);
741 send_change (Properties::position);
745 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
747 _ancestral_length = l;
748 _ancestral_start = s;
754 Region::set_start (framepos_t pos)
756 if (locked() || position_locked() || video_locked()) {
759 /* This just sets the start, nothing else. It effectively shifts
760 the contents of the Region within the overall extent of the Source,
761 without changing the Region's position or length
766 if (!verify_start (pos)) {
770 set_start_internal (pos);
773 maybe_invalidate_transients ();
775 send_change (Properties::start);
780 Region::move_start (frameoffset_t distance, const int32_t sub_num)
782 if (locked() || position_locked() || video_locked()) {
786 framepos_t new_start;
790 if (_start > max_framepos - distance) {
791 new_start = max_framepos; // makes no sense
793 new_start = _start + distance;
796 if (!verify_start (new_start)) {
800 } else if (distance < 0) {
802 if (_start < -distance) {
805 new_start = _start + distance;
812 if (new_start == _start) {
816 set_start_internal (new_start, sub_num);
821 send_change (Properties::start);
825 Region::trim_front (framepos_t new_position, const int32_t sub_num)
827 modify_front (new_position, false, sub_num);
831 Region::cut_front (framepos_t new_position, const int32_t sub_num)
833 modify_front (new_position, true, sub_num);
837 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
839 modify_end (new_endpoint, true, sub_num);
843 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
849 framepos_t end = last_frame();
850 framepos_t source_zero;
852 if (_position > _start) {
853 source_zero = _position - _start;
855 source_zero = 0; // its actually negative, but this will work for us
858 if (new_position < end) { /* can't trim it zero or negative length */
860 framecnt_t newlen = 0;
862 if (!can_trim_start_before_source_start ()) {
863 /* can't trim it back past where source position zero is located */
864 new_position = max (new_position, source_zero);
867 if (new_position > _position) {
868 newlen = _length - (new_position - _position);
870 newlen = _length + (_position - new_position);
873 trim_to_internal (new_position, newlen, sub_num);
876 _right_of_split = true;
879 if (!property_changes_suspended()) {
880 recompute_at_start ();
883 maybe_invalidate_transients ();
888 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
894 if (new_endpoint > _position) {
895 trim_to_internal (_position, new_endpoint - _position, sub_num);
897 _left_of_split = true;
899 if (!property_changes_suspended()) {
905 /** @param new_endpoint New region end point, such that, for example,
906 * a region at 0 of length 10 has an endpoint of 9.
910 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
912 modify_end (new_endpoint, false, sub_num);
916 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
922 trim_to_internal (position, length, sub_num);
924 if (!property_changes_suspended()) {
925 recompute_at_start ();
931 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
933 framepos_t new_start;
939 frameoffset_t const start_shift = position - _position;
941 if (start_shift > 0) {
943 if (_start > max_framepos - start_shift) {
944 new_start = max_framepos;
946 new_start = _start + start_shift;
949 } else if (start_shift < 0) {
951 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
954 new_start = _start + start_shift;
961 if (!verify_start_and_length (new_start, length)) {
965 PropertyChange what_changed;
967 if (_start != new_start) {
968 set_start_internal (new_start, sub_num);
969 what_changed.add (Properties::start);
973 /* Set position before length, otherwise for MIDI regions this bad thing happens:
974 * 1. we call set_length_internal; length in beats is computed using the region's current
975 * (soon-to-be old) position
976 * 2. we call set_position_internal; position is set and length in frames re-computed using
977 * length in beats from (1) but at the new position, which is wrong if the region
978 * straddles a tempo/meter change.
981 if (_position != position) {
982 if (!property_changes_suspended()) {
983 _last_position = _position;
985 set_position_internal (position, true, sub_num);
986 what_changed.add (Properties::position);
989 if (_length != length) {
990 if (!property_changes_suspended()) {
991 _last_length = _length;
993 set_length_internal (length, sub_num);
994 what_changed.add (Properties::length);
999 PropertyChange start_and_length;
1001 start_and_length.add (Properties::start);
1002 start_and_length.add (Properties::length);
1004 if (what_changed.contains (start_and_length)) {
1008 if (!what_changed.empty()) {
1009 send_change (what_changed);
1014 Region::set_hidden (bool yn)
1016 if (hidden() != yn) {
1018 send_change (Properties::hidden);
1023 Region::set_whole_file (bool yn)
1026 /* no change signal */
1030 Region::set_automatic (bool yn)
1033 /* no change signal */
1037 Region::set_muted (bool yn)
1039 if (muted() != yn) {
1041 send_change (Properties::muted);
1046 Region::set_opaque (bool yn)
1048 if (opaque() != yn) {
1050 send_change (Properties::opaque);
1055 Region::set_locked (bool yn)
1057 if (locked() != yn) {
1059 send_change (Properties::locked);
1064 Region::set_video_locked (bool yn)
1066 if (video_locked() != yn) {
1068 send_change (Properties::video_locked);
1073 Region::set_position_locked (bool yn)
1075 if (position_locked() != yn) {
1076 _position_locked = yn;
1077 send_change (Properties::locked);
1081 /** Set the region's sync point.
1082 * @param absolute_pos Session time.
1085 Region::set_sync_position (framepos_t absolute_pos)
1087 /* position within our file */
1088 framepos_t const file_pos = _start + (absolute_pos - _position);
1090 if (file_pos != _sync_position) {
1091 _sync_marked = true;
1092 _sync_position = file_pos;
1093 if (!property_changes_suspended()) {
1097 send_change (Properties::sync_position);
1102 Region::clear_sync_position ()
1104 if (sync_marked()) {
1105 _sync_marked = false;
1106 if (!property_changes_suspended()) {
1110 send_change (Properties::sync_position);
1114 /* @return the sync point relative the first frame of the region */
1116 Region::sync_offset (int& dir) const
1118 if (sync_marked()) {
1119 if (_sync_position > _start) {
1121 return _sync_position - _start;
1124 return _start - _sync_position;
1133 Region::adjust_to_sync (framepos_t pos) const
1136 frameoffset_t offset = sync_offset (sync_dir);
1138 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1147 if (max_framepos - pos > offset) {
1155 /** @return Sync position in session time */
1157 Region::sync_position() const
1159 if (sync_marked()) {
1160 return _position - _start + _sync_position;
1162 /* if sync has not been marked, use the start of the region */
1170 boost::shared_ptr<Playlist> pl (playlist());
1172 pl->raise_region (shared_from_this ());
1179 boost::shared_ptr<Playlist> pl (playlist());
1181 pl->lower_region (shared_from_this ());
1187 Region::raise_to_top ()
1189 boost::shared_ptr<Playlist> pl (playlist());
1191 pl->raise_region_to_top (shared_from_this());
1196 Region::lower_to_bottom ()
1198 boost::shared_ptr<Playlist> pl (playlist());
1200 pl->lower_region_to_bottom (shared_from_this());
1205 Region::set_layer (layer_t l)
1213 XMLNode *node = new XMLNode ("Region");
1217 const char* fe = NULL;
1219 /* custom version of 'add_properties (*node);'
1220 * skip values that have have dedicated save functions
1221 * in AudioRegion::state()
1223 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1224 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1225 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1226 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1227 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1228 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1229 i->second->get_value (*node);
1232 id().print (buf, sizeof (buf));
1233 node->add_property ("id", buf);
1234 node->add_property ("type", _type.to_string());
1236 switch (_first_edit) {
1237 case EditChangesNothing:
1240 case EditChangesName:
1246 default: /* should be unreachable but makes g++ happy */
1251 node->add_property ("first-edit", fe);
1253 /* note: flags are stored by derived classes */
1255 for (uint32_t n=0; n < _sources.size(); ++n) {
1256 snprintf (buf2, sizeof(buf2), "source-%d", n);
1257 _sources[n]->id().print (buf, sizeof(buf));
1258 node->add_property (buf2, buf);
1261 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1262 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1263 _master_sources[n]->id().print (buf, sizeof (buf));
1264 node->add_property (buf2, buf);
1267 /* Only store nested sources for the whole-file region that acts
1268 as the parent/root of all regions using it.
1271 if (_whole_file && max_source_level() > 0) {
1273 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1275 /* region is compound - get its playlist and
1276 store that before we list the region that
1280 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1281 nested_node->add_child_nocopy ((*s)->get_state ());
1285 node->add_child_nocopy (*nested_node);
1290 node->add_child_copy (*_extra_xml);
1297 Region::get_state ()
1303 Region::set_state (const XMLNode& node, int version)
1305 PropertyChange what_changed;
1306 return _set_state (node, version, what_changed, true);
1310 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1312 XMLProperty const * prop;
1313 Timecode::BBT_Time bbt_time;
1315 Stateful::save_extra_xml (node);
1317 what_changed = set_values (node);
1321 if (_position_lock_style == MusicTime) {
1322 if ((prop = node.property ("bbt-position")) != 0) {
1323 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1326 &bbt_time.ticks) != 3) {
1327 _position_lock_style = AudioTime;
1328 _beat = _session.tempo_map().beat_at_frame (_position);
1330 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1335 /* fix problems with old sessions corrupted by impossible
1336 values for _stretch or _shift
1338 if (_stretch == 0.0f) {
1342 if (_shift == 0.0f) {
1347 send_change (what_changed);
1350 /* Quick fix for 2.x sessions when region is muted */
1351 if ((prop = node.property (X_("flags")))) {
1352 if (string::npos != prop->value().find("Muted")){
1357 // saved property is invalid, region-transients are not saved
1358 if (_user_transients.size() == 0){
1359 _valid_transients = false;
1366 Region::suspend_property_changes ()
1368 Stateful::suspend_property_changes ();
1369 _last_length = _length;
1370 _last_position = _position;
1374 Region::mid_thaw (const PropertyChange& what_changed)
1376 if (what_changed.contains (Properties::length)) {
1377 if (what_changed.contains (Properties::position)) {
1378 recompute_at_start ();
1380 recompute_at_end ();
1385 Region::send_change (const PropertyChange& what_changed)
1387 if (what_changed.empty()) {
1391 Stateful::send_change (what_changed);
1393 if (!Stateful::property_changes_suspended()) {
1395 /* Try and send a shared_pointer unless this is part of the constructor.
1400 boost::shared_ptr<Region> rptr = shared_from_this();
1401 RegionPropertyChanged (rptr, what_changed);
1403 /* no shared_ptr available, relax; */
1409 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1411 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1415 Region::equivalent (boost::shared_ptr<const Region> other) const
1417 return _start == other->_start &&
1418 _position == other->_position &&
1419 _length == other->_length;
1423 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1425 return _start == other->_start &&
1426 _length == other->_length;
1430 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1432 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1436 Region::source_deleted (boost::weak_ptr<Source>)
1440 if (!_session.deletion_in_progress()) {
1441 /* this is a very special case: at least one of the region's
1442 sources has bee deleted, so invalidate all references to
1443 ourselves. Do NOT do this during session deletion, because
1444 then we run the risk that this will actually result
1445 in this object being deleted (as refcnt goes to zero)
1446 while emitting DropReferences.
1454 Region::master_source_names ()
1456 SourceList::iterator i;
1458 vector<string> names;
1459 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1460 names.push_back((*i)->name());
1467 Region::set_master_sources (const SourceList& srcs)
1469 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1470 (*i)->dec_use_count ();
1473 _master_sources = srcs;
1474 assert (_sources.size() == _master_sources.size());
1476 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1477 (*i)->inc_use_count ();
1482 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1487 if ((_sources.size() != other->_sources.size()) ||
1488 (_master_sources.size() != other->_master_sources.size())) {
1492 SourceList::const_iterator i;
1493 SourceList::const_iterator io;
1495 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1496 if ((*i)->id() != (*io)->id()) {
1501 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1502 if ((*i)->id() != (*io)->id()) {
1511 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1517 SourceList::const_iterator i;
1518 SourceList::const_iterator io;
1520 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1521 if ((*i)->id() == (*io)->id()) {
1530 Region::source_string () const
1532 //string res = itos(_sources.size());
1535 res << _sources.size() << ":";
1537 SourceList::const_iterator i;
1539 for (i = _sources.begin(); i != _sources.end(); ++i) {
1540 res << (*i)->id() << ":";
1543 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1544 res << (*i)->id() << ":";
1551 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1553 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1555 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1558 if (sources.find (ps) == sources.end()) {
1559 /* (Playlist)Source not currently in
1560 accumulating set, so recurse.
1562 ps->playlist()->deep_sources (sources);
1566 /* add this source */
1567 sources.insert (*i);
1570 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1572 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1575 if (sources.find (ps) == sources.end()) {
1576 /* (Playlist)Source not currently in
1577 accumulating set, so recurse.
1579 ps->playlist()->deep_sources (sources);
1583 /* add this source */
1584 sources.insert (*i);
1589 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1591 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1597 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1600 if (ps->playlist()->uses_source (source)) {
1607 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1613 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1616 if (ps->playlist()->uses_source (source)) {
1628 Region::source_length(uint32_t n) const
1630 assert (n < _sources.size());
1631 return _sources[n]->length (_position - _start);
1635 Region::verify_length (framecnt_t& len)
1637 if (source() && (source()->destructive() || source()->length_mutable())) {
1641 framecnt_t maxlen = 0;
1643 for (uint32_t n = 0; n < _sources.size(); ++n) {
1644 maxlen = max (maxlen, source_length(n) - _start);
1647 len = min (len, maxlen);
1653 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1655 if (source() && (source()->destructive() || source()->length_mutable())) {
1659 framecnt_t maxlen = 0;
1661 for (uint32_t n = 0; n < _sources.size(); ++n) {
1662 maxlen = max (maxlen, source_length(n) - new_start);
1665 new_length = min (new_length, maxlen);
1671 Region::verify_start (framepos_t pos)
1673 if (source() && (source()->destructive() || source()->length_mutable())) {
1677 for (uint32_t n = 0; n < _sources.size(); ++n) {
1678 if (pos > source_length(n) - _length) {
1686 Region::verify_start_mutable (framepos_t& new_start)
1688 if (source() && (source()->destructive() || source()->length_mutable())) {
1692 for (uint32_t n = 0; n < _sources.size(); ++n) {
1693 if (new_start > source_length(n) - _length) {
1694 new_start = source_length(n) - _length;
1700 boost::shared_ptr<Region>
1701 Region::get_parent() const
1703 boost::shared_ptr<Playlist> pl (playlist());
1706 boost::shared_ptr<Region> r;
1707 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1709 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1710 return boost::static_pointer_cast<Region> (r);
1714 return boost::shared_ptr<Region>();
1718 Region::apply (Filter& filter, Progress* progress)
1720 return filter.run (shared_from_this(), progress);
1725 Region::maybe_invalidate_transients ()
1727 bool changed = !_onsets.empty();
1730 if (_valid_transients || changed) {
1731 send_change (PropertyChange (Properties::valid_transients));
1737 Region::transients (AnalysisFeatureList& afl)
1739 int cnt = afl.empty() ? 0 : 1;
1741 Region::merge_features (afl, _onsets, _position);
1742 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1743 if (!_onsets.empty ()) {
1746 if (!_user_transients.empty ()) {
1751 // remove exact duplicates
1752 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1757 Region::has_transients () const
1759 if (!_user_transients.empty ()) {
1760 assert (_valid_transients);
1763 if (!_onsets.empty ()) {
1770 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1772 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1773 const frameoffset_t p = (*x) + off;
1774 if (p < first_frame() || p > last_frame()) {
1777 result.push_back (p);
1782 Region::drop_sources ()
1784 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1785 (*i)->dec_use_count ();
1790 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1791 (*i)->dec_use_count ();
1794 _master_sources.clear ();
1798 Region::use_sources (SourceList const & s)
1800 set<boost::shared_ptr<Source> > unique_srcs;
1802 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1804 _sources.push_back (*i);
1805 (*i)->inc_use_count ();
1806 _master_sources.push_back (*i);
1807 (*i)->inc_use_count ();
1809 /* connect only once to DropReferences, even if sources are replicated
1812 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1813 unique_srcs.insert (*i);
1814 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1820 Region::can_trim () const
1822 CanTrim ct = CanTrim (0);
1828 /* if not locked, we can always move the front later, and the end earlier
1831 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1833 if (start() != 0 || can_trim_start_before_source_start ()) {
1834 ct = CanTrim (ct | FrontTrimEarlier);
1837 if (!_sources.empty()) {
1838 if ((start() + length()) < _sources.front()->length (0)) {
1839 ct = CanTrim (ct | EndTrimLater);
1847 Region::max_source_level () const
1851 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1852 lvl = max (lvl, (*i)->level());
1859 Region::is_compound () const
1861 return max_source_level() > 0;
1865 Region::post_set (const PropertyChange& pc)
1867 if (pc.contains (Properties::position)) {
1868 if (playlist() && _session.tempo_map().frame_at_beat (_beat) != _position && position_lock_style() == MusicTime) {
1870 << " Region::post_set MusicTime position error!!! FRAME AT BEAT : " <<_session.tempo_map().frame_at_beat (_beat)
1871 << " position : " << _position << " beat : " << _beat << std::endl;
1872 //_position = _session.tempo_map().frame_at_beat (_beat);
1874 if (playlist() && _session.tempo_map().frame_at_beat (_beat) != _position && position_lock_style() == AudioTime) {
1876 << " Region::post_set AudioTime position error!!! FRAME AT BEAT : " <<_session.tempo_map().frame_at_beat (_beat)
1877 << " position : " << _position << " beat : " << _beat << std::endl;
1878 //_beat = _session.tempo_map().beat_at_frame (_position);
1880 _pos_beats = _session.tempo_map().quarter_note_at_beat (_beat);
1885 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1891 Region::earliest_possible_position () const
1893 if (_start > _position) {
1896 return _position - _start;
1901 Region::latest_possible_frame () const
1903 framecnt_t minlen = max_framecnt;
1905 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1906 /* non-audio regions have a length that may vary based on their
1907 * position, so we have to pass it in the call.
1909 minlen = min (minlen, (*i)->length (_position));
1912 /* the latest possible last frame is determined by the current
1913 * position, plus the shortest source extent past _start.
1916 return _position + (minlen - _start) - 1;