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