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<framecnt_t> sync_position;
68 PBD::PropertyDescriptor<layer_t> layer;
69 PBD::PropertyDescriptor<framepos_t> ancestral_start;
70 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
71 PBD::PropertyDescriptor<float> stretch;
72 PBD::PropertyDescriptor<float> shift;
73 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
74 PBD::PropertyDescriptor<uint64_t> layering_index;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
91 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
93 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
95 Properties::import.property_id = g_quark_from_static_string (X_("import"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
97 Properties::external.property_id = g_quark_from_static_string (X_("external"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
99 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
101 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
103 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
105 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
107 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
109 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
111 Properties::start.property_id = g_quark_from_static_string (X_("start"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
113 Properties::length.property_id = g_quark_from_static_string (X_("length"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
115 Properties::position.property_id = g_quark_from_static_string (X_("position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
117 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
119 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
121 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
123 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
125 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
127 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
129 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
136 Region::register_properties ()
138 _xml_node_name = X_("Region");
140 add_property (_muted);
141 add_property (_opaque);
142 add_property (_locked);
143 add_property (_video_locked);
144 add_property (_automatic);
145 add_property (_whole_file);
146 add_property (_import);
147 add_property (_external);
148 add_property (_sync_marked);
149 add_property (_left_of_split);
150 add_property (_right_of_split);
151 add_property (_hidden);
152 add_property (_position_locked);
153 add_property (_valid_transients);
154 add_property (_start);
155 add_property (_length);
156 add_property (_position);
157 add_property (_sync_position);
158 add_property (_ancestral_start);
159 add_property (_ancestral_length);
160 add_property (_stretch);
161 add_property (_shift);
162 add_property (_position_lock_style);
163 add_property (_layering_index);
166 #define REGION_DEFAULT_STATE(s,l) \
167 _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _valid_transients (Properties::valid_transients, false) \
171 , _start (Properties::start, (s)) \
172 , _length (Properties::length, (l)) \
173 , _position (Properties::position, 0) \
174 , _sync_position (Properties::sync_position, (s)) \
175 , _transient_user_start (0) \
176 , _transient_analysis_start (0) \
177 , _transient_analysis_end (0) \
178 , _muted (Properties::muted, false) \
179 , _opaque (Properties::opaque, true) \
180 , _locked (Properties::locked, false) \
181 , _video_locked (Properties::video_locked, false) \
182 , _automatic (Properties::automatic, false) \
183 , _whole_file (Properties::whole_file, false) \
184 , _import (Properties::import, false) \
185 , _external (Properties::external, false) \
186 , _hidden (Properties::hidden, false) \
187 , _position_locked (Properties::position_locked, false) \
188 , _ancestral_start (Properties::ancestral_start, (s)) \
189 , _ancestral_length (Properties::ancestral_length, (l)) \
190 , _stretch (Properties::stretch, 1.0) \
191 , _shift (Properties::shift, 1.0) \
192 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
193 , _layering_index (Properties::layering_index, 0)
195 #define REGION_COPY_STATE(other) \
196 _sync_marked (Properties::sync_marked, other->_sync_marked) \
197 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
198 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _user_transients (other->_user_transients) \
205 , _transient_user_start (other->_transient_user_start) \
206 , _transients (other->_transients) \
207 , _transient_analysis_start (other->_transient_analysis_start) \
208 , _transient_analysis_end (other->_transient_analysis_end) \
209 , _muted (Properties::muted, other->_muted) \
210 , _opaque (Properties::opaque, other->_opaque) \
211 , _locked (Properties::locked, other->_locked) \
212 , _video_locked (Properties::video_locked, other->_video_locked) \
213 , _automatic (Properties::automatic, other->_automatic) \
214 , _whole_file (Properties::whole_file, other->_whole_file) \
215 , _import (Properties::import, other->_import) \
216 , _external (Properties::external, other->_external) \
217 , _hidden (Properties::hidden, other->_hidden) \
218 , _position_locked (Properties::position_locked, other->_position_locked) \
219 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
220 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
221 , _stretch (Properties::stretch, other->_stretch) \
222 , _shift (Properties::shift, other->_shift) \
223 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
224 , _layering_index (Properties::layering_index, other->_layering_index)
226 /* derived-from-derived constructor (no sources in constructor) */
227 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
228 : SessionObject(s, name)
230 , REGION_DEFAULT_STATE(start,length)
231 , _last_length (length)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 /* no sources at this point */
241 /** Basic Region constructor (many sources) */
242 Region::Region (const SourceList& srcs)
243 : SessionObject(srcs.front()->session(), "toBeRenamed")
244 , _type (srcs.front()->type())
245 , REGION_DEFAULT_STATE(0,0)
248 , _first_edit (EditChangesNothing)
251 register_properties ();
253 _type = srcs.front()->type();
257 assert(_sources.size() > 0);
258 assert (_type == srcs.front()->type());
261 /** Create a new Region from an existing one */
262 Region::Region (boost::shared_ptr<const Region> other)
263 : SessionObject(other->session(), other->name())
264 , _type (other->data_type())
265 , REGION_COPY_STATE (other)
266 , _last_length (other->_last_length)
267 , _last_position(other->_last_position) \
268 , _first_edit (EditChangesNothing)
269 , _layer (other->_layer)
271 register_properties ();
273 /* override state that may have been incorrectly inherited from the other region
276 _position = other->_position;
281 use_sources (other->_sources);
282 set_master_sources (other->_master_sources);
284 _position_lock_style = other->_position_lock_style;
285 _first_edit = other->_first_edit;
287 _start = other->_start;
288 _beat = other->_beat;
290 /* sync pos is relative to start of file. our start-in-file is now zero,
291 so set our sync position to whatever the the difference between
292 _start and _sync_pos was in the other region.
294 result is that our new sync pos points to the same point in our source(s)
295 as the sync in the other region did in its source(s).
297 since we start at zero in our source(s), it is not possible to use a sync point that
298 is before the start. reset it to _start if that was true in the other region.
301 if (other->sync_marked()) {
302 if (other->_start < other->_sync_position) {
303 /* sync pos was after the start point of the other region */
304 _sync_position = other->_sync_position - other->_start;
306 /* sync pos was before the start point of the other region. not possible here. */
307 _sync_marked = false;
308 _sync_position = _start;
311 _sync_marked = false;
312 _sync_position = _start;
315 assert (_type == other->data_type());
318 /** Create a new Region from part of an existing one.
320 the start within \a other is given by \a offset
321 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
323 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
324 : SessionObject(other->session(), other->name())
325 , _type (other->data_type())
326 , REGION_COPY_STATE (other)
327 , _last_length (other->_last_length)
328 , _last_position(other->_last_position) \
329 , _first_edit (EditChangesNothing)
331 , _layer (other->_layer)
333 register_properties ();
335 /* override state that may have been incorrectly inherited from the other region
338 _position = other->_position + offset;
343 use_sources (other->_sources);
344 set_master_sources (other->_master_sources);
346 _start = other->_start + offset;
347 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
349 /* if the other region had a distinct sync point
350 set, then continue to use it as best we can.
351 otherwise, reset sync point back to start.
354 if (other->sync_marked()) {
355 if (other->_sync_position < _start) {
356 _sync_marked = false;
357 _sync_position = _start;
359 _sync_position = other->_sync_position;
362 _sync_marked = false;
363 _sync_position = _start;
366 assert (_type == other->data_type());
369 /** Create a copy of @param other but with different sources. Used by filters */
370 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
371 : SessionObject (other->session(), other->name())
372 , _type (srcs.front()->type())
373 , REGION_COPY_STATE (other)
374 , _last_length (other->_last_length)
375 , _last_position (other->_last_position)
376 , _first_edit (EditChangesID)
377 , _layer (other->_layer)
379 register_properties ();
382 _position_locked = false;
384 other->_first_edit = EditChangesName;
386 if (other->_extra_xml) {
387 _extra_xml = new XMLNode (*other->_extra_xml);
393 assert(_sources.size() > 0);
398 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
403 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
405 _playlist = wpl.lock();
409 Region::set_name (const std::string& str)
412 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
413 assert(_name == str);
415 send_change (Properties::name);
422 Region::set_length (framecnt_t len, const int32_t sub_num)
424 //cerr << "Region::set_length() len = " << len << endl;
429 if (_length != len && len != 0) {
431 /* check that the current _position wouldn't make the new
435 if (max_framepos - len < _position) {
439 if (!verify_length (len)) {
444 set_length_internal (len, sub_num);
448 maybe_invalidate_transients ();
450 if (!property_changes_suspended()) {
454 send_change (Properties::length);
459 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
461 _last_length = _length;
466 Region::maybe_uncopy ()
468 /* this does nothing but marked a semantic moment once upon a time */
472 Region::first_edit ()
474 boost::shared_ptr<Playlist> pl (playlist());
476 if (_first_edit != EditChangesNothing && pl) {
478 _name = RegionFactory::new_region_name (_name);
479 _first_edit = EditChangesNothing;
481 send_change (Properties::name);
483 RegionFactory::CheckNewRegion (shared_from_this());
488 Region::at_natural_position () const
490 boost::shared_ptr<Playlist> pl (playlist());
496 boost::shared_ptr<Region> whole_file_region = get_parent();
498 if (whole_file_region) {
499 if (_position == whole_file_region->position() + _start) {
508 Region::move_to_natural_position ()
510 boost::shared_ptr<Playlist> pl (playlist());
516 boost::shared_ptr<Region> whole_file_region = get_parent();
518 if (whole_file_region) {
519 set_position (whole_file_region->position() + _start);
524 Region::special_set_position (framepos_t pos)
526 /* this is used when creating a whole file region as
527 a way to store its "natural" or "captured" position.
530 _position = _position;
535 Region::set_position_lock_style (PositionLockStyle ps)
537 if (_position_lock_style != ps) {
539 boost::shared_ptr<Playlist> pl (playlist());
541 _position_lock_style = ps;
543 if (_position_lock_style == MusicTime) {
544 _beat = _session.tempo_map().beat_at_frame (_position);
547 send_change (Properties::position_lock_style);
552 Region::update_after_tempo_map_change (bool send)
554 boost::shared_ptr<Playlist> pl (playlist());
556 if (!pl || _position_lock_style != MusicTime) {
560 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
561 /* we have _beat. update frame position non-musically */
562 set_position_internal (pos, false, 0);
564 /* do this even if the position is the same. this helps out
565 a GUI that has moved its representation already.
569 send_change (Properties::position);
574 Region::set_position (framepos_t pos, int32_t sub_num)
581 set_position_internal (pos, true, 0);
583 double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
585 set_position_internal (pos, false, sub_num);
588 /* do this even if the position is the same. this helps out
589 a GUI that has moved its representation already.
591 PropertyChange p_and_l;
593 p_and_l.add (Properties::position);
594 /* Currently length change due to position change is only implemented
595 for MidiRegion (Region has no length in beats).
596 Notify a length change regardless (its more efficient for MidiRegions),
597 and when Region has a _length_beats we will need it here anyway).
599 if (position_lock_style() == MusicTime) {
600 p_and_l.add (Properties::length);
603 send_change (p_and_l);
607 /** A gui may need to create a region, then place it in an initial
608 * position determined by the user.
609 * When this takes place within one gui operation, we have to reset
610 * _last_position to prevent an implied move.
613 Region::set_initial_position (framepos_t pos)
619 if (_position != pos) {
622 /* check that the new _position wouldn't make the current
623 length impossible - if so, change the length.
625 XXX is this the right thing to do?
628 if (max_framepos - _length < _position) {
629 _last_length = _length;
630 _length = max_framepos - _position;
633 recompute_position_from_lock_style (0);
634 /* ensure that this move doesn't cause a range move */
635 _last_position = _position;
639 /* do this even if the position is the same. this helps out
640 a GUI that has moved its representation already.
642 send_change (Properties::position);
646 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
648 /* We emit a change of Properties::position even if the position hasn't changed
649 (see Region::set_position), so we must always set this up so that
650 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
652 _last_position = _position;
654 if (_position != pos) {
657 if (allow_bbt_recompute) {
658 recompute_position_from_lock_style (sub_num);
660 /* check that the new _position wouldn't make the current
661 length impossible - if so, change the length.
663 XXX is this the right thing to do?
665 if (max_framepos - _length < _position) {
666 _last_length = _length;
667 _length = max_framepos - _position;
673 Region::recompute_position_from_lock_style (const int32_t sub_num)
675 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
679 Region::nudge_position (frameoffset_t n)
681 if (locked() || video_locked()) {
689 framepos_t new_position = _position;
692 if (_position > max_framepos - n) {
693 new_position = max_framepos;
698 if (_position < -n) {
704 /* assumes non-musical nudge */
705 set_position_internal (new_position, true, 0);
707 send_change (Properties::position);
711 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
713 _ancestral_length = l;
714 _ancestral_start = s;
720 Region::set_start (framepos_t pos)
722 if (locked() || position_locked() || video_locked()) {
725 /* This just sets the start, nothing else. It effectively shifts
726 the contents of the Region within the overall extent of the Source,
727 without changing the Region's position or length
732 if (!verify_start (pos)) {
736 set_start_internal (pos);
739 maybe_invalidate_transients ();
741 send_change (Properties::start);
746 Region::move_start (frameoffset_t distance, const int32_t sub_num)
748 if (locked() || position_locked() || video_locked()) {
752 framepos_t new_start;
756 if (_start > max_framepos - distance) {
757 new_start = max_framepos; // makes no sense
759 new_start = _start + distance;
762 if (!verify_start (new_start)) {
766 } else if (distance < 0) {
768 if (_start < -distance) {
771 new_start = _start + distance;
778 if (new_start == _start) {
782 set_start_internal (new_start, sub_num);
787 send_change (Properties::start);
791 Region::trim_front (framepos_t new_position, const int32_t sub_num)
793 modify_front (new_position, false, sub_num);
797 Region::cut_front (framepos_t new_position, const int32_t sub_num)
799 modify_front (new_position, true, sub_num);
803 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
805 modify_end (new_endpoint, true, sub_num);
809 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
815 framepos_t end = last_frame();
816 framepos_t source_zero;
818 if (_position > _start) {
819 source_zero = _position - _start;
821 source_zero = 0; // its actually negative, but this will work for us
824 if (new_position < end) { /* can't trim it zero or negative length */
826 framecnt_t newlen = 0;
828 if (!can_trim_start_before_source_start ()) {
829 /* can't trim it back past where source position zero is located */
830 new_position = max (new_position, source_zero);
833 if (new_position > _position) {
834 newlen = _length - (new_position - _position);
836 newlen = _length + (_position - new_position);
839 trim_to_internal (new_position, newlen, sub_num);
842 _right_of_split = true;
845 if (!property_changes_suspended()) {
846 recompute_at_start ();
849 maybe_invalidate_transients ();
854 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
860 if (new_endpoint > _position) {
861 trim_to_internal (_position, new_endpoint - _position, sub_num);
863 _left_of_split = true;
865 if (!property_changes_suspended()) {
871 /** @param new_endpoint New region end point, such that, for example,
872 * a region at 0 of length 10 has an endpoint of 9.
876 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
878 modify_end (new_endpoint, false, sub_num);
882 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
888 trim_to_internal (position, length, sub_num);
890 if (!property_changes_suspended()) {
891 recompute_at_start ();
897 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
899 framepos_t new_start;
905 frameoffset_t const start_shift = position - _position;
907 if (start_shift > 0) {
909 if (_start > max_framepos - start_shift) {
910 new_start = max_framepos;
912 new_start = _start + start_shift;
915 } else if (start_shift < 0) {
917 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
920 new_start = _start + start_shift;
927 if (!verify_start_and_length (new_start, length)) {
931 PropertyChange what_changed;
933 if (_start != new_start) {
934 set_start_internal (new_start, sub_num);
935 what_changed.add (Properties::start);
939 /* Set position before length, otherwise for MIDI regions this bad thing happens:
940 * 1. we call set_length_internal; length in beats is computed using the region's current
941 * (soon-to-be old) position
942 * 2. we call set_position_internal; position is set and length in frames re-computed using
943 * length in beats from (1) but at the new position, which is wrong if the region
944 * straddles a tempo/meter change.
947 if (_position != position) {
948 if (!property_changes_suspended()) {
949 _last_position = _position;
951 set_position_internal (position, true, sub_num);
952 what_changed.add (Properties::position);
955 if (_length != length) {
956 if (!property_changes_suspended()) {
957 _last_length = _length;
959 set_length_internal (length, sub_num);
960 what_changed.add (Properties::length);
965 PropertyChange start_and_length;
967 start_and_length.add (Properties::start);
968 start_and_length.add (Properties::length);
970 if (what_changed.contains (start_and_length)) {
974 if (!what_changed.empty()) {
975 send_change (what_changed);
980 Region::set_hidden (bool yn)
982 if (hidden() != yn) {
984 send_change (Properties::hidden);
989 Region::set_whole_file (bool yn)
992 /* no change signal */
996 Region::set_automatic (bool yn)
999 /* no change signal */
1003 Region::set_muted (bool yn)
1005 if (muted() != yn) {
1007 send_change (Properties::muted);
1012 Region::set_opaque (bool yn)
1014 if (opaque() != yn) {
1016 send_change (Properties::opaque);
1021 Region::set_locked (bool yn)
1023 if (locked() != yn) {
1025 send_change (Properties::locked);
1030 Region::set_video_locked (bool yn)
1032 if (video_locked() != yn) {
1034 send_change (Properties::video_locked);
1039 Region::set_position_locked (bool yn)
1041 if (position_locked() != yn) {
1042 _position_locked = yn;
1043 send_change (Properties::locked);
1047 /** Set the region's sync point.
1048 * @param absolute_pos Session time.
1051 Region::set_sync_position (framepos_t absolute_pos)
1053 /* position within our file */
1054 framepos_t const file_pos = _start + (absolute_pos - _position);
1056 if (file_pos != _sync_position) {
1057 _sync_marked = true;
1058 _sync_position = file_pos;
1059 if (!property_changes_suspended()) {
1063 send_change (Properties::sync_position);
1068 Region::clear_sync_position ()
1070 if (sync_marked()) {
1071 _sync_marked = false;
1072 if (!property_changes_suspended()) {
1076 send_change (Properties::sync_position);
1080 /* @return the sync point relative the first frame of the region */
1082 Region::sync_offset (int& dir) const
1084 if (sync_marked()) {
1085 if (_sync_position > _start) {
1087 return _sync_position - _start;
1090 return _start - _sync_position;
1099 Region::adjust_to_sync (framepos_t pos) const
1102 frameoffset_t offset = sync_offset (sync_dir);
1104 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1113 if (max_framepos - pos > offset) {
1121 /** @return Sync position in session time */
1123 Region::sync_position() const
1125 if (sync_marked()) {
1126 return _position - _start + _sync_position;
1128 /* if sync has not been marked, use the start of the region */
1136 boost::shared_ptr<Playlist> pl (playlist());
1138 pl->raise_region (shared_from_this ());
1145 boost::shared_ptr<Playlist> pl (playlist());
1147 pl->lower_region (shared_from_this ());
1153 Region::raise_to_top ()
1155 boost::shared_ptr<Playlist> pl (playlist());
1157 pl->raise_region_to_top (shared_from_this());
1162 Region::lower_to_bottom ()
1164 boost::shared_ptr<Playlist> pl (playlist());
1166 pl->lower_region_to_bottom (shared_from_this());
1171 Region::set_layer (layer_t l)
1179 XMLNode *node = new XMLNode ("Region");
1183 const char* fe = NULL;
1185 /* custom version of 'add_properties (*node);'
1186 * skip values that have have dedicated save functions
1187 * in AudioRegion::state()
1189 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1190 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1191 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1192 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1193 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1194 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1195 i->second->get_value (*node);
1198 id().print (buf, sizeof (buf));
1199 node->add_property ("id", buf);
1200 node->add_property ("type", _type.to_string());
1202 switch (_first_edit) {
1203 case EditChangesNothing:
1206 case EditChangesName:
1212 default: /* should be unreachable but makes g++ happy */
1217 node->add_property ("first-edit", fe);
1219 /* note: flags are stored by derived classes */
1221 if (_position_lock_style != AudioTime) {
1222 snprintf (buf, sizeof(buf), "%lf", _beat);
1223 node->add_property ("beat", buf);
1226 for (uint32_t n=0; n < _sources.size(); ++n) {
1227 snprintf (buf2, sizeof(buf2), "source-%d", n);
1228 _sources[n]->id().print (buf, sizeof(buf));
1229 node->add_property (buf2, buf);
1232 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1233 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1234 _master_sources[n]->id().print (buf, sizeof (buf));
1235 node->add_property (buf2, buf);
1238 /* Only store nested sources for the whole-file region that acts
1239 as the parent/root of all regions using it.
1242 if (_whole_file && max_source_level() > 0) {
1244 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1246 /* region is compound - get its playlist and
1247 store that before we list the region that
1251 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1252 nested_node->add_child_nocopy ((*s)->get_state ());
1256 node->add_child_nocopy (*nested_node);
1261 node->add_child_copy (*_extra_xml);
1268 Region::get_state ()
1274 Region::set_state (const XMLNode& node, int version)
1276 PropertyChange what_changed;
1277 return _set_state (node, version, what_changed, true);
1281 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1283 XMLProperty const * prop;
1284 Timecode::BBT_Time bbt_time;
1286 Stateful::save_extra_xml (node);
1288 what_changed = set_values (node);
1292 if (_position_lock_style == MusicTime) {
1293 if ((prop = node.property ("bbt-position")) == 0) {
1294 if ((prop = node.property ("beat")) == 0) {
1295 /* missing BBT info, revert to audio time locking */
1296 _position_lock_style = AudioTime;
1298 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1299 _position_lock_style = AudioTime;
1304 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1307 &bbt_time.ticks) != 3) {
1308 _position_lock_style = AudioTime;
1310 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1315 /* fix problems with old sessions corrupted by impossible
1316 values for _stretch or _shift
1318 if (_stretch == 0.0f) {
1322 if (_shift == 0.0f) {
1327 send_change (what_changed);
1330 /* Quick fix for 2.x sessions when region is muted */
1331 if ((prop = node.property (X_("flags")))) {
1332 if (string::npos != prop->value().find("Muted")){
1337 // saved property is invalid, region-transients are not saved
1338 if (_user_transients.size() == 0){
1339 _valid_transients = false;
1346 Region::suspend_property_changes ()
1348 Stateful::suspend_property_changes ();
1349 _last_length = _length;
1350 _last_position = _position;
1354 Region::mid_thaw (const PropertyChange& what_changed)
1356 if (what_changed.contains (Properties::length)) {
1357 if (what_changed.contains (Properties::position)) {
1358 recompute_at_start ();
1360 recompute_at_end ();
1365 Region::send_change (const PropertyChange& what_changed)
1367 if (what_changed.empty()) {
1371 Stateful::send_change (what_changed);
1373 if (!Stateful::property_changes_suspended()) {
1375 /* Try and send a shared_pointer unless this is part of the constructor.
1380 boost::shared_ptr<Region> rptr = shared_from_this();
1381 RegionPropertyChanged (rptr, what_changed);
1383 /* no shared_ptr available, relax; */
1389 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1391 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1395 Region::equivalent (boost::shared_ptr<const Region> other) const
1397 return _start == other->_start &&
1398 _position == other->_position &&
1399 _length == other->_length;
1403 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1405 return _start == other->_start &&
1406 _length == other->_length;
1410 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1412 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1416 Region::source_deleted (boost::weak_ptr<Source>)
1420 if (!_session.deletion_in_progress()) {
1421 /* this is a very special case: at least one of the region's
1422 sources has bee deleted, so invalidate all references to
1423 ourselves. Do NOT do this during session deletion, because
1424 then we run the risk that this will actually result
1425 in this object being deleted (as refcnt goes to zero)
1426 while emitting DropReferences.
1434 Region::master_source_names ()
1436 SourceList::iterator i;
1438 vector<string> names;
1439 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1440 names.push_back((*i)->name());
1447 Region::set_master_sources (const SourceList& srcs)
1449 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1450 (*i)->dec_use_count ();
1453 _master_sources = srcs;
1454 assert (_sources.size() == _master_sources.size());
1456 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1457 (*i)->inc_use_count ();
1462 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1467 if ((_sources.size() != other->_sources.size()) ||
1468 (_master_sources.size() != other->_master_sources.size())) {
1472 SourceList::const_iterator i;
1473 SourceList::const_iterator io;
1475 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1476 if ((*i)->id() != (*io)->id()) {
1481 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1482 if ((*i)->id() != (*io)->id()) {
1491 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1497 SourceList::const_iterator i;
1498 SourceList::const_iterator io;
1500 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1501 if ((*i)->id() == (*io)->id()) {
1510 Region::source_string () const
1512 //string res = itos(_sources.size());
1515 res << _sources.size() << ":";
1517 SourceList::const_iterator i;
1519 for (i = _sources.begin(); i != _sources.end(); ++i) {
1520 res << (*i)->id() << ":";
1523 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1524 res << (*i)->id() << ":";
1531 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1533 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1535 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1538 if (sources.find (ps) == sources.end()) {
1539 /* (Playlist)Source not currently in
1540 accumulating set, so recurse.
1542 ps->playlist()->deep_sources (sources);
1546 /* add this source */
1547 sources.insert (*i);
1550 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1552 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1555 if (sources.find (ps) == sources.end()) {
1556 /* (Playlist)Source not currently in
1557 accumulating set, so recurse.
1559 ps->playlist()->deep_sources (sources);
1563 /* add this source */
1564 sources.insert (*i);
1569 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1571 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1577 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1580 if (ps->playlist()->uses_source (source)) {
1587 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1593 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1596 if (ps->playlist()->uses_source (source)) {
1608 Region::source_length(uint32_t n) const
1610 assert (n < _sources.size());
1611 return _sources[n]->length (_position - _start);
1615 Region::verify_length (framecnt_t& len)
1617 if (source() && (source()->destructive() || source()->length_mutable())) {
1621 framecnt_t maxlen = 0;
1623 for (uint32_t n = 0; n < _sources.size(); ++n) {
1624 maxlen = max (maxlen, source_length(n) - _start);
1627 len = min (len, maxlen);
1633 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1635 if (source() && (source()->destructive() || source()->length_mutable())) {
1639 framecnt_t maxlen = 0;
1641 for (uint32_t n = 0; n < _sources.size(); ++n) {
1642 maxlen = max (maxlen, source_length(n) - new_start);
1645 new_length = min (new_length, maxlen);
1651 Region::verify_start (framepos_t pos)
1653 if (source() && (source()->destructive() || source()->length_mutable())) {
1657 for (uint32_t n = 0; n < _sources.size(); ++n) {
1658 if (pos > source_length(n) - _length) {
1666 Region::verify_start_mutable (framepos_t& new_start)
1668 if (source() && (source()->destructive() || source()->length_mutable())) {
1672 for (uint32_t n = 0; n < _sources.size(); ++n) {
1673 if (new_start > source_length(n) - _length) {
1674 new_start = source_length(n) - _length;
1680 boost::shared_ptr<Region>
1681 Region::get_parent() const
1683 boost::shared_ptr<Playlist> pl (playlist());
1686 boost::shared_ptr<Region> r;
1687 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1689 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1690 return boost::static_pointer_cast<Region> (r);
1694 return boost::shared_ptr<Region>();
1698 Region::apply (Filter& filter, Progress* progress)
1700 return filter.run (shared_from_this(), progress);
1705 Region::maybe_invalidate_transients ()
1707 bool changed = !_onsets.empty();
1710 if (_valid_transients || changed) {
1711 send_change (PropertyChange (Properties::valid_transients));
1717 Region::transients (AnalysisFeatureList& afl)
1719 int cnt = afl.empty() ? 0 : 1;
1721 Region::merge_features (afl, _onsets, _position);
1722 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1723 if (!_onsets.empty ()) {
1726 if (!_user_transients.empty ()) {
1731 // remove exact duplicates
1732 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1737 Region::has_transients () const
1739 if (!_user_transients.empty ()) {
1740 assert (_valid_transients);
1743 if (!_onsets.empty ()) {
1750 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1752 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1753 const frameoffset_t p = (*x) + off;
1754 if (p < first_frame() || p > last_frame()) {
1757 result.push_back (p);
1762 Region::drop_sources ()
1764 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1765 (*i)->dec_use_count ();
1770 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1771 (*i)->dec_use_count ();
1774 _master_sources.clear ();
1778 Region::use_sources (SourceList const & s)
1780 set<boost::shared_ptr<Source> > unique_srcs;
1782 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1784 _sources.push_back (*i);
1785 (*i)->inc_use_count ();
1786 _master_sources.push_back (*i);
1787 (*i)->inc_use_count ();
1789 /* connect only once to DropReferences, even if sources are replicated
1792 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1793 unique_srcs.insert (*i);
1794 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1800 Region::can_trim () const
1802 CanTrim ct = CanTrim (0);
1808 /* if not locked, we can always move the front later, and the end earlier
1811 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1813 if (start() != 0 || can_trim_start_before_source_start ()) {
1814 ct = CanTrim (ct | FrontTrimEarlier);
1817 if (!_sources.empty()) {
1818 if ((start() + length()) < _sources.front()->length (0)) {
1819 ct = CanTrim (ct | EndTrimLater);
1827 Region::max_source_level () const
1831 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1832 lvl = max (lvl, (*i)->level());
1839 Region::is_compound () const
1841 return max_source_level() > 0;
1845 Region::post_set (const PropertyChange& pc)
1847 if (pc.contains (Properties::position)) {
1848 recompute_position_from_lock_style (0);
1853 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1859 Region::earliest_possible_position () const
1861 if (_start > _position) {
1864 return _position - _start;
1869 Region::latest_possible_frame () const
1871 framecnt_t minlen = max_framecnt;
1873 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1874 /* non-audio regions have a length that may vary based on their
1875 * position, so we have to pass it in the call.
1877 minlen = min (minlen, (*i)->length (_position));
1880 /* the latest possible last frame is determined by the current
1881 * position, plus the shortest source extent past _start.
1884 return _position + (minlen - _start) - 1;