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