2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<double> beat;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75 PBD::PropertyDescriptor<uint64_t> layering_index;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
92 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
94 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
96 Properties::import.property_id = g_quark_from_static_string (X_("import"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
98 Properties::external.property_id = g_quark_from_static_string (X_("external"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
100 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
102 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
104 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
106 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
108 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
110 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
112 Properties::start.property_id = g_quark_from_static_string (X_("start"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
114 Properties::length.property_id = g_quark_from_static_string (X_("length"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
116 Properties::position.property_id = g_quark_from_static_string (X_("position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
118 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
139 Region::register_properties ()
141 _xml_node_name = X_("Region");
143 add_property (_muted);
144 add_property (_opaque);
145 add_property (_locked);
146 add_property (_video_locked);
147 add_property (_automatic);
148 add_property (_whole_file);
149 add_property (_import);
150 add_property (_external);
151 add_property (_sync_marked);
152 add_property (_left_of_split);
153 add_property (_right_of_split);
154 add_property (_hidden);
155 add_property (_position_locked);
156 add_property (_valid_transients);
157 add_property (_start);
158 add_property (_length);
159 add_property (_position);
160 add_property (_beat);
161 add_property (_sync_position);
162 add_property (_ancestral_start);
163 add_property (_ancestral_length);
164 add_property (_stretch);
165 add_property (_shift);
166 add_property (_position_lock_style);
167 add_property (_layering_index);
170 #define REGION_DEFAULT_STATE(s,l) \
171 _sync_marked (Properties::sync_marked, false) \
172 , _left_of_split (Properties::left_of_split, false) \
173 , _right_of_split (Properties::right_of_split, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _beat (Properties::beat, 0.0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _quarter_note (0.0) \
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 , _quarter_note (other->_quarter_note) \
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 _quarter_note = other->_quarter_note;
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, MusicFrame offset)
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
350 use_sources (other->_sources);
351 set_master_sources (other->_master_sources);
353 _position = other->_position + offset.frame;
354 _start = other->_start + offset.frame;
356 /* prevent offset of 0 from altering musical position */
357 if (offset.frame != 0) {
358 const double offset_qn = _session.tempo_map().exact_qn_at_frame (other->_position + offset.frame, offset.division)
359 - other->_quarter_note;
361 _quarter_note = other->_quarter_note + offset_qn;
362 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
364 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
367 /* if the other region had a distinct sync point
368 set, then continue to use it as best we can.
369 otherwise, reset sync point back to start.
372 if (other->sync_marked()) {
373 if (other->_sync_position < _start) {
374 _sync_marked = false;
375 _sync_position = _start;
377 _sync_position = other->_sync_position;
380 _sync_marked = false;
381 _sync_position = _start;
384 assert (_type == other->data_type());
387 /** Create a copy of @param other but with different sources. Used by filters */
388 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
389 : SessionObject (other->session(), other->name())
390 , _type (srcs.front()->type())
391 , REGION_COPY_STATE (other)
392 , _last_length (other->_last_length)
393 , _last_position (other->_last_position)
394 , _first_edit (EditChangesID)
395 , _layer (other->_layer)
397 register_properties ();
400 _position_locked = false;
402 other->_first_edit = EditChangesName;
404 if (other->_extra_xml) {
405 _extra_xml = new XMLNode (*other->_extra_xml);
411 assert(_sources.size() > 0);
416 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
421 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
423 _playlist = wpl.lock();
427 Region::set_name (const std::string& str)
430 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
431 assert(_name == str);
433 send_change (Properties::name);
440 Region::set_length (framecnt_t len, const int32_t sub_num)
442 //cerr << "Region::set_length() len = " << len << endl;
447 if (_length != len && len != 0) {
449 /* check that the current _position wouldn't make the new
453 if (max_framepos - len < _position) {
457 if (!verify_length (len)) {
462 set_length_internal (len, sub_num);
466 maybe_invalidate_transients ();
468 if (!property_changes_suspended()) {
472 send_change (Properties::length);
477 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
479 _last_length = _length;
484 Region::maybe_uncopy ()
486 /* this does nothing but marked a semantic moment once upon a time */
490 Region::first_edit ()
492 boost::shared_ptr<Playlist> pl (playlist());
494 if (_first_edit != EditChangesNothing && pl) {
496 _name = RegionFactory::new_region_name (_name);
497 _first_edit = EditChangesNothing;
499 send_change (Properties::name);
501 RegionFactory::CheckNewRegion (shared_from_this());
506 Region::at_natural_position () const
508 boost::shared_ptr<Playlist> pl (playlist());
514 boost::shared_ptr<Region> whole_file_region = get_parent();
516 if (whole_file_region) {
517 if (_position == whole_file_region->position() + _start) {
526 Region::move_to_natural_position ()
528 boost::shared_ptr<Playlist> pl (playlist());
534 boost::shared_ptr<Region> whole_file_region = get_parent();
536 if (whole_file_region) {
537 set_position (whole_file_region->position() + _start);
542 Region::special_set_position (framepos_t pos)
544 /* this is used when creating a whole file region as
545 a way to store its "natural" or "captured" position.
548 _position = _position;
553 Region::set_position_lock_style (PositionLockStyle ps)
555 if (_position_lock_style != ps) {
557 boost::shared_ptr<Playlist> pl (playlist());
559 _position_lock_style = ps;
561 send_change (Properties::position_lock_style);
566 Region::update_after_tempo_map_change (bool send)
568 boost::shared_ptr<Playlist> pl (playlist());
574 if (_position_lock_style == AudioTime) {
575 /* don't signal as the actual position has not chnged */
576 recompute_position_from_lock_style (0);
580 /* prevent movement before 0 */
581 const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
582 /* we have _beat. update frame position non-musically */
583 set_position_internal (pos, false, 0);
585 /* do this even if the position is the same. this helps out
586 a GUI that has moved its representation already.
590 send_change (Properties::position);
595 Region::set_position (framepos_t pos, int32_t sub_num)
601 /* do this even if the position is the same. this helps out
602 a GUI that has moved its representation already.
604 PropertyChange p_and_l;
606 p_and_l.add (Properties::position);
608 if (position_lock_style() == AudioTime) {
609 set_position_internal (pos, true, sub_num);
611 if (!_session.loading()) {
612 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
613 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
616 set_position_internal (pos, false, sub_num);
619 if (position_lock_style() == MusicTime) {
620 p_and_l.add (Properties::length);
623 send_change (p_and_l);
628 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
630 /* We emit a change of Properties::position even if the position hasn't changed
631 (see Region::set_position), so we must always set this up so that
632 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
634 _last_position = _position;
636 if (_position != pos) {
639 if (allow_bbt_recompute) {
640 recompute_position_from_lock_style (sub_num);
642 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
643 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
646 /* check that the new _position wouldn't make the current
647 length impossible - if so, change the length.
649 XXX is this the right thing to do?
651 if (max_framepos - _length < _position) {
652 _last_length = _length;
653 _length = max_framepos - _position;
659 Region::set_position_music (double qn)
665 /* do this even if the position is the same. this helps out
666 a GUI that has moved its representation already.
668 PropertyChange p_and_l;
670 p_and_l.add (Properties::position);
672 if (!_session.loading()) {
673 _beat = _session.tempo_map().beat_at_quarter_note (qn);
676 /* will set frame accordingly */
677 set_position_music_internal (qn);
679 if (position_lock_style() == MusicTime) {
680 p_and_l.add (Properties::length);
683 send_change (p_and_l);
687 Region::set_position_music_internal (double qn)
689 /* We emit a change of Properties::position even if the position hasn't changed
690 (see Region::set_position), so we must always set this up so that
691 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
693 _last_position = _position;
695 if (_quarter_note != qn) {
696 _position = _session.tempo_map().frame_at_quarter_note (qn);
699 /* check that the new _position wouldn't make the current
700 length impossible - if so, change the length.
702 XXX is this the right thing to do?
704 if (max_framepos - _length < _position) {
705 _last_length = _length;
706 _length = max_framepos - _position;
711 /** A gui may need to create a region, then place it in an initial
712 * position determined by the user.
713 * When this takes place within one gui operation, we have to reset
714 * _last_position to prevent an implied move.
717 Region::set_initial_position (framepos_t pos)
723 if (_position != pos) {
726 /* check that the new _position wouldn't make the current
727 length impossible - if so, change the length.
729 XXX is this the right thing to do?
732 if (max_framepos - _length < _position) {
733 _last_length = _length;
734 _length = max_framepos - _position;
737 recompute_position_from_lock_style (0);
738 /* ensure that this move doesn't cause a range move */
739 _last_position = _position;
743 /* do this even if the position is the same. this helps out
744 a GUI that has moved its representation already.
746 send_change (Properties::position);
750 Region::recompute_position_from_lock_style (const int32_t sub_num)
752 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
753 _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
757 Region::nudge_position (frameoffset_t n)
759 if (locked() || video_locked()) {
767 framepos_t new_position = _position;
770 if (_position > max_framepos - n) {
771 new_position = max_framepos;
776 if (_position < -n) {
782 /* assumes non-musical nudge */
783 set_position_internal (new_position, true, 0);
785 send_change (Properties::position);
789 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
791 _ancestral_length = l;
792 _ancestral_start = s;
798 Region::set_start (framepos_t pos)
800 if (locked() || position_locked() || video_locked()) {
803 /* This just sets the start, nothing else. It effectively shifts
804 the contents of the Region within the overall extent of the Source,
805 without changing the Region's position or length
810 if (!verify_start (pos)) {
814 set_start_internal (pos);
817 maybe_invalidate_transients ();
819 send_change (Properties::start);
824 Region::move_start (frameoffset_t distance, const int32_t sub_num)
826 if (locked() || position_locked() || video_locked()) {
830 framepos_t new_start;
834 if (_start > max_framepos - distance) {
835 new_start = max_framepos; // makes no sense
837 new_start = _start + distance;
840 if (!verify_start (new_start)) {
844 } else if (distance < 0) {
846 if (_start < -distance) {
849 new_start = _start + distance;
856 if (new_start == _start) {
860 set_start_internal (new_start, sub_num);
865 send_change (Properties::start);
869 Region::trim_front (framepos_t new_position, const int32_t sub_num)
871 modify_front (new_position, false, sub_num);
875 Region::cut_front (framepos_t new_position, const int32_t sub_num)
877 modify_front (new_position, true, sub_num);
881 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
883 modify_end (new_endpoint, true, sub_num);
887 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
893 framepos_t end = last_frame();
894 framepos_t source_zero;
896 if (_position > _start) {
897 source_zero = _position - _start;
899 source_zero = 0; // its actually negative, but this will work for us
902 if (new_position < end) { /* can't trim it zero or negative length */
904 framecnt_t newlen = 0;
906 if (!can_trim_start_before_source_start ()) {
907 /* can't trim it back past where source position zero is located */
908 new_position = max (new_position, source_zero);
911 if (new_position > _position) {
912 newlen = _length - (new_position - _position);
914 newlen = _length + (_position - new_position);
917 trim_to_internal (new_position, newlen, sub_num);
920 _right_of_split = true;
923 if (!property_changes_suspended()) {
924 recompute_at_start ();
927 maybe_invalidate_transients ();
932 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
938 if (new_endpoint > _position) {
939 trim_to_internal (_position, new_endpoint - _position, sub_num);
941 _left_of_split = true;
943 if (!property_changes_suspended()) {
949 /** @param new_endpoint New region end point, such that, for example,
950 * a region at 0 of length 10 has an endpoint of 9.
954 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
956 modify_end (new_endpoint, false, sub_num);
960 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
966 trim_to_internal (position, length, sub_num);
968 if (!property_changes_suspended()) {
969 recompute_at_start ();
975 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
977 framepos_t new_start;
983 frameoffset_t const start_shift = position - _position;
985 if (start_shift > 0) {
987 if (_start > max_framepos - start_shift) {
988 new_start = max_framepos;
990 new_start = _start + start_shift;
993 } else if (start_shift < 0) {
995 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
998 new_start = _start + start_shift;
1005 if (!verify_start_and_length (new_start, length)) {
1009 PropertyChange what_changed;
1011 if (_start != new_start) {
1012 set_start_internal (new_start, sub_num);
1013 what_changed.add (Properties::start);
1017 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1018 * 1. we call set_length_internal; length in beats is computed using the region's current
1019 * (soon-to-be old) position
1020 * 2. we call set_position_internal; position is set and length in frames re-computed using
1021 * length in beats from (1) but at the new position, which is wrong if the region
1022 * straddles a tempo/meter change.
1025 if (_position != position) {
1026 if (!property_changes_suspended()) {
1027 _last_position = _position;
1029 set_position_internal (position, true, sub_num);
1030 what_changed.add (Properties::position);
1033 if (_length != length) {
1034 if (!property_changes_suspended()) {
1035 _last_length = _length;
1037 set_length_internal (length, sub_num);
1038 what_changed.add (Properties::length);
1041 _whole_file = false;
1043 PropertyChange start_and_length;
1045 start_and_length.add (Properties::start);
1046 start_and_length.add (Properties::length);
1048 if (what_changed.contains (start_and_length)) {
1052 if (!what_changed.empty()) {
1053 send_change (what_changed);
1058 Region::set_hidden (bool yn)
1060 if (hidden() != yn) {
1062 send_change (Properties::hidden);
1067 Region::set_whole_file (bool yn)
1070 /* no change signal */
1074 Region::set_automatic (bool yn)
1077 /* no change signal */
1081 Region::set_muted (bool yn)
1083 if (muted() != yn) {
1085 send_change (Properties::muted);
1090 Region::set_opaque (bool yn)
1092 if (opaque() != yn) {
1094 send_change (Properties::opaque);
1099 Region::set_locked (bool yn)
1101 if (locked() != yn) {
1103 send_change (Properties::locked);
1108 Region::set_video_locked (bool yn)
1110 if (video_locked() != yn) {
1112 send_change (Properties::video_locked);
1117 Region::set_position_locked (bool yn)
1119 if (position_locked() != yn) {
1120 _position_locked = yn;
1121 send_change (Properties::locked);
1125 /** Set the region's sync point.
1126 * @param absolute_pos Session time.
1129 Region::set_sync_position (framepos_t absolute_pos)
1131 /* position within our file */
1132 framepos_t const file_pos = _start + (absolute_pos - _position);
1134 if (file_pos != _sync_position) {
1135 _sync_marked = true;
1136 _sync_position = file_pos;
1137 if (!property_changes_suspended()) {
1141 send_change (Properties::sync_position);
1146 Region::clear_sync_position ()
1148 if (sync_marked()) {
1149 _sync_marked = false;
1150 if (!property_changes_suspended()) {
1154 send_change (Properties::sync_position);
1158 /* @return the sync point relative the first frame of the region */
1160 Region::sync_offset (int& dir) const
1162 if (sync_marked()) {
1163 if (_sync_position > _start) {
1165 return _sync_position - _start;
1168 return _start - _sync_position;
1177 Region::adjust_to_sync (framepos_t pos) const
1180 frameoffset_t offset = sync_offset (sync_dir);
1182 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1191 if (max_framepos - pos > offset) {
1199 /** @return Sync position in session time */
1201 Region::sync_position() const
1203 if (sync_marked()) {
1204 return _position - _start + _sync_position;
1206 /* if sync has not been marked, use the start of the region */
1214 boost::shared_ptr<Playlist> pl (playlist());
1216 pl->raise_region (shared_from_this ());
1223 boost::shared_ptr<Playlist> pl (playlist());
1225 pl->lower_region (shared_from_this ());
1231 Region::raise_to_top ()
1233 boost::shared_ptr<Playlist> pl (playlist());
1235 pl->raise_region_to_top (shared_from_this());
1240 Region::lower_to_bottom ()
1242 boost::shared_ptr<Playlist> pl (playlist());
1244 pl->lower_region_to_bottom (shared_from_this());
1249 Region::set_layer (layer_t l)
1257 XMLNode *node = new XMLNode ("Region");
1261 const char* fe = NULL;
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 id().print (buf, sizeof (buf));
1277 node->add_property ("id", buf);
1278 node->add_property ("type", _type.to_string());
1280 switch (_first_edit) {
1281 case EditChangesNothing:
1284 case EditChangesName:
1290 default: /* should be unreachable but makes g++ happy */
1295 node->add_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 _sources[n]->id().print (buf, sizeof(buf));
1302 node->add_property (buf2, buf);
1305 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1306 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1307 _master_sources[n]->id().print (buf, sizeof (buf));
1308 node->add_property (buf2, buf);
1311 /* Only store nested sources for the whole-file region that acts
1312 as the parent/root of all regions using it.
1315 if (_whole_file && max_source_level() > 0) {
1317 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1319 /* region is compound - get its playlist and
1320 store that before we list the region that
1324 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1325 nested_node->add_child_nocopy ((*s)->get_state ());
1329 node->add_child_nocopy (*nested_node);
1334 node->add_child_copy (*_extra_xml);
1341 Region::get_state ()
1347 Region::set_state (const XMLNode& node, int version)
1349 PropertyChange what_changed;
1350 return _set_state (node, version, what_changed, true);
1354 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1356 XMLProperty const * prop;
1357 Timecode::BBT_Time bbt_time;
1359 Stateful::save_extra_xml (node);
1361 what_changed = set_values (node);
1365 if (_position_lock_style == MusicTime) {
1366 if ((prop = node.property ("bbt-position")) != 0) {
1367 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1370 &bbt_time.ticks) != 3) {
1371 _position_lock_style = AudioTime;
1372 _beat = _session.tempo_map().beat_at_frame (_position);
1374 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1376 /* no position property change for legacy Property, so we do this here */
1377 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1381 /* fix problems with old sessions corrupted by impossible
1382 values for _stretch or _shift
1384 if (_stretch == 0.0f) {
1388 if (_shift == 0.0f) {
1393 send_change (what_changed);
1396 /* Quick fix for 2.x sessions when region is muted */
1397 if ((prop = node.property (X_("flags")))) {
1398 if (string::npos != prop->value().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;