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