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