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