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