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/region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/source.h"
36 #include "ardour/tempo.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/filter.h"
39 #include "ardour/profile.h"
40 #include "ardour/utils.h"
45 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> automatic;
54 PBD::PropertyDescriptor<bool> whole_file;
55 PBD::PropertyDescriptor<bool> import;
56 PBD::PropertyDescriptor<bool> external;
57 PBD::PropertyDescriptor<bool> sync_marked;
58 PBD::PropertyDescriptor<bool> left_of_split;
59 PBD::PropertyDescriptor<bool> right_of_split;
60 PBD::PropertyDescriptor<bool> hidden;
61 PBD::PropertyDescriptor<bool> position_locked;
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;
75 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
78 Region::make_property_quarks ()
80 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
81 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
82 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
83 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
84 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
86 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
88 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
90 Properties::import.property_id = g_quark_from_static_string (X_("import"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
92 Properties::external.property_id = g_quark_from_static_string (X_("external"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
94 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
96 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
98 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
100 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
102 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
104 Properties::start.property_id = g_quark_from_static_string (X_("start"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
106 Properties::length.property_id = g_quark_from_static_string (X_("length"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
108 Properties::position.property_id = g_quark_from_static_string (X_("position"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
110 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
112 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
114 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
116 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
118 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
120 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
122 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
127 Region::register_properties ()
129 _xml_node_name = X_("Region");
131 add_property (_muted);
132 add_property (_opaque);
133 add_property (_locked);
134 add_property (_automatic);
135 add_property (_whole_file);
136 add_property (_import);
137 add_property (_external);
138 add_property (_sync_marked);
139 add_property (_left_of_split);
140 add_property (_right_of_split);
141 add_property (_hidden);
142 add_property (_position_locked);
143 add_property (_start);
144 add_property (_length);
145 add_property (_position);
146 add_property (_sync_position);
147 add_property (_layer);
148 add_property (_ancestral_start);
149 add_property (_ancestral_length);
150 add_property (_stretch);
151 add_property (_shift);
152 add_property (_position_lock_style);
155 #define REGION_DEFAULT_STATE(s,l) \
156 _muted (Properties::muted, false) \
157 , _opaque (Properties::opaque, true) \
158 , _locked (Properties::locked, false) \
159 , _automatic (Properties::automatic, false) \
160 , _whole_file (Properties::whole_file, false) \
161 , _import (Properties::import, false) \
162 , _external (Properties::external, false) \
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 , _hidden (Properties::hidden, false) \
167 , _position_locked (Properties::position_locked, false) \
168 , _start (Properties::start, (s)) \
169 , _length (Properties::length, (l)) \
170 , _position (Properties::position, 0) \
171 , _sync_position (Properties::sync_position, (s)) \
172 , _layer (Properties::layer, 0) \
173 , _ancestral_start (Properties::ancestral_start, (s)) \
174 , _ancestral_length (Properties::ancestral_length, (l)) \
175 , _stretch (Properties::stretch, 1.0) \
176 , _shift (Properties::shift, 1.0) \
177 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
179 #define REGION_COPY_STATE(other) \
180 _muted (other->_muted) \
181 , _opaque (other->_opaque) \
182 , _locked (other->_locked) \
183 , _automatic (other->_automatic) \
184 , _whole_file (other->_whole_file) \
185 , _import (other->_import) \
186 , _external (other->_external) \
187 , _sync_marked (other->_sync_marked) \
188 , _left_of_split (other->_left_of_split) \
189 , _right_of_split (other->_right_of_split) \
190 , _hidden (other->_hidden) \
191 , _position_locked (other->_position_locked) \
192 , _start(other->_start) \
193 , _length(other->_length) \
194 , _position(other->_position) \
195 , _sync_position(other->_sync_position) \
196 , _layer (other->_layer) \
197 , _ancestral_start (other->_ancestral_start) \
198 , _ancestral_length (other->_ancestral_length) \
199 , _stretch (other->_stretch) \
200 , _shift (other->_shift) \
201 , _position_lock_style (other->_position_lock_style)
203 /* derived-from-derived constructor (no sources in constructor) */
204 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
205 : SessionObject(s, name)
207 , REGION_DEFAULT_STATE(start,length)
208 , _last_length (length)
210 , _first_edit (EditChangesNothing)
211 , _read_data_count(0)
213 , _pending_explicit_relayer (false)
215 register_properties ();
217 /* no sources at this point */
220 /** Basic Region constructor (many sources) */
221 Region::Region (const SourceList& srcs)
222 : SessionObject(srcs.front()->session(), "toBeRenamed")
223 , _type (srcs.front()->type())
224 , REGION_DEFAULT_STATE(0,0)
227 , _first_edit (EditChangesNothing)
228 , _valid_transients(false)
229 , _read_data_count(0)
231 , _pending_explicit_relayer (false)
233 register_properties ();
235 _type = srcs.front()->type();
239 assert(_sources.size() > 0);
240 assert (_type == srcs.front()->type());
243 /** Create a new Region from part of an existing one, starting at one of two places:
245 if @param offset_relative is true, then the start within @param other is given by @param offset
246 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
248 if @param offset_relative is false, then the start within the source is given @param offset.
250 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
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)
257 , _valid_transients(false)
258 , _read_data_count(0)
260 , _pending_explicit_relayer (false)
263 register_properties ();
265 /* override state that may have been incorrectly inherited from the other region
273 use_sources (other->_sources);
275 if (!offset_relative) {
277 /* not sure why we do this, but its a hangover from ardour before
278 property lists. this would be nice to remove.
281 _position_lock_style = other->_position_lock_style;
282 _first_edit = other->_first_edit;
288 /* sync pos is relative to start of file. our start-in-file is now zero,
289 so set our sync position to whatever the the difference between
290 _start and _sync_pos was in the other region.
292 result is that our new sync pos points to the same point in our source(s)
293 as the sync in the other region did in its source(s).
295 since we start at zero in our source(s), it is not possible to use a sync point that
296 is before the start. reset it to _start if that was true in the other region.
299 if (other->sync_marked()) {
300 if (other->_start < other->_sync_position) {
301 /* sync pos was after the start point of the other region */
302 _sync_position = other->_sync_position - other->_start;
304 /* sync pos was before the start point of the other region. not possible here. */
305 _sync_marked = false;
306 _sync_position = _start;
309 _sync_marked = false;
310 _sync_position = _start;
313 /* XXX do something else ! */
314 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
321 _start = other->_start + offset;
323 /* if the other region had a distinct sync point
324 set, then continue to use it as best we can.
325 otherwise, reset sync point back to start.
328 if (other->sync_marked()) {
329 if (other->_sync_position < _start) {
330 _sync_marked = false;
331 _sync_position = _start;
333 _sync_position = other->_sync_position;
336 _sync_marked = false;
337 _sync_position = _start;
341 if (Profile->get_sae()) {
342 /* reset sync point to start if its ended up
343 outside region bounds.
346 if (_sync_position < _start || _sync_position >= _start + _length) {
347 _sync_marked = false;
348 _sync_position = _start;
352 assert (_type == other->data_type());
355 /** Create a copy of @param other but with different sources. Used by filters */
356 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
357 : SessionObject (other->session(), other->name())
358 , _type (srcs.front()->type())
359 , REGION_COPY_STATE (other)
360 , _last_length (other->_last_length)
361 , _last_position (other->_last_position)
362 , _first_edit (EditChangesID)
363 , _valid_transients (false)
364 , _read_data_count (0)
365 , _last_layer_op (other->_last_layer_op)
366 , _pending_explicit_relayer (false)
368 register_properties ();
371 _position_locked = false;
373 other->_first_edit = EditChangesName;
375 if (other->_extra_xml) {
376 _extra_xml = new XMLNode (*other->_extra_xml);
382 assert(_sources.size() > 0);
385 /** Simple "copy" constructor */
386 Region::Region (boost::shared_ptr<const Region> other)
387 : SessionObject(other->session(), other->name())
388 , _type(other->data_type())
389 , REGION_COPY_STATE (other)
390 , _last_length (other->_last_length)
391 , _last_position (other->_last_position)
392 , _first_edit (EditChangesID)
393 , _valid_transients(false)
394 , _read_data_count(0)
395 , _last_layer_op(other->_last_layer_op)
396 , _pending_explicit_relayer (false)
398 register_properties ();
401 _position_locked = false;
403 other->_first_edit = EditChangesName;
405 if (other->_extra_xml) {
406 _extra_xml = new XMLNode (*other->_extra_xml);
411 use_sources (other->_sources);
412 assert(_sources.size() > 0);
417 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
422 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
424 _playlist = wpl.lock();
428 Region::set_name (const std::string& str)
431 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
432 assert(_name == str);
433 send_change (Properties::name);
440 Region::set_length (framecnt_t len, void */*src*/)
442 //cerr << "Region::set_length() len = " << len << endl;
447 if (_length != len && len != 0) {
449 /* check that the current _position wouldn't make the new
453 if (max_frames - len < _position) {
457 if (!verify_length (len)) {
462 _last_length = _length;
467 invalidate_transients ();
469 if (!property_changes_suspended()) {
473 send_change (Properties::length);
478 Region::maybe_uncopy ()
480 /* this does nothing but marked a semantic moment once upon a time */
484 Region::first_edit ()
486 boost::shared_ptr<Playlist> pl (playlist());
488 if (_first_edit != EditChangesNothing && pl) {
490 _name = RegionFactory::new_region_name (_name);
491 _first_edit = EditChangesNothing;
493 send_change (Properties::name);
494 RegionFactory::CheckNewRegion (shared_from_this());
499 Region::at_natural_position () const
501 boost::shared_ptr<Playlist> pl (playlist());
507 boost::shared_ptr<Region> whole_file_region = get_parent();
509 if (whole_file_region) {
510 if (_position == whole_file_region->position() + _start) {
519 Region::move_to_natural_position (void *src)
521 boost::shared_ptr<Playlist> pl (playlist());
527 boost::shared_ptr<Region> whole_file_region = get_parent();
529 if (whole_file_region) {
530 set_position (whole_file_region->position() + _start, src);
535 Region::special_set_position (framepos_t pos)
537 /* this is used when creating a whole file region as
538 a way to store its "natural" or "captured" position.
541 _position = _position;
546 Region::set_position_lock_style (PositionLockStyle ps)
548 if (_position_lock_style != ps) {
550 boost::shared_ptr<Playlist> pl (playlist());
556 _position_lock_style = ps;
558 if (_position_lock_style == MusicTime) {
559 _session.tempo_map().bbt_time (_position, _bbt_time);
562 send_change (Properties::position_lock_style);
567 Region::update_position_after_tempo_map_change ()
569 boost::shared_ptr<Playlist> pl (playlist());
571 if (!pl || _position_lock_style != MusicTime) {
575 TempoMap& map (_session.tempo_map());
576 framepos_t pos = map.frame_time (_bbt_time);
577 set_position_internal (pos, false);
581 Region::set_position (framepos_t pos, void* /*src*/)
587 set_position_internal (pos, true);
591 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
593 if (_position != pos) {
594 _last_position = _position;
597 /* check that the new _position wouldn't make the current
598 length impossible - if so, change the length.
600 XXX is this the right thing to do?
603 if (max_frames - _length < _position) {
604 _last_length = _length;
605 _length = max_frames - _position;
608 if (allow_bbt_recompute) {
609 recompute_position_from_lock_style ();
612 invalidate_transients ();
615 /* do this even if the position is the same. this helps out
616 a GUI that has moved its representation already.
619 send_change (Properties::position);
623 Region::set_position_on_top (framepos_t pos, void* /*src*/)
629 if (_position != pos) {
630 _last_position = _position;
634 boost::shared_ptr<Playlist> pl (playlist());
637 pl->raise_region_to_top (shared_from_this ());
640 /* do this even if the position is the same. this helps out
641 a GUI that has moved its representation already.
644 send_change (Properties::position);
648 Region::recompute_position_from_lock_style ()
650 if (_position_lock_style == MusicTime) {
651 _session.tempo_map().bbt_time (_position, _bbt_time);
656 Region::nudge_position (frameoffset_t n, void* /*src*/)
666 _last_position = _position;
669 if (_position > max_frames - n) {
670 _position = max_frames;
675 if (_position < -n) {
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, void* /*src*/)
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, void */*src*/)
723 if (locked() || position_locked()) {
726 framepos_t new_start;
727 frameoffset_t start_shift;
729 if (new_position > _position) {
730 start_shift = new_position - _position;
732 start_shift = -(_position - new_position);
735 if (start_shift > 0) {
737 if (_start > max_frames - start_shift) {
738 new_start = max_frames;
740 new_start = _start + start_shift;
743 if (!verify_start (new_start)) {
747 } else if (start_shift < 0) {
749 if (_start < -start_shift) {
752 new_start = _start + start_shift;
758 if (new_start == _start) {
766 send_change (Properties::start);
770 Region::trim_front (framepos_t new_position, void *src)
772 modify_front (new_position, false, src);
776 Region::cut_front (nframes_t new_position, void *src)
778 modify_front (new_position, true, src);
782 Region::cut_end (nframes_t new_endpoint, void *src)
784 modify_end (new_endpoint, true, src);
788 Region::modify_front (nframes_t new_position, bool reset_fade, void *src)
794 nframes_t end = last_frame();
795 nframes_t source_zero;
797 if (_position > _start) {
798 source_zero = _position - _start;
800 source_zero = 0; // its actually negative, but this will work for us
803 if (new_position < end) { /* can't trim it zero or negative length */
807 /* can't trim it back passed where source position zero is located */
809 new_position = max (new_position, source_zero);
811 if (new_position > _position) {
812 newlen = _length - (new_position - _position);
814 newlen = _length + (_position - new_position);
817 trim_to_internal (new_position, newlen, src);
819 _right_of_split = true;
822 if (!property_changes_suspended()) {
823 recompute_at_start ();
829 Region::modify_end (nframes_t new_endpoint, bool reset_fade, void *src)
835 if (new_endpoint > _position) {
836 trim_to_internal (_position, new_endpoint - _position +1, this);
838 _left_of_split = true;
840 if (!property_changes_suspended()) {
846 /** @param new_endpoint New region end point, such that, for example,
847 * a region at 0 of length 10 has an endpoint of 9.
851 Region::trim_end (framepos_t new_endpoint, void* src)
853 modify_end (new_endpoint, false, src);
857 Region::trim_to (framepos_t position, framecnt_t length, void *src)
863 trim_to_internal (position, length, src);
865 if (!property_changes_suspended()) {
866 recompute_at_start ();
872 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
874 frameoffset_t start_shift;
875 framepos_t new_start;
881 if (position > _position) {
882 start_shift = position - _position;
884 start_shift = -(_position - position);
887 if (start_shift > 0) {
889 if (_start > max_frames - start_shift) {
890 new_start = max_frames;
892 new_start = _start + start_shift;
896 } else if (start_shift < 0) {
898 if (_start < -start_shift) {
901 new_start = _start + start_shift;
907 if (!verify_start_and_length (new_start, length)) {
911 PropertyChange what_changed;
913 if (_start != new_start) {
915 what_changed.add (Properties::start);
917 if (_length != length) {
918 if (!property_changes_suspended()) {
919 _last_length = _length;
922 what_changed.add (Properties::length);
924 if (_position != position) {
925 if (!property_changes_suspended()) {
926 _last_position = _position;
928 _position = position;
929 what_changed.add (Properties::position);
934 PropertyChange start_and_length;
936 start_and_length.add (Properties::start);
937 start_and_length.add (Properties::length);
939 if (what_changed.contains (start_and_length)) {
943 if (!what_changed.empty()) {
944 send_change (what_changed);
949 Region::set_hidden (bool yn)
951 if (hidden() != yn) {
953 send_change (Properties::hidden);
958 Region::set_whole_file (bool yn)
961 /* no change signal */
965 Region::set_automatic (bool yn)
968 /* no change signal */
972 Region::set_muted (bool yn)
976 send_change (Properties::muted);
981 Region::set_opaque (bool yn)
983 if (opaque() != yn) {
985 send_change (Properties::opaque);
990 Region::set_locked (bool yn)
992 if (locked() != yn) {
994 send_change (Properties::locked);
999 Region::set_position_locked (bool yn)
1001 if (position_locked() != yn) {
1002 _position_locked = yn;
1003 send_change (Properties::locked);
1008 Region::set_sync_position (framepos_t absolute_pos)
1010 framepos_t const file_pos = _start + (absolute_pos - _position);
1012 if (file_pos != _sync_position) {
1013 _sync_marked = true;
1014 _sync_position = file_pos;
1015 if (!property_changes_suspended()) {
1018 send_change (Properties::sync_position);
1023 Region::clear_sync_position ()
1025 if (sync_marked()) {
1026 _sync_marked = false;
1027 if (!property_changes_suspended()) {
1030 send_change (Properties::sync_position);
1035 Region::sync_offset (int& dir) const
1037 /* returns the sync point relative the first frame of the region */
1039 if (sync_marked()) {
1040 if (_sync_position > _start) {
1042 return _sync_position - _start;
1045 return _start - _sync_position;
1054 Region::adjust_to_sync (framepos_t pos) const
1057 frameoffset_t offset = sync_offset (sync_dir);
1059 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1068 if (max_frames - pos > offset) {
1077 Region::sync_position() const
1079 if (sync_marked()) {
1080 return _sync_position;
1089 boost::shared_ptr<Playlist> pl (playlist());
1091 pl->raise_region (shared_from_this ());
1098 boost::shared_ptr<Playlist> pl (playlist());
1100 pl->lower_region (shared_from_this ());
1106 Region::raise_to_top ()
1108 boost::shared_ptr<Playlist> pl (playlist());
1110 pl->raise_region_to_top (shared_from_this());
1115 Region::lower_to_bottom ()
1117 boost::shared_ptr<Playlist> pl (playlist());
1119 pl->lower_region_to_bottom (shared_from_this());
1124 Region::set_layer (layer_t l)
1129 send_change (Properties::layer);
1134 Region::state (bool full)
1136 XMLNode *node = new XMLNode ("Region");
1139 LocaleGuard lg (X_("POSIX"));
1140 const char* fe = NULL;
1142 add_properties (*node);
1144 _id.print (buf, sizeof (buf));
1145 node->add_property ("id", buf);
1146 node->add_property ("type", _type.to_string());
1148 switch (_first_edit) {
1149 case EditChangesNothing:
1152 case EditChangesName:
1158 default: /* should be unreachable but makes g++ happy */
1163 node->add_property ("first-edit", fe);
1165 /* note: flags are stored by derived classes */
1167 if (_position_lock_style != AudioTime) {
1170 node->add_property ("bbt-position", str.str());
1173 for (uint32_t n=0; n < _sources.size(); ++n) {
1174 snprintf (buf2, sizeof(buf2), "source-%d", n);
1175 _sources[n]->id().print (buf, sizeof(buf));
1176 node->add_property (buf2, buf);
1179 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1180 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1181 _master_sources[n]->id().print (buf, sizeof (buf));
1182 node->add_property (buf2, buf);
1185 if (full && _extra_xml) {
1186 node->add_child_copy (*_extra_xml);
1193 Region::get_state ()
1195 return state (true);
1199 Region::set_state (const XMLNode& node, int version)
1201 PropertyChange what_changed;
1202 return _set_state (node, version, what_changed, true);
1206 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1208 const XMLProperty* prop;
1210 what_changed = set_properties (node);
1212 if ((prop = node.property (X_("id")))) {
1213 _id = prop->value();
1216 if (_position_lock_style == MusicTime) {
1217 if ((prop = node.property ("bbt-position")) == 0) {
1218 /* missing BBT info, revert to audio time locking */
1219 _position_lock_style = AudioTime;
1221 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1224 &_bbt_time.ticks) != 3) {
1225 _position_lock_style = AudioTime;
1230 /* fix problems with old sessions corrupted by impossible
1231 values for _stretch or _shift
1233 if (_stretch == 0.0f) {
1237 if (_shift == 0.0f) {
1241 const XMLNodeList& nlist = node.children();
1243 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1249 if (child->name () == "Extra") {
1251 _extra_xml = new XMLNode (*child);
1257 send_change (what_changed);
1260 /* Quick fix for 2.x sessions when region is muted */
1261 if ((prop = node.property (X_("flags")))) {
1262 if (string::npos != prop->value().find("Muted")){
1272 Region::suspend_property_changes ()
1274 Stateful::suspend_property_changes ();
1275 _last_length = _length;
1276 _last_position = _position;
1280 Region::mid_thaw (const PropertyChange& what_changed)
1282 if (what_changed.contains (Properties::length)) {
1283 if (what_changed.contains (Properties::position)) {
1284 recompute_at_start ();
1286 recompute_at_end ();
1291 Region::send_change (const PropertyChange& what_changed)
1293 if (what_changed.empty()) {
1297 Stateful::send_change (what_changed);
1299 if (!_no_property_changes) {
1301 /* Try and send a shared_pointer unless this is part of the constructor.
1306 boost::shared_ptr<Region> rptr = shared_from_this();
1307 RegionPropertyChanged (rptr, what_changed);
1309 /* no shared_ptr available, relax; */
1315 Region::set_last_layer_op (uint64_t when)
1317 _last_layer_op = when;
1321 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1323 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1327 Region::equivalent (boost::shared_ptr<const Region> other) const
1329 return _start == other->_start &&
1330 _position == other->_position &&
1331 _length == other->_length;
1335 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1337 return _start == other->_start &&
1338 _length == other->_length;
1342 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1344 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1348 Region::source_deleted (boost::weak_ptr<Source>)
1352 if (!_session.deletion_in_progress()) {
1353 /* this is a very special case: at least one of the region's
1354 sources has bee deleted, so invalidate all references to
1355 ourselves. Do NOT do this during session deletion, because
1356 then we run the risk that this will actually result
1357 in this object being deleted (as refcnt goes to zero)
1358 while emitting DropReferences.
1366 Region::master_source_names ()
1368 SourceList::iterator i;
1370 vector<string> names;
1371 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1372 names.push_back((*i)->name());
1379 Region::set_master_sources (const SourceList& srcs)
1381 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1382 cerr << name() << " " << id() << " DEC M SMS\n";
1383 (*i)->dec_use_count ();
1386 _master_sources = srcs;
1387 assert (_sources.size() == _master_sources.size());
1389 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1390 (*i)->inc_use_count ();
1395 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1400 SourceList::const_iterator i;
1401 SourceList::const_iterator io;
1403 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1404 if ((*i)->id() != (*io)->id()) {
1409 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1410 if ((*i)->id() != (*io)->id()) {
1419 Region::uses_source (boost::shared_ptr<const Source> source) const
1421 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1430 Region::source_length(uint32_t n) const
1432 assert (n < _sources.size());
1433 return _sources[n]->length(_position - _start);
1437 Region::verify_length (framecnt_t len)
1439 if (source() && (source()->destructive() || source()->length_mutable())) {
1443 framecnt_t maxlen = 0;
1445 for (uint32_t n = 0; n < _sources.size(); ++n) {
1446 maxlen = max (maxlen, source_length(n) - _start);
1449 len = min (len, maxlen);
1455 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1457 if (source() && (source()->destructive() || source()->length_mutable())) {
1461 framecnt_t maxlen = 0;
1463 for (uint32_t n = 0; n < _sources.size(); ++n) {
1464 maxlen = max (maxlen, source_length(n) - new_start);
1467 new_length = min (new_length, maxlen);
1473 Region::verify_start (framepos_t pos)
1475 if (source() && (source()->destructive() || source()->length_mutable())) {
1479 for (uint32_t n = 0; n < _sources.size(); ++n) {
1480 if (pos > source_length(n) - _length) {
1488 Region::verify_start_mutable (framepos_t& new_start)
1490 if (source() && (source()->destructive() || source()->length_mutable())) {
1494 for (uint32_t n = 0; n < _sources.size(); ++n) {
1495 if (new_start > source_length(n) - _length) {
1496 new_start = source_length(n) - _length;
1502 boost::shared_ptr<Region>
1503 Region::get_parent() const
1505 boost::shared_ptr<Playlist> pl (playlist());
1508 boost::shared_ptr<Region> r;
1509 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1511 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1512 return boost::static_pointer_cast<Region> (r);
1516 return boost::shared_ptr<Region>();
1520 Region::apply (Filter& filter)
1522 return filter.run (shared_from_this());
1527 Region::invalidate_transients ()
1529 _valid_transients = false;
1530 _transients.clear ();
1534 Region::drop_sources ()
1536 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1537 cerr << name() << " " << id() << " DEC DS\n";
1538 (*i)->dec_use_count ();
1543 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1544 cerr << name() << " " << id() << " DEC MDS \n";
1545 (*i)->dec_use_count ();
1548 _master_sources.clear ();
1552 Region::use_sources (SourceList const & s)
1554 set<boost::shared_ptr<Source> > unique_srcs;
1556 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1558 _sources.push_back (*i);
1559 (*i)->inc_use_count ();
1560 _master_sources.push_back (*i);
1561 (*i)->inc_use_count ();
1563 /* connect only once to DropReferences, even if sources are replicated
1566 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1567 unique_srcs.insert (*i);
1568 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1574 Region::property_factory (const XMLNode& history_node) const
1576 PropertyList* prop_list = new PropertyList;
1578 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
1579 PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node);
1582 prop_list->add (prop);