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/threads.h>
27 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
43 using namespace ARDOUR;
48 namespace Properties {
49 PBD::PropertyDescriptor<bool> muted;
50 PBD::PropertyDescriptor<bool> opaque;
51 PBD::PropertyDescriptor<bool> locked;
52 PBD::PropertyDescriptor<bool> video_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;
73 PBD::PropertyDescriptor<uint64_t> layering_index;
77 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
80 Region::make_property_quarks ()
82 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
83 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
84 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
86 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
88 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
90 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
92 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
94 Properties::import.property_id = g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
96 Properties::external.property_id = g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
98 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
100 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
102 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
104 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
106 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
108 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
110 Properties::start.property_id = g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
112 Properties::length.property_id = g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
114 Properties::position.property_id = g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
116 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
118 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
120 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
122 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
124 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
126 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
128 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
130 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
135 Region::register_properties ()
137 _xml_node_name = X_("Region");
139 add_property (_muted);
140 add_property (_opaque);
141 add_property (_locked);
142 add_property (_video_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 , _video_locked (Properties::video_locked, false) \
178 , _automatic (Properties::automatic, false) \
179 , _whole_file (Properties::whole_file, false) \
180 , _import (Properties::import, false) \
181 , _external (Properties::external, false) \
182 , _hidden (Properties::hidden, false) \
183 , _position_locked (Properties::position_locked, false) \
184 , _ancestral_start (Properties::ancestral_start, (s)) \
185 , _ancestral_length (Properties::ancestral_length, (l)) \
186 , _stretch (Properties::stretch, 1.0) \
187 , _shift (Properties::shift, 1.0) \
188 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
189 , _layering_index (Properties::layering_index, 0)
191 #define REGION_COPY_STATE(other) \
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 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
196 , _start(Properties::start, other->_start) \
197 , _length(Properties::length, other->_length) \
198 , _position(Properties::position, other->_position) \
199 , _sync_position(Properties::sync_position, other->_sync_position) \
200 , _muted (Properties::muted, other->_muted) \
201 , _opaque (Properties::opaque, other->_opaque) \
202 , _locked (Properties::locked, other->_locked) \
203 , _video_locked (Properties::video_locked, other->_video_locked) \
204 , _automatic (Properties::automatic, other->_automatic) \
205 , _whole_file (Properties::whole_file, other->_whole_file) \
206 , _import (Properties::import, other->_import) \
207 , _external (Properties::external, other->_external) \
208 , _hidden (Properties::hidden, other->_hidden) \
209 , _position_locked (Properties::position_locked, other->_position_locked) \
210 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
211 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
212 , _stretch (Properties::stretch, other->_stretch) \
213 , _shift (Properties::shift, other->_shift) \
214 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
215 , _layering_index (Properties::layering_index, other->_layering_index)
217 /* derived-from-derived constructor (no sources in constructor) */
218 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
219 : SessionObject(s, name)
221 , REGION_DEFAULT_STATE(start,length)
222 , _last_length (length)
224 , _first_edit (EditChangesNothing)
227 register_properties ();
229 /* no sources at this point */
232 /** Basic Region constructor (many sources) */
233 Region::Region (const SourceList& srcs)
234 : SessionObject(srcs.front()->session(), "toBeRenamed")
235 , _type (srcs.front()->type())
236 , REGION_DEFAULT_STATE(0,0)
239 , _first_edit (EditChangesNothing)
242 register_properties ();
244 _type = srcs.front()->type();
248 assert(_sources.size() > 0);
249 assert (_type == srcs.front()->type());
252 /** Create a new Region from an existing one */
253 Region::Region (boost::shared_ptr<const Region> other)
254 : SessionObject(other->session(), other->name())
255 , _type (other->data_type())
256 , REGION_COPY_STATE (other)
257 , _last_length (other->_last_length)
258 , _last_position(other->_last_position) \
259 , _first_edit (EditChangesNothing)
260 , _layer (other->_layer)
262 register_properties ();
264 /* override state that may have been incorrectly inherited from the other region
272 use_sources (other->_sources);
274 _position_lock_style = other->_position_lock_style;
275 _first_edit = other->_first_edit;
277 _start = 0; // It seems strange _start is not inherited here?
279 /* sync pos is relative to start of file. our start-in-file is now zero,
280 so set our sync position to whatever the the difference between
281 _start and _sync_pos was in the other region.
283 result is that our new sync pos points to the same point in our source(s)
284 as the sync in the other region did in its source(s).
286 since we start at zero in our source(s), it is not possible to use a sync point that
287 is before the start. reset it to _start if that was true in the other region.
290 if (other->sync_marked()) {
291 if (other->_start < other->_sync_position) {
292 /* sync pos was after the start point of the other region */
293 _sync_position = other->_sync_position - other->_start;
295 /* sync pos was before the start point of the other region. not possible here. */
296 _sync_marked = false;
297 _sync_position = _start;
300 _sync_marked = false;
301 _sync_position = _start;
304 if (Profile->get_sae()) {
305 /* reset sync point to start if its ended up
306 outside region bounds.
309 if (_sync_position < _start || _sync_position >= _start + _length) {
310 _sync_marked = false;
311 _sync_position = _start;
315 assert (_type == other->data_type());
318 /** Create a new Region from part of an existing one.
320 the start within \a other is given by \a offset
321 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
323 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
324 : SessionObject(other->session(), other->name())
325 , _type (other->data_type())
326 , REGION_COPY_STATE (other)
327 , _last_length (other->_last_length)
328 , _last_position(other->_last_position) \
329 , _first_edit (EditChangesNothing)
330 , _layer (other->_layer)
332 register_properties ();
334 /* override state that may have been incorrectly inherited from the other region
342 use_sources (other->_sources);
344 _start = other->_start + offset;
346 /* if the other region had a distinct sync point
347 set, then continue to use it as best we can.
348 otherwise, reset sync point back to start.
351 if (other->sync_marked()) {
352 if (other->_sync_position < _start) {
353 _sync_marked = false;
354 _sync_position = _start;
356 _sync_position = other->_sync_position;
359 _sync_marked = false;
360 _sync_position = _start;
363 if (Profile->get_sae()) {
364 /* reset sync point to start if its ended up
365 outside region bounds.
368 if (_sync_position < _start || _sync_position >= _start + _length) {
369 _sync_marked = false;
370 _sync_position = _start;
374 assert (_type == other->data_type());
377 /** Create a copy of @param other but with different sources. Used by filters */
378 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
379 : SessionObject (other->session(), other->name())
380 , _type (srcs.front()->type())
381 , REGION_COPY_STATE (other)
382 , _last_length (other->_last_length)
383 , _last_position (other->_last_position)
384 , _first_edit (EditChangesID)
385 , _layer (other->_layer)
387 register_properties ();
390 _position_locked = false;
392 other->_first_edit = EditChangesName;
394 if (other->_extra_xml) {
395 _extra_xml = new XMLNode (*other->_extra_xml);
401 assert(_sources.size() > 0);
406 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
411 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
413 _playlist = wpl.lock();
417 Region::set_name (const std::string& str)
420 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
421 assert(_name == str);
423 send_change (Properties::name);
430 Region::set_length (framecnt_t len)
432 //cerr << "Region::set_length() len = " << len << endl;
437 if (_length != len && len != 0) {
439 /* check that the current _position wouldn't make the new
443 if (max_framepos - len < _position) {
447 if (!verify_length (len)) {
452 _last_length = _length;
453 set_length_internal (len);
457 invalidate_transients ();
459 if (!property_changes_suspended()) {
463 send_change (Properties::length);
468 Region::set_length_internal (framecnt_t len)
474 Region::maybe_uncopy ()
476 /* this does nothing but marked a semantic moment once upon a time */
480 Region::first_edit ()
482 boost::shared_ptr<Playlist> pl (playlist());
484 if (_first_edit != EditChangesNothing && pl) {
486 _name = RegionFactory::new_region_name (_name);
487 _first_edit = EditChangesNothing;
489 send_change (Properties::name);
491 RegionFactory::CheckNewRegion (shared_from_this());
496 Region::at_natural_position () const
498 boost::shared_ptr<Playlist> pl (playlist());
504 boost::shared_ptr<Region> whole_file_region = get_parent();
506 if (whole_file_region) {
507 if (_position == whole_file_region->position() + _start) {
516 Region::move_to_natural_position ()
518 boost::shared_ptr<Playlist> pl (playlist());
524 boost::shared_ptr<Region> whole_file_region = get_parent();
526 if (whole_file_region) {
527 set_position (whole_file_region->position() + _start);
532 Region::special_set_position (framepos_t pos)
534 /* this is used when creating a whole file region as
535 a way to store its "natural" or "captured" position.
538 _position = _position;
543 Region::set_position_lock_style (PositionLockStyle ps)
545 if (_position_lock_style != ps) {
547 boost::shared_ptr<Playlist> pl (playlist());
549 _position_lock_style = ps;
551 if (_position_lock_style == MusicTime) {
552 _session.bbt_time (_position, _bbt_time);
555 send_change (Properties::position_lock_style);
560 Region::update_after_tempo_map_change ()
562 boost::shared_ptr<Playlist> pl (playlist());
564 if (!pl || _position_lock_style != MusicTime) {
568 TempoMap& map (_session.tempo_map());
569 framepos_t pos = map.frame_time (_bbt_time);
570 set_position_internal (pos, false);
572 /* do this even if the position is the same. this helps out
573 a GUI that has moved its representation already.
575 send_change (Properties::position);
579 Region::set_position (framepos_t pos)
585 set_position_internal (pos, true);
587 /* do this even if the position is the same. this helps out
588 a GUI that has moved its representation already.
590 send_change (Properties::position);
595 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
597 /* We emit a change of Properties::position even if the position hasn't changed
598 (see Region::set_position), so we must always set this up so that
599 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
601 _last_position = _position;
603 if (_position != pos) {
606 /* check that the new _position wouldn't make the current
607 length impossible - if so, change the length.
609 XXX is this the right thing to do?
612 if (max_framepos - _length < _position) {
613 _last_length = _length;
614 _length = max_framepos - _position;
617 if (allow_bbt_recompute) {
618 recompute_position_from_lock_style ();
621 //invalidate_transients ();
626 Region::recompute_position_from_lock_style ()
628 if (_position_lock_style == MusicTime) {
629 _session.bbt_time (_position, _bbt_time);
634 Region::nudge_position (frameoffset_t n)
636 if (locked() || video_locked()) {
644 framepos_t new_position = _position;
647 if (_position > max_framepos - n) {
648 new_position = max_framepos;
653 if (_position < -n) {
660 set_position_internal (new_position, true);
662 send_change (Properties::position);
666 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
668 _ancestral_length = l;
669 _ancestral_start = s;
675 Region::set_start (framepos_t pos)
677 if (locked() || position_locked() || video_locked()) {
680 /* This just sets the start, nothing else. It effectively shifts
681 the contents of the Region within the overall extent of the Source,
682 without changing the Region's position or length
687 if (!verify_start (pos)) {
691 set_start_internal (pos);
694 invalidate_transients ();
696 send_change (Properties::start);
701 Region::move_start (frameoffset_t distance)
703 if (locked() || position_locked() || video_locked()) {
707 framepos_t new_start;
711 if (_start > max_framepos - distance) {
712 new_start = max_framepos; // makes no sense
714 new_start = _start + distance;
717 if (!verify_start (new_start)) {
721 } else if (distance < 0) {
723 if (_start < -distance) {
726 new_start = _start + distance;
733 if (new_start == _start) {
737 set_start_internal (new_start);
742 send_change (Properties::start);
746 Region::trim_front (framepos_t new_position)
748 modify_front (new_position, false);
752 Region::cut_front (framepos_t new_position)
754 modify_front (new_position, true);
758 Region::cut_end (framepos_t new_endpoint)
760 modify_end (new_endpoint, true);
764 Region::modify_front (framepos_t new_position, bool reset_fade)
770 framepos_t end = last_frame();
771 framepos_t source_zero;
773 if (_position > _start) {
774 source_zero = _position - _start;
776 source_zero = 0; // its actually negative, but this will work for us
779 if (new_position < end) { /* can't trim it zero or negative length */
781 framecnt_t newlen = 0;
782 framepos_t delta = 0;
784 if (!can_trim_start_before_source_start ()) {
785 /* can't trim it back past where source position zero is located */
786 new_position = max (new_position, source_zero);
789 if (new_position > _position) {
790 newlen = _length - (new_position - _position);
791 delta = -1 * (new_position - _position);
793 newlen = _length + (_position - new_position);
794 delta = _position - new_position;
797 trim_to_internal (new_position, newlen);
800 _right_of_split = true;
803 if (!property_changes_suspended()) {
804 recompute_at_start ();
807 if (_transients.size() > 0){
808 adjust_transients(delta);
814 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
820 if (new_endpoint > _position) {
821 trim_to_internal (_position, new_endpoint - _position);
823 _left_of_split = true;
825 if (!property_changes_suspended()) {
831 /** @param new_endpoint New region end point, such that, for example,
832 * a region at 0 of length 10 has an endpoint of 9.
836 Region::trim_end (framepos_t new_endpoint)
838 modify_end (new_endpoint, false);
842 Region::trim_to (framepos_t position, framecnt_t length)
848 trim_to_internal (position, length);
850 if (!property_changes_suspended()) {
851 recompute_at_start ();
857 Region::trim_to_internal (framepos_t position, framecnt_t length)
859 framepos_t new_start;
865 frameoffset_t const start_shift = position - _position;
867 if (start_shift > 0) {
869 if (_start > max_framepos - start_shift) {
870 new_start = max_framepos;
872 new_start = _start + start_shift;
875 } else if (start_shift < 0) {
877 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
880 new_start = _start + start_shift;
887 if (!verify_start_and_length (new_start, length)) {
891 PropertyChange what_changed;
893 if (_start != new_start) {
894 set_start_internal (new_start);
895 what_changed.add (Properties::start);
898 /* Set position before length, otherwise for MIDI regions this bad thing happens:
899 * 1. we call set_length_internal; length in beats is computed using the region's current
900 * (soon-to-be old) position
901 * 2. we call set_position_internal; position is set and length in frames re-computed using
902 * length in beats from (1) but at the new position, which is wrong if the region
903 * straddles a tempo/meter change.
906 if (_position != position) {
907 if (!property_changes_suspended()) {
908 _last_position = _position;
910 set_position_internal (position, true);
911 what_changed.add (Properties::position);
914 if (_length != length) {
915 if (!property_changes_suspended()) {
916 _last_length = _length;
918 set_length_internal (length);
919 what_changed.add (Properties::length);
924 PropertyChange start_and_length;
926 start_and_length.add (Properties::start);
927 start_and_length.add (Properties::length);
929 if (what_changed.contains (start_and_length)) {
933 if (!what_changed.empty()) {
934 send_change (what_changed);
939 Region::set_hidden (bool yn)
941 if (hidden() != yn) {
943 send_change (Properties::hidden);
948 Region::set_whole_file (bool yn)
951 /* no change signal */
955 Region::set_automatic (bool yn)
958 /* no change signal */
962 Region::set_muted (bool yn)
966 send_change (Properties::muted);
971 Region::set_opaque (bool yn)
973 if (opaque() != yn) {
975 send_change (Properties::opaque);
980 Region::set_locked (bool yn)
982 if (locked() != yn) {
984 send_change (Properties::locked);
989 Region::set_video_locked (bool yn)
991 if (video_locked() != yn) {
993 send_change (Properties::video_locked);
998 Region::set_position_locked (bool yn)
1000 if (position_locked() != yn) {
1001 _position_locked = yn;
1002 send_change (Properties::locked);
1006 /** Set the region's sync point.
1007 * @param absolute_pos Session time.
1010 Region::set_sync_position (framepos_t absolute_pos)
1012 /* position within our file */
1013 framepos_t const file_pos = _start + (absolute_pos - _position);
1015 if (file_pos != _sync_position) {
1016 _sync_marked = true;
1017 _sync_position = file_pos;
1018 if (!property_changes_suspended()) {
1022 send_change (Properties::sync_position);
1027 Region::clear_sync_position ()
1029 if (sync_marked()) {
1030 _sync_marked = false;
1031 if (!property_changes_suspended()) {
1035 send_change (Properties::sync_position);
1039 /* @return the sync point relative the first frame of the region */
1041 Region::sync_offset (int& dir) const
1043 if (sync_marked()) {
1044 if (_sync_position > _start) {
1046 return _sync_position - _start;
1049 return _start - _sync_position;
1058 Region::adjust_to_sync (framepos_t pos) const
1061 frameoffset_t offset = sync_offset (sync_dir);
1063 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1072 if (max_framepos - pos > offset) {
1080 /** @return Sync position in session time */
1082 Region::sync_position() const
1084 if (sync_marked()) {
1085 return _position - _start + _sync_position;
1087 /* if sync has not been marked, use the start of the region */
1095 boost::shared_ptr<Playlist> pl (playlist());
1097 pl->raise_region (shared_from_this ());
1104 boost::shared_ptr<Playlist> pl (playlist());
1106 pl->lower_region (shared_from_this ());
1112 Region::raise_to_top ()
1114 boost::shared_ptr<Playlist> pl (playlist());
1116 pl->raise_region_to_top (shared_from_this());
1121 Region::lower_to_bottom ()
1123 boost::shared_ptr<Playlist> pl (playlist());
1125 pl->lower_region_to_bottom (shared_from_this());
1130 Region::set_layer (layer_t l)
1138 XMLNode *node = new XMLNode ("Region");
1141 LocaleGuard lg (X_("C"));
1142 const char* fe = NULL;
1144 /* custom version of 'add_properties (*node);'
1145 * skip values that have have dedicated save functions
1146 * in AudioRegion::state()
1148 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1149 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1150 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1151 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1152 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1153 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1154 i->second->get_value (*node);
1157 id().print (buf, sizeof (buf));
1158 node->add_property ("id", buf);
1159 node->add_property ("type", _type.to_string());
1161 switch (_first_edit) {
1162 case EditChangesNothing:
1165 case EditChangesName:
1171 default: /* should be unreachable but makes g++ happy */
1176 node->add_property ("first-edit", fe);
1178 /* note: flags are stored by derived classes */
1180 if (_position_lock_style != AudioTime) {
1183 node->add_property ("bbt-position", str.str());
1186 for (uint32_t n=0; n < _sources.size(); ++n) {
1187 snprintf (buf2, sizeof(buf2), "source-%d", n);
1188 _sources[n]->id().print (buf, sizeof(buf));
1189 node->add_property (buf2, buf);
1192 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1193 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1194 _master_sources[n]->id().print (buf, sizeof (buf));
1195 node->add_property (buf2, buf);
1198 /* Only store nested sources for the whole-file region that acts
1199 as the parent/root of all regions using it.
1202 if (_whole_file && max_source_level() > 0) {
1204 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1206 /* region is compound - get its playlist and
1207 store that before we list the region that
1211 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1212 nested_node->add_child_nocopy ((*s)->get_state ());
1216 node->add_child_nocopy (*nested_node);
1221 node->add_child_copy (*_extra_xml);
1228 Region::get_state ()
1234 Region::set_state (const XMLNode& node, int version)
1236 PropertyChange what_changed;
1237 return _set_state (node, version, what_changed, true);
1241 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1243 const XMLProperty* prop;
1245 Stateful::save_extra_xml (node);
1247 what_changed = set_values (node);
1251 if (_position_lock_style == MusicTime) {
1252 if ((prop = node.property ("bbt-position")) == 0) {
1253 /* missing BBT info, revert to audio time locking */
1254 _position_lock_style = AudioTime;
1256 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1259 &_bbt_time.ticks) != 3) {
1260 _position_lock_style = AudioTime;
1265 /* fix problems with old sessions corrupted by impossible
1266 values for _stretch or _shift
1268 if (_stretch == 0.0f) {
1272 if (_shift == 0.0f) {
1277 send_change (what_changed);
1280 /* Quick fix for 2.x sessions when region is muted */
1281 if ((prop = node.property (X_("flags")))) {
1282 if (string::npos != prop->value().find("Muted")){
1287 // saved property is invalid, region-transients are not saved
1288 if (_transients.size() == 0){
1289 _valid_transients = false;
1296 Region::suspend_property_changes ()
1298 Stateful::suspend_property_changes ();
1299 _last_length = _length;
1300 _last_position = _position;
1304 Region::mid_thaw (const PropertyChange& what_changed)
1306 if (what_changed.contains (Properties::length)) {
1307 if (what_changed.contains (Properties::position)) {
1308 recompute_at_start ();
1310 recompute_at_end ();
1315 Region::send_change (const PropertyChange& what_changed)
1317 if (what_changed.empty()) {
1321 Stateful::send_change (what_changed);
1323 if (!Stateful::property_changes_suspended()) {
1325 /* Try and send a shared_pointer unless this is part of the constructor.
1330 boost::shared_ptr<Region> rptr = shared_from_this();
1331 RegionPropertyChanged (rptr, what_changed);
1333 /* no shared_ptr available, relax; */
1339 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1341 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1345 Region::equivalent (boost::shared_ptr<const Region> other) const
1347 return _start == other->_start &&
1348 _position == other->_position &&
1349 _length == other->_length;
1353 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1355 return _start == other->_start &&
1356 _length == other->_length;
1360 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1362 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1366 Region::source_deleted (boost::weak_ptr<Source>)
1370 if (!_session.deletion_in_progress()) {
1371 /* this is a very special case: at least one of the region's
1372 sources has bee deleted, so invalidate all references to
1373 ourselves. Do NOT do this during session deletion, because
1374 then we run the risk that this will actually result
1375 in this object being deleted (as refcnt goes to zero)
1376 while emitting DropReferences.
1384 Region::master_source_names ()
1386 SourceList::iterator i;
1388 vector<string> names;
1389 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1390 names.push_back((*i)->name());
1397 Region::set_master_sources (const SourceList& srcs)
1399 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1400 (*i)->dec_use_count ();
1403 _master_sources = srcs;
1404 assert (_sources.size() == _master_sources.size());
1406 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1407 (*i)->inc_use_count ();
1412 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1417 if ((_sources.size() != other->_sources.size()) ||
1418 (_master_sources.size() != other->_master_sources.size())) {
1422 SourceList::const_iterator i;
1423 SourceList::const_iterator io;
1425 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1426 if ((*i)->id() != (*io)->id()) {
1431 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1432 if ((*i)->id() != (*io)->id()) {
1441 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1447 SourceList::const_iterator i;
1448 SourceList::const_iterator io;
1450 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1451 if ((*i)->id() == (*io)->id()) {
1460 Region::source_string () const
1462 //string res = itos(_sources.size());
1465 res << _sources.size() << ":";
1467 SourceList::const_iterator i;
1469 for (i = _sources.begin(); i != _sources.end(); ++i) {
1470 res << (*i)->id() << ":";
1473 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1474 res << (*i)->id() << ":";
1481 Region::uses_source (boost::shared_ptr<const Source> source) const
1483 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1488 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1491 if (ps->playlist()->uses_source (source)) {
1497 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1502 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1505 if (ps->playlist()->uses_source (source)) {
1515 Region::source_length(uint32_t n) const
1517 assert (n < _sources.size());
1518 return _sources[n]->length (_position - _start);
1522 Region::verify_length (framecnt_t& len)
1524 if (source() && (source()->destructive() || source()->length_mutable())) {
1528 framecnt_t maxlen = 0;
1530 for (uint32_t n = 0; n < _sources.size(); ++n) {
1531 maxlen = max (maxlen, source_length(n) - _start);
1534 len = min (len, maxlen);
1540 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1542 if (source() && (source()->destructive() || source()->length_mutable())) {
1546 framecnt_t maxlen = 0;
1548 for (uint32_t n = 0; n < _sources.size(); ++n) {
1549 maxlen = max (maxlen, source_length(n) - new_start);
1552 new_length = min (new_length, maxlen);
1558 Region::verify_start (framepos_t pos)
1560 if (source() && (source()->destructive() || source()->length_mutable())) {
1564 for (uint32_t n = 0; n < _sources.size(); ++n) {
1565 if (pos > source_length(n) - _length) {
1573 Region::verify_start_mutable (framepos_t& new_start)
1575 if (source() && (source()->destructive() || source()->length_mutable())) {
1579 for (uint32_t n = 0; n < _sources.size(); ++n) {
1580 if (new_start > source_length(n) - _length) {
1581 new_start = source_length(n) - _length;
1587 boost::shared_ptr<Region>
1588 Region::get_parent() const
1590 boost::shared_ptr<Playlist> pl (playlist());
1593 boost::shared_ptr<Region> r;
1594 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1596 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1597 return boost::static_pointer_cast<Region> (r);
1601 return boost::shared_ptr<Region>();
1605 Region::apply (Filter& filter, Progress* progress)
1607 return filter.run (shared_from_this(), progress);
1612 Region::invalidate_transients ()
1614 _valid_transients = false;
1615 _transients.clear ();
1617 send_change (PropertyChange (Properties::valid_transients));
1621 Region::drop_sources ()
1623 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1624 (*i)->dec_use_count ();
1629 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1630 (*i)->dec_use_count ();
1633 _master_sources.clear ();
1637 Region::use_sources (SourceList const & s)
1639 set<boost::shared_ptr<Source> > unique_srcs;
1641 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1643 _sources.push_back (*i);
1644 (*i)->inc_use_count ();
1645 _master_sources.push_back (*i);
1646 (*i)->inc_use_count ();
1648 /* connect only once to DropReferences, even if sources are replicated
1651 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1652 unique_srcs.insert (*i);
1653 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1659 Region::can_trim () const
1661 CanTrim ct = CanTrim (0);
1667 /* if not locked, we can always move the front later, and the end earlier
1670 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1672 if (start() != 0 || can_trim_start_before_source_start ()) {
1673 ct = CanTrim (ct | FrontTrimEarlier);
1676 if (!_sources.empty()) {
1677 if ((start() + length()) < _sources.front()->length (0)) {
1678 ct = CanTrim (ct | EndTrimLater);
1686 Region::max_source_level () const
1690 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1691 lvl = max (lvl, (*i)->level());
1698 Region::is_compound () const
1700 return max_source_level() > 0;
1704 Region::post_set (const PropertyChange& pc)
1706 if (pc.contains (Properties::position)) {
1707 recompute_position_from_lock_style ();
1712 Region::set_start_internal (framecnt_t s)
1718 Region::earliest_possible_position () const
1720 if (_start > _position) {
1723 return _position - _start;
1728 Region::latest_possible_frame () const
1730 framecnt_t minlen = max_framecnt;
1732 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1733 /* non-audio regions have a length that may vary based on their
1734 * position, so we have to pass it in the call.
1736 minlen = min (minlen, (*i)->length (_position));
1739 /* the latest possible last frame is determined by the current
1740 * position, plus the shortest source extent past _start.
1743 return _position + (minlen - _start) - 1;