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