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 an existing one */
249 Region::Region (boost::shared_ptr<const Region> other)
250 : SessionObject(other->session(), other->name())
251 , _type (other->data_type())
252 , REGION_COPY_STATE (other)
253 , _last_length (other->_last_length)
254 , _last_position(other->_last_position) \
255 , _first_edit (EditChangesNothing)
256 , _read_data_count(0)
258 , _pending_explicit_relayer (false)
261 register_properties ();
263 /* override state that may have been incorrectly inherited from the other region
271 use_sources (other->_sources);
273 _position_lock_style = other->_position_lock_style;
274 _first_edit = other->_first_edit;
276 _start = 0; // It seems strange _start is not inherited here?
278 /* sync pos is relative to start of file. our start-in-file is now zero,
279 so set our sync position to whatever the the difference between
280 _start and _sync_pos was in the other region.
282 result is that our new sync pos points to the same point in our source(s)
283 as the sync in the other region did in its source(s).
285 since we start at zero in our source(s), it is not possible to use a sync point that
286 is before the start. reset it to _start if that was true in the other region.
289 if (other->sync_marked()) {
290 if (other->_start < other->_sync_position) {
291 /* sync pos was after the start point of the other region */
292 _sync_position = other->_sync_position - other->_start;
294 /* sync pos was before the start point of the other region. not possible here. */
295 _sync_marked = false;
296 _sync_position = _start;
299 _sync_marked = false;
300 _sync_position = _start;
303 if (Profile->get_sae()) {
304 /* reset sync point to start if its ended up
305 outside region bounds.
308 if (_sync_position < _start || _sync_position >= _start + _length) {
309 _sync_marked = false;
310 _sync_position = _start;
314 assert (_type == other->data_type());
317 /** Create a new Region from part of an existing one.
319 the start within \a other is given by \a offset
320 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323 : SessionObject(other->session(), other->name())
324 , _type (other->data_type())
325 , REGION_COPY_STATE (other)
326 , _last_length (other->_last_length)
327 , _last_position(other->_last_position) \
328 , _first_edit (EditChangesNothing)
329 , _read_data_count(0)
331 , _pending_explicit_relayer (false)
334 register_properties ();
336 /* override state that may have been incorrectly inherited from the other region
344 use_sources (other->_sources);
346 _start = other->_start + offset;
348 /* if the other region had a distinct sync point
349 set, then continue to use it as best we can.
350 otherwise, reset sync point back to start.
353 if (other->sync_marked()) {
354 if (other->_sync_position < _start) {
355 _sync_marked = false;
356 _sync_position = _start;
358 _sync_position = other->_sync_position;
361 _sync_marked = false;
362 _sync_position = _start;
365 if (Profile->get_sae()) {
366 /* reset sync point to start if its ended up
367 outside region bounds.
370 if (_sync_position < _start || _sync_position >= _start + _length) {
371 _sync_marked = false;
372 _sync_position = _start;
376 assert (_type == other->data_type());
379 /** Create a copy of @param other but with different sources. Used by filters */
380 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
381 : SessionObject (other->session(), other->name())
382 , _type (srcs.front()->type())
383 , REGION_COPY_STATE (other)
384 , _last_length (other->_last_length)
385 , _last_position (other->_last_position)
386 , _first_edit (EditChangesID)
387 , _read_data_count (0)
388 , _last_layer_op (other->_last_layer_op)
389 , _pending_explicit_relayer (false)
391 register_properties ();
394 _position_locked = false;
396 other->_first_edit = EditChangesName;
398 if (other->_extra_xml) {
399 _extra_xml = new XMLNode (*other->_extra_xml);
405 assert(_sources.size() > 0);
410 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
415 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
417 _playlist = wpl.lock();
421 Region::set_name (const std::string& str)
424 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
425 assert(_name == str);
427 send_change (Properties::name);
434 Region::set_length (framecnt_t len, void */*src*/)
436 //cerr << "Region::set_length() len = " << len << endl;
441 if (_length != len && len != 0) {
443 /* check that the current _position wouldn't make the new
447 if (max_framepos - len < _position) {
451 if (!verify_length (len)) {
456 _last_length = _length;
457 set_length_internal (len);
461 invalidate_transients ();
463 if (!property_changes_suspended()) {
467 send_change (Properties::length);
472 Region::set_length_internal (framecnt_t len)
478 Region::maybe_uncopy ()
480 /* this does nothing but marked a semantic moment once upon a time */
484 Region::first_edit ()
486 boost::shared_ptr<Playlist> pl (playlist());
488 if (_first_edit != EditChangesNothing && pl) {
490 _name = RegionFactory::new_region_name (_name);
491 _first_edit = EditChangesNothing;
493 send_change (Properties::name);
495 RegionFactory::CheckNewRegion (shared_from_this());
500 Region::at_natural_position () const
502 boost::shared_ptr<Playlist> pl (playlist());
508 boost::shared_ptr<Region> whole_file_region = get_parent();
510 if (whole_file_region) {
511 if (_position == whole_file_region->position() + _start) {
520 Region::move_to_natural_position (void *src)
522 boost::shared_ptr<Playlist> pl (playlist());
528 boost::shared_ptr<Region> whole_file_region = get_parent();
530 if (whole_file_region) {
531 set_position (whole_file_region->position() + _start, src);
536 Region::special_set_position (framepos_t pos)
538 /* this is used when creating a whole file region as
539 a way to store its "natural" or "captured" position.
542 _position = _position;
547 Region::set_position_lock_style (PositionLockStyle ps)
549 if (_position_lock_style != ps) {
551 boost::shared_ptr<Playlist> pl (playlist());
557 _position_lock_style = ps;
559 if (_position_lock_style == MusicTime) {
560 _session.tempo_map().bbt_time (_position, _bbt_time);
563 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.
649 send_change (Properties::position);
653 Region::recompute_position_from_lock_style ()
655 if (_position_lock_style == MusicTime) {
656 _session.tempo_map().bbt_time (_position, _bbt_time);
661 Region::nudge_position (frameoffset_t n, void* /*src*/)
671 framepos_t new_position = _position;
674 if (_position > max_framepos - n) {
675 new_position = max_framepos;
680 if (_position < -n) {
687 set_position_internal (new_position, true);
689 send_change (Properties::position);
693 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
695 _ancestral_length = l;
696 _ancestral_start = s;
702 Region::set_start (framepos_t pos, void* /*src*/)
704 if (locked() || position_locked()) {
707 /* This just sets the start, nothing else. It effectively shifts
708 the contents of the Region within the overall extent of the Source,
709 without changing the Region's position or length
714 if (!verify_start (pos)) {
721 invalidate_transients ();
723 send_change (Properties::start);
728 Region::trim_start (framepos_t new_position, void */*src*/)
730 if (locked() || position_locked()) {
733 framepos_t new_start;
734 frameoffset_t const start_shift = new_position - _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;
760 if (new_start == _start) {
768 send_change (Properties::start);
772 Region::trim_front (framepos_t new_position, void *src)
774 modify_front (new_position, false, src);
778 Region::cut_front (framepos_t new_position, void *src)
780 modify_front (new_position, true, src);
784 Region::cut_end (framepos_t new_endpoint, void *src)
786 modify_end (new_endpoint, true, src);
790 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
796 framepos_t end = last_frame();
797 framepos_t source_zero;
799 if (_position > _start) {
800 source_zero = _position - _start;
802 source_zero = 0; // its actually negative, but this will work for us
805 if (new_position < end) { /* can't trim it zero or negative length */
807 framecnt_t newlen = 0;
808 framepos_t delta = 0;
810 if (!can_trim_start_before_source_start ()) {
811 /* can't trim it back past where source position zero is located */
812 new_position = max (new_position, source_zero);
815 if (new_position > _position) {
816 newlen = _length - (new_position - _position);
817 delta = -1 * (new_position - _position);
819 newlen = _length + (_position - new_position);
820 delta = _position - new_position;
823 trim_to_internal (new_position, newlen, src);
826 _right_of_split = true;
829 if (!property_changes_suspended()) {
830 recompute_at_start ();
833 if (_transients.size() > 0){
834 adjust_transients(delta);
840 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
846 if (new_endpoint > _position) {
847 trim_to_internal (_position, new_endpoint - _position +1, this);
849 _left_of_split = true;
851 if (!property_changes_suspended()) {
857 /** @param new_endpoint New region end point, such that, for example,
858 * a region at 0 of length 10 has an endpoint of 9.
862 Region::trim_end (framepos_t new_endpoint, void* src)
864 modify_end (new_endpoint, false, src);
868 Region::trim_to (framepos_t position, framecnt_t length, void *src)
874 trim_to_internal (position, length, src);
876 if (!property_changes_suspended()) {
877 recompute_at_start ();
883 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
885 framepos_t new_start;
891 frameoffset_t const start_shift = position - _position;
893 if (start_shift > 0) {
895 if (_start > max_framepos - start_shift) {
896 new_start = max_framepos;
898 new_start = _start + start_shift;
901 } else if (start_shift < 0) {
903 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
906 new_start = _start + start_shift;
913 if (!verify_start_and_length (new_start, length)) {
917 PropertyChange what_changed;
919 if (_start != new_start) {
921 what_changed.add (Properties::start);
924 /* Set position before length, otherwise for MIDI regions this bad thing happens:
925 * 1. we call set_length_internal; length in beats is computed using the region's current
926 * (soon-to-be old) position
927 * 2. we call set_position_internal; position is set and length in frames re-computed using
928 * length in beats from (1) but at the new position, which is wrong if the region
929 * straddles a tempo/meter change.
932 if (_position != position) {
933 if (!property_changes_suspended()) {
934 _last_position = _position;
936 set_position_internal (position, true);
937 what_changed.add (Properties::position);
940 if (_length != length) {
941 if (!property_changes_suspended()) {
942 _last_length = _length;
944 set_length_internal (length);
945 what_changed.add (Properties::length);
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()) {
1039 send_change (Properties::sync_position);
1044 Region::clear_sync_position ()
1046 if (sync_marked()) {
1047 _sync_marked = false;
1048 if (!property_changes_suspended()) {
1052 send_change (Properties::sync_position);
1056 /* @return the sync point relative the first frame of the region */
1058 Region::sync_offset (int& dir) const
1060 if (sync_marked()) {
1061 if (_sync_position > _start) {
1063 return _sync_position - _start;
1066 return _start - _sync_position;
1075 Region::adjust_to_sync (framepos_t pos) const
1078 frameoffset_t offset = sync_offset (sync_dir);
1080 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1089 if (max_framepos - pos > offset) {
1097 /** @return Sync position in session time */
1099 Region::sync_position() const
1101 if (sync_marked()) {
1102 return _position - _start + _sync_position;
1104 /* if sync has not been marked, use the start of the region */
1112 boost::shared_ptr<Playlist> pl (playlist());
1114 pl->raise_region (shared_from_this ());
1121 boost::shared_ptr<Playlist> pl (playlist());
1123 pl->lower_region (shared_from_this ());
1129 Region::raise_to_top ()
1131 boost::shared_ptr<Playlist> pl (playlist());
1133 pl->raise_region_to_top (shared_from_this());
1138 Region::lower_to_bottom ()
1140 boost::shared_ptr<Playlist> pl (playlist());
1142 pl->lower_region_to_bottom (shared_from_this());
1147 Region::set_layer (layer_t l)
1152 send_change (Properties::layer);
1159 XMLNode *node = new XMLNode ("Region");
1162 LocaleGuard lg (X_("POSIX"));
1163 const char* fe = NULL;
1165 add_properties (*node);
1167 _id.print (buf, sizeof (buf));
1168 node->add_property ("id", buf);
1169 node->add_property ("type", _type.to_string());
1171 switch (_first_edit) {
1172 case EditChangesNothing:
1175 case EditChangesName:
1181 default: /* should be unreachable but makes g++ happy */
1186 node->add_property ("first-edit", fe);
1188 /* note: flags are stored by derived classes */
1190 if (_position_lock_style != AudioTime) {
1193 node->add_property ("bbt-position", str.str());
1196 for (uint32_t n=0; n < _sources.size(); ++n) {
1197 snprintf (buf2, sizeof(buf2), "source-%d", n);
1198 _sources[n]->id().print (buf, sizeof(buf));
1199 node->add_property (buf2, buf);
1202 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1203 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1204 _master_sources[n]->id().print (buf, sizeof (buf));
1205 node->add_property (buf2, buf);
1209 node->add_child_copy (*_extra_xml);
1216 Region::get_state ()
1222 Region::set_state (const XMLNode& node, int version)
1224 PropertyChange what_changed;
1225 return _set_state (node, version, what_changed, true);
1229 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1231 const XMLProperty* prop;
1233 what_changed = set_values (node);
1235 if ((prop = node.property (X_("id")))) {
1236 _id = prop->value();
1239 if (_position_lock_style == MusicTime) {
1240 if ((prop = node.property ("bbt-position")) == 0) {
1241 /* missing BBT info, revert to audio time locking */
1242 _position_lock_style = AudioTime;
1244 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1247 &_bbt_time.ticks) != 3) {
1248 _position_lock_style = AudioTime;
1253 /* fix problems with old sessions corrupted by impossible
1254 values for _stretch or _shift
1256 if (_stretch == 0.0f) {
1260 if (_shift == 0.0f) {
1264 const XMLNodeList& nlist = node.children();
1266 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1272 if (child->name () == "Extra") {
1274 _extra_xml = new XMLNode (*child);
1280 send_change (what_changed);
1283 /* Quick fix for 2.x sessions when region is muted */
1284 if ((prop = node.property (X_("flags")))) {
1285 if (string::npos != prop->value().find("Muted")){
1295 Region::suspend_property_changes ()
1297 Stateful::suspend_property_changes ();
1298 _last_length = _length;
1299 _last_position = _position;
1303 Region::mid_thaw (const PropertyChange& what_changed)
1305 if (what_changed.contains (Properties::length)) {
1306 if (what_changed.contains (Properties::position)) {
1307 recompute_at_start ();
1309 recompute_at_end ();
1314 Region::send_change (const PropertyChange& what_changed)
1316 if (what_changed.empty()) {
1320 Stateful::send_change (what_changed);
1322 if (!Stateful::frozen()) {
1324 /* Try and send a shared_pointer unless this is part of the constructor.
1329 boost::shared_ptr<Region> rptr = shared_from_this();
1330 RegionPropertyChanged (rptr, what_changed);
1332 /* no shared_ptr available, relax; */
1338 Region::set_last_layer_op (uint64_t when)
1340 _last_layer_op = when;
1344 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1346 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1350 Region::equivalent (boost::shared_ptr<const Region> other) const
1352 return _start == other->_start &&
1353 _position == other->_position &&
1354 _length == other->_length;
1358 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1360 return _start == other->_start &&
1361 _length == other->_length;
1365 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1367 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1371 Region::source_deleted (boost::weak_ptr<Source>)
1375 if (!_session.deletion_in_progress()) {
1376 /* this is a very special case: at least one of the region's
1377 sources has bee deleted, so invalidate all references to
1378 ourselves. Do NOT do this during session deletion, because
1379 then we run the risk that this will actually result
1380 in this object being deleted (as refcnt goes to zero)
1381 while emitting DropReferences.
1389 Region::master_source_names ()
1391 SourceList::iterator i;
1393 vector<string> names;
1394 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1395 names.push_back((*i)->name());
1402 Region::set_master_sources (const SourceList& srcs)
1404 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1405 (*i)->dec_use_count ();
1408 _master_sources = srcs;
1409 assert (_sources.size() == _master_sources.size());
1411 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1412 (*i)->inc_use_count ();
1417 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1422 if ((_sources.size() != other->_sources.size()) ||
1423 (_master_sources.size() != other->_master_sources.size())) {
1427 SourceList::const_iterator i;
1428 SourceList::const_iterator io;
1430 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1431 if ((*i)->id() != (*io)->id()) {
1436 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1437 if ((*i)->id() != (*io)->id()) {
1446 Region::source_string () const
1448 //string res = itos(_sources.size());
1451 res << _sources.size() << ":";
1453 SourceList::const_iterator i;
1455 for (i = _sources.begin(); i != _sources.end(); ++i) {
1456 res << (*i)->id() << ":";
1459 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1460 res << (*i)->id() << ":";
1467 Region::uses_source (boost::shared_ptr<const Source> source) const
1469 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1478 Region::source_length(uint32_t n) const
1480 assert (n < _sources.size());
1481 return _sources[n]->length (_position - _start);
1485 Region::verify_length (framecnt_t len)
1487 if (source() && (source()->destructive() || source()->length_mutable())) {
1491 framecnt_t maxlen = 0;
1493 for (uint32_t n = 0; n < _sources.size(); ++n) {
1494 maxlen = max (maxlen, source_length(n) - _start);
1497 len = min (len, maxlen);
1503 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1505 if (source() && (source()->destructive() || source()->length_mutable())) {
1509 framecnt_t maxlen = 0;
1511 for (uint32_t n = 0; n < _sources.size(); ++n) {
1512 maxlen = max (maxlen, source_length(n) - new_start);
1515 new_length = min (new_length, maxlen);
1521 Region::verify_start (framepos_t pos)
1523 if (source() && (source()->destructive() || source()->length_mutable())) {
1527 for (uint32_t n = 0; n < _sources.size(); ++n) {
1528 if (pos > source_length(n) - _length) {
1536 Region::verify_start_mutable (framepos_t& new_start)
1538 if (source() && (source()->destructive() || source()->length_mutable())) {
1542 for (uint32_t n = 0; n < _sources.size(); ++n) {
1543 if (new_start > source_length(n) - _length) {
1544 new_start = source_length(n) - _length;
1550 boost::shared_ptr<Region>
1551 Region::get_parent() const
1553 boost::shared_ptr<Playlist> pl (playlist());
1556 boost::shared_ptr<Region> r;
1557 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1559 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1560 return boost::static_pointer_cast<Region> (r);
1564 return boost::shared_ptr<Region>();
1568 Region::apply (Filter& filter, Progress* progress)
1570 return filter.run (shared_from_this(), progress);
1575 Region::invalidate_transients ()
1577 _valid_transients = false;
1578 _transients.clear ();
1580 send_change (PropertyChange (Properties::valid_transients));
1584 Region::drop_sources ()
1586 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1587 (*i)->dec_use_count ();
1592 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1593 (*i)->dec_use_count ();
1596 _master_sources.clear ();
1600 Region::use_sources (SourceList const & s)
1602 set<boost::shared_ptr<Source> > unique_srcs;
1604 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1606 _sources.push_back (*i);
1607 (*i)->inc_use_count ();
1608 _master_sources.push_back (*i);
1609 (*i)->inc_use_count ();
1611 /* connect only once to DropReferences, even if sources are replicated
1614 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1615 unique_srcs.insert (*i);
1616 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1622 Region::can_trim () const
1624 CanTrim ct = CanTrim (0);
1630 /* if not locked, we can always move the front later, and the end earlier
1633 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1635 if (start() != 0 || can_trim_start_before_source_start ()) {
1636 ct = CanTrim (ct | FrontTrimEarlier);
1639 if (!_sources.empty()) {
1640 if ((start() + length()) < _sources.front()->length (0)) {
1641 ct = CanTrim (ct | EndTrimLater);