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