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 #ifdef WITH_VIDEOTIMELINE
53 PBD::PropertyDescriptor<bool> video_locked;
55 PBD::PropertyDescriptor<bool> automatic;
56 PBD::PropertyDescriptor<bool> whole_file;
57 PBD::PropertyDescriptor<bool> import;
58 PBD::PropertyDescriptor<bool> external;
59 PBD::PropertyDescriptor<bool> sync_marked;
60 PBD::PropertyDescriptor<bool> left_of_split;
61 PBD::PropertyDescriptor<bool> right_of_split;
62 PBD::PropertyDescriptor<bool> hidden;
63 PBD::PropertyDescriptor<bool> position_locked;
64 PBD::PropertyDescriptor<bool> valid_transients;
65 PBD::PropertyDescriptor<framepos_t> start;
66 PBD::PropertyDescriptor<framecnt_t> length;
67 PBD::PropertyDescriptor<framepos_t> position;
68 PBD::PropertyDescriptor<framecnt_t> sync_position;
69 PBD::PropertyDescriptor<layer_t> layer;
70 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72 PBD::PropertyDescriptor<float> stretch;
73 PBD::PropertyDescriptor<float> shift;
74 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75 PBD::PropertyDescriptor<uint64_t> layering_index;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 #ifdef WITH_VIDEOTIMELINE
91 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
94 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
96 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
98 Properties::import.property_id = g_quark_from_static_string (X_("import"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
100 Properties::external.property_id = g_quark_from_static_string (X_("external"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
102 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
104 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
106 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
108 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
110 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
112 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
114 Properties::start.property_id = g_quark_from_static_string (X_("start"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
116 Properties::length.property_id = g_quark_from_static_string (X_("length"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
118 Properties::position.property_id = g_quark_from_static_string (X_("position"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
139 Region::register_properties ()
141 _xml_node_name = X_("Region");
143 add_property (_muted);
144 add_property (_opaque);
145 add_property (_locked);
146 #ifdef WITH_VIDEOTIMELINE
147 add_property (_video_locked);
149 add_property (_automatic);
150 add_property (_whole_file);
151 add_property (_import);
152 add_property (_external);
153 add_property (_sync_marked);
154 add_property (_left_of_split);
155 add_property (_right_of_split);
156 add_property (_hidden);
157 add_property (_position_locked);
158 add_property (_valid_transients);
159 add_property (_start);
160 add_property (_length);
161 add_property (_position);
162 add_property (_sync_position);
163 add_property (_ancestral_start);
164 add_property (_ancestral_length);
165 add_property (_stretch);
166 add_property (_shift);
167 add_property (_position_lock_style);
168 add_property (_layering_index);
170 #ifdef WITH_VIDEOTIMELINE
171 #define VTLSTATE , _video_locked (Properties::video_locked, false)
172 #define VTLCSTATE , _video_locked (Properties::video_locked, other->_video_locked)
178 #define REGION_DEFAULT_STATE(s,l) \
179 _sync_marked (Properties::sync_marked, false) \
180 , _left_of_split (Properties::left_of_split, false) \
181 , _right_of_split (Properties::right_of_split, false) \
182 , _valid_transients (Properties::valid_transients, false) \
183 , _start (Properties::start, (s)) \
184 , _length (Properties::length, (l)) \
185 , _position (Properties::position, 0) \
186 , _sync_position (Properties::sync_position, (s)) \
187 , _muted (Properties::muted, false) \
188 , _opaque (Properties::opaque, true) \
189 , _locked (Properties::locked, false) \
191 , _automatic (Properties::automatic, false) \
192 , _whole_file (Properties::whole_file, false) \
193 , _import (Properties::import, false) \
194 , _external (Properties::external, false) \
195 , _hidden (Properties::hidden, false) \
196 , _position_locked (Properties::position_locked, false) \
197 , _ancestral_start (Properties::ancestral_start, (s)) \
198 , _ancestral_length (Properties::ancestral_length, (l)) \
199 , _stretch (Properties::stretch, 1.0) \
200 , _shift (Properties::shift, 1.0) \
201 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
202 , _layering_index (Properties::layering_index, 0)
204 #define REGION_COPY_STATE(other) \
205 _sync_marked (Properties::sync_marked, other->_sync_marked) \
206 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
207 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
208 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
209 , _start(Properties::start, other->_start) \
210 , _length(Properties::length, other->_length) \
211 , _position(Properties::position, other->_position) \
212 , _sync_position(Properties::sync_position, other->_sync_position) \
213 , _muted (Properties::muted, other->_muted) \
214 , _opaque (Properties::opaque, other->_opaque) \
215 , _locked (Properties::locked, other->_locked) \
217 , _automatic (Properties::automatic, other->_automatic) \
218 , _whole_file (Properties::whole_file, other->_whole_file) \
219 , _import (Properties::import, other->_import) \
220 , _external (Properties::external, other->_external) \
221 , _hidden (Properties::hidden, other->_hidden) \
222 , _position_locked (Properties::position_locked, other->_position_locked) \
223 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
224 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
225 , _stretch (Properties::stretch, other->_stretch) \
226 , _shift (Properties::shift, other->_shift) \
227 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
228 , _layering_index (Properties::layering_index, other->_layering_index)
230 /* derived-from-derived constructor (no sources in constructor) */
231 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
232 : SessionObject(s, name)
234 , REGION_DEFAULT_STATE(start,length)
235 , _last_length (length)
237 , _first_edit (EditChangesNothing)
240 register_properties ();
242 /* no sources at this point */
245 /** Basic Region constructor (many sources) */
246 Region::Region (const SourceList& srcs)
247 : SessionObject(srcs.front()->session(), "toBeRenamed")
248 , _type (srcs.front()->type())
249 , REGION_DEFAULT_STATE(0,0)
252 , _first_edit (EditChangesNothing)
255 register_properties ();
257 _type = srcs.front()->type();
261 assert(_sources.size() > 0);
262 assert (_type == srcs.front()->type());
265 /** Create a new Region from an existing one */
266 Region::Region (boost::shared_ptr<const Region> other)
267 : SessionObject(other->session(), other->name())
268 , _type (other->data_type())
269 , REGION_COPY_STATE (other)
270 , _last_length (other->_last_length)
271 , _last_position(other->_last_position) \
272 , _first_edit (EditChangesNothing)
273 , _layer (other->_layer)
275 register_properties ();
277 /* override state that may have been incorrectly inherited from the other region
285 use_sources (other->_sources);
287 _position_lock_style = other->_position_lock_style;
288 _first_edit = other->_first_edit;
290 _start = 0; // It seems strange _start is not inherited here?
292 /* sync pos is relative to start of file. our start-in-file is now zero,
293 so set our sync position to whatever the the difference between
294 _start and _sync_pos was in the other region.
296 result is that our new sync pos points to the same point in our source(s)
297 as the sync in the other region did in its source(s).
299 since we start at zero in our source(s), it is not possible to use a sync point that
300 is before the start. reset it to _start if that was true in the other region.
303 if (other->sync_marked()) {
304 if (other->_start < other->_sync_position) {
305 /* sync pos was after the start point of the other region */
306 _sync_position = other->_sync_position - other->_start;
308 /* sync pos was before the start point of the other region. not possible here. */
309 _sync_marked = false;
310 _sync_position = _start;
313 _sync_marked = false;
314 _sync_position = _start;
317 if (Profile->get_sae()) {
318 /* reset sync point to start if its ended up
319 outside region bounds.
322 if (_sync_position < _start || _sync_position >= _start + _length) {
323 _sync_marked = false;
324 _sync_position = _start;
328 assert (_type == other->data_type());
331 /** Create a new Region from part of an existing one.
333 the start within \a other is given by \a offset
334 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
336 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
337 : SessionObject(other->session(), other->name())
338 , _type (other->data_type())
339 , REGION_COPY_STATE (other)
340 , _last_length (other->_last_length)
341 , _last_position(other->_last_position) \
342 , _first_edit (EditChangesNothing)
343 , _layer (other->_layer)
345 register_properties ();
347 /* override state that may have been incorrectly inherited from the other region
355 use_sources (other->_sources);
357 _start = other->_start + offset;
359 /* if the other region had a distinct sync point
360 set, then continue to use it as best we can.
361 otherwise, reset sync point back to start.
364 if (other->sync_marked()) {
365 if (other->_sync_position < _start) {
366 _sync_marked = false;
367 _sync_position = _start;
369 _sync_position = other->_sync_position;
372 _sync_marked = false;
373 _sync_position = _start;
376 if (Profile->get_sae()) {
377 /* reset sync point to start if its ended up
378 outside region bounds.
381 if (_sync_position < _start || _sync_position >= _start + _length) {
382 _sync_marked = false;
383 _sync_position = _start;
387 assert (_type == other->data_type());
390 /** Create a copy of @param other but with different sources. Used by filters */
391 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
392 : SessionObject (other->session(), other->name())
393 , _type (srcs.front()->type())
394 , REGION_COPY_STATE (other)
395 , _last_length (other->_last_length)
396 , _last_position (other->_last_position)
397 , _first_edit (EditChangesID)
398 , _layer (other->_layer)
400 register_properties ();
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
414 assert(_sources.size() > 0);
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
430 Region::set_name (const std::string& str)
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
436 send_change (Properties::name);
443 Region::set_length (framecnt_t len)
445 //cerr << "Region::set_length() len = " << len << endl;
450 if (_length != len && len != 0) {
452 /* check that the current _position wouldn't make the new
456 if (max_framepos - len < _position) {
460 if (!verify_length (len)) {
465 _last_length = _length;
466 set_length_internal (len);
470 invalidate_transients ();
472 if (!property_changes_suspended()) {
476 send_change (Properties::length);
481 Region::set_length_internal (framecnt_t len)
487 Region::maybe_uncopy ()
489 /* this does nothing but marked a semantic moment once upon a time */
493 Region::first_edit ()
495 boost::shared_ptr<Playlist> pl (playlist());
497 if (_first_edit != EditChangesNothing && pl) {
499 _name = RegionFactory::new_region_name (_name);
500 _first_edit = EditChangesNothing;
502 send_change (Properties::name);
504 RegionFactory::CheckNewRegion (shared_from_this());
509 Region::at_natural_position () const
511 boost::shared_ptr<Playlist> pl (playlist());
517 boost::shared_ptr<Region> whole_file_region = get_parent();
519 if (whole_file_region) {
520 if (_position == whole_file_region->position() + _start) {
529 Region::move_to_natural_position ()
531 boost::shared_ptr<Playlist> pl (playlist());
537 boost::shared_ptr<Region> whole_file_region = get_parent();
539 if (whole_file_region) {
540 set_position (whole_file_region->position() + _start);
545 Region::special_set_position (framepos_t pos)
547 /* this is used when creating a whole file region as
548 a way to store its "natural" or "captured" position.
551 _position = _position;
556 Region::set_position_lock_style (PositionLockStyle ps)
558 if (_position_lock_style != ps) {
560 boost::shared_ptr<Playlist> pl (playlist());
562 _position_lock_style = ps;
564 if (_position_lock_style == MusicTime) {
565 _session.bbt_time (_position, _bbt_time);
568 send_change (Properties::position_lock_style);
573 Region::update_after_tempo_map_change ()
575 boost::shared_ptr<Playlist> pl (playlist());
577 if (!pl || _position_lock_style != MusicTime) {
581 TempoMap& map (_session.tempo_map());
582 framepos_t pos = map.frame_time (_bbt_time);
583 set_position_internal (pos, false);
585 /* do this even if the position is the same. this helps out
586 a GUI that has moved its representation already.
588 send_change (Properties::position);
592 Region::set_position (framepos_t pos)
598 set_position_internal (pos, true);
600 /* do this even if the position is the same. this helps out
601 a GUI that has moved its representation already.
603 send_change (Properties::position);
608 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
610 /* We emit a change of Properties::position even if the position hasn't changed
611 (see Region::set_position), so we must always set this up so that
612 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
614 _last_position = _position;
616 if (_position != pos) {
619 /* check that the new _position wouldn't make the current
620 length impossible - if so, change the length.
622 XXX is this the right thing to do?
625 if (max_framepos - _length < _position) {
626 _last_length = _length;
627 _length = max_framepos - _position;
630 if (allow_bbt_recompute) {
631 recompute_position_from_lock_style ();
634 //invalidate_transients ();
639 Region::recompute_position_from_lock_style ()
641 if (_position_lock_style == MusicTime) {
642 _session.bbt_time (_position, _bbt_time);
647 Region::nudge_position (frameoffset_t n)
650 #ifdef WITH_VIDEOTIMELINE
661 framepos_t new_position = _position;
664 if (_position > max_framepos - n) {
665 new_position = max_framepos;
670 if (_position < -n) {
677 set_position_internal (new_position, true);
679 send_change (Properties::position);
683 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
685 _ancestral_length = l;
686 _ancestral_start = s;
692 Region::set_start (framepos_t pos)
694 if (locked() || position_locked()
695 #ifdef WITH_VIDEOTIMELINE
701 /* This just sets the start, nothing else. It effectively shifts
702 the contents of the Region within the overall extent of the Source,
703 without changing the Region's position or length
708 if (!verify_start (pos)) {
712 set_start_internal (pos);
715 invalidate_transients ();
717 send_change (Properties::start);
722 Region::trim_start (framepos_t new_position)
724 if (locked() || position_locked()
725 #ifdef WITH_VIDEOTIMELINE
732 framepos_t new_start;
733 frameoffset_t const start_shift = new_position - _position;
735 if (start_shift > 0) {
737 if (_start > max_framepos - start_shift) {
738 new_start = max_framepos;
740 new_start = _start + start_shift;
743 if (!verify_start (new_start)) {
747 } else if (start_shift < 0) {
749 if (_start < -start_shift) {
752 new_start = _start + start_shift;
759 if (new_start == _start) {
763 set_start_internal (new_start);
767 send_change (Properties::start);
771 Region::trim_front (framepos_t new_position)
773 modify_front (new_position, false);
777 Region::cut_front (framepos_t new_position)
779 modify_front (new_position, true);
783 Region::cut_end (framepos_t new_endpoint)
785 modify_end (new_endpoint, true);
789 Region::modify_front (framepos_t new_position, bool reset_fade)
795 framepos_t end = last_frame();
796 framepos_t source_zero;
798 if (_position > _start) {
799 source_zero = _position - _start;
801 source_zero = 0; // its actually negative, but this will work for us
804 if (new_position < end) { /* can't trim it zero or negative length */
806 framecnt_t newlen = 0;
807 framepos_t delta = 0;
809 if (!can_trim_start_before_source_start ()) {
810 /* can't trim it back past where source position zero is located */
811 new_position = max (new_position, source_zero);
814 if (new_position > _position) {
815 newlen = _length - (new_position - _position);
816 delta = -1 * (new_position - _position);
818 newlen = _length + (_position - new_position);
819 delta = _position - new_position;
822 trim_to_internal (new_position, newlen);
825 _right_of_split = true;
828 if (!property_changes_suspended()) {
829 recompute_at_start ();
832 if (_transients.size() > 0){
833 adjust_transients(delta);
839 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
845 if (new_endpoint > _position) {
846 trim_to_internal (_position, new_endpoint - _position);
848 _left_of_split = true;
850 if (!property_changes_suspended()) {
856 /** @param new_endpoint New region end point, such that, for example,
857 * a region at 0 of length 10 has an endpoint of 9.
861 Region::trim_end (framepos_t new_endpoint)
863 modify_end (new_endpoint, false);
867 Region::trim_to (framepos_t position, framecnt_t length)
873 trim_to_internal (position, length);
875 if (!property_changes_suspended()) {
876 recompute_at_start ();
882 Region::trim_to_internal (framepos_t position, framecnt_t length)
884 framepos_t new_start;
890 frameoffset_t const start_shift = position - _position;
892 if (start_shift > 0) {
894 if (_start > max_framepos - start_shift) {
895 new_start = max_framepos;
897 new_start = _start + start_shift;
900 } else if (start_shift < 0) {
902 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
905 new_start = _start + start_shift;
912 if (!verify_start_and_length (new_start, length)) {
916 PropertyChange what_changed;
918 if (_start != new_start) {
919 set_start_internal (new_start);
920 what_changed.add (Properties::start);
923 /* Set position before length, otherwise for MIDI regions this bad thing happens:
924 * 1. we call set_length_internal; length in beats is computed using the region's current
925 * (soon-to-be old) position
926 * 2. we call set_position_internal; position is set and length in frames re-computed using
927 * length in beats from (1) but at the new position, which is wrong if the region
928 * straddles a tempo/meter change.
931 if (_position != position) {
932 if (!property_changes_suspended()) {
933 _last_position = _position;
935 set_position_internal (position, true);
936 what_changed.add (Properties::position);
939 if (_length != length) {
940 if (!property_changes_suspended()) {
941 _last_length = _length;
943 set_length_internal (length);
944 what_changed.add (Properties::length);
949 PropertyChange start_and_length;
951 start_and_length.add (Properties::start);
952 start_and_length.add (Properties::length);
954 if (what_changed.contains (start_and_length)) {
958 if (!what_changed.empty()) {
959 send_change (what_changed);
964 Region::set_hidden (bool yn)
966 if (hidden() != yn) {
968 send_change (Properties::hidden);
973 Region::set_whole_file (bool yn)
976 /* no change signal */
980 Region::set_automatic (bool yn)
983 /* no change signal */
987 Region::set_muted (bool yn)
991 send_change (Properties::muted);
996 Region::set_opaque (bool yn)
998 if (opaque() != yn) {
1000 send_change (Properties::opaque);
1005 Region::set_locked (bool yn)
1007 if (locked() != yn) {
1009 send_change (Properties::locked);
1013 #ifdef WITH_VIDEOTIMELINE
1015 Region::set_video_locked (bool yn)
1017 if (video_locked() != yn) {
1019 send_change (Properties::video_locked);
1025 Region::set_position_locked (bool yn)
1027 if (position_locked() != yn) {
1028 _position_locked = yn;
1029 send_change (Properties::locked);
1033 /** Set the region's sync point.
1034 * @param absolute_pos Session time.
1037 Region::set_sync_position (framepos_t absolute_pos)
1039 /* position within our file */
1040 framepos_t const file_pos = _start + (absolute_pos - _position);
1042 if (file_pos != _sync_position) {
1043 _sync_marked = true;
1044 _sync_position = file_pos;
1045 if (!property_changes_suspended()) {
1049 send_change (Properties::sync_position);
1054 Region::clear_sync_position ()
1056 if (sync_marked()) {
1057 _sync_marked = false;
1058 if (!property_changes_suspended()) {
1062 send_change (Properties::sync_position);
1066 /* @return the sync point relative the first frame of the region */
1068 Region::sync_offset (int& dir) const
1070 if (sync_marked()) {
1071 if (_sync_position > _start) {
1073 return _sync_position - _start;
1076 return _start - _sync_position;
1085 Region::adjust_to_sync (framepos_t pos) const
1088 frameoffset_t offset = sync_offset (sync_dir);
1090 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1099 if (max_framepos - pos > offset) {
1107 /** @return Sync position in session time */
1109 Region::sync_position() const
1111 if (sync_marked()) {
1112 return _position - _start + _sync_position;
1114 /* if sync has not been marked, use the start of the region */
1122 boost::shared_ptr<Playlist> pl (playlist());
1124 pl->raise_region (shared_from_this ());
1131 boost::shared_ptr<Playlist> pl (playlist());
1133 pl->lower_region (shared_from_this ());
1139 Region::raise_to_top ()
1141 boost::shared_ptr<Playlist> pl (playlist());
1143 pl->raise_region_to_top (shared_from_this());
1148 Region::lower_to_bottom ()
1150 boost::shared_ptr<Playlist> pl (playlist());
1152 pl->lower_region_to_bottom (shared_from_this());
1157 Region::set_layer (layer_t l)
1165 XMLNode *node = new XMLNode ("Region");
1168 LocaleGuard lg (X_("POSIX"));
1169 const char* fe = NULL;
1171 /* custom version of 'add_properties (*node);'
1172 * skip values that have have dedicated save functions
1173 * in AudioRegion::state()
1175 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1176 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1177 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1178 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1179 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1180 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1181 i->second->get_value (*node);
1184 id().print (buf, sizeof (buf));
1185 node->add_property ("id", buf);
1186 node->add_property ("type", _type.to_string());
1188 switch (_first_edit) {
1189 case EditChangesNothing:
1192 case EditChangesName:
1198 default: /* should be unreachable but makes g++ happy */
1203 node->add_property ("first-edit", fe);
1205 /* note: flags are stored by derived classes */
1207 if (_position_lock_style != AudioTime) {
1210 node->add_property ("bbt-position", str.str());
1213 for (uint32_t n=0; n < _sources.size(); ++n) {
1214 snprintf (buf2, sizeof(buf2), "source-%d", n);
1215 _sources[n]->id().print (buf, sizeof(buf));
1216 node->add_property (buf2, buf);
1219 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1220 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1221 _master_sources[n]->id().print (buf, sizeof (buf));
1222 node->add_property (buf2, buf);
1225 /* Only store nested sources for the whole-file region that acts
1226 as the parent/root of all regions using it.
1229 if (_whole_file && max_source_level() > 0) {
1231 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1233 /* region is compound - get its playlist and
1234 store that before we list the region that
1238 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1239 nested_node->add_child_nocopy ((*s)->get_state ());
1243 node->add_child_nocopy (*nested_node);
1248 node->add_child_copy (*_extra_xml);
1255 Region::get_state ()
1261 Region::set_state (const XMLNode& node, int version)
1263 PropertyChange what_changed;
1264 return _set_state (node, version, what_changed, true);
1268 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1270 const XMLProperty* prop;
1272 Stateful::save_extra_xml (node);
1274 what_changed = set_values (node);
1278 if (_position_lock_style == MusicTime) {
1279 if ((prop = node.property ("bbt-position")) == 0) {
1280 /* missing BBT info, revert to audio time locking */
1281 _position_lock_style = AudioTime;
1283 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1286 &_bbt_time.ticks) != 3) {
1287 _position_lock_style = AudioTime;
1292 /* fix problems with old sessions corrupted by impossible
1293 values for _stretch or _shift
1295 if (_stretch == 0.0f) {
1299 if (_shift == 0.0f) {
1304 send_change (what_changed);
1307 /* Quick fix for 2.x sessions when region is muted */
1308 if ((prop = node.property (X_("flags")))) {
1309 if (string::npos != prop->value().find("Muted")){
1319 Region::suspend_property_changes ()
1321 Stateful::suspend_property_changes ();
1322 _last_length = _length;
1323 _last_position = _position;
1327 Region::mid_thaw (const PropertyChange& what_changed)
1329 if (what_changed.contains (Properties::length)) {
1330 if (what_changed.contains (Properties::position)) {
1331 recompute_at_start ();
1333 recompute_at_end ();
1338 Region::send_change (const PropertyChange& what_changed)
1340 if (what_changed.empty()) {
1344 Stateful::send_change (what_changed);
1346 if (!Stateful::property_changes_suspended()) {
1348 /* Try and send a shared_pointer unless this is part of the constructor.
1353 boost::shared_ptr<Region> rptr = shared_from_this();
1354 RegionPropertyChanged (rptr, what_changed);
1356 /* no shared_ptr available, relax; */
1362 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1364 return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1368 Region::equivalent (boost::shared_ptr<const Region> other) const
1370 return _start == other->_start &&
1371 _position == other->_position &&
1372 _length == other->_length;
1376 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1378 return _start == other->_start &&
1379 _length == other->_length;
1383 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1385 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1389 Region::source_deleted (boost::weak_ptr<Source>)
1393 if (!_session.deletion_in_progress()) {
1394 /* this is a very special case: at least one of the region's
1395 sources has bee deleted, so invalidate all references to
1396 ourselves. Do NOT do this during session deletion, because
1397 then we run the risk that this will actually result
1398 in this object being deleted (as refcnt goes to zero)
1399 while emitting DropReferences.
1407 Region::master_source_names ()
1409 SourceList::iterator i;
1411 vector<string> names;
1412 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1413 names.push_back((*i)->name());
1420 Region::set_master_sources (const SourceList& srcs)
1422 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1423 (*i)->dec_use_count ();
1426 _master_sources = srcs;
1427 assert (_sources.size() == _master_sources.size());
1429 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1430 (*i)->inc_use_count ();
1435 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1440 if ((_sources.size() != other->_sources.size()) ||
1441 (_master_sources.size() != other->_master_sources.size())) {
1445 SourceList::const_iterator i;
1446 SourceList::const_iterator io;
1448 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1449 if ((*i)->id() != (*io)->id()) {
1454 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1455 if ((*i)->id() != (*io)->id()) {
1464 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1470 SourceList::const_iterator i;
1471 SourceList::const_iterator io;
1473 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1474 if ((*i)->id() == (*io)->id()) {
1483 Region::source_string () const
1485 //string res = itos(_sources.size());
1488 res << _sources.size() << ":";
1490 SourceList::const_iterator i;
1492 for (i = _sources.begin(); i != _sources.end(); ++i) {
1493 res << (*i)->id() << ":";
1496 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1497 res << (*i)->id() << ":";
1504 Region::uses_source (boost::shared_ptr<const Source> source) const
1506 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1511 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1514 if (ps->playlist()->uses_source (source)) {
1524 Region::source_length(uint32_t n) const
1526 assert (n < _sources.size());
1527 return _sources[n]->length (_position - _start);
1531 Region::verify_length (framecnt_t len)
1533 if (source() && (source()->destructive() || source()->length_mutable())) {
1537 framecnt_t maxlen = 0;
1539 for (uint32_t n = 0; n < _sources.size(); ++n) {
1540 maxlen = max (maxlen, source_length(n) - _start);
1543 len = min (len, maxlen);
1549 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1551 if (source() && (source()->destructive() || source()->length_mutable())) {
1555 framecnt_t maxlen = 0;
1557 for (uint32_t n = 0; n < _sources.size(); ++n) {
1558 maxlen = max (maxlen, source_length(n) - new_start);
1561 new_length = min (new_length, maxlen);
1567 Region::verify_start (framepos_t pos)
1569 if (source() && (source()->destructive() || source()->length_mutable())) {
1573 for (uint32_t n = 0; n < _sources.size(); ++n) {
1574 if (pos > source_length(n) - _length) {
1582 Region::verify_start_mutable (framepos_t& new_start)
1584 if (source() && (source()->destructive() || source()->length_mutable())) {
1588 for (uint32_t n = 0; n < _sources.size(); ++n) {
1589 if (new_start > source_length(n) - _length) {
1590 new_start = source_length(n) - _length;
1596 boost::shared_ptr<Region>
1597 Region::get_parent() const
1599 boost::shared_ptr<Playlist> pl (playlist());
1602 boost::shared_ptr<Region> r;
1603 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1605 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1606 return boost::static_pointer_cast<Region> (r);
1610 return boost::shared_ptr<Region>();
1614 Region::apply (Filter& filter, Progress* progress)
1616 return filter.run (shared_from_this(), progress);
1621 Region::invalidate_transients ()
1623 _valid_transients = false;
1624 _transients.clear ();
1626 send_change (PropertyChange (Properties::valid_transients));
1630 Region::drop_sources ()
1632 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1633 (*i)->dec_use_count ();
1638 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1639 (*i)->dec_use_count ();
1642 _master_sources.clear ();
1646 Region::use_sources (SourceList const & s)
1648 set<boost::shared_ptr<Source> > unique_srcs;
1650 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1652 _sources.push_back (*i);
1653 (*i)->inc_use_count ();
1654 _master_sources.push_back (*i);
1655 (*i)->inc_use_count ();
1657 /* connect only once to DropReferences, even if sources are replicated
1660 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1661 unique_srcs.insert (*i);
1662 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1668 Region::can_trim () const
1670 CanTrim ct = CanTrim (0);
1676 /* if not locked, we can always move the front later, and the end earlier
1679 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1681 if (start() != 0 || can_trim_start_before_source_start ()) {
1682 ct = CanTrim (ct | FrontTrimEarlier);
1685 if (!_sources.empty()) {
1686 if ((start() + length()) < _sources.front()->length (0)) {
1687 ct = CanTrim (ct | EndTrimLater);
1695 Region::max_source_level () const
1699 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1700 lvl = max (lvl, (*i)->level());
1707 Region::is_compound () const
1709 return max_source_level() > 0;
1713 Region::post_set (const PropertyChange& pc)
1715 if (pc.contains (Properties::position)) {
1716 recompute_position_from_lock_style ();
1721 Region::set_start_internal (framecnt_t s)
1727 Region::earliest_possible_position () const
1729 if (_start > _position) {
1732 return _position - _start;
1737 Region::latest_possible_frame () const
1739 framecnt_t minlen = max_framecnt;
1741 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1742 /* non-audio regions have a length that may vary based on their
1743 * position, so we have to pass it in the call.
1745 minlen = min (minlen, (*i)->length (_position));
1748 /* the latest possible last frame is determined by the current
1749 * position, plus the shortest source extent past _start.
1752 return _position + (minlen - _start) - 1;