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 if (_position_lock_style == MusicTime) {
676 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
681 Region::nudge_position (frameoffset_t n)
683 if (locked() || video_locked()) {
691 framepos_t new_position = _position;
694 if (_position > max_framepos - n) {
695 new_position = max_framepos;
700 if (_position < -n) {
706 /* assumes non-musical nudge */
707 set_position_internal (new_position, true, 0);
709 send_change (Properties::position);
713 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
715 _ancestral_length = l;
716 _ancestral_start = s;
722 Region::set_start (framepos_t pos)
724 if (locked() || position_locked() || video_locked()) {
727 /* This just sets the start, nothing else. It effectively shifts
728 the contents of the Region within the overall extent of the Source,
729 without changing the Region's position or length
734 if (!verify_start (pos)) {
738 set_start_internal (pos);
741 maybe_invalidate_transients ();
743 send_change (Properties::start);
748 Region::move_start (frameoffset_t distance, const int32_t& sub_num)
750 if (locked() || position_locked() || video_locked()) {
754 framepos_t new_start;
758 if (_start > max_framepos - distance) {
759 new_start = max_framepos; // makes no sense
761 new_start = _start + distance;
764 if (!verify_start (new_start)) {
768 } else if (distance < 0) {
770 if (_start < -distance) {
773 new_start = _start + distance;
780 if (new_start == _start) {
784 set_start_internal (new_start, sub_num);
789 send_change (Properties::start);
793 Region::trim_front (framepos_t new_position, const int32_t& sub_num)
795 modify_front (new_position, false, sub_num);
799 Region::cut_front (framepos_t new_position, const int32_t& sub_num)
801 modify_front (new_position, true, sub_num);
805 Region::cut_end (framepos_t new_endpoint, const int32_t& sub_num)
807 modify_end (new_endpoint, true, sub_num);
811 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t& sub_num)
817 framepos_t end = last_frame();
818 framepos_t source_zero;
820 if (_position > _start) {
821 source_zero = _position - _start;
823 source_zero = 0; // its actually negative, but this will work for us
826 if (new_position < end) { /* can't trim it zero or negative length */
828 framecnt_t newlen = 0;
830 if (!can_trim_start_before_source_start ()) {
831 /* can't trim it back past where source position zero is located */
832 new_position = max (new_position, source_zero);
835 if (new_position > _position) {
836 newlen = _length - (new_position - _position);
838 newlen = _length + (_position - new_position);
841 trim_to_internal (new_position, newlen, sub_num);
844 _right_of_split = true;
847 if (!property_changes_suspended()) {
848 recompute_at_start ();
851 maybe_invalidate_transients ();
856 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t& sub_num)
862 if (new_endpoint > _position) {
863 trim_to_internal (_position, new_endpoint - _position, sub_num);
865 _left_of_split = true;
867 if (!property_changes_suspended()) {
873 /** @param new_endpoint New region end point, such that, for example,
874 * a region at 0 of length 10 has an endpoint of 9.
878 Region::trim_end (framepos_t new_endpoint, const int32_t& sub_num)
880 modify_end (new_endpoint, false, sub_num);
884 Region::trim_to (framepos_t position, framecnt_t length, const int32_t& sub_num)
890 trim_to_internal (position, length, sub_num);
892 if (!property_changes_suspended()) {
893 recompute_at_start ();
899 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t& sub_num)
901 framepos_t new_start;
907 frameoffset_t const start_shift = position - _position;
909 if (start_shift > 0) {
911 if (_start > max_framepos - start_shift) {
912 new_start = max_framepos;
914 new_start = _start + start_shift;
917 } else if (start_shift < 0) {
919 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
922 new_start = _start + start_shift;
929 if (!verify_start_and_length (new_start, length)) {
933 PropertyChange what_changed;
935 if (_start != new_start) {
936 set_start_internal (new_start, sub_num);
937 what_changed.add (Properties::start);
941 /* Set position before length, otherwise for MIDI regions this bad thing happens:
942 * 1. we call set_length_internal; length in beats is computed using the region's current
943 * (soon-to-be old) position
944 * 2. we call set_position_internal; position is set and length in frames re-computed using
945 * length in beats from (1) but at the new position, which is wrong if the region
946 * straddles a tempo/meter change.
949 if (_position != position) {
950 if (!property_changes_suspended()) {
951 _last_position = _position;
953 set_position_internal (position, true, sub_num);
954 what_changed.add (Properties::position);
957 if (_length != length) {
958 if (!property_changes_suspended()) {
959 _last_length = _length;
961 set_length_internal (length, sub_num);
962 what_changed.add (Properties::length);
967 PropertyChange start_and_length;
969 start_and_length.add (Properties::start);
970 start_and_length.add (Properties::length);
972 if (what_changed.contains (start_and_length)) {
976 if (!what_changed.empty()) {
977 send_change (what_changed);
982 Region::set_hidden (bool yn)
984 if (hidden() != yn) {
986 send_change (Properties::hidden);
991 Region::set_whole_file (bool yn)
994 /* no change signal */
998 Region::set_automatic (bool yn)
1001 /* no change signal */
1005 Region::set_muted (bool yn)
1007 if (muted() != yn) {
1009 send_change (Properties::muted);
1014 Region::set_opaque (bool yn)
1016 if (opaque() != yn) {
1018 send_change (Properties::opaque);
1023 Region::set_locked (bool yn)
1025 if (locked() != yn) {
1027 send_change (Properties::locked);
1032 Region::set_video_locked (bool yn)
1034 if (video_locked() != yn) {
1036 send_change (Properties::video_locked);
1041 Region::set_position_locked (bool yn)
1043 if (position_locked() != yn) {
1044 _position_locked = yn;
1045 send_change (Properties::locked);
1049 /** Set the region's sync point.
1050 * @param absolute_pos Session time.
1053 Region::set_sync_position (framepos_t absolute_pos)
1055 /* position within our file */
1056 framepos_t const file_pos = _start + (absolute_pos - _position);
1058 if (file_pos != _sync_position) {
1059 _sync_marked = true;
1060 _sync_position = file_pos;
1061 if (!property_changes_suspended()) {
1065 send_change (Properties::sync_position);
1070 Region::clear_sync_position ()
1072 if (sync_marked()) {
1073 _sync_marked = false;
1074 if (!property_changes_suspended()) {
1078 send_change (Properties::sync_position);
1082 /* @return the sync point relative the first frame of the region */
1084 Region::sync_offset (int& dir) const
1086 if (sync_marked()) {
1087 if (_sync_position > _start) {
1089 return _sync_position - _start;
1092 return _start - _sync_position;
1101 Region::adjust_to_sync (framepos_t pos) const
1104 frameoffset_t offset = sync_offset (sync_dir);
1106 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1115 if (max_framepos - pos > offset) {
1123 /** @return Sync position in session time */
1125 Region::sync_position() const
1127 if (sync_marked()) {
1128 return _position - _start + _sync_position;
1130 /* if sync has not been marked, use the start of the region */
1138 boost::shared_ptr<Playlist> pl (playlist());
1140 pl->raise_region (shared_from_this ());
1147 boost::shared_ptr<Playlist> pl (playlist());
1149 pl->lower_region (shared_from_this ());
1155 Region::raise_to_top ()
1157 boost::shared_ptr<Playlist> pl (playlist());
1159 pl->raise_region_to_top (shared_from_this());
1164 Region::lower_to_bottom ()
1166 boost::shared_ptr<Playlist> pl (playlist());
1168 pl->lower_region_to_bottom (shared_from_this());
1173 Region::set_layer (layer_t l)
1181 XMLNode *node = new XMLNode ("Region");
1185 const char* fe = NULL;
1187 /* custom version of 'add_properties (*node);'
1188 * skip values that have have dedicated save functions
1189 * in AudioRegion::state()
1191 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1192 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1193 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1194 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1195 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1196 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1197 i->second->get_value (*node);
1200 id().print (buf, sizeof (buf));
1201 node->add_property ("id", buf);
1202 node->add_property ("type", _type.to_string());
1204 switch (_first_edit) {
1205 case EditChangesNothing:
1208 case EditChangesName:
1214 default: /* should be unreachable but makes g++ happy */
1219 node->add_property ("first-edit", fe);
1221 /* note: flags are stored by derived classes */
1223 if (_position_lock_style != AudioTime) {
1224 snprintf (buf, sizeof(buf), "%lf", _beat);
1225 node->add_property ("beat", buf);
1228 for (uint32_t n=0; n < _sources.size(); ++n) {
1229 snprintf (buf2, sizeof(buf2), "source-%d", n);
1230 _sources[n]->id().print (buf, sizeof(buf));
1231 node->add_property (buf2, buf);
1234 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1235 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1236 _master_sources[n]->id().print (buf, sizeof (buf));
1237 node->add_property (buf2, buf);
1240 /* Only store nested sources for the whole-file region that acts
1241 as the parent/root of all regions using it.
1244 if (_whole_file && max_source_level() > 0) {
1246 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1248 /* region is compound - get its playlist and
1249 store that before we list the region that
1253 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1254 nested_node->add_child_nocopy ((*s)->get_state ());
1258 node->add_child_nocopy (*nested_node);
1263 node->add_child_copy (*_extra_xml);
1270 Region::get_state ()
1276 Region::set_state (const XMLNode& node, int version)
1278 PropertyChange what_changed;
1279 return _set_state (node, version, what_changed, true);
1283 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1285 XMLProperty const * prop;
1286 Timecode::BBT_Time bbt_time;
1288 Stateful::save_extra_xml (node);
1290 what_changed = set_values (node);
1294 if (_position_lock_style == MusicTime) {
1295 if ((prop = node.property ("bbt-position")) == 0) {
1296 if ((prop = node.property ("beat")) == 0) {
1297 /* missing BBT info, revert to audio time locking */
1298 _position_lock_style = AudioTime;
1300 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1301 _position_lock_style = AudioTime;
1306 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1309 &bbt_time.ticks) != 3) {
1310 _position_lock_style = AudioTime;
1312 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1317 /* fix problems with old sessions corrupted by impossible
1318 values for _stretch or _shift
1320 if (_stretch == 0.0f) {
1324 if (_shift == 0.0f) {
1329 send_change (what_changed);
1332 /* Quick fix for 2.x sessions when region is muted */
1333 if ((prop = node.property (X_("flags")))) {
1334 if (string::npos != prop->value().find("Muted")){
1339 // saved property is invalid, region-transients are not saved
1340 if (_user_transients.size() == 0){
1341 _valid_transients = false;
1348 Region::suspend_property_changes ()
1350 Stateful::suspend_property_changes ();
1351 _last_length = _length;
1352 _last_position = _position;
1356 Region::mid_thaw (const PropertyChange& what_changed)
1358 if (what_changed.contains (Properties::length)) {
1359 if (what_changed.contains (Properties::position)) {
1360 recompute_at_start ();
1362 recompute_at_end ();
1367 Region::send_change (const PropertyChange& what_changed)
1369 if (what_changed.empty()) {
1373 Stateful::send_change (what_changed);
1375 if (!Stateful::property_changes_suspended()) {
1377 /* Try and send a shared_pointer unless this is part of the constructor.
1382 boost::shared_ptr<Region> rptr = shared_from_this();
1383 RegionPropertyChanged (rptr, what_changed);
1385 /* no shared_ptr available, relax; */
1391 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1393 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1397 Region::equivalent (boost::shared_ptr<const Region> other) const
1399 return _start == other->_start &&
1400 _position == other->_position &&
1401 _length == other->_length;
1405 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1407 return _start == other->_start &&
1408 _length == other->_length;
1412 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1414 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1418 Region::source_deleted (boost::weak_ptr<Source>)
1422 if (!_session.deletion_in_progress()) {
1423 /* this is a very special case: at least one of the region's
1424 sources has bee deleted, so invalidate all references to
1425 ourselves. Do NOT do this during session deletion, because
1426 then we run the risk that this will actually result
1427 in this object being deleted (as refcnt goes to zero)
1428 while emitting DropReferences.
1436 Region::master_source_names ()
1438 SourceList::iterator i;
1440 vector<string> names;
1441 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1442 names.push_back((*i)->name());
1449 Region::set_master_sources (const SourceList& srcs)
1451 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1452 (*i)->dec_use_count ();
1455 _master_sources = srcs;
1456 assert (_sources.size() == _master_sources.size());
1458 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1459 (*i)->inc_use_count ();
1464 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1469 if ((_sources.size() != other->_sources.size()) ||
1470 (_master_sources.size() != other->_master_sources.size())) {
1474 SourceList::const_iterator i;
1475 SourceList::const_iterator io;
1477 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1478 if ((*i)->id() != (*io)->id()) {
1483 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1484 if ((*i)->id() != (*io)->id()) {
1493 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1499 SourceList::const_iterator i;
1500 SourceList::const_iterator io;
1502 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1503 if ((*i)->id() == (*io)->id()) {
1512 Region::source_string () const
1514 //string res = itos(_sources.size());
1517 res << _sources.size() << ":";
1519 SourceList::const_iterator i;
1521 for (i = _sources.begin(); i != _sources.end(); ++i) {
1522 res << (*i)->id() << ":";
1525 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1526 res << (*i)->id() << ":";
1533 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1535 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1537 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1540 if (sources.find (ps) == sources.end()) {
1541 /* (Playlist)Source not currently in
1542 accumulating set, so recurse.
1544 ps->playlist()->deep_sources (sources);
1548 /* add this source */
1549 sources.insert (*i);
1552 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1554 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1557 if (sources.find (ps) == sources.end()) {
1558 /* (Playlist)Source not currently in
1559 accumulating set, so recurse.
1561 ps->playlist()->deep_sources (sources);
1565 /* add this source */
1566 sources.insert (*i);
1571 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1573 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1579 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1582 if (ps->playlist()->uses_source (source)) {
1589 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1595 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1598 if (ps->playlist()->uses_source (source)) {
1610 Region::source_length(uint32_t n) const
1612 assert (n < _sources.size());
1613 return _sources[n]->length (_position - _start);
1617 Region::verify_length (framecnt_t& len)
1619 if (source() && (source()->destructive() || source()->length_mutable())) {
1623 framecnt_t maxlen = 0;
1625 for (uint32_t n = 0; n < _sources.size(); ++n) {
1626 maxlen = max (maxlen, source_length(n) - _start);
1629 len = min (len, maxlen);
1635 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1637 if (source() && (source()->destructive() || source()->length_mutable())) {
1641 framecnt_t maxlen = 0;
1643 for (uint32_t n = 0; n < _sources.size(); ++n) {
1644 maxlen = max (maxlen, source_length(n) - new_start);
1647 new_length = min (new_length, maxlen);
1653 Region::verify_start (framepos_t pos)
1655 if (source() && (source()->destructive() || source()->length_mutable())) {
1659 for (uint32_t n = 0; n < _sources.size(); ++n) {
1660 if (pos > source_length(n) - _length) {
1668 Region::verify_start_mutable (framepos_t& new_start)
1670 if (source() && (source()->destructive() || source()->length_mutable())) {
1674 for (uint32_t n = 0; n < _sources.size(); ++n) {
1675 if (new_start > source_length(n) - _length) {
1676 new_start = source_length(n) - _length;
1682 boost::shared_ptr<Region>
1683 Region::get_parent() const
1685 boost::shared_ptr<Playlist> pl (playlist());
1688 boost::shared_ptr<Region> r;
1689 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1691 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1692 return boost::static_pointer_cast<Region> (r);
1696 return boost::shared_ptr<Region>();
1700 Region::apply (Filter& filter, Progress* progress)
1702 return filter.run (shared_from_this(), progress);
1707 Region::maybe_invalidate_transients ()
1709 bool changed = !_onsets.empty();
1712 if (_valid_transients || changed) {
1713 send_change (PropertyChange (Properties::valid_transients));
1719 Region::transients (AnalysisFeatureList& afl)
1721 int cnt = afl.empty() ? 0 : 1;
1723 Region::merge_features (afl, _onsets, _position);
1724 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1725 if (!_onsets.empty ()) {
1728 if (!_user_transients.empty ()) {
1733 // remove exact duplicates
1734 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1739 Region::has_transients () const
1741 if (!_user_transients.empty ()) {
1742 assert (_valid_transients);
1745 if (!_onsets.empty ()) {
1752 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1754 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1755 const frameoffset_t p = (*x) + off;
1756 if (p < first_frame() || p > last_frame()) {
1759 result.push_back (p);
1764 Region::drop_sources ()
1766 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1767 (*i)->dec_use_count ();
1772 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1773 (*i)->dec_use_count ();
1776 _master_sources.clear ();
1780 Region::use_sources (SourceList const & s)
1782 set<boost::shared_ptr<Source> > unique_srcs;
1784 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1786 _sources.push_back (*i);
1787 (*i)->inc_use_count ();
1788 _master_sources.push_back (*i);
1789 (*i)->inc_use_count ();
1791 /* connect only once to DropReferences, even if sources are replicated
1794 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1795 unique_srcs.insert (*i);
1796 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1802 Region::can_trim () const
1804 CanTrim ct = CanTrim (0);
1810 /* if not locked, we can always move the front later, and the end earlier
1813 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1815 if (start() != 0 || can_trim_start_before_source_start ()) {
1816 ct = CanTrim (ct | FrontTrimEarlier);
1819 if (!_sources.empty()) {
1820 if ((start() + length()) < _sources.front()->length (0)) {
1821 ct = CanTrim (ct | EndTrimLater);
1829 Region::max_source_level () const
1833 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1834 lvl = max (lvl, (*i)->level());
1841 Region::is_compound () const
1843 return max_source_level() > 0;
1847 Region::post_set (const PropertyChange& pc)
1849 if (pc.contains (Properties::position)) {
1850 recompute_position_from_lock_style (0);
1855 Region::set_start_internal (framecnt_t s, const int32_t& sub_num)
1861 Region::earliest_possible_position () const
1863 if (_start > _position) {
1866 return _position - _start;
1871 Region::latest_possible_frame () const
1873 framecnt_t minlen = max_framecnt;
1875 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1876 /* non-audio regions have a length that may vary based on their
1877 * position, so we have to pass it in the call.
1879 minlen = min (minlen, (*i)->length (_position));
1882 /* the latest possible last frame is determined by the current
1883 * position, plus the shortest source extent past _start.
1886 return _position + (minlen - _start) - 1;