2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<double> beat;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75 PBD::PropertyDescriptor<uint64_t> layering_index;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
92 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
94 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
96 Properties::import.property_id = g_quark_from_static_string (X_("import"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
98 Properties::external.property_id = g_quark_from_static_string (X_("external"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
100 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
102 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
104 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
106 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
108 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
110 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
112 Properties::start.property_id = g_quark_from_static_string (X_("start"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
114 Properties::length.property_id = g_quark_from_static_string (X_("length"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
116 Properties::position.property_id = g_quark_from_static_string (X_("position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
118 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
139 Region::register_properties ()
141 _xml_node_name = X_("Region");
143 add_property (_muted);
144 add_property (_opaque);
145 add_property (_locked);
146 add_property (_video_locked);
147 add_property (_automatic);
148 add_property (_whole_file);
149 add_property (_import);
150 add_property (_external);
151 add_property (_sync_marked);
152 add_property (_left_of_split);
153 add_property (_right_of_split);
154 add_property (_hidden);
155 add_property (_position_locked);
156 add_property (_valid_transients);
157 add_property (_start);
158 add_property (_length);
159 add_property (_position);
160 add_property (_beat);
161 add_property (_sync_position);
162 add_property (_ancestral_start);
163 add_property (_ancestral_length);
164 add_property (_stretch);
165 add_property (_shift);
166 add_property (_position_lock_style);
167 add_property (_layering_index);
170 #define REGION_DEFAULT_STATE(s,l) \
171 _sync_marked (Properties::sync_marked, false) \
172 , _left_of_split (Properties::left_of_split, false) \
173 , _right_of_split (Properties::right_of_split, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _beat (Properties::beat, 0.0) \
179 , _sync_position (Properties::sync_position, (s)) \
181 , _transient_user_start (0) \
182 , _transient_analysis_start (0) \
183 , _transient_analysis_end (0) \
184 , _muted (Properties::muted, false) \
185 , _opaque (Properties::opaque, true) \
186 , _locked (Properties::locked, false) \
187 , _video_locked (Properties::video_locked, false) \
188 , _automatic (Properties::automatic, false) \
189 , _whole_file (Properties::whole_file, false) \
190 , _import (Properties::import, false) \
191 , _external (Properties::external, false) \
192 , _hidden (Properties::hidden, false) \
193 , _position_locked (Properties::position_locked, false) \
194 , _ancestral_start (Properties::ancestral_start, (s)) \
195 , _ancestral_length (Properties::ancestral_length, (l)) \
196 , _stretch (Properties::stretch, 1.0) \
197 , _shift (Properties::shift, 1.0) \
198 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
199 , _layering_index (Properties::layering_index, 0)
201 #define REGION_COPY_STATE(other) \
202 _sync_marked (Properties::sync_marked, other->_sync_marked) \
203 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
204 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
205 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
206 , _start(Properties::start, other->_start) \
207 , _length(Properties::length, other->_length) \
208 , _position(Properties::position, other->_position) \
209 , _beat (Properties::beat, other->_beat) \
210 , _sync_position(Properties::sync_position, other->_sync_position) \
211 , _pulse (other->_pulse) \
212 , _user_transients (other->_user_transients) \
213 , _transient_user_start (other->_transient_user_start) \
214 , _transients (other->_transients) \
215 , _transient_analysis_start (other->_transient_analysis_start) \
216 , _transient_analysis_end (other->_transient_analysis_end) \
217 , _muted (Properties::muted, other->_muted) \
218 , _opaque (Properties::opaque, other->_opaque) \
219 , _locked (Properties::locked, other->_locked) \
220 , _video_locked (Properties::video_locked, other->_video_locked) \
221 , _automatic (Properties::automatic, other->_automatic) \
222 , _whole_file (Properties::whole_file, other->_whole_file) \
223 , _import (Properties::import, other->_import) \
224 , _external (Properties::external, other->_external) \
225 , _hidden (Properties::hidden, other->_hidden) \
226 , _position_locked (Properties::position_locked, other->_position_locked) \
227 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
228 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
229 , _stretch (Properties::stretch, other->_stretch) \
230 , _shift (Properties::shift, other->_shift) \
231 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
232 , _layering_index (Properties::layering_index, other->_layering_index)
234 /* derived-from-derived constructor (no sources in constructor) */
235 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
236 : SessionObject(s, name)
238 , REGION_DEFAULT_STATE(start,length)
239 , _last_length (length)
241 , _first_edit (EditChangesNothing)
244 register_properties ();
246 /* no sources at this point */
249 /** Basic Region constructor (many sources) */
250 Region::Region (const SourceList& srcs)
251 : SessionObject(srcs.front()->session(), "toBeRenamed")
252 , _type (srcs.front()->type())
253 , REGION_DEFAULT_STATE(0,0)
256 , _first_edit (EditChangesNothing)
259 register_properties ();
261 _type = srcs.front()->type();
265 assert(_sources.size() > 0);
266 assert (_type == srcs.front()->type());
269 /** Create a new Region from an existing one */
270 Region::Region (boost::shared_ptr<const Region> other)
271 : SessionObject(other->session(), other->name())
272 , _type (other->data_type())
273 , REGION_COPY_STATE (other)
274 , _last_length (other->_last_length)
275 , _last_position(other->_last_position) \
276 , _first_edit (EditChangesNothing)
277 , _layer (other->_layer)
279 register_properties ();
281 /* override state that may have been incorrectly inherited from the other region
284 _position = other->_position;
289 use_sources (other->_sources);
290 set_master_sources (other->_master_sources);
292 _position_lock_style = other->_position_lock_style;
293 _first_edit = other->_first_edit;
295 _start = other->_start;
296 _beat = other->_beat;
297 _pulse = other->_pulse;
299 /* sync pos is relative to start of file. our start-in-file is now zero,
300 so set our sync position to whatever the the difference between
301 _start and _sync_pos was in the other region.
303 result is that our new sync pos points to the same point in our source(s)
304 as the sync in the other region did in its source(s).
306 since we start at zero in our source(s), it is not possible to use a sync point that
307 is before the start. reset it to _start if that was true in the other region.
310 if (other->sync_marked()) {
311 if (other->_start < other->_sync_position) {
312 /* sync pos was after the start point of the other region */
313 _sync_position = other->_sync_position - other->_start;
315 /* sync pos was before the start point of the other region. not possible here. */
316 _sync_marked = false;
317 _sync_position = _start;
320 _sync_marked = false;
321 _sync_position = _start;
324 assert (_type == other->data_type());
327 /** Create a new Region from part of an existing one.
329 the start within \a other is given by \a offset
330 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
332 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
333 : SessionObject(other->session(), other->name())
334 , _type (other->data_type())
335 , REGION_COPY_STATE (other)
336 , _last_length (other->_last_length)
337 , _last_position(other->_last_position) \
338 , _first_edit (EditChangesNothing)
339 , _layer (other->_layer)
341 register_properties ();
343 /* override state that may have been incorrectly inherited from the other region
346 _position = other->_position + offset;
351 use_sources (other->_sources);
352 set_master_sources (other->_master_sources);
354 _start = other->_start + offset;
355 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
356 _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
358 /* if the other region had a distinct sync point
359 set, then continue to use it as best we can.
360 otherwise, reset sync point back to start.
363 if (other->sync_marked()) {
364 if (other->_sync_position < _start) {
365 _sync_marked = false;
366 _sync_position = _start;
368 _sync_position = other->_sync_position;
371 _sync_marked = false;
372 _sync_position = _start;
375 assert (_type == other->data_type());
378 /** Create a copy of @param other but with different sources. Used by filters */
379 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
380 : SessionObject (other->session(), other->name())
381 , _type (srcs.front()->type())
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _first_edit (EditChangesID)
386 , _layer (other->_layer)
388 register_properties ();
391 _position_locked = false;
393 other->_first_edit = EditChangesName;
395 if (other->_extra_xml) {
396 _extra_xml = new XMLNode (*other->_extra_xml);
402 assert(_sources.size() > 0);
407 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
412 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
414 _playlist = wpl.lock();
418 Region::set_name (const std::string& str)
421 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
422 assert(_name == str);
424 send_change (Properties::name);
431 Region::set_length (framecnt_t len, const int32_t sub_num)
433 //cerr << "Region::set_length() len = " << len << endl;
438 if (_length != len && len != 0) {
440 /* check that the current _position wouldn't make the new
444 if (max_framepos - len < _position) {
448 if (!verify_length (len)) {
453 set_length_internal (len, sub_num);
457 maybe_invalidate_transients ();
459 if (!property_changes_suspended()) {
463 send_change (Properties::length);
468 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
470 _last_length = _length;
475 Region::maybe_uncopy ()
477 /* this does nothing but marked a semantic moment once upon a time */
481 Region::first_edit ()
483 boost::shared_ptr<Playlist> pl (playlist());
485 if (_first_edit != EditChangesNothing && pl) {
487 _name = RegionFactory::new_region_name (_name);
488 _first_edit = EditChangesNothing;
490 send_change (Properties::name);
492 RegionFactory::CheckNewRegion (shared_from_this());
497 Region::at_natural_position () const
499 boost::shared_ptr<Playlist> pl (playlist());
505 boost::shared_ptr<Region> whole_file_region = get_parent();
507 if (whole_file_region) {
508 if (_position == whole_file_region->position() + _start) {
517 Region::move_to_natural_position ()
519 boost::shared_ptr<Playlist> pl (playlist());
525 boost::shared_ptr<Region> whole_file_region = get_parent();
527 if (whole_file_region) {
528 set_position (whole_file_region->position() + _start);
533 Region::special_set_position (framepos_t pos)
535 /* this is used when creating a whole file region as
536 a way to store its "natural" or "captured" position.
539 _position = _position;
544 Region::set_position_lock_style (PositionLockStyle ps)
546 if (_position_lock_style != ps) {
548 boost::shared_ptr<Playlist> pl (playlist());
550 _position_lock_style = ps;
552 if (_position_lock_style == MusicTime) {
553 _beat = _session.tempo_map().beat_at_frame (_position);
554 _pulse = _session.tempo_map().pulse_at_frame (_position);
557 send_change (Properties::position_lock_style);
562 Region::update_after_tempo_map_change (bool send)
564 boost::shared_ptr<Playlist> pl (playlist());
570 if (_position_lock_style == AudioTime) {
571 /* don't signal as the actual position has not chnged */
572 recompute_position_from_lock_style (0);
576 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
577 /* we have _beat. update frame position non-musically */
578 set_position_internal (pos, false, 0);
580 /* do this even if the position is the same. this helps out
581 a GUI that has moved its representation already.
585 send_change (Properties::position);
590 Region::set_position (framepos_t pos, int32_t sub_num)
597 set_position_internal (pos, true, 0);
599 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
600 /* will set pulse accordingly */
601 set_position_internal (pos, false, sub_num);
604 /* do this even if the position is the same. this helps out
605 a GUI that has moved its representation already.
607 PropertyChange p_and_l;
609 p_and_l.add (Properties::position);
610 /* Currently length change due to position change is only implemented
611 for MidiRegion (Region has no length in beats).
612 Notify a length change regardless (its more efficient for MidiRegions),
613 and when Region has a _length_beats we will need it here anyway).
615 p_and_l.add (Properties::length);
617 send_change (p_and_l);
621 /** A gui may need to create a region, then place it in an initial
622 * position determined by the user.
623 * When this takes place within one gui operation, we have to reset
624 * _last_position to prevent an implied move.
627 Region::set_initial_position (framepos_t pos)
633 if (_position != pos) {
636 /* check that the new _position wouldn't make the current
637 length impossible - if so, change the length.
639 XXX is this the right thing to do?
642 if (max_framepos - _length < _position) {
643 _last_length = _length;
644 _length = max_framepos - _position;
647 recompute_position_from_lock_style (0);
648 /* ensure that this move doesn't cause a range move */
649 _last_position = _position;
653 /* do this even if the position is the same. this helps out
654 a GUI that has moved its representation already.
656 send_change (Properties::position);
660 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
662 /* We emit a change of Properties::position even if the position hasn't changed
663 (see Region::set_position), so we must always set this up so that
664 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
666 _last_position = _position;
668 if (_position != pos) {
671 if (allow_bbt_recompute) {
672 recompute_position_from_lock_style (sub_num);
674 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
675 _pulse = _session.tempo_map().pulse_at_beat (_beat);
678 /* check that the new _position wouldn't make the current
679 length impossible - if so, change the length.
681 XXX is this the right thing to do?
683 if (max_framepos - _length < _position) {
684 _last_length = _length;
685 _length = max_framepos - _position;
691 Region::recompute_position_from_lock_style (const int32_t sub_num)
693 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
694 _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
698 Region::nudge_position (frameoffset_t n)
700 if (locked() || video_locked()) {
708 framepos_t new_position = _position;
711 if (_position > max_framepos - n) {
712 new_position = max_framepos;
717 if (_position < -n) {
723 /* assumes non-musical nudge */
724 set_position_internal (new_position, true, 0);
726 send_change (Properties::position);
730 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
732 _ancestral_length = l;
733 _ancestral_start = s;
739 Region::set_start (framepos_t pos)
741 if (locked() || position_locked() || video_locked()) {
744 /* This just sets the start, nothing else. It effectively shifts
745 the contents of the Region within the overall extent of the Source,
746 without changing the Region's position or length
751 if (!verify_start (pos)) {
755 set_start_internal (pos);
758 maybe_invalidate_transients ();
760 send_change (Properties::start);
765 Region::move_start (frameoffset_t distance, const int32_t sub_num)
767 if (locked() || position_locked() || video_locked()) {
771 framepos_t new_start;
775 if (_start > max_framepos - distance) {
776 new_start = max_framepos; // makes no sense
778 new_start = _start + distance;
781 if (!verify_start (new_start)) {
785 } else if (distance < 0) {
787 if (_start < -distance) {
790 new_start = _start + distance;
797 if (new_start == _start) {
801 set_start_internal (new_start, sub_num);
806 send_change (Properties::start);
810 Region::trim_front (framepos_t new_position, const int32_t sub_num)
812 modify_front (new_position, false, sub_num);
816 Region::cut_front (framepos_t new_position, const int32_t sub_num)
818 modify_front (new_position, true, sub_num);
822 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
824 modify_end (new_endpoint, true, sub_num);
828 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
834 framepos_t end = last_frame();
835 framepos_t source_zero;
837 if (_position > _start) {
838 source_zero = _position - _start;
840 source_zero = 0; // its actually negative, but this will work for us
843 if (new_position < end) { /* can't trim it zero or negative length */
845 framecnt_t newlen = 0;
847 if (!can_trim_start_before_source_start ()) {
848 /* can't trim it back past where source position zero is located */
849 new_position = max (new_position, source_zero);
852 if (new_position > _position) {
853 newlen = _length - (new_position - _position);
855 newlen = _length + (_position - new_position);
858 trim_to_internal (new_position, newlen, sub_num);
861 _right_of_split = true;
864 if (!property_changes_suspended()) {
865 recompute_at_start ();
868 maybe_invalidate_transients ();
873 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
879 if (new_endpoint > _position) {
880 trim_to_internal (_position, new_endpoint - _position, sub_num);
882 _left_of_split = true;
884 if (!property_changes_suspended()) {
890 /** @param new_endpoint New region end point, such that, for example,
891 * a region at 0 of length 10 has an endpoint of 9.
895 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
897 modify_end (new_endpoint, false, sub_num);
901 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
907 trim_to_internal (position, length, sub_num);
909 if (!property_changes_suspended()) {
910 recompute_at_start ();
916 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
918 framepos_t new_start;
924 frameoffset_t const start_shift = position - _position;
926 if (start_shift > 0) {
928 if (_start > max_framepos - start_shift) {
929 new_start = max_framepos;
931 new_start = _start + start_shift;
934 } else if (start_shift < 0) {
936 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
939 new_start = _start + start_shift;
946 if (!verify_start_and_length (new_start, length)) {
950 PropertyChange what_changed;
952 if (_start != new_start) {
953 set_start_internal (new_start, sub_num);
954 what_changed.add (Properties::start);
958 /* Set position before length, otherwise for MIDI regions this bad thing happens:
959 * 1. we call set_length_internal; length in beats is computed using the region's current
960 * (soon-to-be old) position
961 * 2. we call set_position_internal; position is set and length in frames re-computed using
962 * length in beats from (1) but at the new position, which is wrong if the region
963 * straddles a tempo/meter change.
966 if (_position != position) {
967 if (!property_changes_suspended()) {
968 _last_position = _position;
970 set_position_internal (position, true, sub_num);
971 what_changed.add (Properties::position);
974 if (_length != length) {
975 if (!property_changes_suspended()) {
976 _last_length = _length;
978 set_length_internal (length, sub_num);
979 what_changed.add (Properties::length);
984 PropertyChange start_and_length;
986 start_and_length.add (Properties::start);
987 start_and_length.add (Properties::length);
989 if (what_changed.contains (start_and_length)) {
993 if (!what_changed.empty()) {
994 send_change (what_changed);
999 Region::set_hidden (bool yn)
1001 if (hidden() != yn) {
1003 send_change (Properties::hidden);
1008 Region::set_whole_file (bool yn)
1011 /* no change signal */
1015 Region::set_automatic (bool yn)
1018 /* no change signal */
1022 Region::set_muted (bool yn)
1024 if (muted() != yn) {
1026 send_change (Properties::muted);
1031 Region::set_opaque (bool yn)
1033 if (opaque() != yn) {
1035 send_change (Properties::opaque);
1040 Region::set_locked (bool yn)
1042 if (locked() != yn) {
1044 send_change (Properties::locked);
1049 Region::set_video_locked (bool yn)
1051 if (video_locked() != yn) {
1053 send_change (Properties::video_locked);
1058 Region::set_position_locked (bool yn)
1060 if (position_locked() != yn) {
1061 _position_locked = yn;
1062 send_change (Properties::locked);
1066 /** Set the region's sync point.
1067 * @param absolute_pos Session time.
1070 Region::set_sync_position (framepos_t absolute_pos)
1072 /* position within our file */
1073 framepos_t const file_pos = _start + (absolute_pos - _position);
1075 if (file_pos != _sync_position) {
1076 _sync_marked = true;
1077 _sync_position = file_pos;
1078 if (!property_changes_suspended()) {
1082 send_change (Properties::sync_position);
1087 Region::clear_sync_position ()
1089 if (sync_marked()) {
1090 _sync_marked = false;
1091 if (!property_changes_suspended()) {
1095 send_change (Properties::sync_position);
1099 /* @return the sync point relative the first frame of the region */
1101 Region::sync_offset (int& dir) const
1103 if (sync_marked()) {
1104 if (_sync_position > _start) {
1106 return _sync_position - _start;
1109 return _start - _sync_position;
1118 Region::adjust_to_sync (framepos_t pos) const
1121 frameoffset_t offset = sync_offset (sync_dir);
1123 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1132 if (max_framepos - pos > offset) {
1140 /** @return Sync position in session time */
1142 Region::sync_position() const
1144 if (sync_marked()) {
1145 return _position - _start + _sync_position;
1147 /* if sync has not been marked, use the start of the region */
1155 boost::shared_ptr<Playlist> pl (playlist());
1157 pl->raise_region (shared_from_this ());
1164 boost::shared_ptr<Playlist> pl (playlist());
1166 pl->lower_region (shared_from_this ());
1172 Region::raise_to_top ()
1174 boost::shared_ptr<Playlist> pl (playlist());
1176 pl->raise_region_to_top (shared_from_this());
1181 Region::lower_to_bottom ()
1183 boost::shared_ptr<Playlist> pl (playlist());
1185 pl->lower_region_to_bottom (shared_from_this());
1190 Region::set_layer (layer_t l)
1198 XMLNode *node = new XMLNode ("Region");
1202 const char* fe = NULL;
1204 /* custom version of 'add_properties (*node);'
1205 * skip values that have have dedicated save functions
1206 * in AudioRegion::state()
1208 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1209 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1210 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1211 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1212 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1213 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1214 i->second->get_value (*node);
1217 id().print (buf, sizeof (buf));
1218 node->add_property ("id", buf);
1219 node->add_property ("type", _type.to_string());
1221 switch (_first_edit) {
1222 case EditChangesNothing:
1225 case EditChangesName:
1231 default: /* should be unreachable but makes g++ happy */
1236 node->add_property ("first-edit", fe);
1238 /* note: flags are stored by derived classes */
1240 for (uint32_t n=0; n < _sources.size(); ++n) {
1241 snprintf (buf2, sizeof(buf2), "source-%d", n);
1242 _sources[n]->id().print (buf, sizeof(buf));
1243 node->add_property (buf2, buf);
1246 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1247 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1248 _master_sources[n]->id().print (buf, sizeof (buf));
1249 node->add_property (buf2, buf);
1252 /* Only store nested sources for the whole-file region that acts
1253 as the parent/root of all regions using it.
1256 if (_whole_file && max_source_level() > 0) {
1258 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1260 /* region is compound - get its playlist and
1261 store that before we list the region that
1265 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1266 nested_node->add_child_nocopy ((*s)->get_state ());
1270 node->add_child_nocopy (*nested_node);
1275 node->add_child_copy (*_extra_xml);
1282 Region::get_state ()
1288 Region::set_state (const XMLNode& node, int version)
1290 PropertyChange what_changed;
1291 return _set_state (node, version, what_changed, true);
1295 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1297 XMLProperty const * prop;
1298 Timecode::BBT_Time bbt_time;
1300 Stateful::save_extra_xml (node);
1302 what_changed = set_values (node);
1306 if (_position_lock_style == MusicTime) {
1307 if ((prop = node.property ("bbt-position")) != 0) {
1308 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1311 &bbt_time.ticks) != 3) {
1312 _position_lock_style = AudioTime;
1313 _beat = _session.tempo_map().beat_at_frame (_position);
1315 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1320 _pulse = _session.tempo_map().pulse_at_beat (_beat);
1322 /* fix problems with old sessions corrupted by impossible
1323 values for _stretch or _shift
1325 if (_stretch == 0.0f) {
1329 if (_shift == 0.0f) {
1334 send_change (what_changed);
1337 /* Quick fix for 2.x sessions when region is muted */
1338 if ((prop = node.property (X_("flags")))) {
1339 if (string::npos != prop->value().find("Muted")){
1344 // saved property is invalid, region-transients are not saved
1345 if (_user_transients.size() == 0){
1346 _valid_transients = false;
1353 Region::suspend_property_changes ()
1355 Stateful::suspend_property_changes ();
1356 _last_length = _length;
1357 _last_position = _position;
1361 Region::mid_thaw (const PropertyChange& what_changed)
1363 if (what_changed.contains (Properties::length)) {
1364 if (what_changed.contains (Properties::position)) {
1365 recompute_at_start ();
1367 recompute_at_end ();
1372 Region::send_change (const PropertyChange& what_changed)
1374 if (what_changed.empty()) {
1378 Stateful::send_change (what_changed);
1380 if (!Stateful::property_changes_suspended()) {
1382 /* Try and send a shared_pointer unless this is part of the constructor.
1387 boost::shared_ptr<Region> rptr = shared_from_this();
1388 RegionPropertyChanged (rptr, what_changed);
1390 /* no shared_ptr available, relax; */
1396 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1398 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1402 Region::equivalent (boost::shared_ptr<const Region> other) const
1404 return _start == other->_start &&
1405 _position == other->_position &&
1406 _length == other->_length;
1410 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1412 return _start == other->_start &&
1413 _length == other->_length;
1417 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1419 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1423 Region::source_deleted (boost::weak_ptr<Source>)
1427 if (!_session.deletion_in_progress()) {
1428 /* this is a very special case: at least one of the region's
1429 sources has bee deleted, so invalidate all references to
1430 ourselves. Do NOT do this during session deletion, because
1431 then we run the risk that this will actually result
1432 in this object being deleted (as refcnt goes to zero)
1433 while emitting DropReferences.
1441 Region::master_source_names ()
1443 SourceList::iterator i;
1445 vector<string> names;
1446 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1447 names.push_back((*i)->name());
1454 Region::set_master_sources (const SourceList& srcs)
1456 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1457 (*i)->dec_use_count ();
1460 _master_sources = srcs;
1461 assert (_sources.size() == _master_sources.size());
1463 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1464 (*i)->inc_use_count ();
1469 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1474 if ((_sources.size() != other->_sources.size()) ||
1475 (_master_sources.size() != other->_master_sources.size())) {
1479 SourceList::const_iterator i;
1480 SourceList::const_iterator io;
1482 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1483 if ((*i)->id() != (*io)->id()) {
1488 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1489 if ((*i)->id() != (*io)->id()) {
1498 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1504 SourceList::const_iterator i;
1505 SourceList::const_iterator io;
1507 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1508 if ((*i)->id() == (*io)->id()) {
1517 Region::source_string () const
1519 //string res = itos(_sources.size());
1522 res << _sources.size() << ":";
1524 SourceList::const_iterator i;
1526 for (i = _sources.begin(); i != _sources.end(); ++i) {
1527 res << (*i)->id() << ":";
1530 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1531 res << (*i)->id() << ":";
1538 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1540 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1542 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1545 if (sources.find (ps) == sources.end()) {
1546 /* (Playlist)Source not currently in
1547 accumulating set, so recurse.
1549 ps->playlist()->deep_sources (sources);
1553 /* add this source */
1554 sources.insert (*i);
1557 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1559 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1562 if (sources.find (ps) == sources.end()) {
1563 /* (Playlist)Source not currently in
1564 accumulating set, so recurse.
1566 ps->playlist()->deep_sources (sources);
1570 /* add this source */
1571 sources.insert (*i);
1576 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1578 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1584 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1587 if (ps->playlist()->uses_source (source)) {
1594 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1600 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1603 if (ps->playlist()->uses_source (source)) {
1615 Region::source_length(uint32_t n) const
1617 assert (n < _sources.size());
1618 return _sources[n]->length (_position - _start);
1622 Region::verify_length (framecnt_t& len)
1624 if (source() && (source()->destructive() || source()->length_mutable())) {
1628 framecnt_t maxlen = 0;
1630 for (uint32_t n = 0; n < _sources.size(); ++n) {
1631 maxlen = max (maxlen, source_length(n) - _start);
1634 len = min (len, maxlen);
1640 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1642 if (source() && (source()->destructive() || source()->length_mutable())) {
1646 framecnt_t maxlen = 0;
1648 for (uint32_t n = 0; n < _sources.size(); ++n) {
1649 maxlen = max (maxlen, source_length(n) - new_start);
1652 new_length = min (new_length, maxlen);
1658 Region::verify_start (framepos_t pos)
1660 if (source() && (source()->destructive() || source()->length_mutable())) {
1664 for (uint32_t n = 0; n < _sources.size(); ++n) {
1665 if (pos > source_length(n) - _length) {
1673 Region::verify_start_mutable (framepos_t& new_start)
1675 if (source() && (source()->destructive() || source()->length_mutable())) {
1679 for (uint32_t n = 0; n < _sources.size(); ++n) {
1680 if (new_start > source_length(n) - _length) {
1681 new_start = source_length(n) - _length;
1687 boost::shared_ptr<Region>
1688 Region::get_parent() const
1690 boost::shared_ptr<Playlist> pl (playlist());
1693 boost::shared_ptr<Region> r;
1694 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1696 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1697 return boost::static_pointer_cast<Region> (r);
1701 return boost::shared_ptr<Region>();
1705 Region::apply (Filter& filter, Progress* progress)
1707 return filter.run (shared_from_this(), progress);
1712 Region::maybe_invalidate_transients ()
1714 bool changed = !_onsets.empty();
1717 if (_valid_transients || changed) {
1718 send_change (PropertyChange (Properties::valid_transients));
1724 Region::transients (AnalysisFeatureList& afl)
1726 int cnt = afl.empty() ? 0 : 1;
1728 Region::merge_features (afl, _onsets, _position);
1729 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1730 if (!_onsets.empty ()) {
1733 if (!_user_transients.empty ()) {
1738 // remove exact duplicates
1739 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1744 Region::has_transients () const
1746 if (!_user_transients.empty ()) {
1747 assert (_valid_transients);
1750 if (!_onsets.empty ()) {
1757 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1759 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1760 const frameoffset_t p = (*x) + off;
1761 if (p < first_frame() || p > last_frame()) {
1764 result.push_back (p);
1769 Region::drop_sources ()
1771 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1772 (*i)->dec_use_count ();
1777 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1778 (*i)->dec_use_count ();
1781 _master_sources.clear ();
1785 Region::use_sources (SourceList const & s)
1787 set<boost::shared_ptr<Source> > unique_srcs;
1789 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1791 _sources.push_back (*i);
1792 (*i)->inc_use_count ();
1793 _master_sources.push_back (*i);
1794 (*i)->inc_use_count ();
1796 /* connect only once to DropReferences, even if sources are replicated
1799 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1800 unique_srcs.insert (*i);
1801 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1807 Region::can_trim () const
1809 CanTrim ct = CanTrim (0);
1815 /* if not locked, we can always move the front later, and the end earlier
1818 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1820 if (start() != 0 || can_trim_start_before_source_start ()) {
1821 ct = CanTrim (ct | FrontTrimEarlier);
1824 if (!_sources.empty()) {
1825 if ((start() + length()) < _sources.front()->length (0)) {
1826 ct = CanTrim (ct | EndTrimLater);
1834 Region::max_source_level () const
1838 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1839 lvl = max (lvl, (*i)->level());
1846 Region::is_compound () const
1848 return max_source_level() > 0;
1852 Region::post_set (const PropertyChange& pc)
1854 if (pc.contains (Properties::position)) {
1855 recompute_position_from_lock_style (0);
1860 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1866 Region::earliest_possible_position () const
1868 if (_start > _position) {
1871 return _position - _start;
1876 Region::latest_possible_frame () const
1878 framecnt_t minlen = max_framecnt;
1880 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1881 /* non-audio regions have a length that may vary based on their
1882 * position, so we have to pass it in the call.
1884 minlen = min (minlen, (*i)->length (_position));
1887 /* the latest possible last frame is determined by the current
1888 * position, plus the shortest source extent past _start.
1891 return _position + (minlen - _start) - 1;