2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
44 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> video_locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
63 PBD::PropertyDescriptor<bool> valid_transients;
64 PBD::PropertyDescriptor<framepos_t> start;
65 PBD::PropertyDescriptor<framecnt_t> length;
66 PBD::PropertyDescriptor<framepos_t> position;
67 PBD::PropertyDescriptor<double> beat;
68 PBD::PropertyDescriptor<double> pulse;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::PropertyDescriptor<uint64_t> layering_index;
80 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
83 Region::make_property_quarks ()
85 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
87 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
89 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
91 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
93 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
95 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
97 Properties::import.property_id = g_quark_from_static_string (X_("import"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
99 Properties::external.property_id = g_quark_from_static_string (X_("external"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
101 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
103 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
105 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
107 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
109 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
111 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
113 Properties::start.property_id = g_quark_from_static_string (X_("start"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
115 Properties::length.property_id = g_quark_from_static_string (X_("length"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
117 Properties::position.property_id = g_quark_from_static_string (X_("position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
119 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
121 Properties::pulse.property_id = g_quark_from_static_string (X_("pulse"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for pulse = %1\n", Properties::pulse.property_id));
123 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
125 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
127 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
129 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
131 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
133 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
134 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
135 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
136 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
137 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
138 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
142 Region::register_properties ()
144 _xml_node_name = X_("Region");
146 add_property (_muted);
147 add_property (_opaque);
148 add_property (_locked);
149 add_property (_video_locked);
150 add_property (_automatic);
151 add_property (_whole_file);
152 add_property (_import);
153 add_property (_external);
154 add_property (_sync_marked);
155 add_property (_left_of_split);
156 add_property (_right_of_split);
157 add_property (_hidden);
158 add_property (_position_locked);
159 add_property (_valid_transients);
160 add_property (_start);
161 add_property (_length);
162 add_property (_position);
163 add_property (_beat);
164 add_property (_pulse);
165 add_property (_sync_position);
166 add_property (_ancestral_start);
167 add_property (_ancestral_length);
168 add_property (_stretch);
169 add_property (_shift);
170 add_property (_position_lock_style);
171 add_property (_layering_index);
174 #define REGION_DEFAULT_STATE(s,l) \
175 _sync_marked (Properties::sync_marked, false) \
176 , _left_of_split (Properties::left_of_split, false) \
177 , _right_of_split (Properties::right_of_split, false) \
178 , _valid_transients (Properties::valid_transients, false) \
179 , _start (Properties::start, (s)) \
180 , _length (Properties::length, (l)) \
181 , _position (Properties::position, 0) \
182 , _beat (Properties::beat, 0.0) \
183 , _pulse (Properties::pulse, 0.0) \
184 , _sync_position (Properties::sync_position, (s)) \
185 , _transient_user_start (0) \
186 , _transient_analysis_start (0) \
187 , _transient_analysis_end (0) \
188 , _muted (Properties::muted, false) \
189 , _opaque (Properties::opaque, true) \
190 , _locked (Properties::locked, false) \
191 , _video_locked (Properties::video_locked, false) \
192 , _automatic (Properties::automatic, false) \
193 , _whole_file (Properties::whole_file, false) \
194 , _import (Properties::import, false) \
195 , _external (Properties::external, false) \
196 , _hidden (Properties::hidden, false) \
197 , _position_locked (Properties::position_locked, false) \
198 , _ancestral_start (Properties::ancestral_start, (s)) \
199 , _ancestral_length (Properties::ancestral_length, (l)) \
200 , _stretch (Properties::stretch, 1.0) \
201 , _shift (Properties::shift, 1.0) \
202 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
203 , _layering_index (Properties::layering_index, 0)
205 #define REGION_COPY_STATE(other) \
206 _sync_marked (Properties::sync_marked, other->_sync_marked) \
207 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
208 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
209 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
210 , _start(Properties::start, other->_start) \
211 , _length(Properties::length, other->_length) \
212 , _position(Properties::position, other->_position) \
213 , _beat (Properties::beat, other->_beat) \
214 , _pulse (Properties::pulse, other->_pulse) \
215 , _sync_position(Properties::sync_position, other->_sync_position) \
216 , _user_transients (other->_user_transients) \
217 , _transient_user_start (other->_transient_user_start) \
218 , _transients (other->_transients) \
219 , _transient_analysis_start (other->_transient_analysis_start) \
220 , _transient_analysis_end (other->_transient_analysis_end) \
221 , _muted (Properties::muted, other->_muted) \
222 , _opaque (Properties::opaque, other->_opaque) \
223 , _locked (Properties::locked, other->_locked) \
224 , _video_locked (Properties::video_locked, other->_video_locked) \
225 , _automatic (Properties::automatic, other->_automatic) \
226 , _whole_file (Properties::whole_file, other->_whole_file) \
227 , _import (Properties::import, other->_import) \
228 , _external (Properties::external, other->_external) \
229 , _hidden (Properties::hidden, other->_hidden) \
230 , _position_locked (Properties::position_locked, other->_position_locked) \
231 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
232 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
233 , _stretch (Properties::stretch, other->_stretch) \
234 , _shift (Properties::shift, other->_shift) \
235 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
236 , _layering_index (Properties::layering_index, other->_layering_index)
238 /* derived-from-derived constructor (no sources in constructor) */
239 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
240 : SessionObject(s, name)
242 , REGION_DEFAULT_STATE(start,length)
243 , _last_length (length)
245 , _first_edit (EditChangesNothing)
248 register_properties ();
250 /* no sources at this point */
253 /** Basic Region constructor (many sources) */
254 Region::Region (const SourceList& srcs)
255 : SessionObject(srcs.front()->session(), "toBeRenamed")
256 , _type (srcs.front()->type())
257 , REGION_DEFAULT_STATE(0,0)
260 , _first_edit (EditChangesNothing)
263 register_properties ();
265 _type = srcs.front()->type();
269 assert(_sources.size() > 0);
270 assert (_type == srcs.front()->type());
273 /** Create a new Region from an existing one */
274 Region::Region (boost::shared_ptr<const Region> other)
275 : SessionObject(other->session(), other->name())
276 , _type (other->data_type())
277 , REGION_COPY_STATE (other)
278 , _last_length (other->_last_length)
279 , _last_position(other->_last_position) \
280 , _first_edit (EditChangesNothing)
281 , _layer (other->_layer)
283 register_properties ();
285 /* override state that may have been incorrectly inherited from the other region
288 _position = other->_position;
293 use_sources (other->_sources);
294 set_master_sources (other->_master_sources);
296 _position_lock_style = other->_position_lock_style;
297 _first_edit = other->_first_edit;
299 _start = other->_start;
300 _beat = other->_beat;
301 _pulse = other->_pulse;
303 /* sync pos is relative to start of file. our start-in-file is now zero,
304 so set our sync position to whatever the the difference between
305 _start and _sync_pos was in the other region.
307 result is that our new sync pos points to the same point in our source(s)
308 as the sync in the other region did in its source(s).
310 since we start at zero in our source(s), it is not possible to use a sync point that
311 is before the start. reset it to _start if that was true in the other region.
314 if (other->sync_marked()) {
315 if (other->_start < other->_sync_position) {
316 /* sync pos was after the start point of the other region */
317 _sync_position = other->_sync_position - other->_start;
319 /* sync pos was before the start point of the other region. not possible here. */
320 _sync_marked = false;
321 _sync_position = _start;
324 _sync_marked = false;
325 _sync_position = _start;
328 assert (_type == other->data_type());
331 /** Create a new Region from part of an existing one.
333 the start within \a other is given by \a offset
334 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
336 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
337 : SessionObject(other->session(), other->name())
338 , _type (other->data_type())
339 , REGION_COPY_STATE (other)
340 , _last_length (other->_last_length)
341 , _last_position(other->_last_position) \
342 , _first_edit (EditChangesNothing)
343 , _layer (other->_layer)
345 register_properties ();
347 /* override state that may have been incorrectly inherited from the other region
350 _position = other->_position + offset;
355 use_sources (other->_sources);
356 set_master_sources (other->_master_sources);
358 _start = other->_start + offset;
359 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
360 _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
362 /* if the other region had a distinct sync point
363 set, then continue to use it as best we can.
364 otherwise, reset sync point back to start.
367 if (other->sync_marked()) {
368 if (other->_sync_position < _start) {
369 _sync_marked = false;
370 _sync_position = _start;
372 _sync_position = other->_sync_position;
375 _sync_marked = false;
376 _sync_position = _start;
379 assert (_type == other->data_type());
382 /** Create a copy of @param other but with different sources. Used by filters */
383 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
384 : SessionObject (other->session(), other->name())
385 , _type (srcs.front()->type())
386 , REGION_COPY_STATE (other)
387 , _last_length (other->_last_length)
388 , _last_position (other->_last_position)
389 , _first_edit (EditChangesID)
390 , _layer (other->_layer)
392 register_properties ();
395 _position_locked = false;
397 other->_first_edit = EditChangesName;
399 if (other->_extra_xml) {
400 _extra_xml = new XMLNode (*other->_extra_xml);
406 assert(_sources.size() > 0);
411 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
416 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
418 _playlist = wpl.lock();
422 Region::set_name (const std::string& str)
425 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
426 assert(_name == str);
428 send_change (Properties::name);
435 Region::set_length (framecnt_t len, const int32_t sub_num)
437 //cerr << "Region::set_length() len = " << len << endl;
442 if (_length != len && len != 0) {
444 /* check that the current _position wouldn't make the new
448 if (max_framepos - len < _position) {
452 if (!verify_length (len)) {
457 set_length_internal (len, sub_num);
461 maybe_invalidate_transients ();
463 if (!property_changes_suspended()) {
467 send_change (Properties::length);
472 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
474 _last_length = _length;
479 Region::maybe_uncopy ()
481 /* this does nothing but marked a semantic moment once upon a time */
485 Region::first_edit ()
487 boost::shared_ptr<Playlist> pl (playlist());
489 if (_first_edit != EditChangesNothing && pl) {
491 _name = RegionFactory::new_region_name (_name);
492 _first_edit = EditChangesNothing;
494 send_change (Properties::name);
496 RegionFactory::CheckNewRegion (shared_from_this());
501 Region::at_natural_position () const
503 boost::shared_ptr<Playlist> pl (playlist());
509 boost::shared_ptr<Region> whole_file_region = get_parent();
511 if (whole_file_region) {
512 if (_position == whole_file_region->position() + _start) {
521 Region::move_to_natural_position ()
523 boost::shared_ptr<Playlist> pl (playlist());
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 set_position (whole_file_region->position() + _start);
537 Region::special_set_position (framepos_t pos)
539 /* this is used when creating a whole file region as
540 a way to store its "natural" or "captured" position.
543 _position = _position;
548 Region::set_position_lock_style (PositionLockStyle ps)
550 if (_position_lock_style != ps) {
552 boost::shared_ptr<Playlist> pl (playlist());
554 _position_lock_style = ps;
556 if (_position_lock_style == MusicTime) {
557 _beat = _session.tempo_map().beat_at_frame (_position);
558 _pulse = _session.tempo_map().pulse_at_frame (_position);
561 send_change (Properties::position_lock_style);
566 Region::update_after_tempo_map_change (bool send)
568 boost::shared_ptr<Playlist> pl (playlist());
574 if (_position_lock_style == AudioTime) {
575 /* don't signal as the actual position has not chnged */
576 recompute_position_from_lock_style (0);
580 const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
581 /* we have _beat. update frame position non-musically */
582 set_position_internal (pos, false, 0);
584 /* do this even if the position is the same. this helps out
585 a GUI that has moved its representation already.
589 send_change (Properties::position);
594 Region::set_position (framepos_t pos, int32_t sub_num)
601 set_position_internal (pos, true, 0);
603 double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
605 _pulse = _session.tempo_map().exact_qn_at_frame (pos, sub_num) / 4.0;
606 set_position_internal (pos, false, sub_num);
609 /* do this even if the position is the same. this helps out
610 a GUI that has moved its representation already.
612 PropertyChange p_and_l;
614 p_and_l.add (Properties::position);
615 /* Currently length change due to position change is only implemented
616 for MidiRegion (Region has no length in beats).
617 Notify a length change regardless (its more efficient for MidiRegions),
618 and when Region has a _length_beats we will need it here anyway).
620 p_and_l.add (Properties::length);
622 send_change (p_and_l);
626 /** A gui may need to create a region, then place it in an initial
627 * position determined by the user.
628 * When this takes place within one gui operation, we have to reset
629 * _last_position to prevent an implied move.
632 Region::set_initial_position (framepos_t pos)
638 if (_position != pos) {
641 /* check that the new _position wouldn't make the current
642 length impossible - if so, change the length.
644 XXX is this the right thing to do?
647 if (max_framepos - _length < _position) {
648 _last_length = _length;
649 _length = max_framepos - _position;
652 recompute_position_from_lock_style (0);
653 /* ensure that this move doesn't cause a range move */
654 _last_position = _position;
658 /* do this even if the position is the same. this helps out
659 a GUI that has moved its representation already.
661 send_change (Properties::position);
665 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
667 /* We emit a change of Properties::position even if the position hasn't changed
668 (see Region::set_position), so we must always set this up so that
669 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
671 _last_position = _position;
673 if (_position != pos) {
676 if (allow_bbt_recompute) {
677 recompute_position_from_lock_style (sub_num);
679 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
680 _pulse = _session.tempo_map().pulse_at_beat (_beat);
683 /* check that the new _position wouldn't make the current
684 length impossible - if so, change the length.
686 XXX is this the right thing to do?
688 if (max_framepos - _length < _position) {
689 _last_length = _length;
690 _length = max_framepos - _position;
696 Region::recompute_position_from_lock_style (const int32_t sub_num)
698 _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
699 _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
703 Region::nudge_position (frameoffset_t n)
705 if (locked() || video_locked()) {
713 framepos_t new_position = _position;
716 if (_position > max_framepos - n) {
717 new_position = max_framepos;
722 if (_position < -n) {
728 /* assumes non-musical nudge */
729 set_position_internal (new_position, true, 0);
731 send_change (Properties::position);
735 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
737 _ancestral_length = l;
738 _ancestral_start = s;
744 Region::set_start (framepos_t pos)
746 if (locked() || position_locked() || video_locked()) {
749 /* This just sets the start, nothing else. It effectively shifts
750 the contents of the Region within the overall extent of the Source,
751 without changing the Region's position or length
756 if (!verify_start (pos)) {
760 set_start_internal (pos);
763 maybe_invalidate_transients ();
765 send_change (Properties::start);
770 Region::move_start (frameoffset_t distance, const int32_t sub_num)
772 if (locked() || position_locked() || video_locked()) {
776 framepos_t new_start;
780 if (_start > max_framepos - distance) {
781 new_start = max_framepos; // makes no sense
783 new_start = _start + distance;
786 if (!verify_start (new_start)) {
790 } else if (distance < 0) {
792 if (_start < -distance) {
795 new_start = _start + distance;
802 if (new_start == _start) {
806 set_start_internal (new_start, sub_num);
811 send_change (Properties::start);
815 Region::trim_front (framepos_t new_position, const int32_t sub_num)
817 modify_front (new_position, false, sub_num);
821 Region::cut_front (framepos_t new_position, const int32_t sub_num)
823 modify_front (new_position, true, sub_num);
827 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
829 modify_end (new_endpoint, true, sub_num);
833 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
839 framepos_t end = last_frame();
840 framepos_t source_zero;
842 if (_position > _start) {
843 source_zero = _position - _start;
845 source_zero = 0; // its actually negative, but this will work for us
848 if (new_position < end) { /* can't trim it zero or negative length */
850 framecnt_t newlen = 0;
852 if (!can_trim_start_before_source_start ()) {
853 /* can't trim it back past where source position zero is located */
854 new_position = max (new_position, source_zero);
857 if (new_position > _position) {
858 newlen = _length - (new_position - _position);
860 newlen = _length + (_position - new_position);
863 trim_to_internal (new_position, newlen, sub_num);
866 _right_of_split = true;
869 if (!property_changes_suspended()) {
870 recompute_at_start ();
873 maybe_invalidate_transients ();
878 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
884 if (new_endpoint > _position) {
885 trim_to_internal (_position, new_endpoint - _position, sub_num);
887 _left_of_split = true;
889 if (!property_changes_suspended()) {
895 /** @param new_endpoint New region end point, such that, for example,
896 * a region at 0 of length 10 has an endpoint of 9.
900 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
902 modify_end (new_endpoint, false, sub_num);
906 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
912 trim_to_internal (position, length, sub_num);
914 if (!property_changes_suspended()) {
915 recompute_at_start ();
921 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
923 framepos_t new_start;
929 frameoffset_t const start_shift = position - _position;
931 if (start_shift > 0) {
933 if (_start > max_framepos - start_shift) {
934 new_start = max_framepos;
936 new_start = _start + start_shift;
939 } else if (start_shift < 0) {
941 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
944 new_start = _start + start_shift;
951 if (!verify_start_and_length (new_start, length)) {
955 PropertyChange what_changed;
957 if (_start != new_start) {
958 set_start_internal (new_start, sub_num);
959 what_changed.add (Properties::start);
963 /* Set position before length, otherwise for MIDI regions this bad thing happens:
964 * 1. we call set_length_internal; length in beats is computed using the region's current
965 * (soon-to-be old) position
966 * 2. we call set_position_internal; position is set and length in frames re-computed using
967 * length in beats from (1) but at the new position, which is wrong if the region
968 * straddles a tempo/meter change.
971 if (_position != position) {
972 if (!property_changes_suspended()) {
973 _last_position = _position;
975 set_position_internal (position, true, sub_num);
976 what_changed.add (Properties::position);
979 if (_length != length) {
980 if (!property_changes_suspended()) {
981 _last_length = _length;
983 set_length_internal (length, sub_num);
984 what_changed.add (Properties::length);
989 PropertyChange start_and_length;
991 start_and_length.add (Properties::start);
992 start_and_length.add (Properties::length);
994 if (what_changed.contains (start_and_length)) {
998 if (!what_changed.empty()) {
999 send_change (what_changed);
1004 Region::set_hidden (bool yn)
1006 if (hidden() != yn) {
1008 send_change (Properties::hidden);
1013 Region::set_whole_file (bool yn)
1016 /* no change signal */
1020 Region::set_automatic (bool yn)
1023 /* no change signal */
1027 Region::set_muted (bool yn)
1029 if (muted() != yn) {
1031 send_change (Properties::muted);
1036 Region::set_opaque (bool yn)
1038 if (opaque() != yn) {
1040 send_change (Properties::opaque);
1045 Region::set_locked (bool yn)
1047 if (locked() != yn) {
1049 send_change (Properties::locked);
1054 Region::set_video_locked (bool yn)
1056 if (video_locked() != yn) {
1058 send_change (Properties::video_locked);
1063 Region::set_position_locked (bool yn)
1065 if (position_locked() != yn) {
1066 _position_locked = yn;
1067 send_change (Properties::locked);
1071 /** Set the region's sync point.
1072 * @param absolute_pos Session time.
1075 Region::set_sync_position (framepos_t absolute_pos)
1077 /* position within our file */
1078 framepos_t const file_pos = _start + (absolute_pos - _position);
1080 if (file_pos != _sync_position) {
1081 _sync_marked = true;
1082 _sync_position = file_pos;
1083 if (!property_changes_suspended()) {
1087 send_change (Properties::sync_position);
1092 Region::clear_sync_position ()
1094 if (sync_marked()) {
1095 _sync_marked = false;
1096 if (!property_changes_suspended()) {
1100 send_change (Properties::sync_position);
1104 /* @return the sync point relative the first frame of the region */
1106 Region::sync_offset (int& dir) const
1108 if (sync_marked()) {
1109 if (_sync_position > _start) {
1111 return _sync_position - _start;
1114 return _start - _sync_position;
1123 Region::adjust_to_sync (framepos_t pos) const
1126 frameoffset_t offset = sync_offset (sync_dir);
1128 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1137 if (max_framepos - pos > offset) {
1145 /** @return Sync position in session time */
1147 Region::sync_position() const
1149 if (sync_marked()) {
1150 return _position - _start + _sync_position;
1152 /* if sync has not been marked, use the start of the region */
1160 boost::shared_ptr<Playlist> pl (playlist());
1162 pl->raise_region (shared_from_this ());
1169 boost::shared_ptr<Playlist> pl (playlist());
1171 pl->lower_region (shared_from_this ());
1177 Region::raise_to_top ()
1179 boost::shared_ptr<Playlist> pl (playlist());
1181 pl->raise_region_to_top (shared_from_this());
1186 Region::lower_to_bottom ()
1188 boost::shared_ptr<Playlist> pl (playlist());
1190 pl->lower_region_to_bottom (shared_from_this());
1195 Region::set_layer (layer_t l)
1203 XMLNode *node = new XMLNode ("Region");
1207 const char* fe = NULL;
1209 /* custom version of 'add_properties (*node);'
1210 * skip values that have have dedicated save functions
1211 * in AudioRegion::state()
1213 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1214 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1215 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1216 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1217 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1218 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1219 i->second->get_value (*node);
1222 id().print (buf, sizeof (buf));
1223 node->add_property ("id", buf);
1224 node->add_property ("type", _type.to_string());
1226 switch (_first_edit) {
1227 case EditChangesNothing:
1230 case EditChangesName:
1236 default: /* should be unreachable but makes g++ happy */
1241 node->add_property ("first-edit", fe);
1243 /* note: flags are stored by derived classes */
1245 for (uint32_t n=0; n < _sources.size(); ++n) {
1246 snprintf (buf2, sizeof(buf2), "source-%d", n);
1247 _sources[n]->id().print (buf, sizeof(buf));
1248 node->add_property (buf2, buf);
1251 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1252 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1253 _master_sources[n]->id().print (buf, sizeof (buf));
1254 node->add_property (buf2, buf);
1257 /* Only store nested sources for the whole-file region that acts
1258 as the parent/root of all regions using it.
1261 if (_whole_file && max_source_level() > 0) {
1263 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1265 /* region is compound - get its playlist and
1266 store that before we list the region that
1270 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1271 nested_node->add_child_nocopy ((*s)->get_state ());
1275 node->add_child_nocopy (*nested_node);
1280 node->add_child_copy (*_extra_xml);
1287 Region::get_state ()
1293 Region::set_state (const XMLNode& node, int version)
1295 PropertyChange what_changed;
1296 return _set_state (node, version, what_changed, true);
1300 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1302 XMLProperty const * prop;
1303 Timecode::BBT_Time bbt_time;
1305 Stateful::save_extra_xml (node);
1307 what_changed = set_values (node);
1311 if (_position_lock_style == MusicTime) {
1312 if ((prop = node.property ("bbt-position")) != 0) {
1313 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1316 &bbt_time.ticks) != 3) {
1317 _position_lock_style = AudioTime;
1319 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1320 _pulse = _session.tempo_map().pulse_at_beat (_beat);
1325 /* fix problems with old sessions corrupted by impossible
1326 values for _stretch or _shift
1328 if (_stretch == 0.0f) {
1332 if (_shift == 0.0f) {
1337 send_change (what_changed);
1340 /* Quick fix for 2.x sessions when region is muted */
1341 if ((prop = node.property (X_("flags")))) {
1342 if (string::npos != prop->value().find("Muted")){
1347 // saved property is invalid, region-transients are not saved
1348 if (_user_transients.size() == 0){
1349 _valid_transients = false;
1356 Region::suspend_property_changes ()
1358 Stateful::suspend_property_changes ();
1359 _last_length = _length;
1360 _last_position = _position;
1364 Region::mid_thaw (const PropertyChange& what_changed)
1366 if (what_changed.contains (Properties::length)) {
1367 if (what_changed.contains (Properties::position)) {
1368 recompute_at_start ();
1370 recompute_at_end ();
1375 Region::send_change (const PropertyChange& what_changed)
1377 if (what_changed.empty()) {
1381 Stateful::send_change (what_changed);
1383 if (!Stateful::property_changes_suspended()) {
1385 /* Try and send a shared_pointer unless this is part of the constructor.
1390 boost::shared_ptr<Region> rptr = shared_from_this();
1391 RegionPropertyChanged (rptr, what_changed);
1393 /* no shared_ptr available, relax; */
1399 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1401 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1405 Region::equivalent (boost::shared_ptr<const Region> other) const
1407 return _start == other->_start &&
1408 _position == other->_position &&
1409 _length == other->_length;
1413 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1415 return _start == other->_start &&
1416 _length == other->_length;
1420 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1422 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1426 Region::source_deleted (boost::weak_ptr<Source>)
1430 if (!_session.deletion_in_progress()) {
1431 /* this is a very special case: at least one of the region's
1432 sources has bee deleted, so invalidate all references to
1433 ourselves. Do NOT do this during session deletion, because
1434 then we run the risk that this will actually result
1435 in this object being deleted (as refcnt goes to zero)
1436 while emitting DropReferences.
1444 Region::master_source_names ()
1446 SourceList::iterator i;
1448 vector<string> names;
1449 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1450 names.push_back((*i)->name());
1457 Region::set_master_sources (const SourceList& srcs)
1459 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1460 (*i)->dec_use_count ();
1463 _master_sources = srcs;
1464 assert (_sources.size() == _master_sources.size());
1466 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1467 (*i)->inc_use_count ();
1472 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1477 if ((_sources.size() != other->_sources.size()) ||
1478 (_master_sources.size() != other->_master_sources.size())) {
1482 SourceList::const_iterator i;
1483 SourceList::const_iterator io;
1485 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1486 if ((*i)->id() != (*io)->id()) {
1491 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1492 if ((*i)->id() != (*io)->id()) {
1501 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1507 SourceList::const_iterator i;
1508 SourceList::const_iterator io;
1510 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1511 if ((*i)->id() == (*io)->id()) {
1520 Region::source_string () const
1522 //string res = itos(_sources.size());
1525 res << _sources.size() << ":";
1527 SourceList::const_iterator i;
1529 for (i = _sources.begin(); i != _sources.end(); ++i) {
1530 res << (*i)->id() << ":";
1533 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1534 res << (*i)->id() << ":";
1541 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1543 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1545 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1548 if (sources.find (ps) == sources.end()) {
1549 /* (Playlist)Source not currently in
1550 accumulating set, so recurse.
1552 ps->playlist()->deep_sources (sources);
1556 /* add this source */
1557 sources.insert (*i);
1560 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1562 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1565 if (sources.find (ps) == sources.end()) {
1566 /* (Playlist)Source not currently in
1567 accumulating set, so recurse.
1569 ps->playlist()->deep_sources (sources);
1573 /* add this source */
1574 sources.insert (*i);
1579 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1581 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1587 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1590 if (ps->playlist()->uses_source (source)) {
1597 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1603 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1606 if (ps->playlist()->uses_source (source)) {
1618 Region::source_length(uint32_t n) const
1620 assert (n < _sources.size());
1621 return _sources[n]->length (_position - _start);
1625 Region::verify_length (framecnt_t& len)
1627 if (source() && (source()->destructive() || source()->length_mutable())) {
1631 framecnt_t maxlen = 0;
1633 for (uint32_t n = 0; n < _sources.size(); ++n) {
1634 maxlen = max (maxlen, source_length(n) - _start);
1637 len = min (len, maxlen);
1643 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1645 if (source() && (source()->destructive() || source()->length_mutable())) {
1649 framecnt_t maxlen = 0;
1651 for (uint32_t n = 0; n < _sources.size(); ++n) {
1652 maxlen = max (maxlen, source_length(n) - new_start);
1655 new_length = min (new_length, maxlen);
1661 Region::verify_start (framepos_t pos)
1663 if (source() && (source()->destructive() || source()->length_mutable())) {
1667 for (uint32_t n = 0; n < _sources.size(); ++n) {
1668 if (pos > source_length(n) - _length) {
1676 Region::verify_start_mutable (framepos_t& new_start)
1678 if (source() && (source()->destructive() || source()->length_mutable())) {
1682 for (uint32_t n = 0; n < _sources.size(); ++n) {
1683 if (new_start > source_length(n) - _length) {
1684 new_start = source_length(n) - _length;
1690 boost::shared_ptr<Region>
1691 Region::get_parent() const
1693 boost::shared_ptr<Playlist> pl (playlist());
1696 boost::shared_ptr<Region> r;
1697 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1699 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1700 return boost::static_pointer_cast<Region> (r);
1704 return boost::shared_ptr<Region>();
1708 Region::apply (Filter& filter, Progress* progress)
1710 return filter.run (shared_from_this(), progress);
1715 Region::maybe_invalidate_transients ()
1717 bool changed = !_onsets.empty();
1720 if (_valid_transients || changed) {
1721 send_change (PropertyChange (Properties::valid_transients));
1727 Region::transients (AnalysisFeatureList& afl)
1729 int cnt = afl.empty() ? 0 : 1;
1731 Region::merge_features (afl, _onsets, _position);
1732 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1733 if (!_onsets.empty ()) {
1736 if (!_user_transients.empty ()) {
1741 // remove exact duplicates
1742 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1747 Region::has_transients () const
1749 if (!_user_transients.empty ()) {
1750 assert (_valid_transients);
1753 if (!_onsets.empty ()) {
1760 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1762 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1763 const frameoffset_t p = (*x) + off;
1764 if (p < first_frame() || p > last_frame()) {
1767 result.push_back (p);
1772 Region::drop_sources ()
1774 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1775 (*i)->dec_use_count ();
1780 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1781 (*i)->dec_use_count ();
1784 _master_sources.clear ();
1788 Region::use_sources (SourceList const & s)
1790 set<boost::shared_ptr<Source> > unique_srcs;
1792 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1794 _sources.push_back (*i);
1795 (*i)->inc_use_count ();
1796 _master_sources.push_back (*i);
1797 (*i)->inc_use_count ();
1799 /* connect only once to DropReferences, even if sources are replicated
1802 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1803 unique_srcs.insert (*i);
1804 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1810 Region::can_trim () const
1812 CanTrim ct = CanTrim (0);
1818 /* if not locked, we can always move the front later, and the end earlier
1821 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1823 if (start() != 0 || can_trim_start_before_source_start ()) {
1824 ct = CanTrim (ct | FrontTrimEarlier);
1827 if (!_sources.empty()) {
1828 if ((start() + length()) < _sources.front()->length (0)) {
1829 ct = CanTrim (ct | EndTrimLater);
1837 Region::max_source_level () const
1841 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1842 lvl = max (lvl, (*i)->level());
1849 Region::is_compound () const
1851 return max_source_level() > 0;
1855 Region::post_set (const PropertyChange& pc)
1857 if (pc.contains (Properties::position)) {
1858 recompute_position_from_lock_style (0);
1863 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1869 Region::earliest_possible_position () const
1871 if (_start > _position) {
1874 return _position - _start;
1879 Region::latest_possible_frame () const
1881 framecnt_t minlen = max_framecnt;
1883 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1884 /* non-audio regions have a length that may vary based on their
1885 * position, so we have to pass it in the call.
1887 minlen = min (minlen, (*i)->length (_position));
1890 /* the latest possible last frame is determined by the current
1891 * position, plus the shortest source extent past _start.
1894 return _position + (minlen - _start) - 1;