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/thread.h>
27 #include "pbd/xml++.h"
28 #include "pbd/stacktrace.h"
29 #include "pbd/enumwriter.h"
31 #include "ardour/debug.h"
32 #include "ardour/file_source.h"
33 #include "ardour/filter.h"
34 #include "ardour/playlist.h"
35 #include "ardour/playlist_source.h"
36 #include "ardour/profile.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
40 #include "ardour/source.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/utils.h"
48 using namespace ARDOUR;
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> muted;
54 PBD::PropertyDescriptor<bool> opaque;
55 PBD::PropertyDescriptor<bool> locked;
56 PBD::PropertyDescriptor<bool> automatic;
57 PBD::PropertyDescriptor<bool> whole_file;
58 PBD::PropertyDescriptor<bool> import;
59 PBD::PropertyDescriptor<bool> external;
60 PBD::PropertyDescriptor<bool> sync_marked;
61 PBD::PropertyDescriptor<bool> left_of_split;
62 PBD::PropertyDescriptor<bool> right_of_split;
63 PBD::PropertyDescriptor<bool> hidden;
64 PBD::PropertyDescriptor<bool> position_locked;
65 PBD::PropertyDescriptor<bool> valid_transients;
66 PBD::PropertyDescriptor<framepos_t> start;
67 PBD::PropertyDescriptor<framecnt_t> length;
68 PBD::PropertyDescriptor<framepos_t> position;
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;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
92 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
94 Properties::import.property_id = g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
96 Properties::external.property_id = g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
98 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
100 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
102 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
104 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
106 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
108 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
110 Properties::start.property_id = g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
112 Properties::length.property_id = g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
114 Properties::position.property_id = g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
116 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
118 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
120 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
122 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
124 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
126 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
128 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
133 Region::register_properties ()
135 _xml_node_name = X_("Region");
137 add_property (_muted);
138 add_property (_opaque);
139 add_property (_locked);
140 add_property (_automatic);
141 add_property (_whole_file);
142 add_property (_import);
143 add_property (_external);
144 add_property (_sync_marked);
145 add_property (_left_of_split);
146 add_property (_right_of_split);
147 add_property (_hidden);
148 add_property (_position_locked);
149 add_property (_valid_transients);
150 add_property (_start);
151 add_property (_length);
152 add_property (_position);
153 add_property (_sync_position);
154 add_property (_layer);
155 add_property (_ancestral_start);
156 add_property (_ancestral_length);
157 add_property (_stretch);
158 add_property (_shift);
159 add_property (_position_lock_style);
162 #define REGION_DEFAULT_STATE(s,l) \
163 _muted (Properties::muted, false) \
164 , _opaque (Properties::opaque, true) \
165 , _locked (Properties::locked, false) \
166 , _automatic (Properties::automatic, false) \
167 , _whole_file (Properties::whole_file, false) \
168 , _import (Properties::import, false) \
169 , _external (Properties::external, false) \
170 , _sync_marked (Properties::sync_marked, false) \
171 , _left_of_split (Properties::left_of_split, false) \
172 , _right_of_split (Properties::right_of_split, false) \
173 , _hidden (Properties::hidden, false) \
174 , _position_locked (Properties::position_locked, false) \
175 , _valid_transients (Properties::valid_transients, false) \
176 , _start (Properties::start, (s)) \
177 , _length (Properties::length, (l)) \
178 , _position (Properties::position, 0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _layer (Properties::layer, 0) \
181 , _ancestral_start (Properties::ancestral_start, (s)) \
182 , _ancestral_length (Properties::ancestral_length, (l)) \
183 , _stretch (Properties::stretch, 1.0) \
184 , _shift (Properties::shift, 1.0) \
185 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
187 #define REGION_COPY_STATE(other) \
188 _muted (Properties::muted, other->_muted) \
189 , _opaque (Properties::opaque, other->_opaque) \
190 , _locked (Properties::locked, other->_locked) \
191 , _automatic (Properties::automatic, other->_automatic) \
192 , _whole_file (Properties::whole_file, other->_whole_file) \
193 , _import (Properties::import, other->_import) \
194 , _external (Properties::external, other->_external) \
195 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
196 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
197 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
198 , _hidden (Properties::hidden, other->_hidden) \
199 , _position_locked (Properties::position_locked, other->_position_locked) \
200 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
201 , _start(Properties::start, other->_start) \
202 , _length(Properties::length, other->_length) \
203 , _position(Properties::position, other->_position) \
204 , _sync_position(Properties::sync_position, other->_sync_position) \
205 , _layer (Properties::layer, other->_layer) \
206 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
207 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
208 , _stretch (Properties::stretch, other->_stretch) \
209 , _shift (Properties::shift, other->_shift) \
210 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
212 /* derived-from-derived constructor (no sources in constructor) */
213 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
214 : SessionObject(s, name)
216 , REGION_DEFAULT_STATE(start,length)
217 , _last_length (length)
219 , _first_edit (EditChangesNothing)
221 , _pending_explicit_relayer (false)
223 register_properties ();
225 /* no sources at this point */
228 /** Basic Region constructor (many sources) */
229 Region::Region (const SourceList& srcs)
230 : SessionObject(srcs.front()->session(), "toBeRenamed")
231 , _type (srcs.front()->type())
232 , REGION_DEFAULT_STATE(0,0)
235 , _first_edit (EditChangesNothing)
237 , _pending_explicit_relayer (false)
239 register_properties ();
241 _type = srcs.front()->type();
245 assert(_sources.size() > 0);
246 assert (_type == srcs.front()->type());
249 /** Create a new Region from an existing one */
250 Region::Region (boost::shared_ptr<const Region> other)
251 : SessionObject(other->session(), other->name())
252 , _type (other->data_type())
253 , REGION_COPY_STATE (other)
254 , _last_length (other->_last_length)
255 , _last_position(other->_last_position) \
256 , _first_edit (EditChangesNothing)
258 , _pending_explicit_relayer (false)
261 register_properties ();
263 /* override state that may have been incorrectly inherited from the other region
271 use_sources (other->_sources);
273 _position_lock_style = other->_position_lock_style;
274 _first_edit = other->_first_edit;
276 _start = 0; // It seems strange _start is not inherited here?
278 /* sync pos is relative to start of file. our start-in-file is now zero,
279 so set our sync position to whatever the the difference between
280 _start and _sync_pos was in the other region.
282 result is that our new sync pos points to the same point in our source(s)
283 as the sync in the other region did in its source(s).
285 since we start at zero in our source(s), it is not possible to use a sync point that
286 is before the start. reset it to _start if that was true in the other region.
289 if (other->sync_marked()) {
290 if (other->_start < other->_sync_position) {
291 /* sync pos was after the start point of the other region */
292 _sync_position = other->_sync_position - other->_start;
294 /* sync pos was before the start point of the other region. not possible here. */
295 _sync_marked = false;
296 _sync_position = _start;
299 _sync_marked = false;
300 _sync_position = _start;
303 if (Profile->get_sae()) {
304 /* reset sync point to start if its ended up
305 outside region bounds.
308 if (_sync_position < _start || _sync_position >= _start + _length) {
309 _sync_marked = false;
310 _sync_position = _start;
314 assert (_type == other->data_type());
317 /** Create a new Region from part of an existing one.
319 the start within \a other is given by \a offset
320 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323 : SessionObject(other->session(), other->name())
324 , _type (other->data_type())
325 , REGION_COPY_STATE (other)
326 , _last_length (other->_last_length)
327 , _last_position(other->_last_position) \
328 , _first_edit (EditChangesNothing)
330 , _pending_explicit_relayer (false)
333 register_properties ();
335 /* override state that may have been incorrectly inherited from the other region
343 use_sources (other->_sources);
345 _start = other->_start + offset;
347 /* if the other region had a distinct sync point
348 set, then continue to use it as best we can.
349 otherwise, reset sync point back to start.
352 if (other->sync_marked()) {
353 if (other->_sync_position < _start) {
354 _sync_marked = false;
355 _sync_position = _start;
357 _sync_position = other->_sync_position;
360 _sync_marked = false;
361 _sync_position = _start;
364 if (Profile->get_sae()) {
365 /* reset sync point to start if its ended up
366 outside region bounds.
369 if (_sync_position < _start || _sync_position >= _start + _length) {
370 _sync_marked = false;
371 _sync_position = _start;
375 assert (_type == other->data_type());
378 /** Create a copy of @param other but with different sources. Used by filters */
379 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
380 : SessionObject (other->session(), other->name())
381 , _type (srcs.front()->type())
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _first_edit (EditChangesID)
386 , _last_layer_op (other->_last_layer_op)
387 , _pending_explicit_relayer (false)
389 register_properties ();
392 _position_locked = false;
394 other->_first_edit = EditChangesName;
396 if (other->_extra_xml) {
397 _extra_xml = new XMLNode (*other->_extra_xml);
403 assert(_sources.size() > 0);
408 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
413 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
415 _playlist = wpl.lock();
419 Region::set_name (const std::string& str)
422 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
423 assert(_name == str);
425 send_change (Properties::name);
432 Region::set_length (framecnt_t len)
434 //cerr << "Region::set_length() len = " << len << endl;
439 if (_length != len && len != 0) {
441 /* check that the current _position wouldn't make the new
445 if (max_framepos - len < _position) {
449 if (!verify_length (len)) {
454 _last_length = _length;
455 set_length_internal (len);
459 invalidate_transients ();
461 if (!property_changes_suspended()) {
465 send_change (Properties::length);
470 Region::set_length_internal (framecnt_t len)
476 Region::maybe_uncopy ()
478 /* this does nothing but marked a semantic moment once upon a time */
482 Region::first_edit ()
484 boost::shared_ptr<Playlist> pl (playlist());
486 if (_first_edit != EditChangesNothing && pl) {
488 _name = RegionFactory::new_region_name (_name);
489 _first_edit = EditChangesNothing;
491 send_change (Properties::name);
493 RegionFactory::CheckNewRegion (shared_from_this());
498 Region::at_natural_position () const
500 boost::shared_ptr<Playlist> pl (playlist());
506 boost::shared_ptr<Region> whole_file_region = get_parent();
508 if (whole_file_region) {
509 if (_position == whole_file_region->position() + _start) {
518 Region::move_to_natural_position ()
520 boost::shared_ptr<Playlist> pl (playlist());
526 boost::shared_ptr<Region> whole_file_region = get_parent();
528 if (whole_file_region) {
529 set_position (whole_file_region->position() + _start);
534 Region::special_set_position (framepos_t pos)
536 /* this is used when creating a whole file region as
537 a way to store its "natural" or "captured" position.
540 _position = _position;
545 Region::set_position_lock_style (PositionLockStyle ps)
547 if (_position_lock_style != ps) {
549 boost::shared_ptr<Playlist> pl (playlist());
551 _position_lock_style = ps;
553 if (_position_lock_style == MusicTime) {
554 _session.tempo_map().bbt_time (_position, _bbt_time);
557 send_change (Properties::position_lock_style);
562 Region::update_after_tempo_map_change ()
564 boost::shared_ptr<Playlist> pl (playlist());
566 if (!pl || _position_lock_style != MusicTime) {
570 TempoMap& map (_session.tempo_map());
571 framepos_t pos = map.frame_time (_bbt_time);
572 set_position_internal (pos, false);
574 /* do this even if the position is the same. this helps out
575 a GUI that has moved its representation already.
577 send_change (Properties::position);
581 Region::set_position (framepos_t pos)
587 set_position_internal (pos, true);
589 /* do this even if the position is the same. this helps out
590 a GUI that has moved its representation already.
592 send_change (Properties::position);
597 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
599 if (_position != pos) {
600 _last_position = _position;
603 /* check that the new _position wouldn't make the current
604 length impossible - if so, change the length.
606 XXX is this the right thing to do?
609 if (max_framepos - _length < _position) {
610 _last_length = _length;
611 _length = max_framepos - _position;
614 if (allow_bbt_recompute) {
615 recompute_position_from_lock_style ();
618 //invalidate_transients ();
623 Region::set_position_on_top (framepos_t pos)
629 if (_position != pos) {
630 set_position_internal (pos, true);
633 boost::shared_ptr<Playlist> pl (playlist());
636 pl->raise_region_to_top (shared_from_this ());
639 /* do this even if the position is the same. this helps out
640 a GUI that has moved its representation already.
642 send_change (Properties::position);
646 Region::recompute_position_from_lock_style ()
648 if (_position_lock_style == MusicTime) {
649 _session.tempo_map().bbt_time (_position, _bbt_time);
654 Region::nudge_position (frameoffset_t n)
664 framepos_t new_position = _position;
667 if (_position > max_framepos - n) {
668 new_position = max_framepos;
673 if (_position < -n) {
680 set_position_internal (new_position, true);
682 send_change (Properties::position);
686 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
688 _ancestral_length = l;
689 _ancestral_start = s;
695 Region::set_start (framepos_t pos)
697 if (locked() || position_locked()) {
700 /* This just sets the start, nothing else. It effectively shifts
701 the contents of the Region within the overall extent of the Source,
702 without changing the Region's position or length
707 if (!verify_start (pos)) {
714 invalidate_transients ();
716 send_change (Properties::start);
721 Region::trim_start (framepos_t new_position)
723 if (locked() || position_locked()) {
726 framepos_t new_start;
727 frameoffset_t const start_shift = new_position - _position;
729 if (start_shift > 0) {
731 if (_start > max_framepos - start_shift) {
732 new_start = max_framepos;
734 new_start = _start + start_shift;
737 if (!verify_start (new_start)) {
741 } else if (start_shift < 0) {
743 if (_start < -start_shift) {
746 new_start = _start + start_shift;
753 if (new_start == _start) {
761 send_change (Properties::start);
765 Region::trim_front (framepos_t new_position)
767 modify_front (new_position, false);
771 Region::cut_front (framepos_t new_position)
773 modify_front (new_position, true);
777 Region::cut_end (framepos_t new_endpoint)
779 modify_end (new_endpoint, true);
783 Region::modify_front (framepos_t new_position, bool reset_fade)
789 framepos_t end = last_frame();
790 framepos_t source_zero;
792 if (_position > _start) {
793 source_zero = _position - _start;
795 source_zero = 0; // its actually negative, but this will work for us
798 if (new_position < end) { /* can't trim it zero or negative length */
800 framecnt_t newlen = 0;
801 framepos_t delta = 0;
803 if (!can_trim_start_before_source_start ()) {
804 /* can't trim it back past where source position zero is located */
805 new_position = max (new_position, source_zero);
808 if (new_position > _position) {
809 newlen = _length - (new_position - _position);
810 delta = -1 * (new_position - _position);
812 newlen = _length + (_position - new_position);
813 delta = _position - new_position;
816 trim_to_internal (new_position, newlen);
819 _right_of_split = true;
822 if (!property_changes_suspended()) {
823 recompute_at_start ();
826 if (_transients.size() > 0){
827 adjust_transients(delta);
833 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
839 if (new_endpoint > _position) {
840 trim_to_internal (_position, new_endpoint - _position);
842 _left_of_split = true;
844 if (!property_changes_suspended()) {
850 /** @param new_endpoint New region end point, such that, for example,
851 * a region at 0 of length 10 has an endpoint of 9.
855 Region::trim_end (framepos_t new_endpoint)
857 modify_end (new_endpoint, false);
861 Region::trim_to (framepos_t position, framecnt_t length)
867 trim_to_internal (position, length);
869 if (!property_changes_suspended()) {
870 recompute_at_start ();
876 Region::trim_to_internal (framepos_t position, framecnt_t length)
878 framepos_t new_start;
884 frameoffset_t const start_shift = position - _position;
886 if (start_shift > 0) {
888 if (_start > max_framepos - start_shift) {
889 new_start = max_framepos;
891 new_start = _start + start_shift;
894 } else if (start_shift < 0) {
896 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
899 new_start = _start + start_shift;
906 if (!verify_start_and_length (new_start, length)) {
910 PropertyChange what_changed;
912 if (_start != new_start) {
914 what_changed.add (Properties::start);
917 /* Set position before length, otherwise for MIDI regions this bad thing happens:
918 * 1. we call set_length_internal; length in beats is computed using the region's current
919 * (soon-to-be old) position
920 * 2. we call set_position_internal; position is set and length in frames re-computed using
921 * length in beats from (1) but at the new position, which is wrong if the region
922 * straddles a tempo/meter change.
925 if (_position != position) {
926 if (!property_changes_suspended()) {
927 _last_position = _position;
929 set_position_internal (position, true);
930 what_changed.add (Properties::position);
933 if (_length != length) {
934 if (!property_changes_suspended()) {
935 _last_length = _length;
937 set_length_internal (length);
938 what_changed.add (Properties::length);
943 PropertyChange start_and_length;
945 start_and_length.add (Properties::start);
946 start_and_length.add (Properties::length);
948 if (what_changed.contains (start_and_length)) {
952 if (!what_changed.empty()) {
953 send_change (what_changed);
958 Region::set_hidden (bool yn)
960 if (hidden() != yn) {
962 send_change (Properties::hidden);
967 Region::set_whole_file (bool yn)
970 /* no change signal */
974 Region::set_automatic (bool yn)
977 /* no change signal */
981 Region::set_muted (bool yn)
985 send_change (Properties::muted);
990 Region::set_opaque (bool yn)
992 if (opaque() != yn) {
994 send_change (Properties::opaque);
999 Region::set_locked (bool yn)
1001 if (locked() != yn) {
1003 send_change (Properties::locked);
1008 Region::set_position_locked (bool yn)
1010 if (position_locked() != yn) {
1011 _position_locked = yn;
1012 send_change (Properties::locked);
1016 /** Set the region's sync point.
1017 * @param absolute_pos Session time.
1020 Region::set_sync_position (framepos_t absolute_pos)
1022 /* position within our file */
1023 framepos_t const file_pos = _start + (absolute_pos - _position);
1025 if (file_pos != _sync_position) {
1026 _sync_marked = true;
1027 _sync_position = file_pos;
1028 if (!property_changes_suspended()) {
1032 send_change (Properties::sync_position);
1037 Region::clear_sync_position ()
1039 if (sync_marked()) {
1040 _sync_marked = false;
1041 if (!property_changes_suspended()) {
1045 send_change (Properties::sync_position);
1049 /* @return the sync point relative the first frame of the region */
1051 Region::sync_offset (int& dir) const
1053 if (sync_marked()) {
1054 if (_sync_position > _start) {
1056 return _sync_position - _start;
1059 return _start - _sync_position;
1068 Region::adjust_to_sync (framepos_t pos) const
1071 frameoffset_t offset = sync_offset (sync_dir);
1073 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1082 if (max_framepos - pos > offset) {
1090 /** @return Sync position in session time */
1092 Region::sync_position() const
1094 if (sync_marked()) {
1095 return _position - _start + _sync_position;
1097 /* if sync has not been marked, use the start of the region */
1105 boost::shared_ptr<Playlist> pl (playlist());
1107 pl->raise_region (shared_from_this ());
1114 boost::shared_ptr<Playlist> pl (playlist());
1116 pl->lower_region (shared_from_this ());
1122 Region::raise_to_top ()
1124 boost::shared_ptr<Playlist> pl (playlist());
1126 pl->raise_region_to_top (shared_from_this());
1131 Region::lower_to_bottom ()
1133 boost::shared_ptr<Playlist> pl (playlist());
1135 pl->lower_region_to_bottom (shared_from_this());
1140 Region::set_layer (layer_t l)
1145 send_change (Properties::layer);
1152 XMLNode *node = new XMLNode ("Region");
1155 LocaleGuard lg (X_("POSIX"));
1156 const char* fe = NULL;
1158 add_properties (*node);
1160 id().print (buf, sizeof (buf));
1161 node->add_property ("id", buf);
1162 node->add_property ("type", _type.to_string());
1164 switch (_first_edit) {
1165 case EditChangesNothing:
1168 case EditChangesName:
1174 default: /* should be unreachable but makes g++ happy */
1179 node->add_property ("first-edit", fe);
1181 /* note: flags are stored by derived classes */
1183 if (_position_lock_style != AudioTime) {
1186 node->add_property ("bbt-position", str.str());
1189 for (uint32_t n=0; n < _sources.size(); ++n) {
1190 snprintf (buf2, sizeof(buf2), "source-%d", n);
1191 _sources[n]->id().print (buf, sizeof(buf));
1192 node->add_property (buf2, buf);
1195 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1196 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1197 _master_sources[n]->id().print (buf, sizeof (buf));
1198 node->add_property (buf2, buf);
1201 /* Only store nested sources for the whole-file region that acts
1202 as the parent/root of all regions using it.
1205 if (_whole_file && max_source_level() > 0) {
1207 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1209 /* region is compound - get its playlist and
1210 store that before we list the region that
1214 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1215 nested_node->add_child_nocopy ((*s)->get_state ());
1219 node->add_child_nocopy (*nested_node);
1224 node->add_child_copy (*_extra_xml);
1231 Region::get_state ()
1237 Region::set_state (const XMLNode& node, int version)
1239 PropertyChange what_changed;
1240 return _set_state (node, version, what_changed, true);
1244 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1246 const XMLProperty* prop;
1248 Stateful::save_extra_xml (node);
1250 what_changed = set_values (node);
1254 if (_position_lock_style == MusicTime) {
1255 if ((prop = node.property ("bbt-position")) == 0) {
1256 /* missing BBT info, revert to audio time locking */
1257 _position_lock_style = AudioTime;
1259 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1262 &_bbt_time.ticks) != 3) {
1263 _position_lock_style = AudioTime;
1268 /* fix problems with old sessions corrupted by impossible
1269 values for _stretch or _shift
1271 if (_stretch == 0.0f) {
1275 if (_shift == 0.0f) {
1280 send_change (what_changed);
1283 /* Quick fix for 2.x sessions when region is muted */
1284 if ((prop = node.property (X_("flags")))) {
1285 if (string::npos != prop->value().find("Muted")){
1295 Region::suspend_property_changes ()
1297 Stateful::suspend_property_changes ();
1298 _last_length = _length;
1299 _last_position = _position;
1303 Region::mid_thaw (const PropertyChange& what_changed)
1305 if (what_changed.contains (Properties::length)) {
1306 if (what_changed.contains (Properties::position)) {
1307 recompute_at_start ();
1309 recompute_at_end ();
1314 Region::send_change (const PropertyChange& what_changed)
1316 if (what_changed.empty()) {
1320 Stateful::send_change (what_changed);
1322 if (!Stateful::frozen()) {
1324 /* Try and send a shared_pointer unless this is part of the constructor.
1329 boost::shared_ptr<Region> rptr = shared_from_this();
1330 RegionPropertyChanged (rptr, what_changed);
1332 /* no shared_ptr available, relax; */
1338 Region::set_last_layer_op (uint64_t when)
1340 _last_layer_op = when;
1344 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1346 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1350 Region::equivalent (boost::shared_ptr<const Region> other) const
1352 return _start == other->_start &&
1353 _position == other->_position &&
1354 _length == other->_length;
1358 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1360 return _start == other->_start &&
1361 _length == other->_length;
1365 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1367 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1371 Region::source_deleted (boost::weak_ptr<Source>)
1375 if (!_session.deletion_in_progress()) {
1376 /* this is a very special case: at least one of the region's
1377 sources has bee deleted, so invalidate all references to
1378 ourselves. Do NOT do this during session deletion, because
1379 then we run the risk that this will actually result
1380 in this object being deleted (as refcnt goes to zero)
1381 while emitting DropReferences.
1389 Region::master_source_names ()
1391 SourceList::iterator i;
1393 vector<string> names;
1394 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1395 names.push_back((*i)->name());
1402 Region::set_master_sources (const SourceList& srcs)
1404 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1405 (*i)->dec_use_count ();
1408 _master_sources = srcs;
1409 assert (_sources.size() == _master_sources.size());
1411 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1412 (*i)->inc_use_count ();
1417 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1422 if ((_sources.size() != other->_sources.size()) ||
1423 (_master_sources.size() != other->_master_sources.size())) {
1427 SourceList::const_iterator i;
1428 SourceList::const_iterator io;
1430 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1431 if ((*i)->id() != (*io)->id()) {
1436 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1437 if ((*i)->id() != (*io)->id()) {
1446 Region::source_string () const
1448 //string res = itos(_sources.size());
1451 res << _sources.size() << ":";
1453 SourceList::const_iterator i;
1455 for (i = _sources.begin(); i != _sources.end(); ++i) {
1456 res << (*i)->id() << ":";
1459 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1460 res << (*i)->id() << ":";
1467 Region::uses_source (boost::shared_ptr<const Source> source) const
1469 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1474 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1477 if (ps->playlist()->uses_source (source)) {
1487 Region::uses_source_path (const std::string& path) const
1489 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1490 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1492 if (fs->path() == path) {
1501 Region::source_length(uint32_t n) const
1503 assert (n < _sources.size());
1504 return _sources[n]->length (_position - _start);
1508 Region::verify_length (framecnt_t len)
1510 if (source() && (source()->destructive() || source()->length_mutable())) {
1514 framecnt_t maxlen = 0;
1516 for (uint32_t n = 0; n < _sources.size(); ++n) {
1517 maxlen = max (maxlen, source_length(n) - _start);
1520 len = min (len, maxlen);
1526 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1528 if (source() && (source()->destructive() || source()->length_mutable())) {
1532 framecnt_t maxlen = 0;
1534 for (uint32_t n = 0; n < _sources.size(); ++n) {
1535 maxlen = max (maxlen, source_length(n) - new_start);
1538 new_length = min (new_length, maxlen);
1544 Region::verify_start (framepos_t pos)
1546 if (source() && (source()->destructive() || source()->length_mutable())) {
1550 for (uint32_t n = 0; n < _sources.size(); ++n) {
1551 if (pos > source_length(n) - _length) {
1559 Region::verify_start_mutable (framepos_t& new_start)
1561 if (source() && (source()->destructive() || source()->length_mutable())) {
1565 for (uint32_t n = 0; n < _sources.size(); ++n) {
1566 if (new_start > source_length(n) - _length) {
1567 new_start = source_length(n) - _length;
1573 boost::shared_ptr<Region>
1574 Region::get_parent() const
1576 boost::shared_ptr<Playlist> pl (playlist());
1579 boost::shared_ptr<Region> r;
1580 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1582 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1583 return boost::static_pointer_cast<Region> (r);
1587 return boost::shared_ptr<Region>();
1591 Region::apply (Filter& filter, Progress* progress)
1593 return filter.run (shared_from_this(), progress);
1598 Region::invalidate_transients ()
1600 _valid_transients = false;
1601 _transients.clear ();
1603 send_change (PropertyChange (Properties::valid_transients));
1607 Region::drop_sources ()
1609 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1610 (*i)->dec_use_count ();
1615 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1616 (*i)->dec_use_count ();
1619 _master_sources.clear ();
1623 Region::use_sources (SourceList const & s)
1625 set<boost::shared_ptr<Source> > unique_srcs;
1627 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1629 _sources.push_back (*i);
1630 (*i)->inc_use_count ();
1631 _master_sources.push_back (*i);
1632 (*i)->inc_use_count ();
1634 /* connect only once to DropReferences, even if sources are replicated
1637 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1638 unique_srcs.insert (*i);
1639 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1645 Region::can_trim () const
1647 CanTrim ct = CanTrim (0);
1653 /* if not locked, we can always move the front later, and the end earlier
1656 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1658 if (start() != 0 || can_trim_start_before_source_start ()) {
1659 ct = CanTrim (ct | FrontTrimEarlier);
1662 if (!_sources.empty()) {
1663 if ((start() + length()) < _sources.front()->length (0)) {
1664 ct = CanTrim (ct | EndTrimLater);
1672 Region::max_source_level () const
1676 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1677 lvl = max (lvl, (*i)->level());
1684 Region::is_compound () const
1686 return max_source_level() > 0;