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