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"
28 #include "pbd/types_convert.h"
30 #include "ardour/debug.h"
31 #include "ardour/filter.h"
32 #include "ardour/playlist.h"
33 #include "ardour/playlist_source.h"
34 #include "ardour/profile.h"
35 #include "ardour/region.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
38 #include "ardour/source.h"
39 #include "ardour/tempo.h"
40 #include "ardour/transient_detector.h"
41 #include "ardour/types_convert.h"
46 using namespace ARDOUR;
51 namespace Properties {
52 PBD::PropertyDescriptor<bool> muted;
53 PBD::PropertyDescriptor<bool> opaque;
54 PBD::PropertyDescriptor<bool> locked;
55 PBD::PropertyDescriptor<bool> video_locked;
56 PBD::PropertyDescriptor<bool> automatic;
57 PBD::PropertyDescriptor<bool> whole_file;
58 PBD::PropertyDescriptor<bool> import;
59 PBD::PropertyDescriptor<bool> external;
60 PBD::PropertyDescriptor<bool> sync_marked;
61 PBD::PropertyDescriptor<bool> left_of_split;
62 PBD::PropertyDescriptor<bool> right_of_split;
63 PBD::PropertyDescriptor<bool> hidden;
64 PBD::PropertyDescriptor<bool> position_locked;
65 PBD::PropertyDescriptor<bool> valid_transients;
66 PBD::PropertyDescriptor<framepos_t> start;
67 PBD::PropertyDescriptor<framecnt_t> length;
68 PBD::PropertyDescriptor<framepos_t> position;
69 PBD::PropertyDescriptor<double> beat;
70 PBD::PropertyDescriptor<framecnt_t> sync_position;
71 PBD::PropertyDescriptor<layer_t> layer;
72 PBD::PropertyDescriptor<framepos_t> ancestral_start;
73 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
74 PBD::PropertyDescriptor<float> stretch;
75 PBD::PropertyDescriptor<float> shift;
76 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
77 PBD::PropertyDescriptor<uint64_t> layering_index;
81 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
84 Region::make_property_quarks ()
86 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
88 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
90 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
92 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
94 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
96 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
98 Properties::import.property_id = g_quark_from_static_string (X_("import"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
100 Properties::external.property_id = g_quark_from_static_string (X_("external"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
102 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
104 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
106 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
108 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
110 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
112 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
114 Properties::start.property_id = g_quark_from_static_string (X_("start"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
116 Properties::length.property_id = g_quark_from_static_string (X_("length"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
118 Properties::position.property_id = g_quark_from_static_string (X_("position"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
120 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
122 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
124 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
126 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
128 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
130 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
132 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
134 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
136 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
137 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
141 Region::register_properties ()
143 _xml_node_name = X_("Region");
145 add_property (_muted);
146 add_property (_opaque);
147 add_property (_locked);
148 add_property (_video_locked);
149 add_property (_automatic);
150 add_property (_whole_file);
151 add_property (_import);
152 add_property (_external);
153 add_property (_sync_marked);
154 add_property (_left_of_split);
155 add_property (_right_of_split);
156 add_property (_hidden);
157 add_property (_position_locked);
158 add_property (_valid_transients);
159 add_property (_start);
160 add_property (_length);
161 add_property (_position);
162 add_property (_beat);
163 add_property (_sync_position);
164 add_property (_ancestral_start);
165 add_property (_ancestral_length);
166 add_property (_stretch);
167 add_property (_shift);
168 add_property (_position_lock_style);
169 add_property (_layering_index);
172 #define REGION_DEFAULT_STATE(s,l) \
173 _sync_marked (Properties::sync_marked, false) \
174 , _left_of_split (Properties::left_of_split, false) \
175 , _right_of_split (Properties::right_of_split, false) \
176 , _valid_transients (Properties::valid_transients, false) \
177 , _start (Properties::start, (s)) \
178 , _length (Properties::length, (l)) \
179 , _position (Properties::position, 0) \
180 , _beat (Properties::beat, 0.0) \
181 , _sync_position (Properties::sync_position, (s)) \
182 , _quarter_note (0.0) \
183 , _transient_user_start (0) \
184 , _transient_analysis_start (0) \
185 , _transient_analysis_end (0) \
186 , _muted (Properties::muted, false) \
187 , _opaque (Properties::opaque, true) \
188 , _locked (Properties::locked, false) \
189 , _video_locked (Properties::video_locked, false) \
190 , _automatic (Properties::automatic, false) \
191 , _whole_file (Properties::whole_file, false) \
192 , _import (Properties::import, false) \
193 , _external (Properties::external, false) \
194 , _hidden (Properties::hidden, false) \
195 , _position_locked (Properties::position_locked, false) \
196 , _ancestral_start (Properties::ancestral_start, (s)) \
197 , _ancestral_length (Properties::ancestral_length, (l)) \
198 , _stretch (Properties::stretch, 1.0) \
199 , _shift (Properties::shift, 1.0) \
200 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
201 , _layering_index (Properties::layering_index, 0)
203 #define REGION_COPY_STATE(other) \
204 _sync_marked (Properties::sync_marked, other->_sync_marked) \
205 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
206 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
207 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
208 , _start(Properties::start, other->_start) \
209 , _length(Properties::length, other->_length) \
210 , _position(Properties::position, other->_position) \
211 , _beat (Properties::beat, other->_beat) \
212 , _sync_position(Properties::sync_position, other->_sync_position) \
213 , _quarter_note (other->_quarter_note) \
214 , _user_transients (other->_user_transients) \
215 , _transient_user_start (other->_transient_user_start) \
216 , _transients (other->_transients) \
217 , _transient_analysis_start (other->_transient_analysis_start) \
218 , _transient_analysis_end (other->_transient_analysis_end) \
219 , _muted (Properties::muted, other->_muted) \
220 , _opaque (Properties::opaque, other->_opaque) \
221 , _locked (Properties::locked, other->_locked) \
222 , _video_locked (Properties::video_locked, other->_video_locked) \
223 , _automatic (Properties::automatic, other->_automatic) \
224 , _whole_file (Properties::whole_file, other->_whole_file) \
225 , _import (Properties::import, other->_import) \
226 , _external (Properties::external, other->_external) \
227 , _hidden (Properties::hidden, other->_hidden) \
228 , _position_locked (Properties::position_locked, other->_position_locked) \
229 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
230 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
231 , _stretch (Properties::stretch, other->_stretch) \
232 , _shift (Properties::shift, other->_shift) \
233 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
234 , _layering_index (Properties::layering_index, other->_layering_index)
236 /* derived-from-derived constructor (no sources in constructor) */
237 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
238 : SessionObject(s, name)
240 , REGION_DEFAULT_STATE(start,length)
241 , _last_length (length)
243 , _first_edit (EditChangesNothing)
246 register_properties ();
248 /* no sources at this point */
251 /** Basic Region constructor (many sources) */
252 Region::Region (const SourceList& srcs)
253 : SessionObject(srcs.front()->session(), "toBeRenamed")
254 , _type (srcs.front()->type())
255 , REGION_DEFAULT_STATE(0,0)
258 , _first_edit (EditChangesNothing)
261 register_properties ();
263 _type = srcs.front()->type();
267 assert(_sources.size() > 0);
268 assert (_type == srcs.front()->type());
271 /** Create a new Region from an existing one */
272 Region::Region (boost::shared_ptr<const Region> other)
273 : SessionObject(other->session(), other->name())
274 , _type (other->data_type())
275 , REGION_COPY_STATE (other)
276 , _last_length (other->_last_length)
277 , _last_position(other->_last_position) \
278 , _first_edit (EditChangesNothing)
279 , _layer (other->_layer)
281 register_properties ();
283 /* override state that may have been incorrectly inherited from the other region
286 _position = other->_position;
291 use_sources (other->_sources);
292 set_master_sources (other->_master_sources);
294 _position_lock_style = other->_position_lock_style;
295 _first_edit = other->_first_edit;
297 _start = other->_start;
298 _beat = other->_beat;
299 _quarter_note = other->_quarter_note;
301 /* sync pos is relative to start of file. our start-in-file is now zero,
302 so set our sync position to whatever the the difference between
303 _start and _sync_pos was in the other region.
305 result is that our new sync pos points to the same point in our source(s)
306 as the sync in the other region did in its source(s).
308 since we start at zero in our source(s), it is not possible to use a sync point that
309 is before the start. reset it to _start if that was true in the other region.
312 if (other->sync_marked()) {
313 if (other->_start < other->_sync_position) {
314 /* sync pos was after the start point of the other region */
315 _sync_position = other->_sync_position - other->_start;
317 /* sync pos was before the start point of the other region. not possible here. */
318 _sync_marked = false;
319 _sync_position = _start;
322 _sync_marked = false;
323 _sync_position = _start;
326 assert (_type == other->data_type());
329 /** Create a new Region from part of an existing one.
331 the start within \a other is given by \a offset
332 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
334 Region::Region (boost::shared_ptr<const Region> other, MusicFrame offset)
335 : SessionObject(other->session(), other->name())
336 , _type (other->data_type())
337 , REGION_COPY_STATE (other)
338 , _last_length (other->_last_length)
339 , _last_position(other->_last_position) \
340 , _first_edit (EditChangesNothing)
341 , _layer (other->_layer)
343 register_properties ();
345 /* override state that may have been incorrectly inherited from the other region
352 use_sources (other->_sources);
353 set_master_sources (other->_master_sources);
355 _position = other->_position + offset.frame;
356 _start = other->_start + offset.frame;
358 /* prevent offset of 0 from altering musical position */
359 if (offset.frame != 0) {
360 const double offset_qn = _session.tempo_map().exact_qn_at_frame (other->_position + offset.frame, offset.division)
361 - other->_quarter_note;
363 _quarter_note = other->_quarter_note + offset_qn;
364 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
366 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
369 /* if the other region had a distinct sync point
370 set, then continue to use it as best we can.
371 otherwise, reset sync point back to start.
374 if (other->sync_marked()) {
375 if (other->_sync_position < _start) {
376 _sync_marked = false;
377 _sync_position = _start;
379 _sync_position = other->_sync_position;
382 _sync_marked = false;
383 _sync_position = _start;
386 assert (_type == other->data_type());
389 /** Create a copy of @param other but with different sources. Used by filters */
390 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
391 : SessionObject (other->session(), other->name())
392 , _type (srcs.front()->type())
393 , REGION_COPY_STATE (other)
394 , _last_length (other->_last_length)
395 , _last_position (other->_last_position)
396 , _first_edit (EditChangesID)
397 , _layer (other->_layer)
399 register_properties ();
402 _position_locked = false;
404 other->_first_edit = EditChangesName;
406 if (other->_extra_xml) {
407 _extra_xml = new XMLNode (*other->_extra_xml);
413 assert(_sources.size() > 0);
418 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
423 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
425 _playlist = wpl.lock();
429 Region::set_name (const std::string& str)
432 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
433 assert(_name == str);
435 send_change (Properties::name);
442 Region::set_length (framecnt_t len, const int32_t sub_num)
444 //cerr << "Region::set_length() len = " << len << endl;
449 if (_length != len && len != 0) {
451 /* check that the current _position wouldn't make the new
455 if (max_framepos - len < _position) {
459 if (!verify_length (len)) {
464 set_length_internal (len, sub_num);
468 maybe_invalidate_transients ();
470 if (!property_changes_suspended()) {
474 send_change (Properties::length);
479 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
481 _last_length = _length;
486 Region::maybe_uncopy ()
488 /* this does nothing but marked a semantic moment once upon a time */
492 Region::first_edit ()
494 boost::shared_ptr<Playlist> pl (playlist());
496 if (_first_edit != EditChangesNothing && pl) {
498 _name = RegionFactory::new_region_name (_name);
499 _first_edit = EditChangesNothing;
501 send_change (Properties::name);
503 RegionFactory::CheckNewRegion (shared_from_this());
508 Region::at_natural_position () const
510 boost::shared_ptr<Playlist> pl (playlist());
516 boost::shared_ptr<Region> whole_file_region = get_parent();
518 if (whole_file_region) {
519 if (_position == whole_file_region->position() + _start) {
528 Region::move_to_natural_position ()
530 boost::shared_ptr<Playlist> pl (playlist());
536 boost::shared_ptr<Region> whole_file_region = get_parent();
538 if (whole_file_region) {
539 set_position (whole_file_region->position() + _start);
544 Region::special_set_position (framepos_t pos)
546 /* this is used when creating a whole file region as
547 a way to store its "natural" or "captured" position.
550 _position = _position;
555 Region::set_position_lock_style (PositionLockStyle ps)
557 if (_position_lock_style != ps) {
559 boost::shared_ptr<Playlist> pl (playlist());
561 _position_lock_style = ps;
563 send_change (Properties::position_lock_style);
568 Region::update_after_tempo_map_change (bool send)
570 boost::shared_ptr<Playlist> pl (playlist());
576 if (_position_lock_style == AudioTime) {
577 /* don't signal as the actual position has not chnged */
578 recompute_position_from_lock_style (0);
582 /* prevent movement before 0 */
583 const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
584 /* we have _beat. update frame position non-musically */
585 set_position_internal (pos, false, 0);
587 /* do this even if the position is the same. this helps out
588 a GUI that has moved its representation already.
592 send_change (Properties::position);
597 Region::set_position (framepos_t pos, int32_t sub_num)
603 /* do this even if the position is the same. this helps out
604 a GUI that has moved its representation already.
606 PropertyChange p_and_l;
608 p_and_l.add (Properties::position);
610 if (position_lock_style() == AudioTime) {
611 set_position_internal (pos, true, sub_num);
613 if (!_session.loading()) {
614 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
615 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
618 set_position_internal (pos, false, sub_num);
621 if (position_lock_style() == MusicTime) {
622 p_and_l.add (Properties::length);
625 send_change (p_and_l);
630 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
632 /* We emit a change of Properties::position even if the position hasn't changed
633 (see Region::set_position), so we must always set this up so that
634 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
636 _last_position = _position;
638 if (_position != pos) {
641 if (allow_bbt_recompute) {
642 recompute_position_from_lock_style (sub_num);
644 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
645 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
648 /* check that the new _position wouldn't make the current
649 length impossible - if so, change the length.
651 XXX is this the right thing to do?
653 if (max_framepos - _length < _position) {
654 _last_length = _length;
655 _length = max_framepos - _position;
661 Region::set_position_music (double qn)
667 /* do this even if the position is the same. this helps out
668 a GUI that has moved its representation already.
670 PropertyChange p_and_l;
672 p_and_l.add (Properties::position);
674 if (!_session.loading()) {
675 _beat = _session.tempo_map().beat_at_quarter_note (qn);
678 /* will set frame accordingly */
679 set_position_music_internal (qn);
681 if (position_lock_style() == MusicTime) {
682 p_and_l.add (Properties::length);
685 send_change (p_and_l);
689 Region::set_position_music_internal (double qn)
691 /* We emit a change of Properties::position even if the position hasn't changed
692 (see Region::set_position), so we must always set this up so that
693 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
695 _last_position = _position;
697 if (_quarter_note != qn) {
698 _position = _session.tempo_map().frame_at_quarter_note (qn);
701 /* check that the new _position wouldn't make the current
702 length impossible - if so, change the length.
704 XXX is this the right thing to do?
706 if (max_framepos - _length < _position) {
707 _last_length = _length;
708 _length = max_framepos - _position;
713 /** A gui may need to create a region, then place it in an initial
714 * position determined by the user.
715 * When this takes place within one gui operation, we have to reset
716 * _last_position to prevent an implied move.
719 Region::set_initial_position (framepos_t pos)
725 if (_position != pos) {
728 /* check that the new _position wouldn't make the current
729 length impossible - if so, change the length.
731 XXX is this the right thing to do?
734 if (max_framepos - _length < _position) {
735 _last_length = _length;
736 _length = max_framepos - _position;
739 recompute_position_from_lock_style (0);
740 /* ensure that this move doesn't cause a range move */
741 _last_position = _position;
745 /* do this even if the position is the same. this helps out
746 a GUI that has moved its representation already.
748 send_change (Properties::position);
752 Region::recompute_position_from_lock_style (const int32_t sub_num)
754 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
755 _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
759 Region::nudge_position (frameoffset_t n)
761 if (locked() || video_locked()) {
769 framepos_t new_position = _position;
772 if (_position > max_framepos - n) {
773 new_position = max_framepos;
778 if (_position < -n) {
784 /* assumes non-musical nudge */
785 set_position_internal (new_position, true, 0);
787 send_change (Properties::position);
791 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
793 _ancestral_length = l;
794 _ancestral_start = s;
800 Region::set_start (framepos_t pos)
802 if (locked() || position_locked() || video_locked()) {
805 /* This just sets the start, nothing else. It effectively shifts
806 the contents of the Region within the overall extent of the Source,
807 without changing the Region's position or length
812 if (!verify_start (pos)) {
816 set_start_internal (pos);
819 maybe_invalidate_transients ();
821 send_change (Properties::start);
826 Region::move_start (frameoffset_t distance, const int32_t sub_num)
828 if (locked() || position_locked() || video_locked()) {
832 framepos_t new_start;
836 if (_start > max_framepos - distance) {
837 new_start = max_framepos; // makes no sense
839 new_start = _start + distance;
842 if (!verify_start (new_start)) {
846 } else if (distance < 0) {
848 if (_start < -distance) {
851 new_start = _start + distance;
858 if (new_start == _start) {
862 set_start_internal (new_start, sub_num);
867 send_change (Properties::start);
871 Region::trim_front (framepos_t new_position, const int32_t sub_num)
873 modify_front (new_position, false, sub_num);
877 Region::cut_front (framepos_t new_position, const int32_t sub_num)
879 modify_front (new_position, true, sub_num);
883 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
885 modify_end (new_endpoint, true, sub_num);
889 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
895 framepos_t end = last_frame();
896 framepos_t source_zero;
898 if (_position > _start) {
899 source_zero = _position - _start;
901 source_zero = 0; // its actually negative, but this will work for us
904 if (new_position < end) { /* can't trim it zero or negative length */
906 framecnt_t newlen = 0;
908 if (!can_trim_start_before_source_start ()) {
909 /* can't trim it back past where source position zero is located */
910 new_position = max (new_position, source_zero);
913 if (new_position > _position) {
914 newlen = _length - (new_position - _position);
916 newlen = _length + (_position - new_position);
919 trim_to_internal (new_position, newlen, sub_num);
922 _right_of_split = true;
925 if (!property_changes_suspended()) {
926 recompute_at_start ();
929 maybe_invalidate_transients ();
934 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
940 if (new_endpoint > _position) {
941 trim_to_internal (_position, new_endpoint - _position, sub_num);
943 _left_of_split = true;
945 if (!property_changes_suspended()) {
951 /** @param new_endpoint New region end point, such that, for example,
952 * a region at 0 of length 10 has an endpoint of 9.
956 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
958 modify_end (new_endpoint, false, sub_num);
962 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
968 trim_to_internal (position, length, sub_num);
970 if (!property_changes_suspended()) {
971 recompute_at_start ();
977 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
979 framepos_t new_start;
985 frameoffset_t const start_shift = position - _position;
987 if (start_shift > 0) {
989 if (_start > max_framepos - start_shift) {
990 new_start = max_framepos;
992 new_start = _start + start_shift;
995 } else if (start_shift < 0) {
997 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
1000 new_start = _start + start_shift;
1007 if (!verify_start_and_length (new_start, length)) {
1011 PropertyChange what_changed;
1013 if (_start != new_start) {
1014 set_start_internal (new_start, sub_num);
1015 what_changed.add (Properties::start);
1019 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1020 * 1. we call set_length_internal; length in beats is computed using the region's current
1021 * (soon-to-be old) position
1022 * 2. we call set_position_internal; position is set and length in frames re-computed using
1023 * length in beats from (1) but at the new position, which is wrong if the region
1024 * straddles a tempo/meter change.
1027 if (_position != position) {
1028 if (!property_changes_suspended()) {
1029 _last_position = _position;
1031 set_position_internal (position, true, sub_num);
1032 what_changed.add (Properties::position);
1035 if (_length != length) {
1036 if (!property_changes_suspended()) {
1037 _last_length = _length;
1039 set_length_internal (length, sub_num);
1040 what_changed.add (Properties::length);
1043 _whole_file = false;
1045 PropertyChange start_and_length;
1047 start_and_length.add (Properties::start);
1048 start_and_length.add (Properties::length);
1050 if (what_changed.contains (start_and_length)) {
1054 if (!what_changed.empty()) {
1055 send_change (what_changed);
1060 Region::set_hidden (bool yn)
1062 if (hidden() != yn) {
1064 send_change (Properties::hidden);
1069 Region::set_whole_file (bool yn)
1072 /* no change signal */
1076 Region::set_automatic (bool yn)
1079 /* no change signal */
1083 Region::set_muted (bool yn)
1085 if (muted() != yn) {
1087 send_change (Properties::muted);
1092 Region::set_opaque (bool yn)
1094 if (opaque() != yn) {
1096 send_change (Properties::opaque);
1101 Region::set_locked (bool yn)
1103 if (locked() != yn) {
1105 send_change (Properties::locked);
1110 Region::set_video_locked (bool yn)
1112 if (video_locked() != yn) {
1114 send_change (Properties::video_locked);
1119 Region::set_position_locked (bool yn)
1121 if (position_locked() != yn) {
1122 _position_locked = yn;
1123 send_change (Properties::locked);
1127 /** Set the region's sync point.
1128 * @param absolute_pos Session time.
1131 Region::set_sync_position (framepos_t absolute_pos)
1133 /* position within our file */
1134 framepos_t const file_pos = _start + (absolute_pos - _position);
1136 if (file_pos != _sync_position) {
1137 _sync_marked = true;
1138 _sync_position = file_pos;
1139 if (!property_changes_suspended()) {
1143 send_change (Properties::sync_position);
1148 Region::clear_sync_position ()
1150 if (sync_marked()) {
1151 _sync_marked = false;
1152 if (!property_changes_suspended()) {
1156 send_change (Properties::sync_position);
1160 /* @return the sync point relative the first frame of the region */
1162 Region::sync_offset (int& dir) const
1164 if (sync_marked()) {
1165 if (_sync_position > _start) {
1167 return _sync_position - _start;
1170 return _start - _sync_position;
1179 Region::adjust_to_sync (framepos_t pos) const
1182 frameoffset_t offset = sync_offset (sync_dir);
1184 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1193 if (max_framepos - pos > offset) {
1201 /** @return Sync position in session time */
1203 Region::sync_position() const
1205 if (sync_marked()) {
1206 return _position - _start + _sync_position;
1208 /* if sync has not been marked, use the start of the region */
1216 boost::shared_ptr<Playlist> pl (playlist());
1218 pl->raise_region (shared_from_this ());
1225 boost::shared_ptr<Playlist> pl (playlist());
1227 pl->lower_region (shared_from_this ());
1233 Region::raise_to_top ()
1235 boost::shared_ptr<Playlist> pl (playlist());
1237 pl->raise_region_to_top (shared_from_this());
1242 Region::lower_to_bottom ()
1244 boost::shared_ptr<Playlist> pl (playlist());
1246 pl->lower_region_to_bottom (shared_from_this());
1251 Region::set_layer (layer_t l)
1259 XMLNode *node = new XMLNode ("Region");
1263 /* custom version of 'add_properties (*node);'
1264 * skip values that have have dedicated save functions
1265 * in AudioRegion::state()
1267 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1268 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1269 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1270 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1271 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1272 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1273 i->second->get_value (*node);
1276 node->set_property ("id", id ());
1277 node->set_property ("type", _type);
1281 switch (_first_edit) {
1282 case EditChangesNothing:
1285 case EditChangesName:
1291 default: /* should be unreachable but makes g++ happy */
1296 node->set_property ("first-edit", fe);
1298 /* note: flags are stored by derived classes */
1300 for (uint32_t n=0; n < _sources.size(); ++n) {
1301 snprintf (buf2, sizeof(buf2), "source-%d", n);
1302 node->set_property (buf2, _sources[n]->id());
1305 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1306 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1307 node->set_property (buf2, _master_sources[n]->id ());
1310 /* Only store nested sources for the whole-file region that acts
1311 as the parent/root of all regions using it.
1314 if (_whole_file && max_source_level() > 0) {
1316 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1318 /* region is compound - get its playlist and
1319 store that before we list the region that
1323 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1324 nested_node->add_child_nocopy ((*s)->get_state ());
1328 node->add_child_nocopy (*nested_node);
1333 node->add_child_copy (*_extra_xml);
1340 Region::get_state ()
1346 Region::set_state (const XMLNode& node, int version)
1348 PropertyChange what_changed;
1349 return _set_state (node, version, what_changed, true);
1353 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1355 Timecode::BBT_Time bbt_time;
1357 Stateful::save_extra_xml (node);
1359 what_changed = set_values (node);
1363 if (_position_lock_style == MusicTime) {
1364 std::string bbt_str;
1365 if (node.get_property ("bbt-position", bbt_str)) {
1366 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1369 &bbt_time.ticks) != 3) {
1370 _position_lock_style = AudioTime;
1371 _beat = _session.tempo_map().beat_at_frame (_position);
1373 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1375 /* no position property change for legacy Property, so we do this here */
1376 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1380 /* fix problems with old sessions corrupted by impossible
1381 values for _stretch or _shift
1383 if (_stretch == 0.0f) {
1387 if (_shift == 0.0f) {
1392 send_change (what_changed);
1395 /* Quick fix for 2.x sessions when region is muted */
1397 if (node.get_property (X_("flags"), flags)) {
1398 if (string::npos != flags.find("Muted")){
1403 // saved property is invalid, region-transients are not saved
1404 if (_user_transients.size() == 0){
1405 _valid_transients = false;
1412 Region::suspend_property_changes ()
1414 Stateful::suspend_property_changes ();
1415 _last_length = _length;
1416 _last_position = _position;
1420 Region::mid_thaw (const PropertyChange& what_changed)
1422 if (what_changed.contains (Properties::length)) {
1423 if (what_changed.contains (Properties::position)) {
1424 recompute_at_start ();
1426 recompute_at_end ();
1431 Region::send_change (const PropertyChange& what_changed)
1433 if (what_changed.empty()) {
1437 Stateful::send_change (what_changed);
1439 if (!Stateful::property_changes_suspended()) {
1441 /* Try and send a shared_pointer unless this is part of the constructor.
1446 boost::shared_ptr<Region> rptr = shared_from_this();
1447 RegionPropertyChanged (rptr, what_changed);
1449 /* no shared_ptr available, relax; */
1455 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1457 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1461 Region::equivalent (boost::shared_ptr<const Region> other) const
1463 return _start == other->_start &&
1464 _position == other->_position &&
1465 _length == other->_length;
1469 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1471 return _start == other->_start &&
1472 _length == other->_length;
1476 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1478 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1482 Region::source_deleted (boost::weak_ptr<Source>)
1486 if (!_session.deletion_in_progress()) {
1487 /* this is a very special case: at least one of the region's
1488 sources has bee deleted, so invalidate all references to
1489 ourselves. Do NOT do this during session deletion, because
1490 then we run the risk that this will actually result
1491 in this object being deleted (as refcnt goes to zero)
1492 while emitting DropReferences.
1500 Region::master_source_names ()
1502 SourceList::iterator i;
1504 vector<string> names;
1505 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1506 names.push_back((*i)->name());
1513 Region::set_master_sources (const SourceList& srcs)
1515 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1516 (*i)->dec_use_count ();
1519 _master_sources = srcs;
1520 assert (_sources.size() == _master_sources.size());
1522 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1523 (*i)->inc_use_count ();
1528 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1533 if ((_sources.size() != other->_sources.size()) ||
1534 (_master_sources.size() != other->_master_sources.size())) {
1538 SourceList::const_iterator i;
1539 SourceList::const_iterator io;
1541 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1542 if ((*i)->id() != (*io)->id()) {
1547 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1548 if ((*i)->id() != (*io)->id()) {
1557 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1563 SourceList::const_iterator i;
1564 SourceList::const_iterator io;
1566 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1567 if ((*i)->id() == (*io)->id()) {
1576 Region::source_string () const
1578 //string res = itos(_sources.size());
1581 res << _sources.size() << ":";
1583 SourceList::const_iterator i;
1585 for (i = _sources.begin(); i != _sources.end(); ++i) {
1586 res << (*i)->id() << ":";
1589 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1590 res << (*i)->id() << ":";
1597 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1599 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1601 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1604 if (sources.find (ps) == sources.end()) {
1605 /* (Playlist)Source not currently in
1606 accumulating set, so recurse.
1608 ps->playlist()->deep_sources (sources);
1612 /* add this source */
1613 sources.insert (*i);
1616 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1618 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1621 if (sources.find (ps) == sources.end()) {
1622 /* (Playlist)Source not currently in
1623 accumulating set, so recurse.
1625 ps->playlist()->deep_sources (sources);
1629 /* add this source */
1630 sources.insert (*i);
1635 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1637 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1643 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1646 if (ps->playlist()->uses_source (source)) {
1653 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1659 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1662 if (ps->playlist()->uses_source (source)) {
1674 Region::source_length(uint32_t n) const
1676 assert (n < _sources.size());
1677 return _sources[n]->length (_position - _start);
1681 Region::verify_length (framecnt_t& len)
1683 if (source() && (source()->destructive() || source()->length_mutable())) {
1687 framecnt_t maxlen = 0;
1689 for (uint32_t n = 0; n < _sources.size(); ++n) {
1690 maxlen = max (maxlen, source_length(n) - _start);
1693 len = min (len, maxlen);
1699 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1701 if (source() && (source()->destructive() || source()->length_mutable())) {
1705 framecnt_t maxlen = 0;
1707 for (uint32_t n = 0; n < _sources.size(); ++n) {
1708 maxlen = max (maxlen, source_length(n) - new_start);
1711 new_length = min (new_length, maxlen);
1717 Region::verify_start (framepos_t pos)
1719 if (source() && (source()->destructive() || source()->length_mutable())) {
1723 for (uint32_t n = 0; n < _sources.size(); ++n) {
1724 if (pos > source_length(n) - _length) {
1732 Region::verify_start_mutable (framepos_t& new_start)
1734 if (source() && (source()->destructive() || source()->length_mutable())) {
1738 for (uint32_t n = 0; n < _sources.size(); ++n) {
1739 if (new_start > source_length(n) - _length) {
1740 new_start = source_length(n) - _length;
1746 boost::shared_ptr<Region>
1747 Region::get_parent() const
1749 boost::shared_ptr<Playlist> pl (playlist());
1752 boost::shared_ptr<Region> r;
1753 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1755 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1756 return boost::static_pointer_cast<Region> (r);
1760 return boost::shared_ptr<Region>();
1764 Region::apply (Filter& filter, Progress* progress)
1766 return filter.run (shared_from_this(), progress);
1771 Region::maybe_invalidate_transients ()
1773 bool changed = !_onsets.empty();
1776 if (_valid_transients || changed) {
1777 send_change (PropertyChange (Properties::valid_transients));
1783 Region::transients (AnalysisFeatureList& afl)
1785 int cnt = afl.empty() ? 0 : 1;
1787 Region::merge_features (afl, _onsets, _position);
1788 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1789 if (!_onsets.empty ()) {
1792 if (!_user_transients.empty ()) {
1797 // remove exact duplicates
1798 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1803 Region::has_transients () const
1805 if (!_user_transients.empty ()) {
1806 assert (_valid_transients);
1809 if (!_onsets.empty ()) {
1816 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1818 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1819 const frameoffset_t p = (*x) + off;
1820 if (p < first_frame() || p > last_frame()) {
1823 result.push_back (p);
1828 Region::drop_sources ()
1830 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1831 (*i)->dec_use_count ();
1836 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1837 (*i)->dec_use_count ();
1840 _master_sources.clear ();
1844 Region::use_sources (SourceList const & s)
1846 set<boost::shared_ptr<Source> > unique_srcs;
1848 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1850 _sources.push_back (*i);
1851 (*i)->inc_use_count ();
1852 _master_sources.push_back (*i);
1853 (*i)->inc_use_count ();
1855 /* connect only once to DropReferences, even if sources are replicated
1858 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1859 unique_srcs.insert (*i);
1860 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1866 Region::can_trim () const
1868 CanTrim ct = CanTrim (0);
1874 /* if not locked, we can always move the front later, and the end earlier
1877 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1879 if (start() != 0 || can_trim_start_before_source_start ()) {
1880 ct = CanTrim (ct | FrontTrimEarlier);
1883 if (!_sources.empty()) {
1884 if ((start() + length()) < _sources.front()->length (0)) {
1885 ct = CanTrim (ct | EndTrimLater);
1893 Region::max_source_level () const
1897 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1898 lvl = max (lvl, (*i)->level());
1905 Region::is_compound () const
1907 return max_source_level() > 0;
1911 Region::post_set (const PropertyChange& pc)
1913 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1917 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1923 Region::earliest_possible_position () const
1925 if (_start > _position) {
1928 return _position - _start;
1933 Region::latest_possible_frame () const
1935 framecnt_t minlen = max_framecnt;
1937 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1938 /* non-audio regions have a length that may vary based on their
1939 * position, so we have to pass it in the call.
1941 minlen = min (minlen, (*i)->length (_position));
1944 /* the latest possible last frame is determined by the current
1945 * position, plus the shortest source extent past _start.
1948 return _position + (minlen - _start) - 1;