Use XMLNode::get/set_property in ARDOUR::ExportFilename class
[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         LocaleGuard lg;
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->set_property ("id", id ());
1277         node->set_property ("type", _type);
1278
1279         std::string fe;
1280
1281         switch (_first_edit) {
1282         case EditChangesNothing:
1283                 fe = X_("nothing");
1284                 break;
1285         case EditChangesName:
1286                 fe = X_("name");
1287                 break;
1288         case EditChangesID:
1289                 fe = X_("id");
1290                 break;
1291         default: /* should be unreachable but makes g++ happy */
1292                 fe = X_("nothing");
1293                 break;
1294         }
1295
1296         node->set_property ("first-edit", fe);
1297
1298         /* note: flags are stored by derived classes */
1299
1300         for (uint32_t n=0; n < _sources.size(); ++n) {
1301                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1302                 node->set_property (buf2, _sources[n]->id());
1303         }
1304
1305         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1306                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1307                 node->set_property (buf2, _master_sources[n]->id ());
1308         }
1309
1310         /* Only store nested sources for the whole-file region that acts
1311            as the parent/root of all regions using it.
1312         */
1313
1314         if (_whole_file && max_source_level() > 0) {
1315
1316                 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1317
1318                 /* region is compound - get its playlist and
1319                    store that before we list the region that
1320                    needs it ...
1321                 */
1322
1323                 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1324                         nested_node->add_child_nocopy ((*s)->get_state ());
1325                 }
1326
1327                 if (nested_node) {
1328                         node->add_child_nocopy (*nested_node);
1329                 }
1330         }
1331
1332         if (_extra_xml) {
1333                 node->add_child_copy (*_extra_xml);
1334         }
1335
1336         return *node;
1337 }
1338
1339 XMLNode&
1340 Region::get_state ()
1341 {
1342         return state ();
1343 }
1344
1345 int
1346 Region::set_state (const XMLNode& node, int version)
1347 {
1348         PropertyChange what_changed;
1349         return _set_state (node, version, what_changed, true);
1350 }
1351
1352 int
1353 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1354 {
1355         Timecode::BBT_Time bbt_time;
1356
1357         Stateful::save_extra_xml (node);
1358
1359         what_changed = set_values (node);
1360
1361         set_id (node);
1362
1363         if (_position_lock_style == MusicTime) {
1364                 std::string bbt_str;
1365                 if (node.get_property ("bbt-position", bbt_str)) {
1366                         if (sscanf (bbt_str.c_str(), "%d|%d|%d",
1367                                     &bbt_time.bars,
1368                                     &bbt_time.beats,
1369                                     &bbt_time.ticks) != 3) {
1370                                 _position_lock_style = AudioTime;
1371                                 _beat = _session.tempo_map().beat_at_frame (_position);
1372                         } else {
1373                                 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1374                         }
1375                         /* no position property change for legacy Property, so we do this here */
1376                         _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1377                 }
1378         }
1379
1380         /* fix problems with old sessions corrupted by impossible
1381            values for _stretch or _shift
1382         */
1383         if (_stretch == 0.0f) {
1384                 _stretch = 1.0f;
1385         }
1386
1387         if (_shift == 0.0f) {
1388                 _shift = 1.0f;
1389         }
1390
1391         if (send) {
1392                 send_change (what_changed);
1393         }
1394
1395         /* Quick fix for 2.x sessions when region is muted */
1396         std::string flags;
1397         if (node.get_property (X_("flags"), flags)) {
1398                 if (string::npos != flags.find("Muted")){
1399                         set_muted (true);
1400                 }
1401         }
1402
1403         // saved property is invalid, region-transients are not saved
1404         if (_user_transients.size() == 0){
1405                 _valid_transients = false;
1406         }
1407
1408         return 0;
1409 }
1410
1411 void
1412 Region::suspend_property_changes ()
1413 {
1414         Stateful::suspend_property_changes ();
1415         _last_length = _length;
1416         _last_position = _position;
1417 }
1418
1419 void
1420 Region::mid_thaw (const PropertyChange& what_changed)
1421 {
1422         if (what_changed.contains (Properties::length)) {
1423                 if (what_changed.contains (Properties::position)) {
1424                         recompute_at_start ();
1425                 }
1426                 recompute_at_end ();
1427         }
1428 }
1429
1430 void
1431 Region::send_change (const PropertyChange& what_changed)
1432 {
1433         if (what_changed.empty()) {
1434                 return;
1435         }
1436
1437         Stateful::send_change (what_changed);
1438
1439         if (!Stateful::property_changes_suspended()) {
1440
1441                 /* Try and send a shared_pointer unless this is part of the constructor.
1442                    If so, do nothing.
1443                 */
1444
1445                 try {
1446                         boost::shared_ptr<Region> rptr = shared_from_this();
1447                         RegionPropertyChanged (rptr, what_changed);
1448                 } catch (...) {
1449                         /* no shared_ptr available, relax; */
1450                 }
1451         }
1452 }
1453
1454 bool
1455 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1456 {
1457         return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1458 }
1459
1460 bool
1461 Region::equivalent (boost::shared_ptr<const Region> other) const
1462 {
1463         return _start == other->_start &&
1464                 _position == other->_position &&
1465                 _length == other->_length;
1466 }
1467
1468 bool
1469 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1470 {
1471         return _start == other->_start &&
1472                 _length == other->_length;
1473 }
1474
1475 bool
1476 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1477 {
1478         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1479 }
1480
1481 void
1482 Region::source_deleted (boost::weak_ptr<Source>)
1483 {
1484         drop_sources ();
1485
1486         if (!_session.deletion_in_progress()) {
1487                 /* this is a very special case: at least one of the region's
1488                    sources has bee deleted, so invalidate all references to
1489                    ourselves. Do NOT do this during session deletion, because
1490                    then we run the risk that this will actually result
1491                    in this object being deleted (as refcnt goes to zero)
1492                    while emitting DropReferences.
1493                 */
1494
1495                 drop_references ();
1496         }
1497 }
1498
1499 vector<string>
1500 Region::master_source_names ()
1501 {
1502         SourceList::iterator i;
1503
1504         vector<string> names;
1505         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1506                 names.push_back((*i)->name());
1507         }
1508
1509         return names;
1510 }
1511
1512 void
1513 Region::set_master_sources (const SourceList& srcs)
1514 {
1515         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1516                 (*i)->dec_use_count ();
1517         }
1518
1519         _master_sources = srcs;
1520         assert (_sources.size() == _master_sources.size());
1521
1522         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1523                 (*i)->inc_use_count ();
1524         }
1525 }
1526
1527 bool
1528 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1529 {
1530         if (!other)
1531                 return false;
1532
1533         if ((_sources.size() != other->_sources.size()) ||
1534             (_master_sources.size() != other->_master_sources.size())) {
1535                 return false;
1536         }
1537
1538         SourceList::const_iterator i;
1539         SourceList::const_iterator io;
1540
1541         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1542                 if ((*i)->id() != (*io)->id()) {
1543                         return false;
1544                 }
1545         }
1546
1547         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1548                 if ((*i)->id() != (*io)->id()) {
1549                         return false;
1550                 }
1551         }
1552
1553         return true;
1554 }
1555
1556 bool
1557 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1558 {
1559         if (!other) {
1560                 return false;
1561         }
1562
1563         SourceList::const_iterator i;
1564         SourceList::const_iterator io;
1565
1566         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1567                 if ((*i)->id() == (*io)->id()) {
1568                         return true;
1569                 }
1570         }
1571
1572         return false;
1573 }
1574
1575 std::string
1576 Region::source_string () const
1577 {
1578         //string res = itos(_sources.size());
1579
1580         stringstream res;
1581         res << _sources.size() << ":";
1582
1583         SourceList::const_iterator i;
1584
1585         for (i = _sources.begin(); i != _sources.end(); ++i) {
1586                 res << (*i)->id() << ":";
1587         }
1588
1589         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1590                 res << (*i)->id() << ":";
1591         }
1592
1593         return res.str();
1594 }
1595
1596 void
1597 Region::deep_sources (std::set<boost::shared_ptr<Source> > & sources) const
1598 {
1599         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1600
1601                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1602
1603                 if (ps) {
1604                         if (sources.find (ps) == sources.end()) {
1605                                 /* (Playlist)Source not currently in
1606                                    accumulating set, so recurse.
1607                                 */
1608                                 ps->playlist()->deep_sources (sources);
1609                         }
1610                 }
1611
1612                 /* add this source */
1613                 sources.insert (*i);
1614         }
1615
1616         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1617
1618                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1619
1620                 if (ps) {
1621                         if (sources.find (ps) == sources.end()) {
1622                                 /* (Playlist)Source not currently in
1623                                    accumulating set, so recurse.
1624                                 */
1625                                 ps->playlist()->deep_sources (sources);
1626                         }
1627                 }
1628
1629                 /* add this source */
1630                 sources.insert (*i);
1631         }
1632 }
1633
1634 bool
1635 Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
1636 {
1637         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1638                 if (*i == source) {
1639                         return true;
1640                 }
1641
1642                 if (!shallow) {
1643                         boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1644
1645                         if (ps) {
1646                                 if (ps->playlist()->uses_source (source)) {
1647                                         return true;
1648                                 }
1649                         }
1650                 }
1651         }
1652
1653         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1654                 if (*i == source) {
1655                         return true;
1656                 }
1657
1658                 if (!shallow) {
1659                         boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1660
1661                         if (ps) {
1662                                 if (ps->playlist()->uses_source (source)) {
1663                                         return true;
1664                                 }
1665                         }
1666                 }
1667         }
1668
1669         return false;
1670 }
1671
1672
1673 framecnt_t
1674 Region::source_length(uint32_t n) const
1675 {
1676         assert (n < _sources.size());
1677         return _sources[n]->length (_position - _start);
1678 }
1679
1680 bool
1681 Region::verify_length (framecnt_t& len)
1682 {
1683         if (source() && (source()->destructive() || source()->length_mutable())) {
1684                 return true;
1685         }
1686
1687         framecnt_t maxlen = 0;
1688
1689         for (uint32_t n = 0; n < _sources.size(); ++n) {
1690                 maxlen = max (maxlen, source_length(n) - _start);
1691         }
1692
1693         len = min (len, maxlen);
1694
1695         return true;
1696 }
1697
1698 bool
1699 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1700 {
1701         if (source() && (source()->destructive() || source()->length_mutable())) {
1702                 return true;
1703         }
1704
1705         framecnt_t maxlen = 0;
1706
1707         for (uint32_t n = 0; n < _sources.size(); ++n) {
1708                 maxlen = max (maxlen, source_length(n) - new_start);
1709         }
1710
1711         new_length = min (new_length, maxlen);
1712
1713         return true;
1714 }
1715
1716 bool
1717 Region::verify_start (framepos_t pos)
1718 {
1719         if (source() && (source()->destructive() || source()->length_mutable())) {
1720                 return true;
1721         }
1722
1723         for (uint32_t n = 0; n < _sources.size(); ++n) {
1724                 if (pos > source_length(n) - _length) {
1725                         return false;
1726                 }
1727         }
1728         return true;
1729 }
1730
1731 bool
1732 Region::verify_start_mutable (framepos_t& new_start)
1733 {
1734         if (source() && (source()->destructive() || source()->length_mutable())) {
1735                 return true;
1736         }
1737
1738         for (uint32_t n = 0; n < _sources.size(); ++n) {
1739                 if (new_start > source_length(n) - _length) {
1740                         new_start = source_length(n) - _length;
1741                 }
1742         }
1743         return true;
1744 }
1745
1746 boost::shared_ptr<Region>
1747 Region::get_parent() const
1748 {
1749         boost::shared_ptr<Playlist> pl (playlist());
1750
1751         if (pl) {
1752                 boost::shared_ptr<Region> r;
1753                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1754
1755                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1756                         return boost::static_pointer_cast<Region> (r);
1757                 }
1758         }
1759
1760         return boost::shared_ptr<Region>();
1761 }
1762
1763 int
1764 Region::apply (Filter& filter, Progress* progress)
1765 {
1766         return filter.run (shared_from_this(), progress);
1767 }
1768
1769
1770 void
1771 Region::maybe_invalidate_transients ()
1772 {
1773         bool changed = !_onsets.empty();
1774         _onsets.clear ();
1775
1776         if (_valid_transients || changed) {
1777                 send_change (PropertyChange (Properties::valid_transients));
1778                 return;
1779         }
1780 }
1781
1782 void
1783 Region::transients (AnalysisFeatureList& afl)
1784 {
1785         int cnt = afl.empty() ? 0 : 1;
1786
1787         Region::merge_features (afl, _onsets, _position);
1788         Region::merge_features (afl, _user_transients, _position + _transient_user_start - _start);
1789         if (!_onsets.empty ()) {
1790                 ++cnt;
1791         }
1792         if (!_user_transients.empty ()) {
1793                 ++cnt;
1794         }
1795         if (cnt > 1 ) {
1796                 afl.sort ();
1797                 // remove exact duplicates
1798                 TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
1799         }
1800 }
1801
1802 bool
1803 Region::has_transients () const
1804 {
1805         if (!_user_transients.empty ()) {
1806                 assert (_valid_transients);
1807                 return true;
1808         }
1809         if (!_onsets.empty ()) {
1810                 return true;
1811         }
1812         return false;
1813 }
1814
1815 void
1816 Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
1817 {
1818         for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
1819                 const frameoffset_t p = (*x) + off;
1820                 if (p < first_frame() || p > last_frame()) {
1821                         continue;
1822                 }
1823                 result.push_back (p);
1824         }
1825 }
1826
1827 void
1828 Region::drop_sources ()
1829 {
1830         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1831                 (*i)->dec_use_count ();
1832         }
1833
1834         _sources.clear ();
1835
1836         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1837                 (*i)->dec_use_count ();
1838         }
1839
1840         _master_sources.clear ();
1841 }
1842
1843 void
1844 Region::use_sources (SourceList const & s)
1845 {
1846         set<boost::shared_ptr<Source> > unique_srcs;
1847
1848         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1849
1850                 _sources.push_back (*i);
1851                 (*i)->inc_use_count ();
1852                 _master_sources.push_back (*i);
1853                 (*i)->inc_use_count ();
1854
1855                 /* connect only once to DropReferences, even if sources are replicated
1856                  */
1857
1858                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1859                         unique_srcs.insert (*i);
1860                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1861                 }
1862         }
1863 }
1864
1865 Trimmable::CanTrim
1866 Region::can_trim () const
1867 {
1868         CanTrim ct = CanTrim (0);
1869
1870         if (locked()) {
1871                 return ct;
1872         }
1873
1874         /* if not locked, we can always move the front later, and the end earlier
1875          */
1876
1877         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1878
1879         if (start() != 0 || can_trim_start_before_source_start ()) {
1880                 ct = CanTrim (ct | FrontTrimEarlier);
1881         }
1882
1883         if (!_sources.empty()) {
1884                 if ((start() + length()) < _sources.front()->length (0)) {
1885                         ct = CanTrim (ct | EndTrimLater);
1886                 }
1887         }
1888
1889         return ct;
1890 }
1891
1892 uint32_t
1893 Region::max_source_level () const
1894 {
1895         uint32_t lvl = 0;
1896
1897         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1898                 lvl = max (lvl, (*i)->level());
1899         }
1900
1901         return lvl;
1902 }
1903
1904 bool
1905 Region::is_compound () const
1906 {
1907         return max_source_level() > 0;
1908 }
1909
1910 void
1911 Region::post_set (const PropertyChange& pc)
1912 {
1913         _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1914 }
1915
1916 void
1917 Region::set_start_internal (framecnt_t s, const int32_t sub_num)
1918 {
1919         _start = s;
1920 }
1921
1922 framepos_t
1923 Region::earliest_possible_position () const
1924 {
1925         if (_start > _position) {
1926                 return 0;
1927         } else {
1928                 return _position - _start;
1929         }
1930 }
1931
1932 framecnt_t
1933 Region::latest_possible_frame () const
1934 {
1935         framecnt_t minlen = max_framecnt;
1936
1937         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1938                 /* non-audio regions have a length that may vary based on their
1939                  * position, so we have to pass it in the call.
1940                  */
1941                 minlen = min (minlen, (*i)->length (_position));
1942         }
1943
1944         /* the latest possible last frame is determined by the current
1945          * position, plus the shortest source extent past _start.
1946          */
1947
1948         return _position + (minlen - _start) - 1;
1949 }