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::trim_start (framepos_t new_position)
703 if (locked() || position_locked() || video_locked()) {
707 framepos_t new_start;
708 frameoffset_t const start_shift = new_position - _position;
710 if (start_shift > 0) {
712 if (_start > max_framepos - start_shift) {
713 new_start = max_framepos;
715 new_start = _start + start_shift;
718 if (!verify_start (new_start)) {
722 } else if (start_shift < 0) {
724 if (_start < -start_shift) {
727 new_start = _start + start_shift;
734 if (new_start == _start) {
738 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_("POSIX"));
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")){
1292 Region::suspend_property_changes ()
1294 Stateful::suspend_property_changes ();
1295 _last_length = _length;
1296 _last_position = _position;
1300 Region::mid_thaw (const PropertyChange& what_changed)
1302 if (what_changed.contains (Properties::length)) {
1303 if (what_changed.contains (Properties::position)) {
1304 recompute_at_start ();
1306 recompute_at_end ();
1311 Region::send_change (const PropertyChange& what_changed)
1313 if (what_changed.empty()) {
1317 Stateful::send_change (what_changed);
1319 if (!Stateful::property_changes_suspended()) {
1321 /* Try and send a shared_pointer unless this is part of the constructor.
1326 boost::shared_ptr<Region> rptr = shared_from_this();
1327 RegionPropertyChanged (rptr, what_changed);
1329 /* no shared_ptr available, relax; */
1335 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1337 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1341 Region::equivalent (boost::shared_ptr<const Region> other) const
1343 return _start == other->_start &&
1344 _position == other->_position &&
1345 _length == other->_length;
1349 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1351 return _start == other->_start &&
1352 _length == other->_length;
1356 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1358 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1362 Region::source_deleted (boost::weak_ptr<Source>)
1366 if (!_session.deletion_in_progress()) {
1367 /* this is a very special case: at least one of the region's
1368 sources has bee deleted, so invalidate all references to
1369 ourselves. Do NOT do this during session deletion, because
1370 then we run the risk that this will actually result
1371 in this object being deleted (as refcnt goes to zero)
1372 while emitting DropReferences.
1380 Region::master_source_names ()
1382 SourceList::iterator i;
1384 vector<string> names;
1385 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1386 names.push_back((*i)->name());
1393 Region::set_master_sources (const SourceList& srcs)
1395 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1396 (*i)->dec_use_count ();
1399 _master_sources = srcs;
1400 assert (_sources.size() == _master_sources.size());
1402 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1403 (*i)->inc_use_count ();
1408 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1413 if ((_sources.size() != other->_sources.size()) ||
1414 (_master_sources.size() != other->_master_sources.size())) {
1418 SourceList::const_iterator i;
1419 SourceList::const_iterator io;
1421 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1422 if ((*i)->id() != (*io)->id()) {
1427 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1428 if ((*i)->id() != (*io)->id()) {
1437 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1443 SourceList::const_iterator i;
1444 SourceList::const_iterator io;
1446 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1447 if ((*i)->id() == (*io)->id()) {
1456 Region::source_string () const
1458 //string res = itos(_sources.size());
1461 res << _sources.size() << ":";
1463 SourceList::const_iterator i;
1465 for (i = _sources.begin(); i != _sources.end(); ++i) {
1466 res << (*i)->id() << ":";
1469 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1470 res << (*i)->id() << ":";
1477 Region::uses_source (boost::shared_ptr<const Source> source) const
1479 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1484 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1487 if (ps->playlist()->uses_source (source)) {
1497 Region::source_length(uint32_t n) const
1499 assert (n < _sources.size());
1500 return _sources[n]->length (_position - _start);
1504 Region::verify_length (framecnt_t len)
1506 if (source() && (source()->destructive() || source()->length_mutable())) {
1510 framecnt_t maxlen = 0;
1512 for (uint32_t n = 0; n < _sources.size(); ++n) {
1513 maxlen = max (maxlen, source_length(n) - _start);
1516 len = min (len, maxlen);
1522 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
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) - new_start);
1534 new_length = min (new_length, maxlen);
1540 Region::verify_start (framepos_t pos)
1542 if (source() && (source()->destructive() || source()->length_mutable())) {
1546 for (uint32_t n = 0; n < _sources.size(); ++n) {
1547 if (pos > source_length(n) - _length) {
1555 Region::verify_start_mutable (framepos_t& new_start)
1557 if (source() && (source()->destructive() || source()->length_mutable())) {
1561 for (uint32_t n = 0; n < _sources.size(); ++n) {
1562 if (new_start > source_length(n) - _length) {
1563 new_start = source_length(n) - _length;
1569 boost::shared_ptr<Region>
1570 Region::get_parent() const
1572 boost::shared_ptr<Playlist> pl (playlist());
1575 boost::shared_ptr<Region> r;
1576 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1578 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1579 return boost::static_pointer_cast<Region> (r);
1583 return boost::shared_ptr<Region>();
1587 Region::apply (Filter& filter, Progress* progress)
1589 return filter.run (shared_from_this(), progress);
1594 Region::invalidate_transients ()
1596 _valid_transients = false;
1597 _transients.clear ();
1599 send_change (PropertyChange (Properties::valid_transients));
1603 Region::drop_sources ()
1605 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1606 (*i)->dec_use_count ();
1611 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1612 (*i)->dec_use_count ();
1615 _master_sources.clear ();
1619 Region::use_sources (SourceList const & s)
1621 set<boost::shared_ptr<Source> > unique_srcs;
1623 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1625 _sources.push_back (*i);
1626 (*i)->inc_use_count ();
1627 _master_sources.push_back (*i);
1628 (*i)->inc_use_count ();
1630 /* connect only once to DropReferences, even if sources are replicated
1633 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1634 unique_srcs.insert (*i);
1635 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1641 Region::can_trim () const
1643 CanTrim ct = CanTrim (0);
1649 /* if not locked, we can always move the front later, and the end earlier
1652 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1654 if (start() != 0 || can_trim_start_before_source_start ()) {
1655 ct = CanTrim (ct | FrontTrimEarlier);
1658 if (!_sources.empty()) {
1659 if ((start() + length()) < _sources.front()->length (0)) {
1660 ct = CanTrim (ct | EndTrimLater);
1668 Region::max_source_level () const
1672 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1673 lvl = max (lvl, (*i)->level());
1680 Region::is_compound () const
1682 return max_source_level() > 0;
1686 Region::post_set (const PropertyChange& pc)
1688 if (pc.contains (Properties::position)) {
1689 recompute_position_from_lock_style ();
1694 Region::set_start_internal (framecnt_t s)
1700 Region::earliest_possible_position () const
1702 if (_start > _position) {
1705 return _position - _start;
1710 Region::latest_possible_frame () const
1712 framecnt_t minlen = max_framecnt;
1714 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1715 /* non-audio regions have a length that may vary based on their
1716 * position, so we have to pass it in the call.
1718 minlen = min (minlen, (*i)->length (_position));
1721 /* the latest possible last frame is determined by the current
1722 * position, plus the shortest source extent past _start.
1725 return _position + (minlen - _start) - 1;