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