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/file_source.h"
33 #include "ardour/filter.h"
34 #include "ardour/playlist.h"
35 #include "ardour/playlist_source.h"
36 #include "ardour/profile.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
40 #include "ardour/source.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/utils.h"
48 using namespace ARDOUR;
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> muted;
54 PBD::PropertyDescriptor<bool> opaque;
55 PBD::PropertyDescriptor<bool> locked;
56 PBD::PropertyDescriptor<bool> automatic;
57 PBD::PropertyDescriptor<bool> whole_file;
58 PBD::PropertyDescriptor<bool> import;
59 PBD::PropertyDescriptor<bool> external;
60 PBD::PropertyDescriptor<bool> sync_marked;
61 PBD::PropertyDescriptor<bool> left_of_split;
62 PBD::PropertyDescriptor<bool> right_of_split;
63 PBD::PropertyDescriptor<bool> hidden;
64 PBD::PropertyDescriptor<bool> position_locked;
65 PBD::PropertyDescriptor<bool> valid_transients;
66 PBD::PropertyDescriptor<framepos_t> start;
67 PBD::PropertyDescriptor<framecnt_t> length;
68 PBD::PropertyDescriptor<framepos_t> position;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::PropertyDescriptor<uint64_t> layering_index;
80 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
83 Region::make_property_quarks ()
85 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
87 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
89 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
91 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
93 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
95 Properties::import.property_id = g_quark_from_static_string (X_("import"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
97 Properties::external.property_id = g_quark_from_static_string (X_("external"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
99 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
101 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
103 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
105 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
107 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
109 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
111 Properties::start.property_id = g_quark_from_static_string (X_("start"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
113 Properties::length.property_id = g_quark_from_static_string (X_("length"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
115 Properties::position.property_id = g_quark_from_static_string (X_("position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
117 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
119 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
121 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
123 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
125 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
127 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
129 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
130 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
132 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
136 Region::register_properties ()
138 _xml_node_name = X_("Region");
140 add_property (_muted);
141 add_property (_opaque);
142 add_property (_locked);
143 add_property (_automatic);
144 add_property (_whole_file);
145 add_property (_import);
146 add_property (_external);
147 add_property (_sync_marked);
148 add_property (_left_of_split);
149 add_property (_right_of_split);
150 add_property (_hidden);
151 add_property (_position_locked);
152 add_property (_valid_transients);
153 add_property (_start);
154 add_property (_length);
155 add_property (_position);
156 add_property (_sync_position);
157 add_property (_ancestral_start);
158 add_property (_ancestral_length);
159 add_property (_stretch);
160 add_property (_shift);
161 add_property (_position_lock_style);
162 add_property (_layering_index);
165 #define REGION_DEFAULT_STATE(s,l) \
166 _sync_marked (Properties::sync_marked, false) \
167 , _left_of_split (Properties::left_of_split, false) \
168 , _right_of_split (Properties::right_of_split, false) \
169 , _valid_transients (Properties::valid_transients, false) \
170 , _start (Properties::start, (s)) \
171 , _length (Properties::length, (l)) \
172 , _position (Properties::position, 0) \
173 , _sync_position (Properties::sync_position, (s)) \
174 , _muted (Properties::muted, false) \
175 , _opaque (Properties::opaque, true) \
176 , _locked (Properties::locked, false) \
177 , _automatic (Properties::automatic, false) \
178 , _whole_file (Properties::whole_file, false) \
179 , _import (Properties::import, false) \
180 , _external (Properties::external, false) \
181 , _hidden (Properties::hidden, false) \
182 , _position_locked (Properties::position_locked, false) \
183 , _ancestral_start (Properties::ancestral_start, (s)) \
184 , _ancestral_length (Properties::ancestral_length, (l)) \
185 , _stretch (Properties::stretch, 1.0) \
186 , _shift (Properties::shift, 1.0) \
187 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
188 , _layering_index (Properties::layering_index, 0)
190 #define REGION_COPY_STATE(other) \
191 _sync_marked (Properties::sync_marked, other->_sync_marked) \
192 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
193 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
194 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
195 , _start(Properties::start, other->_start) \
196 , _length(Properties::length, other->_length) \
197 , _position(Properties::position, other->_position) \
198 , _sync_position(Properties::sync_position, other->_sync_position) \
199 , _muted (Properties::muted, other->_muted) \
200 , _opaque (Properties::opaque, other->_opaque) \
201 , _locked (Properties::locked, other->_locked) \
202 , _automatic (Properties::automatic, other->_automatic) \
203 , _whole_file (Properties::whole_file, other->_whole_file) \
204 , _import (Properties::import, other->_import) \
205 , _external (Properties::external, other->_external) \
206 , _hidden (Properties::hidden, other->_hidden) \
207 , _position_locked (Properties::position_locked, other->_position_locked) \
208 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
209 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
210 , _stretch (Properties::stretch, other->_stretch) \
211 , _shift (Properties::shift, other->_shift) \
212 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
213 , _layering_index (Properties::layering_index, other->_layering_index)
215 /* derived-from-derived constructor (no sources in constructor) */
216 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
217 : SessionObject(s, name)
219 , REGION_DEFAULT_STATE(start,length)
220 , _last_length (length)
222 , _first_edit (EditChangesNothing)
225 register_properties ();
227 /* no sources at this point */
230 /** Basic Region constructor (many sources) */
231 Region::Region (const SourceList& srcs)
232 : SessionObject(srcs.front()->session(), "toBeRenamed")
233 , _type (srcs.front()->type())
234 , REGION_DEFAULT_STATE(0,0)
237 , _first_edit (EditChangesNothing)
240 register_properties ();
242 _type = srcs.front()->type();
246 assert(_sources.size() > 0);
247 assert (_type == srcs.front()->type());
250 /** Create a new Region from an existing one */
251 Region::Region (boost::shared_ptr<const Region> other)
252 : SessionObject(other->session(), other->name())
253 , _type (other->data_type())
254 , REGION_COPY_STATE (other)
255 , _last_length (other->_last_length)
256 , _last_position(other->_last_position) \
257 , _first_edit (EditChangesNothing)
258 , _layer (other->_layer)
260 register_properties ();
262 /* override state that may have been incorrectly inherited from the other region
270 use_sources (other->_sources);
272 _position_lock_style = other->_position_lock_style;
273 _first_edit = other->_first_edit;
275 _start = 0; // It seems strange _start is not inherited here?
277 /* sync pos is relative to start of file. our start-in-file is now zero,
278 so set our sync position to whatever the the difference between
279 _start and _sync_pos was in the other region.
281 result is that our new sync pos points to the same point in our source(s)
282 as the sync in the other region did in its source(s).
284 since we start at zero in our source(s), it is not possible to use a sync point that
285 is before the start. reset it to _start if that was true in the other region.
288 if (other->sync_marked()) {
289 if (other->_start < other->_sync_position) {
290 /* sync pos was after the start point of the other region */
291 _sync_position = other->_sync_position - other->_start;
293 /* sync pos was before the start point of the other region. not possible here. */
294 _sync_marked = false;
295 _sync_position = _start;
298 _sync_marked = false;
299 _sync_position = _start;
302 if (Profile->get_sae()) {
303 /* reset sync point to start if its ended up
304 outside region bounds.
307 if (_sync_position < _start || _sync_position >= _start + _length) {
308 _sync_marked = false;
309 _sync_position = _start;
313 assert (_type == other->data_type());
316 /** Create a new Region from part of an existing one.
318 the start within \a other is given by \a offset
319 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
321 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
322 : SessionObject(other->session(), other->name())
323 , _type (other->data_type())
324 , REGION_COPY_STATE (other)
325 , _last_length (other->_last_length)
326 , _last_position(other->_last_position) \
327 , _first_edit (EditChangesNothing)
328 , _layer (other->_layer)
330 register_properties ();
332 /* override state that may have been incorrectly inherited from the other region
340 use_sources (other->_sources);
342 _start = other->_start + offset;
344 /* if the other region had a distinct sync point
345 set, then continue to use it as best we can.
346 otherwise, reset sync point back to start.
349 if (other->sync_marked()) {
350 if (other->_sync_position < _start) {
351 _sync_marked = false;
352 _sync_position = _start;
354 _sync_position = other->_sync_position;
357 _sync_marked = false;
358 _sync_position = _start;
361 if (Profile->get_sae()) {
362 /* reset sync point to start if its ended up
363 outside region bounds.
366 if (_sync_position < _start || _sync_position >= _start + _length) {
367 _sync_marked = false;
368 _sync_position = _start;
372 assert (_type == other->data_type());
375 /** Create a copy of @param other but with different sources. Used by filters */
376 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
377 : SessionObject (other->session(), other->name())
378 , _type (srcs.front()->type())
379 , REGION_COPY_STATE (other)
380 , _last_length (other->_last_length)
381 , _last_position (other->_last_position)
382 , _first_edit (EditChangesID)
383 , _layer (other->_layer)
385 register_properties ();
388 _position_locked = false;
390 other->_first_edit = EditChangesName;
392 if (other->_extra_xml) {
393 _extra_xml = new XMLNode (*other->_extra_xml);
399 assert(_sources.size() > 0);
404 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
409 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
411 _playlist = wpl.lock();
415 Region::set_name (const std::string& str)
418 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
419 assert(_name == str);
421 send_change (Properties::name);
428 Region::set_length (framecnt_t len)
430 //cerr << "Region::set_length() len = " << len << endl;
435 if (_length != len && len != 0) {
437 /* check that the current _position wouldn't make the new
441 if (max_framepos - len < _position) {
445 if (!verify_length (len)) {
450 _last_length = _length;
451 set_length_internal (len);
455 invalidate_transients ();
457 if (!property_changes_suspended()) {
461 send_change (Properties::length);
466 Region::set_length_internal (framecnt_t len)
472 Region::maybe_uncopy ()
474 /* this does nothing but marked a semantic moment once upon a time */
478 Region::first_edit ()
480 boost::shared_ptr<Playlist> pl (playlist());
482 if (_first_edit != EditChangesNothing && pl) {
484 _name = RegionFactory::new_region_name (_name);
485 _first_edit = EditChangesNothing;
487 send_change (Properties::name);
489 RegionFactory::CheckNewRegion (shared_from_this());
494 Region::at_natural_position () const
496 boost::shared_ptr<Playlist> pl (playlist());
502 boost::shared_ptr<Region> whole_file_region = get_parent();
504 if (whole_file_region) {
505 if (_position == whole_file_region->position() + _start) {
514 Region::move_to_natural_position ()
516 boost::shared_ptr<Playlist> pl (playlist());
522 boost::shared_ptr<Region> whole_file_region = get_parent();
524 if (whole_file_region) {
525 set_position (whole_file_region->position() + _start);
530 Region::special_set_position (framepos_t pos)
532 /* this is used when creating a whole file region as
533 a way to store its "natural" or "captured" position.
536 _position = _position;
541 Region::set_position_lock_style (PositionLockStyle ps)
543 if (_position_lock_style != ps) {
545 boost::shared_ptr<Playlist> pl (playlist());
547 _position_lock_style = ps;
549 if (_position_lock_style == MusicTime) {
550 _session.bbt_time (_position, _bbt_time);
553 send_change (Properties::position_lock_style);
558 Region::update_after_tempo_map_change ()
560 boost::shared_ptr<Playlist> pl (playlist());
562 if (!pl || _position_lock_style != MusicTime) {
566 TempoMap& map (_session.tempo_map());
567 framepos_t pos = map.frame_time (_bbt_time);
568 set_position_internal (pos, false);
570 /* do this even if the position is the same. this helps out
571 a GUI that has moved its representation already.
573 send_change (Properties::position);
577 Region::set_position (framepos_t pos)
583 set_position_internal (pos, true);
585 /* do this even if the position is the same. this helps out
586 a GUI that has moved its representation already.
588 send_change (Properties::position);
593 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
595 /* We emit a change of Properties::position even if the position hasn't changed
596 (see Region::set_position), so we must always set this up so that
597 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
599 _last_position = _position;
601 if (_position != pos) {
604 /* check that the new _position wouldn't make the current
605 length impossible - if so, change the length.
607 XXX is this the right thing to do?
610 if (max_framepos - _length < _position) {
611 _last_length = _length;
612 _length = max_framepos - _position;
615 if (allow_bbt_recompute) {
616 recompute_position_from_lock_style ();
619 //invalidate_transients ();
624 Region::recompute_position_from_lock_style ()
626 if (_position_lock_style == MusicTime) {
627 _session.bbt_time (_position, _bbt_time);
632 Region::nudge_position (frameoffset_t n)
642 framepos_t new_position = _position;
645 if (_position > max_framepos - n) {
646 new_position = max_framepos;
651 if (_position < -n) {
658 set_position_internal (new_position, true);
660 send_change (Properties::position);
664 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
666 _ancestral_length = l;
667 _ancestral_start = s;
673 Region::set_start (framepos_t pos)
675 if (locked() || position_locked()) {
678 /* This just sets the start, nothing else. It effectively shifts
679 the contents of the Region within the overall extent of the Source,
680 without changing the Region's position or length
685 if (!verify_start (pos)) {
692 invalidate_transients ();
694 send_change (Properties::start);
699 Region::trim_start (framepos_t new_position)
701 if (locked() || position_locked()) {
704 framepos_t new_start;
705 frameoffset_t const start_shift = new_position - _position;
707 if (start_shift > 0) {
709 if (_start > max_framepos - start_shift) {
710 new_start = max_framepos;
712 new_start = _start + start_shift;
715 if (!verify_start (new_start)) {
719 } else if (start_shift < 0) {
721 if (_start < -start_shift) {
724 new_start = _start + start_shift;
731 if (new_start == _start) {
739 send_change (Properties::start);
743 Region::trim_front (framepos_t new_position)
745 modify_front (new_position, false);
749 Region::cut_front (framepos_t new_position)
751 modify_front (new_position, true);
755 Region::cut_end (framepos_t new_endpoint)
757 modify_end (new_endpoint, true);
761 Region::modify_front (framepos_t new_position, bool reset_fade)
767 framepos_t end = last_frame();
768 framepos_t source_zero;
770 if (_position > _start) {
771 source_zero = _position - _start;
773 source_zero = 0; // its actually negative, but this will work for us
776 if (new_position < end) { /* can't trim it zero or negative length */
778 framecnt_t newlen = 0;
779 framepos_t delta = 0;
781 if (!can_trim_start_before_source_start ()) {
782 /* can't trim it back past where source position zero is located */
783 new_position = max (new_position, source_zero);
786 if (new_position > _position) {
787 newlen = _length - (new_position - _position);
788 delta = -1 * (new_position - _position);
790 newlen = _length + (_position - new_position);
791 delta = _position - new_position;
794 trim_to_internal (new_position, newlen);
797 _right_of_split = true;
800 if (!property_changes_suspended()) {
801 recompute_at_start ();
804 if (_transients.size() > 0){
805 adjust_transients(delta);
811 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
817 if (new_endpoint > _position) {
818 trim_to_internal (_position, new_endpoint - _position);
820 _left_of_split = true;
822 if (!property_changes_suspended()) {
828 /** @param new_endpoint New region end point, such that, for example,
829 * a region at 0 of length 10 has an endpoint of 9.
833 Region::trim_end (framepos_t new_endpoint)
835 modify_end (new_endpoint, false);
839 Region::trim_to (framepos_t position, framecnt_t length)
845 trim_to_internal (position, length);
847 if (!property_changes_suspended()) {
848 recompute_at_start ();
854 Region::trim_to_internal (framepos_t position, framecnt_t length)
856 framepos_t new_start;
862 frameoffset_t const start_shift = position - _position;
864 if (start_shift > 0) {
866 if (_start > max_framepos - start_shift) {
867 new_start = max_framepos;
869 new_start = _start + start_shift;
872 } else if (start_shift < 0) {
874 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
877 new_start = _start + start_shift;
884 if (!verify_start_and_length (new_start, length)) {
888 PropertyChange what_changed;
890 if (_start != new_start) {
892 what_changed.add (Properties::start);
895 /* Set position before length, otherwise for MIDI regions this bad thing happens:
896 * 1. we call set_length_internal; length in beats is computed using the region's current
897 * (soon-to-be old) position
898 * 2. we call set_position_internal; position is set and length in frames re-computed using
899 * length in beats from (1) but at the new position, which is wrong if the region
900 * straddles a tempo/meter change.
903 if (_position != position) {
904 if (!property_changes_suspended()) {
905 _last_position = _position;
907 set_position_internal (position, true);
908 what_changed.add (Properties::position);
911 if (_length != length) {
912 if (!property_changes_suspended()) {
913 _last_length = _length;
915 set_length_internal (length);
916 what_changed.add (Properties::length);
921 PropertyChange start_and_length;
923 start_and_length.add (Properties::start);
924 start_and_length.add (Properties::length);
926 if (what_changed.contains (start_and_length)) {
930 if (!what_changed.empty()) {
931 send_change (what_changed);
936 Region::set_hidden (bool yn)
938 if (hidden() != yn) {
940 send_change (Properties::hidden);
945 Region::set_whole_file (bool yn)
948 /* no change signal */
952 Region::set_automatic (bool yn)
955 /* no change signal */
959 Region::set_muted (bool yn)
963 send_change (Properties::muted);
968 Region::set_opaque (bool yn)
970 if (opaque() != yn) {
972 send_change (Properties::opaque);
977 Region::set_locked (bool yn)
979 if (locked() != yn) {
981 send_change (Properties::locked);
986 Region::set_position_locked (bool yn)
988 if (position_locked() != yn) {
989 _position_locked = yn;
990 send_change (Properties::locked);
994 /** Set the region's sync point.
995 * @param absolute_pos Session time.
998 Region::set_sync_position (framepos_t absolute_pos)
1000 /* position within our file */
1001 framepos_t const file_pos = _start + (absolute_pos - _position);
1003 if (file_pos != _sync_position) {
1004 _sync_marked = true;
1005 _sync_position = file_pos;
1006 if (!property_changes_suspended()) {
1010 send_change (Properties::sync_position);
1015 Region::clear_sync_position ()
1017 if (sync_marked()) {
1018 _sync_marked = false;
1019 if (!property_changes_suspended()) {
1023 send_change (Properties::sync_position);
1027 /* @return the sync point relative the first frame of the region */
1029 Region::sync_offset (int& dir) const
1031 if (sync_marked()) {
1032 if (_sync_position > _start) {
1034 return _sync_position - _start;
1037 return _start - _sync_position;
1046 Region::adjust_to_sync (framepos_t pos) const
1049 frameoffset_t offset = sync_offset (sync_dir);
1051 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1060 if (max_framepos - pos > offset) {
1068 /** @return Sync position in session time */
1070 Region::sync_position() const
1072 if (sync_marked()) {
1073 return _position - _start + _sync_position;
1075 /* if sync has not been marked, use the start of the region */
1083 boost::shared_ptr<Playlist> pl (playlist());
1085 pl->raise_region (shared_from_this ());
1092 boost::shared_ptr<Playlist> pl (playlist());
1094 pl->lower_region (shared_from_this ());
1100 Region::raise_to_top ()
1102 boost::shared_ptr<Playlist> pl (playlist());
1104 pl->raise_region_to_top (shared_from_this());
1109 Region::lower_to_bottom ()
1111 boost::shared_ptr<Playlist> pl (playlist());
1113 pl->lower_region_to_bottom (shared_from_this());
1118 Region::set_layer (layer_t l)
1126 XMLNode *node = new XMLNode ("Region");
1129 LocaleGuard lg (X_("POSIX"));
1130 const char* fe = NULL;
1132 add_properties (*node);
1134 id().print (buf, sizeof (buf));
1135 node->add_property ("id", buf);
1136 node->add_property ("type", _type.to_string());
1138 switch (_first_edit) {
1139 case EditChangesNothing:
1142 case EditChangesName:
1148 default: /* should be unreachable but makes g++ happy */
1153 node->add_property ("first-edit", fe);
1155 /* note: flags are stored by derived classes */
1157 if (_position_lock_style != AudioTime) {
1160 node->add_property ("bbt-position", str.str());
1163 for (uint32_t n=0; n < _sources.size(); ++n) {
1164 snprintf (buf2, sizeof(buf2), "source-%d", n);
1165 _sources[n]->id().print (buf, sizeof(buf));
1166 node->add_property (buf2, buf);
1169 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1170 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1171 _master_sources[n]->id().print (buf, sizeof (buf));
1172 node->add_property (buf2, buf);
1175 /* Only store nested sources for the whole-file region that acts
1176 as the parent/root of all regions using it.
1179 if (_whole_file && max_source_level() > 0) {
1181 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1183 /* region is compound - get its playlist and
1184 store that before we list the region that
1188 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1189 nested_node->add_child_nocopy ((*s)->get_state ());
1193 node->add_child_nocopy (*nested_node);
1198 node->add_child_copy (*_extra_xml);
1205 Region::get_state ()
1211 Region::set_state (const XMLNode& node, int version)
1213 PropertyChange what_changed;
1214 return _set_state (node, version, what_changed, true);
1218 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1220 const XMLProperty* prop;
1222 Stateful::save_extra_xml (node);
1224 what_changed = set_values (node);
1228 if (_position_lock_style == MusicTime) {
1229 if ((prop = node.property ("bbt-position")) == 0) {
1230 /* missing BBT info, revert to audio time locking */
1231 _position_lock_style = AudioTime;
1233 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1236 &_bbt_time.ticks) != 3) {
1237 _position_lock_style = AudioTime;
1242 /* fix problems with old sessions corrupted by impossible
1243 values for _stretch or _shift
1245 if (_stretch == 0.0f) {
1249 if (_shift == 0.0f) {
1254 send_change (what_changed);
1257 /* Quick fix for 2.x sessions when region is muted */
1258 if ((prop = node.property (X_("flags")))) {
1259 if (string::npos != prop->value().find("Muted")){
1269 Region::suspend_property_changes ()
1271 Stateful::suspend_property_changes ();
1272 _last_length = _length;
1273 _last_position = _position;
1277 Region::mid_thaw (const PropertyChange& what_changed)
1279 if (what_changed.contains (Properties::length)) {
1280 if (what_changed.contains (Properties::position)) {
1281 recompute_at_start ();
1283 recompute_at_end ();
1288 Region::send_change (const PropertyChange& what_changed)
1290 if (what_changed.empty()) {
1294 Stateful::send_change (what_changed);
1296 if (!Stateful::property_changes_suspended()) {
1298 /* Try and send a shared_pointer unless this is part of the constructor.
1303 boost::shared_ptr<Region> rptr = shared_from_this();
1304 RegionPropertyChanged (rptr, what_changed);
1306 /* no shared_ptr available, relax; */
1312 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1314 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1318 Region::equivalent (boost::shared_ptr<const Region> other) const
1320 return _start == other->_start &&
1321 _position == other->_position &&
1322 _length == other->_length;
1326 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1328 return _start == other->_start &&
1329 _length == other->_length;
1333 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1335 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1339 Region::source_deleted (boost::weak_ptr<Source>)
1343 if (!_session.deletion_in_progress()) {
1344 /* this is a very special case: at least one of the region's
1345 sources has bee deleted, so invalidate all references to
1346 ourselves. Do NOT do this during session deletion, because
1347 then we run the risk that this will actually result
1348 in this object being deleted (as refcnt goes to zero)
1349 while emitting DropReferences.
1357 Region::master_source_names ()
1359 SourceList::iterator i;
1361 vector<string> names;
1362 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1363 names.push_back((*i)->name());
1370 Region::set_master_sources (const SourceList& srcs)
1372 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1373 (*i)->dec_use_count ();
1376 _master_sources = srcs;
1377 assert (_sources.size() == _master_sources.size());
1379 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1380 (*i)->inc_use_count ();
1385 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1390 if ((_sources.size() != other->_sources.size()) ||
1391 (_master_sources.size() != other->_master_sources.size())) {
1395 SourceList::const_iterator i;
1396 SourceList::const_iterator io;
1398 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1399 if ((*i)->id() != (*io)->id()) {
1404 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1405 if ((*i)->id() != (*io)->id()) {
1414 Region::source_string () const
1416 //string res = itos(_sources.size());
1419 res << _sources.size() << ":";
1421 SourceList::const_iterator i;
1423 for (i = _sources.begin(); i != _sources.end(); ++i) {
1424 res << (*i)->id() << ":";
1427 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1428 res << (*i)->id() << ":";
1435 Region::uses_source (boost::shared_ptr<const Source> source) const
1437 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1442 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1445 if (ps->playlist()->uses_source (source)) {
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);
1612 if (start() != 0 || can_trim_start_before_source_start ()) {
1613 ct = CanTrim (ct | FrontTrimEarlier);
1616 if (!_sources.empty()) {
1617 if ((start() + length()) < _sources.front()->length (0)) {
1618 ct = CanTrim (ct | EndTrimLater);
1626 Region::max_source_level () const
1630 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1631 lvl = max (lvl, (*i)->level());
1638 Region::is_compound () const
1640 return max_source_level() > 0;
1644 Region::post_set (const PropertyChange& pc)
1646 if (pc.contains (Properties::position)) {
1647 recompute_position_from_lock_style ();