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"
43 using namespace ARDOUR;
48 namespace Properties {
49 PBD::PropertyDescriptor<bool> muted;
50 PBD::PropertyDescriptor<bool> opaque;
51 PBD::PropertyDescriptor<bool> locked;
52 PBD::PropertyDescriptor<bool> automatic;
53 PBD::PropertyDescriptor<bool> whole_file;
54 PBD::PropertyDescriptor<bool> import;
55 PBD::PropertyDescriptor<bool> external;
56 PBD::PropertyDescriptor<bool> sync_marked;
57 PBD::PropertyDescriptor<bool> left_of_split;
58 PBD::PropertyDescriptor<bool> right_of_split;
59 PBD::PropertyDescriptor<bool> hidden;
60 PBD::PropertyDescriptor<bool> position_locked;
61 PBD::PropertyDescriptor<bool> valid_transients;
62 PBD::PropertyDescriptor<framepos_t> start;
63 PBD::PropertyDescriptor<framecnt_t> length;
64 PBD::PropertyDescriptor<framepos_t> position;
65 PBD::PropertyDescriptor<framecnt_t> sync_position;
66 PBD::PropertyDescriptor<layer_t> layer;
67 PBD::PropertyDescriptor<framepos_t> ancestral_start;
68 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
69 PBD::PropertyDescriptor<float> stretch;
70 PBD::PropertyDescriptor<float> shift;
71 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
72 PBD::PropertyDescriptor<uint64_t> layering_index;
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
79 Region::make_property_quarks ()
81 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
83 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
85 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
87 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
89 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
91 Properties::import.property_id = g_quark_from_static_string (X_("import"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
93 Properties::external.property_id = g_quark_from_static_string (X_("external"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
95 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
97 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
99 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
101 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
103 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
105 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
107 Properties::start.property_id = g_quark_from_static_string (X_("start"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
109 Properties::length.property_id = g_quark_from_static_string (X_("length"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
111 Properties::position.property_id = g_quark_from_static_string (X_("position"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
113 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
115 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
117 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
119 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
121 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
123 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
125 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
127 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
132 Region::register_properties ()
134 _xml_node_name = X_("Region");
136 add_property (_muted);
137 add_property (_opaque);
138 add_property (_locked);
139 add_property (_automatic);
140 add_property (_whole_file);
141 add_property (_import);
142 add_property (_external);
143 add_property (_sync_marked);
144 add_property (_left_of_split);
145 add_property (_right_of_split);
146 add_property (_hidden);
147 add_property (_position_locked);
148 add_property (_valid_transients);
149 add_property (_start);
150 add_property (_length);
151 add_property (_position);
152 add_property (_sync_position);
153 add_property (_ancestral_start);
154 add_property (_ancestral_length);
155 add_property (_stretch);
156 add_property (_shift);
157 add_property (_position_lock_style);
158 add_property (_layering_index);
161 #define REGION_DEFAULT_STATE(s,l) \
162 _sync_marked (Properties::sync_marked, false) \
163 , _left_of_split (Properties::left_of_split, false) \
164 , _right_of_split (Properties::right_of_split, false) \
165 , _valid_transients (Properties::valid_transients, false) \
166 , _start (Properties::start, (s)) \
167 , _length (Properties::length, (l)) \
168 , _position (Properties::position, 0) \
169 , _sync_position (Properties::sync_position, (s)) \
170 , _muted (Properties::muted, false) \
171 , _opaque (Properties::opaque, true) \
172 , _locked (Properties::locked, false) \
173 , _automatic (Properties::automatic, false) \
174 , _whole_file (Properties::whole_file, false) \
175 , _import (Properties::import, false) \
176 , _external (Properties::external, false) \
177 , _hidden (Properties::hidden, false) \
178 , _position_locked (Properties::position_locked, false) \
179 , _ancestral_start (Properties::ancestral_start, (s)) \
180 , _ancestral_length (Properties::ancestral_length, (l)) \
181 , _stretch (Properties::stretch, 1.0) \
182 , _shift (Properties::shift, 1.0) \
183 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
184 , _layering_index (Properties::layering_index, 0)
186 #define REGION_COPY_STATE(other) \
187 _sync_marked (Properties::sync_marked, other->_sync_marked) \
188 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
189 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
190 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
191 , _start(Properties::start, other->_start) \
192 , _length(Properties::length, other->_length) \
193 , _position(Properties::position, other->_position) \
194 , _sync_position(Properties::sync_position, other->_sync_position) \
195 , _muted (Properties::muted, other->_muted) \
196 , _opaque (Properties::opaque, other->_opaque) \
197 , _locked (Properties::locked, other->_locked) \
198 , _automatic (Properties::automatic, other->_automatic) \
199 , _whole_file (Properties::whole_file, other->_whole_file) \
200 , _import (Properties::import, other->_import) \
201 , _external (Properties::external, other->_external) \
202 , _hidden (Properties::hidden, other->_hidden) \
203 , _position_locked (Properties::position_locked, other->_position_locked) \
204 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
205 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
206 , _stretch (Properties::stretch, other->_stretch) \
207 , _shift (Properties::shift, other->_shift) \
208 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
209 , _layering_index (Properties::layering_index, other->_layering_index)
211 /* derived-from-derived constructor (no sources in constructor) */
212 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
213 : SessionObject(s, name)
215 , REGION_DEFAULT_STATE(start,length)
216 , _last_length (length)
218 , _first_edit (EditChangesNothing)
221 register_properties ();
223 /* no sources at this point */
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228 : SessionObject(srcs.front()->session(), "toBeRenamed")
229 , _type (srcs.front()->type())
230 , REGION_DEFAULT_STATE(0,0)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 _type = srcs.front()->type();
242 assert(_sources.size() > 0);
243 assert (_type == srcs.front()->type());
246 /** Create a new Region from an existing one */
247 Region::Region (boost::shared_ptr<const Region> other)
248 : SessionObject(other->session(), other->name())
249 , _type (other->data_type())
250 , REGION_COPY_STATE (other)
251 , _last_length (other->_last_length)
252 , _last_position(other->_last_position) \
253 , _first_edit (EditChangesNothing)
254 , _layer (other->_layer)
256 register_properties ();
258 /* override state that may have been incorrectly inherited from the other region
266 use_sources (other->_sources);
268 _position_lock_style = other->_position_lock_style;
269 _first_edit = other->_first_edit;
271 _start = 0; // It seems strange _start is not inherited here?
273 /* sync pos is relative to start of file. our start-in-file is now zero,
274 so set our sync position to whatever the the difference between
275 _start and _sync_pos was in the other region.
277 result is that our new sync pos points to the same point in our source(s)
278 as the sync in the other region did in its source(s).
280 since we start at zero in our source(s), it is not possible to use a sync point that
281 is before the start. reset it to _start if that was true in the other region.
284 if (other->sync_marked()) {
285 if (other->_start < other->_sync_position) {
286 /* sync pos was after the start point of the other region */
287 _sync_position = other->_sync_position - other->_start;
289 /* sync pos was before the start point of the other region. not possible here. */
290 _sync_marked = false;
291 _sync_position = _start;
294 _sync_marked = false;
295 _sync_position = _start;
298 if (Profile->get_sae()) {
299 /* reset sync point to start if its ended up
300 outside region bounds.
303 if (_sync_position < _start || _sync_position >= _start + _length) {
304 _sync_marked = false;
305 _sync_position = _start;
309 assert (_type == other->data_type());
312 /** Create a new Region from part of an existing one.
314 the start within \a other is given by \a offset
315 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
317 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
318 : SessionObject(other->session(), other->name())
319 , _type (other->data_type())
320 , REGION_COPY_STATE (other)
321 , _last_length (other->_last_length)
322 , _last_position(other->_last_position) \
323 , _first_edit (EditChangesNothing)
324 , _layer (other->_layer)
326 register_properties ();
328 /* override state that may have been incorrectly inherited from the other region
336 use_sources (other->_sources);
338 _start = other->_start + offset;
340 /* if the other region had a distinct sync point
341 set, then continue to use it as best we can.
342 otherwise, reset sync point back to start.
345 if (other->sync_marked()) {
346 if (other->_sync_position < _start) {
347 _sync_marked = false;
348 _sync_position = _start;
350 _sync_position = other->_sync_position;
353 _sync_marked = false;
354 _sync_position = _start;
357 if (Profile->get_sae()) {
358 /* reset sync point to start if its ended up
359 outside region bounds.
362 if (_sync_position < _start || _sync_position >= _start + _length) {
363 _sync_marked = false;
364 _sync_position = _start;
368 assert (_type == other->data_type());
371 /** Create a copy of @param other but with different sources. Used by filters */
372 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
373 : SessionObject (other->session(), other->name())
374 , _type (srcs.front()->type())
375 , REGION_COPY_STATE (other)
376 , _last_length (other->_last_length)
377 , _last_position (other->_last_position)
378 , _first_edit (EditChangesID)
379 , _layer (other->_layer)
381 register_properties ();
384 _position_locked = false;
386 other->_first_edit = EditChangesName;
388 if (other->_extra_xml) {
389 _extra_xml = new XMLNode (*other->_extra_xml);
395 assert(_sources.size() > 0);
400 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
405 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
407 _playlist = wpl.lock();
411 Region::set_name (const std::string& str)
414 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
415 assert(_name == str);
417 send_change (Properties::name);
424 Region::set_length (framecnt_t len)
426 //cerr << "Region::set_length() len = " << len << endl;
431 if (_length != len && len != 0) {
433 /* check that the current _position wouldn't make the new
437 if (max_framepos - len < _position) {
441 if (!verify_length (len)) {
446 _last_length = _length;
447 set_length_internal (len);
451 invalidate_transients ();
453 if (!property_changes_suspended()) {
457 send_change (Properties::length);
462 Region::set_length_internal (framecnt_t len)
468 Region::maybe_uncopy ()
470 /* this does nothing but marked a semantic moment once upon a time */
474 Region::first_edit ()
476 boost::shared_ptr<Playlist> pl (playlist());
478 if (_first_edit != EditChangesNothing && pl) {
480 _name = RegionFactory::new_region_name (_name);
481 _first_edit = EditChangesNothing;
483 send_change (Properties::name);
485 RegionFactory::CheckNewRegion (shared_from_this());
490 Region::at_natural_position () const
492 boost::shared_ptr<Playlist> pl (playlist());
498 boost::shared_ptr<Region> whole_file_region = get_parent();
500 if (whole_file_region) {
501 if (_position == whole_file_region->position() + _start) {
510 Region::move_to_natural_position ()
512 boost::shared_ptr<Playlist> pl (playlist());
518 boost::shared_ptr<Region> whole_file_region = get_parent();
520 if (whole_file_region) {
521 set_position (whole_file_region->position() + _start);
526 Region::special_set_position (framepos_t pos)
528 /* this is used when creating a whole file region as
529 a way to store its "natural" or "captured" position.
532 _position = _position;
537 Region::set_position_lock_style (PositionLockStyle ps)
539 if (_position_lock_style != ps) {
541 boost::shared_ptr<Playlist> pl (playlist());
543 _position_lock_style = ps;
545 if (_position_lock_style == MusicTime) {
546 _session.bbt_time (_position, _bbt_time);
549 send_change (Properties::position_lock_style);
554 Region::update_after_tempo_map_change ()
556 boost::shared_ptr<Playlist> pl (playlist());
558 if (!pl || _position_lock_style != MusicTime) {
562 TempoMap& map (_session.tempo_map());
563 framepos_t pos = map.frame_time (_bbt_time);
564 set_position_internal (pos, false);
566 /* do this even if the position is the same. this helps out
567 a GUI that has moved its representation already.
569 send_change (Properties::position);
573 Region::set_position (framepos_t pos)
579 set_position_internal (pos, true);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
584 send_change (Properties::position);
589 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
591 /* We emit a change of Properties::position even if the position hasn't changed
592 (see Region::set_position), so we must always set this up so that
593 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
595 _last_position = _position;
597 if (_position != pos) {
600 /* check that the new _position wouldn't make the current
601 length impossible - if so, change the length.
603 XXX is this the right thing to do?
606 if (max_framepos - _length < _position) {
607 _last_length = _length;
608 _length = max_framepos - _position;
611 if (allow_bbt_recompute) {
612 recompute_position_from_lock_style ();
615 //invalidate_transients ();
620 Region::recompute_position_from_lock_style ()
622 if (_position_lock_style == MusicTime) {
623 _session.bbt_time (_position, _bbt_time);
628 Region::nudge_position (frameoffset_t n)
638 framepos_t new_position = _position;
641 if (_position > max_framepos - n) {
642 new_position = max_framepos;
647 if (_position < -n) {
654 set_position_internal (new_position, true);
656 send_change (Properties::position);
660 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
662 _ancestral_length = l;
663 _ancestral_start = s;
669 Region::set_start (framepos_t pos)
671 if (locked() || position_locked()) {
674 /* This just sets the start, nothing else. It effectively shifts
675 the contents of the Region within the overall extent of the Source,
676 without changing the Region's position or length
681 if (!verify_start (pos)) {
685 set_start_internal (pos);
688 invalidate_transients ();
690 send_change (Properties::start);
695 Region::trim_start (framepos_t new_position)
697 if (locked() || position_locked()) {
701 framepos_t new_start;
702 frameoffset_t const start_shift = new_position - _position;
704 if (start_shift > 0) {
706 if (_start > max_framepos - start_shift) {
707 new_start = max_framepos;
709 new_start = _start + start_shift;
712 if (!verify_start (new_start)) {
716 } else if (start_shift < 0) {
718 if (_start < -start_shift) {
721 new_start = _start + start_shift;
728 if (new_start == _start) {
732 set_start_internal (new_start);
736 send_change (Properties::start);
740 Region::trim_front (framepos_t new_position)
742 modify_front (new_position, false);
746 Region::cut_front (framepos_t new_position)
748 modify_front (new_position, true);
752 Region::cut_end (framepos_t new_endpoint)
754 modify_end (new_endpoint, true);
758 Region::modify_front (framepos_t new_position, bool reset_fade)
764 framepos_t end = last_frame();
765 framepos_t source_zero;
767 if (_position > _start) {
768 source_zero = _position - _start;
770 source_zero = 0; // its actually negative, but this will work for us
773 if (new_position < end) { /* can't trim it zero or negative length */
775 framecnt_t newlen = 0;
776 framepos_t delta = 0;
778 if (!can_trim_start_before_source_start ()) {
779 /* can't trim it back past where source position zero is located */
780 new_position = max (new_position, source_zero);
783 if (new_position > _position) {
784 newlen = _length - (new_position - _position);
785 delta = -1 * (new_position - _position);
787 newlen = _length + (_position - new_position);
788 delta = _position - new_position;
791 trim_to_internal (new_position, newlen);
794 _right_of_split = true;
797 if (!property_changes_suspended()) {
798 recompute_at_start ();
801 if (_transients.size() > 0){
802 adjust_transients(delta);
808 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
814 if (new_endpoint > _position) {
815 trim_to_internal (_position, new_endpoint - _position);
817 _left_of_split = true;
819 if (!property_changes_suspended()) {
825 /** @param new_endpoint New region end point, such that, for example,
826 * a region at 0 of length 10 has an endpoint of 9.
830 Region::trim_end (framepos_t new_endpoint)
832 modify_end (new_endpoint, false);
836 Region::trim_to (framepos_t position, framecnt_t length)
842 trim_to_internal (position, length);
844 if (!property_changes_suspended()) {
845 recompute_at_start ();
851 Region::trim_to_internal (framepos_t position, framecnt_t length)
853 framepos_t new_start;
859 frameoffset_t const start_shift = position - _position;
861 if (start_shift > 0) {
863 if (_start > max_framepos - start_shift) {
864 new_start = max_framepos;
866 new_start = _start + start_shift;
869 } else if (start_shift < 0) {
871 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
874 new_start = _start + start_shift;
881 if (!verify_start_and_length (new_start, length)) {
885 PropertyChange what_changed;
887 if (_start != new_start) {
888 set_start_internal (new_start);
889 what_changed.add (Properties::start);
892 /* Set position before length, otherwise for MIDI regions this bad thing happens:
893 * 1. we call set_length_internal; length in beats is computed using the region's current
894 * (soon-to-be old) position
895 * 2. we call set_position_internal; position is set and length in frames re-computed using
896 * length in beats from (1) but at the new position, which is wrong if the region
897 * straddles a tempo/meter change.
900 if (_position != position) {
901 if (!property_changes_suspended()) {
902 _last_position = _position;
904 set_position_internal (position, true);
905 what_changed.add (Properties::position);
908 if (_length != length) {
909 if (!property_changes_suspended()) {
910 _last_length = _length;
912 set_length_internal (length);
913 what_changed.add (Properties::length);
918 PropertyChange start_and_length;
920 start_and_length.add (Properties::start);
921 start_and_length.add (Properties::length);
923 if (what_changed.contains (start_and_length)) {
927 if (!what_changed.empty()) {
928 send_change (what_changed);
933 Region::set_hidden (bool yn)
935 if (hidden() != yn) {
937 send_change (Properties::hidden);
942 Region::set_whole_file (bool yn)
945 /* no change signal */
949 Region::set_automatic (bool yn)
952 /* no change signal */
956 Region::set_muted (bool yn)
960 send_change (Properties::muted);
965 Region::set_opaque (bool yn)
967 if (opaque() != yn) {
969 send_change (Properties::opaque);
974 Region::set_locked (bool yn)
976 if (locked() != yn) {
978 send_change (Properties::locked);
983 Region::set_position_locked (bool yn)
985 if (position_locked() != yn) {
986 _position_locked = yn;
987 send_change (Properties::locked);
991 /** Set the region's sync point.
992 * @param absolute_pos Session time.
995 Region::set_sync_position (framepos_t absolute_pos)
997 /* position within our file */
998 framepos_t const file_pos = _start + (absolute_pos - _position);
1000 if (file_pos != _sync_position) {
1001 _sync_marked = true;
1002 _sync_position = file_pos;
1003 if (!property_changes_suspended()) {
1007 send_change (Properties::sync_position);
1012 Region::clear_sync_position ()
1014 if (sync_marked()) {
1015 _sync_marked = false;
1016 if (!property_changes_suspended()) {
1020 send_change (Properties::sync_position);
1024 /* @return the sync point relative the first frame of the region */
1026 Region::sync_offset (int& dir) const
1028 if (sync_marked()) {
1029 if (_sync_position > _start) {
1031 return _sync_position - _start;
1034 return _start - _sync_position;
1043 Region::adjust_to_sync (framepos_t pos) const
1046 frameoffset_t offset = sync_offset (sync_dir);
1048 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1057 if (max_framepos - pos > offset) {
1065 /** @return Sync position in session time */
1067 Region::sync_position() const
1069 if (sync_marked()) {
1070 return _position - _start + _sync_position;
1072 /* if sync has not been marked, use the start of the region */
1080 boost::shared_ptr<Playlist> pl (playlist());
1082 pl->raise_region (shared_from_this ());
1089 boost::shared_ptr<Playlist> pl (playlist());
1091 pl->lower_region (shared_from_this ());
1097 Region::raise_to_top ()
1099 boost::shared_ptr<Playlist> pl (playlist());
1101 pl->raise_region_to_top (shared_from_this());
1106 Region::lower_to_bottom ()
1108 boost::shared_ptr<Playlist> pl (playlist());
1110 pl->lower_region_to_bottom (shared_from_this());
1115 Region::set_layer (layer_t l)
1123 XMLNode *node = new XMLNode ("Region");
1126 LocaleGuard lg (X_("POSIX"));
1127 const char* fe = NULL;
1129 /* custom version of 'add_properties (*node);'
1130 * skip values that have have dedicated save functions
1131 * in AudioRegion::state()
1133 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1134 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1135 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1136 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1137 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1138 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1139 i->second->get_value (*node);
1142 id().print (buf, sizeof (buf));
1143 node->add_property ("id", buf);
1144 node->add_property ("type", _type.to_string());
1146 switch (_first_edit) {
1147 case EditChangesNothing:
1150 case EditChangesName:
1156 default: /* should be unreachable but makes g++ happy */
1161 node->add_property ("first-edit", fe);
1163 /* note: flags are stored by derived classes */
1165 if (_position_lock_style != AudioTime) {
1168 node->add_property ("bbt-position", str.str());
1171 for (uint32_t n=0; n < _sources.size(); ++n) {
1172 snprintf (buf2, sizeof(buf2), "source-%d", n);
1173 _sources[n]->id().print (buf, sizeof(buf));
1174 node->add_property (buf2, buf);
1177 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1178 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1179 _master_sources[n]->id().print (buf, sizeof (buf));
1180 node->add_property (buf2, buf);
1183 /* Only store nested sources for the whole-file region that acts
1184 as the parent/root of all regions using it.
1187 if (_whole_file && max_source_level() > 0) {
1189 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1191 /* region is compound - get its playlist and
1192 store that before we list the region that
1196 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1197 nested_node->add_child_nocopy ((*s)->get_state ());
1201 node->add_child_nocopy (*nested_node);
1206 node->add_child_copy (*_extra_xml);
1213 Region::get_state ()
1219 Region::set_state (const XMLNode& node, int version)
1221 PropertyChange what_changed;
1222 return _set_state (node, version, what_changed, true);
1226 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1228 const XMLProperty* prop;
1230 Stateful::save_extra_xml (node);
1232 what_changed = set_values (node);
1236 if (_position_lock_style == MusicTime) {
1237 if ((prop = node.property ("bbt-position")) == 0) {
1238 /* missing BBT info, revert to audio time locking */
1239 _position_lock_style = AudioTime;
1241 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1244 &_bbt_time.ticks) != 3) {
1245 _position_lock_style = AudioTime;
1250 /* fix problems with old sessions corrupted by impossible
1251 values for _stretch or _shift
1253 if (_stretch == 0.0f) {
1257 if (_shift == 0.0f) {
1262 send_change (what_changed);
1265 /* Quick fix for 2.x sessions when region is muted */
1266 if ((prop = node.property (X_("flags")))) {
1267 if (string::npos != prop->value().find("Muted")){
1277 Region::suspend_property_changes ()
1279 Stateful::suspend_property_changes ();
1280 _last_length = _length;
1281 _last_position = _position;
1285 Region::mid_thaw (const PropertyChange& what_changed)
1287 if (what_changed.contains (Properties::length)) {
1288 if (what_changed.contains (Properties::position)) {
1289 recompute_at_start ();
1291 recompute_at_end ();
1296 Region::send_change (const PropertyChange& what_changed)
1298 if (what_changed.empty()) {
1302 Stateful::send_change (what_changed);
1304 if (!Stateful::property_changes_suspended()) {
1306 /* Try and send a shared_pointer unless this is part of the constructor.
1311 boost::shared_ptr<Region> rptr = shared_from_this();
1312 RegionPropertyChanged (rptr, what_changed);
1314 /* no shared_ptr available, relax; */
1320 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1322 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1326 Region::equivalent (boost::shared_ptr<const Region> other) const
1328 return _start == other->_start &&
1329 _position == other->_position &&
1330 _length == other->_length;
1334 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1336 return _start == other->_start &&
1337 _length == other->_length;
1341 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1343 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1347 Region::source_deleted (boost::weak_ptr<Source>)
1351 if (!_session.deletion_in_progress()) {
1352 /* this is a very special case: at least one of the region's
1353 sources has bee deleted, so invalidate all references to
1354 ourselves. Do NOT do this during session deletion, because
1355 then we run the risk that this will actually result
1356 in this object being deleted (as refcnt goes to zero)
1357 while emitting DropReferences.
1365 Region::master_source_names ()
1367 SourceList::iterator i;
1369 vector<string> names;
1370 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1371 names.push_back((*i)->name());
1378 Region::set_master_sources (const SourceList& srcs)
1380 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1381 (*i)->dec_use_count ();
1384 _master_sources = srcs;
1385 assert (_sources.size() == _master_sources.size());
1387 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1388 (*i)->inc_use_count ();
1393 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1398 if ((_sources.size() != other->_sources.size()) ||
1399 (_master_sources.size() != other->_master_sources.size())) {
1403 SourceList::const_iterator i;
1404 SourceList::const_iterator io;
1406 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1407 if ((*i)->id() != (*io)->id()) {
1412 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1413 if ((*i)->id() != (*io)->id()) {
1422 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1428 SourceList::const_iterator i;
1429 SourceList::const_iterator io;
1431 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1432 if ((*i)->id() == (*io)->id()) {
1441 Region::source_string () const
1443 //string res = itos(_sources.size());
1446 res << _sources.size() << ":";
1448 SourceList::const_iterator i;
1450 for (i = _sources.begin(); i != _sources.end(); ++i) {
1451 res << (*i)->id() << ":";
1454 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1455 res << (*i)->id() << ":";
1462 Region::uses_source (boost::shared_ptr<const Source> source) const
1464 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1469 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1472 if (ps->playlist()->uses_source (source)) {
1482 Region::source_length(uint32_t n) const
1484 assert (n < _sources.size());
1485 return _sources[n]->length (_position - _start);
1489 Region::verify_length (framecnt_t len)
1491 if (source() && (source()->destructive() || source()->length_mutable())) {
1495 framecnt_t maxlen = 0;
1497 for (uint32_t n = 0; n < _sources.size(); ++n) {
1498 maxlen = max (maxlen, source_length(n) - _start);
1501 len = min (len, maxlen);
1507 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1509 if (source() && (source()->destructive() || source()->length_mutable())) {
1513 framecnt_t maxlen = 0;
1515 for (uint32_t n = 0; n < _sources.size(); ++n) {
1516 maxlen = max (maxlen, source_length(n) - new_start);
1519 new_length = min (new_length, maxlen);
1525 Region::verify_start (framepos_t pos)
1527 if (source() && (source()->destructive() || source()->length_mutable())) {
1531 for (uint32_t n = 0; n < _sources.size(); ++n) {
1532 if (pos > source_length(n) - _length) {
1540 Region::verify_start_mutable (framepos_t& new_start)
1542 if (source() && (source()->destructive() || source()->length_mutable())) {
1546 for (uint32_t n = 0; n < _sources.size(); ++n) {
1547 if (new_start > source_length(n) - _length) {
1548 new_start = source_length(n) - _length;
1554 boost::shared_ptr<Region>
1555 Region::get_parent() const
1557 boost::shared_ptr<Playlist> pl (playlist());
1560 boost::shared_ptr<Region> r;
1561 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1563 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1564 return boost::static_pointer_cast<Region> (r);
1568 return boost::shared_ptr<Region>();
1572 Region::apply (Filter& filter, Progress* progress)
1574 return filter.run (shared_from_this(), progress);
1579 Region::invalidate_transients ()
1581 _valid_transients = false;
1582 _transients.clear ();
1584 send_change (PropertyChange (Properties::valid_transients));
1588 Region::drop_sources ()
1590 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1591 (*i)->dec_use_count ();
1596 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1597 (*i)->dec_use_count ();
1600 _master_sources.clear ();
1604 Region::use_sources (SourceList const & s)
1606 set<boost::shared_ptr<Source> > unique_srcs;
1608 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1610 _sources.push_back (*i);
1611 (*i)->inc_use_count ();
1612 _master_sources.push_back (*i);
1613 (*i)->inc_use_count ();
1615 /* connect only once to DropReferences, even if sources are replicated
1618 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1619 unique_srcs.insert (*i);
1620 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1626 Region::can_trim () const
1628 CanTrim ct = CanTrim (0);
1634 /* if not locked, we can always move the front later, and the end earlier
1637 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1639 if (start() != 0 || can_trim_start_before_source_start ()) {
1640 ct = CanTrim (ct | FrontTrimEarlier);
1643 if (!_sources.empty()) {
1644 if ((start() + length()) < _sources.front()->length (0)) {
1645 ct = CanTrim (ct | EndTrimLater);
1653 Region::max_source_level () const
1657 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1658 lvl = max (lvl, (*i)->level());
1665 Region::is_compound () const
1667 return max_source_level() > 0;
1671 Region::post_set (const PropertyChange& pc)
1673 if (pc.contains (Properties::position)) {
1674 recompute_position_from_lock_style ();
1679 Region::set_start_internal (framecnt_t s)
1685 Region::earliest_possible_position () const
1687 if (_start > _position) {
1690 return _position - _start;
1695 Region::latest_possible_frame () const
1697 framecnt_t minlen = max_framecnt;
1699 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1700 /* non-audio regions have a length that may vary based on their
1701 * position, so we have to pass it in the call.
1703 minlen = min (minlen, (*i)->length (_position));
1706 /* the latest possible last frame is determined by the current
1707 * position, plus the shortest source extent past _start.
1710 return _position + (minlen - _start) - 1;