various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / libs / ardour / region.cc
blob2ce823777b2e308801f38208ca1ff8ae224149e6
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/playlist_source.h"
36 #include "ardour/profile.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
40 #include "ardour/source.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/utils.h"
45 #include "i18n.h"
47 using namespace std;
48 using namespace ARDOUR;
49 using namespace PBD;
51 namespace ARDOUR {
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> muted;
54 PBD::PropertyDescriptor<bool> opaque;
55 PBD::PropertyDescriptor<bool> locked;
56 PBD::PropertyDescriptor<bool> automatic;
57 PBD::PropertyDescriptor<bool> whole_file;
58 PBD::PropertyDescriptor<bool> import;
59 PBD::PropertyDescriptor<bool> external;
60 PBD::PropertyDescriptor<bool> sync_marked;
61 PBD::PropertyDescriptor<bool> left_of_split;
62 PBD::PropertyDescriptor<bool> right_of_split;
63 PBD::PropertyDescriptor<bool> hidden;
64 PBD::PropertyDescriptor<bool> position_locked;
65 PBD::PropertyDescriptor<bool> valid_transients;
66 PBD::PropertyDescriptor<framepos_t> start;
67 PBD::PropertyDescriptor<framecnt_t> length;
68 PBD::PropertyDescriptor<framepos_t> position;
69 PBD::PropertyDescriptor<framecnt_t> sync_position;
70 PBD::PropertyDescriptor<layer_t> layer;
71 PBD::PropertyDescriptor<framepos_t> ancestral_start;
72 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
73 PBD::PropertyDescriptor<float> stretch;
74 PBD::PropertyDescriptor<float> shift;
75 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
81 void
82 Region::make_property_quarks ()
84 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
86 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
88 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
90 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
92 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
94 Properties::import.property_id = g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
96 Properties::external.property_id = g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
98 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
100 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
102 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
104 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
106 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
108 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
110 Properties::start.property_id = g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
112 Properties::length.property_id = g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
114 Properties::position.property_id = g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
116 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
118 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
120 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
122 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
124 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
126 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
128 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
132 void
133 Region::register_properties ()
135 _xml_node_name = X_("Region");
137 add_property (_muted);
138 add_property (_opaque);
139 add_property (_locked);
140 add_property (_automatic);
141 add_property (_whole_file);
142 add_property (_import);
143 add_property (_external);
144 add_property (_sync_marked);
145 add_property (_left_of_split);
146 add_property (_right_of_split);
147 add_property (_hidden);
148 add_property (_position_locked);
149 add_property (_valid_transients);
150 add_property (_start);
151 add_property (_length);
152 add_property (_position);
153 add_property (_sync_position);
154 add_property (_layer);
155 add_property (_ancestral_start);
156 add_property (_ancestral_length);
157 add_property (_stretch);
158 add_property (_shift);
159 add_property (_position_lock_style);
162 #define REGION_DEFAULT_STATE(s,l) \
163 _muted (Properties::muted, false) \
164 , _opaque (Properties::opaque, true) \
165 , _locked (Properties::locked, false) \
166 , _automatic (Properties::automatic, false) \
167 , _whole_file (Properties::whole_file, false) \
168 , _import (Properties::import, false) \
169 , _external (Properties::external, false) \
170 , _sync_marked (Properties::sync_marked, false) \
171 , _left_of_split (Properties::left_of_split, false) \
172 , _right_of_split (Properties::right_of_split, false) \
173 , _hidden (Properties::hidden, false) \
174 , _position_locked (Properties::position_locked, false) \
175 , _valid_transients (Properties::valid_transients, false) \
176 , _start (Properties::start, (s)) \
177 , _length (Properties::length, (l)) \
178 , _position (Properties::position, 0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _layer (Properties::layer, 0) \
181 , _ancestral_start (Properties::ancestral_start, (s)) \
182 , _ancestral_length (Properties::ancestral_length, (l)) \
183 , _stretch (Properties::stretch, 1.0) \
184 , _shift (Properties::shift, 1.0) \
185 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
187 #define REGION_COPY_STATE(other) \
188 _muted (Properties::muted, other->_muted) \
189 , _opaque (Properties::opaque, other->_opaque) \
190 , _locked (Properties::locked, other->_locked) \
191 , _automatic (Properties::automatic, other->_automatic) \
192 , _whole_file (Properties::whole_file, other->_whole_file) \
193 , _import (Properties::import, other->_import) \
194 , _external (Properties::external, other->_external) \
195 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
196 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
197 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
198 , _hidden (Properties::hidden, other->_hidden) \
199 , _position_locked (Properties::position_locked, other->_position_locked) \
200 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
201 , _start(Properties::start, other->_start) \
202 , _length(Properties::length, other->_length) \
203 , _position(Properties::position, other->_position) \
204 , _sync_position(Properties::sync_position, other->_sync_position) \
205 , _layer (Properties::layer, other->_layer) \
206 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
207 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
208 , _stretch (Properties::stretch, other->_stretch) \
209 , _shift (Properties::shift, other->_shift) \
210 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
212 /* derived-from-derived constructor (no sources in constructor) */
213 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
214 : SessionObject(s, name)
215 , _type(type)
216 , REGION_DEFAULT_STATE(start,length)
217 , _last_length (length)
218 , _last_position (0)
219 , _first_edit (EditChangesNothing)
220 , _read_data_count(0)
221 , _last_layer_op(0)
222 , _pending_explicit_relayer (false)
224 register_properties ();
226 /* no sources at this point */
229 /** Basic Region constructor (many sources) */
230 Region::Region (const SourceList& srcs)
231 : SessionObject(srcs.front()->session(), "toBeRenamed")
232 , _type (srcs.front()->type())
233 , REGION_DEFAULT_STATE(0,0)
234 , _last_length (0)
235 , _last_position (0)
236 , _first_edit (EditChangesNothing)
237 , _read_data_count(0)
238 , _last_layer_op (0)
239 , _pending_explicit_relayer (false)
241 register_properties ();
243 _type = srcs.front()->type();
245 use_sources (srcs);
247 assert(_sources.size() > 0);
248 assert (_type == srcs.front()->type());
251 /** Create a new Region from an existing one */
252 Region::Region (boost::shared_ptr<const Region> other)
253 : SessionObject(other->session(), other->name())
254 , _type (other->data_type())
255 , REGION_COPY_STATE (other)
256 , _last_length (other->_last_length)
257 , _last_position(other->_last_position) \
258 , _first_edit (EditChangesNothing)
259 , _read_data_count(0)
260 , _last_layer_op (0)
261 , _pending_explicit_relayer (false)
264 register_properties ();
266 /* override state that may have been incorrectly inherited from the other region
269 _position = 0;
270 _locked = false;
271 _whole_file = false;
272 _hidden = false;
274 use_sources (other->_sources);
276 _position_lock_style = other->_position_lock_style;
277 _first_edit = other->_first_edit;
279 _start = 0; // It seems strange _start is not inherited here?
281 /* sync pos is relative to start of file. our start-in-file is now zero,
282 so set our sync position to whatever the the difference between
283 _start and _sync_pos was in the other region.
285 result is that our new sync pos points to the same point in our source(s)
286 as the sync in the other region did in its source(s).
288 since we start at zero in our source(s), it is not possible to use a sync point that
289 is before the start. reset it to _start if that was true in the other region.
292 if (other->sync_marked()) {
293 if (other->_start < other->_sync_position) {
294 /* sync pos was after the start point of the other region */
295 _sync_position = other->_sync_position - other->_start;
296 } else {
297 /* sync pos was before the start point of the other region. not possible here. */
298 _sync_marked = false;
299 _sync_position = _start;
301 } else {
302 _sync_marked = false;
303 _sync_position = _start;
306 if (Profile->get_sae()) {
307 /* reset sync point to start if its ended up
308 outside region bounds.
311 if (_sync_position < _start || _sync_position >= _start + _length) {
312 _sync_marked = false;
313 _sync_position = _start;
317 assert (_type == other->data_type());
320 /** Create a new Region from part of an existing one.
322 the start within \a other is given by \a offset
323 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
325 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
326 : SessionObject(other->session(), other->name())
327 , _type (other->data_type())
328 , REGION_COPY_STATE (other)
329 , _last_length (other->_last_length)
330 , _last_position(other->_last_position) \
331 , _first_edit (EditChangesNothing)
332 , _read_data_count(0)
333 , _last_layer_op (0)
334 , _pending_explicit_relayer (false)
337 register_properties ();
339 /* override state that may have been incorrectly inherited from the other region
342 _position = 0;
343 _locked = false;
344 _whole_file = false;
345 _hidden = false;
347 use_sources (other->_sources);
349 _start = other->_start + offset;
351 /* if the other region had a distinct sync point
352 set, then continue to use it as best we can.
353 otherwise, reset sync point back to start.
356 if (other->sync_marked()) {
357 if (other->_sync_position < _start) {
358 _sync_marked = false;
359 _sync_position = _start;
360 } else {
361 _sync_position = other->_sync_position;
363 } else {
364 _sync_marked = false;
365 _sync_position = _start;
368 if (Profile->get_sae()) {
369 /* reset sync point to start if its ended up
370 outside region bounds.
373 if (_sync_position < _start || _sync_position >= _start + _length) {
374 _sync_marked = false;
375 _sync_position = _start;
379 assert (_type == other->data_type());
382 /** Create a copy of @param other but with different sources. Used by filters */
383 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
384 : SessionObject (other->session(), other->name())
385 , _type (srcs.front()->type())
386 , REGION_COPY_STATE (other)
387 , _last_length (other->_last_length)
388 , _last_position (other->_last_position)
389 , _first_edit (EditChangesID)
390 , _read_data_count (0)
391 , _last_layer_op (other->_last_layer_op)
392 , _pending_explicit_relayer (false)
394 register_properties ();
396 _locked = false;
397 _position_locked = false;
399 other->_first_edit = EditChangesName;
401 if (other->_extra_xml) {
402 _extra_xml = new XMLNode (*other->_extra_xml);
403 } else {
404 _extra_xml = 0;
407 use_sources (srcs);
408 assert(_sources.size() > 0);
411 Region::~Region ()
413 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
414 drop_sources ();
417 void
418 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
420 _playlist = wpl.lock();
423 bool
424 Region::set_name (const std::string& str)
426 if (_name != str) {
427 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
428 assert(_name == str);
430 send_change (Properties::name);
433 return true;
436 void
437 Region::set_length (framecnt_t len)
439 //cerr << "Region::set_length() len = " << len << endl;
440 if (locked()) {
441 return;
444 if (_length != len && len != 0) {
446 /* check that the current _position wouldn't make the new
447 length impossible.
450 if (max_framepos - len < _position) {
451 return;
454 if (!verify_length (len)) {
455 return;
459 _last_length = _length;
460 set_length_internal (len);
461 _whole_file = false;
462 first_edit ();
463 maybe_uncopy ();
464 invalidate_transients ();
466 if (!property_changes_suspended()) {
467 recompute_at_end ();
470 send_change (Properties::length);
474 void
475 Region::set_length_internal (framecnt_t len)
477 _length = len;
480 void
481 Region::maybe_uncopy ()
483 /* this does nothing but marked a semantic moment once upon a time */
486 void
487 Region::first_edit ()
489 boost::shared_ptr<Playlist> pl (playlist());
491 if (_first_edit != EditChangesNothing && pl) {
493 _name = RegionFactory::new_region_name (_name);
494 _first_edit = EditChangesNothing;
496 send_change (Properties::name);
498 RegionFactory::CheckNewRegion (shared_from_this());
502 bool
503 Region::at_natural_position () const
505 boost::shared_ptr<Playlist> pl (playlist());
507 if (!pl) {
508 return false;
511 boost::shared_ptr<Region> whole_file_region = get_parent();
513 if (whole_file_region) {
514 if (_position == whole_file_region->position() + _start) {
515 return true;
519 return false;
522 void
523 Region::move_to_natural_position ()
525 boost::shared_ptr<Playlist> pl (playlist());
527 if (!pl) {
528 return;
531 boost::shared_ptr<Region> whole_file_region = get_parent();
533 if (whole_file_region) {
534 set_position (whole_file_region->position() + _start);
538 void
539 Region::special_set_position (framepos_t pos)
541 /* this is used when creating a whole file region as
542 a way to store its "natural" or "captured" position.
545 _position = _position;
546 _position = pos;
549 void
550 Region::set_position_lock_style (PositionLockStyle ps)
552 if (_position_lock_style != ps) {
554 boost::shared_ptr<Playlist> pl (playlist());
556 if (!pl) {
557 return;
560 _position_lock_style = ps;
562 if (_position_lock_style == MusicTime) {
563 _session.tempo_map().bbt_time (_position, _bbt_time);
566 send_change (Properties::position_lock_style);
571 void
572 Region::update_position_after_tempo_map_change ()
574 boost::shared_ptr<Playlist> pl (playlist());
576 if (!pl || _position_lock_style != MusicTime) {
577 return;
580 TempoMap& map (_session.tempo_map());
581 framepos_t pos = map.frame_time (_bbt_time);
582 set_position_internal (pos, false);
584 /* do this even if the position is the same. this helps out
585 a GUI that has moved its representation already.
587 send_change (Properties::position);
590 void
591 Region::set_position (framepos_t pos)
593 if (!can_move()) {
594 return;
597 set_position_internal (pos, true);
599 /* do this even if the position is the same. this helps out
600 a GUI that has moved its representation already.
602 send_change (Properties::position);
606 void
607 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
609 if (_position != pos) {
610 _last_position = _position;
611 _position = pos;
613 /* check that the new _position wouldn't make the current
614 length impossible - if so, change the length.
616 XXX is this the right thing to do?
619 if (max_framepos - _length < _position) {
620 _last_length = _length;
621 _length = max_framepos - _position;
624 if (allow_bbt_recompute) {
625 recompute_position_from_lock_style ();
628 //invalidate_transients ();
632 void
633 Region::set_position_on_top (framepos_t pos)
635 if (locked()) {
636 return;
639 if (_position != pos) {
640 set_position_internal (pos, true);
643 boost::shared_ptr<Playlist> pl (playlist());
645 if (pl) {
646 pl->raise_region_to_top (shared_from_this ());
649 /* do this even if the position is the same. this helps out
650 a GUI that has moved its representation already.
652 send_change (Properties::position);
655 void
656 Region::recompute_position_from_lock_style ()
658 if (_position_lock_style == MusicTime) {
659 _session.tempo_map().bbt_time (_position, _bbt_time);
663 void
664 Region::nudge_position (frameoffset_t n)
666 if (locked()) {
667 return;
670 if (n == 0) {
671 return;
674 framepos_t new_position = _position;
676 if (n > 0) {
677 if (_position > max_framepos - n) {
678 new_position = max_framepos;
679 } else {
680 new_position += n;
682 } else {
683 if (_position < -n) {
684 new_position = 0;
685 } else {
686 new_position += n;
690 set_position_internal (new_position, true);
692 send_change (Properties::position);
695 void
696 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
698 _ancestral_length = l;
699 _ancestral_start = s;
700 _stretch = st;
701 _shift = sh;
704 void
705 Region::set_start (framepos_t pos)
707 if (locked() || position_locked()) {
708 return;
710 /* This just sets the start, nothing else. It effectively shifts
711 the contents of the Region within the overall extent of the Source,
712 without changing the Region's position or length
715 if (_start != pos) {
717 if (!verify_start (pos)) {
718 return;
721 _start = pos;
722 _whole_file = false;
723 first_edit ();
724 invalidate_transients ();
726 send_change (Properties::start);
730 void
731 Region::trim_start (framepos_t new_position)
733 if (locked() || position_locked()) {
734 return;
736 framepos_t new_start;
737 frameoffset_t const start_shift = new_position - _position;
739 if (start_shift > 0) {
741 if (_start > max_framepos - start_shift) {
742 new_start = max_framepos;
743 } else {
744 new_start = _start + start_shift;
747 if (!verify_start (new_start)) {
748 return;
751 } else if (start_shift < 0) {
753 if (_start < -start_shift) {
754 new_start = 0;
755 } else {
756 new_start = _start + start_shift;
759 } else {
760 return;
763 if (new_start == _start) {
764 return;
767 _start = new_start;
768 _whole_file = false;
769 first_edit ();
771 send_change (Properties::start);
774 void
775 Region::trim_front (framepos_t new_position)
777 modify_front (new_position, false);
780 void
781 Region::cut_front (framepos_t new_position)
783 modify_front (new_position, true);
786 void
787 Region::cut_end (framepos_t new_endpoint)
789 modify_end (new_endpoint, true);
792 void
793 Region::modify_front (framepos_t new_position, bool reset_fade)
795 if (locked()) {
796 return;
799 framepos_t end = last_frame();
800 framepos_t source_zero;
802 if (_position > _start) {
803 source_zero = _position - _start;
804 } else {
805 source_zero = 0; // its actually negative, but this will work for us
808 if (new_position < end) { /* can't trim it zero or negative length */
810 framecnt_t newlen = 0;
811 framepos_t delta = 0;
813 if (!can_trim_start_before_source_start ()) {
814 /* can't trim it back past where source position zero is located */
815 new_position = max (new_position, source_zero);
818 if (new_position > _position) {
819 newlen = _length - (new_position - _position);
820 delta = -1 * (new_position - _position);
821 } else {
822 newlen = _length + (_position - new_position);
823 delta = _position - new_position;
826 trim_to_internal (new_position, newlen);
828 if (reset_fade) {
829 _right_of_split = true;
832 if (!property_changes_suspended()) {
833 recompute_at_start ();
836 if (_transients.size() > 0){
837 adjust_transients(delta);
842 void
843 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
845 if (locked()) {
846 return;
849 if (new_endpoint > _position) {
850 trim_to_internal (_position, new_endpoint - _position +1);
851 if (reset_fade) {
852 _left_of_split = true;
854 if (!property_changes_suspended()) {
855 recompute_at_end ();
860 /** @param new_endpoint New region end point, such that, for example,
861 * a region at 0 of length 10 has an endpoint of 9.
864 void
865 Region::trim_end (framepos_t new_endpoint)
867 modify_end (new_endpoint, false);
870 void
871 Region::trim_to (framepos_t position, framecnt_t length)
873 if (locked()) {
874 return;
877 trim_to_internal (position, length);
879 if (!property_changes_suspended()) {
880 recompute_at_start ();
881 recompute_at_end ();
885 void
886 Region::trim_to_internal (framepos_t position, framecnt_t length)
888 framepos_t new_start;
890 if (locked()) {
891 return;
894 frameoffset_t const start_shift = position - _position;
896 if (start_shift > 0) {
898 if (_start > max_framepos - start_shift) {
899 new_start = max_framepos;
900 } else {
901 new_start = _start + start_shift;
904 } else if (start_shift < 0) {
906 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
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);
927 /* Set position before length, otherwise for MIDI regions this bad thing happens:
928 * 1. we call set_length_internal; length in beats is computed using the region's current
929 * (soon-to-be old) position
930 * 2. we call set_position_internal; position is set and length in frames re-computed using
931 * length in beats from (1) but at the new position, which is wrong if the region
932 * straddles a tempo/meter change.
935 if (_position != position) {
936 if (!property_changes_suspended()) {
937 _last_position = _position;
939 set_position_internal (position, true);
940 what_changed.add (Properties::position);
943 if (_length != length) {
944 if (!property_changes_suspended()) {
945 _last_length = _length;
947 set_length_internal (length);
948 what_changed.add (Properties::length);
951 _whole_file = false;
953 PropertyChange start_and_length;
955 start_and_length.add (Properties::start);
956 start_and_length.add (Properties::length);
958 if (what_changed.contains (start_and_length)) {
959 first_edit ();
962 if (!what_changed.empty()) {
963 send_change (what_changed);
967 void
968 Region::set_hidden (bool yn)
970 if (hidden() != yn) {
971 _hidden = yn;
972 send_change (Properties::hidden);
976 void
977 Region::set_whole_file (bool yn)
979 _whole_file = yn;
980 /* no change signal */
983 void
984 Region::set_automatic (bool yn)
986 _automatic = yn;
987 /* no change signal */
990 void
991 Region::set_muted (bool yn)
993 if (muted() != yn) {
994 _muted = yn;
995 send_change (Properties::muted);
999 void
1000 Region::set_opaque (bool yn)
1002 if (opaque() != yn) {
1003 _opaque = yn;
1004 send_change (Properties::opaque);
1008 void
1009 Region::set_locked (bool yn)
1011 if (locked() != yn) {
1012 _locked = yn;
1013 send_change (Properties::locked);
1017 void
1018 Region::set_position_locked (bool yn)
1020 if (position_locked() != yn) {
1021 _position_locked = yn;
1022 send_change (Properties::locked);
1026 /** Set the region's sync point.
1027 * @param absolute_pos Session time.
1029 void
1030 Region::set_sync_position (framepos_t absolute_pos)
1032 /* position within our file */
1033 framepos_t const file_pos = _start + (absolute_pos - _position);
1035 if (file_pos != _sync_position) {
1036 _sync_marked = true;
1037 _sync_position = file_pos;
1038 if (!property_changes_suspended()) {
1039 maybe_uncopy ();
1042 send_change (Properties::sync_position);
1046 void
1047 Region::clear_sync_position ()
1049 if (sync_marked()) {
1050 _sync_marked = false;
1051 if (!property_changes_suspended()) {
1052 maybe_uncopy ();
1055 send_change (Properties::sync_position);
1059 /* @return the sync point relative the first frame of the region */
1060 frameoffset_t
1061 Region::sync_offset (int& dir) const
1063 if (sync_marked()) {
1064 if (_sync_position > _start) {
1065 dir = 1;
1066 return _sync_position - _start;
1067 } else {
1068 dir = -1;
1069 return _start - _sync_position;
1071 } else {
1072 dir = 0;
1073 return 0;
1077 framepos_t
1078 Region::adjust_to_sync (framepos_t pos) const
1080 int sync_dir;
1081 frameoffset_t offset = sync_offset (sync_dir);
1083 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1085 if (sync_dir > 0) {
1086 if (pos > offset) {
1087 pos -= offset;
1088 } else {
1089 pos = 0;
1091 } else {
1092 if (max_framepos - pos > offset) {
1093 pos += offset;
1097 return pos;
1100 /** @return Sync position in session time */
1101 framepos_t
1102 Region::sync_position() const
1104 if (sync_marked()) {
1105 return _position - _start + _sync_position;
1106 } else {
1107 /* if sync has not been marked, use the start of the region */
1108 return _position;
1112 void
1113 Region::raise ()
1115 boost::shared_ptr<Playlist> pl (playlist());
1116 if (pl) {
1117 pl->raise_region (shared_from_this ());
1121 void
1122 Region::lower ()
1124 boost::shared_ptr<Playlist> pl (playlist());
1125 if (pl) {
1126 pl->lower_region (shared_from_this ());
1131 void
1132 Region::raise_to_top ()
1134 boost::shared_ptr<Playlist> pl (playlist());
1135 if (pl) {
1136 pl->raise_region_to_top (shared_from_this());
1140 void
1141 Region::lower_to_bottom ()
1143 boost::shared_ptr<Playlist> pl (playlist());
1144 if (pl) {
1145 pl->lower_region_to_bottom (shared_from_this());
1149 void
1150 Region::set_layer (layer_t l)
1152 if (_layer != l) {
1153 _layer = l;
1155 send_change (Properties::layer);
1159 XMLNode&
1160 Region::state ()
1162 XMLNode *node = new XMLNode ("Region");
1163 char buf[64];
1164 char buf2[64];
1165 LocaleGuard lg (X_("POSIX"));
1166 const char* fe = NULL;
1168 add_properties (*node);
1170 _id.print (buf, sizeof (buf));
1171 node->add_property ("id", buf);
1172 node->add_property ("type", _type.to_string());
1174 switch (_first_edit) {
1175 case EditChangesNothing:
1176 fe = X_("nothing");
1177 break;
1178 case EditChangesName:
1179 fe = X_("name");
1180 break;
1181 case EditChangesID:
1182 fe = X_("id");
1183 break;
1184 default: /* should be unreachable but makes g++ happy */
1185 fe = X_("nothing");
1186 break;
1189 node->add_property ("first-edit", fe);
1191 /* note: flags are stored by derived classes */
1193 if (_position_lock_style != AudioTime) {
1194 stringstream str;
1195 str << _bbt_time;
1196 node->add_property ("bbt-position", str.str());
1199 for (uint32_t n=0; n < _sources.size(); ++n) {
1200 snprintf (buf2, sizeof(buf2), "source-%d", n);
1201 _sources[n]->id().print (buf, sizeof(buf));
1202 node->add_property (buf2, buf);
1205 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1206 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1207 _master_sources[n]->id().print (buf, sizeof (buf));
1208 node->add_property (buf2, buf);
1211 /* Only store nested sources for the whole-file region that acts
1212 as the parent/root of all regions using it.
1215 if (_whole_file && max_source_level() > 0) {
1217 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1219 /* region is compound - get its playlist and
1220 store that before we list the region that
1221 needs it ...
1224 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1225 nested_node->add_child_nocopy ((*s)->get_state ());
1228 if (nested_node) {
1229 node->add_child_nocopy (*nested_node);
1233 if (_extra_xml) {
1234 node->add_child_copy (*_extra_xml);
1237 return *node;
1240 XMLNode&
1241 Region::get_state ()
1243 return state ();
1247 Region::set_state (const XMLNode& node, int version)
1249 PropertyChange what_changed;
1250 return _set_state (node, version, what_changed, true);
1254 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1256 const XMLProperty* prop;
1258 Stateful::save_extra_xml (node);
1260 what_changed = set_values (node);
1262 if ((prop = node.property (X_("id")))) {
1263 _id = prop->value();
1266 if (_position_lock_style == MusicTime) {
1267 if ((prop = node.property ("bbt-position")) == 0) {
1268 /* missing BBT info, revert to audio time locking */
1269 _position_lock_style = AudioTime;
1270 } else {
1271 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1272 &_bbt_time.bars,
1273 &_bbt_time.beats,
1274 &_bbt_time.ticks) != 3) {
1275 _position_lock_style = AudioTime;
1280 /* fix problems with old sessions corrupted by impossible
1281 values for _stretch or _shift
1283 if (_stretch == 0.0f) {
1284 _stretch = 1.0f;
1287 if (_shift == 0.0f) {
1288 _shift = 1.0f;
1291 if (send) {
1292 send_change (what_changed);
1295 /* Quick fix for 2.x sessions when region is muted */
1296 if ((prop = node.property (X_("flags")))) {
1297 if (string::npos != prop->value().find("Muted")){
1298 set_muted (true);
1303 return 0;
1306 void
1307 Region::suspend_property_changes ()
1309 Stateful::suspend_property_changes ();
1310 _last_length = _length;
1311 _last_position = _position;
1314 void
1315 Region::mid_thaw (const PropertyChange& what_changed)
1317 if (what_changed.contains (Properties::length)) {
1318 if (what_changed.contains (Properties::position)) {
1319 recompute_at_start ();
1321 recompute_at_end ();
1325 void
1326 Region::send_change (const PropertyChange& what_changed)
1328 if (what_changed.empty()) {
1329 return;
1332 Stateful::send_change (what_changed);
1334 if (!Stateful::frozen()) {
1336 /* Try and send a shared_pointer unless this is part of the constructor.
1337 If so, do nothing.
1340 try {
1341 boost::shared_ptr<Region> rptr = shared_from_this();
1342 RegionPropertyChanged (rptr, what_changed);
1343 } catch (...) {
1344 /* no shared_ptr available, relax; */
1349 void
1350 Region::set_last_layer_op (uint64_t when)
1352 _last_layer_op = when;
1355 bool
1356 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1358 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1361 bool
1362 Region::equivalent (boost::shared_ptr<const Region> other) const
1364 return _start == other->_start &&
1365 _position == other->_position &&
1366 _length == other->_length;
1369 bool
1370 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1372 return _start == other->_start &&
1373 _length == other->_length;
1376 bool
1377 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1379 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1382 void
1383 Region::source_deleted (boost::weak_ptr<Source>)
1385 drop_sources ();
1387 if (!_session.deletion_in_progress()) {
1388 /* this is a very special case: at least one of the region's
1389 sources has bee deleted, so invalidate all references to
1390 ourselves. Do NOT do this during session deletion, because
1391 then we run the risk that this will actually result
1392 in this object being deleted (as refcnt goes to zero)
1393 while emitting DropReferences.
1396 drop_references ();
1400 vector<string>
1401 Region::master_source_names ()
1403 SourceList::iterator i;
1405 vector<string> names;
1406 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1407 names.push_back((*i)->name());
1410 return names;
1413 void
1414 Region::set_master_sources (const SourceList& srcs)
1416 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1417 (*i)->dec_use_count ();
1420 _master_sources = srcs;
1421 assert (_sources.size() == _master_sources.size());
1423 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1424 (*i)->inc_use_count ();
1428 bool
1429 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1431 if (!other)
1432 return false;
1434 if ((_sources.size() != other->_sources.size()) ||
1435 (_master_sources.size() != other->_master_sources.size())) {
1436 return false;
1439 SourceList::const_iterator i;
1440 SourceList::const_iterator io;
1442 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1443 if ((*i)->id() != (*io)->id()) {
1444 return false;
1448 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1449 if ((*i)->id() != (*io)->id()) {
1450 return false;
1454 return true;
1457 std::string
1458 Region::source_string () const
1460 //string res = itos(_sources.size());
1462 stringstream res;
1463 res << _sources.size() << ":";
1465 SourceList::const_iterator i;
1467 for (i = _sources.begin(); i != _sources.end(); ++i) {
1468 res << (*i)->id() << ":";
1471 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1472 res << (*i)->id() << ":";
1475 return res.str();
1478 bool
1479 Region::uses_source (boost::shared_ptr<const Source> source) const
1481 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1482 if (*i == source) {
1483 return true;
1486 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1488 if (ps) {
1489 if (ps->playlist()->uses_source (source)) {
1490 return true;
1495 return false;
1498 bool
1499 Region::uses_source_path (const std::string& path) const
1501 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1502 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1503 if (fs) {
1504 if (fs->path() == path) {
1505 return true;
1509 return false;
1512 framecnt_t
1513 Region::source_length(uint32_t n) const
1515 assert (n < _sources.size());
1516 return _sources[n]->length (_position - _start);
1519 bool
1520 Region::verify_length (framecnt_t len)
1522 if (source() && (source()->destructive() || source()->length_mutable())) {
1523 return true;
1526 framecnt_t maxlen = 0;
1528 for (uint32_t n = 0; n < _sources.size(); ++n) {
1529 maxlen = max (maxlen, source_length(n) - _start);
1532 len = min (len, maxlen);
1534 return true;
1537 bool
1538 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1540 if (source() && (source()->destructive() || source()->length_mutable())) {
1541 return true;
1544 framecnt_t maxlen = 0;
1546 for (uint32_t n = 0; n < _sources.size(); ++n) {
1547 maxlen = max (maxlen, source_length(n) - new_start);
1550 new_length = min (new_length, maxlen);
1552 return true;
1555 bool
1556 Region::verify_start (framepos_t pos)
1558 if (source() && (source()->destructive() || source()->length_mutable())) {
1559 return true;
1562 for (uint32_t n = 0; n < _sources.size(); ++n) {
1563 if (pos > source_length(n) - _length) {
1564 return false;
1567 return true;
1570 bool
1571 Region::verify_start_mutable (framepos_t& new_start)
1573 if (source() && (source()->destructive() || source()->length_mutable())) {
1574 return true;
1577 for (uint32_t n = 0; n < _sources.size(); ++n) {
1578 if (new_start > source_length(n) - _length) {
1579 new_start = source_length(n) - _length;
1582 return true;
1585 boost::shared_ptr<Region>
1586 Region::get_parent() const
1588 boost::shared_ptr<Playlist> pl (playlist());
1590 if (pl) {
1591 boost::shared_ptr<Region> r;
1592 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1594 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1595 return boost::static_pointer_cast<Region> (r);
1599 return boost::shared_ptr<Region>();
1603 Region::apply (Filter& filter, Progress* progress)
1605 return filter.run (shared_from_this(), progress);
1609 void
1610 Region::invalidate_transients ()
1612 _valid_transients = false;
1613 _transients.clear ();
1615 send_change (PropertyChange (Properties::valid_transients));
1618 void
1619 Region::drop_sources ()
1621 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1622 (*i)->dec_use_count ();
1625 _sources.clear ();
1627 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1628 (*i)->dec_use_count ();
1631 _master_sources.clear ();
1634 void
1635 Region::use_sources (SourceList const & s)
1637 set<boost::shared_ptr<Source> > unique_srcs;
1639 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1641 _sources.push_back (*i);
1642 (*i)->inc_use_count ();
1643 _master_sources.push_back (*i);
1644 (*i)->inc_use_count ();
1646 /* connect only once to DropReferences, even if sources are replicated
1649 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1650 unique_srcs.insert (*i);
1651 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1656 Trimmable::CanTrim
1657 Region::can_trim () const
1659 CanTrim ct = CanTrim (0);
1661 if (locked()) {
1662 return ct;
1665 /* if not locked, we can always move the front later, and the end earlier
1668 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1670 if (start() != 0 || can_trim_start_before_source_start ()) {
1671 ct = CanTrim (ct | FrontTrimEarlier);
1674 if (!_sources.empty()) {
1675 if ((start() + length()) < _sources.front()->length (0)) {
1676 ct = CanTrim (ct | EndTrimLater);
1680 return ct;
1683 uint32_t
1684 Region::max_source_level () const
1686 uint32_t lvl = 0;
1688 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1689 lvl = max (lvl, (*i)->level());
1692 return lvl;
1695 bool
1696 Region::is_compound () const
1698 return max_source_level() > 0;