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.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
46 using namespace ARDOUR;
49 PropertyChange Region::FadeChanged = PBD::new_change ();
50 PropertyChange Region::SyncOffsetChanged = PBD::new_change ();
51 PropertyChange Region::MuteChanged = PBD::new_change ();
52 PropertyChange Region::OpacityChanged = PBD::new_change ();
53 PropertyChange Region::LockChanged = PBD::new_change ();
54 PropertyChange Region::LayerChanged = PBD::new_change ();
55 PropertyChange Region::HiddenChanged = PBD::new_change ();
58 namespace Properties {
59 PBD::PropertyDescriptor<bool> muted;
60 PBD::PropertyDescriptor<bool> opaque;
61 PBD::PropertyDescriptor<bool> locked;
62 PBD::PropertyDescriptor<bool> automatic;
63 PBD::PropertyDescriptor<bool> whole_file;
64 PBD::PropertyDescriptor<bool> import;
65 PBD::PropertyDescriptor<bool> external;
66 PBD::PropertyDescriptor<bool> sync_marked;
67 PBD::PropertyDescriptor<bool> left_of_split;
68 PBD::PropertyDescriptor<bool> right_of_split;
69 PBD::PropertyDescriptor<bool> hidden;
70 PBD::PropertyDescriptor<bool> position_locked;
71 PBD::PropertyDescriptor<framepos_t> start;
72 PBD::PropertyDescriptor<framecnt_t> length;
73 PBD::PropertyDescriptor<framepos_t> position;
74 PBD::PropertyDescriptor<framecnt_t> sync_position;
75 PBD::PropertyDescriptor<layer_t> layer;
76 PBD::PropertyDescriptor<framepos_t> ancestral_start;
77 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
78 PBD::PropertyDescriptor<float> stretch;
79 PBD::PropertyDescriptor<float> shift;
83 PBD::Signal1<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
86 Region::make_property_quarks ()
88 Properties::muted.id = g_quark_from_static_string (X_("muted"));
89 Properties::opaque.id = g_quark_from_static_string (X_("opaque"));
90 Properties::locked.id = g_quark_from_static_string (X_("locked"));
91 Properties::automatic.id = g_quark_from_static_string (X_("automatic"));
92 Properties::whole_file.id = g_quark_from_static_string (X_("whole-file"));
93 Properties::import.id = g_quark_from_static_string (X_("import"));
94 Properties::external.id = g_quark_from_static_string (X_("external"));
95 Properties::sync_marked.id = g_quark_from_static_string (X_("sync-marked"));
96 Properties::left_of_split.id = g_quark_from_static_string (X_("left-of-split"));
97 Properties::right_of_split.id = g_quark_from_static_string (X_("right-of-split"));
98 Properties::hidden.id = g_quark_from_static_string (X_("hidden"));
99 Properties::position_locked.id = g_quark_from_static_string (X_("position-locked"));
100 Properties::start.id = g_quark_from_static_string (X_("start"));
101 Properties::length.id = g_quark_from_static_string (X_("length"));
102 Properties::position.id = g_quark_from_static_string (X_("position"));
103 Properties::sync_position.id = g_quark_from_static_string (X_("sync-position"));
104 Properties::layer.id = g_quark_from_static_string (X_("layer"));
105 Properties::ancestral_start.id = g_quark_from_static_string (X_("ancestral-start"));
106 Properties::ancestral_length.id = g_quark_from_static_string (X_("ancestral-length"));
107 Properties::stretch.id = g_quark_from_static_string (X_("stretch"));
108 Properties::shift.id = g_quark_from_static_string (X_("shift"));
112 Region::register_properties ()
114 _xml_node_name = X_("Region");
116 add_property (_muted);
117 add_property (_opaque);
118 add_property (_locked);
119 add_property (_automatic);
120 add_property (_whole_file);
121 add_property (_import);
122 add_property (_external);
123 add_property (_sync_marked);
124 add_property (_left_of_split);
125 add_property (_right_of_split);
126 add_property (_hidden);
127 add_property (_position_locked);
128 add_property (_start);
129 add_property (_length);
130 add_property (_position);
131 add_property (_sync_position);
132 add_property (_layer);
133 add_property (_ancestral_start);
134 add_property (_ancestral_length);
135 add_property (_stretch);
136 add_property (_shift);
139 #define REGION_DEFAULT_STATE(s,l) \
140 _muted (Properties::muted, MuteChanged, false) \
141 , _opaque (Properties::opaque, OpacityChanged, true) \
142 , _locked (Properties::locked, LockChanged, false) \
143 , _automatic (Properties::automatic, PropertyChange (0), false) \
144 , _whole_file (Properties::whole_file, PropertyChange (0), false) \
145 , _import (Properties::import, PropertyChange (0), false) \
146 , _external (Properties::external, PropertyChange (0), false) \
147 , _sync_marked (Properties::sync_marked, SyncOffsetChanged, false) \
148 , _left_of_split (Properties::left_of_split, PropertyChange (0), false) \
149 , _right_of_split (Properties::right_of_split, PropertyChange (0), false) \
150 , _hidden (Properties::hidden, HiddenChanged, false) \
151 , _position_locked (Properties::position_locked, PropertyChange (0), false) \
152 , _start (Properties::start, StartChanged, (s)) \
153 , _length (Properties::length, LengthChanged, (l)) \
154 , _position (Properties::position, PositionChanged, 0) \
155 , _sync_position (Properties::sync_position, SyncOffsetChanged, (s)) \
156 , _layer (Properties::layer, LayerChanged, 0) \
157 , _ancestral_start (Properties::ancestral_start, PropertyChange (0), (s)) \
158 , _ancestral_length (Properties::ancestral_length, PropertyChange (0), (l)) \
159 , _stretch (Properties::stretch, PropertyChange (0), 1.0) \
160 , _shift (Properties::shift, PropertyChange (0), 1.0)
162 #define REGION_COPY_STATE(other) \
163 _muted (other->_muted) \
164 , _opaque (other->_opaque) \
165 , _locked (other->_locked) \
166 , _automatic (other->_automatic) \
167 , _whole_file (other->_whole_file) \
168 , _import (other->_import) \
169 , _external (other->_external) \
170 , _sync_marked (other->_sync_marked) \
171 , _left_of_split (other->_left_of_split) \
172 , _right_of_split (other->_right_of_split) \
173 , _hidden (other->_hidden) \
174 , _position_locked (other->_position_locked) \
175 , _start(other->_start) \
176 , _length(other->_length) \
177 , _position(other->_position) \
178 , _sync_position(other->_sync_position) \
179 , _layer (other->_layer) \
180 , _ancestral_start (other->_ancestral_start) \
181 , _ancestral_length (other->_ancestral_length) \
182 , _stretch (other->_stretch) \
183 , _shift (other->_shift)
185 /* derived-from-derived constructor (no sources in constructor) */
186 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
187 : SessionObject(s, name)
189 , _no_property_changes (true)
190 , REGION_DEFAULT_STATE(start,length)
191 , _last_length (length)
193 , _positional_lock_style(AudioTime)
194 , _first_edit (EditChangesNothing)
196 , _read_data_count(0)
197 , _pending_changed(PropertyChange (0))
199 , _pending_explicit_relayer (false)
201 register_properties ();
203 /* no sources at this point */
206 /** Basic Region constructor (single source) */
207 Region::Region (boost::shared_ptr<Source> src)
208 : SessionObject(src->session(), "toBeRenamed")
209 , _type (src->type())
210 , _no_property_changes (true)
211 , REGION_DEFAULT_STATE(0,0)
214 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
215 , _first_edit (EditChangesNothing)
217 , _valid_transients(false)
218 , _read_data_count(0)
219 , _pending_changed(PropertyChange (0))
221 , _pending_explicit_relayer (false)
223 register_properties ();
225 _sources.push_back (src);
226 _master_sources.push_back (src);
228 src->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(src)));
230 assert (_sources.size() > 0);
231 assert (_type == src->type());
234 /** Basic Region constructor (many sources) */
235 Region::Region (const SourceList& srcs)
236 : SessionObject(srcs.front()->session(), "toBeRenamed")
237 , _type (srcs.front()->type())
238 , _no_property_changes (true)
239 , REGION_DEFAULT_STATE(0,0)
242 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
243 , _first_edit (EditChangesNothing)
245 , _valid_transients(false)
246 , _read_data_count(0)
247 , _pending_changed(PropertyChange (0))
249 , _pending_explicit_relayer (false)
251 register_properties ();
253 _type = srcs.front()->type();
257 assert(_sources.size() > 0);
258 assert (_type == srcs.front()->type());
261 /** Create a new Region from part of an existing one, starting at one of two places:
263 if @param offset_relative is true, then the start within @param other is given by @param offset
264 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
266 if @param offset_relative is false, then the start within the source is given @param offset.
268 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
269 : SessionObject(other->session(), "toBeRenamed")
270 , _type (other->data_type())
271 , _no_property_changes (true)
272 , REGION_COPY_STATE (other)
273 , _last_length (other->_last_length)
274 , _last_position(other->_last_position) \
275 , _positional_lock_style(other->_positional_lock_style) \
276 , _first_edit (EditChangesNothing)
278 , _valid_transients(false)
279 , _read_data_count(0)
280 , _pending_changed(PropertyChange (0))
282 , _pending_explicit_relayer (false)
285 register_properties ();
287 /* override state that may have been incorrectly inherited from the other region
295 use_sources (other->_sources);
297 if (!offset_relative) {
299 /* not sure why we do this, but its a hangover from ardour before
300 property lists. this would be nice to remove.
303 _positional_lock_style = other->_positional_lock_style;
304 _first_edit = other->_first_edit;
310 /* sync pos is relative to start of file. our start-in-file is now zero,
311 so set our sync position to whatever the the difference between
312 _start and _sync_pos was in the other region.
314 result is that our new sync pos points to the same point in our source(s)
315 as the sync in the other region did in its source(s).
317 since we start at zero in our source(s), it is not possible to use a sync point that
318 is before the start. reset it to _start if that was true in the other region.
321 if (other->sync_marked()) {
322 if (other->_start < other->_sync_position) {
323 /* sync pos was after the start point of the other region */
324 _sync_position = other->_sync_position - other->_start;
326 /* sync pos was before the start point of the other region. not possible here. */
327 _sync_marked = false;
328 _sync_position = _start;
331 _sync_marked = false;
332 _sync_position = _start;
335 /* XXX do something else ! */
336 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
343 _start = other->_start + offset;
345 /* if the other region had a distinct sync point
346 set, then continue to use it as best we can.
347 otherwise, reset sync point back to start.
350 if (other->sync_marked()) {
351 if (other->_sync_position < _start) {
352 _sync_marked = false;
353 _sync_position = _start;
355 _sync_position = other->_sync_position;
358 _sync_marked = false;
359 _sync_position = _start;
363 if (Profile->get_sae()) {
364 /* reset sync point to start if its ended up
365 outside region bounds.
368 if (_sync_position < _start || _sync_position >= _start + _length) {
369 _sync_marked = false;
370 _sync_position = _start;
374 assert (_type == other->data_type());
377 /** Create a copy of @param other but with different sources. Used by filters */
378 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
379 : SessionObject (other->session(), other->name())
380 , _type (srcs.front()->type())
381 , _no_property_changes (true)
382 , REGION_COPY_STATE (other)
383 , _last_length (other->_last_length)
384 , _last_position (other->_last_position)
385 , _positional_lock_style (other->_positional_lock_style)
386 , _first_edit (EditChangesID)
388 , _valid_transients (false)
389 , _read_data_count (0)
390 , _pending_changed (PropertyChange(0))
391 , _last_layer_op (other->_last_layer_op)
392 , _pending_explicit_relayer (false)
394 register_properties ();
397 _position_locked = false;
399 other->_first_edit = EditChangesName;
401 if (other->_extra_xml) {
402 _extra_xml = new XMLNode (*other->_extra_xml);
408 assert(_sources.size() > 0);
411 /** Simple "copy" constructor */
412 Region::Region (boost::shared_ptr<const Region> other)
413 : SessionObject(other->session(), other->name())
414 , _type(other->data_type())
415 , _no_property_changes (true)
416 , REGION_COPY_STATE (other)
417 , _last_length (other->_last_length)
418 , _last_position (other->_last_position)
419 , _positional_lock_style (other->_positional_lock_style)
420 , _first_edit (EditChangesID)
422 , _valid_transients(false)
423 , _read_data_count(0)
424 , _pending_changed(PropertyChange(0))
425 , _last_layer_op(other->_last_layer_op)
426 , _pending_explicit_relayer (false)
428 register_properties ();
431 _position_locked = false;
433 other->_first_edit = EditChangesName;
435 if (other->_extra_xml) {
436 _extra_xml = new XMLNode (*other->_extra_xml);
441 use_sources (other->_sources);
442 assert(_sources.size() > 0);
445 Region::Region (const SourceList& srcs, const XMLNode& node)
446 : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
447 , _type (srcs.front()->type())
448 , REGION_DEFAULT_STATE(0,0)
451 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
452 , _first_edit (EditChangesNothing)
454 , _valid_transients(false)
455 , _read_data_count(0)
456 , _pending_changed(PropertyChange(0))
458 , _pending_explicit_relayer (false)
460 const XMLProperty* prop;
462 register_properties ();
464 if ((prop = node.property (X_("id")))) {
470 if (set_state (node, Stateful::loading_state_version)) {
471 throw failed_constructor();
474 assert(_type != DataType::NIL);
475 assert(_sources.size() > 0);
476 assert (_type == srcs.front()->type());
480 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
481 : SessionObject(src->session(), X_("error: XML did not reset this"))
482 , _type (src->type())
483 , REGION_DEFAULT_STATE(0,0)
486 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
487 , _first_edit (EditChangesNothing)
489 , _read_data_count (0)
490 , _pending_changed (PropertyChange(0))
492 , _pending_explicit_relayer (false)
494 const XMLProperty *prop;
496 register_properties ();
498 _sources.push_back (src);
500 if ((prop = node.property (X_("id")))) {
504 if (set_state (node, Stateful::loading_state_version)) {
505 throw failed_constructor();
508 assert (_type != DataType::NIL);
509 assert (_sources.size() > 0);
510 assert (_type == src->type());
515 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
519 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
521 _playlist = wpl.lock();
525 Region::set_name (const std::string& str)
528 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
529 assert(_name == str);
530 send_change (ARDOUR::NameChanged);
537 Region::set_length (framecnt_t len, void */*src*/)
539 //cerr << "Region::set_length() len = " << len << endl;
544 if (_length != len && len != 0) {
546 /* check that the current _position wouldn't make the new
550 if (max_frames - len < _position) {
554 if (!verify_length (len)) {
559 _last_length = _length;
564 invalidate_transients ();
570 send_change (LengthChanged);
575 Region::maybe_uncopy ()
577 /* this does nothing but marked a semantic moment once upon a time */
581 Region::first_edit ()
583 boost::shared_ptr<Playlist> pl (playlist());
585 if (_first_edit != EditChangesNothing && pl) {
587 _name = _session.new_region_name (_name);
588 _first_edit = EditChangesNothing;
590 send_change (ARDOUR::NameChanged);
591 RegionFactory::CheckNewRegion (shared_from_this());
596 Region::at_natural_position () const
598 boost::shared_ptr<Playlist> pl (playlist());
604 boost::shared_ptr<Region> whole_file_region = get_parent();
606 if (whole_file_region) {
607 if (_position == whole_file_region->position() + _start) {
616 Region::move_to_natural_position (void *src)
618 boost::shared_ptr<Playlist> pl (playlist());
624 boost::shared_ptr<Region> whole_file_region = get_parent();
626 if (whole_file_region) {
627 set_position (whole_file_region->position() + _start, src);
632 Region::special_set_position (framepos_t pos)
634 /* this is used when creating a whole file region as
635 a way to store its "natural" or "captured" position.
638 _position = _position;
643 Region::set_position_lock_style (PositionLockStyle ps)
645 boost::shared_ptr<Playlist> pl (playlist());
651 _positional_lock_style = ps;
653 if (_positional_lock_style == MusicTime) {
654 _session.tempo_map().bbt_time (_position, _bbt_time);
660 Region::update_position_after_tempo_map_change ()
662 boost::shared_ptr<Playlist> pl (playlist());
664 if (!pl || _positional_lock_style != MusicTime) {
668 TempoMap& map (_session.tempo_map());
669 framepos_t pos = map.frame_time (_bbt_time);
670 set_position_internal (pos, false);
674 Region::set_position (framepos_t pos, void* /*src*/)
680 set_position_internal (pos, true);
684 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
686 if (_position != pos) {
687 _last_position = _position;
690 /* check that the new _position wouldn't make the current
691 length impossible - if so, change the length.
693 XXX is this the right thing to do?
696 if (max_frames - _length < _position) {
697 _last_length = _length;
698 _length = max_frames - _position;
701 if (allow_bbt_recompute) {
702 recompute_position_from_lock_style ();
705 invalidate_transients ();
708 /* do this even if the position is the same. this helps out
709 a GUI that has moved its representation already.
712 send_change (PositionChanged);
716 Region::set_position_on_top (framepos_t pos, void* /*src*/)
722 if (_position != pos) {
723 _last_position = _position;
727 boost::shared_ptr<Playlist> pl (playlist());
730 pl->raise_region_to_top (shared_from_this ());
733 /* do this even if the position is the same. this helps out
734 a GUI that has moved its representation already.
737 send_change (PositionChanged);
741 Region::recompute_position_from_lock_style ()
743 if (_positional_lock_style == MusicTime) {
744 _session.tempo_map().bbt_time (_position, _bbt_time);
749 Region::nudge_position (frameoffset_t n, void* /*src*/)
759 _last_position = _position;
762 if (_position > max_frames - n) {
763 _position = max_frames;
768 if (_position < -n) {
775 send_change (PositionChanged);
779 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
781 _ancestral_length = l;
782 _ancestral_start = s;
788 Region::set_start (framepos_t pos, void* /*src*/)
790 if (locked() || position_locked()) {
793 /* This just sets the start, nothing else. It effectively shifts
794 the contents of the Region within the overall extent of the Source,
795 without changing the Region's position or length
800 if (!verify_start (pos)) {
807 invalidate_transients ();
809 send_change (StartChanged);
814 Region::trim_start (framepos_t new_position, void */*src*/)
816 if (locked() || position_locked()) {
819 framepos_t new_start;
820 frameoffset_t start_shift;
822 if (new_position > _position) {
823 start_shift = new_position - _position;
825 start_shift = -(_position - new_position);
828 if (start_shift > 0) {
830 if (_start > max_frames - start_shift) {
831 new_start = max_frames;
833 new_start = _start + start_shift;
836 if (!verify_start (new_start)) {
840 } else if (start_shift < 0) {
842 if (_start < -start_shift) {
845 new_start = _start + start_shift;
851 if (new_start == _start) {
859 send_change (StartChanged);
863 Region::trim_front (framepos_t new_position, void *src)
869 framepos_t end = last_frame();
870 framepos_t source_zero;
872 if (_position > _start) {
873 source_zero = _position - _start;
875 source_zero = 0; // its actually negative, but this will work for us
878 if (new_position < end) { /* can't trim it zero or negative length */
882 /* can't trim it back passed where source position zero is located */
884 new_position = max (new_position, source_zero);
887 if (new_position > _position) {
888 newlen = _length - (new_position - _position);
890 newlen = _length + (_position - new_position);
893 trim_to_internal (new_position, newlen, src);
895 recompute_at_start ();
900 /** @param new_endpoint New region end point, such that, for example,
901 * a region at 0 of length 10 has an endpoint of 9.
905 Region::trim_end (framepos_t new_endpoint, void */*src*/)
911 if (new_endpoint > _position) {
912 trim_to_internal (_position, new_endpoint - _position + 1, this);
920 Region::trim_to (framepos_t position, framecnt_t length, void *src)
926 trim_to_internal (position, length, src);
929 recompute_at_start ();
935 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
937 frameoffset_t start_shift;
938 framepos_t new_start;
944 if (position > _position) {
945 start_shift = position - _position;
947 start_shift = -(_position - position);
950 if (start_shift > 0) {
952 if (_start > max_frames - start_shift) {
953 new_start = max_frames;
955 new_start = _start + start_shift;
959 } else if (start_shift < 0) {
961 if (_start < -start_shift) {
964 new_start = _start + start_shift;
970 if (!verify_start_and_length (new_start, length)) {
974 PropertyChange what_changed = PropertyChange (0);
976 if (_start != new_start) {
978 what_changed = PropertyChange (what_changed|StartChanged);
980 if (_length != length) {
982 _last_length = _length;
985 what_changed = PropertyChange (what_changed|LengthChanged);
987 if (_position != position) {
989 _last_position = _position;
991 _position = position;
992 what_changed = PropertyChange (what_changed|PositionChanged);
997 if (what_changed & (StartChanged|LengthChanged)) {
1002 send_change (what_changed);
1007 Region::set_hidden (bool yn)
1009 if (hidden() != yn) {
1011 send_change (HiddenChanged);
1016 Region::set_whole_file (bool yn)
1019 /* no change signal */
1023 Region::set_automatic (bool yn)
1026 /* no change signal */
1030 Region::set_muted (bool yn)
1032 if (muted() != yn) {
1034 send_change (MuteChanged);
1039 Region::set_opaque (bool yn)
1041 if (opaque() != yn) {
1043 send_change (OpacityChanged);
1048 Region::set_locked (bool yn)
1050 if (locked() != yn) {
1052 send_change (LockChanged);
1057 Region::set_position_locked (bool yn)
1059 if (position_locked() != yn) {
1060 _position_locked = yn;
1061 send_change (LockChanged);
1066 Region::set_sync_position (framepos_t absolute_pos)
1068 framepos_t const file_pos = _start + (absolute_pos - _position);
1070 if (file_pos != _sync_position) {
1071 _sync_marked = true;
1072 _sync_position = file_pos;
1076 send_change (SyncOffsetChanged);
1081 Region::clear_sync_position ()
1083 if (sync_marked()) {
1084 _sync_marked = false;
1088 send_change (SyncOffsetChanged);
1093 Region::sync_offset (int& dir) const
1095 /* returns the sync point relative the first frame of the region */
1097 if (sync_marked()) {
1098 if (_sync_position > _start) {
1100 return _sync_position - _start;
1103 return _start - _sync_position;
1112 Region::adjust_to_sync (framepos_t pos) const
1115 frameoffset_t offset = sync_offset (sync_dir);
1117 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1126 if (max_frames - pos > offset) {
1135 Region::sync_position() const
1137 if (sync_marked()) {
1138 return _sync_position;
1147 boost::shared_ptr<Playlist> pl (playlist());
1149 pl->raise_region (shared_from_this ());
1156 boost::shared_ptr<Playlist> pl (playlist());
1158 pl->lower_region (shared_from_this ());
1164 Region::raise_to_top ()
1166 boost::shared_ptr<Playlist> pl (playlist());
1168 pl->raise_region_to_top (shared_from_this());
1173 Region::lower_to_bottom ()
1175 boost::shared_ptr<Playlist> pl (playlist());
1177 pl->lower_region_to_bottom (shared_from_this());
1182 Region::set_layer (layer_t l)
1187 send_change (LayerChanged);
1192 Region::state (bool /*full_state*/)
1194 XMLNode *node = new XMLNode ("Region");
1196 const char* fe = NULL;
1198 add_properties (*node);
1200 _id.print (buf, sizeof (buf));
1201 node->add_property ("id", buf);
1202 node->add_property ("type", _type.to_string());
1204 switch (_first_edit) {
1205 case EditChangesNothing:
1208 case EditChangesName:
1214 default: /* should be unreachable but makes g++ happy */
1219 node->add_property ("first-edit", fe);
1221 /* note: flags are stored by derived classes */
1223 if (_positional_lock_style != AudioTime) {
1224 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1227 node->add_property ("bbt-position", str.str());
1234 Region::get_state ()
1236 return state (true);
1240 Region::set_state (const XMLNode& node, int version)
1242 PropertyChange what_changed = PropertyChange (0);
1243 return _set_state (node, version, what_changed, true);
1247 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1249 const XMLProperty* prop;
1251 what_changed = set_properties (node);
1253 if ((prop = node.property (X_("id")))) {
1254 _id = prop->value();
1257 if ((prop = node.property ("positional-lock-style")) != 0) {
1258 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1260 if (_positional_lock_style == MusicTime) {
1261 if ((prop = node.property ("bbt-position")) == 0) {
1262 /* missing BBT info, revert to audio time locking */
1263 _positional_lock_style = AudioTime;
1265 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1268 &_bbt_time.ticks) != 3) {
1269 _positional_lock_style = AudioTime;
1276 /* fix problems with old sessions corrupted by impossible
1277 values for _stretch or _shift
1279 if (_stretch == 0.0f) {
1283 if (_shift == 0.0f) {
1287 const XMLNodeList& nlist = node.children();
1289 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1295 if (child->name () == "Extra") {
1297 _extra_xml = new XMLNode (*child);
1303 cerr << _name << ": final change to be sent: " << hex << what_changed << dec << endl;
1304 send_change (what_changed);
1314 _last_length = _length;
1315 _last_position = _position;
1321 PropertyChange what_changed = PropertyChange (0);
1324 Glib::Mutex::Lock lm (_lock);
1326 if (_frozen && --_frozen > 0) {
1330 if (_pending_changed) {
1331 what_changed = _pending_changed;
1332 _pending_changed = PropertyChange (0);
1336 if (what_changed == PropertyChange (0)) {
1340 if (what_changed & LengthChanged) {
1341 if (what_changed & PositionChanged) {
1342 recompute_at_start ();
1344 recompute_at_end ();
1347 send_change (what_changed);
1351 Region::send_change (PropertyChange what_changed)
1355 Glib::Mutex::Lock lm (_lock);
1357 _pending_changed = PropertyChange (_pending_changed|what_changed);
1362 cerr << _name << " actually sends " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1363 StateChanged (what_changed);
1364 cerr << _name << " done with " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1366 if (!_no_property_changes) {
1368 /* Try and send a shared_pointer unless this is part of the constructor.
1373 boost::shared_ptr<Region> rptr = shared_from_this();
1374 cerr << _name << " actually sends prop change " << hex << what_changed << dec << " @ " << get_microseconds() << endl;
1375 RegionPropertyChanged (rptr);
1376 cerr << _name << " done with prop change @ " << get_microseconds() << endl;
1379 /* no shared_ptr available, relax; */
1386 Region::set_last_layer_op (uint64_t when)
1388 _last_layer_op = when;
1392 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1394 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1398 Region::equivalent (boost::shared_ptr<const Region> other) const
1400 return _start == other->_start &&
1401 _position == other->_position &&
1402 _length == other->_length;
1406 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1408 return _start == other->_start &&
1409 _length == other->_length;
1413 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1415 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1419 Region::source_deleted (boost::weak_ptr<Source>)
1423 if (!_session.deletion_in_progress()) {
1424 /* this is a very special case: at least one of the region's
1425 sources has bee deleted, so invalidate all references to
1426 ourselves. Do NOT do this during session deletion, because
1427 then we run the risk that this will actually result
1428 in this object being deleted (as refcnt goes to zero)
1429 while emitting DropReferences.
1437 Region::master_source_names ()
1439 SourceList::iterator i;
1441 vector<string> names;
1442 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1443 names.push_back((*i)->name());
1450 Region::set_master_sources (const SourceList& srcs)
1452 _master_sources = srcs;
1453 assert (_sources.size() == _master_sources.size());
1457 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1462 SourceList::const_iterator i;
1463 SourceList::const_iterator io;
1465 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1466 if ((*i)->id() != (*io)->id()) {
1471 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1472 if ((*i)->id() != (*io)->id()) {
1481 Region::uses_source (boost::shared_ptr<const Source> source) const
1483 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1492 Region::source_length(uint32_t n) const
1494 return _sources[n]->length(_position - _start);
1498 Region::verify_length (framecnt_t len)
1500 if (source() && (source()->destructive() || source()->length_mutable())) {
1504 framecnt_t maxlen = 0;
1506 for (uint32_t n=0; n < _sources.size(); ++n) {
1507 maxlen = max (maxlen, source_length(n) - _start);
1510 len = min (len, maxlen);
1516 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1518 if (source() && (source()->destructive() || source()->length_mutable())) {
1522 framecnt_t maxlen = 0;
1524 for (uint32_t n=0; n < _sources.size(); ++n) {
1525 maxlen = max (maxlen, source_length(n) - new_start);
1528 new_length = min (new_length, maxlen);
1534 Region::verify_start (framepos_t pos)
1536 if (source() && (source()->destructive() || source()->length_mutable())) {
1540 for (uint32_t n=0; n < _sources.size(); ++n) {
1541 if (pos > source_length(n) - _length) {
1549 Region::verify_start_mutable (framepos_t& new_start)
1551 if (source() && (source()->destructive() || source()->length_mutable())) {
1555 for (uint32_t n=0; n < _sources.size(); ++n) {
1556 if (new_start > source_length(n) - _length) {
1557 new_start = source_length(n) - _length;
1563 boost::shared_ptr<Region>
1564 Region::get_parent() const
1566 boost::shared_ptr<Playlist> pl (playlist());
1569 boost::shared_ptr<Region> r;
1570 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1572 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1573 return boost::static_pointer_cast<Region> (r);
1577 return boost::shared_ptr<Region>();
1581 Region::apply (Filter& filter)
1583 return filter.run (shared_from_this());
1588 Region::invalidate_transients ()
1590 _valid_transients = false;
1591 _transients.clear ();
1596 Region::use_sources (SourceList const & s)
1598 set<boost::shared_ptr<Source> > unique_srcs;
1600 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1601 _sources.push_back (*i);
1602 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1603 unique_srcs.insert (*i);
1606 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1607 _master_sources.push_back (*i);
1608 if (unique_srcs.find (*i) == unique_srcs.end()) {
1609 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1616 Region::set_property (const PropertyBase& prop)
1618 PropertyChange c = PropertyChange (0);
1620 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 set property %2\n", _name.val(), prop.property_name()));
1622 if (prop == Properties::muted.id) {
1623 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1624 if (val != _muted) {
1625 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 muted changed from %2 to %3",
1626 _name.val(), _muted.val(), val));
1630 } else if (prop == Properties::opaque.id) {
1631 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1632 if (val != _opaque) {
1633 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 opaque changed from %2 to %3",
1634 _name.val(), _opaque.val(), val));
1638 } else if (prop == Properties::locked.id) {
1639 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1640 if (val != _locked) {
1641 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 locked changed from %2 to %3",
1642 _name.val(), _locked.val(), val));
1646 } else if (prop == Properties::automatic.id) {
1647 _automatic = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1648 } else if (prop == Properties::whole_file.id) {
1649 _whole_file = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1650 } else if (prop == Properties::import.id) {
1651 _import = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1652 } else if (prop == Properties::external.id) {
1653 _external = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1654 } else if (prop == Properties::sync_marked.id) {
1655 _sync_marked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1656 } else if (prop == Properties::left_of_split.id) {
1657 _left_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1658 } else if (prop == Properties::right_of_split.id) {
1659 _right_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1660 } else if (prop == Properties::hidden.id) {
1661 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1662 if (val != _hidden) {
1666 } else if (prop == Properties::position_locked.id) {
1667 _position_locked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1668 } else if (prop == Properties::start.id) {
1669 _start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1670 } else if (prop == Properties::length.id) {
1671 const PropertyTemplate<framecnt_t>* pt1 = dynamic_cast<const PropertyTemplate<framecnt_t>* >(&prop);
1672 const PropertyTemplate<int>* pt2 = dynamic_cast<const PropertyTemplate<int>* >(&prop);
1674 cerr << "Cast to frmecnt = " << pt1 << " to int = " << pt2 << endl;
1676 framecnt_t val = dynamic_cast<const PropertyTemplate<framecnt_t>* > (&prop)->val();
1677 if (val != _length) {
1678 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 length changed from %2 to %3",
1679 _name.val(), _length.val(), val));
1683 DEBUG_TRACE (DEBUG::Properties, string_compose ("length %1 matches %2\n", _length.val(), val));
1686 } else if (prop == Properties::position.id) {
1687 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1688 if (val != _position) {
1689 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 position changed from %2 to %3",
1690 _name.val(), _position.val(), val));
1692 c = PositionChanged;
1694 } else if (prop == Properties::sync_position.id) {
1695 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1696 if (val != _sync_position) {
1697 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 syncpos changed from %2 to %3",
1698 _name.val(), _sync_position, val));
1699 _sync_position = val;
1700 c = SyncOffsetChanged;
1702 } else if (prop == Properties::layer.id) {
1703 layer_t val = dynamic_cast<const PropertyTemplate<layer_t>*>(&prop)->val();
1704 if (val != _layer) {
1705 DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 syncpos changed from %2 to %3",
1706 _name.val(), _sync_position, val));
1710 } else if (prop == Properties::ancestral_start.id) {
1711 _ancestral_start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1712 } else if (prop == Properties::ancestral_length.id) {
1713 _ancestral_length = dynamic_cast<const PropertyTemplate<framecnt_t>*>(&prop)->val();
1714 } else if (prop == Properties::stretch.id) {
1715 _stretch = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1716 } else if (prop == Properties::shift.id) {
1717 _shift = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1719 return SessionObject::set_property (prop);