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");
1262 /* custom version of 'add_properties (*node);'
1263 * skip values that have have dedicated save functions
1264 * in AudioRegion::state()
1266 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1267 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1268 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1269 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1270 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1271 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1272 i->second->get_value (*node);
1275 node->set_property ("id", id ());
1276 node->set_property ("type", _type);
1280 switch (_first_edit) {
1281 case EditChangesNothing:
1284 case EditChangesName:
1290 default: /* should be unreachable but makes g++ happy */
1295 node->set_property ("first-edit", fe);
1297 /* note: flags are stored by derived classes */
1299 for (uint32_t n=0; n < _sources.size(); ++n) {
1300 snprintf (buf2, sizeof(buf2), "source-%d", n);
1301 node->set_property (buf2, _sources[n]->id());
1304 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1305 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1306 node->set_property (buf2, _master_sources[n]->id ());
1309 /* Only store nested sources for the whole-file region that acts
1310 as the parent/root of all regions using it.
1313 if (_whole_file && max_source_level() > 0) {
1315 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1317 /* region is compound - get its playlist and
1318 store that before we list the region that
1322 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1323 nested_node->add_child_nocopy ((*s)->get_state ());
1327 node->add_child_nocopy (*nested_node);
1332 node->add_child_copy (*_extra_xml);
1339 Region::get_state ()
1345 Region::set_state (const XMLNode& node, int version)
1347 PropertyChange what_changed;
1348 return _set_state (node, version, what_changed, true);
1352 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1354 Timecode::BBT_Time bbt_time;
1356 Stateful::save_extra_xml (node);
1358 what_changed = set_values (node);
1362 if (_position_lock_style == MusicTime) {
1363 std::string bbt_str;
1364 if (node.get_property ("bbt-position", bbt_str)) {
1365 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1368 &bbt_time.ticks) != 3) {
1369 _position_lock_style = AudioTime;
1370 _beat = _session.tempo_map().beat_at_frame (_position);
1372 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1374 /* no position property change for legacy Property, so we do this here */
1375 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1379 /* fix problems with old sessions corrupted by impossible
1380 values for _stretch or _shift
1382 if (_stretch == 0.0f) {
1386 if (_shift == 0.0f) {
1391 send_change (what_changed);
1394 /* Quick fix for 2.x sessions when region is muted */
1396 if (node.get_property (X_("flags"), flags)) {
1397 if (string::npos != flags.find("Muted")){
1402 // saved property is invalid, region-transients are not saved
1403 if (_user_transients.size() == 0){
1404 _valid_transients = false;
1411 Region::suspend_property_changes ()
1413 Stateful::suspend_property_changes ();
1414 _last_length = _length;
1415 _last_position = _position;
1419 Region::mid_thaw (const PropertyChange& what_changed)
1421 if (what_changed.contains (Properties::length)) {
1422 if (what_changed.contains (Properties::position)) {
1423 recompute_at_start ();
1425 recompute_at_end ();
1430 Region::send_change (const PropertyChange& what_changed)
1432 if (what_changed.empty()) {
1436 Stateful::send_change (what_changed);
1438 if (!Stateful::property_changes_suspended()) {
1440 /* Try and send a shared_pointer unless this is part of the constructor.
1445 boost::shared_ptr<Region> rptr = shared_from_this();
1446 RegionPropertyChanged (rptr, what_changed);
1448 /* no shared_ptr available, relax; */
1454 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1456 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1460 Region::equivalent (boost::shared_ptr<const Region> other) const
1462 return _start == other->_start &&
1463 _position == other->_position &&
1464 _length == other->_length;
1468 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1470 return _start == other->_start &&
1471 _length == other->_length;
1475 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1477 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1481 Region::source_deleted (boost::weak_ptr<Source>)
1485 if (!_session.deletion_in_progress()) {
1486 /* this is a very special case: at least one of the region's
1487 sources has bee deleted, so invalidate all references to
1488 ourselves. Do NOT do this during session deletion, because
1489 then we run the risk that this will actually result
1490 in this object being deleted (as refcnt goes to zero)
1491 while emitting DropReferences.
1499 Region::master_source_names ()
1501 SourceList::iterator i;
1503 vector<string> names;
1504 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1505 names.push_back((*i)->name());
1512 Region::set_master_sources (const SourceList& srcs)
1514 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1515 (*i)->dec_use_count ();
1518 _master_sources = srcs;
1519 assert (_sources.size() == _master_sources.size());
1521 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1522 (*i)->inc_use_count ();
1527 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1532 if ((_sources.size() != other->_sources.size()) ||
1533 (_master_sources.size() != other->_master_sources.size())) {
1537 SourceList::const_iterator i;
1538 SourceList::const_iterator io;
1540 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1541 if ((*i)->id() != (*io)->id()) {
1546 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1547 if ((*i)->id() != (*io)->id()) {
1556 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1562 SourceList::const_iterator i;
1563 SourceList::const_iterator io;
1565 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1566 if ((*i)->id() == (*io)->id()) {
1575 Region::source_string () const
1577 //string res = itos(_sources.size());
1580 res << _sources.size() << ":";
1582 SourceList::const_iterator i;
1584 for (i = _sources.begin(); i != _sources.end(); ++i) {
1585 res << (*i)->id() << ":";
1588 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1589 res << (*i)->id() << ":";
1596 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1598 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1600 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1603 if (sources.find (ps) == sources.end()) {
1604 /* (Playlist)Source not currently in
1605 accumulating set, so recurse.
1607 ps->playlist()->deep_sources (sources);
1611 /* add this source */
1612 sources.insert (*i);
1615 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1617 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1620 if (sources.find (ps) == sources.end()) {
1621 /* (Playlist)Source not currently in
1622 accumulating set, so recurse.
1624 ps->playlist()->deep_sources (sources);
1628 /* add this source */
1629 sources.insert (*i);
1634 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1636 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1642 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1645 if (ps->playlist()->uses_source (source)) {
1652 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1658 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1661 if (ps->playlist()->uses_source (source)) {
1673 Region::source_length(uint32_t n) const
1675 assert (n < _sources.size());
1676 return _sources[n]->length (_position - _start);
1680 Region::verify_length (framecnt_t& len)
1682 if (source() && (source()->destructive() || source()->length_mutable())) {
1686 framecnt_t maxlen = 0;
1688 for (uint32_t n = 0; n < _sources.size(); ++n) {
1689 maxlen = max (maxlen, source_length(n) - _start);
1692 len = min (len, maxlen);
1698 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1700 if (source() && (source()->destructive() || source()->length_mutable())) {
1704 framecnt_t maxlen = 0;
1706 for (uint32_t n = 0; n < _sources.size(); ++n) {
1707 maxlen = max (maxlen, source_length(n) - new_start);
1710 new_length = min (new_length, maxlen);
1716 Region::verify_start (framepos_t pos)
1718 if (source() && (source()->destructive() || source()->length_mutable())) {
1722 for (uint32_t n = 0; n < _sources.size(); ++n) {
1723 if (pos > source_length(n) - _length) {
1731 Region::verify_start_mutable (framepos_t& new_start)
1733 if (source() && (source()->destructive() || source()->length_mutable())) {
1737 for (uint32_t n = 0; n < _sources.size(); ++n) {
1738 if (new_start > source_length(n) - _length) {
1739 new_start = source_length(n) - _length;
1745 boost::shared_ptr<Region>
1746 Region::get_parent() const
1748 boost::shared_ptr<Playlist> pl (playlist());
1751 boost::shared_ptr<Region> r;
1752 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1754 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1755 return boost::static_pointer_cast<Region> (r);
1759 return boost::shared_ptr<Region>();
1763 Region::apply (Filter& filter, Progress* progress)
1765 return filter.run (shared_from_this(), progress);
1770 Region::maybe_invalidate_transients ()
1772 bool changed = !_onsets.empty();
1775 if (_valid_transients || changed) {
1776 send_change (PropertyChange (Properties::valid_transients));
1782 Region::transients (AnalysisFeatureList& afl)
1784 int cnt = afl.empty() ? 0 : 1;
1786 Region::merge_features (afl, _onsets, _position);
1787 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1788 if (!_onsets.empty ()) {
1791 if (!_user_transients.empty ()) {
1796 // remove exact duplicates
1797 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1802 Region::has_transients () const
1804 if (!_user_transients.empty ()) {
1805 assert (_valid_transients);
1808 if (!_onsets.empty ()) {
1815 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1817 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1818 const frameoffset_t p = (*x) + off;
1819 if (p < first_frame() || p > last_frame()) {
1822 result.push_back (p);
1827 Region::drop_sources ()
1829 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1830 (*i)->dec_use_count ();
1835 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1836 (*i)->dec_use_count ();
1839 _master_sources.clear ();
1843 Region::use_sources (SourceList const & s)
1845 set<boost::shared_ptr<Source> > unique_srcs;
1847 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1849 _sources.push_back (*i);
1850 (*i)->inc_use_count ();
1851 _master_sources.push_back (*i);
1852 (*i)->inc_use_count ();
1854 /* connect only once to DropReferences, even if sources are replicated
1857 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1858 unique_srcs.insert (*i);
1859 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1865 Region::can_trim () const
1867 CanTrim ct = CanTrim (0);
1873 /* if not locked, we can always move the front later, and the end earlier
1876 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1878 if (start() != 0 || can_trim_start_before_source_start ()) {
1879 ct = CanTrim (ct | FrontTrimEarlier);
1882 if (!_sources.empty()) {
1883 if ((start() + length()) < _sources.front()->length (0)) {
1884 ct = CanTrim (ct | EndTrimLater);
1892 Region::max_source_level () const
1896 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1897 lvl = max (lvl, (*i)->level());
1904 Region::is_compound () const
1906 return max_source_level() > 0;
1910 Region::post_set (const PropertyChange& pc)
1912 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1916 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1922 Region::earliest_possible_position () const
1924 if (_start > _position) {
1927 return _position - _start;
1932 Region::latest_possible_frame () const
1934 framecnt_t minlen = max_framecnt;
1936 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1937 /* non-audio regions have a length that may vary based on their
1938 * position, so we have to pass it in the call.
1940 minlen = min (minlen, (*i)->length (_position));
1943 /* the latest possible last frame is determined by the current
1944 * position, plus the shortest source extent past _start.
1947 return _position + (minlen - _start) - 1;