much ado about nothing when it comes to gain control
[ardour2.git] / libs / ardour / region.cc
blob8a95f22f8dda0d295d7865ff5209c02b2a2bfdc5
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 if (max_source_level() > 0) {
1212 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1214 /* region is compound - get its playlist and
1215 store that before we list the region that
1216 needs it ...
1219 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1220 nested_node->add_child_nocopy ((*s)->get_state ());
1223 if (nested_node) {
1224 node->add_child_nocopy (*nested_node);
1228 if (_extra_xml) {
1229 node->add_child_copy (*_extra_xml);
1232 return *node;
1235 XMLNode&
1236 Region::get_state ()
1238 return state ();
1242 Region::set_state (const XMLNode& node, int version)
1244 PropertyChange what_changed;
1245 return _set_state (node, version, what_changed, true);
1249 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1251 const XMLProperty* prop;
1253 Stateful::save_extra_xml (node);
1255 what_changed = set_values (node);
1257 if ((prop = node.property (X_("id")))) {
1258 _id = prop->value();
1261 if (_position_lock_style == MusicTime) {
1262 if ((prop = node.property ("bbt-position")) == 0) {
1263 /* missing BBT info, revert to audio time locking */
1264 _position_lock_style = AudioTime;
1265 } else {
1266 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1267 &_bbt_time.bars,
1268 &_bbt_time.beats,
1269 &_bbt_time.ticks) != 3) {
1270 _position_lock_style = AudioTime;
1275 /* fix problems with old sessions corrupted by impossible
1276 values for _stretch or _shift
1278 if (_stretch == 0.0f) {
1279 _stretch = 1.0f;
1282 if (_shift == 0.0f) {
1283 _shift = 1.0f;
1286 if (send) {
1287 send_change (what_changed);
1290 /* Quick fix for 2.x sessions when region is muted */
1291 if ((prop = node.property (X_("flags")))) {
1292 if (string::npos != prop->value().find("Muted")){
1293 set_muted (true);
1298 return 0;
1301 void
1302 Region::suspend_property_changes ()
1304 Stateful::suspend_property_changes ();
1305 _last_length = _length;
1306 _last_position = _position;
1309 void
1310 Region::mid_thaw (const PropertyChange& what_changed)
1312 if (what_changed.contains (Properties::length)) {
1313 if (what_changed.contains (Properties::position)) {
1314 recompute_at_start ();
1316 recompute_at_end ();
1320 void
1321 Region::send_change (const PropertyChange& what_changed)
1323 if (what_changed.empty()) {
1324 return;
1327 Stateful::send_change (what_changed);
1329 if (!Stateful::frozen()) {
1331 /* Try and send a shared_pointer unless this is part of the constructor.
1332 If so, do nothing.
1335 try {
1336 boost::shared_ptr<Region> rptr = shared_from_this();
1337 RegionPropertyChanged (rptr, what_changed);
1338 } catch (...) {
1339 /* no shared_ptr available, relax; */
1344 void
1345 Region::set_last_layer_op (uint64_t when)
1347 _last_layer_op = when;
1350 bool
1351 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1353 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1356 bool
1357 Region::equivalent (boost::shared_ptr<const Region> other) const
1359 return _start == other->_start &&
1360 _position == other->_position &&
1361 _length == other->_length;
1364 bool
1365 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1367 return _start == other->_start &&
1368 _length == other->_length;
1371 bool
1372 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1374 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1377 void
1378 Region::source_deleted (boost::weak_ptr<Source>)
1380 drop_sources ();
1382 if (!_session.deletion_in_progress()) {
1383 /* this is a very special case: at least one of the region's
1384 sources has bee deleted, so invalidate all references to
1385 ourselves. Do NOT do this during session deletion, because
1386 then we run the risk that this will actually result
1387 in this object being deleted (as refcnt goes to zero)
1388 while emitting DropReferences.
1391 drop_references ();
1395 vector<string>
1396 Region::master_source_names ()
1398 SourceList::iterator i;
1400 vector<string> names;
1401 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1402 names.push_back((*i)->name());
1405 return names;
1408 void
1409 Region::set_master_sources (const SourceList& srcs)
1411 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1412 (*i)->dec_use_count ();
1415 _master_sources = srcs;
1416 assert (_sources.size() == _master_sources.size());
1418 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1419 (*i)->inc_use_count ();
1423 bool
1424 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1426 if (!other)
1427 return false;
1429 if ((_sources.size() != other->_sources.size()) ||
1430 (_master_sources.size() != other->_master_sources.size())) {
1431 return false;
1434 SourceList::const_iterator i;
1435 SourceList::const_iterator io;
1437 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1438 if ((*i)->id() != (*io)->id()) {
1439 return false;
1443 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1444 if ((*i)->id() != (*io)->id()) {
1445 return false;
1449 return true;
1452 std::string
1453 Region::source_string () const
1455 //string res = itos(_sources.size());
1457 stringstream res;
1458 res << _sources.size() << ":";
1460 SourceList::const_iterator i;
1462 for (i = _sources.begin(); i != _sources.end(); ++i) {
1463 res << (*i)->id() << ":";
1466 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1467 res << (*i)->id() << ":";
1470 return res.str();
1473 bool
1474 Region::uses_source (boost::shared_ptr<const Source> source) const
1476 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1477 if (*i == source) {
1478 return true;
1481 return false;
1484 bool
1485 Region::uses_source_path (const std::string& path) const
1487 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1488 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1489 if (fs) {
1490 if (fs->path() == path) {
1491 return true;
1495 return false;
1498 framecnt_t
1499 Region::source_length(uint32_t n) const
1501 assert (n < _sources.size());
1502 return _sources[n]->length (_position - _start);
1505 bool
1506 Region::verify_length (framecnt_t len)
1508 if (source() && (source()->destructive() || source()->length_mutable())) {
1509 return true;
1512 framecnt_t maxlen = 0;
1514 for (uint32_t n = 0; n < _sources.size(); ++n) {
1515 maxlen = max (maxlen, source_length(n) - _start);
1518 len = min (len, maxlen);
1520 return true;
1523 bool
1524 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1526 if (source() && (source()->destructive() || source()->length_mutable())) {
1527 return true;
1530 framecnt_t maxlen = 0;
1532 for (uint32_t n = 0; n < _sources.size(); ++n) {
1533 maxlen = max (maxlen, source_length(n) - new_start);
1536 new_length = min (new_length, maxlen);
1538 return true;
1541 bool
1542 Region::verify_start (framepos_t pos)
1544 if (source() && (source()->destructive() || source()->length_mutable())) {
1545 return true;
1548 for (uint32_t n = 0; n < _sources.size(); ++n) {
1549 if (pos > source_length(n) - _length) {
1550 return false;
1553 return true;
1556 bool
1557 Region::verify_start_mutable (framepos_t& new_start)
1559 if (source() && (source()->destructive() || source()->length_mutable())) {
1560 return true;
1563 for (uint32_t n = 0; n < _sources.size(); ++n) {
1564 if (new_start > source_length(n) - _length) {
1565 new_start = source_length(n) - _length;
1568 return true;
1571 boost::shared_ptr<Region>
1572 Region::get_parent() const
1574 boost::shared_ptr<Playlist> pl (playlist());
1576 if (pl) {
1577 boost::shared_ptr<Region> r;
1578 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1580 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1581 return boost::static_pointer_cast<Region> (r);
1585 return boost::shared_ptr<Region>();
1589 Region::apply (Filter& filter, Progress* progress)
1591 return filter.run (shared_from_this(), progress);
1595 void
1596 Region::invalidate_transients ()
1598 _valid_transients = false;
1599 _transients.clear ();
1601 send_change (PropertyChange (Properties::valid_transients));
1604 void
1605 Region::drop_sources ()
1607 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1608 (*i)->dec_use_count ();
1611 _sources.clear ();
1613 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1614 (*i)->dec_use_count ();
1617 _master_sources.clear ();
1620 void
1621 Region::use_sources (SourceList const & s)
1623 set<boost::shared_ptr<Source> > unique_srcs;
1625 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1627 _sources.push_back (*i);
1628 (*i)->inc_use_count ();
1629 _master_sources.push_back (*i);
1630 (*i)->inc_use_count ();
1632 /* connect only once to DropReferences, even if sources are replicated
1635 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1636 unique_srcs.insert (*i);
1637 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1642 Trimmable::CanTrim
1643 Region::can_trim () const
1645 CanTrim ct = CanTrim (0);
1647 if (locked()) {
1648 return ct;
1651 /* if not locked, we can always move the front later, and the end earlier
1654 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1656 if (start() != 0 || can_trim_start_before_source_start ()) {
1657 ct = CanTrim (ct | FrontTrimEarlier);
1660 if (!_sources.empty()) {
1661 if ((start() + length()) < _sources.front()->length (0)) {
1662 ct = CanTrim (ct | EndTrimLater);
1666 return ct;
1669 uint32_t
1670 Region::max_source_level () const
1672 uint32_t lvl = 0;
1674 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1675 lvl = max (lvl, (*i)->level());
1678 return lvl;
1681 bool
1682 Region::is_compound () const
1684 return max_source_level() > 0;