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