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