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<bool> valid_transients;
63 PBD::PropertyDescriptor<framepos_t> start;
64 PBD::PropertyDescriptor<framecnt_t> length;
65 PBD::PropertyDescriptor<framepos_t> position;
66 PBD::PropertyDescriptor<framecnt_t> sync_position;
67 PBD::PropertyDescriptor<layer_t> layer;
68 PBD::PropertyDescriptor<framepos_t> ancestral_start;
69 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
70 PBD::PropertyDescriptor<float> stretch;
71 PBD::PropertyDescriptor<float> shift;
72 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
79 Region::make_property_quarks ()
81 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
83 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
85 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
87 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
89 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
91 Properties::import.property_id = g_quark_from_static_string (X_("import"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
93 Properties::external.property_id = g_quark_from_static_string (X_("external"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
95 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
97 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
99 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
101 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
103 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
105 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
107 Properties::start.property_id = g_quark_from_static_string (X_("start"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
109 Properties::length.property_id = g_quark_from_static_string (X_("length"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
111 Properties::position.property_id = g_quark_from_static_string (X_("position"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
113 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
115 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
117 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
119 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
121 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
123 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
125 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
130 Region::register_properties ()
132 _xml_node_name = X_("Region");
134 add_property (_muted);
135 add_property (_opaque);
136 add_property (_locked);
137 add_property (_automatic);
138 add_property (_whole_file);
139 add_property (_import);
140 add_property (_external);
141 add_property (_sync_marked);
142 add_property (_left_of_split);
143 add_property (_right_of_split);
144 add_property (_hidden);
145 add_property (_position_locked);
146 add_property (_valid_transients);
147 add_property (_start);
148 add_property (_length);
149 add_property (_position);
150 add_property (_sync_position);
151 add_property (_layer);
152 add_property (_ancestral_start);
153 add_property (_ancestral_length);
154 add_property (_stretch);
155 add_property (_shift);
156 add_property (_position_lock_style);
159 #define REGION_DEFAULT_STATE(s,l) \
160 _muted (Properties::muted, false) \
161 , _opaque (Properties::opaque, true) \
162 , _locked (Properties::locked, false) \
163 , _automatic (Properties::automatic, false) \
164 , _whole_file (Properties::whole_file, false) \
165 , _import (Properties::import, false) \
166 , _external (Properties::external, false) \
167 , _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _hidden (Properties::hidden, false) \
171 , _position_locked (Properties::position_locked, false) \
172 , _valid_transients (Properties::valid_transients, false) \
173 , _start (Properties::start, (s)) \
174 , _length (Properties::length, (l)) \
175 , _position (Properties::position, 0) \
176 , _sync_position (Properties::sync_position, (s)) \
177 , _layer (Properties::layer, 0) \
178 , _ancestral_start (Properties::ancestral_start, (s)) \
179 , _ancestral_length (Properties::ancestral_length, (l)) \
180 , _stretch (Properties::stretch, 1.0) \
181 , _shift (Properties::shift, 1.0) \
182 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
184 #define REGION_COPY_STATE(other) \
185 _muted (Properties::muted, other->_muted) \
186 , _opaque (Properties::opaque, other->_opaque) \
187 , _locked (Properties::locked, other->_locked) \
188 , _automatic (Properties::automatic, other->_automatic) \
189 , _whole_file (Properties::whole_file, other->_whole_file) \
190 , _import (Properties::import, other->_import) \
191 , _external (Properties::external, other->_external) \
192 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
193 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
194 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
195 , _hidden (Properties::hidden, other->_hidden) \
196 , _position_locked (Properties::position_locked, other->_position_locked) \
197 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
198 , _start(Properties::start, other->_start) \
199 , _length(Properties::length, other->_length) \
200 , _position(Properties::position, other->_position) \
201 , _sync_position(Properties::sync_position, other->_sync_position) \
202 , _layer (Properties::layer, other->_layer) \
203 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
204 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
205 , _stretch (Properties::stretch, other->_stretch) \
206 , _shift (Properties::shift, other->_shift) \
207 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
209 /* derived-from-derived constructor (no sources in constructor) */
210 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
211 : SessionObject(s, name)
213 , REGION_DEFAULT_STATE(start,length)
214 , _last_length (length)
216 , _first_edit (EditChangesNothing)
217 , _read_data_count(0)
219 , _pending_explicit_relayer (false)
221 register_properties ();
223 /* no sources at this point */
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228 : SessionObject(srcs.front()->session(), "toBeRenamed")
229 , _type (srcs.front()->type())
230 , REGION_DEFAULT_STATE(0,0)
233 , _first_edit (EditChangesNothing)
234 , _read_data_count(0)
236 , _pending_explicit_relayer (false)
238 register_properties ();
240 _type = srcs.front()->type();
244 assert(_sources.size() > 0);
245 assert (_type == srcs.front()->type());
248 /** Create a new Region from part of an existing one, starting at one of two places:
250 if \a offset_relative is true, then the start within \a other is given by \a offset
251 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
253 if @param offset_relative is false, then the start within the source is given \a offset.
255 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
256 : SessionObject(other->session(), other->name())
257 , _type (other->data_type())
258 , REGION_COPY_STATE (other)
259 , _last_length (other->_last_length)
260 , _last_position(other->_last_position) \
261 , _first_edit (EditChangesNothing)
262 , _read_data_count(0)
264 , _pending_explicit_relayer (false)
267 register_properties ();
269 /* override state that may have been incorrectly inherited from the other region
277 use_sources (other->_sources);
279 if (!offset_relative) {
281 /* not sure why we do this, but its a hangover from ardour before
282 property lists. this would be nice to remove.
285 _position_lock_style = other->_position_lock_style;
286 _first_edit = other->_first_edit;
292 /* sync pos is relative to start of file. our start-in-file is now zero,
293 so set our sync position to whatever the the difference between
294 _start and _sync_pos was in the other region.
296 result is that our new sync pos points to the same point in our source(s)
297 as the sync in the other region did in its source(s).
299 since we start at zero in our source(s), it is not possible to use a sync point that
300 is before the start. reset it to _start if that was true in the other region.
303 if (other->sync_marked()) {
304 if (other->_start < other->_sync_position) {
305 /* sync pos was after the start point of the other region */
306 _sync_position = other->_sync_position - other->_start;
308 /* sync pos was before the start point of the other region. not possible here. */
309 _sync_marked = false;
310 _sync_position = _start;
313 _sync_marked = false;
314 _sync_position = _start;
317 /* XXX do something else ! */
318 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
325 _start = other->_start + offset;
327 /* if the other region had a distinct sync point
328 set, then continue to use it as best we can.
329 otherwise, reset sync point back to start.
332 if (other->sync_marked()) {
333 if (other->_sync_position < _start) {
334 _sync_marked = false;
335 _sync_position = _start;
337 _sync_position = other->_sync_position;
340 _sync_marked = false;
341 _sync_position = _start;
345 if (Profile->get_sae()) {
346 /* reset sync point to start if its ended up
347 outside region bounds.
350 if (_sync_position < _start || _sync_position >= _start + _length) {
351 _sync_marked = false;
352 _sync_position = _start;
356 assert (_type == other->data_type());
359 /** Create a copy of @param other but with different sources. Used by filters */
360 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
361 : SessionObject (other->session(), other->name())
362 , _type (srcs.front()->type())
363 , REGION_COPY_STATE (other)
364 , _last_length (other->_last_length)
365 , _last_position (other->_last_position)
366 , _first_edit (EditChangesID)
367 , _read_data_count (0)
368 , _last_layer_op (other->_last_layer_op)
369 , _pending_explicit_relayer (false)
371 register_properties ();
374 _position_locked = false;
376 other->_first_edit = EditChangesName;
378 if (other->_extra_xml) {
379 _extra_xml = new XMLNode (*other->_extra_xml);
385 assert(_sources.size() > 0);
388 /** Simple "copy" constructor */
389 Region::Region (boost::shared_ptr<const Region> other)
390 : SessionObject(other->session(), other->name())
391 , _type(other->data_type())
392 , REGION_COPY_STATE (other)
393 , _last_length (other->_last_length)
394 , _last_position (other->_last_position)
395 , _first_edit (EditChangesID)
396 , _read_data_count(0)
397 , _last_layer_op(other->_last_layer_op)
398 , _pending_explicit_relayer (false)
400 register_properties ();
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
413 use_sources (other->_sources);
414 assert(_sources.size() > 0);
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
430 Region::set_name (const std::string& str)
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
435 send_change (Properties::name);
442 Region::set_length (framecnt_t len, void */*src*/)
444 //cerr << "Region::set_length() len = " << len << endl;
449 if (_length != len && len != 0) {
451 /* check that the current _position wouldn't make the new
455 if (max_framepos - len < _position) {
459 if (!verify_length (len)) {
464 _last_length = _length;
469 invalidate_transients ();
471 if (!property_changes_suspended()) {
475 send_change (Properties::length);
480 Region::maybe_uncopy ()
482 /* this does nothing but marked a semantic moment once upon a time */
486 Region::first_edit ()
488 boost::shared_ptr<Playlist> pl (playlist());
490 if (_first_edit != EditChangesNothing && pl) {
492 _name = RegionFactory::new_region_name (_name);
493 _first_edit = EditChangesNothing;
495 send_change (Properties::name);
496 RegionFactory::CheckNewRegion (shared_from_this());
501 Region::at_natural_position () const
503 boost::shared_ptr<Playlist> pl (playlist());
509 boost::shared_ptr<Region> whole_file_region = get_parent();
511 if (whole_file_region) {
512 if (_position == whole_file_region->position() + _start) {
521 Region::move_to_natural_position (void *src)
523 boost::shared_ptr<Playlist> pl (playlist());
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 set_position (whole_file_region->position() + _start, src);
537 Region::special_set_position (framepos_t pos)
539 /* this is used when creating a whole file region as
540 a way to store its "natural" or "captured" position.
543 _position = _position;
548 Region::set_position_lock_style (PositionLockStyle ps)
550 if (_position_lock_style != ps) {
552 boost::shared_ptr<Playlist> pl (playlist());
558 _position_lock_style = ps;
560 if (_position_lock_style == MusicTime) {
561 _session.tempo_map().bbt_time (_position, _bbt_time);
564 send_change (Properties::position_lock_style);
569 Region::update_position_after_tempo_map_change ()
571 boost::shared_ptr<Playlist> pl (playlist());
573 if (!pl || _position_lock_style != MusicTime) {
577 TempoMap& map (_session.tempo_map());
578 framepos_t pos = map.frame_time (_bbt_time);
579 set_position_internal (pos, false);
583 Region::set_position (framepos_t pos, void* /*src*/)
589 set_position_internal (pos, true);
593 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
595 if (_position != pos) {
596 _last_position = _position;
599 /* check that the new _position wouldn't make the current
600 length impossible - if so, change the length.
602 XXX is this the right thing to do?
605 if (max_framepos - _length < _position) {
606 _last_length = _length;
607 _length = max_framepos - _position;
610 if (allow_bbt_recompute) {
611 recompute_position_from_lock_style ();
614 //invalidate_transients ();
617 /* do this even if the position is the same. this helps out
618 a GUI that has moved its representation already.
620 send_change (Properties::position);
624 Region::set_position_on_top (framepos_t pos, void* /*src*/)
630 if (_position != pos) {
631 _last_position = _position;
635 boost::shared_ptr<Playlist> pl (playlist());
638 pl->raise_region_to_top (shared_from_this ());
641 /* do this even if the position is the same. this helps out
642 a GUI that has moved its representation already.
645 send_change (Properties::position);
649 Region::recompute_position_from_lock_style ()
651 if (_position_lock_style == MusicTime) {
652 _session.tempo_map().bbt_time (_position, _bbt_time);
657 Region::nudge_position (frameoffset_t n, void* /*src*/)
667 _last_position = _position;
670 if (_position > max_framepos - n) {
671 _position = max_framepos;
676 if (_position < -n) {
683 send_change (Properties::position);
687 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
689 _ancestral_length = l;
690 _ancestral_start = s;
696 Region::set_start (framepos_t pos, void* /*src*/)
698 if (locked() || position_locked()) {
701 /* This just sets the start, nothing else. It effectively shifts
702 the contents of the Region within the overall extent of the Source,
703 without changing the Region's position or length
708 if (!verify_start (pos)) {
715 invalidate_transients ();
717 send_change (Properties::start);
722 Region::trim_start (framepos_t new_position, void */*src*/)
724 if (locked() || position_locked()) {
727 framepos_t new_start;
728 frameoffset_t start_shift;
730 if (new_position > _position) {
731 start_shift = new_position - _position;
733 start_shift = -(_position - new_position);
736 if (start_shift > 0) {
738 if (_start > max_framepos - start_shift) {
739 new_start = max_framepos;
741 new_start = _start + start_shift;
744 if (!verify_start (new_start)) {
748 } else if (start_shift < 0) {
750 if (_start < -start_shift) {
753 new_start = _start + start_shift;
759 if (new_start == _start) {
767 send_change (Properties::start);
771 Region::trim_front (framepos_t new_position, void *src)
773 modify_front (new_position, false, src);
777 Region::cut_front (nframes_t new_position, void *src)
779 modify_front (new_position, true, src);
783 Region::cut_end (nframes_t new_endpoint, void *src)
785 modify_end (new_endpoint, true, src);
789 Region::modify_front (nframes_t new_position, bool reset_fade, void *src)
795 nframes_t end = last_frame();
796 nframes_t source_zero;
798 if (_position > _start) {
799 source_zero = _position - _start;
801 source_zero = 0; // its actually negative, but this will work for us
804 if (new_position < end) { /* can't trim it zero or negative length */
806 nframes_t newlen = 0;
807 nframes64_t delta = 0;
809 /* can't trim it back passed where source position zero is located */
811 new_position = max (new_position, source_zero);
813 if (new_position > _position) {
814 newlen = _length - (new_position - _position);
815 delta = -1 * (new_position - _position);
817 newlen = _length + (_position - new_position);
818 delta = _position - new_position;
821 trim_to_internal (new_position, newlen, src);
824 _right_of_split = true;
827 if (!property_changes_suspended()) {
828 recompute_at_start ();
831 if (_transients.size() > 0){
832 adjust_transients(delta);
838 Region::modify_end (nframes_t new_endpoint, bool reset_fade, void* /*src*/)
844 if (new_endpoint > _position) {
845 trim_to_internal (_position, new_endpoint - _position +1, this);
847 _left_of_split = true;
849 if (!property_changes_suspended()) {
855 /** @param new_endpoint New region end point, such that, for example,
856 * a region at 0 of length 10 has an endpoint of 9.
860 Region::trim_end (framepos_t new_endpoint, void* src)
862 modify_end (new_endpoint, false, src);
866 Region::trim_to (framepos_t position, framecnt_t length, void *src)
872 trim_to_internal (position, length, src);
874 if (!property_changes_suspended()) {
875 recompute_at_start ();
881 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
883 frameoffset_t start_shift;
884 framepos_t new_start;
890 if (position > _position) {
891 start_shift = position - _position;
893 start_shift = -(_position - position);
896 if (start_shift > 0) {
898 if (_start > max_framepos - start_shift) {
899 new_start = max_framepos;
901 new_start = _start + start_shift;
904 } else if (start_shift < 0) {
906 if (_start < -start_shift) {
909 new_start = _start + start_shift;
916 if (!verify_start_and_length (new_start, length)) {
920 PropertyChange what_changed;
922 if (_start != new_start) {
924 what_changed.add (Properties::start);
926 if (_length != length) {
927 if (!property_changes_suspended()) {
928 _last_length = _length;
931 what_changed.add (Properties::length);
933 if (_position != position) {
934 if (!property_changes_suspended()) {
935 _last_position = _position;
937 _position = position;
938 what_changed.add (Properties::position);
943 PropertyChange start_and_length;
945 start_and_length.add (Properties::start);
946 start_and_length.add (Properties::length);
948 if (what_changed.contains (start_and_length)) {
952 if (!what_changed.empty()) {
953 send_change (what_changed);
958 Region::set_hidden (bool yn)
960 if (hidden() != yn) {
962 send_change (Properties::hidden);
967 Region::set_whole_file (bool yn)
970 /* no change signal */
974 Region::set_automatic (bool yn)
977 /* no change signal */
981 Region::set_muted (bool yn)
985 send_change (Properties::muted);
990 Region::set_opaque (bool yn)
992 if (opaque() != yn) {
994 send_change (Properties::opaque);
999 Region::set_locked (bool yn)
1001 if (locked() != yn) {
1003 send_change (Properties::locked);
1008 Region::set_position_locked (bool yn)
1010 if (position_locked() != yn) {
1011 _position_locked = yn;
1012 send_change (Properties::locked);
1016 /** Set the region's sync point.
1017 * @param absolute_pos Session time.
1020 Region::set_sync_position (framepos_t absolute_pos)
1022 /* position within our file */
1023 framepos_t const file_pos = _start + (absolute_pos - _position);
1025 if (file_pos != _sync_position) {
1026 _sync_marked = true;
1027 _sync_position = file_pos;
1028 if (!property_changes_suspended()) {
1031 send_change (Properties::sync_position);
1036 Region::clear_sync_position ()
1038 if (sync_marked()) {
1039 _sync_marked = false;
1040 if (!property_changes_suspended()) {
1043 send_change (Properties::sync_position);
1047 /* @return the sync point relative the first frame of the region */
1049 Region::sync_offset (int& dir) const
1051 if (sync_marked()) {
1052 if (_sync_position > _start) {
1054 return _sync_position - _start;
1057 return _start - _sync_position;
1066 Region::adjust_to_sync (framepos_t pos) const
1069 frameoffset_t offset = sync_offset (sync_dir);
1071 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1080 if (max_framepos - pos > offset) {
1088 /** @return Sync position in session time */
1090 Region::sync_position() const
1092 if (sync_marked()) {
1093 return _position - _start + _sync_position;
1095 /* if sync has not been marked, use the start of the region */
1103 boost::shared_ptr<Playlist> pl (playlist());
1105 pl->raise_region (shared_from_this ());
1112 boost::shared_ptr<Playlist> pl (playlist());
1114 pl->lower_region (shared_from_this ());
1120 Region::raise_to_top ()
1122 boost::shared_ptr<Playlist> pl (playlist());
1124 pl->raise_region_to_top (shared_from_this());
1129 Region::lower_to_bottom ()
1131 boost::shared_ptr<Playlist> pl (playlist());
1133 pl->lower_region_to_bottom (shared_from_this());
1138 Region::set_layer (layer_t l)
1143 send_change (Properties::layer);
1150 XMLNode *node = new XMLNode ("Region");
1153 LocaleGuard lg (X_("POSIX"));
1154 const char* fe = NULL;
1156 add_properties (*node);
1158 _id.print (buf, sizeof (buf));
1159 node->add_property ("id", buf);
1160 node->add_property ("type", _type.to_string());
1162 switch (_first_edit) {
1163 case EditChangesNothing:
1166 case EditChangesName:
1172 default: /* should be unreachable but makes g++ happy */
1177 node->add_property ("first-edit", fe);
1179 /* note: flags are stored by derived classes */
1181 if (_position_lock_style != AudioTime) {
1184 node->add_property ("bbt-position", str.str());
1187 for (uint32_t n=0; n < _sources.size(); ++n) {
1188 snprintf (buf2, sizeof(buf2), "source-%d", n);
1189 _sources[n]->id().print (buf, sizeof(buf));
1190 node->add_property (buf2, buf);
1193 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1194 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1195 _master_sources[n]->id().print (buf, sizeof (buf));
1196 node->add_property (buf2, buf);
1200 node->add_child_copy (*_extra_xml);
1207 Region::get_state ()
1213 Region::set_state (const XMLNode& node, int version)
1215 PropertyChange what_changed;
1216 return _set_state (node, version, what_changed, true);
1220 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1222 const XMLProperty* prop;
1224 what_changed = set_values (node);
1226 if ((prop = node.property (X_("id")))) {
1227 _id = prop->value();
1230 if (_position_lock_style == MusicTime) {
1231 if ((prop = node.property ("bbt-position")) == 0) {
1232 /* missing BBT info, revert to audio time locking */
1233 _position_lock_style = AudioTime;
1235 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1238 &_bbt_time.ticks) != 3) {
1239 _position_lock_style = AudioTime;
1244 /* fix problems with old sessions corrupted by impossible
1245 values for _stretch or _shift
1247 if (_stretch == 0.0f) {
1251 if (_shift == 0.0f) {
1255 const XMLNodeList& nlist = node.children();
1257 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1263 if (child->name () == "Extra") {
1265 _extra_xml = new XMLNode (*child);
1271 send_change (what_changed);
1274 /* Quick fix for 2.x sessions when region is muted */
1275 if ((prop = node.property (X_("flags")))) {
1276 if (string::npos != prop->value().find("Muted")){
1286 Region::suspend_property_changes ()
1288 Stateful::suspend_property_changes ();
1289 _last_length = _length;
1290 _last_position = _position;
1294 Region::mid_thaw (const PropertyChange& what_changed)
1296 if (what_changed.contains (Properties::length)) {
1297 if (what_changed.contains (Properties::position)) {
1298 recompute_at_start ();
1300 recompute_at_end ();
1305 Region::send_change (const PropertyChange& what_changed)
1307 if (what_changed.empty()) {
1311 Stateful::send_change (what_changed);
1313 if (!_no_property_changes) {
1315 /* Try and send a shared_pointer unless this is part of the constructor.
1320 boost::shared_ptr<Region> rptr = shared_from_this();
1321 RegionPropertyChanged (rptr, what_changed);
1323 /* no shared_ptr available, relax; */
1329 Region::set_last_layer_op (uint64_t when)
1331 _last_layer_op = when;
1335 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1337 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1341 Region::equivalent (boost::shared_ptr<const Region> other) const
1343 return _start == other->_start &&
1344 _position == other->_position &&
1345 _length == other->_length;
1349 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1351 return _start == other->_start &&
1352 _length == other->_length;
1356 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1358 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1362 Region::source_deleted (boost::weak_ptr<Source>)
1366 if (!_session.deletion_in_progress()) {
1367 /* this is a very special case: at least one of the region's
1368 sources has bee deleted, so invalidate all references to
1369 ourselves. Do NOT do this during session deletion, because
1370 then we run the risk that this will actually result
1371 in this object being deleted (as refcnt goes to zero)
1372 while emitting DropReferences.
1380 Region::master_source_names ()
1382 SourceList::iterator i;
1384 vector<string> names;
1385 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1386 names.push_back((*i)->name());
1393 Region::set_master_sources (const SourceList& srcs)
1395 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1396 (*i)->dec_use_count ();
1399 _master_sources = srcs;
1400 assert (_sources.size() == _master_sources.size());
1402 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1403 (*i)->inc_use_count ();
1408 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1413 if ((_sources.size() != other->_sources.size()) ||
1414 (_master_sources.size() != other->_master_sources.size())) {
1418 SourceList::const_iterator i;
1419 SourceList::const_iterator io;
1421 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1422 if ((*i)->id() != (*io)->id()) {
1427 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1428 if ((*i)->id() != (*io)->id()) {
1437 Region::uses_source (boost::shared_ptr<const Source> source) const
1439 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1448 Region::source_length(uint32_t n) const
1450 assert (n < _sources.size());
1451 return _sources[n]->length(_position - _start);
1455 Region::verify_length (framecnt_t len)
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) - _start);
1467 len = min (len, maxlen);
1473 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1475 if (source() && (source()->destructive() || source()->length_mutable())) {
1479 framecnt_t maxlen = 0;
1481 for (uint32_t n = 0; n < _sources.size(); ++n) {
1482 maxlen = max (maxlen, source_length(n) - new_start);
1485 new_length = min (new_length, maxlen);
1491 Region::verify_start (framepos_t pos)
1493 if (source() && (source()->destructive() || source()->length_mutable())) {
1497 for (uint32_t n = 0; n < _sources.size(); ++n) {
1498 if (pos > source_length(n) - _length) {
1506 Region::verify_start_mutable (framepos_t& new_start)
1508 if (source() && (source()->destructive() || source()->length_mutable())) {
1512 for (uint32_t n = 0; n < _sources.size(); ++n) {
1513 if (new_start > source_length(n) - _length) {
1514 new_start = source_length(n) - _length;
1520 boost::shared_ptr<Region>
1521 Region::get_parent() const
1523 boost::shared_ptr<Playlist> pl (playlist());
1526 boost::shared_ptr<Region> r;
1527 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1529 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1530 return boost::static_pointer_cast<Region> (r);
1534 return boost::shared_ptr<Region>();
1538 Region::apply (Filter& filter)
1540 return filter.run (shared_from_this());
1545 Region::invalidate_transients ()
1547 _valid_transients = false;
1548 _transients.clear ();
1550 send_change (PropertyChange (Properties::valid_transients));
1554 Region::drop_sources ()
1556 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1557 (*i)->dec_use_count ();
1562 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1563 (*i)->dec_use_count ();
1566 _master_sources.clear ();
1570 Region::use_sources (SourceList const & s)
1572 set<boost::shared_ptr<Source> > unique_srcs;
1574 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1576 _sources.push_back (*i);
1577 (*i)->inc_use_count ();
1578 _master_sources.push_back (*i);
1579 (*i)->inc_use_count ();
1581 /* connect only once to DropReferences, even if sources are replicated
1584 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1585 unique_srcs.insert (*i);
1586 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));