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<samplepos_t> start;
67 PBD::PropertyDescriptor<samplecnt_t> length;
68 PBD::PropertyDescriptor<samplepos_t> position;
69 PBD::PropertyDescriptor<double> beat;
70 PBD::PropertyDescriptor<samplecnt_t> sync_position;
71 PBD::PropertyDescriptor<layer_t> layer;
72 PBD::PropertyDescriptor<samplepos_t> ancestral_start;
73 PBD::PropertyDescriptor<samplecnt_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 , _soloSelected (false) \
187 , _muted (Properties::muted, false) \
188 , _opaque (Properties::opaque, true) \
189 , _locked (Properties::locked, false) \
190 , _video_locked (Properties::video_locked, false) \
191 , _automatic (Properties::automatic, false) \
192 , _whole_file (Properties::whole_file, false) \
193 , _import (Properties::import, false) \
194 , _external (Properties::external, false) \
195 , _hidden (Properties::hidden, false) \
196 , _position_locked (Properties::position_locked, false) \
197 , _ancestral_start (Properties::ancestral_start, (s)) \
198 , _ancestral_length (Properties::ancestral_length, (l)) \
199 , _stretch (Properties::stretch, 1.0) \
200 , _shift (Properties::shift, 1.0) \
201 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
202 , _layering_index (Properties::layering_index, 0)
204 #define REGION_COPY_STATE(other) \
205 _sync_marked (Properties::sync_marked, other->_sync_marked) \
206 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
207 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
208 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
209 , _start(Properties::start, other->_start) \
210 , _length(Properties::length, other->_length) \
211 , _position(Properties::position, other->_position) \
212 , _beat (Properties::beat, other->_beat) \
213 , _sync_position(Properties::sync_position, other->_sync_position) \
214 , _quarter_note (other->_quarter_note) \
215 , _user_transients (other->_user_transients) \
216 , _transient_user_start (other->_transient_user_start) \
217 , _transients (other->_transients) \
218 , _transient_analysis_start (other->_transient_analysis_start) \
219 , _transient_analysis_end (other->_transient_analysis_end) \
220 , _soloSelected (false) \
221 , _muted (Properties::muted, other->_muted) \
222 , _opaque (Properties::opaque, other->_opaque) \
223 , _locked (Properties::locked, other->_locked) \
224 , _video_locked (Properties::video_locked, other->_video_locked) \
225 , _automatic (Properties::automatic, other->_automatic) \
226 , _whole_file (Properties::whole_file, other->_whole_file) \
227 , _import (Properties::import, other->_import) \
228 , _external (Properties::external, other->_external) \
229 , _hidden (Properties::hidden, other->_hidden) \
230 , _position_locked (Properties::position_locked, other->_position_locked) \
231 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
232 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
233 , _stretch (Properties::stretch, other->_stretch) \
234 , _shift (Properties::shift, other->_shift) \
235 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
236 , _layering_index (Properties::layering_index, other->_layering_index)
238 /* derived-from-derived constructor (no sources in constructor) */
239 Region::Region (Session& s, samplepos_t start, samplecnt_t length, const string& name, DataType type)
240 : SessionObject(s, name)
242 , REGION_DEFAULT_STATE(start,length)
243 , _last_length (length)
245 , _first_edit (EditChangesNothing)
248 register_properties ();
250 /* no sources at this point */
253 /** Basic Region constructor (many sources) */
254 Region::Region (const SourceList& srcs)
255 : SessionObject(srcs.front()->session(), "toBeRenamed")
256 , _type (srcs.front()->type())
257 , REGION_DEFAULT_STATE(0,0)
260 , _first_edit (EditChangesNothing)
263 register_properties ();
265 _type = srcs.front()->type();
269 assert(_sources.size() > 0);
270 assert (_type == srcs.front()->type());
273 /** Create a new Region from an existing one */
274 Region::Region (boost::shared_ptr<const Region> other)
275 : SessionObject(other->session(), other->name())
276 , _type (other->data_type())
277 , REGION_COPY_STATE (other)
278 , _last_length (other->_last_length)
279 , _last_position(other->_last_position) \
280 , _first_edit (EditChangesNothing)
281 , _layer (other->_layer)
283 register_properties ();
285 /* override state that may have been incorrectly inherited from the other region
288 _position = other->_position;
293 use_sources (other->_sources);
294 set_master_sources (other->_master_sources);
296 _position_lock_style = other->_position_lock_style;
297 _first_edit = other->_first_edit;
299 _start = other->_start;
300 _beat = other->_beat;
301 _quarter_note = other->_quarter_note;
303 /* sync pos is relative to start of file. our start-in-file is now zero,
304 so set our sync position to whatever the the difference between
305 _start and _sync_pos was in the other region.
307 result is that our new sync pos points to the same point in our source(s)
308 as the sync in the other region did in its source(s).
310 since we start at zero in our source(s), it is not possible to use a sync point that
311 is before the start. reset it to _start if that was true in the other region.
314 if (other->sync_marked()) {
315 if (other->_start < other->_sync_position) {
316 /* sync pos was after the start point of the other region */
317 _sync_position = other->_sync_position - other->_start;
319 /* sync pos was before the start point of the other region. not possible here. */
320 _sync_marked = false;
321 _sync_position = _start;
324 _sync_marked = false;
325 _sync_position = _start;
328 assert (_type == other->data_type());
331 /** Create a new Region from part of an existing one.
333 the start within \a other is given by \a offset
334 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
336 Region::Region (boost::shared_ptr<const Region> other, MusicSample offset)
337 : SessionObject(other->session(), other->name())
338 , _type (other->data_type())
339 , REGION_COPY_STATE (other)
340 , _last_length (other->_last_length)
341 , _last_position(other->_last_position) \
342 , _first_edit (EditChangesNothing)
343 , _layer (other->_layer)
345 register_properties ();
347 /* override state that may have been incorrectly inherited from the other region
354 use_sources (other->_sources);
355 set_master_sources (other->_master_sources);
357 _position = other->_position + offset.sample;
358 _start = other->_start + offset.sample;
360 /* prevent offset of 0 from altering musical position */
361 if (offset.sample != 0) {
362 const double offset_qn = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division)
363 - other->_quarter_note;
365 _quarter_note = other->_quarter_note + offset_qn;
366 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
368 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
371 /* if the other region had a distinct sync point
372 set, then continue to use it as best we can.
373 otherwise, reset sync point back to start.
376 if (other->sync_marked()) {
377 if (other->_sync_position < _start) {
378 _sync_marked = false;
379 _sync_position = _start;
381 _sync_position = other->_sync_position;
384 _sync_marked = false;
385 _sync_position = _start;
388 assert (_type == other->data_type());
391 /** Create a copy of @param other but with different sources. Used by filters */
392 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
393 : SessionObject (other->session(), other->name())
394 , _type (srcs.front()->type())
395 , REGION_COPY_STATE (other)
396 , _last_length (other->_last_length)
397 , _last_position (other->_last_position)
398 , _first_edit (EditChangesID)
399 , _layer (other->_layer)
401 register_properties ();
404 _position_locked = false;
406 other->_first_edit = EditChangesName;
408 if (other->_extra_xml) {
409 _extra_xml = new XMLNode (*other->_extra_xml);
415 assert(_sources.size() > 0);
420 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
425 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
427 _playlist = wpl.lock();
431 Region::set_name (const std::string& str)
434 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
435 assert(_name == str);
437 send_change (Properties::name);
444 Region::set_selected_for_solo(bool yn)
446 if (_soloSelected != yn) {
448 boost::shared_ptr<Playlist> pl (playlist());
451 pl->AddToSoloSelectedList(this);
453 pl->RemoveFromSoloSelectedList(this);
463 Region::set_length (samplecnt_t len, const int32_t sub_num)
465 //cerr << "Region::set_length() len = " << len << endl;
470 if (_length != len && len != 0) {
472 /* check that the current _position wouldn't make the new
476 if (max_samplepos - len < _position) {
480 if (!verify_length (len)) {
485 set_length_internal (len, sub_num);
489 maybe_invalidate_transients ();
491 if (!property_changes_suspended()) {
495 send_change (Properties::length);
500 Region::set_length_internal (samplecnt_t len, const int32_t sub_num)
502 _last_length = _length;
507 Region::maybe_uncopy ()
509 /* this does nothing but marked a semantic moment once upon a time */
513 Region::first_edit ()
515 boost::shared_ptr<Playlist> pl (playlist());
517 if (_first_edit != EditChangesNothing && pl) {
519 _name = RegionFactory::new_region_name (_name);
520 _first_edit = EditChangesNothing;
522 send_change (Properties::name);
524 RegionFactory::CheckNewRegion (shared_from_this());
529 Region::at_natural_position () const
531 boost::shared_ptr<Playlist> pl (playlist());
537 boost::shared_ptr<Region> whole_file_region = get_parent();
539 if (whole_file_region) {
540 if (_position == whole_file_region->position() + _start) {
549 Region::move_to_natural_position ()
551 boost::shared_ptr<Playlist> pl (playlist());
557 boost::shared_ptr<Region> whole_file_region = get_parent();
559 if (whole_file_region) {
560 set_position (whole_file_region->position() + _start);
565 Region::special_set_position (samplepos_t pos)
567 /* this is used when creating a whole file region as
568 a way to store its "natural" or "captured" position.
571 _position = _position;
576 Region::set_position_lock_style (PositionLockStyle ps)
578 if (_position_lock_style != ps) {
580 boost::shared_ptr<Playlist> pl (playlist());
582 _position_lock_style = ps;
584 send_change (Properties::position_lock_style);
589 Region::update_after_tempo_map_change (bool send)
591 boost::shared_ptr<Playlist> pl (playlist());
597 if (_position_lock_style == AudioTime) {
598 /* don't signal as the actual position has not chnged */
599 recompute_position_from_lock_style (0);
603 /* prevent movement before 0 */
604 const samplepos_t pos = max ((samplepos_t) 0, _session.tempo_map().sample_at_beat (_beat));
605 /* we have _beat. update sample position non-musically */
606 set_position_internal (pos, false, 0);
608 /* do this even if the position is the same. this helps out
609 a GUI that has moved its representation already.
613 send_change (Properties::position);
618 Region::set_position (samplepos_t pos, int32_t sub_num)
624 /* do this even if the position is the same. this helps out
625 a GUI that has moved its representation already.
627 PropertyChange p_and_l;
629 p_and_l.add (Properties::position);
631 if (position_lock_style() == AudioTime) {
632 set_position_internal (pos, true, sub_num);
634 if (!_session.loading()) {
635 _beat = _session.tempo_map().exact_beat_at_sample (pos, sub_num);
636 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
639 set_position_internal (pos, false, sub_num);
642 if (position_lock_style() == MusicTime) {
643 p_and_l.add (Properties::length);
646 send_change (p_and_l);
651 Region::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
653 /* We emit a change of Properties::position even if the position hasn't changed
654 (see Region::set_position), so we must always set this up so that
655 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
657 _last_position = _position;
659 if (_position != pos) {
662 if (allow_bbt_recompute) {
663 recompute_position_from_lock_style (sub_num);
665 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
666 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
669 /* check that the new _position wouldn't make the current
670 length impossible - if so, change the length.
672 XXX is this the right thing to do?
674 if (max_samplepos - _length < _position) {
675 _last_length = _length;
676 _length = max_samplepos - _position;
682 Region::set_position_music (double qn)
688 /* do this even if the position is the same. this helps out
689 a GUI that has moved its representation already.
691 PropertyChange p_and_l;
693 p_and_l.add (Properties::position);
695 if (!_session.loading()) {
696 _beat = _session.tempo_map().beat_at_quarter_note (qn);
699 /* will set sample accordingly */
700 set_position_music_internal (qn);
702 if (position_lock_style() == MusicTime) {
703 p_and_l.add (Properties::length);
706 send_change (p_and_l);
710 Region::set_position_music_internal (double qn)
712 /* We emit a change of Properties::position even if the position hasn't changed
713 (see Region::set_position), so we must always set this up so that
714 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
716 _last_position = _position;
718 if (_quarter_note != qn) {
719 _position = _session.tempo_map().sample_at_quarter_note (qn);
722 /* check that the new _position wouldn't make the current
723 length impossible - if so, change the length.
725 XXX is this the right thing to do?
727 if (max_samplepos - _length < _position) {
728 _last_length = _length;
729 _length = max_samplepos - _position;
734 /** A gui may need to create a region, then place it in an initial
735 * position determined by the user.
736 * When this takes place within one gui operation, we have to reset
737 * _last_position to prevent an implied move.
740 Region::set_initial_position (samplepos_t pos)
746 if (_position != pos) {
749 /* check that the new _position wouldn't make the current
750 length impossible - if so, change the length.
752 XXX is this the right thing to do?
755 if (max_samplepos - _length < _position) {
756 _last_length = _length;
757 _length = max_samplepos - _position;
760 recompute_position_from_lock_style (0);
761 /* ensure that this move doesn't cause a range move */
762 _last_position = _position;
766 /* do this even if the position is the same. this helps out
767 a GUI that has moved its representation already.
769 send_change (Properties::position);
773 Region::recompute_position_from_lock_style (const int32_t sub_num)
775 _beat = _session.tempo_map().exact_beat_at_sample (_position, sub_num);
776 _quarter_note = _session.tempo_map().exact_qn_at_sample (_position, sub_num);
780 Region::nudge_position (sampleoffset_t n)
782 if (locked() || video_locked()) {
790 samplepos_t new_position = _position;
793 if (_position > max_samplepos - n) {
794 new_position = max_samplepos;
799 if (_position < -n) {
805 /* assumes non-musical nudge */
806 set_position_internal (new_position, true, 0);
808 send_change (Properties::position);
812 Region::set_ancestral_data (samplepos_t s, samplecnt_t l, float st, float sh)
814 _ancestral_length = l;
815 _ancestral_start = s;
821 Region::set_start (samplepos_t pos)
823 if (locked() || position_locked() || video_locked()) {
826 /* This just sets the start, nothing else. It effectively shifts
827 the contents of the Region within the overall extent of the Source,
828 without changing the Region's position or length
833 if (!verify_start (pos)) {
837 set_start_internal (pos);
840 maybe_invalidate_transients ();
842 send_change (Properties::start);
847 Region::move_start (sampleoffset_t distance, const int32_t sub_num)
849 if (locked() || position_locked() || video_locked()) {
853 samplepos_t new_start;
857 if (_start > max_samplepos - distance) {
858 new_start = max_samplepos; // makes no sense
860 new_start = _start + distance;
863 if (!verify_start (new_start)) {
867 } else if (distance < 0) {
869 if (_start < -distance) {
872 new_start = _start + distance;
879 if (new_start == _start) {
883 set_start_internal (new_start, sub_num);
888 send_change (Properties::start);
892 Region::trim_front (samplepos_t new_position, const int32_t sub_num)
894 modify_front (new_position, false, sub_num);
898 Region::cut_front (samplepos_t new_position, const int32_t sub_num)
900 modify_front (new_position, true, sub_num);
904 Region::cut_end (samplepos_t new_endpoint, const int32_t sub_num)
906 modify_end (new_endpoint, true, sub_num);
910 Region::modify_front (samplepos_t new_position, bool reset_fade, const int32_t sub_num)
916 samplepos_t end = last_sample();
917 samplepos_t source_zero;
919 if (_position > _start) {
920 source_zero = _position - _start;
922 source_zero = 0; // its actually negative, but this will work for us
925 if (new_position < end) { /* can't trim it zero or negative length */
927 samplecnt_t newlen = 0;
929 if (!can_trim_start_before_source_start ()) {
930 /* can't trim it back past where source position zero is located */
931 new_position = max (new_position, source_zero);
934 if (new_position > _position) {
935 newlen = _length - (new_position - _position);
937 newlen = _length + (_position - new_position);
940 trim_to_internal (new_position, newlen, sub_num);
943 _right_of_split = true;
946 if (!property_changes_suspended()) {
947 recompute_at_start ();
950 maybe_invalidate_transients ();
955 Region::modify_end (samplepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
961 if (new_endpoint > _position) {
962 trim_to_internal (_position, new_endpoint - _position, sub_num);
964 _left_of_split = true;
966 if (!property_changes_suspended()) {
972 /** @param new_endpoint New region end point, such that, for example,
973 * a region at 0 of length 10 has an endpoint of 9.
977 Region::trim_end (samplepos_t new_endpoint, const int32_t sub_num)
979 modify_end (new_endpoint, false, sub_num);
983 Region::trim_to (samplepos_t position, samplecnt_t length, const int32_t sub_num)
989 trim_to_internal (position, length, sub_num);
991 if (!property_changes_suspended()) {
992 recompute_at_start ();
998 Region::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
1000 samplepos_t new_start;
1006 sampleoffset_t const start_shift = position - _position;
1008 if (start_shift > 0) {
1010 if (_start > max_samplepos - start_shift) {
1011 new_start = max_samplepos;
1013 new_start = _start + start_shift;
1016 } else if (start_shift < 0) {
1018 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
1021 new_start = _start + start_shift;
1028 if (!verify_start_and_length (new_start, length)) {
1032 PropertyChange what_changed;
1034 if (_start != new_start) {
1035 set_start_internal (new_start, sub_num);
1036 what_changed.add (Properties::start);
1040 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1041 * 1. we call set_length_internal; length in beats is computed using the region's current
1042 * (soon-to-be old) position
1043 * 2. we call set_position_internal; position is set and length in samples re-computed using
1044 * length in beats from (1) but at the new position, which is wrong if the region
1045 * straddles a tempo/meter change.
1048 if (_position != position) {
1049 if (!property_changes_suspended()) {
1050 _last_position = _position;
1052 set_position_internal (position, true, sub_num);
1053 what_changed.add (Properties::position);
1056 if (_length != length) {
1057 if (!property_changes_suspended()) {
1058 _last_length = _length;
1060 set_length_internal (length, sub_num);
1061 what_changed.add (Properties::length);
1064 _whole_file = false;
1066 PropertyChange start_and_length;
1068 start_and_length.add (Properties::start);
1069 start_and_length.add (Properties::length);
1071 if (what_changed.contains (start_and_length)) {
1075 if (!what_changed.empty()) {
1076 send_change (what_changed);
1081 Region::set_hidden (bool yn)
1083 if (hidden() != yn) {
1085 send_change (Properties::hidden);
1090 Region::set_whole_file (bool yn)
1093 /* no change signal */
1097 Region::set_automatic (bool yn)
1100 /* no change signal */
1104 Region::set_muted (bool yn)
1106 if (muted() != yn) {
1108 send_change (Properties::muted);
1113 Region::set_opaque (bool yn)
1115 if (opaque() != yn) {
1117 send_change (Properties::opaque);
1122 Region::set_locked (bool yn)
1124 if (locked() != yn) {
1126 send_change (Properties::locked);
1131 Region::set_video_locked (bool yn)
1133 if (video_locked() != yn) {
1135 send_change (Properties::video_locked);
1140 Region::set_position_locked (bool yn)
1142 if (position_locked() != yn) {
1143 _position_locked = yn;
1144 send_change (Properties::locked);
1148 /** Set the region's sync point.
1149 * @param absolute_pos Session time.
1152 Region::set_sync_position (samplepos_t absolute_pos)
1154 /* position within our file */
1155 samplepos_t const file_pos = _start + (absolute_pos - _position);
1157 if (file_pos != _sync_position) {
1158 _sync_marked = true;
1159 _sync_position = file_pos;
1160 if (!property_changes_suspended()) {
1164 send_change (Properties::sync_position);
1169 Region::clear_sync_position ()
1171 if (sync_marked()) {
1172 _sync_marked = false;
1173 if (!property_changes_suspended()) {
1177 send_change (Properties::sync_position);
1181 /* @return the sync point relative the first sample of the region */
1183 Region::sync_offset (int& dir) const
1185 if (sync_marked()) {
1186 if (_sync_position > _start) {
1188 return _sync_position - _start;
1191 return _start - _sync_position;
1200 Region::adjust_to_sync (samplepos_t pos) const
1203 sampleoffset_t offset = sync_offset (sync_dir);
1205 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1214 if (max_samplepos - pos > offset) {
1222 /** @return Sync position in session time */
1224 Region::sync_position() const
1226 if (sync_marked()) {
1227 return _position - _start + _sync_position;
1229 /* if sync has not been marked, use the start of the region */
1237 boost::shared_ptr<Playlist> pl (playlist());
1239 pl->raise_region (shared_from_this ());
1246 boost::shared_ptr<Playlist> pl (playlist());
1248 pl->lower_region (shared_from_this ());
1254 Region::raise_to_top ()
1256 boost::shared_ptr<Playlist> pl (playlist());
1258 pl->raise_region_to_top (shared_from_this());
1263 Region::lower_to_bottom ()
1265 boost::shared_ptr<Playlist> pl (playlist());
1267 pl->lower_region_to_bottom (shared_from_this());
1272 Region::set_layer (layer_t l)
1280 XMLNode *node = new XMLNode ("Region");
1283 /* custom version of 'add_properties (*node);'
1284 * skip values that have have dedicated save functions
1285 * in AudioRegion::state()
1287 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1288 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1289 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1290 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1291 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1292 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1293 i->second->get_value (*node);
1296 node->set_property ("id", id ());
1297 node->set_property ("type", _type);
1301 switch (_first_edit) {
1302 case EditChangesNothing:
1305 case EditChangesName:
1311 default: /* should be unreachable but makes g++ happy */
1316 node->set_property ("first-edit", fe);
1318 /* note: flags are stored by derived classes */
1320 for (uint32_t n=0; n < _sources.size(); ++n) {
1321 snprintf (buf2, sizeof(buf2), "source-%d", n);
1322 node->set_property (buf2, _sources[n]->id());
1325 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1326 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1327 node->set_property (buf2, _master_sources[n]->id ());
1330 /* Only store nested sources for the whole-file region that acts
1331 as the parent/root of all regions using it.
1334 if (_whole_file && max_source_level() > 0) {
1336 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1338 /* region is compound - get its playlist and
1339 store that before we list the region that
1343 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1344 nested_node->add_child_nocopy ((*s)->get_state ());
1348 node->add_child_nocopy (*nested_node);
1353 node->add_child_copy (*_extra_xml);
1360 Region::get_state ()
1366 Region::set_state (const XMLNode& node, int version)
1368 PropertyChange what_changed;
1369 return _set_state (node, version, what_changed, true);
1373 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1375 Timecode::BBT_Time bbt_time;
1377 Stateful::save_extra_xml (node);
1379 what_changed = set_values (node);
1383 if (_position_lock_style == MusicTime) {
1384 std::string bbt_str;
1385 if (node.get_property ("bbt-position", bbt_str)) {
1386 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1389 &bbt_time.ticks) != 3) {
1390 _position_lock_style = AudioTime;
1391 _beat = _session.tempo_map().beat_at_sample (_position);
1393 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1395 /* no position property change for legacy Property, so we do this here */
1396 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1400 /* fix problems with old sessions corrupted by impossible
1401 values for _stretch or _shift
1403 if (_stretch == 0.0f) {
1407 if (_shift == 0.0f) {
1412 send_change (what_changed);
1415 /* Quick fix for 2.x sessions when region is muted */
1417 if (node.get_property (X_("flags"), flags)) {
1418 if (string::npos != flags.find("Muted")){
1423 // saved property is invalid, region-transients are not saved
1424 if (_user_transients.size() == 0){
1425 _valid_transients = false;
1432 Region::suspend_property_changes ()
1434 Stateful::suspend_property_changes ();
1435 _last_length = _length;
1436 _last_position = _position;
1440 Region::mid_thaw (const PropertyChange& what_changed)
1442 if (what_changed.contains (Properties::length)) {
1443 if (what_changed.contains (Properties::position)) {
1444 recompute_at_start ();
1446 recompute_at_end ();
1451 Region::send_change (const PropertyChange& what_changed)
1453 if (what_changed.empty()) {
1457 Stateful::send_change (what_changed);
1459 if (!Stateful::property_changes_suspended()) {
1461 /* Try and send a shared_pointer unless this is part of the constructor.
1466 boost::shared_ptr<Region> rptr = shared_from_this();
1467 RegionPropertyChanged (rptr, what_changed);
1469 /* no shared_ptr available, relax; */
1475 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1477 return coverage (other->first_sample(), other->last_sample()) != Evoral::OverlapNone;
1481 Region::enclosed_equivalent (boost::shared_ptr<const Region> other) const
1483 return (first_sample() >= other->first_sample() && last_sample() <= other->last_sample()) ||
1484 (first_sample() <= other->first_sample() && last_sample() >= other->last_sample()) ;
1488 Region::exact_equivalent (boost::shared_ptr<const Region> other) const
1490 return _start == other->_start &&
1491 _position == other->_position &&
1492 _length == other->_length;
1496 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1498 return _start == other->_start &&
1499 _length == other->_length;
1503 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1505 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1509 Region::source_deleted (boost::weak_ptr<Source>)
1513 if (!_session.deletion_in_progress()) {
1514 /* this is a very special case: at least one of the region's
1515 sources has bee deleted, so invalidate all references to
1516 ourselves. Do NOT do this during session deletion, because
1517 then we run the risk that this will actually result
1518 in this object being deleted (as refcnt goes to zero)
1519 while emitting DropReferences.
1527 Region::master_source_names ()
1529 SourceList::iterator i;
1531 vector<string> names;
1532 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1533 names.push_back((*i)->name());
1540 Region::set_master_sources (const SourceList& srcs)
1542 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1543 (*i)->dec_use_count ();
1546 _master_sources = srcs;
1547 assert (_sources.size() == _master_sources.size());
1549 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1550 (*i)->inc_use_count ();
1555 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1560 if ((_sources.size() != other->_sources.size()) ||
1561 (_master_sources.size() != other->_master_sources.size())) {
1565 SourceList::const_iterator i;
1566 SourceList::const_iterator io;
1568 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1569 if ((*i)->id() != (*io)->id()) {
1574 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1575 if ((*i)->id() != (*io)->id()) {
1584 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1590 SourceList::const_iterator i;
1591 SourceList::const_iterator io;
1593 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1594 if ((*i)->id() == (*io)->id()) {
1603 Region::source_string () const
1605 //string res = itos(_sources.size());
1608 res << _sources.size() << ":";
1610 SourceList::const_iterator i;
1612 for (i = _sources.begin(); i != _sources.end(); ++i) {
1613 res << (*i)->id() << ":";
1616 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1617 res << (*i)->id() << ":";
1624 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1626 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1628 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1631 if (sources.find (ps) == sources.end()) {
1632 /* (Playlist)Source not currently in
1633 accumulating set, so recurse.
1635 ps->playlist()->deep_sources (sources);
1639 /* add this source */
1640 sources.insert (*i);
1643 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1645 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1648 if (sources.find (ps) == sources.end()) {
1649 /* (Playlist)Source not currently in
1650 accumulating set, so recurse.
1652 ps->playlist()->deep_sources (sources);
1656 /* add this source */
1657 sources.insert (*i);
1662 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1664 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1670 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1673 if (ps->playlist()->uses_source (source)) {
1680 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1686 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1689 if (ps->playlist()->uses_source (source)) {
1701 Region::source_length(uint32_t n) const
1703 assert (n < _sources.size());
1704 return _sources[n]->length (_position - _start);
1708 Region::verify_length (samplecnt_t& len)
1710 if (source() && (source()->destructive() || source()->length_mutable())) {
1714 samplecnt_t maxlen = 0;
1716 for (uint32_t n = 0; n < _sources.size(); ++n) {
1717 maxlen = max (maxlen, source_length(n) - _start);
1720 len = min (len, maxlen);
1726 Region::verify_start_and_length (samplepos_t new_start, samplecnt_t& new_length)
1728 if (source() && (source()->destructive() || source()->length_mutable())) {
1732 samplecnt_t maxlen = 0;
1734 for (uint32_t n = 0; n < _sources.size(); ++n) {
1735 maxlen = max (maxlen, source_length(n) - new_start);
1738 new_length = min (new_length, maxlen);
1744 Region::verify_start (samplepos_t pos)
1746 if (source() && (source()->destructive() || source()->length_mutable())) {
1750 for (uint32_t n = 0; n < _sources.size(); ++n) {
1751 if (pos > source_length(n) - _length) {
1759 Region::verify_start_mutable (samplepos_t& new_start)
1761 if (source() && (source()->destructive() || source()->length_mutable())) {
1765 for (uint32_t n = 0; n < _sources.size(); ++n) {
1766 if (new_start > source_length(n) - _length) {
1767 new_start = source_length(n) - _length;
1773 boost::shared_ptr<Region>
1774 Region::get_parent() const
1776 boost::shared_ptr<Playlist> pl (playlist());
1779 boost::shared_ptr<Region> r;
1780 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1782 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1783 return boost::static_pointer_cast<Region> (r);
1787 return boost::shared_ptr<Region>();
1791 Region::apply (Filter& filter, Progress* progress)
1793 return filter.run (shared_from_this(), progress);
1798 Region::maybe_invalidate_transients ()
1800 bool changed = !_onsets.empty();
1803 if (_valid_transients || changed) {
1804 send_change (PropertyChange (Properties::valid_transients));
1810 Region::transients (AnalysisFeatureList& afl)
1812 int cnt = afl.empty() ? 0 : 1;
1814 Region::merge_features (afl, _onsets, _position);
1815 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1816 if (!_onsets.empty ()) {
1819 if (!_user_transients.empty ()) {
1824 // remove exact duplicates
1825 TransientDetector::cleanup_transients (afl, _session.sample_rate(), 0);
1830 Region::has_transients () const
1832 if (!_user_transients.empty ()) {
1833 assert (_valid_transients);
1836 if (!_onsets.empty ()) {
1843 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const sampleoffset_t off) const
1845 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1846 const sampleoffset_t p = (*x) + off;
1847 if (p < first_sample() || p > last_sample()) {
1850 result.push_back (p);
1855 Region::drop_sources ()
1857 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1858 (*i)->dec_use_count ();
1863 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1864 (*i)->dec_use_count ();
1867 _master_sources.clear ();
1871 Region::use_sources (SourceList const & s)
1873 set<boost::shared_ptr<Source> > unique_srcs;
1875 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1877 _sources.push_back (*i);
1878 (*i)->inc_use_count ();
1879 _master_sources.push_back (*i);
1880 (*i)->inc_use_count ();
1882 /* connect only once to DropReferences, even if sources are replicated
1885 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1886 unique_srcs.insert (*i);
1887 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1893 Region::can_trim () const
1895 CanTrim ct = CanTrim (0);
1901 /* if not locked, we can always move the front later, and the end earlier
1904 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1906 if (start() != 0 || can_trim_start_before_source_start ()) {
1907 ct = CanTrim (ct | FrontTrimEarlier);
1910 if (!_sources.empty()) {
1911 if ((start() + length()) < _sources.front()->length (0)) {
1912 ct = CanTrim (ct | EndTrimLater);
1920 Region::max_source_level () const
1924 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1925 lvl = max (lvl, (*i)->level());
1932 Region::is_compound () const
1934 return max_source_level() > 0;
1938 Region::post_set (const PropertyChange& pc)
1940 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1944 Region::set_start_internal (samplecnt_t s, const int32_t sub_num)
1950 Region::earliest_possible_position () const
1952 if (_start > _position) {
1955 return _position - _start;
1960 Region::latest_possible_sample () const
1962 samplecnt_t minlen = max_samplecnt;
1964 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1965 /* non-audio regions have a length that may vary based on their
1966 * position, so we have to pass it in the call.
1968 minlen = min (minlen, (*i)->length (_position));
1971 /* the latest possible last sample is determined by the current
1972 * position, plus the shortest source extent past _start.
1975 return _position + (minlen - _start) - 1;