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"
40 #include "ardour/types_convert.h"
45 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> muted;
52 PBD::PropertyDescriptor<bool> opaque;
53 PBD::PropertyDescriptor<bool> locked;
54 PBD::PropertyDescriptor<bool> video_locked;
55 PBD::PropertyDescriptor<bool> automatic;
56 PBD::PropertyDescriptor<bool> whole_file;
57 PBD::PropertyDescriptor<bool> import;
58 PBD::PropertyDescriptor<bool> external;
59 PBD::PropertyDescriptor<bool> sync_marked;
60 PBD::PropertyDescriptor<bool> left_of_split;
61 PBD::PropertyDescriptor<bool> right_of_split;
62 PBD::PropertyDescriptor<bool> hidden;
63 PBD::PropertyDescriptor<bool> position_locked;
64 PBD::PropertyDescriptor<bool> valid_transients;
65 PBD::PropertyDescriptor<framepos_t> start;
66 PBD::PropertyDescriptor<framecnt_t> length;
67 PBD::PropertyDescriptor<framepos_t> position;
68 PBD::PropertyDescriptor<double> beat;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::PropertyDescriptor<uint64_t> layering_index;
80 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
83 Region::make_property_quarks ()
85 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
87 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
89 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
91 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
93 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
95 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
97 Properties::import.property_id = g_quark_from_static_string (X_("import"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
99 Properties::external.property_id = g_quark_from_static_string (X_("external"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
101 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
103 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
105 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
107 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
109 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
111 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
113 Properties::start.property_id = g_quark_from_static_string (X_("start"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
115 Properties::length.property_id = g_quark_from_static_string (X_("length"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
117 Properties::position.property_id = g_quark_from_static_string (X_("position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
119 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
121 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
123 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
125 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
127 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
129 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
131 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
133 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
134 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
135 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
136 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
140 Region::register_properties ()
142 _xml_node_name = X_("Region");
144 add_property (_muted);
145 add_property (_opaque);
146 add_property (_locked);
147 add_property (_video_locked);
148 add_property (_automatic);
149 add_property (_whole_file);
150 add_property (_import);
151 add_property (_external);
152 add_property (_sync_marked);
153 add_property (_left_of_split);
154 add_property (_right_of_split);
155 add_property (_hidden);
156 add_property (_position_locked);
157 add_property (_valid_transients);
158 add_property (_start);
159 add_property (_length);
160 add_property (_position);
161 add_property (_beat);
162 add_property (_sync_position);
163 add_property (_ancestral_start);
164 add_property (_ancestral_length);
165 add_property (_stretch);
166 add_property (_shift);
167 add_property (_position_lock_style);
168 add_property (_layering_index);
171 #define REGION_DEFAULT_STATE(s,l) \
172 _sync_marked (Properties::sync_marked, false) \
173 , _left_of_split (Properties::left_of_split, false) \
174 , _right_of_split (Properties::right_of_split, false) \
175 , _valid_transients (Properties::valid_transients, false) \
176 , _start (Properties::start, (s)) \
177 , _length (Properties::length, (l)) \
178 , _position (Properties::position, 0) \
179 , _beat (Properties::beat, 0.0) \
180 , _sync_position (Properties::sync_position, (s)) \
181 , _quarter_note (0.0) \
182 , _transient_user_start (0) \
183 , _transient_analysis_start (0) \
184 , _transient_analysis_end (0) \
185 , _muted (Properties::muted, false) \
186 , _opaque (Properties::opaque, true) \
187 , _locked (Properties::locked, false) \
188 , _video_locked (Properties::video_locked, false) \
189 , _automatic (Properties::automatic, false) \
190 , _whole_file (Properties::whole_file, false) \
191 , _import (Properties::import, false) \
192 , _external (Properties::external, false) \
193 , _hidden (Properties::hidden, false) \
194 , _position_locked (Properties::position_locked, false) \
195 , _ancestral_start (Properties::ancestral_start, (s)) \
196 , _ancestral_length (Properties::ancestral_length, (l)) \
197 , _stretch (Properties::stretch, 1.0) \
198 , _shift (Properties::shift, 1.0) \
199 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
200 , _layering_index (Properties::layering_index, 0)
202 #define REGION_COPY_STATE(other) \
203 _sync_marked (Properties::sync_marked, other->_sync_marked) \
204 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
205 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
206 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
207 , _start(Properties::start, other->_start) \
208 , _length(Properties::length, other->_length) \
209 , _position(Properties::position, other->_position) \
210 , _beat (Properties::beat, other->_beat) \
211 , _sync_position(Properties::sync_position, other->_sync_position) \
212 , _quarter_note (other->_quarter_note) \
213 , _user_transients (other->_user_transients) \
214 , _transient_user_start (other->_transient_user_start) \
215 , _transients (other->_transients) \
216 , _transient_analysis_start (other->_transient_analysis_start) \
217 , _transient_analysis_end (other->_transient_analysis_end) \
218 , _muted (Properties::muted, other->_muted) \
219 , _opaque (Properties::opaque, other->_opaque) \
220 , _locked (Properties::locked, other->_locked) \
221 , _video_locked (Properties::video_locked, other->_video_locked) \
222 , _automatic (Properties::automatic, other->_automatic) \
223 , _whole_file (Properties::whole_file, other->_whole_file) \
224 , _import (Properties::import, other->_import) \
225 , _external (Properties::external, other->_external) \
226 , _hidden (Properties::hidden, other->_hidden) \
227 , _position_locked (Properties::position_locked, other->_position_locked) \
228 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
229 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
230 , _stretch (Properties::stretch, other->_stretch) \
231 , _shift (Properties::shift, other->_shift) \
232 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
233 , _layering_index (Properties::layering_index, other->_layering_index)
235 /* derived-from-derived constructor (no sources in constructor) */
236 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
237 : SessionObject(s, name)
239 , REGION_DEFAULT_STATE(start,length)
240 , _last_length (length)
242 , _first_edit (EditChangesNothing)
245 register_properties ();
247 /* no sources at this point */
250 /** Basic Region constructor (many sources) */
251 Region::Region (const SourceList& srcs)
252 : SessionObject(srcs.front()->session(), "toBeRenamed")
253 , _type (srcs.front()->type())
254 , REGION_DEFAULT_STATE(0,0)
257 , _first_edit (EditChangesNothing)
260 register_properties ();
262 _type = srcs.front()->type();
266 assert(_sources.size() > 0);
267 assert (_type == srcs.front()->type());
270 /** Create a new Region from an existing one */
271 Region::Region (boost::shared_ptr<const Region> other)
272 : SessionObject(other->session(), other->name())
273 , _type (other->data_type())
274 , REGION_COPY_STATE (other)
275 , _last_length (other->_last_length)
276 , _last_position(other->_last_position) \
277 , _first_edit (EditChangesNothing)
278 , _layer (other->_layer)
280 register_properties ();
282 /* override state that may have been incorrectly inherited from the other region
285 _position = other->_position;
290 use_sources (other->_sources);
291 set_master_sources (other->_master_sources);
293 _position_lock_style = other->_position_lock_style;
294 _first_edit = other->_first_edit;
296 _start = other->_start;
297 _beat = other->_beat;
298 _quarter_note = other->_quarter_note;
300 /* sync pos is relative to start of file. our start-in-file is now zero,
301 so set our sync position to whatever the the difference between
302 _start and _sync_pos was in the other region.
304 result is that our new sync pos points to the same point in our source(s)
305 as the sync in the other region did in its source(s).
307 since we start at zero in our source(s), it is not possible to use a sync point that
308 is before the start. reset it to _start if that was true in the other region.
311 if (other->sync_marked()) {
312 if (other->_start < other->_sync_position) {
313 /* sync pos was after the start point of the other region */
314 _sync_position = other->_sync_position - other->_start;
316 /* sync pos was before the start point of the other region. not possible here. */
317 _sync_marked = false;
318 _sync_position = _start;
321 _sync_marked = false;
322 _sync_position = _start;
325 assert (_type == other->data_type());
328 /** Create a new Region from part of an existing one.
330 the start within \a other is given by \a offset
331 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
333 Region::Region (boost::shared_ptr<const Region> other, MusicFrame offset)
334 : SessionObject(other->session(), other->name())
335 , _type (other->data_type())
336 , REGION_COPY_STATE (other)
337 , _last_length (other->_last_length)
338 , _last_position(other->_last_position) \
339 , _first_edit (EditChangesNothing)
340 , _layer (other->_layer)
342 register_properties ();
344 /* override state that may have been incorrectly inherited from the other region
351 use_sources (other->_sources);
352 set_master_sources (other->_master_sources);
354 _position = other->_position + offset.frame;
355 _start = other->_start + offset.frame;
357 /* prevent offset of 0 from altering musical position */
358 if (offset.frame != 0) {
359 const double offset_qn = _session.tempo_map().exact_qn_at_frame (other->_position + offset.frame, offset.division)
360 - other->_quarter_note;
362 _quarter_note = other->_quarter_note + offset_qn;
363 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
365 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
368 /* if the other region had a distinct sync point
369 set, then continue to use it as best we can.
370 otherwise, reset sync point back to start.
373 if (other->sync_marked()) {
374 if (other->_sync_position < _start) {
375 _sync_marked = false;
376 _sync_position = _start;
378 _sync_position = other->_sync_position;
381 _sync_marked = false;
382 _sync_position = _start;
385 assert (_type == other->data_type());
388 /** Create a copy of @param other but with different sources. Used by filters */
389 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
390 : SessionObject (other->session(), other->name())
391 , _type (srcs.front()->type())
392 , REGION_COPY_STATE (other)
393 , _last_length (other->_last_length)
394 , _last_position (other->_last_position)
395 , _first_edit (EditChangesID)
396 , _layer (other->_layer)
398 register_properties ();
401 _position_locked = false;
403 other->_first_edit = EditChangesName;
405 if (other->_extra_xml) {
406 _extra_xml = new XMLNode (*other->_extra_xml);
412 assert(_sources.size() > 0);
417 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
422 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
424 _playlist = wpl.lock();
428 Region::set_name (const std::string& str)
431 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
432 assert(_name == str);
434 send_change (Properties::name);
441 Region::set_length (framecnt_t len, const int32_t sub_num)
443 //cerr << "Region::set_length() len = " << len << endl;
448 if (_length != len && len != 0) {
450 /* check that the current _position wouldn't make the new
454 if (max_framepos - len < _position) {
458 if (!verify_length (len)) {
463 set_length_internal (len, sub_num);
467 maybe_invalidate_transients ();
469 if (!property_changes_suspended()) {
473 send_change (Properties::length);
478 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
480 _last_length = _length;
485 Region::maybe_uncopy ()
487 /* this does nothing but marked a semantic moment once upon a time */
491 Region::first_edit ()
493 boost::shared_ptr<Playlist> pl (playlist());
495 if (_first_edit != EditChangesNothing && pl) {
497 _name = RegionFactory::new_region_name (_name);
498 _first_edit = EditChangesNothing;
500 send_change (Properties::name);
502 RegionFactory::CheckNewRegion (shared_from_this());
507 Region::at_natural_position () const
509 boost::shared_ptr<Playlist> pl (playlist());
515 boost::shared_ptr<Region> whole_file_region = get_parent();
517 if (whole_file_region) {
518 if (_position == whole_file_region->position() + _start) {
527 Region::move_to_natural_position ()
529 boost::shared_ptr<Playlist> pl (playlist());
535 boost::shared_ptr<Region> whole_file_region = get_parent();
537 if (whole_file_region) {
538 set_position (whole_file_region->position() + _start);
543 Region::special_set_position (framepos_t pos)
545 /* this is used when creating a whole file region as
546 a way to store its "natural" or "captured" position.
549 _position = _position;
554 Region::set_position_lock_style (PositionLockStyle ps)
556 if (_position_lock_style != ps) {
558 boost::shared_ptr<Playlist> pl (playlist());
560 _position_lock_style = ps;
562 send_change (Properties::position_lock_style);
567 Region::update_after_tempo_map_change (bool send)
569 boost::shared_ptr<Playlist> pl (playlist());
575 if (_position_lock_style == AudioTime) {
576 /* don't signal as the actual position has not chnged */
577 recompute_position_from_lock_style (0);
581 /* prevent movement before 0 */
582 const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
583 /* we have _beat. update frame position non-musically */
584 set_position_internal (pos, false, 0);
586 /* do this even if the position is the same. this helps out
587 a GUI that has moved its representation already.
591 send_change (Properties::position);
596 Region::set_position (framepos_t pos, int32_t sub_num)
602 /* do this even if the position is the same. this helps out
603 a GUI that has moved its representation already.
605 PropertyChange p_and_l;
607 p_and_l.add (Properties::position);
609 if (position_lock_style() == AudioTime) {
610 set_position_internal (pos, true, sub_num);
612 if (!_session.loading()) {
613 _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
614 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
617 set_position_internal (pos, false, sub_num);
620 if (position_lock_style() == MusicTime) {
621 p_and_l.add (Properties::length);
624 send_change (p_and_l);
629 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
631 /* We emit a change of Properties::position even if the position hasn't changed
632 (see Region::set_position), so we must always set this up so that
633 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
635 _last_position = _position;
637 if (_position != pos) {
640 if (allow_bbt_recompute) {
641 recompute_position_from_lock_style (sub_num);
643 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
644 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
647 /* check that the new _position wouldn't make the current
648 length impossible - if so, change the length.
650 XXX is this the right thing to do?
652 if (max_framepos - _length < _position) {
653 _last_length = _length;
654 _length = max_framepos - _position;
660 Region::set_position_music (double qn)
666 /* do this even if the position is the same. this helps out
667 a GUI that has moved its representation already.
669 PropertyChange p_and_l;
671 p_and_l.add (Properties::position);
673 if (!_session.loading()) {
674 _beat = _session.tempo_map().beat_at_quarter_note (qn);
677 /* will set frame accordingly */
678 set_position_music_internal (qn);
680 if (position_lock_style() == MusicTime) {
681 p_and_l.add (Properties::length);
684 send_change (p_and_l);
688 Region::set_position_music_internal (double qn)
690 /* We emit a change of Properties::position even if the position hasn't changed
691 (see Region::set_position), so we must always set this up so that
692 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
694 _last_position = _position;
696 if (_quarter_note != qn) {
697 _position = _session.tempo_map().frame_at_quarter_note (qn);
700 /* check that the new _position wouldn't make the current
701 length impossible - if so, change the length.
703 XXX is this the right thing to do?
705 if (max_framepos - _length < _position) {
706 _last_length = _length;
707 _length = max_framepos - _position;
712 /** A gui may need to create a region, then place it in an initial
713 * position determined by the user.
714 * When this takes place within one gui operation, we have to reset
715 * _last_position to prevent an implied move.
718 Region::set_initial_position (framepos_t pos)
724 if (_position != pos) {
727 /* check that the new _position wouldn't make the current
728 length impossible - if so, change the length.
730 XXX is this the right thing to do?
733 if (max_framepos - _length < _position) {
734 _last_length = _length;
735 _length = max_framepos - _position;
738 recompute_position_from_lock_style (0);
739 /* ensure that this move doesn't cause a range move */
740 _last_position = _position;
744 /* do this even if the position is the same. this helps out
745 a GUI that has moved its representation already.
747 send_change (Properties::position);
751 Region::recompute_position_from_lock_style (const int32_t sub_num)
753 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
754 _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
758 Region::nudge_position (frameoffset_t n)
760 if (locked() || video_locked()) {
768 framepos_t new_position = _position;
771 if (_position > max_framepos - n) {
772 new_position = max_framepos;
777 if (_position < -n) {
783 /* assumes non-musical nudge */
784 set_position_internal (new_position, true, 0);
786 send_change (Properties::position);
790 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
792 _ancestral_length = l;
793 _ancestral_start = s;
799 Region::set_start (framepos_t pos)
801 if (locked() || position_locked() || video_locked()) {
804 /* This just sets the start, nothing else. It effectively shifts
805 the contents of the Region within the overall extent of the Source,
806 without changing the Region's position or length
811 if (!verify_start (pos)) {
815 set_start_internal (pos);
818 maybe_invalidate_transients ();
820 send_change (Properties::start);
825 Region::move_start (frameoffset_t distance, const int32_t sub_num)
827 if (locked() || position_locked() || video_locked()) {
831 framepos_t new_start;
835 if (_start > max_framepos - distance) {
836 new_start = max_framepos; // makes no sense
838 new_start = _start + distance;
841 if (!verify_start (new_start)) {
845 } else if (distance < 0) {
847 if (_start < -distance) {
850 new_start = _start + distance;
857 if (new_start == _start) {
861 set_start_internal (new_start, sub_num);
866 send_change (Properties::start);
870 Region::trim_front (framepos_t new_position, const int32_t sub_num)
872 modify_front (new_position, false, sub_num);
876 Region::cut_front (framepos_t new_position, const int32_t sub_num)
878 modify_front (new_position, true, sub_num);
882 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
884 modify_end (new_endpoint, true, sub_num);
888 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
894 framepos_t end = last_frame();
895 framepos_t source_zero;
897 if (_position > _start) {
898 source_zero = _position - _start;
900 source_zero = 0; // its actually negative, but this will work for us
903 if (new_position < end) { /* can't trim it zero or negative length */
905 framecnt_t newlen = 0;
907 if (!can_trim_start_before_source_start ()) {
908 /* can't trim it back past where source position zero is located */
909 new_position = max (new_position, source_zero);
912 if (new_position > _position) {
913 newlen = _length - (new_position - _position);
915 newlen = _length + (_position - new_position);
918 trim_to_internal (new_position, newlen, sub_num);
921 _right_of_split = true;
924 if (!property_changes_suspended()) {
925 recompute_at_start ();
928 maybe_invalidate_transients ();
933 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
939 if (new_endpoint > _position) {
940 trim_to_internal (_position, new_endpoint - _position, sub_num);
942 _left_of_split = true;
944 if (!property_changes_suspended()) {
950 /** @param new_endpoint New region end point, such that, for example,
951 * a region at 0 of length 10 has an endpoint of 9.
955 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
957 modify_end (new_endpoint, false, sub_num);
961 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
967 trim_to_internal (position, length, sub_num);
969 if (!property_changes_suspended()) {
970 recompute_at_start ();
976 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
978 framepos_t new_start;
984 frameoffset_t const start_shift = position - _position;
986 if (start_shift > 0) {
988 if (_start > max_framepos - start_shift) {
989 new_start = max_framepos;
991 new_start = _start + start_shift;
994 } else if (start_shift < 0) {
996 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
999 new_start = _start + start_shift;
1006 if (!verify_start_and_length (new_start, length)) {
1010 PropertyChange what_changed;
1012 if (_start != new_start) {
1013 set_start_internal (new_start, sub_num);
1014 what_changed.add (Properties::start);
1018 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1019 * 1. we call set_length_internal; length in beats is computed using the region's current
1020 * (soon-to-be old) position
1021 * 2. we call set_position_internal; position is set and length in frames re-computed using
1022 * length in beats from (1) but at the new position, which is wrong if the region
1023 * straddles a tempo/meter change.
1026 if (_position != position) {
1027 if (!property_changes_suspended()) {
1028 _last_position = _position;
1030 set_position_internal (position, true, sub_num);
1031 what_changed.add (Properties::position);
1034 if (_length != length) {
1035 if (!property_changes_suspended()) {
1036 _last_length = _length;
1038 set_length_internal (length, sub_num);
1039 what_changed.add (Properties::length);
1042 _whole_file = false;
1044 PropertyChange start_and_length;
1046 start_and_length.add (Properties::start);
1047 start_and_length.add (Properties::length);
1049 if (what_changed.contains (start_and_length)) {
1053 if (!what_changed.empty()) {
1054 send_change (what_changed);
1059 Region::set_hidden (bool yn)
1061 if (hidden() != yn) {
1063 send_change (Properties::hidden);
1068 Region::set_whole_file (bool yn)
1071 /* no change signal */
1075 Region::set_automatic (bool yn)
1078 /* no change signal */
1082 Region::set_muted (bool yn)
1084 if (muted() != yn) {
1086 send_change (Properties::muted);
1091 Region::set_opaque (bool yn)
1093 if (opaque() != yn) {
1095 send_change (Properties::opaque);
1100 Region::set_locked (bool yn)
1102 if (locked() != yn) {
1104 send_change (Properties::locked);
1109 Region::set_video_locked (bool yn)
1111 if (video_locked() != yn) {
1113 send_change (Properties::video_locked);
1118 Region::set_position_locked (bool yn)
1120 if (position_locked() != yn) {
1121 _position_locked = yn;
1122 send_change (Properties::locked);
1126 /** Set the region's sync point.
1127 * @param absolute_pos Session time.
1130 Region::set_sync_position (framepos_t absolute_pos)
1132 /* position within our file */
1133 framepos_t const file_pos = _start + (absolute_pos - _position);
1135 if (file_pos != _sync_position) {
1136 _sync_marked = true;
1137 _sync_position = file_pos;
1138 if (!property_changes_suspended()) {
1142 send_change (Properties::sync_position);
1147 Region::clear_sync_position ()
1149 if (sync_marked()) {
1150 _sync_marked = false;
1151 if (!property_changes_suspended()) {
1155 send_change (Properties::sync_position);
1159 /* @return the sync point relative the first frame of the region */
1161 Region::sync_offset (int& dir) const
1163 if (sync_marked()) {
1164 if (_sync_position > _start) {
1166 return _sync_position - _start;
1169 return _start - _sync_position;
1178 Region::adjust_to_sync (framepos_t pos) const
1181 frameoffset_t offset = sync_offset (sync_dir);
1183 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1192 if (max_framepos - pos > offset) {
1200 /** @return Sync position in session time */
1202 Region::sync_position() const
1204 if (sync_marked()) {
1205 return _position - _start + _sync_position;
1207 /* if sync has not been marked, use the start of the region */
1215 boost::shared_ptr<Playlist> pl (playlist());
1217 pl->raise_region (shared_from_this ());
1224 boost::shared_ptr<Playlist> pl (playlist());
1226 pl->lower_region (shared_from_this ());
1232 Region::raise_to_top ()
1234 boost::shared_ptr<Playlist> pl (playlist());
1236 pl->raise_region_to_top (shared_from_this());
1241 Region::lower_to_bottom ()
1243 boost::shared_ptr<Playlist> pl (playlist());
1245 pl->lower_region_to_bottom (shared_from_this());
1250 Region::set_layer (layer_t l)
1258 XMLNode *node = new XMLNode ("Region");
1261 const char* fe = NULL;
1263 /* custom version of 'add_properties (*node);'
1264 * skip values that have have dedicated save functions
1265 * in AudioRegion::state()
1267 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1268 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1269 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1270 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1271 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1272 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1273 i->second->get_value (*node);
1276 node->add_property ("id", id ().to_s ());
1277 node->add_property ("type", _type.to_string());
1279 switch (_first_edit) {
1280 case EditChangesNothing:
1283 case EditChangesName:
1289 default: /* should be unreachable but makes g++ happy */
1294 node->add_property ("first-edit", fe);
1296 /* note: flags are stored by derived classes */
1298 for (uint32_t n=0; n < _sources.size(); ++n) {
1299 snprintf (buf2, sizeof(buf2), "source-%d", n);
1300 node->add_property (buf2, _sources[n]->id().to_s ());
1303 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1304 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1305 node->add_property (buf2, _master_sources[n]->id ().to_s ());
1308 /* Only store nested sources for the whole-file region that acts
1309 as the parent/root of all regions using it.
1312 if (_whole_file && max_source_level() > 0) {
1314 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1316 /* region is compound - get its playlist and
1317 store that before we list the region that
1321 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1322 nested_node->add_child_nocopy ((*s)->get_state ());
1326 node->add_child_nocopy (*nested_node);
1331 node->add_child_copy (*_extra_xml);
1338 Region::get_state ()
1344 Region::set_state (const XMLNode& node, int version)
1346 PropertyChange what_changed;
1347 return _set_state (node, version, what_changed, true);
1351 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1353 XMLProperty const * prop;
1354 Timecode::BBT_Time bbt_time;
1356 Stateful::save_extra_xml (node);
1358 what_changed = set_values (node);
1362 if (_position_lock_style == MusicTime) {
1363 if ((prop = node.property ("bbt-position")) != 0) {
1364 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1367 &bbt_time.ticks) != 3) {
1368 _position_lock_style = AudioTime;
1369 _beat = _session.tempo_map().beat_at_frame (_position);
1371 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1373 /* no position property change for legacy Property, so we do this here */
1374 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1378 /* fix problems with old sessions corrupted by impossible
1379 values for _stretch or _shift
1381 if (_stretch == 0.0f) {
1385 if (_shift == 0.0f) {
1390 send_change (what_changed);
1393 /* Quick fix for 2.x sessions when region is muted */
1394 if ((prop = node.property (X_("flags")))) {
1395 if (string::npos != prop->value().find("Muted")){
1400 // saved property is invalid, region-transients are not saved
1401 if (_user_transients.size() == 0){
1402 _valid_transients = false;
1409 Region::suspend_property_changes ()
1411 Stateful::suspend_property_changes ();
1412 _last_length = _length;
1413 _last_position = _position;
1417 Region::mid_thaw (const PropertyChange& what_changed)
1419 if (what_changed.contains (Properties::length)) {
1420 if (what_changed.contains (Properties::position)) {
1421 recompute_at_start ();
1423 recompute_at_end ();
1428 Region::send_change (const PropertyChange& what_changed)
1430 if (what_changed.empty()) {
1434 Stateful::send_change (what_changed);
1436 if (!Stateful::property_changes_suspended()) {
1438 /* Try and send a shared_pointer unless this is part of the constructor.
1443 boost::shared_ptr<Region> rptr = shared_from_this();
1444 RegionPropertyChanged (rptr, what_changed);
1446 /* no shared_ptr available, relax; */
1452 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1454 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1458 Region::equivalent (boost::shared_ptr<const Region> other) const
1460 return _start == other->_start &&
1461 _position == other->_position &&
1462 _length == other->_length;
1466 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1468 return _start == other->_start &&
1469 _length == other->_length;
1473 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1475 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1479 Region::source_deleted (boost::weak_ptr<Source>)
1483 if (!_session.deletion_in_progress()) {
1484 /* this is a very special case: at least one of the region's
1485 sources has bee deleted, so invalidate all references to
1486 ourselves. Do NOT do this during session deletion, because
1487 then we run the risk that this will actually result
1488 in this object being deleted (as refcnt goes to zero)
1489 while emitting DropReferences.
1497 Region::master_source_names ()
1499 SourceList::iterator i;
1501 vector<string> names;
1502 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1503 names.push_back((*i)->name());
1510 Region::set_master_sources (const SourceList& srcs)
1512 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1513 (*i)->dec_use_count ();
1516 _master_sources = srcs;
1517 assert (_sources.size() == _master_sources.size());
1519 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1520 (*i)->inc_use_count ();
1525 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1530 if ((_sources.size() != other->_sources.size()) ||
1531 (_master_sources.size() != other->_master_sources.size())) {
1535 SourceList::const_iterator i;
1536 SourceList::const_iterator io;
1538 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1539 if ((*i)->id() != (*io)->id()) {
1544 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1545 if ((*i)->id() != (*io)->id()) {
1554 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1560 SourceList::const_iterator i;
1561 SourceList::const_iterator io;
1563 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1564 if ((*i)->id() == (*io)->id()) {
1573 Region::source_string () const
1575 //string res = itos(_sources.size());
1578 res << _sources.size() << ":";
1580 SourceList::const_iterator i;
1582 for (i = _sources.begin(); i != _sources.end(); ++i) {
1583 res << (*i)->id() << ":";
1586 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1587 res << (*i)->id() << ":";
1594 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1596 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1598 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1601 if (sources.find (ps) == sources.end()) {
1602 /* (Playlist)Source not currently in
1603 accumulating set, so recurse.
1605 ps->playlist()->deep_sources (sources);
1609 /* add this source */
1610 sources.insert (*i);
1613 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1615 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1618 if (sources.find (ps) == sources.end()) {
1619 /* (Playlist)Source not currently in
1620 accumulating set, so recurse.
1622 ps->playlist()->deep_sources (sources);
1626 /* add this source */
1627 sources.insert (*i);
1632 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1634 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1640 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1643 if (ps->playlist()->uses_source (source)) {
1650 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1656 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1659 if (ps->playlist()->uses_source (source)) {
1671 Region::source_length(uint32_t n) const
1673 assert (n < _sources.size());
1674 return _sources[n]->length (_position - _start);
1678 Region::verify_length (framecnt_t& len)
1680 if (source() && (source()->destructive() || source()->length_mutable())) {
1684 framecnt_t maxlen = 0;
1686 for (uint32_t n = 0; n < _sources.size(); ++n) {
1687 maxlen = max (maxlen, source_length(n) - _start);
1690 len = min (len, maxlen);
1696 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1698 if (source() && (source()->destructive() || source()->length_mutable())) {
1702 framecnt_t maxlen = 0;
1704 for (uint32_t n = 0; n < _sources.size(); ++n) {
1705 maxlen = max (maxlen, source_length(n) - new_start);
1708 new_length = min (new_length, maxlen);
1714 Region::verify_start (framepos_t pos)
1716 if (source() && (source()->destructive() || source()->length_mutable())) {
1720 for (uint32_t n = 0; n < _sources.size(); ++n) {
1721 if (pos > source_length(n) - _length) {
1729 Region::verify_start_mutable (framepos_t& new_start)
1731 if (source() && (source()->destructive() || source()->length_mutable())) {
1735 for (uint32_t n = 0; n < _sources.size(); ++n) {
1736 if (new_start > source_length(n) - _length) {
1737 new_start = source_length(n) - _length;
1743 boost::shared_ptr<Region>
1744 Region::get_parent() const
1746 boost::shared_ptr<Playlist> pl (playlist());
1749 boost::shared_ptr<Region> r;
1750 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1752 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1753 return boost::static_pointer_cast<Region> (r);
1757 return boost::shared_ptr<Region>();
1761 Region::apply (Filter& filter, Progress* progress)
1763 return filter.run (shared_from_this(), progress);
1768 Region::maybe_invalidate_transients ()
1770 bool changed = !_onsets.empty();
1773 if (_valid_transients || changed) {
1774 send_change (PropertyChange (Properties::valid_transients));
1780 Region::transients (AnalysisFeatureList& afl)
1782 int cnt = afl.empty() ? 0 : 1;
1784 Region::merge_features (afl, _onsets, _position);
1785 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1786 if (!_onsets.empty ()) {
1789 if (!_user_transients.empty ()) {
1794 // remove exact duplicates
1795 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1800 Region::has_transients () const
1802 if (!_user_transients.empty ()) {
1803 assert (_valid_transients);
1806 if (!_onsets.empty ()) {
1813 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1815 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1816 const frameoffset_t p = (*x) + off;
1817 if (p < first_frame() || p > last_frame()) {
1820 result.push_back (p);
1825 Region::drop_sources ()
1827 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1828 (*i)->dec_use_count ();
1833 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1834 (*i)->dec_use_count ();
1837 _master_sources.clear ();
1841 Region::use_sources (SourceList const & s)
1843 set<boost::shared_ptr<Source> > unique_srcs;
1845 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1847 _sources.push_back (*i);
1848 (*i)->inc_use_count ();
1849 _master_sources.push_back (*i);
1850 (*i)->inc_use_count ();
1852 /* connect only once to DropReferences, even if sources are replicated
1855 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1856 unique_srcs.insert (*i);
1857 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1863 Region::can_trim () const
1865 CanTrim ct = CanTrim (0);
1871 /* if not locked, we can always move the front later, and the end earlier
1874 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1876 if (start() != 0 || can_trim_start_before_source_start ()) {
1877 ct = CanTrim (ct | FrontTrimEarlier);
1880 if (!_sources.empty()) {
1881 if ((start() + length()) < _sources.front()->length (0)) {
1882 ct = CanTrim (ct | EndTrimLater);
1890 Region::max_source_level () const
1894 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1895 lvl = max (lvl, (*i)->level());
1902 Region::is_compound () const
1904 return max_source_level() > 0;
1908 Region::post_set (const PropertyChange& pc)
1910 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1914 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1920 Region::earliest_possible_position () const
1922 if (_start > _position) {
1925 return _position - _start;
1930 Region::latest_possible_frame () const
1932 framecnt_t minlen = max_framecnt;
1934 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1935 /* non-audio regions have a length that may vary based on their
1936 * position, so we have to pass it in the call.
1938 minlen = min (minlen, (*i)->length (_position));
1941 /* the latest possible last frame is determined by the current
1942 * position, plus the shortest source extent past _start.
1945 return _position + (minlen - _start) - 1;