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> automatic;
53 PBD::PropertyDescriptor<bool> whole_file;
54 PBD::PropertyDescriptor<bool> import;
55 PBD::PropertyDescriptor<bool> external;
56 PBD::PropertyDescriptor<bool> sync_marked;
57 PBD::PropertyDescriptor<bool> left_of_split;
58 PBD::PropertyDescriptor<bool> right_of_split;
59 PBD::PropertyDescriptor<bool> hidden;
60 PBD::PropertyDescriptor<bool> position_locked;
61 PBD::PropertyDescriptor<bool> valid_transients;
62 PBD::PropertyDescriptor<framepos_t> start;
63 PBD::PropertyDescriptor<framecnt_t> length;
64 PBD::PropertyDescriptor<framepos_t> position;
65 PBD::PropertyDescriptor<framecnt_t> sync_position;
66 PBD::PropertyDescriptor<layer_t> layer;
67 PBD::PropertyDescriptor<framepos_t> ancestral_start;
68 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
69 PBD::PropertyDescriptor<float> stretch;
70 PBD::PropertyDescriptor<float> shift;
71 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
72 PBD::PropertyDescriptor<uint64_t> layering_index;
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
79 Region::make_property_quarks ()
81 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
83 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
85 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
87 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
89 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
91 Properties::import.property_id = g_quark_from_static_string (X_("import"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
93 Properties::external.property_id = g_quark_from_static_string (X_("external"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
95 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
97 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
99 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
101 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
103 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
105 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
107 Properties::start.property_id = g_quark_from_static_string (X_("start"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
109 Properties::length.property_id = g_quark_from_static_string (X_("length"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
111 Properties::position.property_id = g_quark_from_static_string (X_("position"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
113 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
115 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
117 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
119 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
121 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
123 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
125 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
127 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
132 Region::register_properties ()
134 _xml_node_name = X_("Region");
136 add_property (_muted);
137 add_property (_opaque);
138 add_property (_locked);
139 add_property (_automatic);
140 add_property (_whole_file);
141 add_property (_import);
142 add_property (_external);
143 add_property (_sync_marked);
144 add_property (_left_of_split);
145 add_property (_right_of_split);
146 add_property (_hidden);
147 add_property (_position_locked);
148 add_property (_valid_transients);
149 add_property (_start);
150 add_property (_length);
151 add_property (_position);
152 add_property (_sync_position);
153 add_property (_ancestral_start);
154 add_property (_ancestral_length);
155 add_property (_stretch);
156 add_property (_shift);
157 add_property (_position_lock_style);
158 add_property (_layering_index);
161 #define REGION_DEFAULT_STATE(s,l) \
162 _sync_marked (Properties::sync_marked, false) \
163 , _left_of_split (Properties::left_of_split, false) \
164 , _right_of_split (Properties::right_of_split, false) \
165 , _valid_transients (Properties::valid_transients, false) \
166 , _start (Properties::start, (s)) \
167 , _length (Properties::length, (l)) \
168 , _position (Properties::position, 0) \
169 , _sync_position (Properties::sync_position, (s)) \
170 , _muted (Properties::muted, false) \
171 , _opaque (Properties::opaque, true) \
172 , _locked (Properties::locked, false) \
173 , _automatic (Properties::automatic, false) \
174 , _whole_file (Properties::whole_file, false) \
175 , _import (Properties::import, false) \
176 , _external (Properties::external, false) \
177 , _hidden (Properties::hidden, false) \
178 , _position_locked (Properties::position_locked, false) \
179 , _ancestral_start (Properties::ancestral_start, (s)) \
180 , _ancestral_length (Properties::ancestral_length, (l)) \
181 , _stretch (Properties::stretch, 1.0) \
182 , _shift (Properties::shift, 1.0) \
183 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
184 , _layering_index (Properties::layering_index, 0)
186 #define REGION_COPY_STATE(other) \
187 _sync_marked (Properties::sync_marked, other->_sync_marked) \
188 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
189 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
190 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
191 , _start(Properties::start, other->_start) \
192 , _length(Properties::length, other->_length) \
193 , _position(Properties::position, other->_position) \
194 , _sync_position(Properties::sync_position, other->_sync_position) \
195 , _muted (Properties::muted, other->_muted) \
196 , _opaque (Properties::opaque, other->_opaque) \
197 , _locked (Properties::locked, other->_locked) \
198 , _automatic (Properties::automatic, other->_automatic) \
199 , _whole_file (Properties::whole_file, other->_whole_file) \
200 , _import (Properties::import, other->_import) \
201 , _external (Properties::external, other->_external) \
202 , _hidden (Properties::hidden, other->_hidden) \
203 , _position_locked (Properties::position_locked, other->_position_locked) \
204 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
205 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
206 , _stretch (Properties::stretch, other->_stretch) \
207 , _shift (Properties::shift, other->_shift) \
208 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
209 , _layering_index (Properties::layering_index, other->_layering_index)
211 /* derived-from-derived constructor (no sources in constructor) */
212 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
213 : SessionObject(s, name)
215 , REGION_DEFAULT_STATE(start,length)
216 , _last_length (length)
218 , _first_edit (EditChangesNothing)
221 register_properties ();
223 /* no sources at this point */
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228 : SessionObject(srcs.front()->session(), "toBeRenamed")
229 , _type (srcs.front()->type())
230 , REGION_DEFAULT_STATE(0,0)
233 , _first_edit (EditChangesNothing)
236 register_properties ();
238 _type = srcs.front()->type();
242 assert(_sources.size() > 0);
243 assert (_type == srcs.front()->type());
246 /** Create a new Region from an existing one */
247 Region::Region (boost::shared_ptr<const Region> other)
248 : SessionObject(other->session(), other->name())
249 , _type (other->data_type())
250 , REGION_COPY_STATE (other)
251 , _last_length (other->_last_length)
252 , _last_position(other->_last_position) \
253 , _first_edit (EditChangesNothing)
254 , _layer (other->_layer)
256 register_properties ();
258 /* override state that may have been incorrectly inherited from the other region
266 use_sources (other->_sources);
268 _position_lock_style = other->_position_lock_style;
269 _first_edit = other->_first_edit;
271 _start = 0; // It seems strange _start is not inherited here?
273 /* sync pos is relative to start of file. our start-in-file is now zero,
274 so set our sync position to whatever the the difference between
275 _start and _sync_pos was in the other region.
277 result is that our new sync pos points to the same point in our source(s)
278 as the sync in the other region did in its source(s).
280 since we start at zero in our source(s), it is not possible to use a sync point that
281 is before the start. reset it to _start if that was true in the other region.
284 if (other->sync_marked()) {
285 if (other->_start < other->_sync_position) {
286 /* sync pos was after the start point of the other region */
287 _sync_position = other->_sync_position - other->_start;
289 /* sync pos was before the start point of the other region. not possible here. */
290 _sync_marked = false;
291 _sync_position = _start;
294 _sync_marked = false;
295 _sync_position = _start;
298 if (Profile->get_sae()) {
299 /* reset sync point to start if its ended up
300 outside region bounds.
303 if (_sync_position < _start || _sync_position >= _start + _length) {
304 _sync_marked = false;
305 _sync_position = _start;
309 assert (_type == other->data_type());
312 /** Create a new Region from part of an existing one.
314 the start within \a other is given by \a offset
315 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
317 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
318 : SessionObject(other->session(), other->name())
319 , _type (other->data_type())
320 , REGION_COPY_STATE (other)
321 , _last_length (other->_last_length)
322 , _last_position(other->_last_position) \
323 , _first_edit (EditChangesNothing)
324 , _layer (other->_layer)
326 register_properties ();
328 /* override state that may have been incorrectly inherited from the other region
336 use_sources (other->_sources);
338 _start = other->_start + offset;
340 /* if the other region had a distinct sync point
341 set, then continue to use it as best we can.
342 otherwise, reset sync point back to start.
345 if (other->sync_marked()) {
346 if (other->_sync_position < _start) {
347 _sync_marked = false;
348 _sync_position = _start;
350 _sync_position = other->_sync_position;
353 _sync_marked = false;
354 _sync_position = _start;
357 if (Profile->get_sae()) {
358 /* reset sync point to start if its ended up
359 outside region bounds.
362 if (_sync_position < _start || _sync_position >= _start + _length) {
363 _sync_marked = false;
364 _sync_position = _start;
368 assert (_type == other->data_type());
371 /** Create a copy of @param other but with different sources. Used by filters */
372 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
373 : SessionObject (other->session(), other->name())
374 , _type (srcs.front()->type())
375 , REGION_COPY_STATE (other)
376 , _last_length (other->_last_length)
377 , _last_position (other->_last_position)
378 , _first_edit (EditChangesID)
379 , _layer (other->_layer)
381 register_properties ();
384 _position_locked = false;
386 other->_first_edit = EditChangesName;
388 if (other->_extra_xml) {
389 _extra_xml = new XMLNode (*other->_extra_xml);
395 assert(_sources.size() > 0);
400 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
405 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
407 _playlist = wpl.lock();
411 Region::set_name (const std::string& str)
414 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
415 assert(_name == str);
417 send_change (Properties::name);
424 Region::set_length (framecnt_t len)
426 //cerr << "Region::set_length() len = " << len << endl;
431 if (_length != len && len != 0) {
433 /* check that the current _position wouldn't make the new
437 if (max_framepos - len < _position) {
441 if (!verify_length (len)) {
446 _last_length = _length;
447 set_length_internal (len);
451 invalidate_transients ();
453 if (!property_changes_suspended()) {
457 send_change (Properties::length);
462 Region::set_length_internal (framecnt_t len)
468 Region::maybe_uncopy ()
470 /* this does nothing but marked a semantic moment once upon a time */
474 Region::first_edit ()
476 boost::shared_ptr<Playlist> pl (playlist());
478 if (_first_edit != EditChangesNothing && pl) {
480 _name = RegionFactory::new_region_name (_name);
481 _first_edit = EditChangesNothing;
483 send_change (Properties::name);
485 RegionFactory::CheckNewRegion (shared_from_this());
490 Region::at_natural_position () const
492 boost::shared_ptr<Playlist> pl (playlist());
498 boost::shared_ptr<Region> whole_file_region = get_parent();
500 if (whole_file_region) {
501 if (_position == whole_file_region->position() + _start) {
510 Region::move_to_natural_position ()
512 boost::shared_ptr<Playlist> pl (playlist());
518 boost::shared_ptr<Region> whole_file_region = get_parent();
520 if (whole_file_region) {
521 set_position (whole_file_region->position() + _start);
526 Region::special_set_position (framepos_t pos)
528 /* this is used when creating a whole file region as
529 a way to store its "natural" or "captured" position.
532 _position = _position;
537 Region::set_position_lock_style (PositionLockStyle ps)
539 if (_position_lock_style != ps) {
541 boost::shared_ptr<Playlist> pl (playlist());
543 _position_lock_style = ps;
545 if (_position_lock_style == MusicTime) {
546 _session.bbt_time (_position, _bbt_time);
549 send_change (Properties::position_lock_style);
554 Region::update_after_tempo_map_change ()
556 boost::shared_ptr<Playlist> pl (playlist());
558 if (!pl || _position_lock_style != MusicTime) {
562 TempoMap& map (_session.tempo_map());
563 framepos_t pos = map.frame_time (_bbt_time);
564 set_position_internal (pos, false);
566 /* do this even if the position is the same. this helps out
567 a GUI that has moved its representation already.
569 send_change (Properties::position);
573 Region::set_position (framepos_t pos)
579 set_position_internal (pos, true);
581 /* do this even if the position is the same. this helps out
582 a GUI that has moved its representation already.
584 send_change (Properties::position);
589 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
591 /* We emit a change of Properties::position even if the position hasn't changed
592 (see Region::set_position), so we must always set this up so that
593 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
595 _last_position = _position;
597 if (_position != pos) {
600 /* check that the new _position wouldn't make the current
601 length impossible - if so, change the length.
603 XXX is this the right thing to do?
606 if (max_framepos - _length < _position) {
607 _last_length = _length;
608 _length = max_framepos - _position;
611 if (allow_bbt_recompute) {
612 recompute_position_from_lock_style ();
615 //invalidate_transients ();
620 Region::recompute_position_from_lock_style ()
622 if (_position_lock_style == MusicTime) {
623 _session.bbt_time (_position, _bbt_time);
628 Region::nudge_position (frameoffset_t n)
638 framepos_t new_position = _position;
641 if (_position > max_framepos - n) {
642 new_position = max_framepos;
647 if (_position < -n) {
654 set_position_internal (new_position, true);
656 send_change (Properties::position);
660 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
662 _ancestral_length = l;
663 _ancestral_start = s;
669 Region::set_start (framepos_t pos)
671 if (locked() || position_locked()) {
674 /* This just sets the start, nothing else. It effectively shifts
675 the contents of the Region within the overall extent of the Source,
676 without changing the Region's position or length
681 if (!verify_start (pos)) {
685 set_start_internal (pos);
688 invalidate_transients ();
690 send_change (Properties::start);
695 Region::trim_start (framepos_t new_position)
697 if (locked() || position_locked()) {
701 framepos_t new_start;
702 frameoffset_t const start_shift = new_position - _position;
704 if (start_shift > 0) {
706 if (_start > max_framepos - start_shift) {
707 new_start = max_framepos;
709 new_start = _start + start_shift;
712 if (!verify_start (new_start)) {
716 } else if (start_shift < 0) {
718 if (_start < -start_shift) {
721 new_start = _start + start_shift;
728 if (new_start == _start) {
732 set_start_internal (new_start);
736 send_change (Properties::start);
740 Region::trim_front (framepos_t new_position)
742 modify_front (new_position, false);
746 Region::cut_front (framepos_t new_position)
748 modify_front (new_position, true);
752 Region::cut_end (framepos_t new_endpoint)
754 modify_end (new_endpoint, true);
758 Region::modify_front (framepos_t new_position, bool reset_fade)
764 framepos_t end = last_frame();
765 framepos_t source_zero;
767 if (_position > _start) {
768 source_zero = _position - _start;
770 source_zero = 0; // its actually negative, but this will work for us
773 if (new_position < end) { /* can't trim it zero or negative length */
775 framecnt_t newlen = 0;
776 framepos_t delta = 0;
778 if (!can_trim_start_before_source_start ()) {
779 /* can't trim it back past where source position zero is located */
780 new_position = max (new_position, source_zero);
783 if (new_position > _position) {
784 newlen = _length - (new_position - _position);
785 delta = -1 * (new_position - _position);
787 newlen = _length + (_position - new_position);
788 delta = _position - new_position;
791 trim_to_internal (new_position, newlen);
794 _right_of_split = true;
797 if (!property_changes_suspended()) {
798 recompute_at_start ();
801 if (_transients.size() > 0){
802 adjust_transients(delta);
808 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
814 if (new_endpoint > _position) {
815 trim_to_internal (_position, new_endpoint - _position);
817 _left_of_split = true;
819 if (!property_changes_suspended()) {
825 /** @param new_endpoint New region end point, such that, for example,
826 * a region at 0 of length 10 has an endpoint of 9.
830 Region::trim_end (framepos_t new_endpoint)
832 modify_end (new_endpoint, false);
836 Region::trim_to (framepos_t position, framecnt_t length)
842 trim_to_internal (position, length);
844 if (!property_changes_suspended()) {
845 recompute_at_start ();
851 Region::trim_to_internal (framepos_t position, framecnt_t length)
853 framepos_t new_start;
859 frameoffset_t const start_shift = position - _position;
861 if (start_shift > 0) {
863 if (_start > max_framepos - start_shift) {
864 new_start = max_framepos;
866 new_start = _start + start_shift;
869 } else if (start_shift < 0) {
871 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
874 new_start = _start + start_shift;
881 if (!verify_start_and_length (new_start, length)) {
885 PropertyChange what_changed;
887 if (_start != new_start) {
888 set_start_internal (new_start);
889 what_changed.add (Properties::start);
892 /* Set position before length, otherwise for MIDI regions this bad thing happens:
893 * 1. we call set_length_internal; length in beats is computed using the region's current
894 * (soon-to-be old) position
895 * 2. we call set_position_internal; position is set and length in frames re-computed using
896 * length in beats from (1) but at the new position, which is wrong if the region
897 * straddles a tempo/meter change.
900 if (_position != position) {
901 if (!property_changes_suspended()) {
902 _last_position = _position;
904 set_position_internal (position, true);
905 what_changed.add (Properties::position);
908 if (_length != length) {
909 if (!property_changes_suspended()) {
910 _last_length = _length;
912 set_length_internal (length);
913 what_changed.add (Properties::length);
918 PropertyChange start_and_length;
920 start_and_length.add (Properties::start);
921 start_and_length.add (Properties::length);
923 if (what_changed.contains (start_and_length)) {
927 if (!what_changed.empty()) {
928 send_change (what_changed);
933 Region::set_hidden (bool yn)
935 if (hidden() != yn) {
937 send_change (Properties::hidden);
942 Region::set_whole_file (bool yn)
945 /* no change signal */
949 Region::set_automatic (bool yn)
952 /* no change signal */
956 Region::set_muted (bool yn)
960 send_change (Properties::muted);
965 Region::set_opaque (bool yn)
967 if (opaque() != yn) {
969 send_change (Properties::opaque);
974 Region::set_locked (bool yn)
976 if (locked() != yn) {
978 send_change (Properties::locked);
983 Region::set_position_locked (bool yn)
985 if (position_locked() != yn) {
986 _position_locked = yn;
987 send_change (Properties::locked);
991 /** Set the region's sync point.
992 * @param absolute_pos Session time.
995 Region::set_sync_position (framepos_t absolute_pos)
997 /* position within our file */
998 framepos_t const file_pos = _start + (absolute_pos - _position);
1000 if (file_pos != _sync_position) {
1001 _sync_marked = true;
1002 _sync_position = file_pos;
1003 if (!property_changes_suspended()) {
1007 send_change (Properties::sync_position);
1012 Region::clear_sync_position ()
1014 if (sync_marked()) {
1015 _sync_marked = false;
1016 if (!property_changes_suspended()) {
1020 send_change (Properties::sync_position);
1024 /* @return the sync point relative the first frame of the region */
1026 Region::sync_offset (int& dir) const
1028 if (sync_marked()) {
1029 if (_sync_position > _start) {
1031 return _sync_position - _start;
1034 return _start - _sync_position;
1043 Region::adjust_to_sync (framepos_t pos) const
1046 frameoffset_t offset = sync_offset (sync_dir);
1048 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1057 if (max_framepos - pos > offset) {
1065 /** @return Sync position in session time */
1067 Region::sync_position() const
1069 if (sync_marked()) {
1070 return _position - _start + _sync_position;
1072 /* if sync has not been marked, use the start of the region */
1080 boost::shared_ptr<Playlist> pl (playlist());
1082 pl->raise_region (shared_from_this ());
1089 boost::shared_ptr<Playlist> pl (playlist());
1091 pl->lower_region (shared_from_this ());
1097 Region::raise_to_top ()
1099 boost::shared_ptr<Playlist> pl (playlist());
1101 pl->raise_region_to_top (shared_from_this());
1106 Region::lower_to_bottom ()
1108 boost::shared_ptr<Playlist> pl (playlist());
1110 pl->lower_region_to_bottom (shared_from_this());
1115 Region::set_layer (layer_t l)
1123 XMLNode *node = new XMLNode ("Region");
1126 LocaleGuard lg (X_("POSIX"));
1127 const char* fe = NULL;
1129 add_properties (*node);
1131 id().print (buf, sizeof (buf));
1132 node->add_property ("id", buf);
1133 node->add_property ("type", _type.to_string());
1135 switch (_first_edit) {
1136 case EditChangesNothing:
1139 case EditChangesName:
1145 default: /* should be unreachable but makes g++ happy */
1150 node->add_property ("first-edit", fe);
1152 /* note: flags are stored by derived classes */
1154 if (_position_lock_style != AudioTime) {
1157 node->add_property ("bbt-position", str.str());
1160 for (uint32_t n=0; n < _sources.size(); ++n) {
1161 snprintf (buf2, sizeof(buf2), "source-%d", n);
1162 _sources[n]->id().print (buf, sizeof(buf));
1163 node->add_property (buf2, buf);
1166 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1167 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1168 _master_sources[n]->id().print (buf, sizeof (buf));
1169 node->add_property (buf2, buf);
1172 /* Only store nested sources for the whole-file region that acts
1173 as the parent/root of all regions using it.
1176 if (_whole_file && max_source_level() > 0) {
1178 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1180 /* region is compound - get its playlist and
1181 store that before we list the region that
1185 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1186 nested_node->add_child_nocopy ((*s)->get_state ());
1190 node->add_child_nocopy (*nested_node);
1195 node->add_child_copy (*_extra_xml);
1202 Region::get_state ()
1208 Region::set_state (const XMLNode& node, int version)
1210 PropertyChange what_changed;
1211 return _set_state (node, version, what_changed, true);
1215 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1217 const XMLProperty* prop;
1219 Stateful::save_extra_xml (node);
1221 what_changed = set_values (node);
1225 if (_position_lock_style == MusicTime) {
1226 if ((prop = node.property ("bbt-position")) == 0) {
1227 /* missing BBT info, revert to audio time locking */
1228 _position_lock_style = AudioTime;
1230 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1233 &_bbt_time.ticks) != 3) {
1234 _position_lock_style = AudioTime;
1239 /* fix problems with old sessions corrupted by impossible
1240 values for _stretch or _shift
1242 if (_stretch == 0.0f) {
1246 if (_shift == 0.0f) {
1251 send_change (what_changed);
1254 /* Quick fix for 2.x sessions when region is muted */
1255 if ((prop = node.property (X_("flags")))) {
1256 if (string::npos != prop->value().find("Muted")){
1266 Region::suspend_property_changes ()
1268 Stateful::suspend_property_changes ();
1269 _last_length = _length;
1270 _last_position = _position;
1274 Region::mid_thaw (const PropertyChange& what_changed)
1276 if (what_changed.contains (Properties::length)) {
1277 if (what_changed.contains (Properties::position)) {
1278 recompute_at_start ();
1280 recompute_at_end ();
1285 Region::send_change (const PropertyChange& what_changed)
1287 if (what_changed.empty()) {
1291 Stateful::send_change (what_changed);
1293 if (!Stateful::property_changes_suspended()) {
1295 /* Try and send a shared_pointer unless this is part of the constructor.
1300 boost::shared_ptr<Region> rptr = shared_from_this();
1301 RegionPropertyChanged (rptr, what_changed);
1303 /* no shared_ptr available, relax; */
1309 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1311 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1315 Region::equivalent (boost::shared_ptr<const Region> other) const
1317 return _start == other->_start &&
1318 _position == other->_position &&
1319 _length == other->_length;
1323 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1325 return _start == other->_start &&
1326 _length == other->_length;
1330 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1332 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1336 Region::source_deleted (boost::weak_ptr<Source>)
1340 if (!_session.deletion_in_progress()) {
1341 /* this is a very special case: at least one of the region's
1342 sources has bee deleted, so invalidate all references to
1343 ourselves. Do NOT do this during session deletion, because
1344 then we run the risk that this will actually result
1345 in this object being deleted (as refcnt goes to zero)
1346 while emitting DropReferences.
1354 Region::master_source_names ()
1356 SourceList::iterator i;
1358 vector<string> names;
1359 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1360 names.push_back((*i)->name());
1367 Region::set_master_sources (const SourceList& srcs)
1369 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1370 (*i)->dec_use_count ();
1373 _master_sources = srcs;
1374 assert (_sources.size() == _master_sources.size());
1376 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1377 (*i)->inc_use_count ();
1382 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1387 if ((_sources.size() != other->_sources.size()) ||
1388 (_master_sources.size() != other->_master_sources.size())) {
1392 SourceList::const_iterator i;
1393 SourceList::const_iterator io;
1395 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1396 if ((*i)->id() != (*io)->id()) {
1401 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1402 if ((*i)->id() != (*io)->id()) {
1411 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1417 SourceList::const_iterator i;
1418 SourceList::const_iterator io;
1420 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1421 if ((*i)->id() == (*io)->id()) {
1430 Region::source_string () const
1432 //string res = itos(_sources.size());
1435 res << _sources.size() << ":";
1437 SourceList::const_iterator i;
1439 for (i = _sources.begin(); i != _sources.end(); ++i) {
1440 res << (*i)->id() << ":";
1443 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1444 res << (*i)->id() << ":";
1451 Region::uses_source (boost::shared_ptr<const Source> source) const
1453 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1458 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1461 if (ps->playlist()->uses_source (source)) {
1471 Region::source_length(uint32_t n) const
1473 assert (n < _sources.size());
1474 return _sources[n]->length (_position - _start);
1478 Region::verify_length (framecnt_t len)
1480 if (source() && (source()->destructive() || source()->length_mutable())) {
1484 framecnt_t maxlen = 0;
1486 for (uint32_t n = 0; n < _sources.size(); ++n) {
1487 maxlen = max (maxlen, source_length(n) - _start);
1490 len = min (len, maxlen);
1496 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1498 if (source() && (source()->destructive() || source()->length_mutable())) {
1502 framecnt_t maxlen = 0;
1504 for (uint32_t n = 0; n < _sources.size(); ++n) {
1505 maxlen = max (maxlen, source_length(n) - new_start);
1508 new_length = min (new_length, maxlen);
1514 Region::verify_start (framepos_t pos)
1516 if (source() && (source()->destructive() || source()->length_mutable())) {
1520 for (uint32_t n = 0; n < _sources.size(); ++n) {
1521 if (pos > source_length(n) - _length) {
1529 Region::verify_start_mutable (framepos_t& new_start)
1531 if (source() && (source()->destructive() || source()->length_mutable())) {
1535 for (uint32_t n = 0; n < _sources.size(); ++n) {
1536 if (new_start > source_length(n) - _length) {
1537 new_start = source_length(n) - _length;
1543 boost::shared_ptr<Region>
1544 Region::get_parent() const
1546 boost::shared_ptr<Playlist> pl (playlist());
1549 boost::shared_ptr<Region> r;
1550 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1552 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1553 return boost::static_pointer_cast<Region> (r);
1557 return boost::shared_ptr<Region>();
1561 Region::apply (Filter& filter, Progress* progress)
1563 return filter.run (shared_from_this(), progress);
1568 Region::invalidate_transients ()
1570 _valid_transients = false;
1571 _transients.clear ();
1573 send_change (PropertyChange (Properties::valid_transients));
1577 Region::drop_sources ()
1579 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1580 (*i)->dec_use_count ();
1585 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1586 (*i)->dec_use_count ();
1589 _master_sources.clear ();
1593 Region::use_sources (SourceList const & s)
1595 set<boost::shared_ptr<Source> > unique_srcs;
1597 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1599 _sources.push_back (*i);
1600 (*i)->inc_use_count ();
1601 _master_sources.push_back (*i);
1602 (*i)->inc_use_count ();
1604 /* connect only once to DropReferences, even if sources are replicated
1607 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1608 unique_srcs.insert (*i);
1609 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1615 Region::can_trim () const
1617 CanTrim ct = CanTrim (0);
1623 /* if not locked, we can always move the front later, and the end earlier
1626 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1628 if (start() != 0 || can_trim_start_before_source_start ()) {
1629 ct = CanTrim (ct | FrontTrimEarlier);
1632 if (!_sources.empty()) {
1633 if ((start() + length()) < _sources.front()->length (0)) {
1634 ct = CanTrim (ct | EndTrimLater);
1642 Region::max_source_level () const
1646 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1647 lvl = max (lvl, (*i)->level());
1654 Region::is_compound () const
1656 return max_source_level() > 0;
1660 Region::post_set (const PropertyChange& pc)
1662 if (pc.contains (Properties::position)) {
1663 recompute_position_from_lock_style ();
1668 Region::set_start_internal (framecnt_t s)
1674 Region::earliest_possible_position () const
1676 if (_start > _position) {
1679 return _position - _start;
1684 Region::latest_possible_frame () const
1686 framecnt_t minlen = max_framecnt;
1688 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1689 /* non-audio regions have a length that may vary based on their
1690 * position, so we have to pass it in the call.
1692 minlen = min (minlen, (*i)->length (_position));
1695 /* the latest possible last frame is determined by the current
1696 * position, plus the shortest source extent past _start.
1699 return _position + (minlen - _start) - 1;