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