2 * Copyright (C) 2000-2018 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
4 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
6 * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
7 * Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
8 * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <glibmm/threads.h>
34 #include "pbd/types_convert.h"
35 #include "pbd/xml++.h"
37 #include "ardour/debug.h"
38 #include "ardour/filter.h"
39 #include "ardour/playlist.h"
40 #include "ardour/playlist_source.h"
41 #include "ardour/profile.h"
42 #include "ardour/region.h"
43 #include "ardour/region_factory.h"
44 #include "ardour/session.h"
45 #include "ardour/source.h"
46 #include "ardour/tempo.h"
47 #include "ardour/transient_detector.h"
48 #include "ardour/types_convert.h"
51 using namespace ARDOUR;
56 namespace Properties {
57 PBD::PropertyDescriptor<bool> muted;
58 PBD::PropertyDescriptor<bool> opaque;
59 PBD::PropertyDescriptor<bool> locked;
60 PBD::PropertyDescriptor<bool> video_locked;
61 PBD::PropertyDescriptor<bool> automatic;
62 PBD::PropertyDescriptor<bool> whole_file;
63 PBD::PropertyDescriptor<bool> import;
64 PBD::PropertyDescriptor<bool> external;
65 PBD::PropertyDescriptor<bool> sync_marked;
66 PBD::PropertyDescriptor<bool> left_of_split;
67 PBD::PropertyDescriptor<bool> right_of_split;
68 PBD::PropertyDescriptor<bool> hidden;
69 PBD::PropertyDescriptor<bool> position_locked;
70 PBD::PropertyDescriptor<bool> valid_transients;
71 PBD::PropertyDescriptor<samplepos_t> start;
72 PBD::PropertyDescriptor<samplecnt_t> length;
73 PBD::PropertyDescriptor<samplepos_t> position;
74 PBD::PropertyDescriptor<double> beat;
75 PBD::PropertyDescriptor<samplecnt_t> sync_position;
76 PBD::PropertyDescriptor<layer_t> layer;
77 PBD::PropertyDescriptor<samplepos_t> ancestral_start;
78 PBD::PropertyDescriptor<samplecnt_t> ancestral_length;
79 PBD::PropertyDescriptor<float> stretch;
80 PBD::PropertyDescriptor<float> shift;
81 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
82 PBD::PropertyDescriptor<uint64_t> layering_index;
83 PBD::PropertyDescriptor<std::string> tags;
87 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
90 Region::make_property_quarks ()
92 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
94 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
96 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
98 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
100 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
102 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
104 Properties::import.property_id = g_quark_from_static_string (X_("import"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
106 Properties::external.property_id = g_quark_from_static_string (X_("external"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
108 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
110 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
112 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
114 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
116 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
118 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
120 Properties::start.property_id = g_quark_from_static_string (X_("start"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
122 Properties::length.property_id = g_quark_from_static_string (X_("length"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
124 Properties::position.property_id = g_quark_from_static_string (X_("position"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
126 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
128 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
130 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
132 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
134 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
136 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
137 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
138 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
139 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
140 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
141 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
142 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
143 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
144 Properties::tags.property_id = g_quark_from_static_string (X_("tags"));
145 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for tags = %1\n", Properties::tags.property_id));
149 Region::register_properties ()
151 _xml_node_name = X_("Region");
153 add_property (_muted);
154 add_property (_opaque);
155 add_property (_locked);
156 add_property (_video_locked);
157 add_property (_automatic);
158 add_property (_whole_file);
159 add_property (_import);
160 add_property (_external);
161 add_property (_sync_marked);
162 add_property (_left_of_split);
163 add_property (_right_of_split);
164 add_property (_hidden);
165 add_property (_position_locked);
166 add_property (_valid_transients);
167 add_property (_start);
168 add_property (_length);
169 add_property (_position);
170 add_property (_beat);
171 add_property (_sync_position);
172 add_property (_ancestral_start);
173 add_property (_ancestral_length);
174 add_property (_stretch);
175 add_property (_shift);
176 add_property (_position_lock_style);
177 add_property (_layering_index);
178 add_property (_tags);
181 #define REGION_DEFAULT_STATE(s,l) \
182 _sync_marked (Properties::sync_marked, false) \
183 , _left_of_split (Properties::left_of_split, false) \
184 , _right_of_split (Properties::right_of_split, false) \
185 , _valid_transients (Properties::valid_transients, false) \
186 , _start (Properties::start, (s)) \
187 , _length (Properties::length, (l)) \
188 , _position (Properties::position, 0) \
189 , _beat (Properties::beat, 0.0) \
190 , _sync_position (Properties::sync_position, (s)) \
191 , _quarter_note (0.0) \
192 , _transient_user_start (0) \
193 , _transient_analysis_start (0) \
194 , _transient_analysis_end (0) \
195 , _soloSelected (false) \
196 , _muted (Properties::muted, false) \
197 , _opaque (Properties::opaque, true) \
198 , _locked (Properties::locked, false) \
199 , _video_locked (Properties::video_locked, false) \
200 , _automatic (Properties::automatic, false) \
201 , _whole_file (Properties::whole_file, false) \
202 , _import (Properties::import, false) \
203 , _external (Properties::external, false) \
204 , _hidden (Properties::hidden, false) \
205 , _position_locked (Properties::position_locked, false) \
206 , _ancestral_start (Properties::ancestral_start, (s)) \
207 , _ancestral_length (Properties::ancestral_length, (l)) \
208 , _stretch (Properties::stretch, 1.0) \
209 , _shift (Properties::shift, 1.0) \
210 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
211 , _layering_index (Properties::layering_index, 0) \
212 , _tags (Properties::tags, "")
214 #define REGION_COPY_STATE(other) \
215 _sync_marked (Properties::sync_marked, other->_sync_marked) \
216 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
217 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
218 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
219 , _start(Properties::start, other->_start) \
220 , _length(Properties::length, other->_length) \
221 , _position(Properties::position, other->_position) \
222 , _beat (Properties::beat, other->_beat) \
223 , _sync_position(Properties::sync_position, other->_sync_position) \
224 , _quarter_note (other->_quarter_note) \
225 , _user_transients (other->_user_transients) \
226 , _transient_user_start (other->_transient_user_start) \
227 , _transients (other->_transients) \
228 , _transient_analysis_start (other->_transient_analysis_start) \
229 , _transient_analysis_end (other->_transient_analysis_end) \
230 , _soloSelected (false) \
231 , _muted (Properties::muted, other->_muted) \
232 , _opaque (Properties::opaque, other->_opaque) \
233 , _locked (Properties::locked, other->_locked) \
234 , _video_locked (Properties::video_locked, other->_video_locked) \
235 , _automatic (Properties::automatic, other->_automatic) \
236 , _whole_file (Properties::whole_file, other->_whole_file) \
237 , _import (Properties::import, other->_import) \
238 , _external (Properties::external, other->_external) \
239 , _hidden (Properties::hidden, other->_hidden) \
240 , _position_locked (Properties::position_locked, other->_position_locked) \
241 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
242 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
243 , _stretch (Properties::stretch, other->_stretch) \
244 , _shift (Properties::shift, other->_shift) \
245 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
246 , _layering_index (Properties::layering_index, other->_layering_index) \
247 , _tags (Properties::tags, other->_tags)
249 /* derived-from-derived constructor (no sources in constructor) */
250 Region::Region (Session& s, samplepos_t start, samplecnt_t length, const string& name, DataType type)
251 : SessionObject(s, name)
253 , REGION_DEFAULT_STATE(start,length)
254 , _last_length (length)
256 , _first_edit (EditChangesNothing)
259 register_properties ();
261 /* no sources at this point */
264 /** Basic Region constructor (many sources) */
265 Region::Region (const SourceList& srcs)
266 : SessionObject(srcs.front()->session(), "toBeRenamed")
267 , _type (srcs.front()->type())
268 , REGION_DEFAULT_STATE(0,0)
271 , _first_edit (EditChangesNothing)
274 register_properties ();
276 _type = srcs.front()->type();
280 assert(_sources.size() > 0);
281 assert (_type == srcs.front()->type());
284 /** Create a new Region from an existing one */
285 Region::Region (boost::shared_ptr<const Region> other)
286 : SessionObject(other->session(), other->name())
287 , _type (other->data_type())
288 , REGION_COPY_STATE (other)
289 , _last_length (other->_last_length)
290 , _last_position(other->_last_position) \
291 , _first_edit (EditChangesNothing)
292 , _layer (other->_layer)
294 register_properties ();
296 /* override state that may have been incorrectly inherited from the other region
299 _position = other->_position;
304 use_sources (other->_sources);
305 set_master_sources (other->_master_sources);
307 _position_lock_style = other->_position_lock_style;
308 _first_edit = other->_first_edit;
310 _start = other->_start;
311 _beat = other->_beat;
312 _quarter_note = other->_quarter_note;
314 /* sync pos is relative to start of file. our start-in-file is now zero,
315 * so set our sync position to whatever the the difference between
316 * _start and _sync_pos was in the other region.
318 * result is that our new sync pos points to the same point in our source(s)
319 * as the sync in the other region did in its source(s).
321 * since we start at zero in our source(s), it is not possible to use a sync point that
322 * is before the start. reset it to _start if that was true in the other region.
325 if (other->sync_marked()) {
326 if (other->_start < other->_sync_position) {
327 /* sync pos was after the start point of the other region */
328 _sync_position = other->_sync_position - other->_start;
330 /* sync pos was before the start point of the other region. not possible here. */
331 _sync_marked = false;
332 _sync_position = _start;
335 _sync_marked = false;
336 _sync_position = _start;
339 assert (_type == other->data_type());
342 /** Create a new Region from part of an existing one.
344 * the start within \a other is given by \a offset
345 * (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
347 Region::Region (boost::shared_ptr<const Region> other, MusicSample offset)
348 : SessionObject(other->session(), other->name())
349 , _type (other->data_type())
350 , REGION_COPY_STATE (other)
351 , _last_length (other->_last_length)
352 , _last_position(other->_last_position) \
353 , _first_edit (EditChangesNothing)
354 , _layer (other->_layer)
356 register_properties ();
358 /* override state that may have been incorrectly inherited from the other region
365 use_sources (other->_sources);
366 set_master_sources (other->_master_sources);
368 _position = other->_position + offset.sample;
369 _start = other->_start + offset.sample;
371 /* prevent offset of 0 from altering musical position */
372 if (offset.sample != 0) {
373 const double offset_qn = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division)
374 - other->_quarter_note;
376 _quarter_note = other->_quarter_note + offset_qn;
377 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
379 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
382 /* if the other region had a distinct sync point
383 * set, then continue to use it as best we can.
384 * otherwise, reset sync point back to start.
387 if (other->sync_marked()) {
388 if (other->_sync_position < _start) {
389 _sync_marked = false;
390 _sync_position = _start;
392 _sync_position = other->_sync_position;
395 _sync_marked = false;
396 _sync_position = _start;
399 assert (_type == other->data_type());
402 /** Create a copy of @param other but with different sources. Used by filters */
403 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
404 : SessionObject (other->session(), other->name())
405 , _type (srcs.front()->type())
406 , REGION_COPY_STATE (other)
407 , _last_length (other->_last_length)
408 , _last_position (other->_last_position)
409 , _first_edit (EditChangesID)
410 , _layer (other->_layer)
412 register_properties ();
415 _position_locked = false;
417 other->_first_edit = EditChangesName;
419 if (other->_extra_xml) {
420 _extra_xml = new XMLNode (*other->_extra_xml);
426 assert(_sources.size() > 0);
431 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
436 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
438 _playlist = wpl.lock();
442 Region::set_name (const std::string& str)
445 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
446 assert(_name == str);
448 send_change (Properties::name);
455 Region::set_selected_for_solo(bool yn)
457 if (_soloSelected != yn) {
459 boost::shared_ptr<Playlist> pl (playlist());
462 pl->AddToSoloSelectedList(this);
464 pl->RemoveFromSoloSelectedList(this);
474 Region::set_length (samplecnt_t len, const int32_t sub_num)
476 //cerr << "Region::set_length() len = " << len << endl;
481 if (_length != len && len != 0) {
483 /* check that the current _position wouldn't make the new
487 if (max_samplepos - len < _position) {
491 if (!verify_length (len)) {
496 set_length_internal (len, sub_num);
500 maybe_invalidate_transients ();
502 if (!property_changes_suspended()) {
506 send_change (Properties::length);
511 Region::set_length_internal (samplecnt_t len, const int32_t sub_num)
513 _last_length = _length;
518 Region::maybe_uncopy ()
520 /* this does nothing but marked a semantic moment once upon a time */
524 Region::first_edit ()
526 boost::shared_ptr<Playlist> pl (playlist());
528 if (_first_edit != EditChangesNothing && pl) {
530 _name = RegionFactory::new_region_name (_name);
531 _first_edit = EditChangesNothing;
533 send_change (Properties::name);
535 RegionFactory::CheckNewRegion (shared_from_this());
540 Region::at_natural_position () const
542 boost::shared_ptr<Playlist> pl (playlist());
548 boost::shared_ptr<Region> whole_file_region = get_parent();
550 if (whole_file_region) {
551 if (_position == whole_file_region->position() + _start) {
560 Region::move_to_natural_position ()
562 boost::shared_ptr<Playlist> pl (playlist());
568 boost::shared_ptr<Region> whole_file_region = get_parent();
570 if (whole_file_region) {
571 set_position (whole_file_region->position() + _start);
576 Region::special_set_position (samplepos_t pos)
578 /* this is used when creating a whole file region as
579 * a way to store its "natural" or "captured" position.
582 _position = _position;
587 Region::set_position_lock_style (PositionLockStyle ps)
589 if (_position_lock_style != ps) {
591 boost::shared_ptr<Playlist> pl (playlist());
593 _position_lock_style = ps;
595 send_change (Properties::position_lock_style);
600 Region::update_after_tempo_map_change (bool send)
602 boost::shared_ptr<Playlist> pl (playlist());
608 if (_position_lock_style == AudioTime) {
609 /* don't signal as the actual position has not chnged */
610 recompute_position_from_lock_style (0);
614 /* prevent movement before 0 */
615 const samplepos_t pos = max ((samplepos_t) 0, _session.tempo_map().sample_at_beat (_beat));
616 /* we have _beat. update sample position non-musically */
617 set_position_internal (pos, false, 0);
619 /* do this even if the position is the same. this helps out
620 * a GUI that has moved its representation already.
624 send_change (Properties::position);
629 Region::set_position (samplepos_t pos, int32_t sub_num)
635 /* do this even if the position is the same. this helps out
636 * a GUI that has moved its representation already.
638 PropertyChange p_and_l;
640 p_and_l.add (Properties::position);
642 if (position_lock_style() == AudioTime) {
643 set_position_internal (pos, true, sub_num);
645 if (!_session.loading()) {
646 _beat = _session.tempo_map().exact_beat_at_sample (pos, sub_num);
647 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
650 set_position_internal (pos, false, sub_num);
653 if (position_lock_style() == MusicTime) {
654 p_and_l.add (Properties::length);
657 send_change (p_and_l);
662 Region::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
664 /* We emit a change of Properties::position even if the position hasn't changed
665 * (see Region::set_position), so we must always set this up so that
666 * e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
668 _last_position = _position;
670 if (_position != pos) {
673 if (allow_bbt_recompute) {
674 recompute_position_from_lock_style (sub_num);
676 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
677 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
680 /* check that the new _position wouldn't make the current
681 * length impossible - if so, change the length.
683 * XXX is this the right thing to do?
685 if (max_samplepos - _length < _position) {
686 _last_length = _length;
687 _length = max_samplepos - _position;
693 Region::set_position_music (double qn)
699 /* do this even if the position is the same. this helps out
700 * a GUI that has moved its representation already.
702 PropertyChange p_and_l;
704 p_and_l.add (Properties::position);
706 if (!_session.loading()) {
707 _beat = _session.tempo_map().beat_at_quarter_note (qn);
710 /* will set sample accordingly */
711 set_position_music_internal (qn);
713 if (position_lock_style() == MusicTime) {
714 p_and_l.add (Properties::length);
717 send_change (p_and_l);
721 Region::set_position_music_internal (double qn)
723 /* We emit a change of Properties::position even if the position hasn't changed
724 * (see Region::set_position), so we must always set this up so that
725 * e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
727 _last_position = _position;
729 if (_quarter_note != qn) {
730 _position = _session.tempo_map().sample_at_quarter_note (qn);
733 /* check that the new _position wouldn't make the current
734 * length impossible - if so, change the length.
736 * XXX is this the right thing to do?
738 if (max_samplepos - _length < _position) {
739 _last_length = _length;
740 _length = max_samplepos - _position;
745 /** A gui may need to create a region, then place it in an initial
746 * position determined by the user.
747 * When this takes place within one gui operation, we have to reset
748 * _last_position to prevent an implied move.
751 Region::set_initial_position (samplepos_t pos)
757 if (_position != pos) {
760 /* check that the new _position wouldn't make the current
761 * length impossible - if so, change the length.
763 * XXX is this the right thing to do?
766 if (max_samplepos - _length < _position) {
767 _last_length = _length;
768 _length = max_samplepos - _position;
771 recompute_position_from_lock_style (0);
772 /* ensure that this move doesn't cause a range move */
773 _last_position = _position;
777 /* do this even if the position is the same. this helps out
778 * a GUI that has moved its representation already.
780 send_change (Properties::position);
784 Region::recompute_position_from_lock_style (const int32_t sub_num)
786 _beat = _session.tempo_map().exact_beat_at_sample (_position, sub_num);
787 _quarter_note = _session.tempo_map().exact_qn_at_sample (_position, sub_num);
791 Region::nudge_position (sampleoffset_t n)
793 if (locked() || video_locked()) {
801 samplepos_t new_position = _position;
804 if (_position > max_samplepos - n) {
805 new_position = max_samplepos;
810 if (_position < -n) {
816 /* assumes non-musical nudge */
817 set_position_internal (new_position, true, 0);
819 send_change (Properties::position);
823 Region::set_ancestral_data (samplepos_t s, samplecnt_t l, float st, float sh)
825 _ancestral_length = l;
826 _ancestral_start = s;
832 Region::set_start (samplepos_t pos)
834 if (locked() || position_locked() || video_locked()) {
837 /* This just sets the start, nothing else. It effectively shifts
838 * the contents of the Region within the overall extent of the Source,
839 * without changing the Region's position or length
844 if (!verify_start (pos)) {
848 set_start_internal (pos);
851 maybe_invalidate_transients ();
853 send_change (Properties::start);
858 Region::move_start (sampleoffset_t distance, const int32_t sub_num)
860 if (locked() || position_locked() || video_locked()) {
864 samplepos_t new_start;
868 if (_start > max_samplepos - distance) {
869 new_start = max_samplepos; // makes no sense
871 new_start = _start + distance;
874 if (!verify_start (new_start)) {
878 } else if (distance < 0) {
880 if (_start < -distance) {
883 new_start = _start + distance;
890 if (new_start == _start) {
894 set_start_internal (new_start, sub_num);
899 send_change (Properties::start);
903 Region::trim_front (samplepos_t new_position, const int32_t sub_num)
905 modify_front (new_position, false, sub_num);
909 Region::cut_front (samplepos_t new_position, const int32_t sub_num)
911 modify_front (new_position, true, sub_num);
915 Region::cut_end (samplepos_t new_endpoint, const int32_t sub_num)
917 modify_end (new_endpoint, true, sub_num);
921 Region::modify_front (samplepos_t new_position, bool reset_fade, const int32_t sub_num)
927 samplepos_t end = last_sample();
928 samplepos_t source_zero;
930 if (_position > _start) {
931 source_zero = _position - _start;
933 source_zero = 0; // its actually negative, but this will work for us
936 if (new_position < end) { /* can't trim it zero or negative length */
938 samplecnt_t newlen = 0;
940 if (!can_trim_start_before_source_start ()) {
941 /* can't trim it back past where source position zero is located */
942 new_position = max (new_position, source_zero);
945 if (new_position > _position) {
946 newlen = _length - (new_position - _position);
948 newlen = _length + (_position - new_position);
951 trim_to_internal (new_position, newlen, sub_num);
954 _right_of_split = true;
957 if (!property_changes_suspended()) {
958 recompute_at_start ();
961 maybe_invalidate_transients ();
966 Region::modify_end (samplepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
972 if (new_endpoint > _position) {
973 trim_to_internal (_position, new_endpoint - _position, sub_num);
975 _left_of_split = true;
977 if (!property_changes_suspended()) {
983 /** @param new_endpoint New region end point, such that, for example,
984 * a region at 0 of length 10 has an endpoint of 9.
987 Region::trim_end (samplepos_t new_endpoint, const int32_t sub_num)
989 modify_end (new_endpoint, false, sub_num);
993 Region::trim_to (samplepos_t position, samplecnt_t length, const int32_t sub_num)
999 trim_to_internal (position, length, sub_num);
1001 if (!property_changes_suspended()) {
1002 recompute_at_start ();
1003 recompute_at_end ();
1008 Region::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
1010 samplepos_t new_start;
1016 sampleoffset_t const start_shift = position - _position;
1018 if (start_shift > 0) {
1020 if (_start > max_samplepos - start_shift) {
1021 new_start = max_samplepos;
1023 new_start = _start + start_shift;
1026 } else if (start_shift < 0) {
1028 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
1031 new_start = _start + start_shift;
1038 if (!verify_start_and_length (new_start, length)) {
1042 PropertyChange what_changed;
1044 if (_start != new_start) {
1045 set_start_internal (new_start, sub_num);
1046 what_changed.add (Properties::start);
1050 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1051 * 1. we call set_length_internal; length in beats is computed using the region's current
1052 * (soon-to-be old) position
1053 * 2. we call set_position_internal; position is set and length in samples re-computed using
1054 * length in beats from (1) but at the new position, which is wrong if the region
1055 * straddles a tempo/meter change.
1058 if (_position != position) {
1059 if (!property_changes_suspended()) {
1060 _last_position = _position;
1062 set_position_internal (position, true, sub_num);
1063 what_changed.add (Properties::position);
1066 if (_length != length) {
1067 if (!property_changes_suspended()) {
1068 _last_length = _length;
1070 set_length_internal (length, sub_num);
1071 what_changed.add (Properties::length);
1074 _whole_file = false;
1076 PropertyChange start_and_length;
1078 start_and_length.add (Properties::start);
1079 start_and_length.add (Properties::length);
1081 if (what_changed.contains (start_and_length)) {
1085 if (!what_changed.empty()) {
1086 send_change (what_changed);
1091 Region::set_hidden (bool yn)
1093 if (hidden() != yn) {
1095 send_change (Properties::hidden);
1100 Region::set_whole_file (bool yn)
1103 /* no change signal */
1107 Region::set_automatic (bool yn)
1110 /* no change signal */
1114 Region::set_muted (bool yn)
1116 if (muted() != yn) {
1118 send_change (Properties::muted);
1123 Region::set_opaque (bool yn)
1125 if (opaque() != yn) {
1127 send_change (Properties::opaque);
1132 Region::set_locked (bool yn)
1134 if (locked() != yn) {
1136 send_change (Properties::locked);
1141 Region::set_video_locked (bool yn)
1143 if (video_locked() != yn) {
1145 send_change (Properties::video_locked);
1150 Region::set_position_locked (bool yn)
1152 if (position_locked() != yn) {
1153 _position_locked = yn;
1154 send_change (Properties::locked);
1158 /** Set the region's sync point.
1159 * @param absolute_pos Session time.
1162 Region::set_sync_position (samplepos_t absolute_pos)
1164 /* position within our file */
1165 samplepos_t const file_pos = _start + (absolute_pos - _position);
1167 if (file_pos != _sync_position) {
1168 _sync_marked = true;
1169 _sync_position = file_pos;
1170 if (!property_changes_suspended()) {
1174 send_change (Properties::sync_position);
1179 Region::clear_sync_position ()
1181 if (sync_marked()) {
1182 _sync_marked = false;
1183 if (!property_changes_suspended()) {
1187 send_change (Properties::sync_position);
1191 /* @return the sync point relative the first sample of the region */
1193 Region::sync_offset (int& dir) const
1195 if (sync_marked()) {
1196 if (_sync_position > _start) {
1198 return _sync_position - _start;
1201 return _start - _sync_position;
1210 Region::adjust_to_sync (samplepos_t pos) const
1213 sampleoffset_t offset = sync_offset (sync_dir);
1215 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1224 if (max_samplepos - pos > offset) {
1232 /** @return Sync position in session time */
1234 Region::sync_position() const
1236 if (sync_marked()) {
1237 return _position - _start + _sync_position;
1239 /* if sync has not been marked, use the start of the region */
1247 boost::shared_ptr<Playlist> pl (playlist());
1249 pl->raise_region (shared_from_this ());
1256 boost::shared_ptr<Playlist> pl (playlist());
1258 pl->lower_region (shared_from_this ());
1264 Region::raise_to_top ()
1266 boost::shared_ptr<Playlist> pl (playlist());
1268 pl->raise_region_to_top (shared_from_this());
1273 Region::lower_to_bottom ()
1275 boost::shared_ptr<Playlist> pl (playlist());
1277 pl->lower_region_to_bottom (shared_from_this());
1282 Region::set_layer (layer_t l)
1290 XMLNode *node = new XMLNode ("Region");
1293 /* custom version of 'add_properties (*node);'
1294 * skip values that have have dedicated save functions
1295 * in AudioRegion::state()
1297 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1298 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1299 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1300 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1301 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1302 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1303 i->second->get_value (*node);
1306 node->set_property ("id", id ());
1307 node->set_property ("type", _type);
1311 switch (_first_edit) {
1312 case EditChangesNothing:
1315 case EditChangesName:
1321 default: /* should be unreachable but makes g++ happy */
1326 node->set_property ("first-edit", fe);
1328 /* note: flags are stored by derived classes */
1330 for (uint32_t n=0; n < _sources.size(); ++n) {
1331 snprintf (buf2, sizeof(buf2), "source-%d", n);
1332 node->set_property (buf2, _sources[n]->id());
1335 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1336 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1337 node->set_property (buf2, _master_sources[n]->id ());
1340 /* Only store nested sources for the whole-file region that acts
1341 as the parent/root of all regions using it.
1344 if (_whole_file && max_source_level() > 0) {
1346 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1348 /* region is compound - get its playlist and
1349 store that before we list the region that
1353 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1354 nested_node->add_child_nocopy ((*s)->get_state ());
1358 node->add_child_nocopy (*nested_node);
1363 node->add_child_copy (*_extra_xml);
1370 Region::get_state ()
1376 Region::set_state (const XMLNode& node, int version)
1378 PropertyChange what_changed;
1379 return _set_state (node, version, what_changed, true);
1383 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1385 Timecode::BBT_Time bbt_time;
1387 Stateful::save_extra_xml (node);
1389 what_changed = set_values (node);
1393 if (_position_lock_style == MusicTime) {
1394 std::string bbt_str;
1395 if (node.get_property ("bbt-position", bbt_str)) {
1396 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1399 &bbt_time.ticks) != 3) {
1400 _position_lock_style = AudioTime;
1401 _beat = _session.tempo_map().beat_at_sample (_position);
1403 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1405 /* no position property change for legacy Property, so we do this here */
1406 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1410 /* fix problems with old sessions corrupted by impossible
1411 values for _stretch or _shift
1413 if (_stretch == 0.0f) {
1417 if (_shift == 0.0f) {
1422 send_change (what_changed);
1425 /* Quick fix for 2.x sessions when region is muted */
1427 if (node.get_property (X_("flags"), flags)) {
1428 if (string::npos != flags.find("Muted")){
1433 // saved property is invalid, region-transients are not saved
1434 if (_user_transients.size() == 0){
1435 _valid_transients = false;
1442 Region::suspend_property_changes ()
1444 Stateful::suspend_property_changes ();
1445 _last_length = _length;
1446 _last_position = _position;
1450 Region::mid_thaw (const PropertyChange& what_changed)
1452 if (what_changed.contains (Properties::length)) {
1453 if (what_changed.contains (Properties::position)) {
1454 recompute_at_start ();
1456 recompute_at_end ();
1461 Region::send_change (const PropertyChange& what_changed)
1463 if (what_changed.empty()) {
1467 Stateful::send_change (what_changed);
1469 if (!Stateful::property_changes_suspended()) {
1471 /* Try and send a shared_pointer unless this is part of the constructor.
1476 boost::shared_ptr<Region> rptr = shared_from_this();
1477 RegionPropertyChanged (rptr, what_changed);
1479 /* no shared_ptr available, relax; */
1485 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1487 return coverage (other->first_sample(), other->last_sample()) != Evoral::OverlapNone;
1491 Region::enclosed_equivalent (boost::shared_ptr<const Region> other) const
1493 return (first_sample() >= other->first_sample() && last_sample() <= other->last_sample()) ||
1494 (first_sample() <= other->first_sample() && last_sample() >= other->last_sample()) ;
1498 Region::layer_and_time_equivalent (boost::shared_ptr<const Region> other) const
1500 return _layer == other->_layer &&
1501 _position == other->_position &&
1502 _length == other->_length;
1506 Region::exact_equivalent (boost::shared_ptr<const Region> other) const
1508 return _start == other->_start &&
1509 _position == other->_position &&
1510 _length == other->_length;
1514 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1516 return _start == other->_start &&
1517 _length == other->_length;
1521 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1523 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1527 Region::source_deleted (boost::weak_ptr<Source>)
1531 if (!_session.deletion_in_progress()) {
1532 /* this is a very special case: at least one of the region's
1533 sources has bee deleted, so invalidate all references to
1534 ourselves. Do NOT do this during session deletion, because
1535 then we run the risk that this will actually result
1536 in this object being deleted (as refcnt goes to zero)
1537 while emitting DropReferences.
1545 Region::master_source_names ()
1547 SourceList::iterator i;
1549 vector<string> names;
1550 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1551 names.push_back((*i)->name());
1558 Region::set_master_sources (const SourceList& srcs)
1560 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1561 (*i)->dec_use_count ();
1564 _master_sources = srcs;
1565 assert (_sources.size() == _master_sources.size());
1567 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1568 (*i)->inc_use_count ();
1569 // Source::SourcePropertyChanged( *i );
1574 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1579 if ((_sources.size() != other->_sources.size()) ||
1580 (_master_sources.size() != other->_master_sources.size())) {
1584 SourceList::const_iterator i;
1585 SourceList::const_iterator io;
1587 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1588 if ((*i)->id() != (*io)->id()) {
1593 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1594 if ((*i)->id() != (*io)->id()) {
1603 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1609 SourceList::const_iterator i;
1610 SourceList::const_iterator io;
1612 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1613 if ((*i)->id() == (*io)->id()) {
1622 Region::source_string () const
1624 //string res = itos(_sources.size());
1627 res << _sources.size() << ":";
1629 SourceList::const_iterator i;
1631 for (i = _sources.begin(); i != _sources.end(); ++i) {
1632 res << (*i)->id() << ":";
1635 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1636 res << (*i)->id() << ":";
1643 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1645 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1647 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1650 if (sources.find (ps) == sources.end()) {
1651 /* (Playlist)Source not currently in
1652 accumulating set, so recurse.
1654 ps->playlist()->deep_sources (sources);
1658 /* add this source */
1659 sources.insert (*i);
1662 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1664 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1667 if (sources.find (ps) == sources.end()) {
1668 /* (Playlist)Source not currently in
1669 accumulating set, so recurse.
1671 ps->playlist()->deep_sources (sources);
1675 /* add this source */
1676 sources.insert (*i);
1681 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1683 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1689 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1692 if (ps->playlist()->uses_source (source)) {
1699 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1705 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1708 if (ps->playlist()->uses_source (source)) {
1720 Region::source_length(uint32_t n) const
1722 assert (n < _sources.size());
1723 return _sources[n]->length (_position - _start);
1727 Region::verify_length (samplecnt_t& len)
1729 if (source() && (source()->destructive() || source()->length_mutable())) {
1733 samplecnt_t maxlen = 0;
1735 for (uint32_t n = 0; n < _sources.size(); ++n) {
1736 maxlen = max (maxlen, source_length(n) - _start);
1739 len = min (len, maxlen);
1745 Region::verify_start_and_length (samplepos_t new_start, samplecnt_t& new_length)
1747 if (source() && (source()->destructive() || source()->length_mutable())) {
1751 samplecnt_t maxlen = 0;
1753 for (uint32_t n = 0; n < _sources.size(); ++n) {
1754 maxlen = max (maxlen, source_length(n) - new_start);
1757 new_length = min (new_length, maxlen);
1763 Region::verify_start (samplepos_t pos)
1765 if (source() && (source()->destructive() || source()->length_mutable())) {
1769 for (uint32_t n = 0; n < _sources.size(); ++n) {
1770 if (pos > source_length(n) - _length) {
1778 Region::verify_start_mutable (samplepos_t& new_start)
1780 if (source() && (source()->destructive() || source()->length_mutable())) {
1784 for (uint32_t n = 0; n < _sources.size(); ++n) {
1785 if (new_start > source_length(n) - _length) {
1786 new_start = source_length(n) - _length;
1792 boost::shared_ptr<Region>
1793 Region::get_parent() const
1795 boost::shared_ptr<Playlist> pl (playlist());
1798 boost::shared_ptr<Region> r;
1799 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1801 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1802 return boost::static_pointer_cast<Region> (r);
1806 return boost::shared_ptr<Region>();
1810 Region::apply (Filter& filter, Progress* progress)
1812 return filter.run (shared_from_this(), progress);
1817 Region::maybe_invalidate_transients ()
1819 bool changed = !_onsets.empty();
1822 if (_valid_transients || changed) {
1823 send_change (PropertyChange (Properties::valid_transients));
1829 Region::transients (AnalysisFeatureList& afl)
1831 int cnt = afl.empty() ? 0 : 1;
1833 Region::merge_features (afl, _onsets, _position);
1834 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1835 if (!_onsets.empty ()) {
1838 if (!_user_transients.empty ()) {
1843 // remove exact duplicates
1844 TransientDetector::cleanup_transients (afl, _session.sample_rate(), 0);
1849 Region::has_transients () const
1851 if (!_user_transients.empty ()) {
1852 assert (_valid_transients);
1855 if (!_onsets.empty ()) {
1862 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const sampleoffset_t off) const
1864 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1865 const sampleoffset_t p = (*x) + off;
1866 if (p < first_sample() || p > last_sample()) {
1869 result.push_back (p);
1874 Region::drop_sources ()
1876 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1877 (*i)->dec_use_count ();
1882 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1883 (*i)->dec_use_count ();
1886 _master_sources.clear ();
1890 Region::use_sources (SourceList const & s)
1892 set<boost::shared_ptr<Source> > unique_srcs;
1894 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1896 _sources.push_back (*i);
1897 (*i)->inc_use_count ();
1898 _master_sources.push_back (*i);
1899 (*i)->inc_use_count ();
1901 /* connect only once to DropReferences, even if sources are replicated
1904 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1905 unique_srcs.insert (*i);
1906 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1912 Region::can_trim () const
1914 CanTrim ct = CanTrim (0);
1920 /* if not locked, we can always move the front later, and the end earlier
1923 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1925 if (start() != 0 || can_trim_start_before_source_start ()) {
1926 ct = CanTrim (ct | FrontTrimEarlier);
1929 if (!_sources.empty()) {
1930 if ((start() + length()) < _sources.front()->length (0)) {
1931 ct = CanTrim (ct | EndTrimLater);
1939 Region::max_source_level () const
1943 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1944 lvl = max (lvl, (*i)->level());
1951 Region::is_compound () const
1953 return max_source_level() > 0;
1957 Region::post_set (const PropertyChange& pc)
1959 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1963 Region::set_start_internal (samplecnt_t s, const int32_t sub_num)
1969 Region::earliest_possible_position () const
1971 if (_start > _position) {
1974 return _position - _start;
1979 Region::latest_possible_sample () const
1981 samplecnt_t minlen = max_samplecnt;
1983 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1984 /* non-audio regions have a length that may vary based on their
1985 * position, so we have to pass it in the call.
1987 minlen = min (minlen, (*i)->length (_position));
1990 /* the latest possible last sample is determined by the current
1991 * position, plus the shortest source extent past _start.
1994 return _position + (minlen - _start) - 1;