new record-step-edit icon for track/bus list
[ardour2.git] / libs / ardour / region.cc
blob76b7ec0ec63f8f8937af583979b496ec1b68d7a2
1 /*
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.
20 #include <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24 #include <sstream>
26 #include <glibmm/thread.h>
27 #include "pbd/xml++.h"
28 #include "pbd/stacktrace.h"
29 #include "pbd/enumwriter.h"
31 #include "ardour/debug.h"
32 #include "ardour/file_source.h"
33 #include "ardour/filter.h"
34 #include "ardour/playlist.h"
35 #include "ardour/profile.h"
36 #include "ardour/region.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/session.h"
39 #include "ardour/source.h"
40 #include "ardour/source_factory.h"
41 #include "ardour/tempo.h"
42 #include "ardour/utils.h"
44 #include "i18n.h"
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
50 namespace ARDOUR {
51 namespace Properties {
52 PBD::PropertyDescriptor<bool> muted;
53 PBD::PropertyDescriptor<bool> opaque;
54 PBD::PropertyDescriptor<bool> 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;
78 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
80 void
81 Region::make_property_quarks ()
83 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
85 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
87 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
89 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
91 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
93 Properties::import.property_id = g_quark_from_static_string (X_("import"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
95 Properties::external.property_id = g_quark_from_static_string (X_("external"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
97 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
99 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
101 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
103 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
105 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
107 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
109 Properties::start.property_id = g_quark_from_static_string (X_("start"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
111 Properties::length.property_id = g_quark_from_static_string (X_("length"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
113 Properties::position.property_id = g_quark_from_static_string (X_("position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
115 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
117 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
119 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
121 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
123 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
125 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
127 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
128 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
131 void
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 (_layer);
154 add_property (_ancestral_start);
155 add_property (_ancestral_length);
156 add_property (_stretch);
157 add_property (_shift);
158 add_property (_position_lock_style);
161 #define REGION_DEFAULT_STATE(s,l) \
162 _muted (Properties::muted, false) \
163 , _opaque (Properties::opaque, true) \
164 , _locked (Properties::locked, false) \
165 , _automatic (Properties::automatic, false) \
166 , _whole_file (Properties::whole_file, false) \
167 , _import (Properties::import, false) \
168 , _external (Properties::external, false) \
169 , _sync_marked (Properties::sync_marked, false) \
170 , _left_of_split (Properties::left_of_split, false) \
171 , _right_of_split (Properties::right_of_split, false) \
172 , _hidden (Properties::hidden, false) \
173 , _position_locked (Properties::position_locked, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _sync_position (Properties::sync_position, (s)) \
179 , _layer (Properties::layer, 0) \
180 , _ancestral_start (Properties::ancestral_start, (s)) \
181 , _ancestral_length (Properties::ancestral_length, (l)) \
182 , _stretch (Properties::stretch, 1.0) \
183 , _shift (Properties::shift, 1.0) \
184 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
186 #define REGION_COPY_STATE(other) \
187 _muted (Properties::muted, other->_muted) \
188 , _opaque (Properties::opaque, other->_opaque) \
189 , _locked (Properties::locked, other->_locked) \
190 , _automatic (Properties::automatic, other->_automatic) \
191 , _whole_file (Properties::whole_file, other->_whole_file) \
192 , _import (Properties::import, other->_import) \
193 , _external (Properties::external, other->_external) \
194 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
195 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
196 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
197 , _hidden (Properties::hidden, other->_hidden) \
198 , _position_locked (Properties::position_locked, other->_position_locked) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _layer (Properties::layer, other->_layer) \
205 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
206 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
207 , _stretch (Properties::stretch, other->_stretch) \
208 , _shift (Properties::shift, other->_shift) \
209 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
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)
214 , _type(type)
215 , REGION_DEFAULT_STATE(start,length)
216 , _last_length (length)
217 , _last_position (0)
218 , _first_edit (EditChangesNothing)
219 , _read_data_count(0)
220 , _last_layer_op(0)
221 , _pending_explicit_relayer (false)
223 register_properties ();
225 /* no sources at this point */
228 /** Basic Region constructor (many sources) */
229 Region::Region (const SourceList& srcs)
230 : SessionObject(srcs.front()->session(), "toBeRenamed")
231 , _type (srcs.front()->type())
232 , REGION_DEFAULT_STATE(0,0)
233 , _last_length (0)
234 , _last_position (0)
235 , _first_edit (EditChangesNothing)
236 , _read_data_count(0)
237 , _last_layer_op (0)
238 , _pending_explicit_relayer (false)
240 register_properties ();
242 _type = srcs.front()->type();
244 use_sources (srcs);
246 assert(_sources.size() > 0);
247 assert (_type == srcs.front()->type());
250 /** Create a new Region from an existing one */
251 Region::Region (boost::shared_ptr<const Region> other)
252 : SessionObject(other->session(), other->name())
253 , _type (other->data_type())
254 , REGION_COPY_STATE (other)
255 , _last_length (other->_last_length)
256 , _last_position(other->_last_position) \
257 , _first_edit (EditChangesNothing)
258 , _read_data_count(0)
259 , _last_layer_op (0)
260 , _pending_explicit_relayer (false)
263 register_properties ();
265 /* override state that may have been incorrectly inherited from the other region
268 _position = 0;
269 _locked = false;
270 _whole_file = false;
271 _hidden = false;
273 use_sources (other->_sources);
275 _position_lock_style = other->_position_lock_style;
276 _first_edit = other->_first_edit;
278 _start = 0; // It seems strange _start is not inherited here?
280 /* sync pos is relative to start of file. our start-in-file is now zero,
281 so set our sync position to whatever the the difference between
282 _start and _sync_pos was in the other region.
284 result is that our new sync pos points to the same point in our source(s)
285 as the sync in the other region did in its source(s).
287 since we start at zero in our source(s), it is not possible to use a sync point that
288 is before the start. reset it to _start if that was true in the other region.
291 if (other->sync_marked()) {
292 if (other->_start < other->_sync_position) {
293 /* sync pos was after the start point of the other region */
294 _sync_position = other->_sync_position - other->_start;
295 } else {
296 /* sync pos was before the start point of the other region. not possible here. */
297 _sync_marked = false;
298 _sync_position = _start;
300 } else {
301 _sync_marked = false;
302 _sync_position = _start;
305 if (Profile->get_sae()) {
306 /* reset sync point to start if its ended up
307 outside region bounds.
310 if (_sync_position < _start || _sync_position >= _start + _length) {
311 _sync_marked = false;
312 _sync_position = _start;
316 assert (_type == other->data_type());
319 /** Create a new Region from part of an existing one.
321 the start within \a other is given by \a offset
322 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
324 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
325 : SessionObject(other->session(), other->name())
326 , _type (other->data_type())
327 , REGION_COPY_STATE (other)
328 , _last_length (other->_last_length)
329 , _last_position(other->_last_position) \
330 , _first_edit (EditChangesNothing)
331 , _read_data_count(0)
332 , _last_layer_op (0)
333 , _pending_explicit_relayer (false)
336 register_properties ();
338 /* override state that may have been incorrectly inherited from the other region
341 _position = 0;
342 _locked = false;
343 _whole_file = false;
344 _hidden = false;
346 use_sources (other->_sources);
348 _start = other->_start + offset;
350 /* if the other region had a distinct sync point
351 set, then continue to use it as best we can.
352 otherwise, reset sync point back to start.
355 if (other->sync_marked()) {
356 if (other->_sync_position < _start) {
357 _sync_marked = false;
358 _sync_position = _start;
359 } else {
360 _sync_position = other->_sync_position;
362 } else {
363 _sync_marked = false;
364 _sync_position = _start;
367 if (Profile->get_sae()) {
368 /* reset sync point to start if its ended up
369 outside region bounds.
372 if (_sync_position < _start || _sync_position >= _start + _length) {
373 _sync_marked = false;
374 _sync_position = _start;
378 assert (_type == other->data_type());
381 /** Create a copy of @param other but with different sources. Used by filters */
382 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
383 : SessionObject (other->session(), other->name())
384 , _type (srcs.front()->type())
385 , REGION_COPY_STATE (other)
386 , _last_length (other->_last_length)
387 , _last_position (other->_last_position)
388 , _first_edit (EditChangesID)
389 , _read_data_count (0)
390 , _last_layer_op (other->_last_layer_op)
391 , _pending_explicit_relayer (false)
393 register_properties ();
395 _locked = false;
396 _position_locked = false;
398 other->_first_edit = EditChangesName;
400 if (other->_extra_xml) {
401 _extra_xml = new XMLNode (*other->_extra_xml);
402 } else {
403 _extra_xml = 0;
406 use_sources (srcs);
407 assert(_sources.size() > 0);
410 Region::~Region ()
412 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
413 drop_sources ();
416 void
417 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
419 _playlist = wpl.lock();
422 bool
423 Region::set_name (const std::string& str)
425 if (_name != str) {
426 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
427 assert(_name == str);
429 send_change (Properties::name);
432 return true;
435 void
436 Region::set_length (framecnt_t len)
438 //cerr << "Region::set_length() len = " << len << endl;
439 if (locked()) {
440 return;
443 if (_length != len && len != 0) {
445 /* check that the current _position wouldn't make the new
446 length impossible.
449 if (max_framepos - len < _position) {
450 return;
453 if (!verify_length (len)) {
454 return;
458 _last_length = _length;
459 set_length_internal (len);
460 _whole_file = false;
461 first_edit ();
462 maybe_uncopy ();
463 invalidate_transients ();
465 if (!property_changes_suspended()) {
466 recompute_at_end ();
469 send_change (Properties::length);
473 void
474 Region::set_length_internal (framecnt_t len)
476 _length = len;
479 void
480 Region::maybe_uncopy ()
482 /* this does nothing but marked a semantic moment once upon a time */
485 void
486 Region::first_edit ()
488 boost::shared_ptr<Playlist> pl (playlist());
490 if (_first_edit != EditChangesNothing && pl) {
492 _name = RegionFactory::new_region_name (_name);
493 _first_edit = EditChangesNothing;
495 send_change (Properties::name);
497 RegionFactory::CheckNewRegion (shared_from_this());
501 bool
502 Region::at_natural_position () const
504 boost::shared_ptr<Playlist> pl (playlist());
506 if (!pl) {
507 return false;
510 boost::shared_ptr<Region> whole_file_region = get_parent();
512 if (whole_file_region) {
513 if (_position == whole_file_region->position() + _start) {
514 return true;
518 return false;
521 void
522 Region::move_to_natural_position ()
524 boost::shared_ptr<Playlist> pl (playlist());
526 if (!pl) {
527 return;
530 boost::shared_ptr<Region> whole_file_region = get_parent();
532 if (whole_file_region) {
533 set_position (whole_file_region->position() + _start);
537 void
538 Region::special_set_position (framepos_t pos)
540 /* this is used when creating a whole file region as
541 a way to store its "natural" or "captured" position.
544 _position = _position;
545 _position = pos;
548 void
549 Region::set_position_lock_style (PositionLockStyle ps)
551 if (_position_lock_style != ps) {
553 boost::shared_ptr<Playlist> pl (playlist());
555 if (!pl) {
556 return;
559 _position_lock_style = ps;
561 if (_position_lock_style == MusicTime) {
562 _session.tempo_map().bbt_time (_position, _bbt_time);
565 send_change (Properties::position_lock_style);
570 void
571 Region::update_position_after_tempo_map_change ()
573 boost::shared_ptr<Playlist> pl (playlist());
575 if (!pl || _position_lock_style != MusicTime) {
576 return;
579 TempoMap& map (_session.tempo_map());
580 framepos_t pos = map.frame_time (_bbt_time);
581 set_position_internal (pos, false);
583 /* do this even if the position is the same. this helps out
584 a GUI that has moved its representation already.
586 send_change (Properties::position);
589 void
590 Region::set_position (framepos_t pos)
592 if (!can_move()) {
593 return;
596 set_position_internal (pos, true);
598 /* do this even if the position is the same. this helps out
599 a GUI that has moved its representation already.
601 send_change (Properties::position);
605 void
606 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
608 if (_position != pos) {
609 _last_position = _position;
610 _position = pos;
612 /* check that the new _position wouldn't make the current
613 length impossible - if so, change the length.
615 XXX is this the right thing to do?
618 if (max_framepos - _length < _position) {
619 _last_length = _length;
620 _length = max_framepos - _position;
623 if (allow_bbt_recompute) {
624 recompute_position_from_lock_style ();
627 //invalidate_transients ();
631 void
632 Region::set_position_on_top (framepos_t pos)
634 if (locked()) {
635 return;
638 if (_position != pos) {
639 set_position_internal (pos, true);
642 boost::shared_ptr<Playlist> pl (playlist());
644 if (pl) {
645 pl->raise_region_to_top (shared_from_this ());
648 /* do this even if the position is the same. this helps out
649 a GUI that has moved its representation already.
651 send_change (Properties::position);
654 void
655 Region::recompute_position_from_lock_style ()
657 if (_position_lock_style == MusicTime) {
658 _session.tempo_map().bbt_time (_position, _bbt_time);
662 void
663 Region::nudge_position (frameoffset_t n)
665 if (locked()) {
666 return;
669 if (n == 0) {
670 return;
673 framepos_t new_position = _position;
675 if (n > 0) {
676 if (_position > max_framepos - n) {
677 new_position = max_framepos;
678 } else {
679 new_position += n;
681 } else {
682 if (_position < -n) {
683 new_position = 0;
684 } else {
685 new_position += n;
689 set_position_internal (new_position, true);
691 send_change (Properties::position);
694 void
695 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
697 _ancestral_length = l;
698 _ancestral_start = s;
699 _stretch = st;
700 _shift = sh;
703 void
704 Region::set_start (framepos_t pos)
706 if (locked() || position_locked()) {
707 return;
709 /* This just sets the start, nothing else. It effectively shifts
710 the contents of the Region within the overall extent of the Source,
711 without changing the Region's position or length
714 if (_start != pos) {
716 if (!verify_start (pos)) {
717 return;
720 _start = pos;
721 _whole_file = false;
722 first_edit ();
723 invalidate_transients ();
725 send_change (Properties::start);
729 void
730 Region::trim_start (framepos_t new_position)
732 if (locked() || position_locked()) {
733 return;
735 framepos_t new_start;
736 frameoffset_t const start_shift = new_position - _position;
738 if (start_shift > 0) {
740 if (_start > max_framepos - start_shift) {
741 new_start = max_framepos;
742 } else {
743 new_start = _start + start_shift;
746 if (!verify_start (new_start)) {
747 return;
750 } else if (start_shift < 0) {
752 if (_start < -start_shift) {
753 new_start = 0;
754 } else {
755 new_start = _start + start_shift;
758 } else {
759 return;
762 if (new_start == _start) {
763 return;
766 _start = new_start;
767 _whole_file = false;
768 first_edit ();
770 send_change (Properties::start);
773 void
774 Region::trim_front (framepos_t new_position)
776 modify_front (new_position, false);
779 void
780 Region::cut_front (framepos_t new_position)
782 modify_front (new_position, true);
785 void
786 Region::cut_end (framepos_t new_endpoint)
788 modify_end (new_endpoint, true);
791 void
792 Region::modify_front (framepos_t new_position, bool reset_fade)
794 if (locked()) {
795 return;
798 framepos_t end = last_frame();
799 framepos_t source_zero;
801 if (_position > _start) {
802 source_zero = _position - _start;
803 } else {
804 source_zero = 0; // its actually negative, but this will work for us
807 if (new_position < end) { /* can't trim it zero or negative length */
809 framecnt_t newlen = 0;
810 framepos_t delta = 0;
812 if (!can_trim_start_before_source_start ()) {
813 /* can't trim it back past where source position zero is located */
814 new_position = max (new_position, source_zero);
817 if (new_position > _position) {
818 newlen = _length - (new_position - _position);
819 delta = -1 * (new_position - _position);
820 } else {
821 newlen = _length + (_position - new_position);
822 delta = _position - new_position;
825 trim_to_internal (new_position, newlen);
827 if (reset_fade) {
828 _right_of_split = true;
831 if (!property_changes_suspended()) {
832 recompute_at_start ();
835 if (_transients.size() > 0){
836 adjust_transients(delta);
841 void
842 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
844 if (locked()) {
845 return;
848 if (new_endpoint > _position) {
849 trim_to_internal (_position, new_endpoint - _position +1);
850 if (reset_fade) {
851 _left_of_split = true;
853 if (!property_changes_suspended()) {
854 recompute_at_end ();
859 /** @param new_endpoint New region end point, such that, for example,
860 * a region at 0 of length 10 has an endpoint of 9.
863 void
864 Region::trim_end (framepos_t new_endpoint)
866 modify_end (new_endpoint, false);
869 void
870 Region::trim_to (framepos_t position, framecnt_t length)
872 if (locked()) {
873 return;
876 trim_to_internal (position, length);
878 if (!property_changes_suspended()) {
879 recompute_at_start ();
880 recompute_at_end ();
884 void
885 Region::trim_to_internal (framepos_t position, framecnt_t length)
887 framepos_t new_start;
889 if (locked()) {
890 return;
893 frameoffset_t const start_shift = position - _position;
895 if (start_shift > 0) {
897 if (_start > max_framepos - start_shift) {
898 new_start = max_framepos;
899 } else {
900 new_start = _start + start_shift;
903 } else if (start_shift < 0) {
905 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
906 new_start = 0;
907 } else {
908 new_start = _start + start_shift;
911 } else {
912 new_start = _start;
915 if (!verify_start_and_length (new_start, length)) {
916 return;
919 PropertyChange what_changed;
921 if (_start != new_start) {
922 _start = new_start;
923 what_changed.add (Properties::start);
926 /* Set position before length, otherwise for MIDI regions this bad thing happens:
927 * 1. we call set_length_internal; length in beats is computed using the region's current
928 * (soon-to-be old) position
929 * 2. we call set_position_internal; position is set and length in frames re-computed using
930 * length in beats from (1) but at the new position, which is wrong if the region
931 * straddles a tempo/meter change.
934 if (_position != position) {
935 if (!property_changes_suspended()) {
936 _last_position = _position;
938 set_position_internal (position, true);
939 what_changed.add (Properties::position);
942 if (_length != length) {
943 if (!property_changes_suspended()) {
944 _last_length = _length;
946 set_length_internal (length);
947 what_changed.add (Properties::length);
950 _whole_file = false;
952 PropertyChange start_and_length;
954 start_and_length.add (Properties::start);
955 start_and_length.add (Properties::length);
957 if (what_changed.contains (start_and_length)) {
958 first_edit ();
961 if (!what_changed.empty()) {
962 send_change (what_changed);
966 void
967 Region::set_hidden (bool yn)
969 if (hidden() != yn) {
970 _hidden = yn;
971 send_change (Properties::hidden);
975 void
976 Region::set_whole_file (bool yn)
978 _whole_file = yn;
979 /* no change signal */
982 void
983 Region::set_automatic (bool yn)
985 _automatic = yn;
986 /* no change signal */
989 void
990 Region::set_muted (bool yn)
992 if (muted() != yn) {
993 _muted = yn;
994 send_change (Properties::muted);
998 void
999 Region::set_opaque (bool yn)
1001 if (opaque() != yn) {
1002 _opaque = yn;
1003 send_change (Properties::opaque);
1007 void
1008 Region::set_locked (bool yn)
1010 if (locked() != yn) {
1011 _locked = yn;
1012 send_change (Properties::locked);
1016 void
1017 Region::set_position_locked (bool yn)
1019 if (position_locked() != yn) {
1020 _position_locked = yn;
1021 send_change (Properties::locked);
1025 /** Set the region's sync point.
1026 * @param absolute_pos Session time.
1028 void
1029 Region::set_sync_position (framepos_t absolute_pos)
1031 /* position within our file */
1032 framepos_t const file_pos = _start + (absolute_pos - _position);
1034 if (file_pos != _sync_position) {
1035 _sync_marked = true;
1036 _sync_position = file_pos;
1037 if (!property_changes_suspended()) {
1038 maybe_uncopy ();
1041 send_change (Properties::sync_position);
1045 void
1046 Region::clear_sync_position ()
1048 if (sync_marked()) {
1049 _sync_marked = false;
1050 if (!property_changes_suspended()) {
1051 maybe_uncopy ();
1054 send_change (Properties::sync_position);
1058 /* @return the sync point relative the first frame of the region */
1059 frameoffset_t
1060 Region::sync_offset (int& dir) const
1062 if (sync_marked()) {
1063 if (_sync_position > _start) {
1064 dir = 1;
1065 return _sync_position - _start;
1066 } else {
1067 dir = -1;
1068 return _start - _sync_position;
1070 } else {
1071 dir = 0;
1072 return 0;
1076 framepos_t
1077 Region::adjust_to_sync (framepos_t pos) const
1079 int sync_dir;
1080 frameoffset_t offset = sync_offset (sync_dir);
1082 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1084 if (sync_dir > 0) {
1085 if (pos > offset) {
1086 pos -= offset;
1087 } else {
1088 pos = 0;
1090 } else {
1091 if (max_framepos - pos > offset) {
1092 pos += offset;
1096 return pos;
1099 /** @return Sync position in session time */
1100 framepos_t
1101 Region::sync_position() const
1103 if (sync_marked()) {
1104 return _position - _start + _sync_position;
1105 } else {
1106 /* if sync has not been marked, use the start of the region */
1107 return _position;
1111 void
1112 Region::raise ()
1114 boost::shared_ptr<Playlist> pl (playlist());
1115 if (pl) {
1116 pl->raise_region (shared_from_this ());
1120 void
1121 Region::lower ()
1123 boost::shared_ptr<Playlist> pl (playlist());
1124 if (pl) {
1125 pl->lower_region (shared_from_this ());
1130 void
1131 Region::raise_to_top ()
1133 boost::shared_ptr<Playlist> pl (playlist());
1134 if (pl) {
1135 pl->raise_region_to_top (shared_from_this());
1139 void
1140 Region::lower_to_bottom ()
1142 boost::shared_ptr<Playlist> pl (playlist());
1143 if (pl) {
1144 pl->lower_region_to_bottom (shared_from_this());
1148 void
1149 Region::set_layer (layer_t l)
1151 if (_layer != l) {
1152 _layer = l;
1154 send_change (Properties::layer);
1158 XMLNode&
1159 Region::state ()
1161 XMLNode *node = new XMLNode ("Region");
1162 char buf[64];
1163 char buf2[64];
1164 LocaleGuard lg (X_("POSIX"));
1165 const char* fe = NULL;
1167 add_properties (*node);
1169 _id.print (buf, sizeof (buf));
1170 node->add_property ("id", buf);
1171 node->add_property ("type", _type.to_string());
1173 switch (_first_edit) {
1174 case EditChangesNothing:
1175 fe = X_("nothing");
1176 break;
1177 case EditChangesName:
1178 fe = X_("name");
1179 break;
1180 case EditChangesID:
1181 fe = X_("id");
1182 break;
1183 default: /* should be unreachable but makes g++ happy */
1184 fe = X_("nothing");
1185 break;
1188 node->add_property ("first-edit", fe);
1190 /* note: flags are stored by derived classes */
1192 if (_position_lock_style != AudioTime) {
1193 stringstream str;
1194 str << _bbt_time;
1195 node->add_property ("bbt-position", str.str());
1198 for (uint32_t n=0; n < _sources.size(); ++n) {
1199 snprintf (buf2, sizeof(buf2), "source-%d", n);
1200 _sources[n]->id().print (buf, sizeof(buf));
1201 node->add_property (buf2, buf);
1204 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1205 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1206 _master_sources[n]->id().print (buf, sizeof (buf));
1207 node->add_property (buf2, buf);
1210 /* Only store nested sources for the whole-file region that acts
1211 as the parent/root of all regions using it.
1214 if (_whole_file && max_source_level() > 0) {
1216 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1218 /* region is compound - get its playlist and
1219 store that before we list the region that
1220 needs it ...
1223 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1224 nested_node->add_child_nocopy ((*s)->get_state ());
1227 if (nested_node) {
1228 node->add_child_nocopy (*nested_node);
1232 if (_extra_xml) {
1233 node->add_child_copy (*_extra_xml);
1236 return *node;
1239 XMLNode&
1240 Region::get_state ()
1242 return state ();
1246 Region::set_state (const XMLNode& node, int version)
1248 PropertyChange what_changed;
1249 return _set_state (node, version, what_changed, true);
1253 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1255 const XMLProperty* prop;
1257 Stateful::save_extra_xml (node);
1259 what_changed = set_values (node);
1261 if ((prop = node.property (X_("id")))) {
1262 _id = prop->value();
1265 if (_position_lock_style == MusicTime) {
1266 if ((prop = node.property ("bbt-position")) == 0) {
1267 /* missing BBT info, revert to audio time locking */
1268 _position_lock_style = AudioTime;
1269 } else {
1270 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1271 &_bbt_time.bars,
1272 &_bbt_time.beats,
1273 &_bbt_time.ticks) != 3) {
1274 _position_lock_style = AudioTime;
1279 /* fix problems with old sessions corrupted by impossible
1280 values for _stretch or _shift
1282 if (_stretch == 0.0f) {
1283 _stretch = 1.0f;
1286 if (_shift == 0.0f) {
1287 _shift = 1.0f;
1290 if (send) {
1291 send_change (what_changed);
1294 /* Quick fix for 2.x sessions when region is muted */
1295 if ((prop = node.property (X_("flags")))) {
1296 if (string::npos != prop->value().find("Muted")){
1297 set_muted (true);
1302 return 0;
1305 void
1306 Region::suspend_property_changes ()
1308 Stateful::suspend_property_changes ();
1309 _last_length = _length;
1310 _last_position = _position;
1313 void
1314 Region::mid_thaw (const PropertyChange& what_changed)
1316 if (what_changed.contains (Properties::length)) {
1317 if (what_changed.contains (Properties::position)) {
1318 recompute_at_start ();
1320 recompute_at_end ();
1324 void
1325 Region::send_change (const PropertyChange& what_changed)
1327 if (what_changed.empty()) {
1328 return;
1331 Stateful::send_change (what_changed);
1333 if (!Stateful::frozen()) {
1335 /* Try and send a shared_pointer unless this is part of the constructor.
1336 If so, do nothing.
1339 try {
1340 boost::shared_ptr<Region> rptr = shared_from_this();
1341 RegionPropertyChanged (rptr, what_changed);
1342 } catch (...) {
1343 /* no shared_ptr available, relax; */
1348 void
1349 Region::set_last_layer_op (uint64_t when)
1351 _last_layer_op = when;
1354 bool
1355 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1357 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1360 bool
1361 Region::equivalent (boost::shared_ptr<const Region> other) const
1363 return _start == other->_start &&
1364 _position == other->_position &&
1365 _length == other->_length;
1368 bool
1369 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1371 return _start == other->_start &&
1372 _length == other->_length;
1375 bool
1376 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1378 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1381 void
1382 Region::source_deleted (boost::weak_ptr<Source>)
1384 drop_sources ();
1386 if (!_session.deletion_in_progress()) {
1387 /* this is a very special case: at least one of the region's
1388 sources has bee deleted, so invalidate all references to
1389 ourselves. Do NOT do this during session deletion, because
1390 then we run the risk that this will actually result
1391 in this object being deleted (as refcnt goes to zero)
1392 while emitting DropReferences.
1395 drop_references ();
1399 vector<string>
1400 Region::master_source_names ()
1402 SourceList::iterator i;
1404 vector<string> names;
1405 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1406 names.push_back((*i)->name());
1409 return names;
1412 void
1413 Region::set_master_sources (const SourceList& srcs)
1415 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1416 (*i)->dec_use_count ();
1419 _master_sources = srcs;
1420 assert (_sources.size() == _master_sources.size());
1422 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1423 (*i)->inc_use_count ();
1427 bool
1428 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1430 if (!other)
1431 return false;
1433 if ((_sources.size() != other->_sources.size()) ||
1434 (_master_sources.size() != other->_master_sources.size())) {
1435 return false;
1438 SourceList::const_iterator i;
1439 SourceList::const_iterator io;
1441 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1442 if ((*i)->id() != (*io)->id()) {
1443 return false;
1447 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1448 if ((*i)->id() != (*io)->id()) {
1449 return false;
1453 return true;
1456 std::string
1457 Region::source_string () const
1459 //string res = itos(_sources.size());
1461 stringstream res;
1462 res << _sources.size() << ":";
1464 SourceList::const_iterator i;
1466 for (i = _sources.begin(); i != _sources.end(); ++i) {
1467 res << (*i)->id() << ":";
1470 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1471 res << (*i)->id() << ":";
1474 return res.str();
1477 bool
1478 Region::uses_source (boost::shared_ptr<const Source> source) const
1480 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1481 if (*i == source) {
1482 return true;
1485 return false;
1488 bool
1489 Region::uses_source_path (const std::string& path) const
1491 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1492 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1493 if (fs) {
1494 if (fs->path() == path) {
1495 return true;
1499 return false;
1502 framecnt_t
1503 Region::source_length(uint32_t n) const
1505 assert (n < _sources.size());
1506 return _sources[n]->length (_position - _start);
1509 bool
1510 Region::verify_length (framecnt_t len)
1512 if (source() && (source()->destructive() || source()->length_mutable())) {
1513 return true;
1516 framecnt_t maxlen = 0;
1518 for (uint32_t n = 0; n < _sources.size(); ++n) {
1519 maxlen = max (maxlen, source_length(n) - _start);
1522 len = min (len, maxlen);
1524 return true;
1527 bool
1528 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1530 if (source() && (source()->destructive() || source()->length_mutable())) {
1531 return true;
1534 framecnt_t maxlen = 0;
1536 for (uint32_t n = 0; n < _sources.size(); ++n) {
1537 maxlen = max (maxlen, source_length(n) - new_start);
1540 new_length = min (new_length, maxlen);
1542 return true;
1545 bool
1546 Region::verify_start (framepos_t pos)
1548 if (source() && (source()->destructive() || source()->length_mutable())) {
1549 return true;
1552 for (uint32_t n = 0; n < _sources.size(); ++n) {
1553 if (pos > source_length(n) - _length) {
1554 return false;
1557 return true;
1560 bool
1561 Region::verify_start_mutable (framepos_t& new_start)
1563 if (source() && (source()->destructive() || source()->length_mutable())) {
1564 return true;
1567 for (uint32_t n = 0; n < _sources.size(); ++n) {
1568 if (new_start > source_length(n) - _length) {
1569 new_start = source_length(n) - _length;
1572 return true;
1575 boost::shared_ptr<Region>
1576 Region::get_parent() const
1578 boost::shared_ptr<Playlist> pl (playlist());
1580 if (pl) {
1581 boost::shared_ptr<Region> r;
1582 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1584 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1585 return boost::static_pointer_cast<Region> (r);
1589 return boost::shared_ptr<Region>();
1593 Region::apply (Filter& filter, Progress* progress)
1595 return filter.run (shared_from_this(), progress);
1599 void
1600 Region::invalidate_transients ()
1602 _valid_transients = false;
1603 _transients.clear ();
1605 send_change (PropertyChange (Properties::valid_transients));
1608 void
1609 Region::drop_sources ()
1611 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1612 (*i)->dec_use_count ();
1615 _sources.clear ();
1617 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1618 (*i)->dec_use_count ();
1621 _master_sources.clear ();
1624 void
1625 Region::use_sources (SourceList const & s)
1627 set<boost::shared_ptr<Source> > unique_srcs;
1629 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1631 _sources.push_back (*i);
1632 (*i)->inc_use_count ();
1633 _master_sources.push_back (*i);
1634 (*i)->inc_use_count ();
1636 /* connect only once to DropReferences, even if sources are replicated
1639 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1640 unique_srcs.insert (*i);
1641 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1646 Trimmable::CanTrim
1647 Region::can_trim () const
1649 CanTrim ct = CanTrim (0);
1651 if (locked()) {
1652 return ct;
1655 /* if not locked, we can always move the front later, and the end earlier
1658 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1660 if (start() != 0 || can_trim_start_before_source_start ()) {
1661 ct = CanTrim (ct | FrontTrimEarlier);
1664 if (!_sources.empty()) {
1665 if ((start() + length()) < _sources.front()->length (0)) {
1666 ct = CanTrim (ct | EndTrimLater);
1670 return ct;
1673 uint32_t
1674 Region::max_source_level () const
1676 uint32_t lvl = 0;
1678 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1679 lvl = max (lvl, (*i)->level());
1682 return lvl;
1685 bool
1686 Region::is_compound () const
1688 return max_source_level() > 0;