1 /*
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>
9 *
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.
14 *
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.
19 *
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.
23 */
24
25 #include <iostream>
26 #include <cmath>
27 #include <climits>
28 #include <algorithm>
29 #include <sstream>
30
31 #include <glibmm/threads.h>
32
33 #include "pbd/types_convert.h"
34 #include "pbd/xml++.h"
35
36 #include "ardour/debug.h"
37 #include "ardour/filter.h"
38 #include "ardour/playlist.h"
39 #include "ardour/playlist_source.h"
40 #include "ardour/profile.h"
41 #include "ardour/region.h"
42 #include "ardour/region_factory.h"
43 #include "ardour/session.h"
44 #include "ardour/source.h"
45 #include "ardour/tempo.h"
46 #include "ardour/transient_detector.h"
47 #include "ardour/types_convert.h"
48
49 #include "pbd/i18n.h"
50
51 using namespace std;
52 using namespace ARDOUR;
53 using namespace PBD;
54
55 namespace ARDOUR {
56 class Progress;
57 namespace Properties {
58 PBD::PropertyDescriptor<bool> muted;
59 PBD::PropertyDescriptor<bool> opaque;
60 PBD::PropertyDescriptor<bool> locked;
61 PBD::PropertyDescriptor<bool> video_locked;
62 PBD::PropertyDescriptor<bool> automatic;
63 PBD::PropertyDescriptor<bool> whole_file;
64 PBD::PropertyDescriptor<bool> import;
65 PBD::PropertyDescriptor<bool> external;
66 PBD::PropertyDescriptor<bool> sync_marked;
67 PBD::PropertyDescriptor<bool> left_of_split;
68 PBD::PropertyDescriptor<bool> right_of_split;
69 PBD::PropertyDescriptor<bool> hidden;
70 PBD::PropertyDescriptor<bool> position_locked;
71 PBD::PropertyDescriptor<bool> valid_transients;
72 PBD::PropertyDescriptor<samplepos_t> start;
73 PBD::PropertyDescriptor<samplecnt_t> length;
74 PBD::PropertyDescriptor<samplepos_t> position;
75 PBD::PropertyDescriptor<double> beat;
76 PBD::PropertyDescriptor<samplecnt_t> sync_position;
77 PBD::PropertyDescriptor<layer_t> layer;
78 PBD::PropertyDescriptor<samplepos_t> ancestral_start;
79 PBD::PropertyDescriptor<samplecnt_t> ancestral_length;
80 PBD::PropertyDescriptor<float> stretch;
81 PBD::PropertyDescriptor<float> shift;
82 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
83 PBD::PropertyDescriptor<uint64_t> layering_index;
84 PBD::PropertyDescriptor<std::string> tags;
85 PBD::PropertyDescriptor<bool> contents;
86 }
87 }
88
89 PBD::Signal2<void,boost::shared_ptr<ARDOUR::RegionList>,const PropertyChange&> Region::RegionsPropertyChanged;
90
91 void
make_property_quarks()92 Region::make_property_quarks ()
93 {
94 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
96 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
98 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
100 Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n", Properties::video_locked.property_id));
102 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
104 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
106 Properties::import.property_id = g_quark_from_static_string (X_("import"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
108 Properties::external.property_id = g_quark_from_static_string (X_("external"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
110 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
112 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
114 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
116 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
118 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
120 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
122 Properties::start.property_id = g_quark_from_static_string (X_("start"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
124 Properties::length.property_id = g_quark_from_static_string (X_("length"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
126 Properties::position.property_id = g_quark_from_static_string (X_("position"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
128 Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n", Properties::beat.property_id));
130 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
132 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
134 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
136 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
137 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
138 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
139 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
140 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
141 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
142 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
143 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
144 Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
145 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
146 Properties::tags.property_id = g_quark_from_static_string (X_("tags"));
147 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for tags = %1\n", Properties::tags.property_id));
148 Properties::contents.property_id = g_quark_from_static_string (X_("contents"));
149 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for contents = %1\n", Properties::contents.property_id));
150 }
151
152 void
register_properties()153 Region::register_properties ()
154 {
155 _xml_node_name = X_("Region");
156
157 add_property (_muted);
158 add_property (_opaque);
159 add_property (_locked);
160 add_property (_video_locked);
161 add_property (_automatic);
162 add_property (_whole_file);
163 add_property (_import);
164 add_property (_external);
165 add_property (_sync_marked);
166 add_property (_left_of_split);
167 add_property (_right_of_split);
168 add_property (_hidden);
169 add_property (_position_locked);
170 add_property (_valid_transients);
171 add_property (_start);
172 add_property (_length);
173 add_property (_position);
174 add_property (_beat);
175 add_property (_sync_position);
176 add_property (_ancestral_start);
177 add_property (_ancestral_length);
178 add_property (_stretch);
179 add_property (_shift);
180 add_property (_position_lock_style);
181 add_property (_layering_index);
182 add_property (_tags);
183 add_property (_contents);
184 }
185
186 #define REGION_DEFAULT_STATE(s,l) \
187 _sync_marked (Properties::sync_marked, false) \
188 , _left_of_split (Properties::left_of_split, false) \
189 , _right_of_split (Properties::right_of_split, false) \
190 , _valid_transients (Properties::valid_transients, false) \
191 , _start (Properties::start, (s)) \
192 , _length (Properties::length, (l)) \
193 , _position (Properties::position, 0) \
194 , _beat (Properties::beat, 0.0) \
195 , _sync_position (Properties::sync_position, (s)) \
196 , _quarter_note (0.0) \
197 , _transient_user_start (0) \
198 , _transient_analysis_start (0) \
199 , _transient_analysis_end (0) \
200 , _soloSelected (false) \
201 , _muted (Properties::muted, false) \
202 , _opaque (Properties::opaque, true) \
203 , _locked (Properties::locked, false) \
204 , _video_locked (Properties::video_locked, false) \
205 , _automatic (Properties::automatic, false) \
206 , _whole_file (Properties::whole_file, false) \
207 , _import (Properties::import, false) \
208 , _external (Properties::external, false) \
209 , _hidden (Properties::hidden, false) \
210 , _position_locked (Properties::position_locked, false) \
211 , _ancestral_start (Properties::ancestral_start, (s)) \
212 , _ancestral_length (Properties::ancestral_length, (l)) \
213 , _stretch (Properties::stretch, 1.0) \
214 , _shift (Properties::shift, 1.0) \
215 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
216 , _layering_index (Properties::layering_index, 0) \
217 , _tags (Properties::tags, "") \
218 , _contents (Properties::contents, false)
219
220 #define REGION_COPY_STATE(other) \
221 _sync_marked (Properties::sync_marked, other->_sync_marked) \
222 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
223 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
224 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
225 , _start(Properties::start, other->_start) \
226 , _length(Properties::length, other->_length) \
227 , _position(Properties::position, other->_position) \
228 , _beat (Properties::beat, other->_beat) \
229 , _sync_position(Properties::sync_position, other->_sync_position) \
230 , _quarter_note (other->_quarter_note) \
231 , _user_transients (other->_user_transients) \
232 , _transient_user_start (other->_transient_user_start) \
233 , _transients (other->_transients) \
234 , _transient_analysis_start (other->_transient_analysis_start) \
235 , _transient_analysis_end (other->_transient_analysis_end) \
236 , _soloSelected (false) \
237 , _muted (Properties::muted, other->_muted) \
238 , _opaque (Properties::opaque, other->_opaque) \
239 , _locked (Properties::locked, other->_locked) \
240 , _video_locked (Properties::video_locked, other->_video_locked) \
241 , _automatic (Properties::automatic, other->_automatic) \
242 , _whole_file (Properties::whole_file, other->_whole_file) \
243 , _import (Properties::import, other->_import) \
244 , _external (Properties::external, other->_external) \
245 , _hidden (Properties::hidden, other->_hidden) \
246 , _position_locked (Properties::position_locked, other->_position_locked) \
247 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
248 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
249 , _stretch (Properties::stretch, other->_stretch) \
250 , _shift (Properties::shift, other->_shift) \
251 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
252 , _layering_index (Properties::layering_index, other->_layering_index) \
253 , _tags (Properties::tags, other->_tags) \
254 , _contents (Properties::contents, other->_contents)
255
256 /* derived-from-derived constructor (no sources in constructor) */
Region(Session & s,samplepos_t start,samplecnt_t length,const string & name,DataType type)257 Region::Region (Session& s, samplepos_t start, samplecnt_t length, const string& name, DataType type)
258 : SessionObject(s, name)
259 , _type(type)
260 , REGION_DEFAULT_STATE(start,length)
261 , _last_length (length)
262 , _last_position (0)
263 , _first_edit (EditChangesNothing)
264 , _layer (0)
265 , _changemap (0)
266 {
267 register_properties ();
268
269 /* no sources at this point */
270 }
271
272 /** Basic Region constructor (many sources) */
Region(const SourceList & srcs)273 Region::Region (const SourceList& srcs)
274 : SessionObject(srcs.front()->session(), "toBeRenamed")
275 , _type (srcs.front()->type())
276 , REGION_DEFAULT_STATE(0,0)
277 , _last_length (0)
278 , _last_position (0)
279 , _first_edit (EditChangesNothing)
280 , _layer (0)
281 , _changemap (0)
282 {
283 register_properties ();
284
285 _type = srcs.front()->type();
286
287 use_sources (srcs);
288
289 assert(_sources.size() > 0);
290 assert (_type == srcs.front()->type());
291 }
292
293 /** Create a new Region from an existing one */
Region(boost::shared_ptr<const Region> other)294 Region::Region (boost::shared_ptr<const Region> other)
295 : SessionObject(other->session(), other->name())
296 , _type (other->data_type())
297 , REGION_COPY_STATE (other)
298 , _last_length (other->_last_length)
299 , _last_position(other->_last_position) \
300 , _first_edit (EditChangesNothing)
301 , _layer (other->_layer)
302 , _changemap (other->_changemap)
303 {
304 register_properties ();
305
306 /* override state that may have been incorrectly inherited from the other region
307 */
308
309 _position = other->_position;
310 _locked = false;
311 _whole_file = false;
312 _hidden = false;
313
314 use_sources (other->_sources);
315 set_master_sources (other->_master_sources);
316
317 _position_lock_style = other->_position_lock_style.val();
318 _first_edit = other->_first_edit;
319
320 _start = other->_start;
321 _beat = other->_beat;
322 _quarter_note = other->_quarter_note;
323
324 /* sync pos is relative to start of file. our start-in-file is now zero,
325 * so set our sync position to whatever the the difference between
326 * _start and _sync_pos was in the other region.
327 *
328 * result is that our new sync pos points to the same point in our source(s)
329 * as the sync in the other region did in its source(s).
330 *
331 * since we start at zero in our source(s), it is not possible to use a sync point that
332 * is before the start. reset it to _start if that was true in the other region.
333 */
334
335 if (other->sync_marked()) {
336 if (other->_start < other->_sync_position) {
337 /* sync pos was after the start point of the other region */
338 _sync_position = other->_sync_position - other->_start;
339 } else {
340 /* sync pos was before the start point of the other region. not possible here. */
341 _sync_marked = false;
342 _sync_position = _start;
343 }
344 } else {
345 _sync_marked = false;
346 _sync_position = _start;
347 }
348
349 assert (_type == other->data_type());
350 }
351
352 /** Create a new Region from part of an existing one.
353 *
354 * the start within \a other is given by \a offset
355 * (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
356 */
Region(boost::shared_ptr<const Region> other,MusicSample offset)357 Region::Region (boost::shared_ptr<const Region> other, MusicSample offset)
358 : SessionObject(other->session(), other->name())
359 , _type (other->data_type())
360 , REGION_COPY_STATE (other)
361 , _last_length (other->_last_length)
362 , _last_position(other->_last_position) \
363 , _first_edit (EditChangesNothing)
364 , _layer (other->_layer)
365 , _changemap (other->_changemap)
366 {
367 register_properties ();
368
369 /* override state that may have been incorrectly inherited from the other region
370 */
371
372 _locked = false;
373 _whole_file = false;
374 _hidden = false;
375
376 use_sources (other->_sources);
377 set_master_sources (other->_master_sources);
378
379 _position = other->_position + offset.sample;
380 _start = other->_start + offset.sample;
381
382 /* prevent offset of 0 from altering musical position */
383 if (offset.sample != 0) {
384 const double offset_qn = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division)
385 - other->_quarter_note;
386
387 _quarter_note = other->_quarter_note + offset_qn;
388 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
389 } else {
390 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
391 }
392
393 /* if the other region had a distinct sync point
394 * set, then continue to use it as best we can.
395 * otherwise, reset sync point back to start.
396 */
397
398 if (other->sync_marked()) {
399 if (other->_sync_position < _start) {
400 _sync_marked = false;
401 _sync_position = _start;
402 } else {
403 _sync_position = other->_sync_position;
404 }
405 } else {
406 _sync_marked = false;
407 _sync_position = _start;
408 }
409
410 assert (_type == other->data_type());
411 }
412
413 /** Create a copy of @param other but with different sources. Used by filters */
Region(boost::shared_ptr<const Region> other,const SourceList & srcs)414 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
415 : SessionObject (other->session(), other->name())
416 , _type (srcs.front()->type())
417 , REGION_COPY_STATE (other)
418 , _last_length (other->_last_length)
419 , _last_position (other->_last_position)
420 , _first_edit (EditChangesID)
421 , _layer (other->_layer)
422 , _changemap (other->_changemap)
423 {
424 register_properties ();
425
426 _locked = false;
427 _position_locked = false;
428
429 other->_first_edit = EditChangesName;
430
431 if (other->_extra_xml) {
432 _extra_xml = new XMLNode (*other->_extra_xml);
433 } else {
434 _extra_xml = 0;
435 }
436
437 use_sources (srcs);
438 assert(_sources.size() > 0);
439 }
440
~Region()441 Region::~Region ()
442 {
443 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
444 drop_sources ();
445 }
446
447 void
set_playlist(boost::weak_ptr<Playlist> wpl)448 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
449 {
450 _playlist = wpl.lock();
451 }
452
453 bool
set_name(const std::string & str)454 Region::set_name (const std::string& str)
455 {
456 if (_name == str) {
457 return true;
458 }
459
460 SessionObject::set_name (str); // EMIT SIGNAL NameChanged()
461 assert (_name == str);
462 send_change (Properties::name);
463 return true;
464 }
465
466 void
set_selected_for_solo(bool yn)467 Region::set_selected_for_solo(bool yn)
468 {
469 if (_soloSelected != yn) {
470
471 boost::shared_ptr<Playlist> pl (playlist());
472 if (pl){
473 if (yn) {
474 pl->AddToSoloSelectedList(this);
475 } else {
476 pl->RemoveFromSoloSelectedList(this);
477 }
478 }
479
480 _soloSelected = yn;
481 }
482 }
483
484 void
set_length(samplecnt_t len,const int32_t sub_num)485 Region::set_length (samplecnt_t len, const int32_t sub_num)
486 {
487 //cerr << "Region::set_length() len = " << len << endl;
488 if (locked()) {
489 return;
490 }
491
492 if (_length != len && len != 0) {
493
494 /* check that the current _position wouldn't make the new
495 * length impossible.
496 */
497
498 if (max_samplepos - len < _position) {
499 return;
500 }
501
502 if (!verify_length (len)) {
503 return;
504 }
505
506
507 set_length_internal (len, sub_num);
508 _whole_file = false;
509 first_edit ();
510 maybe_uncopy ();
511 maybe_invalidate_transients ();
512
513 if (!property_changes_suspended()) {
514 recompute_at_end ();
515 }
516
517 send_change (Properties::length);
518 }
519 }
520
521 void
set_length_internal(samplecnt_t len,const int32_t sub_num)522 Region::set_length_internal (samplecnt_t len, const int32_t sub_num)
523 {
524 _last_length = _length;
525 _length = len;
526 }
527
528 void
maybe_uncopy()529 Region::maybe_uncopy ()
530 {
531 /* this does nothing but marked a semantic moment once upon a time */
532 }
533
534 void
first_edit()535 Region::first_edit ()
536 {
537 boost::shared_ptr<Playlist> pl (playlist());
538
539 if (_first_edit != EditChangesNothing && pl) {
540
541 _name = RegionFactory::new_region_name (_name);
542 _first_edit = EditChangesNothing;
543
544 send_change (Properties::name);
545
546 RegionFactory::CheckNewRegion (shared_from_this());
547 }
548 }
549
550 bool
at_natural_position() const551 Region::at_natural_position () const
552 {
553 boost::shared_ptr<Playlist> pl (playlist());
554
555 if (!pl) {
556 return false;
557 }
558
559 boost::shared_ptr<Region> whole_file_region = get_parent();
560
561 if (whole_file_region) {
562 if (_position == whole_file_region->position() + _start) {
563 return true;
564 }
565 }
566
567 return false;
568 }
569
570 void
move_to_natural_position()571 Region::move_to_natural_position ()
572 {
573 boost::shared_ptr<Playlist> pl (playlist());
574
575 if (!pl) {
576 return;
577 }
578
579 boost::shared_ptr<Region> whole_file_region = get_parent();
580
581 if (whole_file_region) {
582 set_position (whole_file_region->position() + _start);
583 }
584 }
585
586 void
special_set_position(samplepos_t pos)587 Region::special_set_position (samplepos_t pos)
588 {
589 /* this is used when creating a whole file region as
590 * a way to store its "natural" or "captured" position.
591 */
592
593 _position = pos;
594 }
595
596 void
set_position_lock_style(PositionLockStyle ps)597 Region::set_position_lock_style (PositionLockStyle ps)
598 {
599 if (_position_lock_style != ps) {
600
601 boost::shared_ptr<Playlist> pl (playlist());
602
603 _position_lock_style = ps;
604
605 send_change (Properties::position_lock_style);
606 }
607 }
608
609 void
update_after_tempo_map_change(bool send)610 Region::update_after_tempo_map_change (bool send)
611 {
612 boost::shared_ptr<Playlist> pl (playlist());
613
614 if (!pl) {
615 return;
616 }
617
618 if (_position_lock_style == AudioTime) {
619 /* don't signal as the actual position has not chnged */
620 recompute_position_from_lock_style (0);
621 return;
622 }
623
624 /* prevent movement before 0 */
625 const samplepos_t pos = max ((samplepos_t) 0, _session.tempo_map().sample_at_beat (_beat));
626 /* we have _beat. update sample position non-musically */
627 set_position_internal (pos, false, 0);
628
629 /* do this even if the position is the same. this helps out
630 * a GUI that has moved its representation already.
631 */
632
633 if (send) {
634 send_change (Properties::position);
635 }
636 }
637
638 void
set_position(samplepos_t pos,int32_t sub_num)639 Region::set_position (samplepos_t pos, int32_t sub_num)
640 {
641 if (!can_move()) {
642 return;
643 }
644
645 /* do this even if the position is the same. this helps out
646 * a GUI that has moved its representation already.
647 */
648 PropertyChange p_and_l;
649
650 p_and_l.add (Properties::position);
651
652 if (position_lock_style() == AudioTime) {
653 set_position_internal (pos, true, sub_num);
654 } else {
655 if (!_session.loading()) {
656 _beat = _session.tempo_map().exact_beat_at_sample (pos, sub_num);
657 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
658 }
659
660 set_position_internal (pos, false, sub_num);
661 }
662
663 if (position_lock_style() == MusicTime) {
664 p_and_l.add (Properties::length);
665 }
666
667 send_change (p_and_l);
668
669 }
670
671 void
set_position_internal(samplepos_t pos,bool allow_bbt_recompute,const int32_t sub_num)672 Region::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
673 {
674 /* We emit a change of Properties::position even if the position hasn't changed
675 * (see Region::set_position), so we must always set this up so that
676 * e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
677 */
678 _last_position = _position;
679
680 if (_position != pos) {
681 _position = pos;
682
683 if (allow_bbt_recompute) {
684 recompute_position_from_lock_style (sub_num);
685 } else {
686 /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
687 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
688 }
689
690 /* check that the new _position wouldn't make the current
691 * length impossible - if so, change the length.
692 *
693 * XXX is this the right thing to do?
694 */
695 if (max_samplepos - _length < _position) {
696 _last_length = _length;
697 _length = max_samplepos - _position;
698 }
699 }
700 }
701
702 void
set_position_music(double qn)703 Region::set_position_music (double qn)
704 {
705 if (!can_move()) {
706 return;
707 }
708
709 /* do this even if the position is the same. this helps out
710 * a GUI that has moved its representation already.
711 */
712 PropertyChange p_and_l;
713
714 p_and_l.add (Properties::position);
715
716 if (!_session.loading()) {
717 _beat = _session.tempo_map().beat_at_quarter_note (qn);
718 }
719
720 /* will set sample accordingly */
721 set_position_music_internal (qn);
722
723 if (position_lock_style() == MusicTime) {
724 p_and_l.add (Properties::length);
725 }
726
727 send_change (p_and_l);
728 }
729
730 void
set_position_music_internal(double qn)731 Region::set_position_music_internal (double qn)
732 {
733 /* We emit a change of Properties::position even if the position hasn't changed
734 * (see Region::set_position), so we must always set this up so that
735 * e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
736 */
737 _last_position = _position;
738
739 if (_quarter_note != qn) {
740 _position = _session.tempo_map().sample_at_quarter_note (qn);
741 _quarter_note = qn;
742
743 /* check that the new _position wouldn't make the current
744 * length impossible - if so, change the length.
745 *
746 * XXX is this the right thing to do?
747 */
748 if (max_samplepos - _length < _position) {
749 _last_length = _length;
750 _length = max_samplepos - _position;
751 }
752 }
753 }
754
755 /** A gui may need to create a region, then place it in an initial
756 * position determined by the user.
757 * When this takes place within one gui operation, we have to reset
758 * _last_position to prevent an implied move.
759 */
760 void
set_initial_position(samplepos_t pos)761 Region::set_initial_position (samplepos_t pos)
762 {
763 if (!can_move()) {
764 return;
765 }
766
767 if (_position != pos) {
768 _position = pos;
769
770 /* check that the new _position wouldn't make the current
771 * length impossible - if so, change the length.
772 *
773 * XXX is this the right thing to do?
774 */
775
776 if (max_samplepos - _length < _position) {
777 _last_length = _length;
778 _length = max_samplepos - _position;
779 }
780
781 recompute_position_from_lock_style (0);
782 /* ensure that this move doesn't cause a range move */
783 _last_position = _position;
784 }
785
786
787 /* do this even if the position is the same. this helps out
788 * a GUI that has moved its representation already.
789 */
790 send_change (Properties::position);
791 }
792
793 void
recompute_position_from_lock_style(const int32_t sub_num)794 Region::recompute_position_from_lock_style (const int32_t sub_num)
795 {
796 _beat = _session.tempo_map().exact_beat_at_sample (_position, sub_num);
797 _quarter_note = _session.tempo_map().exact_qn_at_sample (_position, sub_num);
798 }
799
800 void
nudge_position(sampleoffset_t n)801 Region::nudge_position (sampleoffset_t n)
802 {
803 if (locked() || video_locked()) {
804 return;
805 }
806
807 if (n == 0) {
808 return;
809 }
810
811 samplepos_t new_position = _position;
812
813 if (n > 0) {
814 if (_position > max_samplepos - n) {
815 new_position = max_samplepos;
816 } else {
817 new_position += n;
818 }
819 } else {
820 if (_position < -n) {
821 new_position = 0;
822 } else {
823 new_position += n;
824 }
825 }
826 /* assumes non-musical nudge */
827 set_position_internal (new_position, true, 0);
828
829 send_change (Properties::position);
830 }
831
832 void
set_ancestral_data(samplepos_t s,samplecnt_t l,float st,float sh)833 Region::set_ancestral_data (samplepos_t s, samplecnt_t l, float st, float sh)
834 {
835 _ancestral_length = l;
836 _ancestral_start = s;
837 _stretch = st;
838 _shift = sh;
839 }
840
841 void
set_start(samplepos_t pos)842 Region::set_start (samplepos_t pos)
843 {
844 if (locked() || position_locked() || video_locked()) {
845 return;
846 }
847 /* This just sets the start, nothing else. It effectively shifts
848 * the contents of the Region within the overall extent of the Source,
849 * without changing the Region's position or length
850 */
851
852 if (_start != pos) {
853
854 if (!verify_start (pos)) {
855 return;
856 }
857
858 set_start_internal (pos);
859 _whole_file = false;
860 first_edit ();
861 maybe_invalidate_transients ();
862
863 send_change (Properties::start);
864 }
865 }
866
867 void
move_start(sampleoffset_t distance,const int32_t sub_num)868 Region::move_start (sampleoffset_t distance, const int32_t sub_num)
869 {
870 if (locked() || position_locked() || video_locked()) {
871 return;
872 }
873
874 samplepos_t new_start;
875
876 if (distance > 0) {
877
878 if (_start > max_samplepos - distance) {
879 new_start = max_samplepos; // makes no sense
880 } else {
881 new_start = _start + distance;
882 }
883
884 if (!verify_start (new_start)) {
885 return;
886 }
887
888 } else if (distance < 0) {
889
890 if (_start < -distance) {
891 new_start = 0;
892 } else {
893 new_start = _start + distance;
894 }
895
896 } else {
897 return;
898 }
899
900 if (new_start == _start) {
901 return;
902 }
903
904 set_start_internal (new_start, sub_num);
905
906 _whole_file = false;
907 first_edit ();
908
909 send_change (Properties::start);
910 }
911
912 void
trim_front(samplepos_t new_position,const int32_t sub_num)913 Region::trim_front (samplepos_t new_position, const int32_t sub_num)
914 {
915 modify_front (new_position, false, sub_num);
916 }
917
918 void
cut_front(samplepos_t new_position,const int32_t sub_num)919 Region::cut_front (samplepos_t new_position, const int32_t sub_num)
920 {
921 modify_front (new_position, true, sub_num);
922 }
923
924 void
cut_end(samplepos_t new_endpoint,const int32_t sub_num)925 Region::cut_end (samplepos_t new_endpoint, const int32_t sub_num)
926 {
927 modify_end (new_endpoint, true, sub_num);
928 }
929
930 void
modify_front(samplepos_t new_position,bool reset_fade,const int32_t sub_num)931 Region::modify_front (samplepos_t new_position, bool reset_fade, const int32_t sub_num)
932 {
933 if (locked()) {
934 return;
935 }
936
937 samplepos_t end = last_sample();
938 samplepos_t source_zero;
939
940 if (_position > _start) {
941 source_zero = _position - _start;
942 } else {
943 source_zero = 0; // its actually negative, but this will work for us
944 }
945
946 if (new_position < end) { /* can't trim it zero or negative length */
947
948 samplecnt_t newlen = 0;
949
950 if (!can_trim_start_before_source_start ()) {
951 /* can't trim it back past where source position zero is located */
952 new_position = max (new_position, source_zero);
953 }
954
955 if (new_position > _position) {
956 newlen = _length - (new_position - _position);
957 } else {
958 newlen = _length + (_position - new_position);
959 }
960
961 trim_to_internal (new_position, newlen, sub_num);
962
963 if (reset_fade) {
964 _right_of_split = true;
965 }
966
967 if (!property_changes_suspended()) {
968 recompute_at_start ();
969 }
970
971 maybe_invalidate_transients ();
972 }
973 }
974
975 void
modify_end(samplepos_t new_endpoint,bool reset_fade,const int32_t sub_num)976 Region::modify_end (samplepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
977 {
978 if (locked()) {
979 return;
980 }
981
982 if (new_endpoint > _position) {
983 trim_to_internal (_position, new_endpoint - _position, sub_num);
984 if (reset_fade) {
985 _left_of_split = true;
986 }
987 if (!property_changes_suspended()) {
988 recompute_at_end ();
989 }
990 }
991 }
992
993 /** @param new_endpoint New region end point, such that, for example,
994 * a region at 0 of length 10 has an endpoint of 9.
995 */
996 void
trim_end(samplepos_t new_endpoint,const int32_t sub_num)997 Region::trim_end (samplepos_t new_endpoint, const int32_t sub_num)
998 {
999 modify_end (new_endpoint, false, sub_num);
1000 }
1001
1002 void
trim_to(samplepos_t position,samplecnt_t length,const int32_t sub_num)1003 Region::trim_to (samplepos_t position, samplecnt_t length, const int32_t sub_num)
1004 {
1005 if (locked()) {
1006 return;
1007 }
1008
1009 trim_to_internal (position, length, sub_num);
1010
1011 if (!property_changes_suspended()) {
1012 recompute_at_start ();
1013 recompute_at_end ();
1014 }
1015 }
1016
1017 void
trim_to_internal(samplepos_t position,samplecnt_t length,const int32_t sub_num)1018 Region::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
1019 {
1020 samplepos_t new_start;
1021
1022 if (locked()) {
1023 return;
1024 }
1025
1026 sampleoffset_t const start_shift = position - _position;
1027
1028 if (start_shift > 0) {
1029
1030 if (_start > max_samplepos - start_shift) {
1031 new_start = max_samplepos;
1032 } else {
1033 new_start = _start + start_shift;
1034 }
1035
1036 } else if (start_shift < 0) {
1037
1038 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
1039 new_start = 0;
1040 } else {
1041 new_start = _start + start_shift;
1042 }
1043
1044 } else {
1045 new_start = _start;
1046 }
1047
1048 if (!verify_start_and_length (new_start, length)) {
1049 return;
1050 }
1051
1052 PropertyChange what_changed;
1053
1054 if (_start != new_start) {
1055 set_start_internal (new_start, sub_num);
1056 what_changed.add (Properties::start);
1057 }
1058
1059
1060 /* Set position before length, otherwise for MIDI regions this bad thing happens:
1061 * 1. we call set_length_internal; length in beats is computed using the region's current
1062 * (soon-to-be old) position
1063 * 2. we call set_position_internal; position is set and length in samples re-computed using
1064 * length in beats from (1) but at the new position, which is wrong if the region
1065 * straddles a tempo/meter change.
1066 */
1067
1068 if (_position != position) {
1069 if (!property_changes_suspended()) {
1070 _last_position = _position;
1071 }
1072 set_position_internal (position, true, sub_num);
1073 what_changed.add (Properties::position);
1074 }
1075
1076 if (_length != length) {
1077 if (!property_changes_suspended()) {
1078 _last_length = _length;
1079 }
1080 set_length_internal (length, sub_num);
1081 what_changed.add (Properties::length);
1082 }
1083
1084 _whole_file = false;
1085
1086 PropertyChange start_and_length;
1087
1088 start_and_length.add (Properties::start);
1089 start_and_length.add (Properties::length);
1090
1091 if (what_changed.contains (start_and_length)) {
1092 first_edit ();
1093 }
1094
1095 if (!what_changed.empty()) {
1096 send_change (what_changed);
1097 }
1098 }
1099
1100 void
set_hidden(bool yn)1101 Region::set_hidden (bool yn)
1102 {
1103 if (hidden() != yn) {
1104 _hidden = yn;
1105 send_change (Properties::hidden);
1106 }
1107 }
1108
1109 void
set_whole_file(bool yn)1110 Region::set_whole_file (bool yn)
1111 {
1112 _whole_file = yn;
1113 /* no change signal */
1114 }
1115
1116 void
set_automatic(bool yn)1117 Region::set_automatic (bool yn)
1118 {
1119 _automatic = yn;
1120 /* no change signal */
1121 }
1122
1123 void
set_muted(bool yn)1124 Region::set_muted (bool yn)
1125 {
1126 if (muted() != yn) {
1127 _muted = yn;
1128 send_change (Properties::muted);
1129 }
1130 }
1131
1132 void
set_opaque(bool yn)1133 Region::set_opaque (bool yn)
1134 {
1135 if (opaque() != yn) {
1136 _opaque = yn;
1137 send_change (Properties::opaque);
1138 }
1139 }
1140
1141 void
set_locked(bool yn)1142 Region::set_locked (bool yn)
1143 {
1144 if (locked() != yn) {
1145 _locked = yn;
1146 send_change (Properties::locked);
1147 }
1148 }
1149
1150 void
set_video_locked(bool yn)1151 Region::set_video_locked (bool yn)
1152 {
1153 if (video_locked() != yn) {
1154 _video_locked = yn;
1155 send_change (Properties::video_locked);
1156 }
1157 }
1158
1159 void
set_position_locked(bool yn)1160 Region::set_position_locked (bool yn)
1161 {
1162 if (position_locked() != yn) {
1163 _position_locked = yn;
1164 send_change (Properties::locked);
1165 }
1166 }
1167
1168 /** Set the region's sync point.
1169 * @param absolute_pos Session time.
1170 */
1171 void
set_sync_position(samplepos_t absolute_pos)1172 Region::set_sync_position (samplepos_t absolute_pos)
1173 {
1174 /* position within our file */
1175 samplepos_t const file_pos = _start + (absolute_pos - _position);
1176
1177 if (file_pos != _sync_position) {
1178 _sync_marked = true;
1179 _sync_position = file_pos;
1180 if (!property_changes_suspended()) {
1181 maybe_uncopy ();
1182 }
1183
1184 send_change (Properties::sync_position);
1185 }
1186 }
1187
1188 void
clear_sync_position()1189 Region::clear_sync_position ()
1190 {
1191 if (sync_marked()) {
1192 _sync_marked = false;
1193 if (!property_changes_suspended()) {
1194 maybe_uncopy ();
1195 }
1196
1197 send_change (Properties::sync_position);
1198 }
1199 }
1200
1201 /* @return the sync point relative the first sample of the region */
1202 sampleoffset_t
sync_offset(int & dir) const1203 Region::sync_offset (int& dir) const
1204 {
1205 if (sync_marked()) {
1206 if (_sync_position > _start) {
1207 dir = 1;
1208 return _sync_position - _start;
1209 } else {
1210 dir = -1;
1211 return _start - _sync_position;
1212 }
1213 } else {
1214 dir = 0;
1215 return 0;
1216 }
1217 }
1218
1219 samplepos_t
adjust_to_sync(samplepos_t pos) const1220 Region::adjust_to_sync (samplepos_t pos) const
1221 {
1222 int sync_dir;
1223 sampleoffset_t offset = sync_offset (sync_dir);
1224
1225 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1226
1227 if (sync_dir > 0) {
1228 if (pos > offset) {
1229 pos -= offset;
1230 } else {
1231 pos = 0;
1232 }
1233 } else {
1234 if (max_samplepos - pos > offset) {
1235 pos += offset;
1236 }
1237 }
1238
1239 return pos;
1240 }
1241
1242 /** @return Sync position in session time */
1243 samplepos_t
sync_position() const1244 Region::sync_position() const
1245 {
1246 if (sync_marked()) {
1247 return _position - _start + _sync_position;
1248 } else {
1249 /* if sync has not been marked, use the start of the region */
1250 return _position;
1251 }
1252 }
1253
1254 void
raise()1255 Region::raise ()
1256 {
1257 boost::shared_ptr<Playlist> pl (playlist());
1258 if (pl) {
1259 pl->raise_region (shared_from_this ());
1260 }
1261 }
1262
1263 void
lower()1264 Region::lower ()
1265 {
1266 boost::shared_ptr<Playlist> pl (playlist());
1267 if (pl) {
1268 pl->lower_region (shared_from_this ());
1269 }
1270 }
1271
1272
1273 void
raise_to_top()1274 Region::raise_to_top ()
1275 {
1276 boost::shared_ptr<Playlist> pl (playlist());
1277 if (pl) {
1278 pl->raise_region_to_top (shared_from_this());
1279 }
1280 }
1281
1282 void
lower_to_bottom()1283 Region::lower_to_bottom ()
1284 {
1285 boost::shared_ptr<Playlist> pl (playlist());
1286 if (pl) {
1287 pl->lower_region_to_bottom (shared_from_this());
1288 }
1289 }
1290
1291 void
set_layer(layer_t l)1292 Region::set_layer (layer_t l)
1293 {
1294 _layer = l;
1295 }
1296
1297 XMLNode&
state()1298 Region::state ()
1299 {
1300 XMLNode *node = new XMLNode ("Region");
1301 char buf2[64];
1302
1303 /* custom version of 'add_properties (*node);'
1304 * skip values that have have dedicated save functions
1305 * in AudioRegion::state()
1306 */
1307 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1308 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1309 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1310 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1311 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1312 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1313 i->second->get_value (*node);
1314 }
1315
1316 node->set_property ("id", id ());
1317 node->set_property ("type", _type);
1318
1319 std::string fe;
1320
1321 switch (_first_edit) {
1322 case EditChangesNothing:
1323 fe = X_("nothing");
1324 break;
1325 case EditChangesName:
1326 fe = X_("name");
1327 break;
1328 case EditChangesID:
1329 fe = X_("id");
1330 break;
1331 default: /* should be unreachable but makes g++ happy */
1332 fe = X_("nothing");
1333 break;
1334 }
1335
1336 node->set_property ("first-edit", fe);
1337
1338 /* note: flags are stored by derived classes */
1339
1340 for (uint32_t n=0; n < _sources.size(); ++n) {
1341 snprintf (buf2, sizeof(buf2), "source-%d", n);
1342 node->set_property (buf2, _sources[n]->id());
1343 }
1344
1345 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1346 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1347 node->set_property (buf2, _master_sources[n]->id ());
1348 }
1349
1350 /* Only store nested sources for the whole-file region that acts
1351 as the parent/root of all regions using it.
1352 */
1353
1354 if (_whole_file && max_source_level() > 0) {
1355
1356 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1357
1358 /* region is compound - get its playlist and
1359 store that before we list the region that
1360 needs it ...
1361 */
1362
1363 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1364 nested_node->add_child_nocopy ((*s)->get_state ());
1365 }
1366
1367 if (nested_node) {
1368 node->add_child_nocopy (*nested_node);
1369 }
1370 }
1371
1372 if (_extra_xml) {
1373 node->add_child_copy (*_extra_xml);
1374 }
1375
1376 return *node;
1377 }
1378
1379 XMLNode&
get_state()1380 Region::get_state ()
1381 {
1382 return state ();
1383 }
1384
1385 int
set_state(const XMLNode & node,int version)1386 Region::set_state (const XMLNode& node, int version)
1387 {
1388 PropertyChange what_changed;
1389 return _set_state (node, version, what_changed, true);
1390 }
1391
1392 int
_set_state(const XMLNode & node,int,PropertyChange & what_changed,bool send)1393 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1394 {
1395 Timecode::BBT_Time bbt_time;
1396
1397 Stateful::save_extra_xml (node);
1398
1399 what_changed = set_values (node);
1400
1401 /* Regions derived from "Destructive/Tape" mode tracks in earlier
1402 * versions will have their length set to an extremely large value
1403 * (essentially the maximum possible length of a file). Detect this
1404 * here and reset to the actual source length (using the first source
1405 * as a proxy for all of them). For "previously destructive" sources,
1406 * this will correspond to the full extent of the data actually written
1407 * to the file (though this may include blank space if discontiguous
1408 * punches/capture passes were carried out.
1409 */
1410
1411 if (!_sources.empty() && _type == DataType::AUDIO) {
1412 if (_length > _sources.front()->length(_position)) {
1413 _length = _sources.front()->length(_position) - _start;
1414 }
1415 }
1416
1417 set_id (node);
1418
1419 if (_position_lock_style == MusicTime) {
1420 std::string bbt_str;
1421 if (node.get_property ("bbt-position", bbt_str)) {
1422 if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1423 &bbt_time.bars,
1424 &bbt_time.beats,
1425 &bbt_time.ticks) != 3) {
1426 _position_lock_style = AudioTime;
1427 _beat = _session.tempo_map().beat_at_sample (_position);
1428 } else {
1429 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1430 }
1431 /* no position property change for legacy Property, so we do this here */
1432 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1433 }
1434 }
1435
1436 /* fix problems with old sessions corrupted by impossible
1437 values for _stretch or _shift
1438 */
1439 if (_stretch == 0.0f) {
1440 _stretch = 1.0f;
1441 }
1442
1443 if (_shift == 0.0f) {
1444 _shift = 1.0f;
1445 }
1446
1447 if (send) {
1448 send_change (what_changed);
1449 }
1450
1451 /* Quick fix for 2.x sessions when region is muted */
1452 std::string flags;
1453 if (node.get_property (X_("flags"), flags)) {
1454 if (string::npos != flags.find("Muted")){
1455 set_muted (true);
1456 }
1457 }
1458
1459 // saved property is invalid, region-transients are not saved
1460 if (_user_transients.size() == 0){
1461 _valid_transients = false;
1462 }
1463
1464 return 0;
1465 }
1466
1467 void
suspend_property_changes()1468 Region::suspend_property_changes ()
1469 {
1470 Stateful::suspend_property_changes ();
1471 _last_length = _length;
1472 _last_position = _position;
1473 }
1474
1475 void
mid_thaw(const PropertyChange & what_changed)1476 Region::mid_thaw (const PropertyChange& what_changed)
1477 {
1478 if (what_changed.contains (Properties::length)) {
1479 if (what_changed.contains (Properties::position)) {
1480 recompute_at_start ();
1481 }
1482 recompute_at_end ();
1483 }
1484 }
1485
1486 void
send_change(const PropertyChange & what_changed)1487 Region::send_change (const PropertyChange& what_changed)
1488 {
1489 if (what_changed.empty()) {
1490 return;
1491 }
1492
1493 Stateful::send_change (what_changed);
1494
1495 if (!Stateful::property_changes_suspended()) {
1496
1497 /* Try and send a shared_pointer unless this is part of the constructor.
1498 * If so, do nothing.
1499 */
1500
1501 try {
1502 boost::shared_ptr<Region> rptr = shared_from_this();
1503 if (_changemap) {
1504 (*_changemap)[what_changed].push_back (rptr);
1505 } else {
1506 boost::shared_ptr<RegionList> rl (new RegionList);
1507 rl->push_back (rptr);
1508 RegionsPropertyChanged (rl, what_changed);
1509 }
1510 } catch (...) {
1511 /* no shared_ptr available, relax; */
1512 }
1513 }
1514 }
1515
1516 bool
overlap_equivalent(boost::shared_ptr<const Region> other) const1517 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1518 {
1519 return coverage (other->first_sample(), other->last_sample()) != Evoral::OverlapNone;
1520 }
1521
1522 bool
enclosed_equivalent(boost::shared_ptr<const Region> other) const1523 Region::enclosed_equivalent (boost::shared_ptr<const Region> other) const
1524 {
1525 return (first_sample() >= other->first_sample() && last_sample() <= other->last_sample()) ||
1526 (first_sample() <= other->first_sample() && last_sample() >= other->last_sample()) ;
1527 }
1528
1529 bool
layer_and_time_equivalent(boost::shared_ptr<const Region> other) const1530 Region::layer_and_time_equivalent (boost::shared_ptr<const Region> other) const
1531 {
1532 return _layer == other->_layer &&
1533 _position == other->_position &&
1534 _length == other->_length;
1535 }
1536
1537 bool
exact_equivalent(boost::shared_ptr<const Region> other) const1538 Region::exact_equivalent (boost::shared_ptr<const Region> other) const
1539 {
1540 return _start == other->_start &&
1541 _position == other->_position &&
1542 _length == other->_length;
1543 }
1544
1545 bool
size_equivalent(boost::shared_ptr<const Region> other) const1546 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1547 {
1548 return _start == other->_start &&
1549 _length == other->_length;
1550 }
1551
1552 void
source_deleted(boost::weak_ptr<Source>)1553 Region::source_deleted (boost::weak_ptr<Source>)
1554 {
1555 drop_sources ();
1556
1557 if (!_session.deletion_in_progress()) {
1558 /* this is a very special case: at least one of the region's
1559 sources has bee deleted, so invalidate all references to
1560 ourselves. Do NOT do this during session deletion, because
1561 then we run the risk that this will actually result
1562 in this object being deleted (as refcnt goes to zero)
1563 while emitting DropReferences.
1564 */
1565
1566 drop_references ();
1567 }
1568 }
1569
1570 vector<string>
master_source_names()1571 Region::master_source_names ()
1572 {
1573 SourceList::iterator i;
1574
1575 vector<string> names;
1576 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1577 names.push_back((*i)->name());
1578 }
1579
1580 return names;
1581 }
1582
1583 void
set_master_sources(const SourceList & srcs)1584 Region::set_master_sources (const SourceList& srcs)
1585 {
1586 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1587 (*i)->dec_use_count ();
1588 }
1589
1590 _master_sources = srcs;
1591 assert (_sources.size() == _master_sources.size());
1592
1593 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1594 (*i)->inc_use_count ();
1595 }
1596 }
1597
1598 bool
source_equivalent(boost::shared_ptr<const Region> other) const1599 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1600 {
1601 if (!other)
1602 return false;
1603
1604 if ((_sources.size() != other->_sources.size()) ||
1605 (_master_sources.size() != other->_master_sources.size())) {
1606 return false;
1607 }
1608
1609 SourceList::const_iterator i;
1610 SourceList::const_iterator io;
1611
1612 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1613 if ((*i)->id() != (*io)->id()) {
1614 return false;
1615 }
1616 }
1617
1618 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1619 if ((*i)->id() != (*io)->id()) {
1620 return false;
1621 }
1622 }
1623
1624 return true;
1625 }
1626
1627 bool
any_source_equivalent(boost::shared_ptr<const Region> other) const1628 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1629 {
1630 if (!other) {
1631 return false;
1632 }
1633
1634 SourceList::const_iterator i;
1635 SourceList::const_iterator io;
1636
1637 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1638 if ((*i)->id() == (*io)->id()) {
1639 return true;
1640 }
1641 }
1642
1643 return false;
1644 }
1645
1646 std::string
source_string() const1647 Region::source_string () const
1648 {
1649 //string res = itos(_sources.size());
1650
1651 stringstream res;
1652 res << _sources.size() << ":";
1653
1654 SourceList::const_iterator i;
1655
1656 for (i = _sources.begin(); i != _sources.end(); ++i) {
1657 res << (*i)->id() << ":";
1658 }
1659
1660 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1661 res << (*i)->id() << ":";
1662 }
1663
1664 return res.str();
1665 }
1666
1667 void
deep_sources(std::set<boost::shared_ptr<Source>> & sources) const1668 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1669 {
1670 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1671
1672 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1673
1674 if (ps) {
1675 if (sources.find (ps) == sources.end()) {
1676 /* (Playlist)Source not currently in
1677 accumulating set, so recurse.
1678 */
1679 ps->playlist()->deep_sources (sources);
1680 }
1681 }
1682
1683 /* add this source */
1684 sources.insert (*i);
1685 }
1686
1687 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1688
1689 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1690
1691 if (ps) {
1692 if (sources.find (ps) == sources.end()) {
1693 /* (Playlist)Source not currently in
1694 accumulating set, so recurse.
1695 */
1696 ps->playlist()->deep_sources (sources);
1697 }
1698 }
1699
1700 /* add this source */
1701 sources.insert (*i);
1702 }
1703 }
1704
1705 bool
uses_source(boost::shared_ptr<const Source> source,bool shallow) const1706 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1707 {
1708 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1709 if (*i == source) {
1710 return true;
1711 }
1712
1713 if (!shallow) {
1714 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1715
1716 if (ps) {
1717 if (ps->playlist()->uses_source (source)) {
1718 return true;
1719 }
1720 }
1721 }
1722 }
1723
1724 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1725 if (*i == source) {
1726 return true;
1727 }
1728
1729 if (!shallow) {
1730 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1731
1732 if (ps) {
1733 if (ps->playlist()->uses_source (source)) {
1734 return true;
1735 }
1736 }
1737 }
1738 }
1739
1740 return false;
1741 }
1742
1743
1744 samplecnt_t
source_length(uint32_t n) const1745 Region::source_length(uint32_t n) const
1746 {
1747 assert (n < _sources.size());
1748 return _sources[n]->length (_position - _start);
1749 }
1750
1751 bool
verify_length(samplecnt_t & len)1752 Region::verify_length (samplecnt_t& len)
1753 {
1754 if (source() && source()->length_mutable()) {
1755 return true;
1756 }
1757
1758 samplecnt_t maxlen = 0;
1759
1760 for (uint32_t n = 0; n < _sources.size(); ++n) {
1761 maxlen = max (maxlen, source_length(n) - _start);
1762 }
1763
1764 len = min (len, maxlen);
1765
1766 return true;
1767 }
1768
1769 bool
verify_start_and_length(samplepos_t new_start,samplecnt_t & new_length)1770 Region::verify_start_and_length (samplepos_t new_start, samplecnt_t& new_length)
1771 {
1772 if (source() && source()->length_mutable()) {
1773 return true;
1774 }
1775
1776 samplecnt_t maxlen = 0;
1777
1778 for (uint32_t n = 0; n < _sources.size(); ++n) {
1779 maxlen = max (maxlen, source_length(n) - new_start);
1780 }
1781
1782 new_length = min (new_length, maxlen);
1783
1784 return true;
1785 }
1786
1787 bool
verify_start(samplepos_t pos)1788 Region::verify_start (samplepos_t pos)
1789 {
1790 if (source() && source()->length_mutable()) {
1791 return true;
1792 }
1793
1794 for (uint32_t n = 0; n < _sources.size(); ++n) {
1795 if (pos > source_length(n) - _length) {
1796 return false;
1797 }
1798 }
1799 return true;
1800 }
1801
1802 bool
verify_start_mutable(samplepos_t & new_start)1803 Region::verify_start_mutable (samplepos_t& new_start)
1804 {
1805 if (source() && source()->length_mutable()) {
1806 return true;
1807 }
1808
1809 for (uint32_t n = 0; n < _sources.size(); ++n) {
1810 if (new_start > source_length(n) - _length) {
1811 new_start = source_length(n) - _length;
1812 }
1813 }
1814 return true;
1815 }
1816
1817 boost::shared_ptr<Region>
get_parent() const1818 Region::get_parent() const
1819 {
1820 boost::shared_ptr<Playlist> pl (playlist());
1821
1822 if (pl) {
1823 boost::shared_ptr<Region> r;
1824 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1825
1826 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1827 return boost::static_pointer_cast<Region> (r);
1828 }
1829 }
1830
1831 return boost::shared_ptr<Region>();
1832 }
1833
1834 int
apply(Filter & filter,Progress * progress)1835 Region::apply (Filter& filter, Progress* progress)
1836 {
1837 return filter.run (shared_from_this(), progress);
1838 }
1839
1840
1841 void
maybe_invalidate_transients()1842 Region::maybe_invalidate_transients ()
1843 {
1844 bool changed = !_onsets.empty();
1845 _onsets.clear ();
1846
1847 if (_valid_transients || changed) {
1848 send_change (PropertyChange (Properties::valid_transients));
1849 return;
1850 }
1851 }
1852
1853 void
transients(AnalysisFeatureList & afl)1854 Region::transients (AnalysisFeatureList& afl)
1855 {
1856 int cnt = afl.empty() ? 0 : 1;
1857
1858 Region::merge_features (afl, _onsets, _position);
1859 Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1860 if (!_onsets.empty ()) {
1861 ++cnt;
1862 }
1863 if (!_user_transients.empty ()) {
1864 ++cnt;
1865 }
1866 if (cnt > 1) {
1867 afl.sort ();
1868 // remove exact duplicates
1869 TransientDetector::cleanup_transients (afl, _session.sample_rate(), 0);
1870 }
1871 }
1872
1873 bool
has_transients() const1874 Region::has_transients () const
1875 {
1876 if (!_user_transients.empty ()) {
1877 assert (_valid_transients);
1878 return true;
1879 }
1880 if (!_onsets.empty ()) {
1881 return true;
1882 }
1883 return false;
1884 }
1885
1886 void
merge_features(AnalysisFeatureList & result,const AnalysisFeatureList & src,const sampleoffset_t off) const1887 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const sampleoffset_t off) const
1888 {
1889 for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1890 const sampleoffset_t p = (*x) + off;
1891 if (p < first_sample() || p > last_sample()) {
1892 continue;
1893 }
1894 result.push_back (p);
1895 }
1896 }
1897
1898 void
captured_xruns(XrunPositions & xruns,bool abs) const1899 Region::captured_xruns (XrunPositions& xruns, bool abs) const
1900 {
1901 bool was_empty = xruns.empty ();
1902 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1903 XrunPositions const& x = (*i)->captured_xruns ();
1904 for (XrunPositions::const_iterator p = x.begin (); p != x.end (); ++p) {
1905 if (abs) {
1906 xruns.push_back (*p);
1907 } else if (*p >= _start && *p < _start + _length) {
1908 xruns.push_back (*p - _start);
1909 }
1910 }
1911 }
1912 if (_sources.size () > 1 || !was_empty) {
1913 sort (xruns.begin (), xruns.end ());
1914 xruns.erase (unique (xruns.begin (), xruns.end ()), xruns.end ());
1915 }
1916 }
1917
1918 void
get_cue_markers(CueMarkers & cues,bool abs) const1919 Region::get_cue_markers (CueMarkers& cues, bool abs) const
1920 {
1921 for (SourceList::const_iterator s = _sources.begin (); s != _sources.end(); ++s) {
1922 CueMarkers const& x = (*s)->cue_markers ();
1923 for (CueMarkers::const_iterator p = x.begin (); p != x.end (); ++p) {
1924 if (abs) {
1925 cues.insert (*p);
1926 } else if (p->position() >= _start && p->position() < _start + _length) {
1927 cues.insert (CueMarker (p->text(), p->position() - _start));
1928 }
1929 }
1930 }
1931 }
1932
1933 void
move_cue_marker(CueMarker const & cm,samplepos_t region_relative_position)1934 Region::move_cue_marker (CueMarker const & cm, samplepos_t region_relative_position)
1935 {
1936 for (SourceList::const_iterator s = _sources.begin (); s != _sources.end(); ++s) {
1937 (*s)->move_cue_marker (cm, start() + region_relative_position);
1938 }
1939 }
1940
1941 void
rename_cue_marker(CueMarker & cm,std::string const & str)1942 Region::rename_cue_marker (CueMarker& cm, std::string const & str)
1943 {
1944 for (SourceList::const_iterator s = _sources.begin (); s != _sources.end(); ++s) {
1945 (*s)->rename_cue_marker (cm, str);
1946 }
1947 }
1948
1949 void
drop_sources()1950 Region::drop_sources ()
1951 {
1952 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1953 (*i)->dec_use_count ();
1954 }
1955
1956 _sources.clear ();
1957
1958 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1959 (*i)->dec_use_count ();
1960 }
1961
1962 _master_sources.clear ();
1963 }
1964
1965 void
use_sources(SourceList const & s)1966 Region::use_sources (SourceList const & s)
1967 {
1968 set<boost::shared_ptr<Source> > unique_srcs;
1969
1970 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1971
1972 _sources.push_back (*i);
1973 (*i)->inc_use_count ();
1974 _master_sources.push_back (*i);
1975 (*i)->inc_use_count ();
1976
1977 /* connect only once to DropReferences, even if sources are replicated
1978 */
1979
1980 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1981 unique_srcs.insert (*i);
1982 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1983 }
1984 }
1985 }
1986
1987 Trimmable::CanTrim
can_trim() const1988 Region::can_trim () const
1989 {
1990 CanTrim ct = CanTrim (0);
1991
1992 if (locked()) {
1993 return ct;
1994 }
1995
1996 /* if not locked, we can always move the front later, and the end earlier
1997 */
1998
1999 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
2000
2001 if (start() != 0 || can_trim_start_before_source_start ()) {
2002 ct = CanTrim (ct | FrontTrimEarlier);
2003 }
2004
2005 if (!_sources.empty()) {
2006 if ((start() + length()) < _sources.front()->length (0)) {
2007 ct = CanTrim (ct | EndTrimLater);
2008 }
2009 }
2010
2011 return ct;
2012 }
2013
2014 uint32_t
max_source_level() const2015 Region::max_source_level () const
2016 {
2017 uint32_t lvl = 0;
2018
2019 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
2020 lvl = max (lvl, (*i)->level());
2021 }
2022
2023 return lvl;
2024 }
2025
2026 bool
is_compound() const2027 Region::is_compound () const
2028 {
2029 return max_source_level() > 0;
2030 }
2031
2032 void
post_set(const PropertyChange & pc)2033 Region::post_set (const PropertyChange& pc)
2034 {
2035 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
2036 }
2037
2038 void
set_start_internal(samplecnt_t s,const int32_t sub_num)2039 Region::set_start_internal (samplecnt_t s, const int32_t sub_num)
2040 {
2041 _start = s;
2042 }
2043
2044 samplepos_t
earliest_possible_position() const2045 Region::earliest_possible_position () const
2046 {
2047 if (_start > _position) {
2048 return 0;
2049 } else {
2050 return _position - _start;
2051 }
2052 }
2053
2054 samplecnt_t
latest_possible_sample() const2055 Region::latest_possible_sample () const
2056 {
2057 samplecnt_t minlen = max_samplecnt;
2058
2059 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
2060 /* non-audio regions have a length that may vary based on their
2061 * position, so we have to pass it in the call.
2062 */
2063 minlen = min (minlen, (*i)->length (_position));
2064 }
2065
2066 /* the latest possible last sample is determined by the current
2067 * position, plus the shortest source extent past _start.
2068 */
2069
2070 return _position + (minlen - _start) - 1;
2071 }
2072