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