remove debug output and add a few suggestive comments
[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)
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().beat_at_frame (_position);
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)
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);
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)
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         set_position_internal (pos, false);
562
563         /* do this even if the position is the same. this helps out
564            a GUI that has moved its representation already.
565         */
566
567         if (send) {
568                 send_change (Properties::position);
569         }
570 }
571
572 void
573 Region::set_position (framepos_t pos)
574 {
575         if (!can_move()) {
576                 return;
577         }
578
579         set_position_internal (pos, true);
580
581         /* do this even if the position is the same. this helps out
582            a GUI that has moved its representation already.
583         */
584         PropertyChange p_and_l;
585
586         p_and_l.add (Properties::position);
587         /* Currently length change due to position change is only implemented
588            for MidiRegion (Region has no length in beats).
589            Notify a length change regardless (its more efficient for MidiRegions),
590            and when Region has a _length_beats we will need it here anyway).
591         */
592         if (position_lock_style() == MusicTime) {
593                 p_and_l.add (Properties::length);
594         }
595
596         send_change (p_and_l);
597
598 }
599
600 /** A gui may need to create a region, then place it in an initial
601  *  position determined by the user.
602  *  When this takes place within one gui operation, we have to reset
603  *  _last_position to prevent an implied move.
604  */
605 void
606 Region::set_initial_position (framepos_t pos)
607 {
608         if (!can_move()) {
609                 return;
610         }
611
612         if (_position != pos) {
613                 _position = pos;
614
615                 /* check that the new _position wouldn't make the current
616                    length impossible - if so, change the length.
617
618                    XXX is this the right thing to do?
619                 */
620
621                 if (max_framepos - _length < _position) {
622                         _last_length = _length;
623                         _length = max_framepos - _position;
624                 }
625
626                 recompute_position_from_lock_style ();
627                 /* ensure that this move doesn't cause a range move */
628                 _last_position = _position;
629         }
630
631
632         /* do this even if the position is the same. this helps out
633            a GUI that has moved its representation already.
634         */
635         send_change (Properties::position);
636 }
637
638 void
639 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
640 {
641         /* We emit a change of Properties::position even if the position hasn't changed
642            (see Region::set_position), so we must always set this up so that
643            e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
644         */
645         _last_position = _position;
646
647         if (_position != pos) {
648                 _position = pos;
649
650                 if (allow_bbt_recompute) {
651                         recompute_position_from_lock_style ();
652                 }
653                 /* check that the new _position wouldn't make the current
654                    length impossible - if so, change the length.
655
656                    XXX is this the right thing to do?
657                 */
658                 if (max_framepos - _length < _position) {
659                         _last_length = _length;
660                         _length = max_framepos - _position;
661                 }
662         }
663 }
664
665 void
666 Region::recompute_position_from_lock_style ()
667 {
668         if (_position_lock_style == MusicTime) {
669                 _beat = _session.tempo_map().beat_at_frame (_position);
670         }
671 }
672
673 void
674 Region::nudge_position (frameoffset_t n)
675 {
676         if (locked() || video_locked()) {
677                 return;
678         }
679
680         if (n == 0) {
681                 return;
682         }
683
684         framepos_t new_position = _position;
685
686         if (n > 0) {
687                 if (_position > max_framepos - n) {
688                         new_position = max_framepos;
689                 } else {
690                         new_position += n;
691                 }
692         } else {
693                 if (_position < -n) {
694                         new_position = 0;
695                 } else {
696                         new_position += n;
697                 }
698         }
699
700         set_position_internal (new_position, true);
701
702         send_change (Properties::position);
703 }
704
705 void
706 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
707 {
708         _ancestral_length = l;
709         _ancestral_start = s;
710         _stretch = st;
711         _shift = sh;
712 }
713
714 void
715 Region::set_start (framepos_t pos)
716 {
717         if (locked() || position_locked() || video_locked()) {
718                 return;
719         }
720         /* This just sets the start, nothing else. It effectively shifts
721            the contents of the Region within the overall extent of the Source,
722            without changing the Region's position or length
723         */
724
725         if (_start != pos) {
726
727                 if (!verify_start (pos)) {
728                         return;
729                 }
730
731                 set_start_internal (pos);
732                 _whole_file = false;
733                 first_edit ();
734                 maybe_invalidate_transients ();
735
736                 send_change (Properties::start);
737         }
738 }
739
740 void
741 Region::move_start (frameoffset_t distance)
742 {
743         if (locked() || position_locked() || video_locked()) {
744                 return;
745         }
746
747         framepos_t new_start;
748
749         if (distance > 0) {
750
751                 if (_start > max_framepos - distance) {
752                         new_start = max_framepos; // makes no sense
753                 } else {
754                         new_start = _start + distance;
755                 }
756
757                 if (!verify_start (new_start)) {
758                         return;
759                 }
760
761         } else if (distance < 0) {
762
763                 if (_start < -distance) {
764                         new_start = 0;
765                 } else {
766                         new_start = _start + distance;
767                 }
768
769         } else {
770                 return;
771         }
772
773         if (new_start == _start) {
774                 return;
775         }
776
777         set_start_internal (new_start);
778
779         _whole_file = false;
780         first_edit ();
781
782         send_change (Properties::start);
783 }
784
785 void
786 Region::trim_front (framepos_t new_position)
787 {
788         modify_front (new_position, false);
789 }
790
791 void
792 Region::cut_front (framepos_t new_position)
793 {
794         modify_front (new_position, true);
795 }
796
797 void
798 Region::cut_end (framepos_t new_endpoint)
799 {
800         modify_end (new_endpoint, true);
801 }
802
803 void
804 Region::modify_front (framepos_t new_position, bool reset_fade)
805 {
806         if (locked()) {
807                 return;
808         }
809
810         framepos_t end = last_frame();
811         framepos_t source_zero;
812
813         if (_position > _start) {
814                 source_zero = _position - _start;
815         } else {
816                 source_zero = 0; // its actually negative, but this will work for us
817         }
818
819         if (new_position < end) { /* can't trim it zero or negative length */
820
821                 framecnt_t newlen = 0;
822
823                 if (!can_trim_start_before_source_start ()) {
824                         /* can't trim it back past where source position zero is located */
825                         new_position = max (new_position, source_zero);
826                 }
827
828                 if (new_position > _position) {
829                         newlen = _length - (new_position - _position);
830                 } else {
831                         newlen = _length + (_position - new_position);
832                 }
833
834                 trim_to_internal (new_position, newlen);
835
836                 if (reset_fade) {
837                         _right_of_split = true;
838                 }
839
840                 if (!property_changes_suspended()) {
841                         recompute_at_start ();
842                 }
843
844                 maybe_invalidate_transients ();
845         }
846 }
847
848 void
849 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
850 {
851         if (locked()) {
852                 return;
853         }
854
855         if (new_endpoint > _position) {
856                 trim_to_internal (_position, new_endpoint - _position);
857                 if (reset_fade) {
858                         _left_of_split = true;
859                 }
860                 if (!property_changes_suspended()) {
861                         recompute_at_end ();
862                 }
863         }
864 }
865
866 /** @param new_endpoint New region end point, such that, for example,
867  *  a region at 0 of length 10 has an endpoint of 9.
868  */
869
870 void
871 Region::trim_end (framepos_t new_endpoint)
872 {
873         modify_end (new_endpoint, false);
874 }
875
876 void
877 Region::trim_to (framepos_t position, framecnt_t length)
878 {
879         if (locked()) {
880                 return;
881         }
882
883         trim_to_internal (position, length);
884
885         if (!property_changes_suspended()) {
886                 recompute_at_start ();
887                 recompute_at_end ();
888         }
889 }
890
891 void
892 Region::trim_to_internal (framepos_t position, framecnt_t length)
893 {
894         framepos_t new_start;
895
896         if (locked()) {
897                 return;
898         }
899
900         frameoffset_t const start_shift = position - _position;
901
902         if (start_shift > 0) {
903
904                 if (_start > max_framepos - start_shift) {
905                         new_start = max_framepos;
906                 } else {
907                         new_start = _start + start_shift;
908                 }
909
910         } else if (start_shift < 0) {
911
912                 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
913                         new_start = 0;
914                 } else {
915                         new_start = _start + start_shift;
916                 }
917
918         } else {
919                 new_start = _start;
920         }
921
922         if (!verify_start_and_length (new_start, length)) {
923                 return;
924         }
925
926         PropertyChange what_changed;
927
928         if (_start != new_start) {
929                 set_start_internal (new_start);
930                 what_changed.add (Properties::start);
931         }
932
933
934         /* Set position before length, otherwise for MIDI regions this bad thing happens:
935          * 1. we call set_length_internal; length in beats is computed using the region's current
936          *    (soon-to-be old) position
937          * 2. we call set_position_internal; position is set and length in frames re-computed using
938          *    length in beats from (1) but at the new position, which is wrong if the region
939          *    straddles a tempo/meter change.
940          */
941
942         if (_position != position) {
943                 if (!property_changes_suspended()) {
944                         _last_position = _position;
945                 }
946                 set_position_internal (position, true);
947                 what_changed.add (Properties::position);
948         }
949
950         if (_length != length) {
951                 if (!property_changes_suspended()) {
952                         _last_length = _length;
953                 }
954                 set_length_internal (length);
955                 what_changed.add (Properties::length);
956         }
957
958         _whole_file = false;
959
960         PropertyChange start_and_length;
961
962         start_and_length.add (Properties::start);
963         start_and_length.add (Properties::length);
964
965         if (what_changed.contains (start_and_length)) {
966                 first_edit ();
967         }
968
969         if (!what_changed.empty()) {
970                 send_change (what_changed);
971         }
972 }
973
974 void
975 Region::set_hidden (bool yn)
976 {
977         if (hidden() != yn) {
978                 _hidden = yn;
979                 send_change (Properties::hidden);
980         }
981 }
982
983 void
984 Region::set_whole_file (bool yn)
985 {
986         _whole_file = yn;
987         /* no change signal */
988 }
989
990 void
991 Region::set_automatic (bool yn)
992 {
993         _automatic = yn;
994         /* no change signal */
995 }
996
997 void
998 Region::set_muted (bool yn)
999 {
1000         if (muted() != yn) {
1001                 _muted = yn;
1002                 send_change (Properties::muted);
1003         }
1004 }
1005
1006 void
1007 Region::set_opaque (bool yn)
1008 {
1009         if (opaque() != yn) {
1010                 _opaque = yn;
1011                 send_change (Properties::opaque);
1012         }
1013 }
1014
1015 void
1016 Region::set_locked (bool yn)
1017 {
1018         if (locked() != yn) {
1019                 _locked = yn;
1020                 send_change (Properties::locked);
1021         }
1022 }
1023
1024 void
1025 Region::set_video_locked (bool yn)
1026 {
1027         if (video_locked() != yn) {
1028                 _video_locked = yn;
1029                 send_change (Properties::video_locked);
1030         }
1031 }
1032
1033 void
1034 Region::set_position_locked (bool yn)
1035 {
1036         if (position_locked() != yn) {
1037                 _position_locked = yn;
1038                 send_change (Properties::locked);
1039         }
1040 }
1041
1042 /** Set the region's sync point.
1043  *  @param absolute_pos Session time.
1044  */
1045 void
1046 Region::set_sync_position (framepos_t absolute_pos)
1047 {
1048         /* position within our file */
1049         framepos_t const file_pos = _start + (absolute_pos - _position);
1050
1051         if (file_pos != _sync_position) {
1052                 _sync_marked = true;
1053                 _sync_position = file_pos;
1054                 if (!property_changes_suspended()) {
1055                         maybe_uncopy ();
1056                 }
1057
1058                 send_change (Properties::sync_position);
1059         }
1060 }
1061
1062 void
1063 Region::clear_sync_position ()
1064 {
1065         if (sync_marked()) {
1066                 _sync_marked = false;
1067                 if (!property_changes_suspended()) {
1068                         maybe_uncopy ();
1069                 }
1070
1071                 send_change (Properties::sync_position);
1072         }
1073 }
1074
1075 /* @return the sync point relative the first frame of the region */
1076 frameoffset_t
1077 Region::sync_offset (int& dir) const
1078 {
1079         if (sync_marked()) {
1080                 if (_sync_position > _start) {
1081                         dir = 1;
1082                         return _sync_position - _start;
1083                 } else {
1084                         dir = -1;
1085                         return _start - _sync_position;
1086                 }
1087         } else {
1088                 dir = 0;
1089                 return 0;
1090         }
1091 }
1092
1093 framepos_t
1094 Region::adjust_to_sync (framepos_t pos) const
1095 {
1096         int sync_dir;
1097         frameoffset_t offset = sync_offset (sync_dir);
1098
1099         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1100
1101         if (sync_dir > 0) {
1102                 if (pos > offset) {
1103                         pos -= offset;
1104                 } else {
1105                         pos = 0;
1106                 }
1107         } else {
1108                 if (max_framepos - pos > offset) {
1109                         pos += offset;
1110                 }
1111         }
1112
1113         return pos;
1114 }
1115
1116 /** @return Sync position in session time */
1117 framepos_t
1118 Region::sync_position() const
1119 {
1120         if (sync_marked()) {
1121                 return _position - _start + _sync_position;
1122         } else {
1123                 /* if sync has not been marked, use the start of the region */
1124                 return _position;
1125         }
1126 }
1127
1128 void
1129 Region::raise ()
1130 {
1131         boost::shared_ptr<Playlist> pl (playlist());
1132         if (pl) {
1133                 pl->raise_region (shared_from_this ());
1134         }
1135 }
1136
1137 void
1138 Region::lower ()
1139 {
1140         boost::shared_ptr<Playlist> pl (playlist());
1141         if (pl) {
1142                 pl->lower_region (shared_from_this ());
1143         }
1144 }
1145
1146
1147 void
1148 Region::raise_to_top ()
1149 {
1150         boost::shared_ptr<Playlist> pl (playlist());
1151         if (pl) {
1152                 pl->raise_region_to_top (shared_from_this());
1153         }
1154 }
1155
1156 void
1157 Region::lower_to_bottom ()
1158 {
1159         boost::shared_ptr<Playlist> pl (playlist());
1160         if (pl) {
1161                 pl->lower_region_to_bottom (shared_from_this());
1162         }
1163 }
1164
1165 void
1166 Region::set_layer (layer_t l)
1167 {
1168         _layer = l;
1169 }
1170
1171 XMLNode&
1172 Region::state ()
1173 {
1174         XMLNode *node = new XMLNode ("Region");
1175         char buf[64];
1176         char buf2[64];
1177         LocaleGuard lg;
1178         const char* fe = NULL;
1179
1180         /* custom version of 'add_properties (*node);'
1181          * skip values that have have dedicated save functions
1182          * in AudioRegion::state()
1183          */
1184         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1185                 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1186                 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1187                 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1188                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1189                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1190                 i->second->get_value (*node);
1191         }
1192
1193         id().print (buf, sizeof (buf));
1194         node->add_property ("id", buf);
1195         node->add_property ("type", _type.to_string());
1196
1197         switch (_first_edit) {
1198         case EditChangesNothing:
1199                 fe = X_("nothing");
1200                 break;
1201         case EditChangesName:
1202                 fe = X_("name");
1203                 break;
1204         case EditChangesID:
1205                 fe = X_("id");
1206                 break;
1207         default: /* should be unreachable but makes g++ happy */
1208                 fe = X_("nothing");
1209                 break;
1210         }
1211
1212         node->add_property ("first-edit", fe);
1213
1214         /* note: flags are stored by derived classes */
1215
1216         if (_position_lock_style != AudioTime) {
1217                 snprintf (buf, sizeof(buf), "%lf", _beat);
1218                 node->add_property ("beat", buf);
1219         }
1220
1221         for (uint32_t n=0; n < _sources.size(); ++n) {
1222                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1223                 _sources[n]->id().print (buf, sizeof(buf));
1224                 node->add_property (buf2, buf);
1225         }
1226
1227         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1228                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1229                 _master_sources[n]->id().print (buf, sizeof (buf));
1230                 node->add_property (buf2, buf);
1231         }
1232
1233         /* Only store nested sources for the whole-file region that acts
1234            as the parent/root of all regions using it.
1235         */
1236
1237         if (_whole_file && max_source_level() > 0) {
1238
1239                 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1240
1241                 /* region is compound - get its playlist and
1242                    store that before we list the region that
1243                    needs it ...
1244                 */
1245
1246                 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1247                         nested_node->add_child_nocopy ((*s)->get_state ());
1248                 }
1249
1250                 if (nested_node) {
1251                         node->add_child_nocopy (*nested_node);
1252                 }
1253         }
1254
1255         if (_extra_xml) {
1256                 node->add_child_copy (*_extra_xml);
1257         }
1258
1259         return *node;
1260 }
1261
1262 XMLNode&
1263 Region::get_state ()
1264 {
1265         return state ();
1266 }
1267
1268 int
1269 Region::set_state (const XMLNode& node, int version)
1270 {
1271         PropertyChange what_changed;
1272         return _set_state (node, version, what_changed, true);
1273 }
1274
1275 int
1276 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1277 {
1278         XMLProperty const * prop;
1279         Timecode::BBT_Time bbt_time;
1280
1281         Stateful::save_extra_xml (node);
1282
1283         what_changed = set_values (node);
1284
1285         set_id (node);
1286
1287         if (_position_lock_style == MusicTime) {
1288                 if ((prop = node.property ("bbt-position")) == 0) {
1289                         if ((prop = node.property ("beat")) == 0) {
1290                                 /* missing BBT info, revert to audio time locking */
1291                                 _position_lock_style = AudioTime;
1292                         } else {
1293                                 if (sscanf (prop->value().c_str(), "%lf", &_beat) != 1) {
1294                                         _position_lock_style = AudioTime;
1295                                 }
1296                         }
1297
1298                 } else {
1299                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1300                                     &bbt_time.bars,
1301                                     &bbt_time.beats,
1302                                     &bbt_time.ticks) != 3) {
1303                                 _position_lock_style = AudioTime;
1304                         } else {
1305                                 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1306                         }
1307                 }
1308         }
1309
1310         /* fix problems with old sessions corrupted by impossible
1311            values for _stretch or _shift
1312         */
1313         if (_stretch == 0.0f) {
1314                 _stretch = 1.0f;
1315         }
1316
1317         if (_shift == 0.0f) {
1318                 _shift = 1.0f;
1319         }
1320
1321         if (send) {
1322                 send_change (what_changed);
1323         }
1324
1325         /* Quick fix for 2.x sessions when region is muted */
1326         if ((prop = node.property (X_("flags")))) {
1327                 if (string::npos != prop->value().find("Muted")){
1328                         set_muted (true);
1329                 }
1330         }
1331
1332         // saved property is invalid, region-transients are not saved
1333         if (_user_transients.size() == 0){
1334                 _valid_transients = false;
1335         }
1336
1337         return 0;
1338 }
1339
1340 void
1341 Region::suspend_property_changes ()
1342 {
1343         Stateful::suspend_property_changes ();
1344         _last_length = _length;
1345         _last_position = _position;
1346 }
1347
1348 void
1349 Region::mid_thaw (const PropertyChange& what_changed)
1350 {
1351         if (what_changed.contains (Properties::length)) {
1352                 if (what_changed.contains (Properties::position)) {
1353                         recompute_at_start ();
1354                 }
1355                 recompute_at_end ();
1356         }
1357 }
1358
1359 void
1360 Region::send_change (const PropertyChange& what_changed)
1361 {
1362         if (what_changed.empty()) {
1363                 return;
1364         }
1365
1366         Stateful::send_change (what_changed);
1367
1368         if (!Stateful::property_changes_suspended()) {
1369
1370                 /* Try and send a shared_pointer unless this is part of the constructor.
1371                    If so, do nothing.
1372                 */
1373
1374                 try {
1375                         boost::shared_ptr<Region> rptr = shared_from_this();
1376                         RegionPropertyChanged (rptr, what_changed);
1377                 } catch (...) {
1378                         /* no shared_ptr available, relax; */
1379                 }
1380         }
1381 }
1382
1383 bool
1384 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1385 {
1386         return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1387 }
1388
1389 bool
1390 Region::equivalent (boost::shared_ptr<const Region> other) const
1391 {
1392         return _start == other->_start &&
1393                 _position == other->_position &&
1394                 _length == other->_length;
1395 }
1396
1397 bool
1398 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1399 {
1400         return _start == other->_start &&
1401                 _length == other->_length;
1402 }
1403
1404 bool
1405 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1406 {
1407         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1408 }
1409
1410 void
1411 Region::source_deleted (boost::weak_ptr<Source>)
1412 {
1413         drop_sources ();
1414
1415         if (!_session.deletion_in_progress()) {
1416                 /* this is a very special case: at least one of the region's
1417                    sources has bee deleted, so invalidate all references to
1418                    ourselves. Do NOT do this during session deletion, because
1419                    then we run the risk that this will actually result
1420                    in this object being deleted (as refcnt goes to zero)
1421                    while emitting DropReferences.
1422                 */
1423
1424                 drop_references ();
1425         }
1426 }
1427
1428 vector<string>
1429 Region::master_source_names ()
1430 {
1431         SourceList::iterator i;
1432
1433         vector<string> names;
1434         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1435                 names.push_back((*i)->name());
1436         }
1437
1438         return names;
1439 }
1440
1441 void
1442 Region::set_master_sources (const SourceList& srcs)
1443 {
1444         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1445                 (*i)->dec_use_count ();
1446         }
1447
1448         _master_sources = srcs;
1449         assert (_sources.size() == _master_sources.size());
1450
1451         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1452                 (*i)->inc_use_count ();
1453         }
1454 }
1455
1456 bool
1457 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1458 {
1459         if (!other)
1460                 return false;
1461
1462         if ((_sources.size() != other->_sources.size()) ||
1463             (_master_sources.size() != other->_master_sources.size())) {
1464                 return false;
1465         }
1466
1467         SourceList::const_iterator i;
1468         SourceList::const_iterator io;
1469
1470         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1471                 if ((*i)->id() != (*io)->id()) {
1472                         return false;
1473                 }
1474         }
1475
1476         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1477                 if ((*i)->id() != (*io)->id()) {
1478                         return false;
1479                 }
1480         }
1481
1482         return true;
1483 }
1484
1485 bool
1486 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1487 {
1488         if (!other) {
1489                 return false;
1490         }
1491
1492         SourceList::const_iterator i;
1493         SourceList::const_iterator io;
1494
1495         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1496                 if ((*i)->id() == (*io)->id()) {
1497                         return true;
1498                 }
1499         }
1500
1501         return false;
1502 }
1503
1504 std::string
1505 Region::source_string () const
1506 {
1507         //string res = itos(_sources.size());
1508
1509         stringstream res;
1510         res << _sources.size() << ":";
1511
1512         SourceList::const_iterator i;
1513
1514         for (i = _sources.begin(); i != _sources.end(); ++i) {
1515                 res << (*i)->id() << ":";
1516         }
1517
1518         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1519                 res << (*i)->id() << ":";
1520         }
1521
1522         return res.str();
1523 }
1524
1525 void
1526 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1527 {
1528         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1529
1530                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1531
1532                 if (ps) {
1533                         if (sources.find (ps) == sources.end()) {
1534                                 /* (Playlist)Source not currently in
1535                                    accumulating set, so recurse.
1536                                 */
1537                                 ps->playlist()->deep_sources (sources);
1538                         }
1539                 }
1540
1541                 /* add this source */
1542                 sources.insert (*i);
1543         }
1544
1545         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1546
1547                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1548
1549                 if (ps) {
1550                         if (sources.find (ps) == sources.end()) {
1551                                 /* (Playlist)Source not currently in
1552                                    accumulating set, so recurse.
1553                                 */
1554                                 ps->playlist()->deep_sources (sources);
1555                         }
1556                 }
1557
1558                 /* add this source */
1559                 sources.insert (*i);
1560         }
1561 }
1562
1563 bool
1564 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1565 {
1566         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1567                 if (*i == source) {
1568                         return true;
1569                 }
1570
1571                 if (!shallow) {
1572                         boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1573
1574                         if (ps) {
1575                                 if (ps->playlist()->uses_source (source)) {
1576                                         return true;
1577                                 }
1578                         }
1579                 }
1580         }
1581
1582         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1583                 if (*i == source) {
1584                         return true;
1585                 }
1586
1587                 if (!shallow) {
1588                         boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1589
1590                         if (ps) {
1591                                 if (ps->playlist()->uses_source (source)) {
1592                                         return true;
1593                                 }
1594                         }
1595                 }
1596         }
1597
1598         return false;
1599 }
1600
1601
1602 framecnt_t
1603 Region::source_length(uint32_t n) const
1604 {
1605         assert (n < _sources.size());
1606         return _sources[n]->length (_position - _start);
1607 }
1608
1609 bool
1610 Region::verify_length (framecnt_t& len)
1611 {
1612         if (source() && (source()->destructive() || source()->length_mutable())) {
1613                 return true;
1614         }
1615
1616         framecnt_t maxlen = 0;
1617
1618         for (uint32_t n = 0; n < _sources.size(); ++n) {
1619                 maxlen = max (maxlen, source_length(n) - _start);
1620         }
1621
1622         len = min (len, maxlen);
1623
1624         return true;
1625 }
1626
1627 bool
1628 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1629 {
1630         if (source() && (source()->destructive() || source()->length_mutable())) {
1631                 return true;
1632         }
1633
1634         framecnt_t maxlen = 0;
1635
1636         for (uint32_t n = 0; n < _sources.size(); ++n) {
1637                 maxlen = max (maxlen, source_length(n) - new_start);
1638         }
1639
1640         new_length = min (new_length, maxlen);
1641
1642         return true;
1643 }
1644
1645 bool
1646 Region::verify_start (framepos_t pos)
1647 {
1648         if (source() && (source()->destructive() || source()->length_mutable())) {
1649                 return true;
1650         }
1651
1652         for (uint32_t n = 0; n < _sources.size(); ++n) {
1653                 if (pos > source_length(n) - _length) {
1654                         return false;
1655                 }
1656         }
1657         return true;
1658 }
1659
1660 bool
1661 Region::verify_start_mutable (framepos_t& new_start)
1662 {
1663         if (source() && (source()->destructive() || source()->length_mutable())) {
1664                 return true;
1665         }
1666
1667         for (uint32_t n = 0; n < _sources.size(); ++n) {
1668                 if (new_start > source_length(n) - _length) {
1669                         new_start = source_length(n) - _length;
1670                 }
1671         }
1672         return true;
1673 }
1674
1675 boost::shared_ptr<Region>
1676 Region::get_parent() const
1677 {
1678         boost::shared_ptr<Playlist> pl (playlist());
1679
1680         if (pl) {
1681                 boost::shared_ptr<Region> r;
1682                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1683
1684                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1685                         return boost::static_pointer_cast<Region> (r);
1686                 }
1687         }
1688
1689         return boost::shared_ptr<Region>();
1690 }
1691
1692 int
1693 Region::apply (Filter& filter, Progress* progress)
1694 {
1695         return filter.run (shared_from_this(), progress);
1696 }
1697
1698
1699 void
1700 Region::maybe_invalidate_transients ()
1701 {
1702         bool changed = !_onsets.empty();
1703         _onsets.clear ();
1704
1705         if (_valid_transients || changed) {
1706                 send_change (PropertyChange (Properties::valid_transients));
1707                 return;
1708         }
1709 }
1710
1711 void
1712 Region::transients (AnalysisFeatureList& afl)
1713 {
1714         int cnt = afl.empty() ? 0 : 1;
1715
1716         Region::merge_features (afl, _onsets, _position);
1717         Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1718         if (!_onsets.empty ()) {
1719                 ++cnt;
1720         }
1721         if (!_user_transients.empty ()) {
1722                 ++cnt;
1723         }
1724         if (cnt > 1 ) {
1725                 afl.sort ();
1726                 // remove exact duplicates
1727                 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1728         }
1729 }
1730
1731 bool
1732 Region::has_transients () const
1733 {
1734         if (!_user_transients.empty ()) {
1735                 assert (_valid_transients);
1736                 return true;
1737         }
1738         if (!_onsets.empty ()) {
1739                 return true;
1740         }
1741         return false;
1742 }
1743
1744 void
1745 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1746 {
1747         for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1748                 const frameoffset_t p = (*x) + off;
1749                 if (p < first_frame() || p > last_frame()) {
1750                         continue;
1751                 }
1752                 result.push_back (p);
1753         }
1754 }
1755
1756 void
1757 Region::drop_sources ()
1758 {
1759         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1760                 (*i)->dec_use_count ();
1761         }
1762
1763         _sources.clear ();
1764
1765         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1766                 (*i)->dec_use_count ();
1767         }
1768
1769         _master_sources.clear ();
1770 }
1771
1772 void
1773 Region::use_sources (SourceList const & s)
1774 {
1775         set<boost::shared_ptr<Source> > unique_srcs;
1776
1777         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1778
1779                 _sources.push_back (*i);
1780                 (*i)->inc_use_count ();
1781                 _master_sources.push_back (*i);
1782                 (*i)->inc_use_count ();
1783
1784                 /* connect only once to DropReferences, even if sources are replicated
1785                  */
1786
1787                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1788                         unique_srcs.insert (*i);
1789                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1790                 }
1791         }
1792 }
1793
1794 Trimmable::CanTrim
1795 Region::can_trim () const
1796 {
1797         CanTrim ct = CanTrim (0);
1798
1799         if (locked()) {
1800                 return ct;
1801         }
1802
1803         /* if not locked, we can always move the front later, and the end earlier
1804          */
1805
1806         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1807
1808         if (start() != 0 || can_trim_start_before_source_start ()) {
1809                 ct = CanTrim (ct | FrontTrimEarlier);
1810         }
1811
1812         if (!_sources.empty()) {
1813                 if ((start() + length()) < _sources.front()->length (0)) {
1814                         ct = CanTrim (ct | EndTrimLater);
1815                 }
1816         }
1817
1818         return ct;
1819 }
1820
1821 uint32_t
1822 Region::max_source_level () const
1823 {
1824         uint32_t lvl = 0;
1825
1826         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1827                 lvl = max (lvl, (*i)->level());
1828         }
1829
1830         return lvl;
1831 }
1832
1833 bool
1834 Region::is_compound () const
1835 {
1836         return max_source_level() > 0;
1837 }
1838
1839 void
1840 Region::post_set (const PropertyChange& pc)
1841 {
1842         if (pc.contains (Properties::position)) {
1843                 recompute_position_from_lock_style ();
1844         }
1845 }
1846
1847 void
1848 Region::set_start_internal (framecnt_t s)
1849 {
1850         _start = s;
1851 }
1852
1853 framepos_t
1854 Region::earliest_possible_position () const
1855 {
1856         if (_start > _position) {
1857                 return 0;
1858         } else {
1859                 return _position - _start;
1860         }
1861 }
1862
1863 framecnt_t
1864 Region::latest_possible_frame () const
1865 {
1866         framecnt_t minlen = max_framecnt;
1867
1868         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1869                 /* non-audio regions have a length that may vary based on their
1870                  * position, so we have to pass it in the call.
1871                  */
1872                 minlen = min (minlen, (*i)->length (_position));
1873         }
1874
1875         /* the latest possible last frame is determined by the current
1876          * position, plus the shortest source extent past _start.
1877          */
1878
1879         return _position + (minlen - _start) - 1;
1880 }