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