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