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