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