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 , _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 , _soloSelected (false) \
220 , _muted (Properties::muted, other->_muted) \
221 , _opaque (Properties::opaque, other->_opaque) \
222 , _locked (Properties::locked, other->_locked) \
223 , _video_locked (Properties::video_locked, other->_video_locked) \
224 , _automatic (Properties::automatic, other->_automatic) \
225 , _whole_file (Properties::whole_file, other->_whole_file) \
226 , _import (Properties::import, other->_import) \
227 , _external (Properties::external, other->_external) \
228 , _hidden (Properties::hidden, other->_hidden) \
229 , _position_locked (Properties::position_locked, other->_position_locked) \
230 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
231 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
232 , _stretch (Properties::stretch, other->_stretch) \
233 , _shift (Properties::shift, other->_shift) \
234 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
235 , _layering_index (Properties::layering_index, other->_layering_index)
237 /* derived-from-derived constructor (no sources in constructor) */
238 Region::Region (Session& s, samplepos_t start, samplecnt_t length, const string& name, DataType type)
239 : SessionObject(s, name)
241 , REGION_DEFAULT_STATE(start,length)
242 , _last_length (length)
244 , _first_edit (EditChangesNothing)
247 register_properties ();
249 /* no sources at this point */
252 /** Basic Region constructor (many sources) */
253 Region::Region (const SourceList& srcs)
254 : SessionObject(srcs.front()->session(), "toBeRenamed")
255 , _type (srcs.front()->type())
256 , REGION_DEFAULT_STATE(0,0)
259 , _first_edit (EditChangesNothing)
262 register_properties ();
264 _type = srcs.front()->type();
268 assert(_sources.size() > 0);
269 assert (_type == srcs.front()->type());
272 /** Create a new Region from an existing one */
273 Region::Region (boost::shared_ptr<const Region> other)
274 : SessionObject(other->session(), other->name())
275 , _type (other->data_type())
276 , REGION_COPY_STATE (other)
277 , _last_length (other->_last_length)
278 , _last_position(other->_last_position) \
279 , _first_edit (EditChangesNothing)
280 , _layer (other->_layer)
282 register_properties ();
284 /* override state that may have been incorrectly inherited from the other region
287 _position = other->_position;
292 use_sources (other->_sources);
293 set_master_sources (other->_master_sources);
295 _position_lock_style = other->_position_lock_style;
296 _first_edit = other->_first_edit;
298 _start = other->_start;
299 _beat = other->_beat;
300 _quarter_note = other->_quarter_note;
302 /* sync pos is relative to start of file. our start-in-file is now zero,
303 so set our sync position to whatever the the difference between
304 _start and _sync_pos was in the other region.
306 result is that our new sync pos points to the same point in our source(s)
307 as the sync in the other region did in its source(s).
309 since we start at zero in our source(s), it is not possible to use a sync point that
310 is before the start. reset it to _start if that was true in the other region.
313 if (other->sync_marked()) {
314 if (other->_start < other->_sync_position) {
315 /* sync pos was after the start point of the other region */
316 _sync_position = other->_sync_position - other->_start;
318 /* sync pos was before the start point of the other region. not possible here. */
319 _sync_marked = false;
320 _sync_position = _start;
323 _sync_marked = false;
324 _sync_position = _start;
327 assert (_type == other->data_type());
330 /** Create a new Region from part of an existing one.
332 the start within \a other is given by \a offset
333 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
335 Region::Region (boost::shared_ptr<const Region> other, MusicSample offset)
336 : SessionObject(other->session(), other->name())
337 , _type (other->data_type())
338 , REGION_COPY_STATE (other)
339 , _last_length (other->_last_length)
340 , _last_position(other->_last_position) \
341 , _first_edit (EditChangesNothing)
342 , _layer (other->_layer)
344 register_properties ();
346 /* override state that may have been incorrectly inherited from the other region
353 use_sources (other->_sources);
354 set_master_sources (other->_master_sources);
356 _position = other->_position + offset.sample;
357 _start = other->_start + offset.sample;
359 /* prevent offset of 0 from altering musical position */
360 if (offset.sample != 0) {
361 const double offset_qn = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division)
362 - other->_quarter_note;
364 _quarter_note = other->_quarter_note + offset_qn;
365 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
367 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
370 /* if the other region had a distinct sync point
371 set, then continue to use it as best we can.
372 otherwise, reset sync point back to start.
375 if (other->sync_marked()) {
376 if (other->_sync_position < _start) {
377 _sync_marked = false;
378 _sync_position = _start;
380 _sync_position = other->_sync_position;
383 _sync_marked = false;
384 _sync_position = _start;
387 assert (_type == other->data_type());
390 /** Create a copy of @param other but with different sources. Used by filters */
391 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
392 : SessionObject (other->session(), other->name())
393 , _type (srcs.front()->type())
394 , REGION_COPY_STATE (other)
395 , _last_length (other->_last_length)
396 , _last_position (other->_last_position)
397 , _first_edit (EditChangesID)
398 , _layer (other->_layer)
400 register_properties ();
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
414 assert(_sources.size() > 0);
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
430 Region::set_name (const std::string& str)
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
436 send_change (Properties::name);
443 Region::set_selected_for_solo(bool yn)
445 if ( _soloSelected != yn) {
447 boost::shared_ptr<Playlist> pl (playlist());
450 pl->AddToSoloSelectedList(this);
452 pl->RemoveFromSoloSelectedList(this);
462 Region::set_length (samplecnt_t len, const int32_t sub_num)
464 //cerr << "Region::set_length() len = " << len << endl;
469 if (_length != len && len != 0) {
471 /* check that the current _position wouldn't make the new
475 if (max_samplepos - len < _position) {
479 if (!verify_length (len)) {
484 set_length_internal (len, sub_num);
488 maybe_invalidate_transients ();
490 if (!property_changes_suspended()) {
494 send_change (Properties::length);
499 Region::set_length_internal (samplecnt_t len, const int32_t sub_num)
501 _last_length = _length;
506 Region::maybe_uncopy ()
508 /* this does nothing but marked a semantic moment once upon a time */
512 Region::first_edit ()
514 boost::shared_ptr<Playlist> pl (playlist());
516 if (_first_edit != EditChangesNothing && pl) {
518 _name = RegionFactory::new_region_name (_name);
519 _first_edit = EditChangesNothing;
521 send_change (Properties::name);
523 RegionFactory::CheckNewRegion (shared_from_this());
528 Region::at_natural_position () const
530 boost::shared_ptr<Playlist> pl (playlist());
536 boost::shared_ptr<Region> whole_file_region = get_parent();
538 if (whole_file_region) {
539 if (_position == whole_file_region->position() + _start) {
548 Region::move_to_natural_position ()
550 boost::shared_ptr<Playlist> pl (playlist());
556 boost::shared_ptr<Region> whole_file_region = get_parent();
558 if (whole_file_region) {
559 set_position (whole_file_region->position() + _start);
564 Region::special_set_position (samplepos_t pos)
566 /* this is used when creating a whole file region as
567 a way to store its "natural" or "captured" position.
570 _position = _position;
575 Region::set_position_lock_style (PositionLockStyle ps)
577 if (_position_lock_style != ps) {
579 boost::shared_ptr<Playlist> pl (playlist());
581 _position_lock_style = ps;
583 send_change (Properties::position_lock_style);
588 Region::update_after_tempo_map_change (bool send)
590 boost::shared_ptr<Playlist> pl (playlist());
596 if (_position_lock_style == AudioTime) {
597 /* don't signal as the actual position has not chnged */
598 recompute_position_from_lock_style (0);
602 /* prevent movement before 0 */
603 const samplepos_t pos = max ((samplepos_t) 0, _session.tempo_map().sample_at_beat (_beat));
604 /* we have _beat. update sample position non-musically */
605 set_position_internal (pos, false, 0);
607 /* do this even if the position is the same. this helps out
608 a GUI that has moved its representation already.
612 send_change (Properties::position);
617 Region::set_position (samplepos_t pos, int32_t sub_num)
623 /* do this even if the position is the same. this helps out
624 a GUI that has moved its representation already.
626 PropertyChange p_and_l;
628 p_and_l.add (Properties::position);
630 if (position_lock_style() == AudioTime) {
631 set_position_internal (pos, true, sub_num);
633 if (!_session.loading()) {
634 _beat = _session.tempo_map().exact_beat_at_sample (pos, sub_num);
635 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
638 set_position_internal (pos, false, sub_num);
641 if (position_lock_style() == MusicTime) {
642 p_and_l.add (Properties::length);
645 send_change (p_and_l);
650 Region::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
652 /* We emit a change of Properties::position even if the position hasn't changed
653 (see Region::set_position), so we must always set this up so that
654 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
656 _last_position = _position;
658 if (_position != pos) {
661 if (allow_bbt_recompute) {
662 recompute_position_from_lock_style (sub_num);
664 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
665 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
668 /* check that the new _position wouldn't make the current
669 length impossible - if so, change the length.
671 XXX is this the right thing to do?
673 if (max_samplepos - _length < _position) {
674 _last_length = _length;
675 _length = max_samplepos - _position;
681 Region::set_position_music (double qn)
687 /* do this even if the position is the same. this helps out
688 a GUI that has moved its representation already.
690 PropertyChange p_and_l;
692 p_and_l.add (Properties::position);
694 if (!_session.loading()) {
695 _beat = _session.tempo_map().beat_at_quarter_note (qn);
698 /* will set sample accordingly */
699 set_position_music_internal (qn);
701 if (position_lock_style() == MusicTime) {
702 p_and_l.add (Properties::length);
705 send_change (p_and_l);
709 Region::set_position_music_internal (double qn)
711 /* We emit a change of Properties::position even if the position hasn't changed
712 (see Region::set_position), so we must always set this up so that
713 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
715 _last_position = _position;
717 if (_quarter_note != qn) {
718 _position = _session.tempo_map().sample_at_quarter_note (qn);
721 /* check that the new _position wouldn't make the current
722 length impossible - if so, change the length.
724 XXX is this the right thing to do?
726 if (max_samplepos - _length < _position) {
727 _last_length = _length;
728 _length = max_samplepos - _position;
733 /** A gui may need to create a region, then place it in an initial
734 * position determined by the user.
735 * When this takes place within one gui operation, we have to reset
736 * _last_position to prevent an implied move.
739 Region::set_initial_position (samplepos_t pos)
745 if (_position != pos) {
748 /* check that the new _position wouldn't make the current
749 length impossible - if so, change the length.
751 XXX is this the right thing to do?
754 if (max_samplepos - _length < _position) {
755 _last_length = _length;
756 _length = max_samplepos - _position;
759 recompute_position_from_lock_style (0);
760 /* ensure that this move doesn't cause a range move */
761 _last_position = _position;
765 /* do this even if the position is the same. this helps out
766 a GUI that has moved its representation already.
768 send_change (Properties::position);
772 Region::recompute_position_from_lock_style (const int32_t sub_num)
774 _beat = _session.tempo_map().exact_beat_at_sample (_position, sub_num);
775 _quarter_note = _session.tempo_map().exact_qn_at_sample (_position, sub_num);
779 Region::nudge_position (sampleoffset_t n)
781 if (locked() || video_locked()) {
789 samplepos_t new_position = _position;
792 if (_position > max_samplepos - n) {
793 new_position = max_samplepos;
798 if (_position < -n) {
804 /* assumes non-musical nudge */
805 set_position_internal (new_position, true, 0);
807 send_change (Properties::position);
811 Region::set_ancestral_data (samplepos_t s, samplecnt_t l, float st, float sh)
813 _ancestral_length = l;
814 _ancestral_start = s;
820 Region::set_start (samplepos_t pos)
822 if (locked() || position_locked() || video_locked()) {
825 /* This just sets the start, nothing else. It effectively shifts
826 the contents of the Region within the overall extent of the Source,
827 without changing the Region's position or length
832 if (!verify_start (pos)) {
836 set_start_internal (pos);
839 maybe_invalidate_transients ();
841 send_change (Properties::start);
846 Region::move_start (sampleoffset_t distance, const int32_t sub_num)
848 if (locked() || position_locked() || video_locked()) {
852 samplepos_t new_start;
856 if (_start > max_samplepos - distance) {
857 new_start = max_samplepos; // makes no sense
859 new_start = _start + distance;
862 if (!verify_start (new_start)) {
866 } else if (distance < 0) {
868 if (_start < -distance) {
871 new_start = _start + distance;
878 if (new_start == _start) {
882 set_start_internal (new_start, sub_num);
887 send_change (Properties::start);
891 Region::trim_front (samplepos_t new_position, const int32_t sub_num)
893 modify_front (new_position, false, sub_num);
897 Region::cut_front (samplepos_t new_position, const int32_t sub_num)
899 modify_front (new_position, true, sub_num);
903 Region::cut_end (samplepos_t new_endpoint, const int32_t sub_num)
905 modify_end (new_endpoint, true, sub_num);
909 Region::modify_front (samplepos_t new_position, bool reset_fade, const int32_t sub_num)
915 samplepos_t end = last_sample();
916 samplepos_t source_zero;
918 if (_position > _start) {
919 source_zero = _position - _start;
921 source_zero = 0; // its actually negative, but this will work for us
924 if (new_position < end) { /* can't trim it zero or negative length */
926 samplecnt_t newlen = 0;
928 if (!can_trim_start_before_source_start ()) {
929 /* can't trim it back past where source position zero is located */
930 new_position = max (new_position, source_zero);
933 if (new_position > _position) {
934 newlen = _length - (new_position - _position);
936 newlen = _length + (_position - new_position);
939 trim_to_internal (new_position, newlen, sub_num);
942 _right_of_split = true;
945 if (!property_changes_suspended()) {
946 recompute_at_start ();
949 maybe_invalidate_transients ();
954 Region::modify_end (samplepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
960 if (new_endpoint > _position) {
961 trim_to_internal (_position, new_endpoint - _position, sub_num);
963 _left_of_split = true;
965 if (!property_changes_suspended()) {
971 /** @param new_endpoint New region end point, such that, for example,
972 * a region at 0 of length 10 has an endpoint of 9.
976 Region::trim_end (samplepos_t new_endpoint, const int32_t sub_num)
978 modify_end (new_endpoint, false, sub_num);
982 Region::trim_to (samplepos_t position, samplecnt_t length, const int32_t sub_num)
988 trim_to_internal (position, length, sub_num);
990 if (!property_changes_suspended()) {
991 recompute_at_start ();
997 Region::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
999 samplepos_t new_start;
1005 sampleoffset_t const start_shift = position - _position;
1007 if (start_shift > 0) {
1009 if (_start > max_samplepos - start_shift) {
1010 new_start = max_samplepos;
1012 new_start = _start + start_shift;
1015 } else if (start_shift < 0) {
1017 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
1020 new_start = _start + start_shift;
1027 if (!verify_start_and_length (new_start, length)) {
1031 PropertyChange what_changed;
1033 if (_start != new_start) {
1034 set_start_internal (new_start, sub_num);
1035 what_changed.add (Properties::start);
1039 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1040 * 1. we call set_length_internal; length in beats is computed using the region's current
1041 * (soon-to-be old) position
1042 * 2. we call set_position_internal; position is set and length in samples re-computed using
1043 * length in beats from (1) but at the new position, which is wrong if the region
1044 * straddles a tempo/meter change.
1047 if (_position != position) {
1048 if (!property_changes_suspended()) {
1049 _last_position = _position;
1051 set_position_internal (position, true, sub_num);
1052 what_changed.add (Properties::position);
1055 if (_length != length) {
1056 if (!property_changes_suspended()) {
1057 _last_length = _length;
1059 set_length_internal (length, sub_num);
1060 what_changed.add (Properties::length);
1063 _whole_file = false;
1065 PropertyChange start_and_length;
1067 start_and_length.add (Properties::start);
1068 start_and_length.add (Properties::length);
1070 if (what_changed.contains (start_and_length)) {
1074 if (!what_changed.empty()) {
1075 send_change (what_changed);
1080 Region::set_hidden (bool yn)
1082 if (hidden() != yn) {
1084 send_change (Properties::hidden);
1089 Region::set_whole_file (bool yn)
1092 /* no change signal */
1096 Region::set_automatic (bool yn)
1099 /* no change signal */
1103 Region::set_muted (bool yn)
1105 if (muted() != yn) {
1107 send_change (Properties::muted);
1112 Region::set_opaque (bool yn)
1114 if (opaque() != yn) {
1116 send_change (Properties::opaque);
1121 Region::set_locked (bool yn)
1123 if (locked() != yn) {
1125 send_change (Properties::locked);
1130 Region::set_video_locked (bool yn)
1132 if (video_locked() != yn) {
1134 send_change (Properties::video_locked);
1139 Region::set_position_locked (bool yn)
1141 if (position_locked() != yn) {
1142 _position_locked = yn;
1143 send_change (Properties::locked);
1147 /** Set the region's sync point.
1148 * @param absolute_pos Session time.
1151 Region::set_sync_position (samplepos_t absolute_pos)
1153 /* position within our file */
1154 samplepos_t const file_pos = _start + (absolute_pos - _position);
1156 if (file_pos != _sync_position) {
1157 _sync_marked = true;
1158 _sync_position = file_pos;
1159 if (!property_changes_suspended()) {
1163 send_change (Properties::sync_position);
1168 Region::clear_sync_position ()
1170 if (sync_marked()) {
1171 _sync_marked = false;
1172 if (!property_changes_suspended()) {
1176 send_change (Properties::sync_position);
1180 /* @return the sync point relative the first sample of the region */
1182 Region::sync_offset (int& dir) const
1184 if (sync_marked()) {
1185 if (_sync_position > _start) {
1187 return _sync_position - _start;
1190 return _start - _sync_position;
1199 Region::adjust_to_sync (samplepos_t pos) const
1202 sampleoffset_t offset = sync_offset (sync_dir);
1204 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1213 if (max_samplepos - pos > offset) {
1221 /** @return Sync position in session time */
1223 Region::sync_position() const
1225 if (sync_marked()) {
1226 return _position - _start + _sync_position;
1228 /* if sync has not been marked, use the start of the region */
1236 boost::shared_ptr<Playlist> pl (playlist());
1238 pl->raise_region (shared_from_this ());
1245 boost::shared_ptr<Playlist> pl (playlist());
1247 pl->lower_region (shared_from_this ());
1253 Region::raise_to_top ()
1255 boost::shared_ptr<Playlist> pl (playlist());
1257 pl->raise_region_to_top (shared_from_this());
1262 Region::lower_to_bottom ()
1264 boost::shared_ptr<Playlist> pl (playlist());
1266 pl->lower_region_to_bottom (shared_from_this());
1271 Region::set_layer (layer_t l)
1279 XMLNode *node = new XMLNode ("Region");
1282 /* custom version of 'add_properties (*node);'
1283 * skip values that have have dedicated save functions
1284 * in AudioRegion::state()
1286 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1287 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1288 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1289 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1290 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1291 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1292 i->second->get_value (*node);
1295 node->set_property ("id", id ());
1296 node->set_property ("type", _type);
1300 switch (_first_edit) {
1301 case EditChangesNothing:
1304 case EditChangesName:
1310 default: /* should be unreachable but makes g++ happy */
1315 node->set_property ("first-edit", fe);
1317 /* note: flags are stored by derived classes */
1319 for (uint32_t n=0; n < _sources.size(); ++n) {
1320 snprintf (buf2, sizeof(buf2), "source-%d", n);
1321 node->set_property (buf2, _sources[n]->id());
1324 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1325 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1326 node->set_property (buf2, _master_sources[n]->id ());
1329 /* Only store nested sources for the whole-file region that acts
1330 as the parent/root of all regions using it.
1333 if (_whole_file && max_source_level() > 0) {
1335 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1337 /* region is compound - get its playlist and
1338 store that before we list the region that
1342 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1343 nested_node->add_child_nocopy ((*s)->get_state ());
1347 node->add_child_nocopy (*nested_node);
1352 node->add_child_copy (*_extra_xml);
1359 Region::get_state ()
1365 Region::set_state (const XMLNode& node, int version)
1367 PropertyChange what_changed;
1368 return _set_state (node, version, what_changed, true);
1372 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1374 Timecode::BBT_Time bbt_time;
1376 Stateful::save_extra_xml (node);
1378 what_changed = set_values (node);
1382 if (_position_lock_style == MusicTime) {
1383 std::string bbt_str;
1384 if (node.get_property ("bbt-position", bbt_str)) {
1385 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1388 &bbt_time.ticks) != 3) {
1389 _position_lock_style = AudioTime;
1390 _beat = _session.tempo_map().beat_at_sample (_position);
1392 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1394 /* no position property change for legacy Property, so we do this here */
1395 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1399 /* fix problems with old sessions corrupted by impossible
1400 values for _stretch or _shift
1402 if (_stretch == 0.0f) {
1406 if (_shift == 0.0f) {
1411 send_change (what_changed);
1414 /* Quick fix for 2.x sessions when region is muted */
1416 if (node.get_property (X_("flags"), flags)) {
1417 if (string::npos != flags.find("Muted")){
1422 // saved property is invalid, region-transients are not saved
1423 if (_user_transients.size() == 0){
1424 _valid_transients = false;
1431 Region::suspend_property_changes ()
1433 Stateful::suspend_property_changes ();
1434 _last_length = _length;
1435 _last_position = _position;
1439 Region::mid_thaw (const PropertyChange& what_changed)
1441 if (what_changed.contains (Properties::length)) {
1442 if (what_changed.contains (Properties::position)) {
1443 recompute_at_start ();
1445 recompute_at_end ();
1450 Region::send_change (const PropertyChange& what_changed)
1452 if (what_changed.empty()) {
1456 Stateful::send_change (what_changed);
1458 if (!Stateful::property_changes_suspended()) {
1460 /* Try and send a shared_pointer unless this is part of the constructor.
1465 boost::shared_ptr<Region> rptr = shared_from_this();
1466 RegionPropertyChanged (rptr, what_changed);
1468 /* no shared_ptr available, relax; */
1474 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1476 return coverage (other->first_sample(), other->last_sample()) != Evoral::OverlapNone;
1480 Region::equivalent (boost::shared_ptr<const Region> other) const
1482 return _start == other->_start &&
1483 _position == other->_position &&
1484 _length == other->_length;
1488 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1490 return _start == other->_start &&
1491 _length == other->_length;
1495 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1497 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1501 Region::source_deleted (boost::weak_ptr<Source>)
1505 if (!_session.deletion_in_progress()) {
1506 /* this is a very special case: at least one of the region's
1507 sources has bee deleted, so invalidate all references to
1508 ourselves. Do NOT do this during session deletion, because
1509 then we run the risk that this will actually result
1510 in this object being deleted (as refcnt goes to zero)
1511 while emitting DropReferences.
1519 Region::master_source_names ()
1521 SourceList::iterator i;
1523 vector<string> names;
1524 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1525 names.push_back((*i)->name());
1532 Region::set_master_sources (const SourceList& srcs)
1534 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1535 (*i)->dec_use_count ();
1538 _master_sources = srcs;
1539 assert (_sources.size() == _master_sources.size());
1541 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1542 (*i)->inc_use_count ();
1547 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1552 if ((_sources.size() != other->_sources.size()) ||
1553 (_master_sources.size() != other->_master_sources.size())) {
1557 SourceList::const_iterator i;
1558 SourceList::const_iterator io;
1560 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1561 if ((*i)->id() != (*io)->id()) {
1566 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1567 if ((*i)->id() != (*io)->id()) {
1576 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1582 SourceList::const_iterator i;
1583 SourceList::const_iterator io;
1585 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1586 if ((*i)->id() == (*io)->id()) {
1595 Region::source_string () const
1597 //string res = itos(_sources.size());
1600 res << _sources.size() << ":";
1602 SourceList::const_iterator i;
1604 for (i = _sources.begin(); i != _sources.end(); ++i) {
1605 res << (*i)->id() << ":";
1608 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1609 res << (*i)->id() << ":";
1616 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1618 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1620 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1623 if (sources.find (ps) == sources.end()) {
1624 /* (Playlist)Source not currently in
1625 accumulating set, so recurse.
1627 ps->playlist()->deep_sources (sources);
1631 /* add this source */
1632 sources.insert (*i);
1635 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1637 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1640 if (sources.find (ps) == sources.end()) {
1641 /* (Playlist)Source not currently in
1642 accumulating set, so recurse.
1644 ps->playlist()->deep_sources (sources);
1648 /* add this source */
1649 sources.insert (*i);
1654 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1656 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1662 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1665 if (ps->playlist()->uses_source (source)) {
1672 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1678 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1681 if (ps->playlist()->uses_source (source)) {
1693 Region::source_length(uint32_t n) const
1695 assert (n < _sources.size());
1696 return _sources[n]->length (_position - _start);
1700 Region::verify_length (samplecnt_t& len)
1702 if (source() && (source()->destructive() || source()->length_mutable())) {
1706 samplecnt_t maxlen = 0;
1708 for (uint32_t n = 0; n < _sources.size(); ++n) {
1709 maxlen = max (maxlen, source_length(n) - _start);
1712 len = min (len, maxlen);
1718 Region::verify_start_and_length (samplepos_t new_start, samplecnt_t& new_length)
1720 if (source() && (source()->destructive() || source()->length_mutable())) {
1724 samplecnt_t maxlen = 0;
1726 for (uint32_t n = 0; n < _sources.size(); ++n) {
1727 maxlen = max (maxlen, source_length(n) - new_start);
1730 new_length = min (new_length, maxlen);
1736 Region::verify_start (samplepos_t pos)
1738 if (source() && (source()->destructive() || source()->length_mutable())) {
1742 for (uint32_t n = 0; n < _sources.size(); ++n) {
1743 if (pos > source_length(n) - _length) {
1751 Region::verify_start_mutable (samplepos_t& new_start)
1753 if (source() && (source()->destructive() || source()->length_mutable())) {
1757 for (uint32_t n = 0; n < _sources.size(); ++n) {
1758 if (new_start > source_length(n) - _length) {
1759 new_start = source_length(n) - _length;
1765 boost::shared_ptr<Region>
1766 Region::get_parent() const
1768 boost::shared_ptr<Playlist> pl (playlist());
1771 boost::shared_ptr<Region> r;
1772 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1774 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1775 return boost::static_pointer_cast<Region> (r);
1779 return boost::shared_ptr<Region>();
1783 Region::apply (Filter& filter, Progress* progress)
1785 return filter.run (shared_from_this(), progress);
1790 Region::maybe_invalidate_transients ()
1792 bool changed = !_onsets.empty();
1795 if (_valid_transients || changed) {
1796 send_change (PropertyChange (Properties::valid_transients));
1802 Region::transients (AnalysisFeatureList& afl)
1804 int cnt = afl.empty() ? 0 : 1;
1806 Region::merge_features (afl, _onsets, _position);
1807 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1808 if (!_onsets.empty ()) {
1811 if (!_user_transients.empty ()) {
1816 // remove exact duplicates
1817 TransientDetector::cleanup_transients (afl, _session.sample_rate(), 0);
1822 Region::has_transients () const
1824 if (!_user_transients.empty ()) {
1825 assert (_valid_transients);
1828 if (!_onsets.empty ()) {
1835 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const sampleoffset_t off) const
1837 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1838 const sampleoffset_t p = (*x) + off;
1839 if (p < first_sample() || p > last_sample()) {
1842 result.push_back (p);
1847 Region::drop_sources ()
1849 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1850 (*i)->dec_use_count ();
1855 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1856 (*i)->dec_use_count ();
1859 _master_sources.clear ();
1863 Region::use_sources (SourceList const & s)
1865 set<boost::shared_ptr<Source> > unique_srcs;
1867 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1869 _sources.push_back (*i);
1870 (*i)->inc_use_count ();
1871 _master_sources.push_back (*i);
1872 (*i)->inc_use_count ();
1874 /* connect only once to DropReferences, even if sources are replicated
1877 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1878 unique_srcs.insert (*i);
1879 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1885 Region::can_trim () const
1887 CanTrim ct = CanTrim (0);
1893 /* if not locked, we can always move the front later, and the end earlier
1896 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1898 if (start() != 0 || can_trim_start_before_source_start ()) {
1899 ct = CanTrim (ct | FrontTrimEarlier);
1902 if (!_sources.empty()) {
1903 if ((start() + length()) < _sources.front()->length (0)) {
1904 ct = CanTrim (ct | EndTrimLater);
1912 Region::max_source_level () const
1916 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1917 lvl = max (lvl, (*i)->level());
1924 Region::is_compound () const
1926 return max_source_level() > 0;
1930 Region::post_set (const PropertyChange& pc)
1932 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1936 Region::set_start_internal (samplecnt_t s, const int32_t sub_num)
1942 Region::earliest_possible_position () const
1944 if (_start > _position) {
1947 return _position - _start;
1952 Region::latest_possible_sample () const
1954 samplecnt_t minlen = max_samplecnt;
1956 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1957 /* non-audio regions have a length that may vary based on their
1958 * position, so we have to pass it in the call.
1960 minlen = min (minlen, (*i)->length (_position));
1963 /* the latest possible last sample is determined by the current
1964 * position, plus the shortest source extent past _start.
1967 return _position + (minlen - _start) - 1;