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