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