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