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