8640b53a6e4cd3f798ea2ea6cab0b2e561458fd3
[ardour.git] / libs / ardour / region.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24 #include <sstream>
25
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28
29 #include "ardour/debug.h"
30 #include "ardour/filter.h"
31 #include "ardour/playlist.h"
32 #include "ardour/playlist_source.h"
33 #include "ardour/profile.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/transient_detector.h"
40
41 #include "pbd/i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46
47 namespace ARDOUR {
48         class Progress;
49         namespace Properties {
50                 PBD::PropertyDescriptor<bool> muted;
51                 PBD::PropertyDescriptor<bool> opaque;
52                 PBD::PropertyDescriptor<bool> locked;
53                 PBD::PropertyDescriptor<bool> video_locked;
54                 PBD::PropertyDescriptor<bool> automatic;
55                 PBD::PropertyDescriptor<bool> whole_file;
56                 PBD::PropertyDescriptor<bool> import;
57                 PBD::PropertyDescriptor<bool> external;
58                 PBD::PropertyDescriptor<bool> sync_marked;
59                 PBD::PropertyDescriptor<bool> left_of_split;
60                 PBD::PropertyDescriptor<bool> right_of_split;
61                 PBD::PropertyDescriptor<bool> hidden;
62                 PBD::PropertyDescriptor<bool> position_locked;
63                 PBD::PropertyDescriptor<bool> valid_transients;
64                 PBD::PropertyDescriptor<framepos_t> start;
65                 PBD::PropertyDescriptor<framecnt_t> length;
66                 PBD::PropertyDescriptor<framepos_t> position;
67                 PBD::PropertyDescriptor<double> beat;
68                 PBD::PropertyDescriptor<framecnt_t> sync_position;
69                 PBD::PropertyDescriptor<layer_t> layer;
70                 PBD::PropertyDescriptor<framepos_t> ancestral_start;
71                 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
72                 PBD::PropertyDescriptor<float> stretch;
73                 PBD::PropertyDescriptor<float> shift;
74                 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
75                 PBD::PropertyDescriptor<uint64_t> layering_index;
76         }
77 }
78
79 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
80
81 void
82 Region::make_property_quarks ()
83 {
84         Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
85         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n",       Properties::muted.property_id));
86         Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
87         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n",      Properties::opaque.property_id));
88         Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
89         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n",      Properties::locked.property_id));
90         Properties::video_locked.property_id = g_quark_from_static_string (X_("video-locked"));
91         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for video-locked = %1\n",        Properties::video_locked.property_id));
92         Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
93         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n",   Properties::automatic.property_id));
94         Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
95         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n",  Properties::whole_file.property_id));
96         Properties::import.property_id = g_quark_from_static_string (X_("import"));
97         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n",      Properties::import.property_id));
98         Properties::external.property_id = g_quark_from_static_string (X_("external"));
99         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n",    Properties::external.property_id));
100         Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
101         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n",         Properties::sync_marked.property_id));
102         Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
103         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n",       Properties::left_of_split.property_id));
104         Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
105         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n",      Properties::right_of_split.property_id));
106         Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
107         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n",      Properties::hidden.property_id));
108         Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
109         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n",     Properties::position_locked.property_id));
110         Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
111         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n",    Properties::valid_transients.property_id));
112         Properties::start.property_id = g_quark_from_static_string (X_("start"));
113         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n",       Properties::start.property_id));
114         Properties::length.property_id = g_quark_from_static_string (X_("length"));
115         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n",      Properties::length.property_id));
116         Properties::position.property_id = g_quark_from_static_string (X_("position"));
117         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n",    Properties::position.property_id));
118         Properties::beat.property_id = g_quark_from_static_string (X_("beat"));
119         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for beat = %1\n",        Properties::beat.property_id));
120         Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n",       Properties::sync_position.property_id));
122         Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n",       Properties::layer.property_id));
124         Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n",     Properties::ancestral_start.property_id));
126         Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n",    Properties::ancestral_length.property_id));
128         Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n",     Properties::stretch.property_id));
130         Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n",       Properties::shift.property_id));
132         Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n",         Properties::position_lock_style.property_id));
134         Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
135         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n",      Properties::layering_index.property_id));
136 }
137
138 void
139 Region::register_properties ()
140 {
141         _xml_node_name = X_("Region");
142
143         add_property (_muted);
144         add_property (_opaque);
145         add_property (_locked);
146         add_property (_video_locked);
147         add_property (_automatic);
148         add_property (_whole_file);
149         add_property (_import);
150         add_property (_external);
151         add_property (_sync_marked);
152         add_property (_left_of_split);
153         add_property (_right_of_split);
154         add_property (_hidden);
155         add_property (_position_locked);
156         add_property (_valid_transients);
157         add_property (_start);
158         add_property (_length);
159         add_property (_position);
160         add_property (_beat);
161         add_property (_sync_position);
162         add_property (_ancestral_start);
163         add_property (_ancestral_length);
164         add_property (_stretch);
165         add_property (_shift);
166         add_property (_position_lock_style);
167         add_property (_layering_index);
168 }
169
170 #define REGION_DEFAULT_STATE(s,l) \
171         _sync_marked (Properties::sync_marked, false) \
172         , _left_of_split (Properties::left_of_split, false) \
173         , _right_of_split (Properties::right_of_split, false) \
174         , _valid_transients (Properties::valid_transients, false) \
175         , _start (Properties::start, (s))       \
176         , _length (Properties::length, (l))     \
177         , _position (Properties::position, 0) \
178         , _beat (Properties::beat, 0.0) \
179         , _sync_position (Properties::sync_position, (s)) \
180         , _quarter_note (0.0) \
181         , _transient_user_start (0) \
182         , _transient_analysis_start (0) \
183         , _transient_analysis_end (0) \
184         , _muted (Properties::muted, false) \
185         , _opaque (Properties::opaque, true) \
186         , _locked (Properties::locked, false) \
187   , _video_locked (Properties::video_locked, false) \
188         , _automatic (Properties::automatic, false) \
189         , _whole_file (Properties::whole_file, false) \
190         , _import (Properties::import, false) \
191         , _external (Properties::external, false) \
192         , _hidden (Properties::hidden, false) \
193         , _position_locked (Properties::position_locked, false) \
194         , _ancestral_start (Properties::ancestral_start, (s)) \
195         , _ancestral_length (Properties::ancestral_length, (l)) \
196         , _stretch (Properties::stretch, 1.0) \
197         , _shift (Properties::shift, 1.0) \
198         , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
199         , _layering_index (Properties::layering_index, 0)
200
201 #define REGION_COPY_STATE(other) \
202           _sync_marked (Properties::sync_marked, other->_sync_marked) \
203         , _left_of_split (Properties::left_of_split, other->_left_of_split) \
204         , _right_of_split (Properties::right_of_split, other->_right_of_split) \
205         , _valid_transients (Properties::valid_transients, other->_valid_transients) \
206         , _start(Properties::start, other->_start)              \
207         , _length(Properties::length, other->_length)           \
208         , _position(Properties::position, other->_position)     \
209         , _beat (Properties::beat, other->_beat)                \
210         , _sync_position(Properties::sync_position, other->_sync_position) \
211         , _quarter_note (other->_quarter_note)                                \
212         , _user_transients (other->_user_transients) \
213         , _transient_user_start (other->_transient_user_start) \
214         , _transients (other->_transients) \
215         , _transient_analysis_start (other->_transient_analysis_start) \
216         , _transient_analysis_end (other->_transient_analysis_end) \
217         , _muted (Properties::muted, other->_muted)             \
218         , _opaque (Properties::opaque, other->_opaque)          \
219         , _locked (Properties::locked, other->_locked)          \
220   , _video_locked (Properties::video_locked, other->_video_locked) \
221         , _automatic (Properties::automatic, other->_automatic) \
222         , _whole_file (Properties::whole_file, other->_whole_file) \
223         , _import (Properties::import, other->_import)          \
224         , _external (Properties::external, other->_external)    \
225         , _hidden (Properties::hidden, other->_hidden)          \
226         , _position_locked (Properties::position_locked, other->_position_locked) \
227         , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
228         , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
229         , _stretch (Properties::stretch, other->_stretch)       \
230         , _shift (Properties::shift, other->_shift)             \
231         , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
232         , _layering_index (Properties::layering_index, other->_layering_index)
233
234 /* derived-from-derived constructor (no sources in constructor) */
235 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
236         : SessionObject(s, name)
237         , _type(type)
238         , REGION_DEFAULT_STATE(start,length)
239         , _last_length (length)
240         , _last_position (0)
241         , _first_edit (EditChangesNothing)
242         , _layer (0)
243 {
244         register_properties ();
245
246         /* no sources at this point */
247 }
248
249 /** Basic Region constructor (many sources) */
250 Region::Region (const SourceList& srcs)
251         : SessionObject(srcs.front()->session(), "toBeRenamed")
252         , _type (srcs.front()->type())
253         , REGION_DEFAULT_STATE(0,0)
254         , _last_length (0)
255         , _last_position (0)
256         , _first_edit (EditChangesNothing)
257         , _layer (0)
258 {
259         register_properties ();
260
261         _type = srcs.front()->type();
262
263         use_sources (srcs);
264
265         assert(_sources.size() > 0);
266         assert (_type == srcs.front()->type());
267 }
268
269 /** Create a new Region from an existing one */
270 Region::Region (boost::shared_ptr<const Region> other)
271         : SessionObject(other->session(), other->name())
272         , _type (other->data_type())
273         , REGION_COPY_STATE (other)
274         , _last_length (other->_last_length)
275         , _last_position(other->_last_position) \
276         , _first_edit (EditChangesNothing)
277         , _layer (other->_layer)
278 {
279         register_properties ();
280
281         /* override state that may have been incorrectly inherited from the other region
282          */
283
284         _position = other->_position;
285         _locked = false;
286         _whole_file = false;
287         _hidden = false;
288
289         use_sources (other->_sources);
290         set_master_sources (other->_master_sources);
291
292         _position_lock_style = other->_position_lock_style;
293         _first_edit = other->_first_edit;
294
295         _start = other->_start;
296         _beat = other->_beat;
297         _quarter_note = other->_quarter_note;
298
299         /* sync pos is relative to start of file. our start-in-file is now zero,
300            so set our sync position to whatever the the difference between
301            _start and _sync_pos was in the other region.
302
303            result is that our new sync pos points to the same point in our source(s)
304            as the sync in the other region did in its source(s).
305
306            since we start at zero in our source(s), it is not possible to use a sync point that
307            is before the start. reset it to _start if that was true in the other region.
308         */
309
310         if (other->sync_marked()) {
311                 if (other->_start < other->_sync_position) {
312                         /* sync pos was after the start point of the other region */
313                         _sync_position = other->_sync_position - other->_start;
314                 } else {
315                         /* sync pos was before the start point of the other region. not possible here. */
316                         _sync_marked = false;
317                         _sync_position = _start;
318                 }
319         } else {
320                 _sync_marked = false;
321                 _sync_position = _start;
322         }
323
324         assert (_type == other->data_type());
325 }
326
327 /** Create a new Region from part of an existing one.
328
329     the start within \a other is given by \a offset
330     (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
331 */
332 Region::Region (boost::shared_ptr<const Region> other, MusicFrame offset)
333         : SessionObject(other->session(), other->name())
334         , _type (other->data_type())
335         , REGION_COPY_STATE (other)
336         , _last_length (other->_last_length)
337         , _last_position(other->_last_position) \
338         , _first_edit (EditChangesNothing)
339         , _layer (other->_layer)
340 {
341         register_properties ();
342
343         /* override state that may have been incorrectly inherited from the other region
344          */
345
346         _locked = false;
347         _whole_file = false;
348         _hidden = false;
349
350         use_sources (other->_sources);
351         set_master_sources (other->_master_sources);
352
353         _position = other->_position + offset.frame;
354         _start = other->_start + offset.frame;
355
356         /* prevent offset of 0 from altering musical position */
357         if (offset.frame != 0) {
358                 const double offset_qn = _session.tempo_map().exact_qn_at_frame (other->_position + offset.frame, offset.division)
359                         - other->_quarter_note;
360
361                 _quarter_note = other->_quarter_note + offset_qn;
362                 _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
363         } else {
364                 _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
365         }
366
367         /* if the other region had a distinct sync point
368            set, then continue to use it as best we can.
369            otherwise, reset sync point back to start.
370         */
371
372         if (other->sync_marked()) {
373                 if (other->_sync_position < _start) {
374                         _sync_marked = false;
375                         _sync_position = _start;
376                 } else {
377                         _sync_position = other->_sync_position;
378                 }
379         } else {
380                 _sync_marked = false;
381                 _sync_position = _start;
382         }
383
384         assert (_type == other->data_type());
385 }
386
387 /** Create a copy of @param other but with different sources. Used by filters */
388 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
389         : SessionObject (other->session(), other->name())
390         , _type (srcs.front()->type())
391         , REGION_COPY_STATE (other)
392         , _last_length (other->_last_length)
393         , _last_position (other->_last_position)
394         , _first_edit (EditChangesID)
395         , _layer (other->_layer)
396 {
397         register_properties ();
398
399         _locked = false;
400         _position_locked = false;
401
402         other->_first_edit = EditChangesName;
403
404         if (other->_extra_xml) {
405                 _extra_xml = new XMLNode (*other->_extra_xml);
406         } else {
407                 _extra_xml = 0;
408         }
409
410         use_sources (srcs);
411         assert(_sources.size() > 0);
412 }
413
414 Region::~Region ()
415 {
416         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
417         drop_sources ();
418 }
419
420 void
421 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
422 {
423         _playlist = wpl.lock();
424 }
425
426 bool
427 Region::set_name (const std::string& str)
428 {
429         if (_name != str) {
430                 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
431                 assert(_name == str);
432
433                 send_change (Properties::name);
434         }
435
436         return true;
437 }
438
439 void
440 Region::set_length (framecnt_t len, const int32_t sub_num)
441 {
442         //cerr << "Region::set_length() len = " << len << endl;
443         if (locked()) {
444                 return;
445         }
446
447         if (_length != len && len != 0) {
448
449                 /* check that the current _position wouldn't make the new
450                    length impossible.
451                 */
452
453                 if (max_framepos - len < _position) {
454                         return;
455                 }
456
457                 if (!verify_length (len)) {
458                         return;
459                 }
460
461
462                 set_length_internal (len, sub_num);
463                 _whole_file = false;
464                 first_edit ();
465                 maybe_uncopy ();
466                 maybe_invalidate_transients ();
467
468                 if (!property_changes_suspended()) {
469                         recompute_at_end ();
470                 }
471
472                 send_change (Properties::length);
473         }
474 }
475
476 void
477 Region::set_length_internal (framecnt_t len, const int32_t sub_num)
478 {
479         _last_length = _length;
480         _length = len;
481 }
482
483 void
484 Region::maybe_uncopy ()
485 {
486         /* this does nothing but marked a semantic moment once upon a time */
487 }
488
489 void
490 Region::first_edit ()
491 {
492         boost::shared_ptr<Playlist> pl (playlist());
493
494         if (_first_edit != EditChangesNothing && pl) {
495
496                 _name = RegionFactory::new_region_name (_name);
497                 _first_edit = EditChangesNothing;
498
499                 send_change (Properties::name);
500
501                 RegionFactory::CheckNewRegion (shared_from_this());
502         }
503 }
504
505 bool
506 Region::at_natural_position () const
507 {
508         boost::shared_ptr<Playlist> pl (playlist());
509
510         if (!pl) {
511                 return false;
512         }
513
514         boost::shared_ptr<Region> whole_file_region = get_parent();
515
516         if (whole_file_region) {
517                 if (_position == whole_file_region->position() + _start) {
518                         return true;
519                 }
520         }
521
522         return false;
523 }
524
525 void
526 Region::move_to_natural_position ()
527 {
528         boost::shared_ptr<Playlist> pl (playlist());
529
530         if (!pl) {
531                 return;
532         }
533
534         boost::shared_ptr<Region> whole_file_region = get_parent();
535
536         if (whole_file_region) {
537                 set_position (whole_file_region->position() + _start);
538         }
539 }
540
541 void
542 Region::special_set_position (framepos_t pos)
543 {
544         /* this is used when creating a whole file region as
545            a way to store its "natural" or "captured" position.
546         */
547
548         _position = _position;
549         _position = pos;
550 }
551
552 void
553 Region::set_position_lock_style (PositionLockStyle ps)
554 {
555         if (_position_lock_style != ps) {
556
557                 boost::shared_ptr<Playlist> pl (playlist());
558
559                 _position_lock_style = ps;
560
561                 send_change (Properties::position_lock_style);
562         }
563 }
564
565 void
566 Region::update_after_tempo_map_change (bool send)
567 {
568         boost::shared_ptr<Playlist> pl (playlist());
569
570         if (!pl) {
571                 return;
572         }
573
574         if (_position_lock_style == AudioTime) {
575                 /* don't signal as the actual position has not chnged */
576                 recompute_position_from_lock_style (0);
577                 return;
578         }
579
580         /* prevent movement before 0 */
581         const framepos_t pos = max ((framepos_t) 0, _session.tempo_map().frame_at_beat (_beat));
582         /* we have _beat. update frame position non-musically */
583         set_position_internal (pos, false, 0);
584
585         /* do this even if the position is the same. this helps out
586            a GUI that has moved its representation already.
587         */
588
589         if (send) {
590                 send_change (Properties::position);
591         }
592 }
593
594 void
595 Region::set_position (framepos_t pos, int32_t sub_num)
596 {
597         if (!can_move()) {
598                 return;
599         }
600
601         /* do this even if the position is the same. this helps out
602            a GUI that has moved its representation already.
603         */
604         PropertyChange p_and_l;
605
606         p_and_l.add (Properties::position);
607
608         if (position_lock_style() == AudioTime) {
609                 set_position_internal (pos, true, sub_num);
610         } else {
611                 if (!_session.loading()) {
612                         _beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
613                         _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
614                 }
615
616                 set_position_internal (pos, false, sub_num);
617         }
618
619         if (position_lock_style() == MusicTime) {
620                 p_and_l.add (Properties::length);
621         }
622
623         send_change (p_and_l);
624
625 }
626
627 void
628 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
629 {
630         /* We emit a change of Properties::position even if the position hasn't changed
631            (see Region::set_position), so we must always set this up so that
632            e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
633         */
634         _last_position = _position;
635
636         if (_position != pos) {
637                 _position = pos;
638
639                 if (allow_bbt_recompute) {
640                         recompute_position_from_lock_style (sub_num);
641                 } else {
642                         /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
643                         _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
644                 }
645
646                 /* check that the new _position wouldn't make the current
647                    length impossible - if so, change the length.
648
649                    XXX is this the right thing to do?
650                 */
651                 if (max_framepos - _length < _position) {
652                         _last_length = _length;
653                         _length = max_framepos - _position;
654                 }
655         }
656 }
657
658 void
659 Region::set_position_music (double qn)
660 {
661         if (!can_move()) {
662                 return;
663         }
664
665         /* do this even if the position is the same. this helps out
666            a GUI that has moved its representation already.
667         */
668         PropertyChange p_and_l;
669
670         p_and_l.add (Properties::position);
671
672         if (!_session.loading()) {
673                 _beat = _session.tempo_map().beat_at_quarter_note (qn);
674         }
675
676         /* will set frame accordingly */
677         set_position_music_internal (qn);
678
679         if (position_lock_style() == MusicTime) {
680                 p_and_l.add (Properties::length);
681         }
682
683         send_change (p_and_l);
684 }
685
686 void
687 Region::set_position_music_internal (double qn)
688 {
689         /* We emit a change of Properties::position even if the position hasn't changed
690            (see Region::set_position), so we must always set this up so that
691            e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
692         */
693         _last_position = _position;
694
695         if (_quarter_note != qn) {
696                 _position = _session.tempo_map().frame_at_quarter_note (qn);
697                 _quarter_note = qn;
698
699                 /* check that the new _position wouldn't make the current
700                    length impossible - if so, change the length.
701
702                    XXX is this the right thing to do?
703                 */
704                 if (max_framepos - _length < _position) {
705                         _last_length = _length;
706                         _length = max_framepos - _position;
707                 }
708         }
709 }
710
711 /** A gui may need to create a region, then place it in an initial
712  *  position determined by the user.
713  *  When this takes place within one gui operation, we have to reset
714  *  _last_position to prevent an implied move.
715  */
716 void
717 Region::set_initial_position (framepos_t pos)
718 {
719         if (!can_move()) {
720                 return;
721         }
722
723         if (_position != pos) {
724                 _position = pos;
725
726                 /* check that the new _position wouldn't make the current
727                    length impossible - if so, change the length.
728
729                    XXX is this the right thing to do?
730                 */
731
732                 if (max_framepos - _length < _position) {
733                         _last_length = _length;
734                         _length = max_framepos - _position;
735                 }
736
737                 recompute_position_from_lock_style (0);
738                 /* ensure that this move doesn't cause a range move */
739                 _last_position = _position;
740         }
741
742
743         /* do this even if the position is the same. this helps out
744            a GUI that has moved its representation already.
745         */
746         send_change (Properties::position);
747 }
748
749 void
750 Region::recompute_position_from_lock_style (const int32_t sub_num)
751 {
752         _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
753         _quarter_note = _session.tempo_map().exact_qn_at_frame (_position, sub_num);
754 }
755
756 void
757 Region::nudge_position (frameoffset_t n)
758 {
759         if (locked() || video_locked()) {
760                 return;
761         }
762
763         if (n == 0) {
764                 return;
765         }
766
767         framepos_t new_position = _position;
768
769         if (n > 0) {
770                 if (_position > max_framepos - n) {
771                         new_position = max_framepos;
772                 } else {
773                         new_position += n;
774                 }
775         } else {
776                 if (_position < -n) {
777                         new_position = 0;
778                 } else {
779                         new_position += n;
780                 }
781         }
782         /* assumes non-musical nudge */
783         set_position_internal (new_position, true, 0);
784
785         send_change (Properties::position);
786 }
787
788 void
789 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
790 {
791         _ancestral_length = l;
792         _ancestral_start = s;
793         _stretch = st;
794         _shift = sh;
795 }
796
797 void
798 Region::set_start (framepos_t pos)
799 {
800         if (locked() || position_locked() || video_locked()) {
801                 return;
802         }
803         /* This just sets the start, nothing else. It effectively shifts
804            the contents of the Region within the overall extent of the Source,
805            without changing the Region's position or length
806         */
807
808         if (_start != pos) {
809
810                 if (!verify_start (pos)) {
811                         return;
812                 }
813
814                 set_start_internal (pos);
815                 _whole_file = false;
816                 first_edit ();
817                 maybe_invalidate_transients ();
818
819                 send_change (Properties::start);
820         }
821 }
822
823 void
824 Region::move_start (frameoffset_t distance, const int32_t sub_num)
825 {
826         if (locked() || position_locked() || video_locked()) {
827                 return;
828         }
829
830         framepos_t new_start;
831
832         if (distance > 0) {
833
834                 if (_start > max_framepos - distance) {
835                         new_start = max_framepos; // makes no sense
836                 } else {
837                         new_start = _start + distance;
838                 }
839
840                 if (!verify_start (new_start)) {
841                         return;
842                 }
843
844         } else if (distance < 0) {
845
846                 if (_start < -distance) {
847                         new_start = 0;
848                 } else {
849                         new_start = _start + distance;
850                 }
851
852         } else {
853                 return;
854         }
855
856         if (new_start == _start) {
857                 return;
858         }
859
860         set_start_internal (new_start, sub_num);
861
862         _whole_file = false;
863         first_edit ();
864
865         send_change (Properties::start);
866 }
867
868 void
869 Region::trim_front (framepos_t new_position, const int32_t sub_num)
870 {
871         modify_front (new_position, false, sub_num);
872 }
873
874 void
875 Region::cut_front (framepos_t new_position, const int32_t sub_num)
876 {
877         modify_front (new_position, true, sub_num);
878 }
879
880 void
881 Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
882 {
883         modify_end (new_endpoint, true, sub_num);
884 }
885
886 void
887 Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
888 {
889         if (locked()) {
890                 return;
891         }
892
893         framepos_t end = last_frame();
894         framepos_t source_zero;
895
896         if (_position > _start) {
897                 source_zero = _position - _start;
898         } else {
899                 source_zero = 0; // its actually negative, but this will work for us
900         }
901
902         if (new_position < end) { /* can't trim it zero or negative length */
903
904                 framecnt_t newlen = 0;
905
906                 if (!can_trim_start_before_source_start ()) {
907                         /* can't trim it back past where source position zero is located */
908                         new_position = max (new_position, source_zero);
909                 }
910
911                 if (new_position > _position) {
912                         newlen = _length - (new_position - _position);
913                 } else {
914                         newlen = _length + (_position - new_position);
915                 }
916
917                 trim_to_internal (new_position, newlen, sub_num);
918
919                 if (reset_fade) {
920                         _right_of_split = true;
921                 }
922
923                 if (!property_changes_suspended()) {
924                         recompute_at_start ();
925                 }
926
927                 maybe_invalidate_transients ();
928         }
929 }
930
931 void
932 Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
933 {
934         if (locked()) {
935                 return;
936         }
937
938         if (new_endpoint > _position) {
939                 trim_to_internal (_position, new_endpoint - _position, sub_num);
940                 if (reset_fade) {
941                         _left_of_split = true;
942                 }
943                 if (!property_changes_suspended()) {
944                         recompute_at_end ();
945                 }
946         }
947 }
948
949 /** @param new_endpoint New region end point, such that, for example,
950  *  a region at 0 of length 10 has an endpoint of 9.
951  */
952
953 void
954 Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
955 {
956         modify_end (new_endpoint, false, sub_num);
957 }
958
959 void
960 Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
961 {
962         if (locked()) {
963                 return;
964         }
965
966         trim_to_internal (position, length, sub_num);
967
968         if (!property_changes_suspended()) {
969                 recompute_at_start ();
970                 recompute_at_end ();
971         }
972 }
973
974 void
975 Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
976 {
977         framepos_t new_start;
978
979         if (locked()) {
980                 return;
981         }
982
983         frameoffset_t const start_shift = position - _position;
984
985         if (start_shift > 0) {
986
987                 if (_start > max_framepos - start_shift) {
988                         new_start = max_framepos;
989                 } else {
990                         new_start = _start + start_shift;
991                 }
992
993         } else if (start_shift < 0) {
994
995                 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
996                         new_start = 0;
997                 } else {
998                         new_start = _start + start_shift;
999                 }
1000
1001         } else {
1002                 new_start = _start;
1003         }
1004
1005         if (!verify_start_and_length (new_start, length)) {
1006                 return;
1007         }
1008
1009         PropertyChange what_changed;
1010
1011         if (_start != new_start) {
1012                 set_start_internal (new_start, sub_num);
1013                 what_changed.add (Properties::start);
1014         }
1015
1016
1017         /* Set position before length, otherwise for MIDI regions this bad thing happens:
1018          * 1. we call set_length_internal; length in beats is computed using the region's current
1019          *    (soon-to-be old) position
1020          * 2. we call set_position_internal; position is set and length in frames re-computed using
1021          *    length in beats from (1) but at the new position, which is wrong if the region
1022          *    straddles a tempo/meter change.
1023          */
1024
1025         if (_position != position) {
1026                 if (!property_changes_suspended()) {
1027                         _last_position = _position;
1028                 }
1029                 set_position_internal (position, true, sub_num);
1030                 what_changed.add (Properties::position);
1031         }
1032
1033         if (_length != length) {
1034                 if (!property_changes_suspended()) {
1035                         _last_length = _length;
1036                 }
1037                 set_length_internal (length, sub_num);
1038                 what_changed.add (Properties::length);
1039         }
1040
1041         _whole_file = false;
1042
1043         PropertyChange start_and_length;
1044
1045         start_and_length.add (Properties::start);
1046         start_and_length.add (Properties::length);
1047
1048         if (what_changed.contains (start_and_length)) {
1049                 first_edit ();
1050         }
1051
1052         if (!what_changed.empty()) {
1053                 send_change (what_changed);
1054         }
1055 }
1056
1057 void
1058 Region::set_hidden (bool yn)
1059 {
1060         if (hidden() != yn) {
1061                 _hidden = yn;
1062                 send_change (Properties::hidden);
1063         }
1064 }
1065
1066 void
1067 Region::set_whole_file (bool yn)
1068 {
1069         _whole_file = yn;
1070         /* no change signal */
1071 }
1072
1073 void
1074 Region::set_automatic (bool yn)
1075 {
1076         _automatic = yn;
1077         /* no change signal */
1078 }
1079
1080 void
1081 Region::set_muted (bool yn)
1082 {
1083         if (muted() != yn) {
1084                 _muted = yn;
1085                 send_change (Properties::muted);
1086         }
1087 }
1088
1089 void
1090 Region::set_opaque (bool yn)
1091 {
1092         if (opaque() != yn) {
1093                 _opaque = yn;
1094                 send_change (Properties::opaque);
1095         }
1096 }
1097
1098 void
1099 Region::set_locked (bool yn)
1100 {
1101         if (locked() != yn) {
1102                 _locked = yn;
1103                 send_change (Properties::locked);
1104         }
1105 }
1106
1107 void
1108 Region::set_video_locked (bool yn)
1109 {
1110         if (video_locked() != yn) {
1111                 _video_locked = yn;
1112                 send_change (Properties::video_locked);
1113         }
1114 }
1115
1116 void
1117 Region::set_position_locked (bool yn)
1118 {
1119         if (position_locked() != yn) {
1120                 _position_locked = yn;
1121                 send_change (Properties::locked);
1122         }
1123 }
1124
1125 /** Set the region's sync point.
1126  *  @param absolute_pos Session time.
1127  */
1128 void
1129 Region::set_sync_position (framepos_t absolute_pos)
1130 {
1131         /* position within our file */
1132         framepos_t const file_pos = _start + (absolute_pos - _position);
1133
1134         if (file_pos != _sync_position) {
1135                 _sync_marked = true;
1136                 _sync_position = file_pos;
1137                 if (!property_changes_suspended()) {
1138                         maybe_uncopy ();
1139                 }
1140
1141                 send_change (Properties::sync_position);
1142         }
1143 }
1144
1145 void
1146 Region::clear_sync_position ()
1147 {
1148         if (sync_marked()) {
1149                 _sync_marked = false;
1150                 if (!property_changes_suspended()) {
1151                         maybe_uncopy ();
1152                 }
1153
1154                 send_change (Properties::sync_position);
1155         }
1156 }
1157
1158 /* @return the sync point relative the first frame of the region */
1159 frameoffset_t
1160 Region::sync_offset (int& dir) const
1161 {
1162         if (sync_marked()) {
1163                 if (_sync_position > _start) {
1164                         dir = 1;
1165                         return _sync_position - _start;
1166                 } else {
1167                         dir = -1;
1168                         return _start - _sync_position;
1169                 }
1170         } else {
1171                 dir = 0;
1172                 return 0;
1173         }
1174 }
1175
1176 framepos_t
1177 Region::adjust_to_sync (framepos_t pos) const
1178 {
1179         int sync_dir;
1180         frameoffset_t offset = sync_offset (sync_dir);
1181
1182         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1183
1184         if (sync_dir > 0) {
1185                 if (pos > offset) {
1186                         pos -= offset;
1187                 } else {
1188                         pos = 0;
1189                 }
1190         } else {
1191                 if (max_framepos - pos > offset) {
1192                         pos += offset;
1193                 }
1194         }
1195
1196         return pos;
1197 }
1198
1199 /** @return Sync position in session time */
1200 framepos_t
1201 Region::sync_position() const
1202 {
1203         if (sync_marked()) {
1204                 return _position - _start + _sync_position;
1205         } else {
1206                 /* if sync has not been marked, use the start of the region */
1207                 return _position;
1208         }
1209 }
1210
1211 void
1212 Region::raise ()
1213 {
1214         boost::shared_ptr<Playlist> pl (playlist());
1215         if (pl) {
1216                 pl->raise_region (shared_from_this ());
1217         }
1218 }
1219
1220 void
1221 Region::lower ()
1222 {
1223         boost::shared_ptr<Playlist> pl (playlist());
1224         if (pl) {
1225                 pl->lower_region (shared_from_this ());
1226         }
1227 }
1228
1229
1230 void
1231 Region::raise_to_top ()
1232 {
1233         boost::shared_ptr<Playlist> pl (playlist());
1234         if (pl) {
1235                 pl->raise_region_to_top (shared_from_this());
1236         }
1237 }
1238
1239 void
1240 Region::lower_to_bottom ()
1241 {
1242         boost::shared_ptr<Playlist> pl (playlist());
1243         if (pl) {
1244                 pl->lower_region_to_bottom (shared_from_this());
1245         }
1246 }
1247
1248 void
1249 Region::set_layer (layer_t l)
1250 {
1251         _layer = l;
1252 }
1253
1254 XMLNode&
1255 Region::state ()
1256 {
1257         XMLNode *node = new XMLNode ("Region");
1258         char buf[64];
1259         char buf2[64];
1260         LocaleGuard lg;
1261         const char* fe = NULL;
1262
1263         /* custom version of 'add_properties (*node);'
1264          * skip values that have have dedicated save functions
1265          * in AudioRegion::state()
1266          */
1267         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1268                 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1269                 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1270                 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1271                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1272                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1273                 i->second->get_value (*node);
1274         }
1275
1276         id().print (buf, sizeof (buf));
1277         node->add_property ("id", buf);
1278         node->add_property ("type", _type.to_string());
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->add_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                 _sources[n]->id().print (buf, sizeof(buf));
1302                 node->add_property (buf2, buf);
1303         }
1304
1305         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1306                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1307                 _master_sources[n]->id().print (buf, sizeof (buf));
1308                 node->add_property (buf2, buf);
1309         }
1310
1311         /* Only store nested sources for the whole-file region that acts
1312            as the parent/root of all regions using it.
1313         */
1314
1315         if (_whole_file && max_source_level() > 0) {
1316
1317                 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1318
1319                 /* region is compound - get its playlist and
1320                    store that before we list the region that
1321                    needs it ...
1322                 */
1323
1324                 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1325                         nested_node->add_child_nocopy ((*s)->get_state ());
1326                 }
1327
1328                 if (nested_node) {
1329                         node->add_child_nocopy (*nested_node);
1330                 }
1331         }
1332
1333         if (_extra_xml) {
1334                 node->add_child_copy (*_extra_xml);
1335         }
1336
1337         return *node;
1338 }
1339
1340 XMLNode&
1341 Region::get_state ()
1342 {
1343         return state ();
1344 }
1345
1346 int
1347 Region::set_state (const XMLNode& node, int version)
1348 {
1349         PropertyChange what_changed;
1350         return _set_state (node, version, what_changed, true);
1351 }
1352
1353 int
1354 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1355 {
1356         XMLProperty const * prop;
1357         Timecode::BBT_Time bbt_time;
1358
1359         Stateful::save_extra_xml (node);
1360
1361         what_changed = set_values (node);
1362
1363         set_id (node);
1364
1365         if (_position_lock_style == MusicTime) {
1366                 if ((prop = node.property ("bbt-position")) != 0) {
1367                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1368                                     &bbt_time.bars,
1369                                     &bbt_time.beats,
1370                                     &bbt_time.ticks) != 3) {
1371                                 _position_lock_style = AudioTime;
1372                                 _beat = _session.tempo_map().beat_at_frame (_position);
1373                         } else {
1374                                 _beat = _session.tempo_map().beat_at_bbt (bbt_time);
1375                         }
1376                         /* no position property change for legacy Property, so we do this here */
1377                         _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
1378                 }
1379         }
1380
1381         /* fix problems with old sessions corrupted by impossible
1382            values for _stretch or _shift
1383         */
1384         if (_stretch == 0.0f) {
1385                 _stretch = 1.0f;
1386         }
1387
1388         if (_shift == 0.0f) {
1389                 _shift = 1.0f;
1390         }
1391
1392         if (send) {
1393                 send_change (what_changed);
1394         }
1395
1396         /* Quick fix for 2.x sessions when region is muted */
1397         if ((prop = node.property (X_("flags")))) {
1398                 if (string::npos != prop->value().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 }