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