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);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
584 send_change (Properties::position);
588 Region::set_position (framepos_t pos, void* /*src*/)
594 set_position_internal (pos, true);
596 /* do this even if the position is the same. this helps out
597 a GUI that has moved its representation already.
599 send_change (Properties::position);
604 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
606 if (_position != pos) {
607 _last_position = _position;
610 /* check that the new _position wouldn't make the current
611 length impossible - if so, change the length.
613 XXX is this the right thing to do?
616 if (max_framepos - _length < _position) {
617 _last_length = _length;
618 _length = max_framepos - _position;
621 if (allow_bbt_recompute) {
622 recompute_position_from_lock_style ();
625 //invalidate_transients ();
630 Region::set_position_on_top (framepos_t pos, void* /*src*/)
636 if (_position != pos) {
637 set_position_internal (pos, true);
640 boost::shared_ptr<Playlist> pl (playlist());
643 pl->raise_region_to_top (shared_from_this ());
646 /* do this even if the position is the same. this helps out
647 a GUI that has moved its representation already.
650 send_change (Properties::position);
654 Region::recompute_position_from_lock_style ()
656 if (_position_lock_style == MusicTime) {
657 _session.tempo_map().bbt_time (_position, _bbt_time);
662 Region::nudge_position (frameoffset_t n, void* /*src*/)
672 framepos_t new_position = _position;
675 if (_position > max_framepos - n) {
676 new_position = max_framepos;
681 if (_position < -n) {
688 set_position_internal (new_position, true);
690 send_change (Properties::position);
694 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
696 _ancestral_length = l;
697 _ancestral_start = s;
703 Region::set_start (framepos_t pos, void* /*src*/)
705 if (locked() || position_locked()) {
708 /* This just sets the start, nothing else. It effectively shifts
709 the contents of the Region within the overall extent of the Source,
710 without changing the Region's position or length
715 if (!verify_start (pos)) {
722 invalidate_transients ();
724 send_change (Properties::start);
729 Region::trim_start (framepos_t new_position, void */*src*/)
731 if (locked() || position_locked()) {
734 framepos_t new_start;
735 frameoffset_t start_shift;
737 if (new_position > _position) {
738 start_shift = new_position - _position;
740 start_shift = -(_position - new_position);
743 if (start_shift > 0) {
745 if (_start > max_framepos - start_shift) {
746 new_start = max_framepos;
748 new_start = _start + start_shift;
751 if (!verify_start (new_start)) {
755 } else if (start_shift < 0) {
757 if (_start < -start_shift) {
760 new_start = _start + start_shift;
766 if (new_start == _start) {
774 send_change (Properties::start);
778 Region::trim_front (framepos_t new_position, void *src)
780 modify_front (new_position, false, src);
784 Region::cut_front (framepos_t new_position, void *src)
786 modify_front (new_position, true, src);
790 Region::cut_end (framepos_t new_endpoint, void *src)
792 modify_end (new_endpoint, true, src);
796 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
802 framepos_t end = last_frame();
803 framepos_t source_zero;
805 if (_position > _start) {
806 source_zero = _position - _start;
808 source_zero = 0; // its actually negative, but this will work for us
811 if (new_position < end) { /* can't trim it zero or negative length */
813 framecnt_t newlen = 0;
814 framepos_t delta = 0;
816 /* can't trim it back passed where source position zero is located */
818 new_position = max (new_position, source_zero);
820 if (new_position > _position) {
821 newlen = _length - (new_position - _position);
822 delta = -1 * (new_position - _position);
824 newlen = _length + (_position - new_position);
825 delta = _position - new_position;
828 trim_to_internal (new_position, newlen, src);
831 _right_of_split = true;
834 if (!property_changes_suspended()) {
835 recompute_at_start ();
838 if (_transients.size() > 0){
839 adjust_transients(delta);
845 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
851 if (new_endpoint > _position) {
852 trim_to_internal (_position, new_endpoint - _position +1, this);
854 _left_of_split = true;
856 if (!property_changes_suspended()) {
862 /** @param new_endpoint New region end point, such that, for example,
863 * a region at 0 of length 10 has an endpoint of 9.
867 Region::trim_end (framepos_t new_endpoint, void* src)
869 modify_end (new_endpoint, false, src);
873 Region::trim_to (framepos_t position, framecnt_t length, void *src)
879 trim_to_internal (position, length, src);
881 if (!property_changes_suspended()) {
882 recompute_at_start ();
888 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
890 frameoffset_t start_shift;
891 framepos_t new_start;
897 if (position > _position) {
898 start_shift = position - _position;
900 start_shift = -(_position - position);
903 if (start_shift > 0) {
905 if (_start > max_framepos - start_shift) {
906 new_start = max_framepos;
908 new_start = _start + start_shift;
911 } else if (start_shift < 0) {
913 if (_start < -start_shift) {
916 new_start = _start + start_shift;
923 if (!verify_start_and_length (new_start, length)) {
927 PropertyChange what_changed;
929 if (_start != new_start) {
931 what_changed.add (Properties::start);
933 if (_length != length) {
934 if (!property_changes_suspended()) {
935 _last_length = _length;
938 what_changed.add (Properties::length);
940 if (_position != position) {
941 if (!property_changes_suspended()) {
942 _last_position = _position;
944 set_position_internal (position, true);
945 what_changed.add (Properties::position);
950 PropertyChange start_and_length;
952 start_and_length.add (Properties::start);
953 start_and_length.add (Properties::length);
955 if (what_changed.contains (start_and_length)) {
959 if (!what_changed.empty()) {
960 send_change (what_changed);
965 Region::set_hidden (bool yn)
967 if (hidden() != yn) {
969 send_change (Properties::hidden);
974 Region::set_whole_file (bool yn)
977 /* no change signal */
981 Region::set_automatic (bool yn)
984 /* no change signal */
988 Region::set_muted (bool yn)
992 send_change (Properties::muted);
997 Region::set_opaque (bool yn)
999 if (opaque() != yn) {
1001 send_change (Properties::opaque);
1006 Region::set_locked (bool yn)
1008 if (locked() != yn) {
1010 send_change (Properties::locked);
1015 Region::set_position_locked (bool yn)
1017 if (position_locked() != yn) {
1018 _position_locked = yn;
1019 send_change (Properties::locked);
1023 /** Set the region's sync point.
1024 * @param absolute_pos Session time.
1027 Region::set_sync_position (framepos_t absolute_pos)
1029 /* position within our file */
1030 framepos_t const file_pos = _start + (absolute_pos - _position);
1032 if (file_pos != _sync_position) {
1033 _sync_marked = true;
1034 _sync_position = file_pos;
1035 if (!property_changes_suspended()) {
1038 send_change (Properties::sync_position);
1043 Region::clear_sync_position ()
1045 if (sync_marked()) {
1046 _sync_marked = false;
1047 if (!property_changes_suspended()) {
1050 send_change (Properties::sync_position);
1054 /* @return the sync point relative the first frame of the region */
1056 Region::sync_offset (int& dir) const
1058 if (sync_marked()) {
1059 if (_sync_position > _start) {
1061 return _sync_position - _start;
1064 return _start - _sync_position;
1073 Region::adjust_to_sync (framepos_t pos) const
1076 frameoffset_t offset = sync_offset (sync_dir);
1078 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1087 if (max_framepos - pos > offset) {
1095 /** @return Sync position in session time */
1097 Region::sync_position() const
1099 if (sync_marked()) {
1100 return _position - _start + _sync_position;
1102 /* if sync has not been marked, use the start of the region */
1110 boost::shared_ptr<Playlist> pl (playlist());
1112 pl->raise_region (shared_from_this ());
1119 boost::shared_ptr<Playlist> pl (playlist());
1121 pl->lower_region (shared_from_this ());
1127 Region::raise_to_top ()
1129 boost::shared_ptr<Playlist> pl (playlist());
1131 pl->raise_region_to_top (shared_from_this());
1136 Region::lower_to_bottom ()
1138 boost::shared_ptr<Playlist> pl (playlist());
1140 pl->lower_region_to_bottom (shared_from_this());
1145 Region::set_layer (layer_t l)
1150 send_change (Properties::layer);
1157 XMLNode *node = new XMLNode ("Region");
1160 LocaleGuard lg (X_("POSIX"));
1161 const char* fe = NULL;
1163 add_properties (*node);
1165 _id.print (buf, sizeof (buf));
1166 node->add_property ("id", buf);
1167 node->add_property ("type", _type.to_string());
1169 switch (_first_edit) {
1170 case EditChangesNothing:
1173 case EditChangesName:
1179 default: /* should be unreachable but makes g++ happy */
1184 node->add_property ("first-edit", fe);
1186 /* note: flags are stored by derived classes */
1188 if (_position_lock_style != AudioTime) {
1191 node->add_property ("bbt-position", str.str());
1194 for (uint32_t n=0; n < _sources.size(); ++n) {
1195 snprintf (buf2, sizeof(buf2), "source-%d", n);
1196 _sources[n]->id().print (buf, sizeof(buf));
1197 node->add_property (buf2, buf);
1200 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1201 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1202 _master_sources[n]->id().print (buf, sizeof (buf));
1203 node->add_property (buf2, buf);
1207 node->add_child_copy (*_extra_xml);
1214 Region::get_state ()
1220 Region::set_state (const XMLNode& node, int version)
1222 PropertyChange what_changed;
1223 return _set_state (node, version, what_changed, true);
1227 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1229 const XMLProperty* prop;
1231 what_changed = set_values (node);
1233 if ((prop = node.property (X_("id")))) {
1234 _id = prop->value();
1237 if (_position_lock_style == MusicTime) {
1238 if ((prop = node.property ("bbt-position")) == 0) {
1239 /* missing BBT info, revert to audio time locking */
1240 _position_lock_style = AudioTime;
1242 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1245 &_bbt_time.ticks) != 3) {
1246 _position_lock_style = AudioTime;
1251 /* fix problems with old sessions corrupted by impossible
1252 values for _stretch or _shift
1254 if (_stretch == 0.0f) {
1258 if (_shift == 0.0f) {
1262 const XMLNodeList& nlist = node.children();
1264 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1270 if (child->name () == "Extra") {
1272 _extra_xml = new XMLNode (*child);
1278 send_change (what_changed);
1281 /* Quick fix for 2.x sessions when region is muted */
1282 if ((prop = node.property (X_("flags")))) {
1283 if (string::npos != prop->value().find("Muted")){
1293 Region::suspend_property_changes ()
1295 Stateful::suspend_property_changes ();
1296 _last_length = _length;
1297 _last_position = _position;
1301 Region::mid_thaw (const PropertyChange& what_changed)
1303 if (what_changed.contains (Properties::length)) {
1304 if (what_changed.contains (Properties::position)) {
1305 recompute_at_start ();
1307 recompute_at_end ();
1312 Region::send_change (const PropertyChange& what_changed)
1314 if (what_changed.empty()) {
1318 Stateful::send_change (what_changed);
1320 if (!Stateful::frozen()) {
1322 /* Try and send a shared_pointer unless this is part of the constructor.
1327 boost::shared_ptr<Region> rptr = shared_from_this();
1328 RegionPropertyChanged (rptr, what_changed);
1330 /* no shared_ptr available, relax; */
1336 Region::set_last_layer_op (uint64_t when)
1338 _last_layer_op = when;
1342 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1344 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1348 Region::equivalent (boost::shared_ptr<const Region> other) const
1350 return _start == other->_start &&
1351 _position == other->_position &&
1352 _length == other->_length;
1356 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1358 return _start == other->_start &&
1359 _length == other->_length;
1363 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1365 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1369 Region::source_deleted (boost::weak_ptr<Source>)
1373 if (!_session.deletion_in_progress()) {
1374 /* this is a very special case: at least one of the region's
1375 sources has bee deleted, so invalidate all references to
1376 ourselves. Do NOT do this during session deletion, because
1377 then we run the risk that this will actually result
1378 in this object being deleted (as refcnt goes to zero)
1379 while emitting DropReferences.
1387 Region::master_source_names ()
1389 SourceList::iterator i;
1391 vector<string> names;
1392 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1393 names.push_back((*i)->name());
1400 Region::set_master_sources (const SourceList& srcs)
1402 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1403 (*i)->dec_use_count ();
1406 _master_sources = srcs;
1407 assert (_sources.size() == _master_sources.size());
1409 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1410 (*i)->inc_use_count ();
1415 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1420 if ((_sources.size() != other->_sources.size()) ||
1421 (_master_sources.size() != other->_master_sources.size())) {
1425 SourceList::const_iterator i;
1426 SourceList::const_iterator io;
1428 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1429 if ((*i)->id() != (*io)->id()) {
1434 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1435 if ((*i)->id() != (*io)->id()) {
1444 Region::uses_source (boost::shared_ptr<const Source> source) const
1446 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1455 Region::source_length(uint32_t n) const
1457 assert (n < _sources.size());
1458 return _sources[n]->length (_position - _start);
1462 Region::verify_length (framecnt_t len)
1464 if (source() && (source()->destructive() || source()->length_mutable())) {
1468 framecnt_t maxlen = 0;
1470 for (uint32_t n = 0; n < _sources.size(); ++n) {
1471 maxlen = max (maxlen, source_length(n) - _start);
1474 len = min (len, maxlen);
1480 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1482 if (source() && (source()->destructive() || source()->length_mutable())) {
1486 framecnt_t maxlen = 0;
1488 for (uint32_t n = 0; n < _sources.size(); ++n) {
1489 maxlen = max (maxlen, source_length(n) - new_start);
1492 new_length = min (new_length, maxlen);
1498 Region::verify_start (framepos_t pos)
1500 if (source() && (source()->destructive() || source()->length_mutable())) {
1504 for (uint32_t n = 0; n < _sources.size(); ++n) {
1505 if (pos > source_length(n) - _length) {
1513 Region::verify_start_mutable (framepos_t& new_start)
1515 if (source() && (source()->destructive() || source()->length_mutable())) {
1519 for (uint32_t n = 0; n < _sources.size(); ++n) {
1520 if (new_start > source_length(n) - _length) {
1521 new_start = source_length(n) - _length;
1527 boost::shared_ptr<Region>
1528 Region::get_parent() const
1530 boost::shared_ptr<Playlist> pl (playlist());
1533 boost::shared_ptr<Region> r;
1534 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1536 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1537 return boost::static_pointer_cast<Region> (r);
1541 return boost::shared_ptr<Region>();
1545 Region::apply (Filter& filter, Progress* progress)
1547 return filter.run (shared_from_this(), progress);
1552 Region::invalidate_transients ()
1554 _valid_transients = false;
1555 _transients.clear ();
1557 send_change (PropertyChange (Properties::valid_transients));
1561 Region::drop_sources ()
1563 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1564 (*i)->dec_use_count ();
1569 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1570 (*i)->dec_use_count ();
1573 _master_sources.clear ();
1577 Region::use_sources (SourceList const & s)
1579 set<boost::shared_ptr<Source> > unique_srcs;
1581 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1583 _sources.push_back (*i);
1584 (*i)->inc_use_count ();
1585 _master_sources.push_back (*i);
1586 (*i)->inc_use_count ();
1588 /* connect only once to DropReferences, even if sources are replicated
1591 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1592 unique_srcs.insert (*i);
1593 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1599 Region::can_trim () const
1601 CanTrim ct = CanTrim (0);
1607 /* if not locked, we can always move the front later, and the end earlier
1610 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1613 ct = CanTrim (ct | FrontTrimEarlier);
1616 if (!_sources.empty()) {
1617 if ((start() + length()) < _sources.front()->length (0)) {
1618 ct = CanTrim (ct | EndTrimLater);