a boatload of minor and middle-sized changes to try to speed up undo. imperfect,...
[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
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
31
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 Change Region::FadeChanged       = ARDOUR::new_change ();
50 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
51 Change Region::MuteChanged       = ARDOUR::new_change ();
52 Change Region::OpacityChanged    = ARDOUR::new_change ();
53 Change Region::LockChanged       = ARDOUR::new_change ();
54 Change Region::LayerChanged      = ARDOUR::new_change ();
55 Change Region::HiddenChanged     = ARDOUR::new_change ();
56
57 PBD::Signal1<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
58
59 /* derived-from-derived constructor (no sources in constructor) */
60 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
61         : SessionObject(s, name)
62         , _type(type)
63         , _flags(Flag (flags|DoNotSendPropertyChanges))
64         , _start(start)
65         , _length(length)
66         , _position(0)
67         , _last_position(0)
68         , _positional_lock_style(AudioTime)
69         , _sync_position(_start)
70         , _layer(layer)
71         , _first_edit(EditChangesNothing)
72         , _frozen(0)
73         , _ancestral_start (0)
74         , _ancestral_length (0)
75         , _stretch(1.0)
76         , _shift(1.0)
77         , _read_data_count(0)
78         , _pending_changed(Change (0))
79         , _last_layer_op(0)
80         , _pending_explicit_relayer (false)
81 {
82         /* no sources at this point */
83 }
84
85 /** Basic Region constructor (single source) */
86 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
87         : SessionObject(src->session(), name)
88         , _type(type)
89         , _flags(Flag (flags|DoNotSendPropertyChanges))
90         , _start(start)
91         , _length(length)
92         , _position(0)
93         , _last_position(0)
94         , _positional_lock_style(AudioTime)
95         , _sync_position(_start)
96         , _layer(layer)
97         , _first_edit(EditChangesNothing)
98         , _frozen(0)
99         , _ancestral_start (0)
100         , _ancestral_length (0)
101         , _stretch (1.0)
102         , _shift (1.0)
103         , _valid_transients(false)
104         , _read_data_count(0)
105         , _pending_changed(Change (0))
106         , _last_layer_op(0)
107         , _pending_explicit_relayer (false)
108 {
109         _sources.push_back (src);
110         _master_sources.push_back (src);
111
112         src->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(src)));
113
114         assert(_sources.size() > 0);
115         _positional_lock_style = AudioTime;
116 }
117
118 /** Basic Region constructor (many sources) */
119 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
120         : SessionObject(srcs.front()->session(), name)
121         , _type(type)
122         , _flags(Flag (flags|DoNotSendPropertyChanges))
123         , _start(start)
124         , _length(length)
125         , _position(0)
126         , _last_position(0)
127         , _positional_lock_style(AudioTime)
128         , _sync_position(_start)
129         , _layer(layer)
130         , _first_edit(EditChangesNothing)
131         , _frozen(0)
132         , _ancestral_start (0)
133         , _ancestral_length (0)
134         , _stretch(1.0)
135         , _shift(1.0)
136         , _read_data_count(0)
137         , _pending_changed(Change (0))
138         , _last_layer_op(0)
139         , _pending_explicit_relayer (false)
140 {
141         use_sources (srcs);
142         assert(_sources.size() > 0);
143 }
144
145 /** Create a new Region from part of an existing one */
146 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
147         : SessionObject(other->session(), name)
148         , _type (other->data_type())
149         , _pending_explicit_relayer (false)
150
151 {
152         _start = other->_start + offset;
153         copy_stuff (other, offset, length, name, layer, flags);
154
155         _flags = Flag (_flags | DoNotSendPropertyChanges);
156
157         /* if the other region had a distinct sync point
158            set, then continue to use it as best we can.
159            otherwise, reset sync point back to start.
160         */
161
162         if (other->flags() & SyncMarked) {
163                 if (other->_sync_position < _start) {
164                         _flags = Flag (_flags & ~SyncMarked);
165                         _sync_position = _start;
166                 } else {
167                         _sync_position = other->_sync_position;
168                 }
169         } else {
170                 _flags = Flag (_flags & ~SyncMarked);
171                 _sync_position = _start;
172         }
173
174         if (Profile->get_sae()) {
175                 /* reset sync point to start if its ended up
176                    outside region bounds.
177                 */
178
179                 if (_sync_position < _start || _sync_position >= _start + _length) {
180                         _flags = Flag (_flags & ~SyncMarked);
181                         _sync_position = _start;
182                 }
183         }
184 }
185
186 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
187         : SessionObject(other->session(), name)
188         , _type (other->data_type())
189         , _pending_explicit_relayer (false)
190 {
191         /* create a new Region exactly like another but starting at 0 in its sources */
192
193         _start = 0;
194         copy_stuff (other, 0, length, name, layer, flags);
195
196         _flags = Flag (_flags | DoNotSendPropertyChanges);
197
198         /* sync pos is relative to start of file. our start-in-file is now zero,
199            so set our sync position to whatever the the difference between
200            _start and _sync_pos was in the other region.
201
202            result is that our new sync pos points to the same point in our source(s)
203            as the sync in the other region did in its source(s).
204
205            since we start at zero in our source(s), it is not possible to use a sync point that
206            is before the start. reset it to _start if that was true in the other region.
207         */
208
209         if (other->flags() & SyncMarked) {
210                 if (other->_start < other->_sync_position) {
211                         /* sync pos was after the start point of the other region */
212                         _sync_position = other->_sync_position - other->_start;
213                 } else {
214                         /* sync pos was before the start point of the other region. not possible here. */
215                         _flags = Flag (_flags & ~SyncMarked);
216                         _sync_position = _start;
217                 }
218         } else {
219                 _flags = Flag (_flags & ~SyncMarked);
220                 _sync_position = _start;
221         }
222
223         if (Profile->get_sae()) {
224                 /* reset sync point to start if its ended up
225                    outside region bounds.
226                 */
227
228                 if (_sync_position < _start || _sync_position >= _start + _length) {
229                         _flags = Flag (_flags & ~SyncMarked);
230                         _sync_position = _start;
231                 }
232         }
233
234         /* reset a couple of things that copy_stuff() gets wrong in this particular case */
235
236         _positional_lock_style = other->_positional_lock_style;
237         _first_edit = other->_first_edit;
238 }
239
240 /** Pure copy constructor */
241 Region::Region (boost::shared_ptr<const Region> other)
242         : SessionObject(other->session(), other->name())
243         , _type(other->data_type())
244         , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
245         , _start(other->_start)
246         , _length(other->_length)
247         , _position(other->_position)
248         , _last_position(other->_last_position)
249         , _positional_lock_style(other->_positional_lock_style)
250         , _sync_position(other->_sync_position)
251         , _layer(other->_layer)
252         , _first_edit(EditChangesID)
253         , _frozen(0)
254         , _ancestral_start (other->_ancestral_start)
255         , _ancestral_length (other->_ancestral_length)
256         , _stretch (other->_stretch)
257         , _shift (other->_shift)
258         , _valid_transients(false)
259         , _read_data_count(0)
260         , _pending_changed(Change(0))
261         , _last_layer_op(other->_last_layer_op)
262         , _pending_explicit_relayer (false)
263 {
264         _flags = Flag (_flags | DoNotSendPropertyChanges);
265
266         other->_first_edit = EditChangesName;
267
268         if (other->_extra_xml) {
269                 _extra_xml = new XMLNode (*other->_extra_xml);
270         } else {
271                 _extra_xml = 0;
272         }
273
274         use_sources (other->_sources);
275         assert(_sources.size() > 0);
276 }
277
278 Region::Region (const SourceList& srcs, const XMLNode& node)
279         : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
280         , _type(DataType::NIL) // to be loaded from XML
281         , _flags(DoNotSendPropertyChanges)
282         , _start(0)
283         , _length(0)
284         , _position(0)
285         , _last_position(0)
286         , _positional_lock_style(AudioTime)
287         , _sync_position(_start)
288         , _layer(0)
289         , _first_edit(EditChangesNothing)
290         , _frozen(0)
291         , _stretch(1.0)
292         , _shift(1.0)
293         , _read_data_count(0)
294         , _pending_changed(Change(0))
295         , _last_layer_op(0)
296         , _pending_explicit_relayer (false)
297 {
298         use_sources (srcs);
299
300         if (set_state (node, Stateful::loading_state_version)) {
301                 throw failed_constructor();
302         }
303
304         assert(_type != DataType::NIL);
305         assert(_sources.size() > 0);
306 }
307
308 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
309         : SessionObject(src->session(), X_("error: XML did not reset this"))
310         , _type(DataType::NIL)
311         , _flags(DoNotSendPropertyChanges)
312         , _start(0)
313         , _length(0)
314         , _position(0)
315         , _last_position(0)
316         , _positional_lock_style(AudioTime)
317         , _sync_position(_start)
318         , _layer(0)
319         , _first_edit(EditChangesNothing)
320         , _frozen(0)
321         , _stretch(1.0)
322         , _shift(1.0)
323         , _read_data_count(0)
324         , _pending_changed(Change(0))
325         , _last_layer_op(0)
326         , _pending_explicit_relayer (false)
327 {
328         _sources.push_back (src);
329
330         if (set_state (node, Stateful::loading_state_version)) {
331                 throw failed_constructor();
332         }
333
334         assert(_type != DataType::NIL);
335         assert(_sources.size() > 0);
336 }
337
338 Region::~Region ()
339 {
340         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
341 }
342
343 void
344 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t /*offset*/, nframes_t length, const string& name, layer_t layer, Flag flags)
345 {
346         _frozen = 0;
347         _pending_changed = Change (0);
348         _read_data_count = 0;
349         _valid_transients = false;
350
351         _length = length;
352         _last_length = length;
353         _sync_position = other->_sync_position;
354         _ancestral_start = other->_ancestral_start;
355         _ancestral_length = other->_ancestral_length;
356         _stretch = other->_stretch;
357         _shift = other->_shift;
358         _name = name;
359         _last_position = 0;
360         _position = 0;
361         _layer = layer;
362         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
363         _first_edit = EditChangesNothing;
364         _last_layer_op = 0;
365         _positional_lock_style = AudioTime;
366
367         use_sources (other->_sources);
368 }
369
370 void
371 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
372 {
373         _playlist = wpl.lock();
374 }
375
376 bool
377 Region::set_name (const std::string& str)
378 {
379         if (_name != str) {
380                 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
381                 assert(_name == str);
382                 send_change (ARDOUR::NameChanged);
383         }
384
385         return true;
386 }
387
388 void
389 Region::set_length (nframes_t len, void */*src*/)
390 {
391         //cerr << "Region::set_length() len = " << len << endl;
392         if (_flags & Locked) {
393                 return;
394         }
395
396         if (_length != len && len != 0) {
397
398                 /* check that the current _position wouldn't make the new
399                    length impossible.
400                 */
401
402                 if (max_frames - len < _position) {
403                         return;
404                 }
405
406                 if (!verify_length (len)) {
407                         return;
408                 }
409
410
411                 _last_length = _length;
412                 _length = len;
413
414                 _flags = Region::Flag (_flags & ~WholeFile);
415
416                 first_edit ();
417                 maybe_uncopy ();
418                 invalidate_transients ();
419
420                 if (!_frozen) {
421                         recompute_at_end ();
422                 }
423
424                 send_change (LengthChanged);
425         }
426 }
427
428 void
429 Region::maybe_uncopy ()
430 {
431 }
432
433 void
434 Region::first_edit ()
435 {
436         boost::shared_ptr<Playlist> pl (playlist());
437
438         if (_first_edit != EditChangesNothing && pl) {
439
440                 _name = _session.new_region_name (_name);
441                 _first_edit = EditChangesNothing;
442
443                 send_change (ARDOUR::NameChanged);
444                 RegionFactory::CheckNewRegion (shared_from_this());
445         }
446 }
447
448 bool
449 Region::at_natural_position () const
450 {
451         boost::shared_ptr<Playlist> pl (playlist());
452
453         if (!pl) {
454                 return false;
455         }
456
457         boost::shared_ptr<Region> whole_file_region = get_parent();
458
459         if (whole_file_region) {
460                 if (_position == whole_file_region->position() + _start) {
461                         return true;
462                 }
463         }
464
465         return false;
466 }
467
468 void
469 Region::move_to_natural_position (void *src)
470 {
471         boost::shared_ptr<Playlist> pl (playlist());
472
473         if (!pl) {
474                 return;
475         }
476
477         boost::shared_ptr<Region> whole_file_region = get_parent();
478
479         if (whole_file_region) {
480                 set_position (whole_file_region->position() + _start, src);
481         }
482 }
483
484 void
485 Region::special_set_position (nframes_t pos)
486 {
487         /* this is used when creating a whole file region as
488            a way to store its "natural" or "captured" position.
489         */
490
491         _position = _position;
492         _position = pos;
493 }
494
495 void
496 Region::set_position_lock_style (PositionLockStyle ps)
497 {
498         boost::shared_ptr<Playlist> pl (playlist());
499
500         if (!pl) {
501                 return;
502         }
503
504         _positional_lock_style = ps;
505
506         if (_positional_lock_style == MusicTime) {
507                 _session.tempo_map().bbt_time (_position, _bbt_time);
508         }
509
510 }
511
512 void
513 Region::update_position_after_tempo_map_change ()
514 {
515         boost::shared_ptr<Playlist> pl (playlist());
516
517         if (!pl || _positional_lock_style != MusicTime) {
518                 return;
519         }
520
521         TempoMap& map (_session.tempo_map());
522         nframes_t pos = map.frame_time (_bbt_time);
523         set_position_internal (pos, false);
524 }
525
526 void
527 Region::set_position (nframes_t pos, void* /*src*/)
528 {
529         if (!can_move()) {
530                 return;
531         }
532
533         set_position_internal (pos, true);
534 }
535
536 void
537 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
538 {
539         if (_position != pos) {
540                 _last_position = _position;
541                 _position = pos;
542
543                 /* check that the new _position wouldn't make the current
544                    length impossible - if so, change the length.
545
546                    XXX is this the right thing to do?
547                 */
548
549                 if (max_frames - _length < _position) {
550                         _last_length = _length;
551                         _length = max_frames - _position;
552                 }
553
554                 if (allow_bbt_recompute) {
555                         recompute_position_from_lock_style ();
556                 }
557
558                 invalidate_transients ();
559         }
560
561         /* do this even if the position is the same. this helps out
562            a GUI that has moved its representation already.
563         */
564
565         send_change (PositionChanged);
566 }
567
568 void
569 Region::set_position_on_top (nframes_t pos, void* /*src*/)
570 {
571         if (_flags & Locked) {
572                 return;
573         }
574
575         if (_position != pos) {
576                 _last_position = _position;
577                 _position = pos;
578         }
579
580         boost::shared_ptr<Playlist> pl (playlist());
581
582         if (pl) {
583                 pl->raise_region_to_top (shared_from_this ());
584         }
585
586         /* do this even if the position is the same. this helps out
587            a GUI that has moved its representation already.
588         */
589
590         send_change (PositionChanged);
591 }
592
593 void
594 Region::recompute_position_from_lock_style ()
595 {
596         if (_positional_lock_style == MusicTime) {
597                 _session.tempo_map().bbt_time (_position, _bbt_time);
598         }
599 }
600
601 void
602 Region::nudge_position (nframes64_t n, void* /*src*/)
603 {
604         if (_flags & Locked) {
605                 return;
606         }
607
608         if (n == 0) {
609                 return;
610         }
611
612         _last_position = _position;
613
614         if (n > 0) {
615                 if (_position > max_frames - n) {
616                         _position = max_frames;
617                 } else {
618                         _position += n;
619                 }
620         } else {
621                 if (_position < (nframes_t) -n) {
622                         _position = 0;
623                 } else {
624                         _position += n;
625                 }
626         }
627
628         send_change (PositionChanged);
629 }
630
631 void
632 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
633 {
634         _ancestral_length = l;
635         _ancestral_start = s;
636         _stretch = st;
637         _shift = sh;
638 }
639
640 void
641 Region::set_start (nframes_t pos, void* /*src*/)
642 {
643         if (_flags & (Locked|PositionLocked)) {
644                 return;
645         }
646         /* This just sets the start, nothing else. It effectively shifts
647            the contents of the Region within the overall extent of the Source,
648            without changing the Region's position or length
649         */
650
651         if (_start != pos) {
652
653                 if (!verify_start (pos)) {
654                         return;
655                 }
656
657                 _start = pos;
658                 _flags = Region::Flag (_flags & ~WholeFile);
659                 first_edit ();
660                 invalidate_transients ();
661
662                 send_change (StartChanged);
663         }
664 }
665
666 void
667 Region::trim_start (nframes_t new_position, void */*src*/)
668 {
669         if (_flags & (Locked|PositionLocked)) {
670                 return;
671         }
672         nframes_t new_start;
673         int32_t start_shift;
674
675         if (new_position > _position) {
676                 start_shift = new_position - _position;
677         } else {
678                 start_shift = -(_position - new_position);
679         }
680
681         if (start_shift > 0) {
682
683                 if (_start > max_frames - start_shift) {
684                         new_start = max_frames;
685                 } else {
686                         new_start = _start + start_shift;
687                 }
688
689                 if (!verify_start (new_start)) {
690                         return;
691                 }
692
693         } else if (start_shift < 0) {
694
695                 if (_start < (nframes_t) -start_shift) {
696                         new_start = 0;
697                 } else {
698                         new_start = _start + start_shift;
699                 }
700         } else {
701                 return;
702         }
703
704         if (new_start == _start) {
705                 return;
706         }
707
708         _start = new_start;
709         _flags = Region::Flag (_flags & ~WholeFile);
710         first_edit ();
711
712         send_change (StartChanged);
713 }
714
715 void
716 Region::trim_front (nframes_t new_position, void *src)
717 {
718         if (_flags & Locked) {
719                 return;
720         }
721
722         nframes_t end = last_frame();
723         nframes_t source_zero;
724
725         if (_position > _start) {
726                 source_zero = _position - _start;
727         } else {
728                 source_zero = 0; // its actually negative, but this will work for us
729         }
730
731         if (new_position < end) { /* can't trim it zero or negative length */
732
733                 nframes_t newlen;
734
735                 /* can't trim it back passed where source position zero is located */
736
737                 new_position = max (new_position, source_zero);
738
739
740                 if (new_position > _position) {
741                         newlen = _length - (new_position - _position);
742                 } else {
743                         newlen = _length + (_position - new_position);
744                 }
745
746                 trim_to_internal (new_position, newlen, src);
747                 if (!_frozen) {
748                         recompute_at_start ();
749                 }
750         }
751 }
752
753 /** @param new_endpoint New region end point, such that, for example,
754  *  a region at 0 of length 10 has an endpoint of 9.
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 + 1, 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 const file_pos = _start + (absolute_pos - _position);
932
933         if (file_pos != _sync_position) {
934
935                 _sync_position = file_pos;
936                 _flags = Flag (_flags|SyncMarked);
937
938                 if (!_frozen) {
939                         maybe_uncopy ();
940                 }
941                 send_change (SyncOffsetChanged);
942         }
943 }
944
945 void
946 Region::clear_sync_position ()
947 {
948         if (_flags & SyncMarked) {
949                 _flags = Flag (_flags & ~SyncMarked);
950
951                 if (!_frozen) {
952                         maybe_uncopy ();
953                 }
954                 send_change (SyncOffsetChanged);
955         }
956 }
957
958 nframes_t
959 Region::sync_offset (int& dir) const
960 {
961         /* returns the sync point relative the first frame of the region */
962
963         if (_flags & SyncMarked) {
964                 if (_sync_position > _start) {
965                         dir = 1;
966                         return _sync_position - _start;
967                 } else {
968                         dir = -1;
969                         return _start - _sync_position;
970                 }
971         } else {
972                 dir = 0;
973                 return 0;
974         }
975 }
976
977 nframes_t
978 Region::adjust_to_sync (nframes_t pos) const
979 {
980         int sync_dir;
981         nframes_t offset = sync_offset (sync_dir);
982
983         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
984
985         if (sync_dir > 0) {
986                 if (pos > offset) {
987                         pos -= offset;
988                 } else {
989                         pos = 0;
990                 }
991         } else {
992                 if (max_frames - pos > offset) {
993                         pos += offset;
994                 }
995         }
996
997         return pos;
998 }
999
1000 nframes_t
1001 Region::sync_position() const
1002 {
1003         if (_flags & SyncMarked) {
1004                 return _sync_position;
1005         } else {
1006                 return _start;
1007         }
1008 }
1009
1010 void
1011 Region::raise ()
1012 {
1013         boost::shared_ptr<Playlist> pl (playlist());
1014         if (pl) {
1015                 pl->raise_region (shared_from_this ());
1016         }
1017 }
1018
1019 void
1020 Region::lower ()
1021 {
1022         boost::shared_ptr<Playlist> pl (playlist());
1023         if (pl) {
1024                 pl->lower_region (shared_from_this ());
1025         }
1026 }
1027
1028
1029 void
1030 Region::raise_to_top ()
1031 {
1032         boost::shared_ptr<Playlist> pl (playlist());
1033         if (pl) {
1034                 pl->raise_region_to_top (shared_from_this());
1035         }
1036 }
1037
1038 void
1039 Region::lower_to_bottom ()
1040 {
1041         boost::shared_ptr<Playlist> pl (playlist());
1042         if (pl) {
1043                 pl->lower_region_to_bottom (shared_from_this());
1044         }
1045 }
1046
1047 void
1048 Region::set_layer (layer_t l)
1049 {
1050         if (_layer != l) {
1051                 _layer = l;
1052
1053                 send_change (LayerChanged);
1054         }
1055 }
1056
1057 XMLNode&
1058 Region::state (bool /*full_state*/)
1059 {
1060         XMLNode *node = new XMLNode ("Region");
1061         char buf[64];
1062         const char* fe = NULL;
1063
1064         _id.print (buf, sizeof (buf));
1065         node->add_property ("id", buf);
1066         node->add_property ("name", _name);
1067         node->add_property ("type", _type.to_string());
1068         snprintf (buf, sizeof (buf), "%u", _start);
1069         node->add_property ("start", buf);
1070         snprintf (buf, sizeof (buf), "%u", _length);
1071         node->add_property ("length", buf);
1072         snprintf (buf, sizeof (buf), "%u", _position);
1073         node->add_property ("position", buf);
1074         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1075         node->add_property ("ancestral-start", buf);
1076         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1077         node->add_property ("ancestral-length", buf);
1078         snprintf (buf, sizeof (buf), "%.12g", _stretch);
1079         node->add_property ("stretch", buf);
1080         snprintf (buf, sizeof (buf), "%.12g", _shift);
1081         node->add_property ("shift", buf);
1082
1083         switch (_first_edit) {
1084         case EditChangesNothing:
1085                 fe = X_("nothing");
1086                 break;
1087         case EditChangesName:
1088                 fe = X_("name");
1089                 break;
1090         case EditChangesID:
1091                 fe = X_("id");
1092                 break;
1093         default: /* should be unreachable but makes g++ happy */
1094                 fe = X_("nothing");
1095                 break;
1096         }
1097
1098         node->add_property ("first-edit", fe);
1099
1100         /* note: flags are stored by derived classes */
1101
1102         snprintf (buf, sizeof (buf), "%d", (int) _layer);
1103         node->add_property ("layer", buf);
1104         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1105         node->add_property ("sync-position", buf);
1106
1107         if (_positional_lock_style != AudioTime) {
1108                 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1109                 stringstream str;
1110                 str << _bbt_time;
1111                 node->add_property ("bbt-position", str.str());
1112         }
1113
1114         return *node;
1115 }
1116
1117 XMLNode&
1118 Region::get_state ()
1119 {
1120         return state (true);
1121 }
1122
1123 int
1124 Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_changed, bool send)
1125 {
1126         const XMLNodeList& nlist = node.children();
1127         const XMLProperty *prop;
1128         nframes_t val;
1129
1130         /* this is responsible for setting those aspects of Region state
1131            that are mutable after construction.
1132         */
1133
1134         if ((prop = node.property ("name")) == 0) {
1135                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1136                 return -1;
1137         }
1138
1139         _name = prop->value();
1140
1141         if ((prop = node.property ("type")) == 0) {
1142                 _type = DataType::AUDIO;
1143         } else {
1144                 _type = DataType(prop->value());
1145         }
1146
1147         if ((prop = node.property ("start")) != 0) {
1148                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1149                 if (val != _start) {
1150                         what_changed = Change (what_changed|StartChanged);
1151                         cerr << _name << " start changed\n";
1152                         _start = val;
1153                 }
1154         } else {
1155                 _start = 0;
1156         }
1157
1158         if ((prop = node.property ("length")) != 0) {
1159                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1160                 if (val != _length) {
1161                         what_changed = Change (what_changed|LengthChanged);
1162                         cerr << _name << " length changed\n";
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                         cerr << _name << " position changed\n";
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                         cerr << _name << " layer changed\n";
1190                         _layer = x;
1191                 }
1192         } else {
1193                 _layer = 0;
1194         }
1195
1196         if ((prop = node.property ("sync-position")) != 0) {
1197                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1198                 if (val != _sync_position) {
1199                         what_changed = Change (what_changed|SyncOffsetChanged);
1200                         cerr << _name << " sync changed\n";
1201                         _sync_position = val;
1202                 }
1203         } else {
1204                 _sync_position = _start;
1205         }
1206
1207         if ((prop = node.property ("positional-lock-style")) != 0) {
1208                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1209
1210                 if (_positional_lock_style == MusicTime) {
1211                         if ((prop = node.property ("bbt-position")) == 0) {
1212                                 /* missing BBT info, revert to audio time locking */
1213                                 _positional_lock_style = AudioTime;
1214                         } else {
1215                                 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1216                                             &_bbt_time.bars,
1217                                             &_bbt_time.beats,
1218                                             &_bbt_time.ticks) != 3) {
1219                                         _positional_lock_style = AudioTime;
1220                                 }
1221                         }
1222                 }
1223
1224         } else {
1225                 _positional_lock_style = AudioTime;
1226         }
1227
1228         /* XXX FIRST EDIT !!! */
1229
1230         /* these 3 properties never change as a result of any editing */
1231
1232         if ((prop = node.property ("ancestral-start")) != 0) {
1233                 _ancestral_start = strtoll (prop->value().c_str(), 0, 10);
1234         } else {
1235                 _ancestral_start = _start;
1236         }
1237
1238         if ((prop = node.property ("ancestral-length")) != 0) {
1239                 _ancestral_length = strtoll (prop->value().c_str(), 0, 10);
1240         } else {
1241                 _ancestral_length = _length;
1242         }
1243
1244         if ((prop = node.property ("stretch")) != 0) {
1245                 _stretch = atof (prop->value());
1246
1247                 /* fix problem with old sessions corrupted by an impossible
1248                    value for _stretch
1249                 */
1250                 if (_stretch == 0.0) {
1251                         _stretch = 1.0;
1252                 }
1253         } else {
1254                 _stretch = 1.0;
1255         }
1256
1257         if ((prop = node.property ("shift")) != 0) {
1258                 _shift = atof (prop->value());
1259
1260                 /* fix problem with old sessions corrupted by an impossible
1261                    value for _shift
1262                 */
1263                 if (_shift == 0.0) {
1264                         _shift = 1.0;
1265                 }
1266         } else {
1267                 _shift = 1.0;
1268         }
1269
1270
1271         /* note: derived classes set flags */
1272
1273         delete _extra_xml;
1274         _extra_xml = 0;
1275
1276         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1277
1278                 XMLNode *child;
1279
1280                 child = (*niter);
1281
1282                 if (child->name () == "Extra") {
1283                         _extra_xml = new XMLNode (*child);
1284                         break;
1285                 }
1286         }
1287
1288         if (send) {
1289                 cerr << _name << ": final change to be sent: " << hex << what_changed << dec << endl;
1290                 send_change (what_changed);
1291         }
1292
1293         return 0;
1294 }
1295
1296 int
1297 Region::set_state (const XMLNode& node, int version)
1298 {
1299         const XMLProperty *prop;
1300         Change what_changed = Change (0);
1301
1302         /* ID is not allowed to change, ever */
1303
1304         if ((prop = node.property ("id")) == 0) {
1305                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1306                 return -1;
1307         }
1308
1309         _id = prop->value();
1310
1311         _first_edit = EditChangesNothing;
1312
1313         set_live_state (node, version, what_changed, true);
1314
1315         return 0;
1316 }
1317
1318 void
1319 Region::freeze ()
1320 {
1321         _frozen++;
1322         _last_length = _length;
1323         _last_position = _position;
1324 }
1325
1326 void
1327 Region::thaw (const string& /*why*/)
1328 {
1329         Change what_changed = Change (0);
1330
1331         {
1332                 Glib::Mutex::Lock lm (_lock);
1333
1334                 if (_frozen && --_frozen > 0) {
1335                         return;
1336                 }
1337
1338                 if (_pending_changed) {
1339                         what_changed = _pending_changed;
1340                         _pending_changed = Change (0);
1341                 }
1342         }
1343
1344         if (what_changed == Change (0)) {
1345                 return;
1346         }
1347
1348         if (what_changed & LengthChanged) {
1349                 if (what_changed & PositionChanged) {
1350                         recompute_at_start ();
1351                 }
1352                 recompute_at_end ();
1353         }
1354
1355         send_change (what_changed);
1356 }
1357
1358 void
1359 Region::send_change (Change what_changed)
1360 {
1361
1362         {
1363                 Glib::Mutex::Lock lm (_lock);
1364                 if (_frozen) {
1365                         _pending_changed = Change (_pending_changed|what_changed);
1366                         return;
1367                 }
1368         }
1369
1370         cerr << _name << " actually sends " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1371         StateChanged (what_changed);
1372         cerr << _name << " done with " << hex << what_changed << dec << " @" << get_microseconds() << endl;
1373
1374         if (!(_flags & DoNotSendPropertyChanges)) {
1375
1376                 /* Try and send a shared_pointer unless this is part of the constructor.
1377                    If so, do nothing.
1378                 */
1379
1380                 try {
1381                         boost::shared_ptr<Region> rptr = shared_from_this();
1382                         cerr << _name << " actually sends prop change " << hex << what_changed << dec <<  " @ " << get_microseconds() << endl;
1383                         RegionPropertyChanged (rptr);
1384                         cerr << _name << " done with prop change  @ " << get_microseconds() << endl;
1385                 } catch (...) {
1386                         /* no shared_ptr available, relax; */
1387                 }
1388         }
1389
1390 }
1391
1392 void
1393 Region::set_last_layer_op (uint64_t when)
1394 {
1395         _last_layer_op = when;
1396 }
1397
1398 bool
1399 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1400 {
1401         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1402 }
1403
1404 bool
1405 Region::equivalent (boost::shared_ptr<const Region> other) const
1406 {
1407         return _start == other->_start &&
1408                 _position == other->_position &&
1409                 _length == other->_length;
1410 }
1411
1412 bool
1413 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1414 {
1415         return _start == other->_start &&
1416                 _length == other->_length;
1417 }
1418
1419 bool
1420 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1421 {
1422         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1423 }
1424
1425 void
1426 Region::source_deleted (boost::weak_ptr<Source>)
1427 {
1428         _sources.clear ();
1429
1430         if (!_session.deletion_in_progress()) {
1431                 /* this is a very special case: at least one of the region's
1432                    sources has bee deleted, so invalidate all references to
1433                    ourselves. Do NOT do this during session deletion, because
1434                    then we run the risk that this will actually result
1435                    in this object being deleted (as refcnt goes to zero)
1436                    while emitting DropReferences.
1437                 */
1438
1439                 drop_references ();
1440         }
1441 }
1442
1443 vector<string>
1444 Region::master_source_names ()
1445 {
1446         SourceList::iterator i;
1447
1448         vector<string> names;
1449         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1450                 names.push_back((*i)->name());
1451         }
1452
1453         return names;
1454 }
1455
1456 void
1457 Region::set_master_sources (const SourceList& srcs)
1458 {
1459         _master_sources = srcs;
1460         assert (_sources.size() == _master_sources.size());
1461 }
1462
1463 bool
1464 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1465 {
1466         if (!other)
1467                 return false;
1468
1469         SourceList::const_iterator i;
1470         SourceList::const_iterator io;
1471
1472         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1473                 if ((*i)->id() != (*io)->id()) {
1474                         return false;
1475                 }
1476         }
1477
1478         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1479                 if ((*i)->id() != (*io)->id()) {
1480                         return false;
1481                 }
1482         }
1483
1484         return true;
1485 }
1486
1487 bool
1488 Region::uses_source (boost::shared_ptr<const Source> source) const
1489 {
1490         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1491                 if (*i == source) {
1492                         return true;
1493                 }
1494         }
1495         return false;
1496 }
1497
1498 sframes_t
1499 Region::source_length(uint32_t n) const
1500 {
1501         return _sources[n]->length(_position - _start);
1502 }
1503
1504 bool
1505 Region::verify_length (nframes_t len)
1506 {
1507         if (source() && (source()->destructive() || source()->length_mutable())) {
1508                 return true;
1509         }
1510
1511         nframes_t maxlen = 0;
1512
1513         for (uint32_t n=0; n < _sources.size(); ++n) {
1514                 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1515         }
1516
1517         len = min (len, maxlen);
1518
1519         return true;
1520 }
1521
1522 bool
1523 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1524 {
1525         if (source() && (source()->destructive() || source()->length_mutable())) {
1526                 return true;
1527         }
1528
1529         nframes_t maxlen = 0;
1530
1531         for (uint32_t n=0; n < _sources.size(); ++n) {
1532                 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1533         }
1534
1535         new_length = min (new_length, maxlen);
1536
1537         return true;
1538 }
1539
1540 bool
1541 Region::verify_start (nframes_t pos)
1542 {
1543         if (source() && (source()->destructive() || source()->length_mutable())) {
1544                 return true;
1545         }
1546
1547         for (uint32_t n=0; n < _sources.size(); ++n) {
1548                 if (pos > source_length(n) - _length) {
1549                         return false;
1550                 }
1551         }
1552         return true;
1553 }
1554
1555 bool
1556 Region::verify_start_mutable (nframes_t& new_start)
1557 {
1558         if (source() && (source()->destructive() || source()->length_mutable())) {
1559                 return true;
1560         }
1561
1562         for (uint32_t n=0; n < _sources.size(); ++n) {
1563                 if (new_start > source_length(n) - _length) {
1564                         new_start = source_length(n) - _length;
1565                 }
1566         }
1567         return true;
1568 }
1569
1570 boost::shared_ptr<Region>
1571 Region::get_parent() const
1572 {
1573         boost::shared_ptr<Playlist> pl (playlist());
1574
1575         if (pl) {
1576                 boost::shared_ptr<Region> r;
1577                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1578
1579                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1580                         return boost::static_pointer_cast<Region> (r);
1581                 }
1582         }
1583
1584         return boost::shared_ptr<Region>();
1585 }
1586
1587 int
1588 Region::apply (Filter& filter)
1589 {
1590         return filter.run (shared_from_this());
1591 }
1592
1593
1594 void
1595 Region::invalidate_transients ()
1596 {
1597         _valid_transients = false;
1598         _transients.clear ();
1599 }
1600
1601
1602 void
1603 Region::use_sources (SourceList const & s)
1604 {
1605         set<boost::shared_ptr<Source> > unique_srcs;
1606
1607         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1608                 _sources.push_back (*i);
1609                 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1610                 unique_srcs.insert (*i);
1611         }
1612
1613         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1614                 _master_sources.push_back (*i);
1615                 if (unique_srcs.find (*i) == unique_srcs.end()) {
1616                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1617                 }
1618         }
1619 }
1620