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<double> pulse;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::PropertyDescriptor<uint64_t> layering_index;
80 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
83 Region::make_property_quarks ()
85 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
87 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
89 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
91 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
93 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
95 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
97 Properties::import.property_id = g_quark_from_static_string (X_("import"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
99 Properties::external.property_id = g_quark_from_static_string (X_("external"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
101 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
103 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
105 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
107 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
109 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
111 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
113 Properties::start.property_id = g_quark_from_static_string (X_("start"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
115 Properties::length.property_id = g_quark_from_static_string (X_("length"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
117 Properties::position.property_id = g_quark_from_static_string (X_("position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
119 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
121 Properties::pulse.property_id = g_quark_from_static_string (X_("pulse"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for pulse = %1\n", Properties::pulse.property_id));
123 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
125 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
127 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
129 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
131 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
133 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
134 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
135 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
136 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
137 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
138 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
142 Region::register_properties ()
144 _xml_node_name = X_("Region");
146 add_property (_muted);
147 add_property (_opaque);
148 add_property (_locked);
149 add_property (_video_locked);
150 add_property (_automatic);
151 add_property (_whole_file);
152 add_property (_import);
153 add_property (_external);
154 add_property (_sync_marked);
155 add_property (_left_of_split);
156 add_property (_right_of_split);
157 add_property (_hidden);
158 add_property (_position_locked);
159 add_property (_valid_transients);
160 add_property (_start);
161 add_property (_length);
162 add_property (_position);
163 add_property (_beat);
164 add_property (_pulse);
165 add_property (_sync_position);
166 add_property (_ancestral_start);
167 add_property (_ancestral_length);
168 add_property (_stretch);
169 add_property (_shift);
170 add_property (_position_lock_style);
171 add_property (_layering_index);
174 #define REGION_DEFAULT_STATE(s,l) \
175 _sync_marked (Properties::sync_marked, false) \
176 , _left_of_split (Properties::left_of_split, false) \
177 , _right_of_split (Properties::right_of_split, false) \
178 , _valid_transients (Properties::valid_transients, false) \
179 , _start (Properties::start, (s)) \
180 , _length (Properties::length, (l)) \
181 , _position (Properties::position, 0) \
182 , _beat (Properties::beat, 0.0) \
183 , _pulse (Properties::pulse, 0.0) \
184 , _sync_position (Properties::sync_position, (s)) \
185 , _transient_user_start (0) \
186 , _transient_analysis_start (0) \
187 , _transient_analysis_end (0) \
188 , _muted (Properties::muted, false) \
189 , _opaque (Properties::opaque, true) \
190 , _locked (Properties::locked, false) \
191 , _video_locked (Properties::video_locked, false) \
192 , _automatic (Properties::automatic, false) \
193 , _whole_file (Properties::whole_file, false) \
194 , _import (Properties::import, false) \
195 , _external (Properties::external, false) \
196 , _hidden (Properties::hidden, false) \
197 , _position_locked (Properties::position_locked, false) \
198 , _ancestral_start (Properties::ancestral_start, (s)) \
199 , _ancestral_length (Properties::ancestral_length, (l)) \
200 , _stretch (Properties::stretch, 1.0) \
201 , _shift (Properties::shift, 1.0) \
202 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
203 , _layering_index (Properties::layering_index, 0)
205 #define REGION_COPY_STATE(other) \
206 _sync_marked (Properties::sync_marked, other->_sync_marked) \
207 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
208 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
209 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
210 , _start(Properties::start, other->_start) \
211 , _length(Properties::length, other->_length) \
212 , _position(Properties::position, other->_position) \
213 , _beat (Properties::beat, other->_beat) \
214 , _pulse (Properties::pulse, other->_pulse) \
215 , _sync_position(Properties::sync_position, other->_sync_position) \
216 , _user_transients (other->_user_transients) \
217 , _transient_user_start (other->_transient_user_start) \
218 , _transients (other->_transients) \
219 , _transient_analysis_start (other->_transient_analysis_start) \
220 , _transient_analysis_end (other->_transient_analysis_end) \
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, framepos_t start, framecnt_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 _pulse = other->_pulse;
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, frameoffset_t offset, const int32_t sub_num)
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
350 _position = other->_position + offset;
355 use_sources (other->_sources);
356 set_master_sources (other->_master_sources);
358 _start = other->_start + offset;
359 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
360 _pulse = _session.tempo_map().pulse_at_beat (_beat);
362 /* if the other region had a distinct sync point
363 set, then continue to use it as best we can.
364 otherwise, reset sync point back to start.
367 if (other->sync_marked()) {
368 if (other->_sync_position < _start) {
369 _sync_marked = false;
370 _sync_position = _start;
372 _sync_position = other->_sync_position;
375 _sync_marked = false;
376 _sync_position = _start;
379 assert (_type == other->data_type());
382 /** Create a copy of @param other but with different sources. Used by filters */
383 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
384 : SessionObject (other->session(), other->name())
385 , _type (srcs.front()->type())
386 , REGION_COPY_STATE (other)
387 , _last_length (other->_last_length)
388 , _last_position (other->_last_position)
389 , _first_edit (EditChangesID)
390 , _layer (other->_layer)
392 register_properties ();
395 _position_locked = false;
397 other->_first_edit = EditChangesName;
399 if (other->_extra_xml) {
400 _extra_xml = new XMLNode (*other->_extra_xml);
406 assert(_sources.size() > 0);
411 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
416 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
418 _playlist = wpl.lock();
422 Region::set_name (const std::string& str)
425 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
426 assert(_name == str);
428 send_change (Properties::name);
435 Region::set_length (framecnt_t len, const int32_t sub_num)
437 //cerr << "Region::set_length() len = " << len << endl;
442 if (_length != len && len != 0) {
444 /* check that the current _position wouldn't make the new
448 if (max_framepos - len < _position) {
452 if (!verify_length (len)) {
457 set_length_internal (len, sub_num);
461 maybe_invalidate_transients ();
463 if (!property_changes_suspended()) {
467 send_change (Properties::length);
472 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
474 _last_length = _length;
479 Region::maybe_uncopy ()
481 /* this does nothing but marked a semantic moment once upon a time */
485 Region::first_edit ()
487 boost::shared_ptr<Playlist> pl (playlist());
489 if (_first_edit != EditChangesNothing && pl) {
491 _name = RegionFactory::new_region_name (_name);
492 _first_edit = EditChangesNothing;
494 send_change (Properties::name);
496 RegionFactory::CheckNewRegion (shared_from_this());
501 Region::at_natural_position () const
503 boost::shared_ptr<Playlist> pl (playlist());
509 boost::shared_ptr<Region> whole_file_region = get_parent();
511 if (whole_file_region) {
512 if (_position == whole_file_region->position() + _start) {
521 Region::move_to_natural_position ()
523 boost::shared_ptr<Playlist> pl (playlist());
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 set_position (whole_file_region->position() + _start);
537 Region::special_set_position (framepos_t pos)
539 /* this is used when creating a whole file region as
540 a way to store its "natural" or "captured" position.
543 _position = _position;
548 Region::set_position_lock_style (PositionLockStyle ps)
550 if (_position_lock_style != ps) {
552 boost::shared_ptr<Playlist> pl (playlist());
554 _position_lock_style = ps;
556 if (_position_lock_style == MusicTime) {
557 _beat = _session.tempo_map().beat_at_frame (_position);
558 _pulse = _session.tempo_map().pulse_at_beat (_beat);
561 send_change (Properties::position_lock_style);
566 Region::update_after_tempo_map_change (bool send)
568 boost::shared_ptr<Playlist> pl (playlist());
574 if (_position_lock_style == AudioTime) {
575 /* don't signal as the actual position has not chnged */
576 recompute_position_from_lock_style (0);
580 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
581 /* we have _beat. update frame position non-musically */
582 set_position_internal (pos, false, 0);
584 /* do this even if the position is the same. this helps out
585 a GUI that has moved its representation already.
589 send_change (Properties::position);
594 Region::set_position (framepos_t pos, int32_t sub_num)
601 set_position_internal (pos, true, 0);
603 double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
605 _pulse = _session.tempo_map().pulse_at_beat (_beat);
606 set_position_internal (pos, false, sub_num);
609 /* do this even if the position is the same. this helps out
610 a GUI that has moved its representation already.
612 PropertyChange p_and_l;
614 p_and_l.add (Properties::position);
615 /* Currently length change due to position change is only implemented
616 for MidiRegion (Region has no length in beats).
617 Notify a length change regardless (its more efficient for MidiRegions),
618 and when Region has a _length_beats we will need it here anyway).
620 p_and_l.add (Properties::length);
622 send_change (p_and_l);
626 /** A gui may need to create a region, then place it in an initial
627 * position determined by the user.
628 * When this takes place within one gui operation, we have to reset
629 * _last_position to prevent an implied move.
632 Region::set_initial_position (framepos_t pos)
638 if (_position != pos) {
641 /* check that the new _position wouldn't make the current
642 length impossible - if so, change the length.
644 XXX is this the right thing to do?
647 if (max_framepos - _length < _position) {
648 _last_length = _length;
649 _length = max_framepos - _position;
652 recompute_position_from_lock_style (0);
653 /* ensure that this move doesn't cause a range move */
654 _last_position = _position;
658 /* do this even if the position is the same. this helps out
659 a GUI that has moved its representation already.
661 send_change (Properties::position);
665 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
667 /* We emit a change of Properties::position even if the position hasn't changed
668 (see Region::set_position), so we must always set this up so that
669 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
671 _last_position = _position;
673 if (_position != pos) {
676 if (allow_bbt_recompute) {
677 recompute_position_from_lock_style (sub_num);
679 /* check that the new _position wouldn't make the current
680 length impossible - if so, change the length.
682 XXX is this the right thing to do?
684 if (max_framepos - _length < _position) {
685 _last_length = _length;
686 _length = max_framepos - _position;
692 Region::recompute_position_from_lock_style (const int32_t sub_num)
694 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
695 _pulse = _session.tempo_map().pulse_at_beat (_beat);
699 Region::nudge_position (frameoffset_t n)
701 if (locked() || video_locked()) {
709 framepos_t new_position = _position;
712 if (_position > max_framepos - n) {
713 new_position = max_framepos;
718 if (_position < -n) {
724 /* assumes non-musical nudge */
725 set_position_internal (new_position, true, 0);
727 send_change (Properties::position);
731 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
733 _ancestral_length = l;
734 _ancestral_start = s;
740 Region::set_start (framepos_t pos)
742 if (locked() || position_locked() || video_locked()) {
745 /* This just sets the start, nothing else. It effectively shifts
746 the contents of the Region within the overall extent of the Source,
747 without changing the Region's position or length
752 if (!verify_start (pos)) {
756 set_start_internal (pos);
759 maybe_invalidate_transients ();
761 send_change (Properties::start);
766 Region::move_start (frameoffset_t distance, const int32_t sub_num)
768 if (locked() || position_locked() || video_locked()) {
772 framepos_t new_start;
776 if (_start > max_framepos - distance) {
777 new_start = max_framepos; // makes no sense
779 new_start = _start + distance;
782 if (!verify_start (new_start)) {
786 } else if (distance < 0) {
788 if (_start < -distance) {
791 new_start = _start + distance;
798 if (new_start == _start) {
802 set_start_internal (new_start, sub_num);
807 send_change (Properties::start);
811 Region::trim_front (framepos_t new_position, const int32_t sub_num)
813 modify_front (new_position, false, sub_num);
817 Region::cut_front (framepos_t new_position, const int32_t sub_num)
819 modify_front (new_position, true, sub_num);
823 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
825 modify_end (new_endpoint, true, sub_num);
829 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
835 framepos_t end = last_frame();
836 framepos_t source_zero;
838 if (_position > _start) {
839 source_zero = _position - _start;
841 source_zero = 0; // its actually negative, but this will work for us
844 if (new_position < end) { /* can't trim it zero or negative length */
846 framecnt_t newlen = 0;
848 if (!can_trim_start_before_source_start ()) {
849 /* can't trim it back past where source position zero is located */
850 new_position = max (new_position, source_zero);
853 if (new_position > _position) {
854 newlen = _length - (new_position - _position);
856 newlen = _length + (_position - new_position);
859 trim_to_internal (new_position, newlen, sub_num);
862 _right_of_split = true;
865 if (!property_changes_suspended()) {
866 recompute_at_start ();
869 maybe_invalidate_transients ();
874 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
880 if (new_endpoint > _position) {
881 trim_to_internal (_position, new_endpoint - _position, sub_num);
883 _left_of_split = true;
885 if (!property_changes_suspended()) {
891 /** @param new_endpoint New region end point, such that, for example,
892 * a region at 0 of length 10 has an endpoint of 9.
896 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
898 modify_end (new_endpoint, false, sub_num);
902 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
908 trim_to_internal (position, length, sub_num);
910 if (!property_changes_suspended()) {
911 recompute_at_start ();
917 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
919 framepos_t new_start;
925 frameoffset_t const start_shift = position - _position;
927 if (start_shift > 0) {
929 if (_start > max_framepos - start_shift) {
930 new_start = max_framepos;
932 new_start = _start + start_shift;
935 } else if (start_shift < 0) {
937 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
940 new_start = _start + start_shift;
947 if (!verify_start_and_length (new_start, length)) {
951 PropertyChange what_changed;
953 if (_start != new_start) {
954 set_start_internal (new_start, sub_num);
955 what_changed.add (Properties::start);
959 /* Set position before length, otherwise for MIDI regions this bad thing happens:
960 * 1. we call set_length_internal; length in beats is computed using the region's current
961 * (soon-to-be old) position
962 * 2. we call set_position_internal; position is set and length in frames re-computed using
963 * length in beats from (1) but at the new position, which is wrong if the region
964 * straddles a tempo/meter change.
967 if (_position != position) {
968 if (!property_changes_suspended()) {
969 _last_position = _position;
971 set_position_internal (position, true, sub_num);
972 what_changed.add (Properties::position);
975 if (_length != length) {
976 if (!property_changes_suspended()) {
977 _last_length = _length;
979 set_length_internal (length, sub_num);
980 what_changed.add (Properties::length);
985 PropertyChange start_and_length;
987 start_and_length.add (Properties::start);
988 start_and_length.add (Properties::length);
990 if (what_changed.contains (start_and_length)) {
994 if (!what_changed.empty()) {
995 send_change (what_changed);
1000 Region::set_hidden (bool yn)
1002 if (hidden() != yn) {
1004 send_change (Properties::hidden);
1009 Region::set_whole_file (bool yn)
1012 /* no change signal */
1016 Region::set_automatic (bool yn)
1019 /* no change signal */
1023 Region::set_muted (bool yn)
1025 if (muted() != yn) {
1027 send_change (Properties::muted);
1032 Region::set_opaque (bool yn)
1034 if (opaque() != yn) {
1036 send_change (Properties::opaque);
1041 Region::set_locked (bool yn)
1043 if (locked() != yn) {
1045 send_change (Properties::locked);
1050 Region::set_video_locked (bool yn)
1052 if (video_locked() != yn) {
1054 send_change (Properties::video_locked);
1059 Region::set_position_locked (bool yn)
1061 if (position_locked() != yn) {
1062 _position_locked = yn;
1063 send_change (Properties::locked);
1067 /** Set the region's sync point.
1068 * @param absolute_pos Session time.
1071 Region::set_sync_position (framepos_t absolute_pos)
1073 /* position within our file */
1074 framepos_t const file_pos = _start + (absolute_pos - _position);
1076 if (file_pos != _sync_position) {
1077 _sync_marked = true;
1078 _sync_position = file_pos;
1079 if (!property_changes_suspended()) {
1083 send_change (Properties::sync_position);
1088 Region::clear_sync_position ()
1090 if (sync_marked()) {
1091 _sync_marked = false;
1092 if (!property_changes_suspended()) {
1096 send_change (Properties::sync_position);
1100 /* @return the sync point relative the first frame of the region */
1102 Region::sync_offset (int& dir) const
1104 if (sync_marked()) {
1105 if (_sync_position > _start) {
1107 return _sync_position - _start;
1110 return _start - _sync_position;
1119 Region::adjust_to_sync (framepos_t pos) const
1122 frameoffset_t offset = sync_offset (sync_dir);
1124 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1133 if (max_framepos - pos > offset) {
1141 /** @return Sync position in session time */
1143 Region::sync_position() const
1145 if (sync_marked()) {
1146 return _position - _start + _sync_position;
1148 /* if sync has not been marked, use the start of the region */
1156 boost::shared_ptr<Playlist> pl (playlist());
1158 pl->raise_region (shared_from_this ());
1165 boost::shared_ptr<Playlist> pl (playlist());
1167 pl->lower_region (shared_from_this ());
1173 Region::raise_to_top ()
1175 boost::shared_ptr<Playlist> pl (playlist());
1177 pl->raise_region_to_top (shared_from_this());
1182 Region::lower_to_bottom ()
1184 boost::shared_ptr<Playlist> pl (playlist());
1186 pl->lower_region_to_bottom (shared_from_this());
1191 Region::set_layer (layer_t l)
1199 XMLNode *node = new XMLNode ("Region");
1203 const char* fe = NULL;
1205 /* custom version of 'add_properties (*node);'
1206 * skip values that have have dedicated save functions
1207 * in AudioRegion::state()
1209 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1210 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1211 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1212 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1213 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1214 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1215 i->second->get_value (*node);
1218 id().print (buf, sizeof (buf));
1219 node->add_property ("id", buf);
1220 node->add_property ("type", _type.to_string());
1222 switch (_first_edit) {
1223 case EditChangesNothing:
1226 case EditChangesName:
1232 default: /* should be unreachable but makes g++ happy */
1237 node->add_property ("first-edit", fe);
1239 /* note: flags are stored by derived classes */
1241 for (uint32_t n=0; n < _sources.size(); ++n) {
1242 snprintf (buf2, sizeof(buf2), "source-%d", n);
1243 _sources[n]->id().print (buf, sizeof(buf));
1244 node->add_property (buf2, buf);
1247 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1248 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1249 _master_sources[n]->id().print (buf, sizeof (buf));
1250 node->add_property (buf2, buf);
1253 /* Only store nested sources for the whole-file region that acts
1254 as the parent/root of all regions using it.
1257 if (_whole_file && max_source_level() > 0) {
1259 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1261 /* region is compound - get its playlist and
1262 store that before we list the region that
1266 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1267 nested_node->add_child_nocopy ((*s)->get_state ());
1271 node->add_child_nocopy (*nested_node);
1276 node->add_child_copy (*_extra_xml);
1283 Region::get_state ()
1289 Region::set_state (const XMLNode& node, int version)
1291 PropertyChange what_changed;
1292 return _set_state (node, version, what_changed, true);
1296 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1298 XMLProperty const * prop;
1299 Timecode::BBT_Time bbt_time;
1301 Stateful::save_extra_xml (node);
1303 what_changed = set_values (node);
1307 if (_position_lock_style == MusicTime) {
1308 if ((prop = node.property ("bbt-position")) != 0) {
1309 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1312 &bbt_time.ticks) != 3) {
1313 _position_lock_style = AudioTime;
1315 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1316 _pulse = _session.tempo_map().pulse_at_beat (_beat);
1321 /* fix problems with old sessions corrupted by impossible
1322 values for _stretch or _shift
1324 if (_stretch == 0.0f) {
1328 if (_shift == 0.0f) {
1333 send_change (what_changed);
1336 /* Quick fix for 2.x sessions when region is muted */
1337 if ((prop = node.property (X_("flags")))) {
1338 if (string::npos != prop->value().find("Muted")){
1343 // saved property is invalid, region-transients are not saved
1344 if (_user_transients.size() == 0){
1345 _valid_transients = false;
1352 Region::suspend_property_changes ()
1354 Stateful::suspend_property_changes ();
1355 _last_length = _length;
1356 _last_position = _position;
1360 Region::mid_thaw (const PropertyChange& what_changed)
1362 if (what_changed.contains (Properties::length)) {
1363 if (what_changed.contains (Properties::position)) {
1364 recompute_at_start ();
1366 recompute_at_end ();
1371 Region::send_change (const PropertyChange& what_changed)
1373 if (what_changed.empty()) {
1377 Stateful::send_change (what_changed);
1379 if (!Stateful::property_changes_suspended()) {
1381 /* Try and send a shared_pointer unless this is part of the constructor.
1386 boost::shared_ptr<Region> rptr = shared_from_this();
1387 RegionPropertyChanged (rptr, what_changed);
1389 /* no shared_ptr available, relax; */
1395 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1397 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1401 Region::equivalent (boost::shared_ptr<const Region> other) const
1403 return _start == other->_start &&
1404 _position == other->_position &&
1405 _length == other->_length;
1409 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1411 return _start == other->_start &&
1412 _length == other->_length;
1416 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1418 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1422 Region::source_deleted (boost::weak_ptr<Source>)
1426 if (!_session.deletion_in_progress()) {
1427 /* this is a very special case: at least one of the region's
1428 sources has bee deleted, so invalidate all references to
1429 ourselves. Do NOT do this during session deletion, because
1430 then we run the risk that this will actually result
1431 in this object being deleted (as refcnt goes to zero)
1432 while emitting DropReferences.
1440 Region::master_source_names ()
1442 SourceList::iterator i;
1444 vector<string> names;
1445 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1446 names.push_back((*i)->name());
1453 Region::set_master_sources (const SourceList& srcs)
1455 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1456 (*i)->dec_use_count ();
1459 _master_sources = srcs;
1460 assert (_sources.size() == _master_sources.size());
1462 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1463 (*i)->inc_use_count ();
1468 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1473 if ((_sources.size() != other->_sources.size()) ||
1474 (_master_sources.size() != other->_master_sources.size())) {
1478 SourceList::const_iterator i;
1479 SourceList::const_iterator io;
1481 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1482 if ((*i)->id() != (*io)->id()) {
1487 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1488 if ((*i)->id() != (*io)->id()) {
1497 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1503 SourceList::const_iterator i;
1504 SourceList::const_iterator io;
1506 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1507 if ((*i)->id() == (*io)->id()) {
1516 Region::source_string () const
1518 //string res = itos(_sources.size());
1521 res << _sources.size() << ":";
1523 SourceList::const_iterator i;
1525 for (i = _sources.begin(); i != _sources.end(); ++i) {
1526 res << (*i)->id() << ":";
1529 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1530 res << (*i)->id() << ":";
1537 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1539 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1541 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1544 if (sources.find (ps) == sources.end()) {
1545 /* (Playlist)Source not currently in
1546 accumulating set, so recurse.
1548 ps->playlist()->deep_sources (sources);
1552 /* add this source */
1553 sources.insert (*i);
1556 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1558 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1561 if (sources.find (ps) == sources.end()) {
1562 /* (Playlist)Source not currently in
1563 accumulating set, so recurse.
1565 ps->playlist()->deep_sources (sources);
1569 /* add this source */
1570 sources.insert (*i);
1575 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1577 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1583 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1586 if (ps->playlist()->uses_source (source)) {
1593 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1599 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1602 if (ps->playlist()->uses_source (source)) {
1614 Region::source_length(uint32_t n) const
1616 assert (n < _sources.size());
1617 return _sources[n]->length (_position - _start);
1621 Region::verify_length (framecnt_t& len)
1623 if (source() && (source()->destructive() || source()->length_mutable())) {
1627 framecnt_t maxlen = 0;
1629 for (uint32_t n = 0; n < _sources.size(); ++n) {
1630 maxlen = max (maxlen, source_length(n) - _start);
1633 len = min (len, maxlen);
1639 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1641 if (source() && (source()->destructive() || source()->length_mutable())) {
1645 framecnt_t maxlen = 0;
1647 for (uint32_t n = 0; n < _sources.size(); ++n) {
1648 maxlen = max (maxlen, source_length(n) - new_start);
1651 new_length = min (new_length, maxlen);
1657 Region::verify_start (framepos_t pos)
1659 if (source() && (source()->destructive() || source()->length_mutable())) {
1663 for (uint32_t n = 0; n < _sources.size(); ++n) {
1664 if (pos > source_length(n) - _length) {
1672 Region::verify_start_mutable (framepos_t& new_start)
1674 if (source() && (source()->destructive() || source()->length_mutable())) {
1678 for (uint32_t n = 0; n < _sources.size(); ++n) {
1679 if (new_start > source_length(n) - _length) {
1680 new_start = source_length(n) - _length;
1686 boost::shared_ptr<Region>
1687 Region::get_parent() const
1689 boost::shared_ptr<Playlist> pl (playlist());
1692 boost::shared_ptr<Region> r;
1693 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1695 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1696 return boost::static_pointer_cast<Region> (r);
1700 return boost::shared_ptr<Region>();
1704 Region::apply (Filter& filter, Progress* progress)
1706 return filter.run (shared_from_this(), progress);
1711 Region::maybe_invalidate_transients ()
1713 bool changed = !_onsets.empty();
1716 if (_valid_transients || changed) {
1717 send_change (PropertyChange (Properties::valid_transients));
1723 Region::transients (AnalysisFeatureList& afl)
1725 int cnt = afl.empty() ? 0 : 1;
1727 Region::merge_features (afl, _onsets, _position);
1728 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1729 if (!_onsets.empty ()) {
1732 if (!_user_transients.empty ()) {
1737 // remove exact duplicates
1738 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1743 Region::has_transients () const
1745 if (!_user_transients.empty ()) {
1746 assert (_valid_transients);
1749 if (!_onsets.empty ()) {
1756 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1758 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1759 const frameoffset_t p = (*x) + off;
1760 if (p < first_frame() || p > last_frame()) {
1763 result.push_back (p);
1768 Region::drop_sources ()
1770 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1771 (*i)->dec_use_count ();
1776 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1777 (*i)->dec_use_count ();
1780 _master_sources.clear ();
1784 Region::use_sources (SourceList const & s)
1786 set<boost::shared_ptr<Source> > unique_srcs;
1788 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1790 _sources.push_back (*i);
1791 (*i)->inc_use_count ();
1792 _master_sources.push_back (*i);
1793 (*i)->inc_use_count ();
1795 /* connect only once to DropReferences, even if sources are replicated
1798 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1799 unique_srcs.insert (*i);
1800 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1806 Region::can_trim () const
1808 CanTrim ct = CanTrim (0);
1814 /* if not locked, we can always move the front later, and the end earlier
1817 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1819 if (start() != 0 || can_trim_start_before_source_start ()) {
1820 ct = CanTrim (ct | FrontTrimEarlier);
1823 if (!_sources.empty()) {
1824 if ((start() + length()) < _sources.front()->length (0)) {
1825 ct = CanTrim (ct | EndTrimLater);
1833 Region::max_source_level () const
1837 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1838 lvl = max (lvl, (*i)->level());
1845 Region::is_compound () const
1847 return max_source_level() > 0;
1851 Region::post_set (const PropertyChange& pc)
1853 if (pc.contains (Properties::position)) {
1854 recompute_position_from_lock_style (0);
1859 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1865 Region::earliest_possible_position () const
1867 if (_start > _position) {
1870 return _position - _start;
1875 Region::latest_possible_frame () const
1877 framecnt_t minlen = max_framecnt;
1879 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1880 /* non-audio regions have a length that may vary based on their
1881 * position, so we have to pass it in the call.
1883 minlen = min (minlen, (*i)->length (_position));
1886 /* the latest possible last frame is determined by the current
1887 * position, plus the shortest source extent past _start.
1890 return _position + (minlen - _start) - 1;