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