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