09f60926c48fd27a2f76bd994b2a506efa511adb
[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                         _start = val;
1152                 }
1153         } else {
1154                 _start = 0;
1155         }
1156
1157         if ((prop = node.property ("length")) != 0) {
1158                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1159                 if (val != _length) {
1160                         what_changed = Change (what_changed|LengthChanged);
1161                         _last_length = _length;
1162                         _length = val;
1163                 }
1164         } else {
1165                 _last_length = _length;
1166                 _length = 1;
1167         }
1168
1169         if ((prop = node.property ("position")) != 0) {
1170                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1171                 if (val != _position) {
1172                         what_changed = Change (what_changed|PositionChanged);
1173                         _last_position = _position;
1174                         _position = val;
1175                 }
1176         } else {
1177                 _last_position = _position;
1178                 _position = 0;
1179         }
1180
1181         if ((prop = node.property ("layer")) != 0) {
1182                 layer_t x;
1183                 x = (layer_t) atoi (prop->value().c_str());
1184                 if (x != _layer) {
1185                         what_changed = Change (what_changed|LayerChanged);
1186                         _layer = x;
1187                 }
1188         } else {
1189                 _layer = 0;
1190         }
1191
1192         if ((prop = node.property ("sync-position")) != 0) {
1193                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1194                 if (val != _sync_position) {
1195                         what_changed = Change (what_changed|SyncOffsetChanged);
1196                         _sync_position = val;
1197                 }
1198         } else {
1199                 _sync_position = _start;
1200         }
1201
1202         if ((prop = node.property ("positional-lock-style")) != 0) {
1203                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1204
1205                 if (_positional_lock_style == MusicTime) {
1206                         if ((prop = node.property ("bbt-position")) == 0) {
1207                                 /* missing BBT info, revert to audio time locking */
1208                                 _positional_lock_style = AudioTime;
1209                         } else {
1210                                 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1211                                             &_bbt_time.bars,
1212                                             &_bbt_time.beats,
1213                                             &_bbt_time.ticks) != 3) {
1214                                         _positional_lock_style = AudioTime;
1215                                 }
1216                         }
1217                 }
1218
1219         } else {
1220                 _positional_lock_style = AudioTime;
1221         }
1222
1223         /* XXX FIRST EDIT !!! */
1224
1225         /* these 3 properties never change as a result of any editing */
1226
1227         if ((prop = node.property ("ancestral-start")) != 0) {
1228                 _ancestral_start = strtoll (prop->value().c_str(), 0, 10);
1229         } else {
1230                 _ancestral_start = _start;
1231         }
1232
1233         if ((prop = node.property ("ancestral-length")) != 0) {
1234                 _ancestral_length = strtoll (prop->value().c_str(), 0, 10);
1235         } else {
1236                 _ancestral_length = _length;
1237         }
1238
1239         if ((prop = node.property ("stretch")) != 0) {
1240                 _stretch = atof (prop->value());
1241
1242                 /* fix problem with old sessions corrupted by an impossible
1243                    value for _stretch
1244                 */
1245                 if (_stretch == 0.0) {
1246                         _stretch = 1.0;
1247                 }
1248         } else {
1249                 _stretch = 1.0;
1250         }
1251
1252         if ((prop = node.property ("shift")) != 0) {
1253                 _shift = atof (prop->value());
1254
1255                 /* fix problem with old sessions corrupted by an impossible
1256                    value for _shift
1257                 */
1258                 if (_shift == 0.0) {
1259                         _shift = 1.0;
1260                 }
1261         } else {
1262                 _shift = 1.0;
1263         }
1264
1265
1266         /* note: derived classes set flags */
1267
1268         delete _extra_xml;
1269         _extra_xml = 0;
1270
1271         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1272
1273                 XMLNode *child;
1274
1275                 child = (*niter);
1276
1277                 if (child->name () == "Extra") {
1278                         _extra_xml = new XMLNode (*child);
1279                         break;
1280                 }
1281         }
1282
1283         if (send) {
1284                 send_change (what_changed);
1285         }
1286
1287         return 0;
1288 }
1289
1290 int
1291 Region::set_state (const XMLNode& node, int version)
1292 {
1293         const XMLProperty *prop;
1294         Change what_changed = Change (0);
1295
1296         /* ID is not allowed to change, ever */
1297
1298         if ((prop = node.property ("id")) == 0) {
1299                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1300                 return -1;
1301         }
1302
1303         _id = prop->value();
1304
1305         _first_edit = EditChangesNothing;
1306
1307         set_live_state (node, version, what_changed, true);
1308
1309         return 0;
1310 }
1311
1312 void
1313 Region::freeze ()
1314 {
1315         _frozen++;
1316         _last_length = _length;
1317         _last_position = _position;
1318 }
1319
1320 void
1321 Region::thaw (const string& /*why*/)
1322 {
1323         Change what_changed = Change (0);
1324
1325         {
1326                 Glib::Mutex::Lock lm (_lock);
1327
1328                 if (_frozen && --_frozen > 0) {
1329                         return;
1330                 }
1331
1332                 if (_pending_changed) {
1333                         what_changed = _pending_changed;
1334                         _pending_changed = Change (0);
1335                 }
1336         }
1337
1338         if (what_changed == Change (0)) {
1339                 return;
1340         }
1341
1342         if (what_changed & LengthChanged) {
1343                 if (what_changed & PositionChanged) {
1344                         recompute_at_start ();
1345                 }
1346                 recompute_at_end ();
1347         }
1348
1349         StateChanged (what_changed);
1350 }
1351
1352 void
1353 Region::send_change (Change what_changed)
1354 {
1355         {
1356                 Glib::Mutex::Lock lm (_lock);
1357                 if (_frozen) {
1358                         _pending_changed = Change (_pending_changed|what_changed);
1359                         return;
1360                 }
1361         }
1362
1363         StateChanged (what_changed);
1364
1365         if (!(_flags & DoNotSendPropertyChanges)) {
1366
1367                 /* Try and send a shared_pointer unless this is part of the constructor.
1368                    If so, do nothing.
1369                 */
1370
1371                 try {
1372                         boost::shared_ptr<Region> rptr = shared_from_this();
1373                         RegionPropertyChanged (rptr);
1374                 } catch (...) {
1375                         /* no shared_ptr available, relax; */
1376                 }
1377         }
1378
1379 }
1380
1381 void
1382 Region::set_last_layer_op (uint64_t when)
1383 {
1384         _last_layer_op = when;
1385 }
1386
1387 bool
1388 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1389 {
1390         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1391 }
1392
1393 bool
1394 Region::equivalent (boost::shared_ptr<const Region> other) const
1395 {
1396         return _start == other->_start &&
1397                 _position == other->_position &&
1398                 _length == other->_length;
1399 }
1400
1401 bool
1402 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1403 {
1404         return _start == other->_start &&
1405                 _length == other->_length;
1406 }
1407
1408 bool
1409 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1410 {
1411         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1412 }
1413
1414 void
1415 Region::source_deleted (boost::weak_ptr<Source>)
1416 {
1417         _sources.clear ();
1418
1419         if (!_session.deletion_in_progress()) {
1420                 /* this is a very special case: at least one of the region's
1421                    sources has bee deleted, so invalidate all references to
1422                    ourselves. Do NOT do this during session deletion, because
1423                    then we run the risk that this will actually result
1424                    in this object being deleted (as refcnt goes to zero)
1425                    while emitting DropReferences.
1426                 */
1427
1428                 drop_references ();
1429         }
1430 }
1431
1432 vector<string>
1433 Region::master_source_names ()
1434 {
1435         SourceList::iterator i;
1436
1437         vector<string> names;
1438         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1439                 names.push_back((*i)->name());
1440         }
1441
1442         return names;
1443 }
1444
1445 void
1446 Region::set_master_sources (const SourceList& srcs)
1447 {
1448         _master_sources = srcs;
1449         assert (_sources.size() == _master_sources.size());
1450 }
1451
1452 bool
1453 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1454 {
1455         if (!other)
1456                 return false;
1457
1458         SourceList::const_iterator i;
1459         SourceList::const_iterator io;
1460
1461         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1462                 if ((*i)->id() != (*io)->id()) {
1463                         return false;
1464                 }
1465         }
1466
1467         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1468                 if ((*i)->id() != (*io)->id()) {
1469                         return false;
1470                 }
1471         }
1472
1473         return true;
1474 }
1475
1476 bool
1477 Region::uses_source (boost::shared_ptr<const Source> source) const
1478 {
1479         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1480                 if (*i == source) {
1481                         return true;
1482                 }
1483         }
1484         return false;
1485 }
1486
1487 sframes_t
1488 Region::source_length(uint32_t n) const
1489 {
1490         return _sources[n]->length(_position - _start);
1491 }
1492
1493 bool
1494 Region::verify_length (nframes_t len)
1495 {
1496         if (source() && (source()->destructive() || source()->length_mutable())) {
1497                 return true;
1498         }
1499
1500         nframes_t maxlen = 0;
1501
1502         for (uint32_t n=0; n < _sources.size(); ++n) {
1503                 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1504         }
1505
1506         len = min (len, maxlen);
1507
1508         return true;
1509 }
1510
1511 bool
1512 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1513 {
1514         if (source() && (source()->destructive() || source()->length_mutable())) {
1515                 return true;
1516         }
1517
1518         nframes_t maxlen = 0;
1519
1520         for (uint32_t n=0; n < _sources.size(); ++n) {
1521                 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1522         }
1523
1524         new_length = min (new_length, maxlen);
1525
1526         return true;
1527 }
1528
1529 bool
1530 Region::verify_start (nframes_t pos)
1531 {
1532         if (source() && (source()->destructive() || source()->length_mutable())) {
1533                 return true;
1534         }
1535
1536         for (uint32_t n=0; n < _sources.size(); ++n) {
1537                 if (pos > source_length(n) - _length) {
1538                         return false;
1539                 }
1540         }
1541         return true;
1542 }
1543
1544 bool
1545 Region::verify_start_mutable (nframes_t& new_start)
1546 {
1547         if (source() && (source()->destructive() || source()->length_mutable())) {
1548                 return true;
1549         }
1550
1551         for (uint32_t n=0; n < _sources.size(); ++n) {
1552                 if (new_start > source_length(n) - _length) {
1553                         new_start = source_length(n) - _length;
1554                 }
1555         }
1556         return true;
1557 }
1558
1559 boost::shared_ptr<Region>
1560 Region::get_parent() const
1561 {
1562         boost::shared_ptr<Playlist> pl (playlist());
1563
1564         if (pl) {
1565                 boost::shared_ptr<Region> r;
1566                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1567
1568                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1569                         return boost::static_pointer_cast<Region> (r);
1570                 }
1571         }
1572
1573         return boost::shared_ptr<Region>();
1574 }
1575
1576 int
1577 Region::apply (Filter& filter)
1578 {
1579         return filter.run (shared_from_this());
1580 }
1581
1582
1583 void
1584 Region::invalidate_transients ()
1585 {
1586         _valid_transients = false;
1587         _transients.clear ();
1588 }
1589
1590
1591 void
1592 Region::use_sources (SourceList const & s)
1593 {
1594         set<boost::shared_ptr<Source> > unique_srcs;
1595
1596         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1597                 _sources.push_back (*i);
1598                 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1599                 unique_srcs.insert (*i);
1600         }
1601
1602         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1603                 _master_sources.push_back (*i);
1604                 if (unique_srcs.find (*i) == unique_srcs.end()) {
1605                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1606                 }
1607         }
1608 }
1609