Remove confuzzling offset_relative stuff from region construction (pre-properties...
[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/thread.h>
27 #include "pbd/xml++.h"
28 #include "pbd/stacktrace.h"
29 #include "pbd/enumwriter.h"
30
31 #include "ardour/debug.h"
32 #include "ardour/region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/source.h"
36 #include "ardour/tempo.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/filter.h"
39 #include "ardour/profile.h"
40 #include "ardour/utils.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47
48 namespace ARDOUR { 
49         namespace Properties {
50                 PBD::PropertyDescriptor<bool> muted;
51                 PBD::PropertyDescriptor<bool> opaque;
52                 PBD::PropertyDescriptor<bool> locked;
53                 PBD::PropertyDescriptor<bool> automatic;
54                 PBD::PropertyDescriptor<bool> whole_file;
55                 PBD::PropertyDescriptor<bool> import;
56                 PBD::PropertyDescriptor<bool> external;
57                 PBD::PropertyDescriptor<bool> sync_marked;
58                 PBD::PropertyDescriptor<bool> left_of_split;
59                 PBD::PropertyDescriptor<bool> right_of_split;
60                 PBD::PropertyDescriptor<bool> hidden;
61                 PBD::PropertyDescriptor<bool> position_locked;
62                 PBD::PropertyDescriptor<bool> valid_transients;
63                 PBD::PropertyDescriptor<framepos_t> start;
64                 PBD::PropertyDescriptor<framecnt_t> length;
65                 PBD::PropertyDescriptor<framepos_t> position;
66                 PBD::PropertyDescriptor<framecnt_t> sync_position;
67                 PBD::PropertyDescriptor<layer_t> layer;
68                 PBD::PropertyDescriptor<framepos_t> ancestral_start;
69                 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
70                 PBD::PropertyDescriptor<float> stretch;
71                 PBD::PropertyDescriptor<float> shift;
72                 PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
73         }
74 }
75         
76 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
77
78 void
79 Region::make_property_quarks ()
80 {
81         Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
82         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n",       Properties::muted.property_id));
83         Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
84         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n",      Properties::opaque.property_id));
85         Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
86         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n",      Properties::locked.property_id));
87         Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
88         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n",   Properties::automatic.property_id));
89         Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
90         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n",  Properties::whole_file.property_id));
91         Properties::import.property_id = g_quark_from_static_string (X_("import"));
92         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n",      Properties::import.property_id));
93         Properties::external.property_id = g_quark_from_static_string (X_("external"));
94         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n",    Properties::external.property_id));
95         Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
96         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n",         Properties::sync_marked.property_id));
97         Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
98         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n",       Properties::left_of_split.property_id));
99         Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
100         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n",      Properties::right_of_split.property_id));
101         Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
102         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n",      Properties::hidden.property_id));
103         Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
104         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n",     Properties::position_locked.property_id));
105         Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
106         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n",    Properties::valid_transients.property_id));
107         Properties::start.property_id = g_quark_from_static_string (X_("start"));
108         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n",       Properties::start.property_id));
109         Properties::length.property_id = g_quark_from_static_string (X_("length"));
110         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n",      Properties::length.property_id));
111         Properties::position.property_id = g_quark_from_static_string (X_("position"));
112         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n",    Properties::position.property_id));
113         Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
114         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n",       Properties::sync_position.property_id));
115         Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
116         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n",       Properties::layer.property_id));
117         Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
118         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n",     Properties::ancestral_start.property_id));
119         Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
120         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n",    Properties::ancestral_length.property_id));
121         Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
122         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n",     Properties::stretch.property_id));
123         Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
124         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n",       Properties::shift.property_id));
125         Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
126         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n",         Properties::position_lock_style.property_id));
127 }
128
129 void
130 Region::register_properties ()
131 {
132         _xml_node_name = X_("Region");
133
134         add_property (_muted);
135         add_property (_opaque);
136         add_property (_locked);
137         add_property (_automatic);
138         add_property (_whole_file);
139         add_property (_import);
140         add_property (_external);
141         add_property (_sync_marked);
142         add_property (_left_of_split);
143         add_property (_right_of_split);
144         add_property (_hidden);
145         add_property (_position_locked);
146         add_property (_valid_transients);
147         add_property (_start);
148         add_property (_length);
149         add_property (_position);
150         add_property (_sync_position);
151         add_property (_layer);
152         add_property (_ancestral_start);
153         add_property (_ancestral_length);
154         add_property (_stretch);
155         add_property (_shift);
156         add_property (_position_lock_style);
157 }
158
159 #define REGION_DEFAULT_STATE(s,l) \
160         _muted (Properties::muted, false)            \
161         , _opaque (Properties::opaque, true) \
162         , _locked (Properties::locked, false) \
163         , _automatic (Properties::automatic, false) \
164         , _whole_file (Properties::whole_file, false) \
165         , _import (Properties::import, false) \
166         , _external (Properties::external, false) \
167         , _sync_marked (Properties::sync_marked, false) \
168         , _left_of_split (Properties::left_of_split, false) \
169         , _right_of_split (Properties::right_of_split, false) \
170         , _hidden (Properties::hidden, false) \
171         , _position_locked (Properties::position_locked, false) \
172         , _valid_transients (Properties::valid_transients, false) \
173         , _start (Properties::start, (s))       \
174         , _length (Properties::length, (l))     \
175         , _position (Properties::position, 0) \
176         , _sync_position (Properties::sync_position, (s)) \
177         , _layer (Properties::layer, 0) \
178         , _ancestral_start (Properties::ancestral_start, (s)) \
179         , _ancestral_length (Properties::ancestral_length, (l)) \
180         , _stretch (Properties::stretch, 1.0) \
181         , _shift (Properties::shift, 1.0) \
182         , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
183
184 #define REGION_COPY_STATE(other) \
185           _muted (Properties::muted, other->_muted)                     \
186         , _opaque (Properties::opaque, other->_opaque)          \
187         , _locked (Properties::locked, other->_locked)          \
188         , _automatic (Properties::automatic, other->_automatic) \
189         , _whole_file (Properties::whole_file, other->_whole_file) \
190         , _import (Properties::import, other->_import)          \
191         , _external (Properties::external, other->_external)    \
192         , _sync_marked (Properties::sync_marked, other->_sync_marked) \
193         , _left_of_split (Properties::left_of_split, other->_left_of_split) \
194         , _right_of_split (Properties::right_of_split, other->_right_of_split) \
195         , _hidden (Properties::hidden, other->_hidden)          \
196         , _position_locked (Properties::position_locked, other->_position_locked) \
197         , _valid_transients (Properties::valid_transients, other->_valid_transients) \
198         , _start(Properties::start, other->_start)              \
199         , _length(Properties::length, other->_length)           \
200         , _position(Properties::position, other->_position)     \
201         , _sync_position(Properties::sync_position, other->_sync_position) \
202         , _layer (Properties::layer, other->_layer)             \
203         , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
204         , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
205         , _stretch (Properties::stretch, other->_stretch)       \
206         , _shift (Properties::shift, other->_shift)             \
207         , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
208
209 /* derived-from-derived constructor (no sources in constructor) */
210 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
211         : SessionObject(s, name)
212         , _type(type)
213         , REGION_DEFAULT_STATE(start,length)
214         , _last_length (length)
215         , _last_position (0)
216         , _first_edit (EditChangesNothing)
217         , _read_data_count(0)
218         , _last_layer_op(0)
219         , _pending_explicit_relayer (false)
220 {
221         register_properties ();
222
223         /* no sources at this point */
224 }
225
226 /** Basic Region constructor (many sources) */
227 Region::Region (const SourceList& srcs)
228         : SessionObject(srcs.front()->session(), "toBeRenamed")
229         , _type (srcs.front()->type())
230         , REGION_DEFAULT_STATE(0,0)
231         , _last_length (0)
232         , _last_position (0)
233         , _first_edit (EditChangesNothing)
234         , _read_data_count(0)
235         , _last_layer_op (0)
236         , _pending_explicit_relayer (false)
237 {
238         register_properties ();
239
240         _type = srcs.front()->type();
241
242         use_sources (srcs);
243
244         assert(_sources.size() > 0);
245         assert (_type == srcs.front()->type());
246 }
247
248 /** Create a new Region from an existing one */
249 Region::Region (boost::shared_ptr<const Region> other)
250         : SessionObject(other->session(), other->name())
251         , _type (other->data_type())
252         , REGION_COPY_STATE (other)
253         , _last_length (other->_last_length)
254         , _last_position(other->_last_position) \
255         , _first_edit (EditChangesNothing)
256         , _read_data_count(0)
257         , _last_layer_op (0)
258         , _pending_explicit_relayer (false)
259
260 {
261         register_properties ();
262
263         /* override state that may have been incorrectly inherited from the other region
264          */
265
266         _position = 0;
267         _locked = false;
268         _whole_file = false;
269         _hidden = false;
270
271         use_sources (other->_sources);
272
273         _position_lock_style = other->_position_lock_style;
274         _first_edit = other->_first_edit;
275
276         _start = 0; // It seems strange _start is not inherited here?
277
278         /* sync pos is relative to start of file. our start-in-file is now zero,
279            so set our sync position to whatever the the difference between
280            _start and _sync_pos was in the other region.
281                            
282            result is that our new sync pos points to the same point in our source(s)
283            as the sync in the other region did in its source(s).
284                            
285            since we start at zero in our source(s), it is not possible to use a sync point that
286            is before the start. reset it to _start if that was true in the other region.
287         */
288                         
289         if (other->sync_marked()) {
290                 if (other->_start < other->_sync_position) {
291                         /* sync pos was after the start point of the other region */
292                         _sync_position = other->_sync_position - other->_start;
293                 } else {
294                         /* sync pos was before the start point of the other region. not possible here. */
295                         _sync_marked = false;
296                         _sync_position = _start;
297                 }
298         } else {
299                 _sync_marked = false;
300                 _sync_position = _start;
301         }
302
303         if (Profile->get_sae()) {
304                 /* reset sync point to start if its ended up
305                    outside region bounds.
306                 */
307
308                 if (_sync_position < _start || _sync_position >= _start + _length) {
309                         _sync_marked = false;
310                         _sync_position = _start;
311                 }
312         }
313
314         assert (_type == other->data_type());
315 }
316
317 /** Create a new Region from part of an existing one.
318
319     the start within \a other is given by \a offset
320     (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
321 */
322 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
323         : SessionObject(other->session(), other->name())
324         , _type (other->data_type())
325         , REGION_COPY_STATE (other)
326         , _last_length (other->_last_length)
327         , _last_position(other->_last_position) \
328         , _first_edit (EditChangesNothing)
329         , _read_data_count(0)
330         , _last_layer_op (0)
331         , _pending_explicit_relayer (false)
332
333 {
334         register_properties ();
335
336         /* override state that may have been incorrectly inherited from the other region
337          */
338
339         _position = 0;
340         _locked = false;
341         _whole_file = false;
342         _hidden = false;
343
344         use_sources (other->_sources);
345
346         _start = other->_start + offset;
347                 
348         /* if the other region had a distinct sync point
349            set, then continue to use it as best we can.
350            otherwise, reset sync point back to start.
351         */
352                 
353         if (other->sync_marked()) {
354                 if (other->_sync_position < _start) {
355                         _sync_marked = false;
356                         _sync_position = _start;
357                 } else {
358                         _sync_position = other->_sync_position;
359                 }
360         } else {
361                 _sync_marked = false;
362                 _sync_position = _start;
363         }
364
365         if (Profile->get_sae()) {
366                 /* reset sync point to start if its ended up
367                    outside region bounds.
368                 */
369
370                 if (_sync_position < _start || _sync_position >= _start + _length) {
371                         _sync_marked = false;
372                         _sync_position = _start;
373                 }
374         }
375
376         assert (_type == other->data_type());
377 }
378
379 /** Create a copy of @param other but with different sources. Used by filters */
380 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
381         : SessionObject (other->session(), other->name())
382         , _type (srcs.front()->type())
383         , REGION_COPY_STATE (other)
384         , _last_length (other->_last_length)
385         , _last_position (other->_last_position)
386         , _first_edit (EditChangesID)
387         , _read_data_count (0)
388         , _last_layer_op (other->_last_layer_op)
389         , _pending_explicit_relayer (false)
390 {
391         register_properties ();
392
393         _locked = false;
394         _position_locked = false;
395
396         other->_first_edit = EditChangesName;
397
398         if (other->_extra_xml) {
399                 _extra_xml = new XMLNode (*other->_extra_xml);
400         } else {
401                 _extra_xml = 0;
402         }
403
404         use_sources (srcs);
405         assert(_sources.size() > 0);
406 }
407
408 Region::~Region ()
409 {
410         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
411         drop_sources ();
412 }
413
414 void
415 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
416 {
417         _playlist = wpl.lock();
418 }
419
420 bool
421 Region::set_name (const std::string& str)
422 {
423         if (_name != str) {
424                 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
425                 assert(_name == str);
426                 send_change (Properties::name);
427         }
428
429         return true;
430 }
431
432 void
433 Region::set_length (framecnt_t len, void */*src*/)
434 {
435         //cerr << "Region::set_length() len = " << len << endl;
436         if (locked()) {
437                 return;
438         }
439
440         if (_length != len && len != 0) {
441
442                 /* check that the current _position wouldn't make the new
443                    length impossible.
444                 */
445
446                 if (max_framepos - len < _position) {
447                         return;
448                 }
449
450                 if (!verify_length (len)) {
451                         return;
452                 }
453
454
455                 _last_length = _length;
456                 set_length_internal (len);
457                 _whole_file = false;
458                 first_edit ();
459                 maybe_uncopy ();
460                 invalidate_transients ();
461
462                 if (!property_changes_suspended()) {
463                         recompute_at_end ();
464                 }
465
466                 send_change (Properties::length);
467         }
468 }
469
470 void
471 Region::set_length_internal (framecnt_t len)
472 {
473         _length = len;
474 }
475
476 void
477 Region::maybe_uncopy ()
478 {
479         /* this does nothing but marked a semantic moment once upon a time */
480 }
481
482 void
483 Region::first_edit ()
484 {
485         boost::shared_ptr<Playlist> pl (playlist());
486
487         if (_first_edit != EditChangesNothing && pl) {
488
489                 _name = RegionFactory::new_region_name (_name);
490                 _first_edit = EditChangesNothing;
491
492                 send_change (Properties::name);
493                 RegionFactory::CheckNewRegion (shared_from_this());
494         }
495 }
496
497 bool
498 Region::at_natural_position () const
499 {
500         boost::shared_ptr<Playlist> pl (playlist());
501
502         if (!pl) {
503                 return false;
504         }
505
506         boost::shared_ptr<Region> whole_file_region = get_parent();
507
508         if (whole_file_region) {
509                 if (_position == whole_file_region->position() + _start) {
510                         return true;
511                 }
512         }
513
514         return false;
515 }
516
517 void
518 Region::move_to_natural_position (void *src)
519 {
520         boost::shared_ptr<Playlist> pl (playlist());
521
522         if (!pl) {
523                 return;
524         }
525
526         boost::shared_ptr<Region> whole_file_region = get_parent();
527
528         if (whole_file_region) {
529                 set_position (whole_file_region->position() + _start, src);
530         }
531 }
532
533 void
534 Region::special_set_position (framepos_t pos)
535 {
536         /* this is used when creating a whole file region as
537            a way to store its "natural" or "captured" position.
538         */
539
540         _position = _position;
541         _position = pos;
542 }
543
544 void
545 Region::set_position_lock_style (PositionLockStyle ps)
546 {
547         if (_position_lock_style != ps) {
548
549                 boost::shared_ptr<Playlist> pl (playlist());
550                 
551                 if (!pl) {
552                         return;
553                 }
554                 
555                 _position_lock_style = ps;
556                 
557                 if (_position_lock_style == MusicTime) {
558                         _session.tempo_map().bbt_time (_position, _bbt_time);
559                 }
560
561                 send_change (Properties::position_lock_style);
562         }
563 }
564
565 void
566 Region::update_position_after_tempo_map_change ()
567 {
568         boost::shared_ptr<Playlist> pl (playlist());
569
570         if (!pl || _position_lock_style != MusicTime) {
571                 return;
572         }
573
574         TempoMap& map (_session.tempo_map());
575         framepos_t pos = map.frame_time (_bbt_time);
576         set_position_internal (pos, false);
577
578         /* do this even if the position is the same. this helps out
579            a GUI that has moved its representation already.
580         */
581         send_change (Properties::position);
582 }
583
584 void
585 Region::set_position (framepos_t pos, void* /*src*/)
586 {
587         if (!can_move()) {
588                 return;
589         }
590
591         set_position_internal (pos, true);
592
593         /* do this even if the position is the same. this helps out
594            a GUI that has moved its representation already.
595         */
596         send_change (Properties::position);
597         
598 }
599
600 void
601 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
602 {
603         if (_position != pos) {
604                 _last_position = _position;
605                 _position = pos;
606
607                 /* check that the new _position wouldn't make the current
608                    length impossible - if so, change the length.
609
610                    XXX is this the right thing to do?
611                 */
612
613                 if (max_framepos - _length < _position) {
614                         _last_length = _length;
615                         _length = max_framepos - _position;
616                 }
617
618                 if (allow_bbt_recompute) {
619                         recompute_position_from_lock_style ();
620                 }
621
622                 //invalidate_transients ();
623         }
624 }
625
626 void
627 Region::set_position_on_top (framepos_t pos, void* /*src*/)
628 {
629         if (locked()) {
630                 return;
631         }
632
633         if (_position != pos) {
634                 set_position_internal (pos, true);
635         }
636
637         boost::shared_ptr<Playlist> pl (playlist());
638
639         if (pl) {
640                 pl->raise_region_to_top (shared_from_this ());
641         }
642
643         /* do this even if the position is the same. this helps out
644            a GUI that has moved its representation already.
645         */
646
647         send_change (Properties::position);
648 }
649
650 void
651 Region::recompute_position_from_lock_style ()
652 {
653         if (_position_lock_style == MusicTime) {
654                 _session.tempo_map().bbt_time (_position, _bbt_time);
655         }
656 }
657
658 void
659 Region::nudge_position (frameoffset_t n, void* /*src*/)
660 {
661         if (locked()) {
662                 return;
663         }
664
665         if (n == 0) {
666                 return;
667         }
668
669         framepos_t new_position = _position;
670
671         if (n > 0) {
672                 if (_position > max_framepos - n) {
673                         new_position = max_framepos;
674                 } else {
675                         new_position += n;
676                 }
677         } else {
678                 if (_position < -n) {
679                         new_position = 0;
680                 } else {
681                         new_position += n;
682                 }
683         }
684
685         set_position_internal (new_position, true);
686
687         send_change (Properties::position);
688 }
689
690 void
691 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
692 {
693         _ancestral_length = l;
694         _ancestral_start = s;
695         _stretch = st;
696         _shift = sh;
697 }
698
699 void
700 Region::set_start (framepos_t pos, void* /*src*/)
701 {
702         if (locked() || position_locked()) {
703                 return;
704         }
705         /* This just sets the start, nothing else. It effectively shifts
706            the contents of the Region within the overall extent of the Source,
707            without changing the Region's position or length
708         */
709
710         if (_start != pos) {
711
712                 if (!verify_start (pos)) {
713                         return;
714                 }
715
716                 _start = pos;
717                 _whole_file = false;
718                 first_edit ();
719                 invalidate_transients ();
720
721                 send_change (Properties::start);
722         }
723 }
724
725 void
726 Region::trim_start (framepos_t new_position, void */*src*/)
727 {
728         if (locked() || position_locked()) {
729                 return;
730         }
731         framepos_t new_start;
732         frameoffset_t const start_shift = new_position - _position;
733
734         if (start_shift > 0) {
735
736                 if (_start > max_framepos - start_shift) {
737                         new_start = max_framepos;
738                 } else {
739                         new_start = _start + start_shift;
740                 }
741
742                 if (!verify_start (new_start)) {
743                         return;
744                 }
745
746         } else if (start_shift < 0) {
747
748                 if (_start < -start_shift) {
749                         new_start = 0;
750                 } else {
751                         new_start = _start + start_shift;
752                 }
753                 
754         } else {
755                 return;
756         }
757
758         if (new_start == _start) {
759                 return;
760         }
761
762         _start = new_start;
763         _whole_file = false;
764         first_edit ();
765
766         send_change (Properties::start);
767 }
768
769 void
770 Region::trim_front (framepos_t new_position, void *src)
771 {
772         modify_front (new_position, false, src);
773 }
774
775 void
776 Region::cut_front (framepos_t new_position, void *src)
777 {
778         modify_front (new_position, true, src);
779 }
780
781 void
782 Region::cut_end (framepos_t new_endpoint, void *src)
783 {
784         modify_end (new_endpoint, true, src);
785 }
786
787 void
788 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
789 {
790         if (locked()) {
791                 return;
792         }
793
794         framepos_t end = last_frame();
795         framepos_t source_zero;
796
797         if (_position > _start) {
798                 source_zero = _position - _start;
799         } else {
800                 source_zero = 0; // its actually negative, but this will work for us
801         }
802
803         if (new_position < end) { /* can't trim it zero or negative length */
804                 
805                 framecnt_t newlen = 0;
806                 framepos_t delta = 0;
807
808                 if (!can_trim_start_before_source_start ()) {
809                         /* can't trim it back past where source position zero is located */
810                         new_position = max (new_position, source_zero);
811                 }
812                 
813                 if (new_position > _position) {
814                         newlen = _length - (new_position - _position);
815                         delta = -1 * (new_position - _position);
816                 } else {
817                         newlen = _length + (_position - new_position);
818                         delta = _position - new_position;
819                 }
820                 
821                 trim_to_internal (new_position, newlen, src);
822                 
823                 if (reset_fade) {
824                         _right_of_split = true;
825                 }
826         
827                 if (!property_changes_suspended()) {
828                         recompute_at_start ();
829                 }
830                 
831                 if (_transients.size() > 0){
832                         adjust_transients(delta);
833                 }
834         }
835 }
836
837 void
838 Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
839 {
840         if (locked()) {
841                 return;
842         }
843
844         if (new_endpoint > _position) {
845                 trim_to_internal (_position, new_endpoint - _position +1, this);
846                 if (reset_fade) {
847                         _left_of_split = true;
848                 }
849                 if (!property_changes_suspended()) {
850                         recompute_at_end ();
851                 }
852         }
853 }
854
855 /** @param new_endpoint New region end point, such that, for example,
856  *  a region at 0 of length 10 has an endpoint of 9.
857  */
858
859 void
860 Region::trim_end (framepos_t new_endpoint, void* src)
861 {
862         modify_end (new_endpoint, false, src);
863 }
864
865 void
866 Region::trim_to (framepos_t position, framecnt_t length, void *src)
867 {
868         if (locked()) {
869                 return;
870         }
871
872         trim_to_internal (position, length, src);
873
874         if (!property_changes_suspended()) {
875                 recompute_at_start ();
876                 recompute_at_end ();
877         }
878 }
879
880 void
881 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
882 {
883         framepos_t new_start;
884
885         if (locked()) {
886                 return;
887         }
888
889         frameoffset_t const start_shift = position - _position;
890
891         if (start_shift > 0) {
892
893                 if (_start > max_framepos - start_shift) {
894                         new_start = max_framepos;
895                 } else {
896                         new_start = _start + start_shift;
897                 }
898
899         } else if (start_shift < 0) {
900
901                 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
902                         new_start = 0;
903                 } else {
904                         new_start = _start + start_shift;
905                 }
906
907         } else {
908                 new_start = _start;
909         }
910
911         if (!verify_start_and_length (new_start, length)) {
912                 return;
913         }
914
915         PropertyChange what_changed;
916
917         if (_start != new_start) {
918                 _start = new_start;
919                 what_changed.add (Properties::start);
920         }
921         if (_length != length) {
922                 if (!property_changes_suspended()) {
923                         _last_length = _length;
924                 }
925                 set_length_internal (length);
926                 what_changed.add (Properties::length);
927         }
928         if (_position != position) {
929                 if (!property_changes_suspended()) {
930                         _last_position = _position;
931                 }
932                 set_position_internal (position, true);
933                 what_changed.add (Properties::position);
934         }
935
936         _whole_file = false;
937
938         PropertyChange start_and_length;
939
940         start_and_length.add (Properties::start);
941         start_and_length.add (Properties::length);
942
943         if (what_changed.contains (start_and_length)) {
944                 first_edit ();
945         }
946
947         if (!what_changed.empty()) {
948                 send_change (what_changed);
949         }
950 }
951
952 void
953 Region::set_hidden (bool yn)
954 {
955         if (hidden() != yn) {
956                 _hidden = yn;
957                 send_change (Properties::hidden);
958         }
959 }
960
961 void
962 Region::set_whole_file (bool yn)
963 {
964         _whole_file = yn;
965         /* no change signal */
966 }
967
968 void
969 Region::set_automatic (bool yn)
970 {
971         _automatic = yn;
972         /* no change signal */
973 }
974
975 void
976 Region::set_muted (bool yn)
977 {
978         if (muted() != yn) {
979                 _muted = yn;
980                 send_change (Properties::muted);
981         }
982 }
983
984 void
985 Region::set_opaque (bool yn)
986 {
987         if (opaque() != yn) {
988                 _opaque = yn;
989                 send_change (Properties::opaque);
990         }
991 }
992
993 void
994 Region::set_locked (bool yn)
995 {
996         if (locked() != yn) {
997                 _locked = yn;
998                 send_change (Properties::locked);
999         }
1000 }
1001
1002 void
1003 Region::set_position_locked (bool yn)
1004 {
1005         if (position_locked() != yn) {
1006                 _position_locked = yn;
1007                 send_change (Properties::locked);
1008         }
1009 }
1010
1011 /** Set the region's sync point.
1012  *  @param absolute_pos Session time.
1013  */
1014 void
1015 Region::set_sync_position (framepos_t absolute_pos)
1016 {
1017         /* position within our file */
1018         framepos_t const file_pos = _start + (absolute_pos - _position);
1019
1020         if (file_pos != _sync_position) {
1021                 _sync_marked = true;
1022                 _sync_position = file_pos;
1023                 if (!property_changes_suspended()) {
1024                         maybe_uncopy ();
1025                 }
1026                 send_change (Properties::sync_position);
1027         }
1028 }
1029
1030 void
1031 Region::clear_sync_position ()
1032 {
1033         if (sync_marked()) {
1034                 _sync_marked = false;
1035                 if (!property_changes_suspended()) {
1036                         maybe_uncopy ();
1037                 }
1038                 send_change (Properties::sync_position);
1039         }
1040 }
1041
1042 /* @return the sync point relative the first frame of the region */
1043 frameoffset_t
1044 Region::sync_offset (int& dir) const
1045 {
1046         if (sync_marked()) {
1047                 if (_sync_position > _start) {
1048                         dir = 1;
1049                         return _sync_position - _start;
1050                 } else {
1051                         dir = -1;
1052                         return _start - _sync_position;
1053                 }
1054         } else {
1055                 dir = 0;
1056                 return 0;
1057         }
1058 }
1059
1060 framepos_t
1061 Region::adjust_to_sync (framepos_t pos) const
1062 {
1063         int sync_dir;
1064         frameoffset_t offset = sync_offset (sync_dir);
1065
1066         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1067
1068         if (sync_dir > 0) {
1069                 if (pos > offset) {
1070                         pos -= offset;
1071                 } else {
1072                         pos = 0;
1073                 }
1074         } else {
1075                 if (max_framepos - pos > offset) {
1076                         pos += offset;
1077                 }
1078         }
1079
1080         return pos;
1081 }
1082
1083 /** @return Sync position in session time */
1084 framepos_t
1085 Region::sync_position() const
1086 {
1087         if (sync_marked()) {
1088                 return _position - _start + _sync_position;
1089         } else {
1090                 /* if sync has not been marked, use the start of the region */
1091                 return _position;
1092         }
1093 }
1094
1095 void
1096 Region::raise ()
1097 {
1098         boost::shared_ptr<Playlist> pl (playlist());
1099         if (pl) {
1100                 pl->raise_region (shared_from_this ());
1101         }
1102 }
1103
1104 void
1105 Region::lower ()
1106 {
1107         boost::shared_ptr<Playlist> pl (playlist());
1108         if (pl) {
1109                 pl->lower_region (shared_from_this ());
1110         }
1111 }
1112
1113
1114 void
1115 Region::raise_to_top ()
1116 {
1117         boost::shared_ptr<Playlist> pl (playlist());
1118         if (pl) {
1119                 pl->raise_region_to_top (shared_from_this());
1120         }
1121 }
1122
1123 void
1124 Region::lower_to_bottom ()
1125 {
1126         boost::shared_ptr<Playlist> pl (playlist());
1127         if (pl) {
1128                 pl->lower_region_to_bottom (shared_from_this());
1129         }
1130 }
1131
1132 void
1133 Region::set_layer (layer_t l)
1134 {
1135         if (_layer != l) {
1136                 _layer = l;
1137
1138                 send_change (Properties::layer);
1139         }
1140 }
1141
1142 XMLNode&
1143 Region::state ()
1144 {
1145         XMLNode *node = new XMLNode ("Region");
1146         char buf[64];
1147         char buf2[64];
1148         LocaleGuard lg (X_("POSIX"));
1149         const char* fe = NULL;
1150
1151         add_properties (*node);
1152
1153         _id.print (buf, sizeof (buf));
1154         node->add_property ("id", buf);
1155         node->add_property ("type", _type.to_string());
1156
1157         switch (_first_edit) {
1158         case EditChangesNothing:
1159                 fe = X_("nothing");
1160                 break;
1161         case EditChangesName:
1162                 fe = X_("name");
1163                 break;
1164         case EditChangesID:
1165                 fe = X_("id");
1166                 break;
1167         default: /* should be unreachable but makes g++ happy */
1168                 fe = X_("nothing");
1169                 break;
1170         }
1171
1172         node->add_property ("first-edit", fe);
1173
1174         /* note: flags are stored by derived classes */
1175
1176         if (_position_lock_style != AudioTime) {
1177                 stringstream str;
1178                 str << _bbt_time;
1179                 node->add_property ("bbt-position", str.str());
1180         }
1181
1182         for (uint32_t n=0; n < _sources.size(); ++n) {
1183                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1184                 _sources[n]->id().print (buf, sizeof(buf));
1185                 node->add_property (buf2, buf);
1186         }
1187
1188         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1189                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1190                 _master_sources[n]->id().print (buf, sizeof (buf));
1191                 node->add_property (buf2, buf);
1192         }
1193
1194         if (_extra_xml) {
1195                 node->add_child_copy (*_extra_xml);
1196         }
1197
1198         return *node;
1199 }
1200
1201 XMLNode&
1202 Region::get_state ()
1203 {
1204         return state ();
1205 }
1206
1207 int
1208 Region::set_state (const XMLNode& node, int version)
1209 {
1210         PropertyChange what_changed;
1211         return _set_state (node, version, what_changed, true);
1212 }
1213
1214 int
1215 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1216 {
1217         const XMLProperty* prop;
1218
1219         what_changed = set_values (node);
1220
1221         if ((prop = node.property (X_("id")))) {
1222                 _id = prop->value();
1223         }
1224
1225         if (_position_lock_style == MusicTime) {
1226                 if ((prop = node.property ("bbt-position")) == 0) {
1227                         /* missing BBT info, revert to audio time locking */
1228                         _position_lock_style = AudioTime;
1229                 } else {
1230                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1231                                     &_bbt_time.bars,
1232                                     &_bbt_time.beats,
1233                                     &_bbt_time.ticks) != 3) {
1234                                 _position_lock_style = AudioTime;
1235                         }
1236                 }
1237         }
1238
1239         /* fix problems with old sessions corrupted by impossible
1240            values for _stretch or _shift
1241         */
1242         if (_stretch == 0.0f) {
1243                 _stretch = 1.0f;
1244         }
1245         
1246         if (_shift == 0.0f) {
1247                 _shift = 1.0f;
1248         }
1249
1250         const XMLNodeList& nlist = node.children();
1251
1252         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1253
1254                 XMLNode *child;
1255
1256                 child = (*niter);
1257
1258                 if (child->name () == "Extra") {
1259                         delete _extra_xml;
1260                         _extra_xml = new XMLNode (*child);
1261                         break;
1262                 }
1263         }
1264
1265         if (send) {
1266                 send_change (what_changed);
1267         }
1268         
1269         /* Quick fix for 2.x sessions when region is muted */
1270         if ((prop = node.property (X_("flags")))) {
1271                 if (string::npos != prop->value().find("Muted")){
1272                         set_muted (true);
1273                 }
1274         }
1275         
1276
1277         return 0;
1278 }
1279
1280 void
1281 Region::suspend_property_changes ()
1282 {
1283         Stateful::suspend_property_changes ();
1284         _last_length = _length;
1285         _last_position = _position;
1286 }
1287
1288 void
1289 Region::mid_thaw (const PropertyChange& what_changed)
1290 {
1291         if (what_changed.contains (Properties::length)) {
1292                 if (what_changed.contains (Properties::position)) {
1293                         recompute_at_start ();
1294                 }
1295                 recompute_at_end ();
1296         }
1297 }
1298
1299 void
1300 Region::send_change (const PropertyChange& what_changed)
1301 {
1302         if (what_changed.empty()) {
1303                 return;
1304         }
1305
1306         Stateful::send_change (what_changed);
1307
1308         if (!Stateful::frozen()) {
1309                 
1310                 /* Try and send a shared_pointer unless this is part of the constructor.
1311                    If so, do nothing.
1312                 */
1313
1314                 try {
1315                         boost::shared_ptr<Region> rptr = shared_from_this();
1316                         RegionPropertyChanged (rptr, what_changed);
1317                 } catch (...) {
1318                         /* no shared_ptr available, relax; */
1319                 }
1320         }
1321 }
1322
1323 void
1324 Region::set_last_layer_op (uint64_t when)
1325 {
1326         _last_layer_op = when;
1327 }
1328
1329 bool
1330 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1331 {
1332         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1333 }
1334
1335 bool
1336 Region::equivalent (boost::shared_ptr<const Region> other) const
1337 {
1338         return _start == other->_start &&
1339                 _position == other->_position &&
1340                 _length == other->_length;
1341 }
1342
1343 bool
1344 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1345 {
1346         return _start == other->_start &&
1347                 _length == other->_length;
1348 }
1349
1350 bool
1351 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1352 {
1353         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1354 }
1355
1356 void
1357 Region::source_deleted (boost::weak_ptr<Source>)
1358 {
1359         drop_sources ();
1360
1361         if (!_session.deletion_in_progress()) {
1362                 /* this is a very special case: at least one of the region's
1363                    sources has bee deleted, so invalidate all references to
1364                    ourselves. Do NOT do this during session deletion, because
1365                    then we run the risk that this will actually result
1366                    in this object being deleted (as refcnt goes to zero)
1367                    while emitting DropReferences.
1368                 */
1369
1370                 drop_references ();
1371         }
1372 }
1373
1374 vector<string>
1375 Region::master_source_names ()
1376 {
1377         SourceList::iterator i;
1378
1379         vector<string> names;
1380         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1381                 names.push_back((*i)->name());
1382         }
1383
1384         return names;
1385 }
1386
1387 void
1388 Region::set_master_sources (const SourceList& srcs)
1389 {
1390         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1391                 (*i)->dec_use_count ();
1392         }
1393
1394         _master_sources = srcs;
1395         assert (_sources.size() == _master_sources.size());
1396
1397         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1398                 (*i)->inc_use_count ();
1399         }
1400 }
1401
1402 bool
1403 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1404 {
1405         if (!other)
1406                 return false;
1407         
1408         if ((_sources.size() != other->_sources.size()) ||
1409             (_master_sources.size() != other->_master_sources.size())) {
1410                 return false;
1411         }
1412
1413         SourceList::const_iterator i;
1414         SourceList::const_iterator io;
1415
1416         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1417                 if ((*i)->id() != (*io)->id()) {
1418                         return false;
1419                 }
1420         }
1421
1422         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1423                 if ((*i)->id() != (*io)->id()) {
1424                         return false;
1425                 }
1426         }
1427
1428         return true;
1429 }
1430
1431 bool
1432 Region::uses_source (boost::shared_ptr<const Source> source) const
1433 {
1434         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1435                 if (*i == source) {
1436                         return true;
1437                 }
1438         }
1439         return false;
1440 }
1441
1442 framecnt_t
1443 Region::source_length(uint32_t n) const
1444 {
1445         assert (n < _sources.size());
1446         return _sources[n]->length (_position - _start);
1447 }
1448
1449 bool
1450 Region::verify_length (framecnt_t len)
1451 {
1452         if (source() && (source()->destructive() || source()->length_mutable())) {
1453                 return true;
1454         }
1455
1456         framecnt_t maxlen = 0;
1457
1458         for (uint32_t n = 0; n < _sources.size(); ++n) {
1459                 maxlen = max (maxlen, source_length(n) - _start);
1460         }
1461
1462         len = min (len, maxlen);
1463
1464         return true;
1465 }
1466
1467 bool
1468 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1469 {
1470         if (source() && (source()->destructive() || source()->length_mutable())) {
1471                 return true;
1472         }
1473
1474         framecnt_t maxlen = 0;
1475
1476         for (uint32_t n = 0; n < _sources.size(); ++n) {
1477                 maxlen = max (maxlen, source_length(n) - new_start);
1478         }
1479
1480         new_length = min (new_length, maxlen);
1481
1482         return true;
1483 }
1484
1485 bool
1486 Region::verify_start (framepos_t pos)
1487 {
1488         if (source() && (source()->destructive() || source()->length_mutable())) {
1489                 return true;
1490         }
1491
1492         for (uint32_t n = 0; n < _sources.size(); ++n) {
1493                 if (pos > source_length(n) - _length) {
1494                         return false;
1495                 }
1496         }
1497         return true;
1498 }
1499
1500 bool
1501 Region::verify_start_mutable (framepos_t& new_start)
1502 {
1503         if (source() && (source()->destructive() || source()->length_mutable())) {
1504                 return true;
1505         }
1506
1507         for (uint32_t n = 0; n < _sources.size(); ++n) {
1508                 if (new_start > source_length(n) - _length) {
1509                         new_start = source_length(n) - _length;
1510                 }
1511         }
1512         return true;
1513 }
1514
1515 boost::shared_ptr<Region>
1516 Region::get_parent() const
1517 {
1518         boost::shared_ptr<Playlist> pl (playlist());
1519
1520         if (pl) {
1521                 boost::shared_ptr<Region> r;
1522                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1523
1524                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1525                         return boost::static_pointer_cast<Region> (r);
1526                 }
1527         }
1528
1529         return boost::shared_ptr<Region>();
1530 }
1531
1532 int
1533 Region::apply (Filter& filter, Progress* progress)
1534 {
1535         return filter.run (shared_from_this(), progress);
1536 }
1537
1538
1539 void
1540 Region::invalidate_transients ()
1541 {
1542         _valid_transients = false;
1543         _transients.clear ();
1544         
1545         send_change (PropertyChange (Properties::valid_transients));
1546 }
1547
1548 void
1549 Region::drop_sources ()
1550 {
1551         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1552                 (*i)->dec_use_count ();
1553         }
1554
1555         _sources.clear ();
1556
1557         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1558                 (*i)->dec_use_count ();
1559         }
1560
1561         _master_sources.clear ();
1562 }
1563
1564 void
1565 Region::use_sources (SourceList const & s)
1566 {
1567         set<boost::shared_ptr<Source> > unique_srcs;
1568
1569         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1570
1571                 _sources.push_back (*i);
1572                 (*i)->inc_use_count ();
1573                 _master_sources.push_back (*i);
1574                 (*i)->inc_use_count ();
1575
1576                 /* connect only once to DropReferences, even if sources are replicated
1577                  */
1578
1579                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1580                         unique_srcs.insert (*i);
1581                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1582                 }
1583         }
1584 }
1585
1586 Trimmable::CanTrim
1587 Region::can_trim () const
1588 {
1589         CanTrim ct = CanTrim (0);
1590
1591         if (locked()) {
1592                 return ct;
1593         }
1594
1595         /* if not locked, we can always move the front later, and the end earlier 
1596          */
1597
1598         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1599
1600         if (start() != 0 || can_trim_start_before_source_start ()) {
1601                 ct = CanTrim (ct | FrontTrimEarlier);
1602         }
1603
1604         if (!_sources.empty()) {
1605                 if ((start() + length()) < _sources.front()->length (0)) {
1606                         ct = CanTrim (ct | EndTrimLater);
1607                 }
1608         }
1609
1610         return ct;
1611 }
1612