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