2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<double> beat;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75 PBD::PropertyDescriptor<uint64_t> layering_index;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
92 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
94 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
96 Properties::import.property_id = g_quark_from_static_string (X_("import"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
98 Properties::external.property_id = g_quark_from_static_string (X_("external"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
100 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
102 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
104 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
106 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
108 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
110 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
112 Properties::start.property_id = g_quark_from_static_string (X_("start"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
114 Properties::length.property_id = g_quark_from_static_string (X_("length"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
116 Properties::position.property_id = g_quark_from_static_string (X_("position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
118 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
139 Region::register_properties ()
141 _xml_node_name = X_("Region");
143 add_property (_muted);
144 add_property (_opaque);
145 add_property (_locked);
146 add_property (_video_locked);
147 add_property (_automatic);
148 add_property (_whole_file);
149 add_property (_import);
150 add_property (_external);
151 add_property (_sync_marked);
152 add_property (_left_of_split);
153 add_property (_right_of_split);
154 add_property (_hidden);
155 add_property (_position_locked);
156 add_property (_valid_transients);
157 add_property (_start);
158 add_property (_length);
159 add_property (_position);
160 add_property (_beat);
161 add_property (_sync_position);
162 add_property (_ancestral_start);
163 add_property (_ancestral_length);
164 add_property (_stretch);
165 add_property (_shift);
166 add_property (_position_lock_style);
167 add_property (_layering_index);
170 #define REGION_DEFAULT_STATE(s,l) \
171 _sync_marked (Properties::sync_marked, false) \
172 , _left_of_split (Properties::left_of_split, false) \
173 , _right_of_split (Properties::right_of_split, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _beat (Properties::beat, 0.0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _quarter_note (0.0) \
181 , _transient_user_start (0) \
182 , _transient_analysis_start (0) \
183 , _transient_analysis_end (0) \
184 , _muted (Properties::muted, false) \
185 , _opaque (Properties::opaque, true) \
186 , _locked (Properties::locked, false) \
187 , _video_locked (Properties::video_locked, false) \
188 , _automatic (Properties::automatic, false) \
189 , _whole_file (Properties::whole_file, false) \
190 , _import (Properties::import, false) \
191 , _external (Properties::external, false) \
192 , _hidden (Properties::hidden, false) \
193 , _position_locked (Properties::position_locked, false) \
194 , _ancestral_start (Properties::ancestral_start, (s)) \
195 , _ancestral_length (Properties::ancestral_length, (l)) \
196 , _stretch (Properties::stretch, 1.0) \
197 , _shift (Properties::shift, 1.0) \
198 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
199 , _layering_index (Properties::layering_index, 0)
201 #define REGION_COPY_STATE(other) \
202 _sync_marked (Properties::sync_marked, other->_sync_marked) \
203 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
204 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
205 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
206 , _start(Properties::start, other->_start) \
207 , _length(Properties::length, other->_length) \
208 , _position(Properties::position, other->_position) \
209 , _beat (Properties::beat, other->_beat) \
210 , _sync_position(Properties::sync_position, other->_sync_position) \
211 , _quarter_note (other->_quarter_note) \
212 , _user_transients (other->_user_transients) \
213 , _transient_user_start (other->_transient_user_start) \
214 , _transients (other->_transients) \
215 , _transient_analysis_start (other->_transient_analysis_start) \
216 , _transient_analysis_end (other->_transient_analysis_end) \
217 , _muted (Properties::muted, other->_muted) \
218 , _opaque (Properties::opaque, other->_opaque) \
219 , _locked (Properties::locked, other->_locked) \
220 , _video_locked (Properties::video_locked, other->_video_locked) \
221 , _automatic (Properties::automatic, other->_automatic) \
222 , _whole_file (Properties::whole_file, other->_whole_file) \
223 , _import (Properties::import, other->_import) \
224 , _external (Properties::external, other->_external) \
225 , _hidden (Properties::hidden, other->_hidden) \
226 , _position_locked (Properties::position_locked, other->_position_locked) \
227 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
228 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
229 , _stretch (Properties::stretch, other->_stretch) \
230 , _shift (Properties::shift, other->_shift) \
231 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
232 , _layering_index (Properties::layering_index, other->_layering_index)
234 /* derived-from-derived constructor (no sources in constructor) */
235 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
236 : SessionObject(s, name)
238 , REGION_DEFAULT_STATE(start,length)
239 , _last_length (length)
241 , _first_edit (EditChangesNothing)
244 register_properties ();
246 /* no sources at this point */
249 /** Basic Region constructor (many sources) */
250 Region::Region (const SourceList& srcs)
251 : SessionObject(srcs.front()->session(), "toBeRenamed")
252 , _type (srcs.front()->type())
253 , REGION_DEFAULT_STATE(0,0)
256 , _first_edit (EditChangesNothing)
259 register_properties ();
261 _type = srcs.front()->type();
265 assert(_sources.size() > 0);
266 assert (_type == srcs.front()->type());
269 /** Create a new Region from an existing one */
270 Region::Region (boost::shared_ptr<const Region> other)
271 : SessionObject(other->session(), other->name())
272 , _type (other->data_type())
273 , REGION_COPY_STATE (other)
274 , _last_length (other->_last_length)
275 , _last_position(other->_last_position) \
276 , _first_edit (EditChangesNothing)
277 , _layer (other->_layer)
279 register_properties ();
281 /* override state that may have been incorrectly inherited from the other region
284 _position = other->_position;
289 use_sources (other->_sources);
290 set_master_sources (other->_master_sources);
292 _position_lock_style = other->_position_lock_style;
293 _first_edit = other->_first_edit;
295 _start = other->_start;
296 _beat = other->_beat;
297 _quarter_note = other->_quarter_note;
299 /* sync pos is relative to start of file. our start-in-file is now zero,
300 so set our sync position to whatever the the difference between
301 _start and _sync_pos was in the other region.
303 result is that our new sync pos points to the same point in our source(s)
304 as the sync in the other region did in its source(s).
306 since we start at zero in our source(s), it is not possible to use a sync point that
307 is before the start. reset it to _start if that was true in the other region.
310 if (other->sync_marked()) {
311 if (other->_start < other->_sync_position) {
312 /* sync pos was after the start point of the other region */
313 _sync_position = other->_sync_position - other->_start;
315 /* sync pos was before the start point of the other region. not possible here. */
316 _sync_marked = false;
317 _sync_position = _start;
320 _sync_marked = false;
321 _sync_position = _start;
324 assert (_type == other->data_type());
327 /** Create a new Region from part of an existing one.
329 the start within \a other is given by \a offset
330 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
332 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
333 : SessionObject(other->session(), other->name())
334 , _type (other->data_type())
335 , REGION_COPY_STATE (other)
336 , _last_length (other->_last_length)
337 , _last_position(other->_last_position) \
338 , _first_edit (EditChangesNothing)
339 , _layer (other->_layer)
341 register_properties ();
343 /* override state that may have been incorrectly inherited from the other region
346 _position = other->_position + offset;
351 use_sources (other->_sources);
352 set_master_sources (other->_master_sources);
354 _start = other->_start + offset;
355 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
356 _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
358 /* if the other region had a distinct sync point
359 set, then continue to use it as best we can.
360 otherwise, reset sync point back to start.
363 if (other->sync_marked()) {
364 if (other->_sync_position < _start) {
365 _sync_marked = false;
366 _sync_position = _start;
368 _sync_position = other->_sync_position;
371 _sync_marked = false;
372 _sync_position = _start;
375 assert (_type == other->data_type());
378 /** Create a copy of @param other but with different sources. Used by filters */
379 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
380 : SessionObject (other->session(), other->name())
381 , _type (srcs.front()->type())
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _first_edit (EditChangesID)
386 , _layer (other->_layer)
388 register_properties ();
391 _position_locked = false;
393 other->_first_edit = EditChangesName;
395 if (other->_extra_xml) {
396 _extra_xml = new XMLNode (*other->_extra_xml);
402 assert(_sources.size() > 0);
407 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
412 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
414 _playlist = wpl.lock();
418 Region::set_name (const std::string& str)
421 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
422 assert(_name == str);
424 send_change (Properties::name);
431 Region::set_length (framecnt_t len, const int32_t sub_num)
433 //cerr << "Region::set_length() len = " << len << endl;
438 if (_length != len && len != 0) {
440 /* check that the current _position wouldn't make the new
444 if (max_framepos - len < _position) {
448 if (!verify_length (len)) {
453 set_length_internal (len, sub_num);
457 maybe_invalidate_transients ();
459 if (!property_changes_suspended()) {
463 send_change (Properties::length);
468 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
470 _last_length = _length;
475 Region::maybe_uncopy ()
477 /* this does nothing but marked a semantic moment once upon a time */
481 Region::first_edit ()
483 boost::shared_ptr<Playlist> pl (playlist());
485 if (_first_edit != EditChangesNothing && pl) {
487 _name = RegionFactory::new_region_name (_name);
488 _first_edit = EditChangesNothing;
490 send_change (Properties::name);
492 RegionFactory::CheckNewRegion (shared_from_this());
497 Region::at_natural_position () const
499 boost::shared_ptr<Playlist> pl (playlist());
505 boost::shared_ptr<Region> whole_file_region = get_parent();
507 if (whole_file_region) {
508 if (_position == whole_file_region->position() + _start) {
517 Region::move_to_natural_position ()
519 boost::shared_ptr<Playlist> pl (playlist());
525 boost::shared_ptr<Region> whole_file_region = get_parent();
527 if (whole_file_region) {
528 set_position (whole_file_region->position() + _start);
533 Region::special_set_position (framepos_t pos)
535 /* this is used when creating a whole file region as
536 a way to store its "natural" or "captured" position.
539 _position = _position;
544 Region::set_position_lock_style (PositionLockStyle ps)
546 if (_position_lock_style != ps) {
548 boost::shared_ptr<Playlist> pl (playlist());
550 _position_lock_style = ps;
552 send_change (Properties::position_lock_style);
557 Region::update_after_tempo_map_change (bool send)
559 boost::shared_ptr<Playlist> pl (playlist());
565 if (_position_lock_style == AudioTime) {
566 /* don't signal as the actual position has not chnged */
567 recompute_position_from_lock_style (0);
571 /* prevent movement before 0 */
572 const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
573 /* we have _beat. update frame position non-musically */
574 set_position_internal (pos, false, 0);
576 /* do this even if the position is the same. this helps out
577 a GUI that has moved its representation already.
581 send_change (Properties::position);
586 Region::set_position (framepos_t pos, int32_t sub_num)
592 if (position_lock_style() == AudioTime) {
593 set_position_internal (pos, true, sub_num);
595 if (!_session.loading()) {
596 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
599 /* will set pulse accordingly */
600 set_position_internal (pos, false, sub_num);
603 /* do this even if the position is the same. this helps out
604 a GUI that has moved its representation already.
606 PropertyChange p_and_l;
608 p_and_l.add (Properties::position);
609 /* Currently length change due to position change is only implemented
610 for MidiRegion (Region has no length in beats).
611 Notify a length change regardless (its more efficient for MidiRegions),
612 and when Region has a _length_beats we will need it here anyway).
614 p_and_l.add (Properties::length);
616 send_change (p_and_l);
620 /** A gui may need to create a region, then place it in an initial
621 * position determined by the user.
622 * When this takes place within one gui operation, we have to reset
623 * _last_position to prevent an implied move.
626 Region::set_initial_position (framepos_t pos)
632 if (_position != pos) {
635 /* check that the new _position wouldn't make the current
636 length impossible - if so, change the length.
638 XXX is this the right thing to do?
641 if (max_framepos - _length < _position) {
642 _last_length = _length;
643 _length = max_framepos - _position;
646 recompute_position_from_lock_style (0);
647 /* ensure that this move doesn't cause a range move */
648 _last_position = _position;
652 /* do this even if the position is the same. this helps out
653 a GUI that has moved its representation already.
655 send_change (Properties::position);
659 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
661 /* We emit a change of Properties::position even if the position hasn't changed
662 (see Region::set_position), so we must always set this up so that
663 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
665 _last_position = _position;
667 if (_position != pos) {
670 if (allow_bbt_recompute) {
671 recompute_position_from_lock_style (sub_num);
673 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
674 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
677 /* check that the new _position wouldn't make the current
678 length impossible - if so, change the length.
680 XXX is this the right thing to do?
682 if (max_framepos - _length < _position) {
683 _last_length = _length;
684 _length = max_framepos - _position;
690 Region::recompute_position_from_lock_style (const int32_t sub_num)
692 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
693 _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
697 Region::nudge_position (frameoffset_t n)
699 if (locked() || video_locked()) {
707 framepos_t new_position = _position;
710 if (_position > max_framepos - n) {
711 new_position = max_framepos;
716 if (_position < -n) {
722 /* assumes non-musical nudge */
723 set_position_internal (new_position, true, 0);
725 send_change (Properties::position);
729 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
731 _ancestral_length = l;
732 _ancestral_start = s;
738 Region::set_start (framepos_t pos)
740 if (locked() || position_locked() || video_locked()) {
743 /* This just sets the start, nothing else. It effectively shifts
744 the contents of the Region within the overall extent of the Source,
745 without changing the Region's position or length
750 if (!verify_start (pos)) {
754 set_start_internal (pos);
757 maybe_invalidate_transients ();
759 send_change (Properties::start);
764 Region::move_start (frameoffset_t distance, const int32_t sub_num)
766 if (locked() || position_locked() || video_locked()) {
770 framepos_t new_start;
774 if (_start > max_framepos - distance) {
775 new_start = max_framepos; // makes no sense
777 new_start = _start + distance;
780 if (!verify_start (new_start)) {
784 } else if (distance < 0) {
786 if (_start < -distance) {
789 new_start = _start + distance;
796 if (new_start == _start) {
800 set_start_internal (new_start, sub_num);
805 send_change (Properties::start);
809 Region::trim_front (framepos_t new_position, const int32_t sub_num)
811 modify_front (new_position, false, sub_num);
815 Region::cut_front (framepos_t new_position, const int32_t sub_num)
817 modify_front (new_position, true, sub_num);
821 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
823 modify_end (new_endpoint, true, sub_num);
827 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
833 framepos_t end = last_frame();
834 framepos_t source_zero;
836 if (_position > _start) {
837 source_zero = _position - _start;
839 source_zero = 0; // its actually negative, but this will work for us
842 if (new_position < end) { /* can't trim it zero or negative length */
844 framecnt_t newlen = 0;
846 if (!can_trim_start_before_source_start ()) {
847 /* can't trim it back past where source position zero is located */
848 new_position = max (new_position, source_zero);
851 if (new_position > _position) {
852 newlen = _length - (new_position - _position);
854 newlen = _length + (_position - new_position);
857 trim_to_internal (new_position, newlen, sub_num);
860 _right_of_split = true;
863 if (!property_changes_suspended()) {
864 recompute_at_start ();
867 maybe_invalidate_transients ();
872 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
878 if (new_endpoint > _position) {
879 trim_to_internal (_position, new_endpoint - _position, sub_num);
881 _left_of_split = true;
883 if (!property_changes_suspended()) {
889 /** @param new_endpoint New region end point, such that, for example,
890 * a region at 0 of length 10 has an endpoint of 9.
894 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
896 modify_end (new_endpoint, false, sub_num);
900 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
906 trim_to_internal (position, length, sub_num);
908 if (!property_changes_suspended()) {
909 recompute_at_start ();
915 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
917 framepos_t new_start;
923 frameoffset_t const start_shift = position - _position;
925 if (start_shift > 0) {
927 if (_start > max_framepos - start_shift) {
928 new_start = max_framepos;
930 new_start = _start + start_shift;
933 } else if (start_shift < 0) {
935 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
938 new_start = _start + start_shift;
945 if (!verify_start_and_length (new_start, length)) {
949 PropertyChange what_changed;
951 if (_start != new_start) {
952 set_start_internal (new_start, sub_num);
953 what_changed.add (Properties::start);
957 /* Set position before length, otherwise for MIDI regions this bad thing happens:
958 * 1. we call set_length_internal; length in beats is computed using the region's current
959 * (soon-to-be old) position
960 * 2. we call set_position_internal; position is set and length in frames re-computed using
961 * length in beats from (1) but at the new position, which is wrong if the region
962 * straddles a tempo/meter change.
965 if (_position != position) {
966 if (!property_changes_suspended()) {
967 _last_position = _position;
969 set_position_internal (position, true, sub_num);
970 what_changed.add (Properties::position);
973 if (_length != length) {
974 if (!property_changes_suspended()) {
975 _last_length = _length;
977 set_length_internal (length, sub_num);
978 what_changed.add (Properties::length);
983 PropertyChange start_and_length;
985 start_and_length.add (Properties::start);
986 start_and_length.add (Properties::length);
988 if (what_changed.contains (start_and_length)) {
992 if (!what_changed.empty()) {
993 send_change (what_changed);
998 Region::set_hidden (bool yn)
1000 if (hidden() != yn) {
1002 send_change (Properties::hidden);
1007 Region::set_whole_file (bool yn)
1010 /* no change signal */
1014 Region::set_automatic (bool yn)
1017 /* no change signal */
1021 Region::set_muted (bool yn)
1023 if (muted() != yn) {
1025 send_change (Properties::muted);
1030 Region::set_opaque (bool yn)
1032 if (opaque() != yn) {
1034 send_change (Properties::opaque);
1039 Region::set_locked (bool yn)
1041 if (locked() != yn) {
1043 send_change (Properties::locked);
1048 Region::set_video_locked (bool yn)
1050 if (video_locked() != yn) {
1052 send_change (Properties::video_locked);
1057 Region::set_position_locked (bool yn)
1059 if (position_locked() != yn) {
1060 _position_locked = yn;
1061 send_change (Properties::locked);
1065 /** Set the region's sync point.
1066 * @param absolute_pos Session time.
1069 Region::set_sync_position (framepos_t absolute_pos)
1071 /* position within our file */
1072 framepos_t const file_pos = _start + (absolute_pos - _position);
1074 if (file_pos != _sync_position) {
1075 _sync_marked = true;
1076 _sync_position = file_pos;
1077 if (!property_changes_suspended()) {
1081 send_change (Properties::sync_position);
1086 Region::clear_sync_position ()
1088 if (sync_marked()) {
1089 _sync_marked = false;
1090 if (!property_changes_suspended()) {
1094 send_change (Properties::sync_position);
1098 /* @return the sync point relative the first frame of the region */
1100 Region::sync_offset (int& dir) const
1102 if (sync_marked()) {
1103 if (_sync_position > _start) {
1105 return _sync_position - _start;
1108 return _start - _sync_position;
1117 Region::adjust_to_sync (framepos_t pos) const
1120 frameoffset_t offset = sync_offset (sync_dir);
1122 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1131 if (max_framepos - pos > offset) {
1139 /** @return Sync position in session time */
1141 Region::sync_position() const
1143 if (sync_marked()) {
1144 return _position - _start + _sync_position;
1146 /* if sync has not been marked, use the start of the region */
1154 boost::shared_ptr<Playlist> pl (playlist());
1156 pl->raise_region (shared_from_this ());
1163 boost::shared_ptr<Playlist> pl (playlist());
1165 pl->lower_region (shared_from_this ());
1171 Region::raise_to_top ()
1173 boost::shared_ptr<Playlist> pl (playlist());
1175 pl->raise_region_to_top (shared_from_this());
1180 Region::lower_to_bottom ()
1182 boost::shared_ptr<Playlist> pl (playlist());
1184 pl->lower_region_to_bottom (shared_from_this());
1189 Region::set_layer (layer_t l)
1197 XMLNode *node = new XMLNode ("Region");
1201 const char* fe = NULL;
1203 /* custom version of 'add_properties (*node);'
1204 * skip values that have have dedicated save functions
1205 * in AudioRegion::state()
1207 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1208 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1209 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1210 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1211 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1212 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1213 i->second->get_value (*node);
1216 id().print (buf, sizeof (buf));
1217 node->add_property ("id", buf);
1218 node->add_property ("type", _type.to_string());
1220 switch (_first_edit) {
1221 case EditChangesNothing:
1224 case EditChangesName:
1230 default: /* should be unreachable but makes g++ happy */
1235 node->add_property ("first-edit", fe);
1237 /* note: flags are stored by derived classes */
1239 for (uint32_t n=0; n < _sources.size(); ++n) {
1240 snprintf (buf2, sizeof(buf2), "source-%d", n);
1241 _sources[n]->id().print (buf, sizeof(buf));
1242 node->add_property (buf2, buf);
1245 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1246 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1247 _master_sources[n]->id().print (buf, sizeof (buf));
1248 node->add_property (buf2, buf);
1251 /* Only store nested sources for the whole-file region that acts
1252 as the parent/root of all regions using it.
1255 if (_whole_file && max_source_level() > 0) {
1257 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1259 /* region is compound - get its playlist and
1260 store that before we list the region that
1264 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1265 nested_node->add_child_nocopy ((*s)->get_state ());
1269 node->add_child_nocopy (*nested_node);
1274 node->add_child_copy (*_extra_xml);
1281 Region::get_state ()
1287 Region::set_state (const XMLNode& node, int version)
1289 PropertyChange what_changed;
1290 return _set_state (node, version, what_changed, true);
1294 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1296 XMLProperty const * prop;
1297 Timecode::BBT_Time bbt_time;
1299 Stateful::save_extra_xml (node);
1301 what_changed = set_values (node);
1305 if (_position_lock_style == MusicTime) {
1306 if ((prop = node.property ("bbt-position")) != 0) {
1307 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1310 &bbt_time.ticks) != 3) {
1311 _position_lock_style = AudioTime;
1312 _beat = _session.tempo_map().beat_at_frame (_position);
1314 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1319 /* fix problems with old sessions corrupted by impossible
1320 values for _stretch or _shift
1322 if (_stretch == 0.0f) {
1326 if (_shift == 0.0f) {
1331 send_change (what_changed);
1334 /* Quick fix for 2.x sessions when region is muted */
1335 if ((prop = node.property (X_("flags")))) {
1336 if (string::npos != prop->value().find("Muted")){
1341 // saved property is invalid, region-transients are not saved
1342 if (_user_transients.size() == 0){
1343 _valid_transients = false;
1350 Region::suspend_property_changes ()
1352 Stateful::suspend_property_changes ();
1353 _last_length = _length;
1354 _last_position = _position;
1358 Region::mid_thaw (const PropertyChange& what_changed)
1360 if (what_changed.contains (Properties::length)) {
1361 if (what_changed.contains (Properties::position)) {
1362 recompute_at_start ();
1364 recompute_at_end ();
1369 Region::send_change (const PropertyChange& what_changed)
1371 if (what_changed.empty()) {
1375 Stateful::send_change (what_changed);
1377 if (!Stateful::property_changes_suspended()) {
1379 /* Try and send a shared_pointer unless this is part of the constructor.
1384 boost::shared_ptr<Region> rptr = shared_from_this();
1385 RegionPropertyChanged (rptr, what_changed);
1387 /* no shared_ptr available, relax; */
1393 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1395 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1399 Region::equivalent (boost::shared_ptr<const Region> other) const
1401 return _start == other->_start &&
1402 _position == other->_position &&
1403 _length == other->_length;
1407 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1409 return _start == other->_start &&
1410 _length == other->_length;
1414 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1416 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1420 Region::source_deleted (boost::weak_ptr<Source>)
1424 if (!_session.deletion_in_progress()) {
1425 /* this is a very special case: at least one of the region's
1426 sources has bee deleted, so invalidate all references to
1427 ourselves. Do NOT do this during session deletion, because
1428 then we run the risk that this will actually result
1429 in this object being deleted (as refcnt goes to zero)
1430 while emitting DropReferences.
1438 Region::master_source_names ()
1440 SourceList::iterator i;
1442 vector<string> names;
1443 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1444 names.push_back((*i)->name());
1451 Region::set_master_sources (const SourceList& srcs)
1453 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1454 (*i)->dec_use_count ();
1457 _master_sources = srcs;
1458 assert (_sources.size() == _master_sources.size());
1460 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1461 (*i)->inc_use_count ();
1466 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1471 if ((_sources.size() != other->_sources.size()) ||
1472 (_master_sources.size() != other->_master_sources.size())) {
1476 SourceList::const_iterator i;
1477 SourceList::const_iterator io;
1479 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1480 if ((*i)->id() != (*io)->id()) {
1485 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1486 if ((*i)->id() != (*io)->id()) {
1495 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1501 SourceList::const_iterator i;
1502 SourceList::const_iterator io;
1504 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1505 if ((*i)->id() == (*io)->id()) {
1514 Region::source_string () const
1516 //string res = itos(_sources.size());
1519 res << _sources.size() << ":";
1521 SourceList::const_iterator i;
1523 for (i = _sources.begin(); i != _sources.end(); ++i) {
1524 res << (*i)->id() << ":";
1527 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1528 res << (*i)->id() << ":";
1535 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1537 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1539 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1542 if (sources.find (ps) == sources.end()) {
1543 /* (Playlist)Source not currently in
1544 accumulating set, so recurse.
1546 ps->playlist()->deep_sources (sources);
1550 /* add this source */
1551 sources.insert (*i);
1554 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1556 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1559 if (sources.find (ps) == sources.end()) {
1560 /* (Playlist)Source not currently in
1561 accumulating set, so recurse.
1563 ps->playlist()->deep_sources (sources);
1567 /* add this source */
1568 sources.insert (*i);
1573 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1575 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1581 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1584 if (ps->playlist()->uses_source (source)) {
1591 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1597 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1600 if (ps->playlist()->uses_source (source)) {
1612 Region::source_length(uint32_t n) const
1614 assert (n < _sources.size());
1615 return _sources[n]->length (_position - _start);
1619 Region::verify_length (framecnt_t& len)
1621 if (source() && (source()->destructive() || source()->length_mutable())) {
1625 framecnt_t maxlen = 0;
1627 for (uint32_t n = 0; n < _sources.size(); ++n) {
1628 maxlen = max (maxlen, source_length(n) - _start);
1631 len = min (len, maxlen);
1637 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1639 if (source() && (source()->destructive() || source()->length_mutable())) {
1643 framecnt_t maxlen = 0;
1645 for (uint32_t n = 0; n < _sources.size(); ++n) {
1646 maxlen = max (maxlen, source_length(n) - new_start);
1649 new_length = min (new_length, maxlen);
1655 Region::verify_start (framepos_t pos)
1657 if (source() && (source()->destructive() || source()->length_mutable())) {
1661 for (uint32_t n = 0; n < _sources.size(); ++n) {
1662 if (pos > source_length(n) - _length) {
1670 Region::verify_start_mutable (framepos_t& new_start)
1672 if (source() && (source()->destructive() || source()->length_mutable())) {
1676 for (uint32_t n = 0; n < _sources.size(); ++n) {
1677 if (new_start > source_length(n) - _length) {
1678 new_start = source_length(n) - _length;
1684 boost::shared_ptr<Region>
1685 Region::get_parent() const
1687 boost::shared_ptr<Playlist> pl (playlist());
1690 boost::shared_ptr<Region> r;
1691 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1693 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1694 return boost::static_pointer_cast<Region> (r);
1698 return boost::shared_ptr<Region>();
1702 Region::apply (Filter& filter, Progress* progress)
1704 return filter.run (shared_from_this(), progress);
1709 Region::maybe_invalidate_transients ()
1711 bool changed = !_onsets.empty();
1714 if (_valid_transients || changed) {
1715 send_change (PropertyChange (Properties::valid_transients));
1721 Region::transients (AnalysisFeatureList& afl)
1723 int cnt = afl.empty() ? 0 : 1;
1725 Region::merge_features (afl, _onsets, _position);
1726 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1727 if (!_onsets.empty ()) {
1730 if (!_user_transients.empty ()) {
1735 // remove exact duplicates
1736 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1741 Region::has_transients () const
1743 if (!_user_transients.empty ()) {
1744 assert (_valid_transients);
1747 if (!_onsets.empty ()) {
1754 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1756 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1757 const frameoffset_t p = (*x) + off;
1758 if (p < first_frame() || p > last_frame()) {
1761 result.push_back (p);
1766 Region::drop_sources ()
1768 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1769 (*i)->dec_use_count ();
1774 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1775 (*i)->dec_use_count ();
1778 _master_sources.clear ();
1782 Region::use_sources (SourceList const & s)
1784 set<boost::shared_ptr<Source> > unique_srcs;
1786 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1788 _sources.push_back (*i);
1789 (*i)->inc_use_count ();
1790 _master_sources.push_back (*i);
1791 (*i)->inc_use_count ();
1793 /* connect only once to DropReferences, even if sources are replicated
1796 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1797 unique_srcs.insert (*i);
1798 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1804 Region::can_trim () const
1806 CanTrim ct = CanTrim (0);
1812 /* if not locked, we can always move the front later, and the end earlier
1815 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1817 if (start() != 0 || can_trim_start_before_source_start ()) {
1818 ct = CanTrim (ct | FrontTrimEarlier);
1821 if (!_sources.empty()) {
1822 if ((start() + length()) < _sources.front()->length (0)) {
1823 ct = CanTrim (ct | EndTrimLater);
1831 Region::max_source_level () const
1835 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1836 lvl = max (lvl, (*i)->level());
1843 Region::is_compound () const
1845 return max_source_level() > 0;
1849 Region::post_set (const PropertyChange& pc)
1851 if (pc.contains (Properties::position)) {
1852 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1857 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1863 Region::earliest_possible_position () const
1865 if (_start > _position) {
1868 return _position - _start;
1873 Region::latest_possible_frame () const
1875 framecnt_t minlen = max_framecnt;
1877 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1878 /* non-audio regions have a length that may vary based on their
1879 * position, so we have to pass it in the call.
1881 minlen = min (minlen, (*i)->length (_position));
1884 /* the latest possible last frame is determined by the current
1885 * position, plus the shortest source extent past _start.
1888 return _position + (minlen - _start) - 1;