use -r argument with JACK if realtime is not requested in engine dialog (also applied...
[ArdourMidi.git] / libs / ardour / region.cc
blob6dc090ce3cc7b8588fedfe92f5adf62453f20ca0
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/region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/source.h"
36 #include "ardour/tempo.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/filter.h"
39 #include "ardour/profile.h"
40 #include "ardour/utils.h"
42 #include "i18n.h"
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
48 namespace ARDOUR {
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> muted;
51 PBD::PropertyDescriptor<bool> opaque;
52 PBD::PropertyDescriptor<bool> locked;
53 PBD::PropertyDescriptor<bool> automatic;
54 PBD::PropertyDescriptor<bool> whole_file;
55 PBD::PropertyDescriptor<bool> import;
56 PBD::PropertyDescriptor<bool> external;
57 PBD::PropertyDescriptor<bool> sync_marked;
58 PBD::PropertyDescriptor<bool> left_of_split;
59 PBD::PropertyDescriptor<bool> right_of_split;
60 PBD::PropertyDescriptor<bool> hidden;
61 PBD::PropertyDescriptor<bool> position_locked;
62 PBD::PropertyDescriptor<bool> valid_transients;
63 PBD::PropertyDescriptor<framepos_t> start;
64 PBD::PropertyDescriptor<framecnt_t> length;
65 PBD::PropertyDescriptor<framepos_t> position;
66 PBD::PropertyDescriptor<framecnt_t> sync_position;
67 PBD::PropertyDescriptor<layer_t> layer;
68 PBD::PropertyDescriptor<framepos_t> ancestral_start;
69 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
70 PBD::PropertyDescriptor<float> stretch;
71 PBD::PropertyDescriptor<float> shift;
72 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
78 void
79 Region::make_property_quarks ()
81 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
83 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
85 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
87 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
89 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
91 Properties::import.property_id = g_quark_from_static_string (X_("import"));
92 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
93 Properties::external.property_id = g_quark_from_static_string (X_("external"));
94 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
95 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
97 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
99 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
101 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
103 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
105 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
107 Properties::start.property_id = g_quark_from_static_string (X_("start"));
108 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
109 Properties::length.property_id = g_quark_from_static_string (X_("length"));
110 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
111 Properties::position.property_id = g_quark_from_static_string (X_("position"));
112 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
113 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
115 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
117 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
119 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
121 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
123 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
125 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
129 void
130 Region::register_properties ()
132 _xml_node_name = X_("Region");
134 add_property (_muted);
135 add_property (_opaque);
136 add_property (_locked);
137 add_property (_automatic);
138 add_property (_whole_file);
139 add_property (_import);
140 add_property (_external);
141 add_property (_sync_marked);
142 add_property (_left_of_split);
143 add_property (_right_of_split);
144 add_property (_hidden);
145 add_property (_position_locked);
146 add_property (_valid_transients);
147 add_property (_start);
148 add_property (_length);
149 add_property (_position);
150 add_property (_sync_position);
151 add_property (_layer);
152 add_property (_ancestral_start);
153 add_property (_ancestral_length);
154 add_property (_stretch);
155 add_property (_shift);
156 add_property (_position_lock_style);
159 #define REGION_DEFAULT_STATE(s,l) \
160 _muted (Properties::muted, false) \
161 , _opaque (Properties::opaque, true) \
162 , _locked (Properties::locked, false) \
163 , _automatic (Properties::automatic, false) \
164 , _whole_file (Properties::whole_file, false) \
165 , _import (Properties::import, false) \
166 , _external (Properties::external, false) \
167 , _sync_marked (Properties::sync_marked, false) \
168 , _left_of_split (Properties::left_of_split, false) \
169 , _right_of_split (Properties::right_of_split, false) \
170 , _hidden (Properties::hidden, false) \
171 , _position_locked (Properties::position_locked, false) \
172 , _valid_transients (Properties::valid_transients, false) \
173 , _start (Properties::start, (s)) \
174 , _length (Properties::length, (l)) \
175 , _position (Properties::position, 0) \
176 , _sync_position (Properties::sync_position, (s)) \
177 , _layer (Properties::layer, 0) \
178 , _ancestral_start (Properties::ancestral_start, (s)) \
179 , _ancestral_length (Properties::ancestral_length, (l)) \
180 , _stretch (Properties::stretch, 1.0) \
181 , _shift (Properties::shift, 1.0) \
182 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
184 #define REGION_COPY_STATE(other) \
185 _muted (other->_muted) \
186 , _opaque (other->_opaque) \
187 , _locked (other->_locked) \
188 , _automatic (other->_automatic) \
189 , _whole_file (other->_whole_file) \
190 , _import (other->_import) \
191 , _external (other->_external) \
192 , _sync_marked (other->_sync_marked) \
193 , _left_of_split (other->_left_of_split) \
194 , _right_of_split (other->_right_of_split) \
195 , _hidden (other->_hidden) \
196 , _position_locked (other->_position_locked) \
197 , _valid_transients (other->_valid_transients) \
198 , _start(other->_start) \
199 , _length(other->_length) \
200 , _position(other->_position) \
201 , _sync_position(other->_sync_position) \
202 , _layer (other->_layer) \
203 , _ancestral_start (other->_ancestral_start) \
204 , _ancestral_length (other->_ancestral_length) \
205 , _stretch (other->_stretch) \
206 , _shift (other->_shift) \
207 , _position_lock_style (other->_position_lock_style)
209 /* derived-from-derived constructor (no sources in constructor) */
210 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
211 : SessionObject(s, name)
212 , _type(type)
213 , REGION_DEFAULT_STATE(start,length)
214 , _last_length (length)
215 , _last_position (0)
216 , _first_edit (EditChangesNothing)
217 , _read_data_count(0)
218 , _last_layer_op(0)
219 , _pending_explicit_relayer (false)
221 register_properties ();
223 /* no sources at this point */
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228 : SessionObject(srcs.front()->session(), "toBeRenamed")
229 , _type (srcs.front()->type())
230 , REGION_DEFAULT_STATE(0,0)
231 , _last_length (0)
232 , _last_position (0)
233 , _first_edit (EditChangesNothing)
234 , _read_data_count(0)
235 , _last_layer_op (0)
236 , _pending_explicit_relayer (false)
238 register_properties ();
240 _type = srcs.front()->type();
242 use_sources (srcs);
244 assert(_sources.size() > 0);
245 assert (_type == srcs.front()->type());
248 /** Create a new Region from part of an existing one, starting at one of two places:
250 if @param offset_relative is true, then the start within @param other is given by @param offset
251 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
253 if @param offset_relative is false, then the start within the source is given @param offset.
255 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
256 : SessionObject(other->session(), other->name())
257 , _type (other->data_type())
258 , REGION_COPY_STATE (other)
259 , _last_length (other->_last_length)
260 , _last_position(other->_last_position) \
261 , _first_edit (EditChangesNothing)
262 , _read_data_count(0)
263 , _last_layer_op (0)
264 , _pending_explicit_relayer (false)
267 register_properties ();
269 /* override state that may have been incorrectly inherited from the other region
272 _position = 0;
273 _locked = false;
274 _whole_file = false;
275 _hidden = false;
277 use_sources (other->_sources);
279 if (!offset_relative) {
281 /* not sure why we do this, but its a hangover from ardour before
282 property lists. this would be nice to remove.
285 _position_lock_style = other->_position_lock_style;
286 _first_edit = other->_first_edit;
288 if (offset == 0) {
290 _start = 0;
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;
307 } else {
308 /* sync pos was before the start point of the other region. not possible here. */
309 _sync_marked = false;
310 _sync_position = _start;
312 } else {
313 _sync_marked = false;
314 _sync_position = _start;
316 } else {
317 /* XXX do something else ! */
318 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
319 << endmsg;
320 /*NOTREACHED*/
323 } else {
325 _start = other->_start + offset;
327 /* if the other region had a distinct sync point
328 set, then continue to use it as best we can.
329 otherwise, reset sync point back to start.
332 if (other->sync_marked()) {
333 if (other->_sync_position < _start) {
334 _sync_marked = false;
335 _sync_position = _start;
336 } else {
337 _sync_position = other->_sync_position;
339 } else {
340 _sync_marked = false;
341 _sync_position = _start;
345 if (Profile->get_sae()) {
346 /* reset sync point to start if its ended up
347 outside region bounds.
350 if (_sync_position < _start || _sync_position >= _start + _length) {
351 _sync_marked = false;
352 _sync_position = _start;
356 assert (_type == other->data_type());
359 /** Create a copy of @param other but with different sources. Used by filters */
360 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
361 : SessionObject (other->session(), other->name())
362 , _type (srcs.front()->type())
363 , REGION_COPY_STATE (other)
364 , _last_length (other->_last_length)
365 , _last_position (other->_last_position)
366 , _first_edit (EditChangesID)
367 , _read_data_count (0)
368 , _last_layer_op (other->_last_layer_op)
369 , _pending_explicit_relayer (false)
371 register_properties ();
373 _locked = false;
374 _position_locked = false;
376 other->_first_edit = EditChangesName;
378 if (other->_extra_xml) {
379 _extra_xml = new XMLNode (*other->_extra_xml);
380 } else {
381 _extra_xml = 0;
384 use_sources (srcs);
385 assert(_sources.size() > 0);
388 /** Simple "copy" constructor */
389 Region::Region (boost::shared_ptr<const Region> other)
390 : SessionObject(other->session(), other->name())
391 , _type(other->data_type())
392 , REGION_COPY_STATE (other)
393 , _last_length (other->_last_length)
394 , _last_position (other->_last_position)
395 , _first_edit (EditChangesID)
396 , _read_data_count(0)
397 , _last_layer_op(other->_last_layer_op)
398 , _pending_explicit_relayer (false)
400 register_properties ();
402 _locked = false;
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
409 } else {
410 _extra_xml = 0;
413 use_sources (other->_sources);
414 assert(_sources.size() > 0);
417 Region::~Region ()
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
420 drop_sources ();
423 void
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
429 bool
430 Region::set_name (const std::string& str)
432 if (_name != str) {
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
435 send_change (Properties::name);
438 return true;
441 void
442 Region::set_length (framecnt_t len, void */*src*/)
444 //cerr << "Region::set_length() len = " << len << endl;
445 if (locked()) {
446 return;
449 if (_length != len && len != 0) {
451 /* check that the current _position wouldn't make the new
452 length impossible.
455 if (max_frames - len < _position) {
456 return;
459 if (!verify_length (len)) {
460 return;
464 _last_length = _length;
465 _length = len;
466 _whole_file = false;
467 first_edit ();
468 maybe_uncopy ();
469 invalidate_transients ();
471 if (!property_changes_suspended()) {
472 recompute_at_end ();
475 send_change (Properties::length);
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);
496 RegionFactory::CheckNewRegion (shared_from_this());
500 bool
501 Region::at_natural_position () const
503 boost::shared_ptr<Playlist> pl (playlist());
505 if (!pl) {
506 return false;
509 boost::shared_ptr<Region> whole_file_region = get_parent();
511 if (whole_file_region) {
512 if (_position == whole_file_region->position() + _start) {
513 return true;
517 return false;
520 void
521 Region::move_to_natural_position (void *src)
523 boost::shared_ptr<Playlist> pl (playlist());
525 if (!pl) {
526 return;
529 boost::shared_ptr<Region> whole_file_region = get_parent();
531 if (whole_file_region) {
532 set_position (whole_file_region->position() + _start, src);
536 void
537 Region::special_set_position (framepos_t pos)
539 /* this is used when creating a whole file region as
540 a way to store its "natural" or "captured" position.
543 _position = _position;
544 _position = pos;
547 void
548 Region::set_position_lock_style (PositionLockStyle ps)
550 if (_position_lock_style != ps) {
552 boost::shared_ptr<Playlist> pl (playlist());
554 if (!pl) {
555 return;
558 _position_lock_style = ps;
560 if (_position_lock_style == MusicTime) {
561 _session.tempo_map().bbt_time (_position, _bbt_time);
564 send_change (Properties::position_lock_style);
568 void
569 Region::update_position_after_tempo_map_change ()
571 boost::shared_ptr<Playlist> pl (playlist());
573 if (!pl || _position_lock_style != MusicTime) {
574 return;
577 TempoMap& map (_session.tempo_map());
578 framepos_t pos = map.frame_time (_bbt_time);
579 set_position_internal (pos, false);
582 void
583 Region::set_position (framepos_t pos, void* /*src*/)
585 if (!can_move()) {
586 return;
589 set_position_internal (pos, true);
592 void
593 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
595 if (_position != pos) {
596 _last_position = _position;
597 _position = pos;
599 /* check that the new _position wouldn't make the current
600 length impossible - if so, change the length.
602 XXX is this the right thing to do?
605 if (max_frames - _length < _position) {
606 _last_length = _length;
607 _length = max_frames - _position;
610 if (allow_bbt_recompute) {
611 recompute_position_from_lock_style ();
614 //invalidate_transients ();
617 /* do this even if the position is the same. this helps out
618 a GUI that has moved its representation already.
620 send_change (Properties::position);
623 void
624 Region::set_position_on_top (framepos_t pos, void* /*src*/)
626 if (locked()) {
627 return;
630 if (_position != pos) {
631 _last_position = _position;
632 _position = pos;
635 boost::shared_ptr<Playlist> pl (playlist());
637 if (pl) {
638 pl->raise_region_to_top (shared_from_this ());
641 /* do this even if the position is the same. this helps out
642 a GUI that has moved its representation already.
645 send_change (Properties::position);
648 void
649 Region::recompute_position_from_lock_style ()
651 if (_position_lock_style == MusicTime) {
652 _session.tempo_map().bbt_time (_position, _bbt_time);
656 void
657 Region::nudge_position (frameoffset_t n, void* /*src*/)
659 if (locked()) {
660 return;
663 if (n == 0) {
664 return;
667 _last_position = _position;
669 if (n > 0) {
670 if (_position > max_frames - n) {
671 _position = max_frames;
672 } else {
673 _position += n;
675 } else {
676 if (_position < -n) {
677 _position = 0;
678 } else {
679 _position += n;
683 send_change (Properties::position);
686 void
687 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
689 _ancestral_length = l;
690 _ancestral_start = s;
691 _stretch = st;
692 _shift = sh;
695 void
696 Region::set_start (framepos_t pos, void* /*src*/)
698 if (locked() || position_locked()) {
699 return;
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
706 if (_start != pos) {
708 if (!verify_start (pos)) {
709 return;
712 _start = pos;
713 _whole_file = false;
714 first_edit ();
715 invalidate_transients ();
717 send_change (Properties::start);
721 void
722 Region::trim_start (framepos_t new_position, void */*src*/)
724 if (locked() || position_locked()) {
725 return;
727 framepos_t new_start;
728 frameoffset_t start_shift;
730 if (new_position > _position) {
731 start_shift = new_position - _position;
732 } else {
733 start_shift = -(_position - new_position);
736 if (start_shift > 0) {
738 if (_start > max_frames - start_shift) {
739 new_start = max_frames;
740 } else {
741 new_start = _start + start_shift;
744 if (!verify_start (new_start)) {
745 return;
748 } else if (start_shift < 0) {
750 if (_start < -start_shift) {
751 new_start = 0;
752 } else {
753 new_start = _start + start_shift;
755 } else {
756 return;
759 if (new_start == _start) {
760 return;
763 _start = new_start;
764 _whole_file = false;
765 first_edit ();
767 send_change (Properties::start);
770 void
771 Region::trim_front (framepos_t new_position, void *src)
773 modify_front (new_position, false, src);
776 void
777 Region::cut_front (nframes_t new_position, void *src)
779 modify_front (new_position, true, src);
782 void
783 Region::cut_end (nframes_t new_endpoint, void *src)
785 modify_end (new_endpoint, true, src);
788 void
789 Region::modify_front (nframes_t new_position, bool reset_fade, void *src)
791 if (locked()) {
792 return;
795 nframes_t end = last_frame();
796 nframes_t source_zero;
798 if (_position > _start) {
799 source_zero = _position - _start;
800 } else {
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 nframes_t newlen = 0;
807 nframes64_t delta = 0;
809 /* can't trim it back passed where source position zero is located */
811 new_position = max (new_position, source_zero);
813 if (new_position > _position) {
814 newlen = _length - (new_position - _position);
815 delta = -1 * (new_position - _position);
816 } else {
817 newlen = _length + (_position - new_position);
818 delta = _position - new_position;
821 trim_to_internal (new_position, newlen, src);
823 if (reset_fade) {
824 _right_of_split = true;
827 if (!property_changes_suspended()) {
828 recompute_at_start ();
831 if (_transients.size() > 0){
832 adjust_transients(delta);
837 void
838 Region::modify_end (nframes_t new_endpoint, bool reset_fade, void *src)
840 if (locked()) {
841 return;
844 if (new_endpoint > _position) {
845 trim_to_internal (_position, new_endpoint - _position +1, this);
846 if (reset_fade) {
847 _left_of_split = true;
849 if (!property_changes_suspended()) {
850 recompute_at_end ();
855 /** @param new_endpoint New region end point, such that, for example,
856 * a region at 0 of length 10 has an endpoint of 9.
859 void
860 Region::trim_end (framepos_t new_endpoint, void* src)
862 modify_end (new_endpoint, false, src);
865 void
866 Region::trim_to (framepos_t position, framecnt_t length, void *src)
868 if (locked()) {
869 return;
872 trim_to_internal (position, length, src);
874 if (!property_changes_suspended()) {
875 recompute_at_start ();
876 recompute_at_end ();
880 void
881 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
883 frameoffset_t start_shift;
884 framepos_t new_start;
886 if (locked()) {
887 return;
890 if (position > _position) {
891 start_shift = position - _position;
892 } else {
893 start_shift = -(_position - position);
896 if (start_shift > 0) {
898 if (_start > max_frames - start_shift) {
899 new_start = max_frames;
900 } else {
901 new_start = _start + start_shift;
904 } else if (start_shift < 0) {
906 if (_start < -start_shift) {
907 new_start = 0;
908 } else {
909 new_start = _start + start_shift;
912 } else {
913 new_start = _start;
916 if (!verify_start_and_length (new_start, length)) {
917 return;
920 PropertyChange what_changed;
922 if (_start != new_start) {
923 _start = new_start;
924 what_changed.add (Properties::start);
926 if (_length != length) {
927 if (!property_changes_suspended()) {
928 _last_length = _length;
930 _length = length;
931 what_changed.add (Properties::length);
933 if (_position != position) {
934 if (!property_changes_suspended()) {
935 _last_position = _position;
937 _position = position;
938 what_changed.add (Properties::position);
941 _whole_file = false;
943 PropertyChange start_and_length;
945 start_and_length.add (Properties::start);
946 start_and_length.add (Properties::length);
948 if (what_changed.contains (start_and_length)) {
949 first_edit ();
952 if (!what_changed.empty()) {
953 send_change (what_changed);
957 void
958 Region::set_hidden (bool yn)
960 if (hidden() != yn) {
961 _hidden = yn;
962 send_change (Properties::hidden);
966 void
967 Region::set_whole_file (bool yn)
969 _whole_file = yn;
970 /* no change signal */
973 void
974 Region::set_automatic (bool yn)
976 _automatic = yn;
977 /* no change signal */
980 void
981 Region::set_muted (bool yn)
983 if (muted() != yn) {
984 _muted = yn;
985 send_change (Properties::muted);
989 void
990 Region::set_opaque (bool yn)
992 if (opaque() != yn) {
993 _opaque = yn;
994 send_change (Properties::opaque);
998 void
999 Region::set_locked (bool yn)
1001 if (locked() != yn) {
1002 _locked = yn;
1003 send_change (Properties::locked);
1007 void
1008 Region::set_position_locked (bool yn)
1010 if (position_locked() != yn) {
1011 _position_locked = yn;
1012 send_change (Properties::locked);
1016 void
1017 Region::set_sync_position (framepos_t absolute_pos)
1019 framepos_t const file_pos = _start + (absolute_pos - _position);
1021 if (file_pos != _sync_position) {
1022 _sync_marked = true;
1023 _sync_position = file_pos;
1024 if (!property_changes_suspended()) {
1025 maybe_uncopy ();
1027 send_change (Properties::sync_position);
1031 void
1032 Region::clear_sync_position ()
1034 if (sync_marked()) {
1035 _sync_marked = false;
1036 if (!property_changes_suspended()) {
1037 maybe_uncopy ();
1039 send_change (Properties::sync_position);
1043 framepos_t
1044 Region::sync_offset (int& dir) const
1046 /* returns the sync point relative the first frame of the region */
1048 if (sync_marked()) {
1049 if (_sync_position > _start) {
1050 dir = 1;
1051 return _sync_position - _start;
1052 } else {
1053 dir = -1;
1054 return _start - _sync_position;
1056 } else {
1057 dir = 0;
1058 return 0;
1062 framepos_t
1063 Region::adjust_to_sync (framepos_t pos) const
1065 int sync_dir;
1066 frameoffset_t offset = sync_offset (sync_dir);
1068 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1070 if (sync_dir > 0) {
1071 if (pos > offset) {
1072 pos -= offset;
1073 } else {
1074 pos = 0;
1076 } else {
1077 if (max_frames - pos > offset) {
1078 pos += offset;
1082 return pos;
1085 framepos_t
1086 Region::sync_position() const
1088 if (sync_marked()) {
1089 return _sync_position;
1090 } else {
1091 return _start;
1095 void
1096 Region::raise ()
1098 boost::shared_ptr<Playlist> pl (playlist());
1099 if (pl) {
1100 pl->raise_region (shared_from_this ());
1104 void
1105 Region::lower ()
1107 boost::shared_ptr<Playlist> pl (playlist());
1108 if (pl) {
1109 pl->lower_region (shared_from_this ());
1114 void
1115 Region::raise_to_top ()
1117 boost::shared_ptr<Playlist> pl (playlist());
1118 if (pl) {
1119 pl->raise_region_to_top (shared_from_this());
1123 void
1124 Region::lower_to_bottom ()
1126 boost::shared_ptr<Playlist> pl (playlist());
1127 if (pl) {
1128 pl->lower_region_to_bottom (shared_from_this());
1132 void
1133 Region::set_layer (layer_t l)
1135 if (_layer != l) {
1136 _layer = l;
1138 send_change (Properties::layer);
1142 XMLNode&
1143 Region::state ()
1145 XMLNode *node = new XMLNode ("Region");
1146 char buf[64];
1147 char buf2[64];
1148 LocaleGuard lg (X_("POSIX"));
1149 const char* fe = NULL;
1151 add_properties (*node);
1153 _id.print (buf, sizeof (buf));
1154 node->add_property ("id", buf);
1155 node->add_property ("type", _type.to_string());
1157 switch (_first_edit) {
1158 case EditChangesNothing:
1159 fe = X_("nothing");
1160 break;
1161 case EditChangesName:
1162 fe = X_("name");
1163 break;
1164 case EditChangesID:
1165 fe = X_("id");
1166 break;
1167 default: /* should be unreachable but makes g++ happy */
1168 fe = X_("nothing");
1169 break;
1172 node->add_property ("first-edit", fe);
1174 /* note: flags are stored by derived classes */
1176 if (_position_lock_style != AudioTime) {
1177 stringstream str;
1178 str << _bbt_time;
1179 node->add_property ("bbt-position", str.str());
1182 for (uint32_t n=0; n < _sources.size(); ++n) {
1183 snprintf (buf2, sizeof(buf2), "source-%d", n);
1184 _sources[n]->id().print (buf, sizeof(buf));
1185 node->add_property (buf2, buf);
1188 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1189 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1190 _master_sources[n]->id().print (buf, sizeof (buf));
1191 node->add_property (buf2, buf);
1194 if (_extra_xml) {
1195 node->add_child_copy (*_extra_xml);
1198 return *node;
1201 XMLNode&
1202 Region::get_state ()
1204 return state ();
1208 Region::set_state (const XMLNode& node, int version)
1210 PropertyChange what_changed;
1211 return _set_state (node, version, what_changed, true);
1215 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1217 const XMLProperty* prop;
1219 what_changed = set_properties (node);
1221 if ((prop = node.property (X_("id")))) {
1222 _id = prop->value();
1225 if (_position_lock_style == MusicTime) {
1226 if ((prop = node.property ("bbt-position")) == 0) {
1227 /* missing BBT info, revert to audio time locking */
1228 _position_lock_style = AudioTime;
1229 } else {
1230 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1231 &_bbt_time.bars,
1232 &_bbt_time.beats,
1233 &_bbt_time.ticks) != 3) {
1234 _position_lock_style = AudioTime;
1239 /* fix problems with old sessions corrupted by impossible
1240 values for _stretch or _shift
1242 if (_stretch == 0.0f) {
1243 _stretch = 1.0f;
1246 if (_shift == 0.0f) {
1247 _shift = 1.0f;
1250 const XMLNodeList& nlist = node.children();
1252 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1254 XMLNode *child;
1256 child = (*niter);
1258 if (child->name () == "Extra") {
1259 delete _extra_xml;
1260 _extra_xml = new XMLNode (*child);
1261 break;
1265 if (send) {
1266 send_change (what_changed);
1269 /* Quick fix for 2.x sessions when region is muted */
1270 if ((prop = node.property (X_("flags")))) {
1271 if (string::npos != prop->value().find("Muted")){
1272 set_muted (true);
1277 return 0;
1280 void
1281 Region::suspend_property_changes ()
1283 Stateful::suspend_property_changes ();
1284 _last_length = _length;
1285 _last_position = _position;
1288 void
1289 Region::mid_thaw (const PropertyChange& what_changed)
1291 if (what_changed.contains (Properties::length)) {
1292 if (what_changed.contains (Properties::position)) {
1293 recompute_at_start ();
1295 recompute_at_end ();
1299 void
1300 Region::send_change (const PropertyChange& what_changed)
1302 if (what_changed.empty()) {
1303 return;
1306 Stateful::send_change (what_changed);
1308 if (!_no_property_changes) {
1310 /* Try and send a shared_pointer unless this is part of the constructor.
1311 If so, do nothing.
1314 try {
1315 boost::shared_ptr<Region> rptr = shared_from_this();
1316 RegionPropertyChanged (rptr, what_changed);
1317 } catch (...) {
1318 /* no shared_ptr available, relax; */
1323 void
1324 Region::set_last_layer_op (uint64_t when)
1326 _last_layer_op = when;
1329 bool
1330 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1332 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1335 bool
1336 Region::equivalent (boost::shared_ptr<const Region> other) const
1338 return _start == other->_start &&
1339 _position == other->_position &&
1340 _length == other->_length;
1343 bool
1344 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1346 return _start == other->_start &&
1347 _length == other->_length;
1350 bool
1351 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1353 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1356 void
1357 Region::source_deleted (boost::weak_ptr<Source>)
1359 drop_sources ();
1361 if (!_session.deletion_in_progress()) {
1362 /* this is a very special case: at least one of the region's
1363 sources has bee deleted, so invalidate all references to
1364 ourselves. Do NOT do this during session deletion, because
1365 then we run the risk that this will actually result
1366 in this object being deleted (as refcnt goes to zero)
1367 while emitting DropReferences.
1370 drop_references ();
1374 vector<string>
1375 Region::master_source_names ()
1377 SourceList::iterator i;
1379 vector<string> names;
1380 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1381 names.push_back((*i)->name());
1384 return names;
1387 void
1388 Region::set_master_sources (const SourceList& srcs)
1390 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1391 (*i)->dec_use_count ();
1394 _master_sources = srcs;
1395 assert (_sources.size() == _master_sources.size());
1397 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1398 (*i)->inc_use_count ();
1402 bool
1403 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1405 if (!other)
1406 return false;
1408 SourceList::const_iterator i;
1409 SourceList::const_iterator io;
1411 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1412 if ((*i)->id() != (*io)->id()) {
1413 return false;
1417 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1418 if ((*i)->id() != (*io)->id()) {
1419 return false;
1423 return true;
1426 bool
1427 Region::uses_source (boost::shared_ptr<const Source> source) const
1429 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1430 if (*i == source) {
1431 return true;
1434 return false;
1437 sframes_t
1438 Region::source_length(uint32_t n) const
1440 assert (n < _sources.size());
1441 return _sources[n]->length(_position - _start);
1444 bool
1445 Region::verify_length (framecnt_t len)
1447 if (source() && (source()->destructive() || source()->length_mutable())) {
1448 return true;
1451 framecnt_t maxlen = 0;
1453 for (uint32_t n = 0; n < _sources.size(); ++n) {
1454 maxlen = max (maxlen, source_length(n) - _start);
1457 len = min (len, maxlen);
1459 return true;
1462 bool
1463 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1465 if (source() && (source()->destructive() || source()->length_mutable())) {
1466 return true;
1469 framecnt_t maxlen = 0;
1471 for (uint32_t n = 0; n < _sources.size(); ++n) {
1472 maxlen = max (maxlen, source_length(n) - new_start);
1475 new_length = min (new_length, maxlen);
1477 return true;
1480 bool
1481 Region::verify_start (framepos_t pos)
1483 if (source() && (source()->destructive() || source()->length_mutable())) {
1484 return true;
1487 for (uint32_t n = 0; n < _sources.size(); ++n) {
1488 if (pos > source_length(n) - _length) {
1489 return false;
1492 return true;
1495 bool
1496 Region::verify_start_mutable (framepos_t& new_start)
1498 if (source() && (source()->destructive() || source()->length_mutable())) {
1499 return true;
1502 for (uint32_t n = 0; n < _sources.size(); ++n) {
1503 if (new_start > source_length(n) - _length) {
1504 new_start = source_length(n) - _length;
1507 return true;
1510 boost::shared_ptr<Region>
1511 Region::get_parent() const
1513 boost::shared_ptr<Playlist> pl (playlist());
1515 if (pl) {
1516 boost::shared_ptr<Region> r;
1517 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1519 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1520 return boost::static_pointer_cast<Region> (r);
1524 return boost::shared_ptr<Region>();
1528 Region::apply (Filter& filter)
1530 return filter.run (shared_from_this());
1534 void
1535 Region::invalidate_transients ()
1537 _valid_transients = false;
1538 _transients.clear ();
1540 send_change (PropertyChange (Properties::valid_transients));
1543 void
1544 Region::drop_sources ()
1546 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1547 (*i)->dec_use_count ();
1550 _sources.clear ();
1552 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1553 (*i)->dec_use_count ();
1556 _master_sources.clear ();
1559 void
1560 Region::use_sources (SourceList const & s)
1562 set<boost::shared_ptr<Source> > unique_srcs;
1564 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1566 _sources.push_back (*i);
1567 (*i)->inc_use_count ();
1568 _master_sources.push_back (*i);
1569 (*i)->inc_use_count ();
1571 /* connect only once to DropReferences, even if sources are replicated
1574 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1575 unique_srcs.insert (*i);
1576 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1581 PropertyList*
1582 Region::property_factory (const XMLNode& history_node) const
1584 PropertyList* prop_list = new PropertyList;
1586 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
1587 PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node);
1589 if (prop) {
1590 prop_list->add (prop);
1594 return prop_list;