lots of details relating to MIDI file management; try to ignore ALSA sequencer MIDI...
[ardour2.git] / libs / ardour / region.cc
blob0ae947063f140eb170ea9d7fb969decb560720e0
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>
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
43 #include "i18n.h"
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
49 namespace ARDOUR {
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> muted;
52 PBD::PropertyDescriptor<bool> opaque;
53 PBD::PropertyDescriptor<bool> locked;
54 PBD::PropertyDescriptor<bool> automatic;
55 PBD::PropertyDescriptor<bool> whole_file;
56 PBD::PropertyDescriptor<bool> import;
57 PBD::PropertyDescriptor<bool> external;
58 PBD::PropertyDescriptor<bool> sync_marked;
59 PBD::PropertyDescriptor<bool> left_of_split;
60 PBD::PropertyDescriptor<bool> right_of_split;
61 PBD::PropertyDescriptor<bool> hidden;
62 PBD::PropertyDescriptor<bool> position_locked;
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;
75 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
77 void
78 Region::make_property_quarks ()
80 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
81 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
82 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
83 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
84 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
85 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
86 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
87 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
88 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
90 Properties::import.property_id = g_quark_from_static_string (X_("import"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
92 Properties::external.property_id = g_quark_from_static_string (X_("external"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
94 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
96 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
98 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
100 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
102 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
104 Properties::start.property_id = g_quark_from_static_string (X_("start"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
106 Properties::length.property_id = g_quark_from_static_string (X_("length"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
108 Properties::position.property_id = g_quark_from_static_string (X_("position"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
110 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
112 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
114 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
116 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
118 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
120 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
124 void
125 Region::register_properties ()
127 _xml_node_name = X_("Region");
129 add_property (_muted);
130 add_property (_opaque);
131 add_property (_locked);
132 add_property (_automatic);
133 add_property (_whole_file);
134 add_property (_import);
135 add_property (_external);
136 add_property (_sync_marked);
137 add_property (_left_of_split);
138 add_property (_right_of_split);
139 add_property (_hidden);
140 add_property (_position_locked);
141 add_property (_start);
142 add_property (_length);
143 add_property (_position);
144 add_property (_sync_position);
145 add_property (_layer);
146 add_property (_ancestral_start);
147 add_property (_ancestral_length);
148 add_property (_stretch);
149 add_property (_shift);
152 #define REGION_DEFAULT_STATE(s,l) \
153 _muted (Properties::muted, false) \
154 , _opaque (Properties::opaque, true) \
155 , _locked (Properties::locked, false) \
156 , _automatic (Properties::automatic, false) \
157 , _whole_file (Properties::whole_file, false) \
158 , _import (Properties::import, false) \
159 , _external (Properties::external, false) \
160 , _sync_marked (Properties::sync_marked, false) \
161 , _left_of_split (Properties::left_of_split, false) \
162 , _right_of_split (Properties::right_of_split, false) \
163 , _hidden (Properties::hidden, false) \
164 , _position_locked (Properties::position_locked, false) \
165 , _start (Properties::start, (s)) \
166 , _length (Properties::length, (l)) \
167 , _position (Properties::position, 0) \
168 , _sync_position (Properties::sync_position, (s)) \
169 , _layer (Properties::layer, 0) \
170 , _ancestral_start (Properties::ancestral_start, (s)) \
171 , _ancestral_length (Properties::ancestral_length, (l)) \
172 , _stretch (Properties::stretch, 1.0) \
173 , _shift (Properties::shift, 1.0)
175 #define REGION_COPY_STATE(other) \
176 _muted (other->_muted) \
177 , _opaque (other->_opaque) \
178 , _locked (other->_locked) \
179 , _automatic (other->_automatic) \
180 , _whole_file (other->_whole_file) \
181 , _import (other->_import) \
182 , _external (other->_external) \
183 , _sync_marked (other->_sync_marked) \
184 , _left_of_split (other->_left_of_split) \
185 , _right_of_split (other->_right_of_split) \
186 , _hidden (other->_hidden) \
187 , _position_locked (other->_position_locked) \
188 , _start(other->_start) \
189 , _length(other->_length) \
190 , _position(other->_position) \
191 , _sync_position(other->_sync_position) \
192 , _layer (other->_layer) \
193 , _ancestral_start (other->_ancestral_start) \
194 , _ancestral_length (other->_ancestral_length) \
195 , _stretch (other->_stretch) \
196 , _shift (other->_shift)
198 /* derived-from-derived constructor (no sources in constructor) */
199 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
200 : SessionObject(s, name)
201 , _type(type)
202 , REGION_DEFAULT_STATE(start,length)
203 , _last_length (length)
204 , _last_position (0)
205 , _positional_lock_style(AudioTime)
206 , _first_edit (EditChangesNothing)
207 , _read_data_count(0)
208 , _last_layer_op(0)
209 , _pending_explicit_relayer (false)
211 register_properties ();
213 /* no sources at this point */
216 /** Basic Region constructor (many sources) */
217 Region::Region (const SourceList& srcs)
218 : SessionObject(srcs.front()->session(), "toBeRenamed")
219 , _type (srcs.front()->type())
220 , REGION_DEFAULT_STATE(0,0)
221 , _last_length (0)
222 , _last_position (0)
223 , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
224 , _first_edit (EditChangesNothing)
225 , _valid_transients(false)
226 , _read_data_count(0)
227 , _last_layer_op (0)
228 , _pending_explicit_relayer (false)
230 register_properties ();
232 _type = srcs.front()->type();
234 use_sources (srcs);
236 assert(_sources.size() > 0);
237 assert (_type == srcs.front()->type());
240 /** Create a new Region from part of an existing one, starting at one of two places:
242 if @param offset_relative is true, then the start within @param other is given by @param offset
243 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
245 if @param offset_relative is false, then the start within the source is given @param offset.
247 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
248 : SessionObject(other->session(), other->name())
249 , _type (other->data_type())
250 , REGION_COPY_STATE (other)
251 , _last_length (other->_last_length)
252 , _last_position(other->_last_position) \
253 , _positional_lock_style(other->_positional_lock_style) \
254 , _first_edit (EditChangesNothing)
255 , _valid_transients(false)
256 , _read_data_count(0)
257 , _last_layer_op (0)
258 , _pending_explicit_relayer (false)
261 register_properties ();
263 /* override state that may have been incorrectly inherited from the other region
266 _position = 0;
267 _locked = false;
268 _whole_file = false;
269 _hidden = false;
271 use_sources (other->_sources);
273 if (!offset_relative) {
275 /* not sure why we do this, but its a hangover from ardour before
276 property lists. this would be nice to remove.
279 _positional_lock_style = other->_positional_lock_style;
280 _first_edit = other->_first_edit;
282 if (offset == 0) {
284 _start = 0;
286 /* sync pos is relative to start of file. our start-in-file is now zero,
287 so set our sync position to whatever the the difference between
288 _start and _sync_pos was in the other region.
290 result is that our new sync pos points to the same point in our source(s)
291 as the sync in the other region did in its source(s).
293 since we start at zero in our source(s), it is not possible to use a sync point that
294 is before the start. reset it to _start if that was true in the other region.
297 if (other->sync_marked()) {
298 if (other->_start < other->_sync_position) {
299 /* sync pos was after the start point of the other region */
300 _sync_position = other->_sync_position - other->_start;
301 } else {
302 /* sync pos was before the start point of the other region. not possible here. */
303 _sync_marked = false;
304 _sync_position = _start;
306 } else {
307 _sync_marked = false;
308 _sync_position = _start;
310 } else {
311 /* XXX do something else ! */
312 fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
313 << endmsg;
314 /*NOTREACHED*/
317 } else {
319 _start = other->_start + offset;
321 /* if the other region had a distinct sync point
322 set, then continue to use it as best we can.
323 otherwise, reset sync point back to start.
326 if (other->sync_marked()) {
327 if (other->_sync_position < _start) {
328 _sync_marked = false;
329 _sync_position = _start;
330 } else {
331 _sync_position = other->_sync_position;
333 } else {
334 _sync_marked = false;
335 _sync_position = _start;
339 if (Profile->get_sae()) {
340 /* reset sync point to start if its ended up
341 outside region bounds.
344 if (_sync_position < _start || _sync_position >= _start + _length) {
345 _sync_marked = false;
346 _sync_position = _start;
350 assert (_type == other->data_type());
353 /** Create a copy of @param other but with different sources. Used by filters */
354 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
355 : SessionObject (other->session(), other->name())
356 , _type (srcs.front()->type())
357 , REGION_COPY_STATE (other)
358 , _last_length (other->_last_length)
359 , _last_position (other->_last_position)
360 , _positional_lock_style (other->_positional_lock_style)
361 , _first_edit (EditChangesID)
362 , _valid_transients (false)
363 , _read_data_count (0)
364 , _last_layer_op (other->_last_layer_op)
365 , _pending_explicit_relayer (false)
367 register_properties ();
369 _locked = false;
370 _position_locked = false;
372 other->_first_edit = EditChangesName;
374 if (other->_extra_xml) {
375 _extra_xml = new XMLNode (*other->_extra_xml);
376 } else {
377 _extra_xml = 0;
380 use_sources (srcs);
381 assert(_sources.size() > 0);
384 /** Simple "copy" constructor */
385 Region::Region (boost::shared_ptr<const Region> other)
386 : SessionObject(other->session(), other->name())
387 , _type(other->data_type())
388 , REGION_COPY_STATE (other)
389 , _last_length (other->_last_length)
390 , _last_position (other->_last_position)
391 , _positional_lock_style (other->_positional_lock_style)
392 , _first_edit (EditChangesID)
393 , _valid_transients(false)
394 , _read_data_count(0)
395 , _last_layer_op(other->_last_layer_op)
396 , _pending_explicit_relayer (false)
398 register_properties ();
400 _locked = false;
401 _position_locked = false;
403 other->_first_edit = EditChangesName;
405 if (other->_extra_xml) {
406 _extra_xml = new XMLNode (*other->_extra_xml);
407 } else {
408 _extra_xml = 0;
411 use_sources (other->_sources);
412 assert(_sources.size() > 0);
415 Region::~Region ()
417 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
418 drop_sources ();
421 void
422 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
424 _playlist = wpl.lock();
427 bool
428 Region::set_name (const std::string& str)
430 if (_name != str) {
431 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
432 assert(_name == str);
433 send_change (Properties::name);
436 return true;
439 void
440 Region::set_length (framecnt_t len, void */*src*/)
442 //cerr << "Region::set_length() len = " << len << endl;
443 if (locked()) {
444 return;
447 if (_length != len && len != 0) {
449 /* check that the current _position wouldn't make the new
450 length impossible.
453 if (max_frames - len < _position) {
454 return;
457 if (!verify_length (len)) {
458 return;
462 _last_length = _length;
463 _length = len;
464 _whole_file = false;
465 first_edit ();
466 maybe_uncopy ();
467 invalidate_transients ();
469 if (!property_changes_suspended()) {
470 recompute_at_end ();
473 send_change (Properties::length);
477 void
478 Region::maybe_uncopy ()
480 /* this does nothing but marked a semantic moment once upon a time */
483 void
484 Region::first_edit ()
486 boost::shared_ptr<Playlist> pl (playlist());
488 if (_first_edit != EditChangesNothing && pl) {
490 _name = RegionFactory::new_region_name (_name);
491 _first_edit = EditChangesNothing;
493 send_change (Properties::name);
494 RegionFactory::CheckNewRegion (shared_from_this());
498 bool
499 Region::at_natural_position () const
501 boost::shared_ptr<Playlist> pl (playlist());
503 if (!pl) {
504 return false;
507 boost::shared_ptr<Region> whole_file_region = get_parent();
509 if (whole_file_region) {
510 if (_position == whole_file_region->position() + _start) {
511 return true;
515 return false;
518 void
519 Region::move_to_natural_position (void *src)
521 boost::shared_ptr<Playlist> pl (playlist());
523 if (!pl) {
524 return;
527 boost::shared_ptr<Region> whole_file_region = get_parent();
529 if (whole_file_region) {
530 set_position (whole_file_region->position() + _start, src);
534 void
535 Region::special_set_position (framepos_t pos)
537 /* this is used when creating a whole file region as
538 a way to store its "natural" or "captured" position.
541 _position = _position;
542 _position = pos;
545 void
546 Region::set_position_lock_style (PositionLockStyle ps)
548 boost::shared_ptr<Playlist> pl (playlist());
550 if (!pl) {
551 return;
554 _positional_lock_style = ps;
556 if (_positional_lock_style == MusicTime) {
557 _session.tempo_map().bbt_time (_position, _bbt_time);
562 void
563 Region::update_position_after_tempo_map_change ()
565 boost::shared_ptr<Playlist> pl (playlist());
567 if (!pl || _positional_lock_style != MusicTime) {
568 return;
571 TempoMap& map (_session.tempo_map());
572 framepos_t pos = map.frame_time (_bbt_time);
573 set_position_internal (pos, false);
576 void
577 Region::set_position (framepos_t pos, void* /*src*/)
579 if (!can_move()) {
580 return;
583 set_position_internal (pos, true);
586 void
587 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
589 if (_position != pos) {
590 _last_position = _position;
591 _position = pos;
593 /* check that the new _position wouldn't make the current
594 length impossible - if so, change the length.
596 XXX is this the right thing to do?
599 if (max_frames - _length < _position) {
600 _last_length = _length;
601 _length = max_frames - _position;
604 if (allow_bbt_recompute) {
605 recompute_position_from_lock_style ();
608 invalidate_transients ();
611 /* do this even if the position is the same. this helps out
612 a GUI that has moved its representation already.
615 send_change (Properties::position);
618 void
619 Region::set_position_on_top (framepos_t pos, void* /*src*/)
621 if (locked()) {
622 return;
625 if (_position != pos) {
626 _last_position = _position;
627 _position = pos;
630 boost::shared_ptr<Playlist> pl (playlist());
632 if (pl) {
633 pl->raise_region_to_top (shared_from_this ());
636 /* do this even if the position is the same. this helps out
637 a GUI that has moved its representation already.
640 send_change (Properties::position);
643 void
644 Region::recompute_position_from_lock_style ()
646 if (_positional_lock_style == MusicTime) {
647 _session.tempo_map().bbt_time (_position, _bbt_time);
651 void
652 Region::nudge_position (frameoffset_t n, void* /*src*/)
654 if (locked()) {
655 return;
658 if (n == 0) {
659 return;
662 _last_position = _position;
664 if (n > 0) {
665 if (_position > max_frames - n) {
666 _position = max_frames;
667 } else {
668 _position += n;
670 } else {
671 if (_position < -n) {
672 _position = 0;
673 } else {
674 _position += n;
678 send_change (Properties::position);
681 void
682 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
684 _ancestral_length = l;
685 _ancestral_start = s;
686 _stretch = st;
687 _shift = sh;
690 void
691 Region::set_start (framepos_t pos, void* /*src*/)
693 if (locked() || position_locked()) {
694 return;
696 /* This just sets the start, nothing else. It effectively shifts
697 the contents of the Region within the overall extent of the Source,
698 without changing the Region's position or length
701 if (_start != pos) {
703 if (!verify_start (pos)) {
704 return;
707 _start = pos;
708 _whole_file = false;
709 first_edit ();
710 invalidate_transients ();
712 send_change (Properties::start);
716 void
717 Region::trim_start (framepos_t new_position, void */*src*/)
719 if (locked() || position_locked()) {
720 return;
722 framepos_t new_start;
723 frameoffset_t start_shift;
725 if (new_position > _position) {
726 start_shift = new_position - _position;
727 } else {
728 start_shift = -(_position - new_position);
731 if (start_shift > 0) {
733 if (_start > max_frames - start_shift) {
734 new_start = max_frames;
735 } else {
736 new_start = _start + start_shift;
739 if (!verify_start (new_start)) {
740 return;
743 } else if (start_shift < 0) {
745 if (_start < -start_shift) {
746 new_start = 0;
747 } else {
748 new_start = _start + start_shift;
750 } else {
751 return;
754 if (new_start == _start) {
755 return;
758 _start = new_start;
759 _whole_file = false;
760 first_edit ();
762 send_change (Properties::start);
765 void
766 Region::trim_front (framepos_t new_position, void *src)
768 modify_front (new_position, false, src);
771 void
772 Region::cut_front (nframes_t new_position, void *src)
774 modify_front (new_position, true, src);
777 void
778 Region::cut_end (nframes_t new_endpoint, void *src)
780 modify_end (new_endpoint, true, src);
783 void
784 Region::modify_front (nframes_t new_position, bool reset_fade, void *src)
786 if (locked()) {
787 return;
790 nframes_t end = last_frame();
791 nframes_t source_zero;
793 if (_position > _start) {
794 source_zero = _position - _start;
795 } else {
796 source_zero = 0; // its actually negative, but this will work for us
799 if (new_position < end) { /* can't trim it zero or negative length */
801 nframes_t newlen;
803 /* can't trim it back passed where source position zero is located */
805 new_position = max (new_position, source_zero);
807 if (new_position > _position) {
808 newlen = _length - (new_position - _position);
809 } else {
810 newlen = _length + (_position - new_position);
813 trim_to_internal (new_position, newlen, src);
814 if (reset_fade) {
815 _right_of_split = true;
818 if (!property_changes_suspended()) {
819 recompute_at_start ();
824 void
825 Region::modify_end (nframes_t new_endpoint, bool reset_fade, void *src)
827 if (locked()) {
828 return;
831 if (new_endpoint > _position) {
832 trim_to_internal (_position, new_endpoint - _position +1, this);
833 if (reset_fade) {
834 _left_of_split = true;
836 if (!property_changes_suspended()) {
837 recompute_at_end ();
842 /** @param new_endpoint New region end point, such that, for example,
843 * a region at 0 of length 10 has an endpoint of 9.
846 void
847 Region::trim_end (framepos_t new_endpoint, void* src)
849 modify_end (new_endpoint, false, src);
852 void
853 Region::trim_to (framepos_t position, framecnt_t length, void *src)
855 if (locked()) {
856 return;
859 trim_to_internal (position, length, src);
861 if (!property_changes_suspended()) {
862 recompute_at_start ();
863 recompute_at_end ();
867 void
868 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
870 frameoffset_t start_shift;
871 framepos_t new_start;
873 if (locked()) {
874 return;
877 if (position > _position) {
878 start_shift = position - _position;
879 } else {
880 start_shift = -(_position - position);
883 if (start_shift > 0) {
885 if (_start > max_frames - start_shift) {
886 new_start = max_frames;
887 } else {
888 new_start = _start + start_shift;
892 } else if (start_shift < 0) {
894 if (_start < -start_shift) {
895 new_start = 0;
896 } else {
897 new_start = _start + start_shift;
899 } else {
900 new_start = _start;
903 if (!verify_start_and_length (new_start, length)) {
904 return;
907 PropertyChange what_changed;
909 if (_start != new_start) {
910 _start = new_start;
911 what_changed.add (Properties::start);
913 if (_length != length) {
914 if (!property_changes_suspended()) {
915 _last_length = _length;
917 _length = length;
918 what_changed.add (Properties::length);
920 if (_position != position) {
921 if (!property_changes_suspended()) {
922 _last_position = _position;
924 _position = position;
925 what_changed.add (Properties::position);
928 _whole_file = false;
930 PropertyChange start_and_length;
932 start_and_length.add (Properties::start);
933 start_and_length.add (Properties::length);
935 if (what_changed.contains (start_and_length)) {
936 first_edit ();
939 if (!what_changed.empty()) {
940 send_change (what_changed);
944 void
945 Region::set_hidden (bool yn)
947 if (hidden() != yn) {
948 _hidden = yn;
949 send_change (Properties::hidden);
953 void
954 Region::set_whole_file (bool yn)
956 _whole_file = yn;
957 /* no change signal */
960 void
961 Region::set_automatic (bool yn)
963 _automatic = yn;
964 /* no change signal */
967 void
968 Region::set_muted (bool yn)
970 if (muted() != yn) {
971 _muted = yn;
972 send_change (Properties::muted);
976 void
977 Region::set_opaque (bool yn)
979 if (opaque() != yn) {
980 _opaque = yn;
981 send_change (Properties::opaque);
985 void
986 Region::set_locked (bool yn)
988 if (locked() != yn) {
989 _locked = yn;
990 send_change (Properties::locked);
994 void
995 Region::set_position_locked (bool yn)
997 if (position_locked() != yn) {
998 _position_locked = yn;
999 send_change (Properties::locked);
1003 void
1004 Region::set_sync_position (framepos_t absolute_pos)
1006 framepos_t const file_pos = _start + (absolute_pos - _position);
1008 if (file_pos != _sync_position) {
1009 _sync_marked = true;
1010 _sync_position = file_pos;
1011 if (!property_changes_suspended()) {
1012 maybe_uncopy ();
1014 send_change (Properties::sync_position);
1018 void
1019 Region::clear_sync_position ()
1021 if (sync_marked()) {
1022 _sync_marked = false;
1023 if (!property_changes_suspended()) {
1024 maybe_uncopy ();
1026 send_change (Properties::sync_position);
1030 framepos_t
1031 Region::sync_offset (int& dir) const
1033 /* returns the sync point relative the first frame of the region */
1035 if (sync_marked()) {
1036 if (_sync_position > _start) {
1037 dir = 1;
1038 return _sync_position - _start;
1039 } else {
1040 dir = -1;
1041 return _start - _sync_position;
1043 } else {
1044 dir = 0;
1045 return 0;
1049 framepos_t
1050 Region::adjust_to_sync (framepos_t pos) const
1052 int sync_dir;
1053 frameoffset_t offset = sync_offset (sync_dir);
1055 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1057 if (sync_dir > 0) {
1058 if (pos > offset) {
1059 pos -= offset;
1060 } else {
1061 pos = 0;
1063 } else {
1064 if (max_frames - pos > offset) {
1065 pos += offset;
1069 return pos;
1072 framepos_t
1073 Region::sync_position() const
1075 if (sync_marked()) {
1076 return _sync_position;
1077 } else {
1078 return _start;
1082 void
1083 Region::raise ()
1085 boost::shared_ptr<Playlist> pl (playlist());
1086 if (pl) {
1087 pl->raise_region (shared_from_this ());
1091 void
1092 Region::lower ()
1094 boost::shared_ptr<Playlist> pl (playlist());
1095 if (pl) {
1096 pl->lower_region (shared_from_this ());
1101 void
1102 Region::raise_to_top ()
1104 boost::shared_ptr<Playlist> pl (playlist());
1105 if (pl) {
1106 pl->raise_region_to_top (shared_from_this());
1110 void
1111 Region::lower_to_bottom ()
1113 boost::shared_ptr<Playlist> pl (playlist());
1114 if (pl) {
1115 pl->lower_region_to_bottom (shared_from_this());
1119 void
1120 Region::set_layer (layer_t l)
1122 if (_layer != l) {
1123 _layer = l;
1125 send_change (Properties::layer);
1129 XMLNode&
1130 Region::state (bool full)
1132 XMLNode *node = new XMLNode ("Region");
1133 char buf[64];
1134 char buf2[64];
1135 LocaleGuard lg (X_("POSIX"));
1136 const char* fe = NULL;
1138 add_properties (*node);
1140 _id.print (buf, sizeof (buf));
1141 node->add_property ("id", buf);
1142 node->add_property ("type", _type.to_string());
1144 switch (_first_edit) {
1145 case EditChangesNothing:
1146 fe = X_("nothing");
1147 break;
1148 case EditChangesName:
1149 fe = X_("name");
1150 break;
1151 case EditChangesID:
1152 fe = X_("id");
1153 break;
1154 default: /* should be unreachable but makes g++ happy */
1155 fe = X_("nothing");
1156 break;
1159 node->add_property ("first-edit", fe);
1161 /* note: flags are stored by derived classes */
1163 if (_positional_lock_style != AudioTime) {
1164 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1165 stringstream str;
1166 str << _bbt_time;
1167 node->add_property ("bbt-position", str.str());
1170 for (uint32_t n=0; n < _sources.size(); ++n) {
1171 snprintf (buf2, sizeof(buf2), "source-%d", n);
1172 _sources[n]->id().print (buf, sizeof(buf));
1173 node->add_property (buf2, buf);
1176 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1177 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1178 _master_sources[n]->id().print (buf, sizeof (buf));
1179 node->add_property (buf2, buf);
1182 if (full && _extra_xml) {
1183 node->add_child_copy (*_extra_xml);
1186 return *node;
1189 XMLNode&
1190 Region::get_state ()
1192 return state (true);
1196 Region::set_state (const XMLNode& node, int version)
1198 PropertyChange what_changed;
1199 return _set_state (node, version, what_changed, true);
1203 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1205 const XMLProperty* prop;
1207 what_changed = set_properties (node);
1209 if ((prop = node.property (X_("id")))) {
1210 _id = prop->value();
1213 if ((prop = node.property ("positional-lock-style")) != 0) {
1214 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1216 if (_positional_lock_style == MusicTime) {
1217 if ((prop = node.property ("bbt-position")) == 0) {
1218 /* missing BBT info, revert to audio time locking */
1219 _positional_lock_style = AudioTime;
1220 } else {
1221 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1222 &_bbt_time.bars,
1223 &_bbt_time.beats,
1224 &_bbt_time.ticks) != 3) {
1225 _positional_lock_style = AudioTime;
1232 /* fix problems with old sessions corrupted by impossible
1233 values for _stretch or _shift
1235 if (_stretch == 0.0f) {
1236 _stretch = 1.0f;
1239 if (_shift == 0.0f) {
1240 _shift = 1.0f;
1243 const XMLNodeList& nlist = node.children();
1245 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1247 XMLNode *child;
1249 child = (*niter);
1251 if (child->name () == "Extra") {
1252 delete _extra_xml;
1253 _extra_xml = new XMLNode (*child);
1254 break;
1258 if (send) {
1259 send_change (what_changed);
1262 /* Quick fix for 2.x sessions when region is muted */
1263 if ((prop = node.property (X_("flags")))) {
1264 if (string::npos != prop->value().find("Muted")){
1265 set_muted (true);
1270 return 0;
1273 void
1274 Region::suspend_property_changes ()
1276 Stateful::suspend_property_changes ();
1277 _last_length = _length;
1278 _last_position = _position;
1281 void
1282 Region::mid_thaw (const PropertyChange& what_changed)
1284 if (what_changed.contains (Properties::length)) {
1285 if (what_changed.contains (Properties::position)) {
1286 recompute_at_start ();
1288 recompute_at_end ();
1292 void
1293 Region::send_change (const PropertyChange& what_changed)
1295 if (what_changed.empty()) {
1296 return;
1299 Stateful::send_change (what_changed);
1301 if (!_no_property_changes) {
1303 /* Try and send a shared_pointer unless this is part of the constructor.
1304 If so, do nothing.
1307 try {
1308 boost::shared_ptr<Region> rptr = shared_from_this();
1309 RegionPropertyChanged (rptr, what_changed);
1310 } catch (...) {
1311 /* no shared_ptr available, relax; */
1316 void
1317 Region::set_last_layer_op (uint64_t when)
1319 _last_layer_op = when;
1322 bool
1323 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1325 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1328 bool
1329 Region::equivalent (boost::shared_ptr<const Region> other) const
1331 return _start == other->_start &&
1332 _position == other->_position &&
1333 _length == other->_length;
1336 bool
1337 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1339 return _start == other->_start &&
1340 _length == other->_length;
1343 bool
1344 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1346 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1349 void
1350 Region::source_deleted (boost::weak_ptr<Source>)
1352 drop_sources ();
1354 if (!_session.deletion_in_progress()) {
1355 /* this is a very special case: at least one of the region's
1356 sources has bee deleted, so invalidate all references to
1357 ourselves. Do NOT do this during session deletion, because
1358 then we run the risk that this will actually result
1359 in this object being deleted (as refcnt goes to zero)
1360 while emitting DropReferences.
1363 drop_references ();
1367 vector<string>
1368 Region::master_source_names ()
1370 SourceList::iterator i;
1372 vector<string> names;
1373 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1374 names.push_back((*i)->name());
1377 return names;
1380 void
1381 Region::set_master_sources (const SourceList& srcs)
1383 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1384 cerr << name() << " " << id() << " DEC M SMS\n";
1385 (*i)->dec_use_count ();
1388 _master_sources = srcs;
1389 assert (_sources.size() == _master_sources.size());
1391 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1392 (*i)->inc_use_count ();
1396 bool
1397 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1399 if (!other)
1400 return false;
1402 SourceList::const_iterator i;
1403 SourceList::const_iterator io;
1405 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1406 if ((*i)->id() != (*io)->id()) {
1407 return false;
1411 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1412 if ((*i)->id() != (*io)->id()) {
1413 return false;
1417 return true;
1420 bool
1421 Region::uses_source (boost::shared_ptr<const Source> source) const
1423 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1424 if (*i == source) {
1425 return true;
1428 return false;
1431 sframes_t
1432 Region::source_length(uint32_t n) const
1434 assert (n < _sources.size());
1435 return _sources[n]->length(_position - _start);
1438 bool
1439 Region::verify_length (framecnt_t len)
1441 if (source() && (source()->destructive() || source()->length_mutable())) {
1442 return true;
1445 framecnt_t maxlen = 0;
1447 for (uint32_t n = 0; n < _sources.size(); ++n) {
1448 maxlen = max (maxlen, source_length(n) - _start);
1451 len = min (len, maxlen);
1453 return true;
1456 bool
1457 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1459 if (source() && (source()->destructive() || source()->length_mutable())) {
1460 return true;
1463 framecnt_t maxlen = 0;
1465 for (uint32_t n = 0; n < _sources.size(); ++n) {
1466 maxlen = max (maxlen, source_length(n) - new_start);
1469 new_length = min (new_length, maxlen);
1471 return true;
1474 bool
1475 Region::verify_start (framepos_t pos)
1477 if (source() && (source()->destructive() || source()->length_mutable())) {
1478 return true;
1481 for (uint32_t n = 0; n < _sources.size(); ++n) {
1482 if (pos > source_length(n) - _length) {
1483 return false;
1486 return true;
1489 bool
1490 Region::verify_start_mutable (framepos_t& new_start)
1492 if (source() && (source()->destructive() || source()->length_mutable())) {
1493 return true;
1496 for (uint32_t n = 0; n < _sources.size(); ++n) {
1497 if (new_start > source_length(n) - _length) {
1498 new_start = source_length(n) - _length;
1501 return true;
1504 boost::shared_ptr<Region>
1505 Region::get_parent() const
1507 boost::shared_ptr<Playlist> pl (playlist());
1509 if (pl) {
1510 boost::shared_ptr<Region> r;
1511 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1513 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1514 return boost::static_pointer_cast<Region> (r);
1518 return boost::shared_ptr<Region>();
1522 Region::apply (Filter& filter)
1524 return filter.run (shared_from_this());
1528 void
1529 Region::invalidate_transients ()
1531 _valid_transients = false;
1532 _transients.clear ();
1535 void
1536 Region::drop_sources ()
1538 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1539 cerr << name() << " " << id() << " DEC DS\n";
1540 (*i)->dec_use_count ();
1543 _sources.clear ();
1545 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1546 cerr << name() << " " << id() << " DEC MDS \n";
1547 (*i)->dec_use_count ();
1550 _master_sources.clear ();
1553 void
1554 Region::use_sources (SourceList const & s)
1556 set<boost::shared_ptr<Source> > unique_srcs;
1558 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1560 _sources.push_back (*i);
1561 (*i)->inc_use_count ();
1562 _master_sources.push_back (*i);
1563 (*i)->inc_use_count ();
1565 /* connect only once to DropReferences, even if sources are replicated
1568 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1569 unique_srcs.insert (*i);
1570 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1575 PropertyList*
1576 Region::property_factory (const XMLNode& history_node) const
1578 PropertyList* prop_list = new PropertyList;
1580 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
1581 PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node);
1583 if (prop) {
1584 prop_list->add (prop);
1588 return prop_list;