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 _sync_marked (Properties::sync_marked, false) \
164 , _left_of_split (Properties::left_of_split, false) \
165 , _right_of_split (Properties::right_of_split, false) \
166 , _valid_transients (Properties::valid_transients, false) \
167 , _start (Properties::start, (s)) \
168 , _length (Properties::length, (l)) \
169 , _position (Properties::position, 0) \
170 , _sync_position (Properties::sync_position, (s)) \
171 , _layer (Properties::layer, 0) \
172 , _muted (Properties::muted, false) \
173 , _opaque (Properties::opaque, true) \
174 , _locked (Properties::locked, false) \
175 , _automatic (Properties::automatic, false) \
176 , _whole_file (Properties::whole_file, false) \
177 , _import (Properties::import, false) \
178 , _external (Properties::external, false) \
179 , _hidden (Properties::hidden, false) \
180 , _position_locked (Properties::position_locked, false) \
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 _sync_marked (Properties::sync_marked, other->_sync_marked) \
189 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
190 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
191 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
192 , _start(Properties::start, other->_start) \
193 , _length(Properties::length, other->_length) \
194 , _position(Properties::position, other->_position) \
195 , _sync_position(Properties::sync_position, other->_sync_position) \
196 , _layer (Properties::layer, other->_layer) \
197 , _muted (Properties::muted, other->_muted) \
198 , _opaque (Properties::opaque, other->_opaque) \
199 , _locked (Properties::locked, other->_locked) \
200 , _automatic (Properties::automatic, other->_automatic) \
201 , _whole_file (Properties::whole_file, other->_whole_file) \
202 , _import (Properties::import, other->_import) \
203 , _external (Properties::external, other->_external) \
204 , _hidden (Properties::hidden, other->_hidden) \
205 , _position_locked (Properties::position_locked, other->_position_locked) \
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 /* We emit a change of Properties::position even if the position hasn't changed
600 (see Region::set_position), so we must always set this up so that
601 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
603 _last_position = _position;
605 if (_position != pos) {
608 /* check that the new _position wouldn't make the current
609 length impossible - if so, change the length.
611 XXX is this the right thing to do?
614 if (max_framepos - _length < _position) {
615 _last_length = _length;
616 _length = max_framepos - _position;
619 if (allow_bbt_recompute) {
620 recompute_position_from_lock_style ();
623 //invalidate_transients ();
628 Region::recompute_position_from_lock_style ()
630 if (_position_lock_style == MusicTime) {
631 _session.tempo_map().bbt_time (_position, _bbt_time);
636 Region::nudge_position (frameoffset_t n)
646 framepos_t new_position = _position;
649 if (_position > max_framepos - n) {
650 new_position = max_framepos;
655 if (_position < -n) {
662 set_position_internal (new_position, true);
664 send_change (Properties::position);
668 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
670 _ancestral_length = l;
671 _ancestral_start = s;
677 Region::set_start (framepos_t pos)
679 if (locked() || position_locked()) {
682 /* This just sets the start, nothing else. It effectively shifts
683 the contents of the Region within the overall extent of the Source,
684 without changing the Region's position or length
689 if (!verify_start (pos)) {
696 invalidate_transients ();
698 send_change (Properties::start);
703 Region::trim_start (framepos_t new_position)
705 if (locked() || position_locked()) {
708 framepos_t new_start;
709 frameoffset_t const start_shift = new_position - _position;
711 if (start_shift > 0) {
713 if (_start > max_framepos - start_shift) {
714 new_start = max_framepos;
716 new_start = _start + start_shift;
719 if (!verify_start (new_start)) {
723 } else if (start_shift < 0) {
725 if (_start < -start_shift) {
728 new_start = _start + start_shift;
735 if (new_start == _start) {
743 send_change (Properties::start);
747 Region::trim_front (framepos_t new_position)
749 modify_front (new_position, false);
753 Region::cut_front (framepos_t new_position)
755 modify_front (new_position, true);
759 Region::cut_end (framepos_t new_endpoint)
761 modify_end (new_endpoint, true);
765 Region::modify_front (framepos_t new_position, bool reset_fade)
771 framepos_t end = last_frame();
772 framepos_t source_zero;
774 if (_position > _start) {
775 source_zero = _position - _start;
777 source_zero = 0; // its actually negative, but this will work for us
780 if (new_position < end) { /* can't trim it zero or negative length */
782 framecnt_t newlen = 0;
783 framepos_t delta = 0;
785 if (!can_trim_start_before_source_start ()) {
786 /* can't trim it back past where source position zero is located */
787 new_position = max (new_position, source_zero);
790 if (new_position > _position) {
791 newlen = _length - (new_position - _position);
792 delta = -1 * (new_position - _position);
794 newlen = _length + (_position - new_position);
795 delta = _position - new_position;
798 trim_to_internal (new_position, newlen);
801 _right_of_split = true;
804 if (!property_changes_suspended()) {
805 recompute_at_start ();
808 if (_transients.size() > 0){
809 adjust_transients(delta);
815 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
821 if (new_endpoint > _position) {
822 trim_to_internal (_position, new_endpoint - _position);
824 _left_of_split = true;
826 if (!property_changes_suspended()) {
832 /** @param new_endpoint New region end point, such that, for example,
833 * a region at 0 of length 10 has an endpoint of 9.
837 Region::trim_end (framepos_t new_endpoint)
839 modify_end (new_endpoint, false);
843 Region::trim_to (framepos_t position, framecnt_t length)
849 trim_to_internal (position, length);
851 if (!property_changes_suspended()) {
852 recompute_at_start ();
858 Region::trim_to_internal (framepos_t position, framecnt_t length)
860 framepos_t new_start;
866 frameoffset_t const start_shift = position - _position;
868 if (start_shift > 0) {
870 if (_start > max_framepos - start_shift) {
871 new_start = max_framepos;
873 new_start = _start + start_shift;
876 } else if (start_shift < 0) {
878 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
881 new_start = _start + start_shift;
888 if (!verify_start_and_length (new_start, length)) {
892 PropertyChange what_changed;
894 if (_start != new_start) {
896 what_changed.add (Properties::start);
899 /* Set position before length, otherwise for MIDI regions this bad thing happens:
900 * 1. we call set_length_internal; length in beats is computed using the region's current
901 * (soon-to-be old) position
902 * 2. we call set_position_internal; position is set and length in frames re-computed using
903 * length in beats from (1) but at the new position, which is wrong if the region
904 * straddles a tempo/meter change.
907 if (_position != position) {
908 if (!property_changes_suspended()) {
909 _last_position = _position;
911 set_position_internal (position, true);
912 what_changed.add (Properties::position);
915 if (_length != length) {
916 if (!property_changes_suspended()) {
917 _last_length = _length;
919 set_length_internal (length);
920 what_changed.add (Properties::length);
925 PropertyChange start_and_length;
927 start_and_length.add (Properties::start);
928 start_and_length.add (Properties::length);
930 if (what_changed.contains (start_and_length)) {
934 if (!what_changed.empty()) {
935 send_change (what_changed);
940 Region::set_hidden (bool yn)
942 if (hidden() != yn) {
944 send_change (Properties::hidden);
949 Region::set_whole_file (bool yn)
952 /* no change signal */
956 Region::set_automatic (bool yn)
959 /* no change signal */
963 Region::set_muted (bool yn)
967 send_change (Properties::muted);
972 Region::set_opaque (bool yn)
974 if (opaque() != yn) {
976 send_change (Properties::opaque);
981 Region::set_locked (bool yn)
983 if (locked() != yn) {
985 send_change (Properties::locked);
990 Region::set_position_locked (bool yn)
992 if (position_locked() != yn) {
993 _position_locked = yn;
994 send_change (Properties::locked);
998 /** Set the region's sync point.
999 * @param absolute_pos Session time.
1002 Region::set_sync_position (framepos_t absolute_pos)
1004 /* position within our file */
1005 framepos_t const file_pos = _start + (absolute_pos - _position);
1007 if (file_pos != _sync_position) {
1008 _sync_marked = true;
1009 _sync_position = file_pos;
1010 if (!property_changes_suspended()) {
1014 send_change (Properties::sync_position);
1019 Region::clear_sync_position ()
1021 if (sync_marked()) {
1022 _sync_marked = false;
1023 if (!property_changes_suspended()) {
1027 send_change (Properties::sync_position);
1031 /* @return the sync point relative the first frame of the region */
1033 Region::sync_offset (int& dir) const
1035 if (sync_marked()) {
1036 if (_sync_position > _start) {
1038 return _sync_position - _start;
1041 return _start - _sync_position;
1050 Region::adjust_to_sync (framepos_t pos) const
1053 frameoffset_t offset = sync_offset (sync_dir);
1055 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1064 if (max_framepos - pos > offset) {
1072 /** @return Sync position in session time */
1074 Region::sync_position() const
1076 if (sync_marked()) {
1077 return _position - _start + _sync_position;
1079 /* if sync has not been marked, use the start of the region */
1087 boost::shared_ptr<Playlist> pl (playlist());
1089 pl->raise_region (shared_from_this ());
1096 boost::shared_ptr<Playlist> pl (playlist());
1098 pl->lower_region (shared_from_this ());
1104 Region::raise_to_top ()
1106 boost::shared_ptr<Playlist> pl (playlist());
1108 pl->raise_region_to_top (shared_from_this());
1113 Region::lower_to_bottom ()
1115 boost::shared_ptr<Playlist> pl (playlist());
1117 pl->lower_region_to_bottom (shared_from_this());
1122 Region::set_layer (layer_t l)
1127 send_change (Properties::layer);
1134 XMLNode *node = new XMLNode ("Region");
1137 LocaleGuard lg (X_("POSIX"));
1138 const char* fe = NULL;
1140 add_properties (*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::set_last_layer_op (uint64_t when)
1322 _last_layer_op = when;
1326 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1328 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1332 Region::equivalent (boost::shared_ptr<const Region> other) const
1334 return _start == other->_start &&
1335 _position == other->_position &&
1336 _length == other->_length;
1340 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1342 return _start == other->_start &&
1343 _length == other->_length;
1347 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1349 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1353 Region::source_deleted (boost::weak_ptr<Source>)
1357 if (!_session.deletion_in_progress()) {
1358 /* this is a very special case: at least one of the region's
1359 sources has bee deleted, so invalidate all references to
1360 ourselves. Do NOT do this during session deletion, because
1361 then we run the risk that this will actually result
1362 in this object being deleted (as refcnt goes to zero)
1363 while emitting DropReferences.
1371 Region::master_source_names ()
1373 SourceList::iterator i;
1375 vector<string> names;
1376 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1377 names.push_back((*i)->name());
1384 Region::set_master_sources (const SourceList& srcs)
1386 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1387 (*i)->dec_use_count ();
1390 _master_sources = srcs;
1391 assert (_sources.size() == _master_sources.size());
1393 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1394 (*i)->inc_use_count ();
1399 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1404 if ((_sources.size() != other->_sources.size()) ||
1405 (_master_sources.size() != other->_master_sources.size())) {
1409 SourceList::const_iterator i;
1410 SourceList::const_iterator io;
1412 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1413 if ((*i)->id() != (*io)->id()) {
1418 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1419 if ((*i)->id() != (*io)->id()) {
1428 Region::source_string () const
1430 //string res = itos(_sources.size());
1433 res << _sources.size() << ":";
1435 SourceList::const_iterator i;
1437 for (i = _sources.begin(); i != _sources.end(); ++i) {
1438 res << (*i)->id() << ":";
1441 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1442 res << (*i)->id() << ":";
1449 Region::uses_source (boost::shared_ptr<const Source> source) const
1451 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1456 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1459 if (ps->playlist()->uses_source (source)) {
1469 Region::source_length(uint32_t n) const
1471 assert (n < _sources.size());
1472 return _sources[n]->length (_position - _start);
1476 Region::verify_length (framecnt_t len)
1478 if (source() && (source()->destructive() || source()->length_mutable())) {
1482 framecnt_t maxlen = 0;
1484 for (uint32_t n = 0; n < _sources.size(); ++n) {
1485 maxlen = max (maxlen, source_length(n) - _start);
1488 len = min (len, maxlen);
1494 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1496 if (source() && (source()->destructive() || source()->length_mutable())) {
1500 framecnt_t maxlen = 0;
1502 for (uint32_t n = 0; n < _sources.size(); ++n) {
1503 maxlen = max (maxlen, source_length(n) - new_start);
1506 new_length = min (new_length, maxlen);
1512 Region::verify_start (framepos_t pos)
1514 if (source() && (source()->destructive() || source()->length_mutable())) {
1518 for (uint32_t n = 0; n < _sources.size(); ++n) {
1519 if (pos > source_length(n) - _length) {
1527 Region::verify_start_mutable (framepos_t& new_start)
1529 if (source() && (source()->destructive() || source()->length_mutable())) {
1533 for (uint32_t n = 0; n < _sources.size(); ++n) {
1534 if (new_start > source_length(n) - _length) {
1535 new_start = source_length(n) - _length;
1541 boost::shared_ptr<Region>
1542 Region::get_parent() const
1544 boost::shared_ptr<Playlist> pl (playlist());
1547 boost::shared_ptr<Region> r;
1548 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1550 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1551 return boost::static_pointer_cast<Region> (r);
1555 return boost::shared_ptr<Region>();
1559 Region::apply (Filter& filter, Progress* progress)
1561 return filter.run (shared_from_this(), progress);
1566 Region::invalidate_transients ()
1568 _valid_transients = false;
1569 _transients.clear ();
1571 send_change (PropertyChange (Properties::valid_transients));
1575 Region::drop_sources ()
1577 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1578 (*i)->dec_use_count ();
1583 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1584 (*i)->dec_use_count ();
1587 _master_sources.clear ();
1591 Region::use_sources (SourceList const & s)
1593 set<boost::shared_ptr<Source> > unique_srcs;
1595 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1597 _sources.push_back (*i);
1598 (*i)->inc_use_count ();
1599 _master_sources.push_back (*i);
1600 (*i)->inc_use_count ();
1602 /* connect only once to DropReferences, even if sources are replicated
1605 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1606 unique_srcs.insert (*i);
1607 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1613 Region::can_trim () const
1615 CanTrim ct = CanTrim (0);
1621 /* if not locked, we can always move the front later, and the end earlier
1624 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1626 if (start() != 0 || can_trim_start_before_source_start ()) {
1627 ct = CanTrim (ct | FrontTrimEarlier);
1630 if (!_sources.empty()) {
1631 if ((start() + length()) < _sources.front()->length (0)) {
1632 ct = CanTrim (ct | EndTrimLater);
1640 Region::max_source_level () const
1644 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1645 lvl = max (lvl, (*i)->level());
1652 Region::is_compound () const
1654 return max_source_level() > 0;
1658 Region::post_set (const PropertyChange& pc)
1660 if (pc.contains (Properties::position)) {
1661 recompute_position_from_lock_style ();