Remove unnecessary 0 checks before delete; see http://www.parashift.com/c++-faq-lite...
[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         delete _extra_xml;
1342         _extra_xml = 0;
1343
1344         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1345                 
1346                 XMLNode *child;
1347                 
1348                 child = (*niter);
1349                 
1350                 if (child->name () == "extra") {
1351                         _extra_xml = new XMLNode (*child);
1352                         break;
1353                 }
1354         }
1355
1356         if (send) {
1357                 send_change (what_changed);
1358         }
1359
1360         return 0;
1361 }
1362
1363 int
1364 Region::set_state (const XMLNode& node)
1365 {
1366         const XMLProperty *prop;
1367         Change what_changed = Change (0);
1368
1369         /* ID is not allowed to change, ever */
1370
1371         if ((prop = node.property ("id")) == 0) {
1372                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1373                 return -1;
1374         }
1375
1376         _id = prop->value();
1377         
1378         _first_edit = EditChangesNothing;
1379         
1380         set_live_state (node, what_changed, true);
1381
1382         return 0;
1383 }
1384
1385 void
1386 Region::freeze ()
1387 {
1388         _frozen++;
1389         _last_length = _length;
1390         _last_position = _position;
1391 }
1392
1393 void
1394 Region::thaw (const string& why)
1395 {
1396         Change what_changed = Change (0);
1397
1398         {
1399                 Glib::Mutex::Lock lm (_lock);
1400
1401                 if (_frozen && --_frozen > 0) {
1402                         return;
1403                 }
1404
1405                 if (_pending_changed) {
1406                         what_changed = _pending_changed;
1407                         _pending_changed = Change (0);
1408                 }
1409         }
1410
1411         if (what_changed == Change (0)) {
1412                 return;
1413         }
1414
1415         if (what_changed & LengthChanged) {
1416                 if (what_changed & PositionChanged) {
1417                         recompute_at_start ();
1418                 } 
1419                 recompute_at_end ();
1420         }
1421                 
1422         StateChanged (what_changed);
1423 }
1424
1425 void
1426 Region::send_change (Change what_changed)
1427 {
1428         {
1429                 Glib::Mutex::Lock lm (_lock);
1430                 if (_frozen) {
1431                         _pending_changed = Change (_pending_changed|what_changed);
1432                         return;
1433                 } 
1434         }
1435
1436         StateChanged (what_changed);
1437         
1438         if (!(_flags & DoNotSaveState)) {
1439                 
1440                 /* Try and send a shared_pointer unless this is part of the constructor.
1441                    If so, do nothing.
1442                 */
1443                 
1444                 try {
1445                         boost::shared_ptr<Region> rptr = shared_from_this();
1446                         RegionPropertyChanged (rptr);
1447                 } catch (...) {
1448                         /* no shared_ptr available, relax; */
1449                 }
1450         }
1451         
1452 }
1453
1454 void
1455 Region::set_last_layer_op (uint64_t when)
1456 {
1457         _last_layer_op = when;
1458 }
1459
1460 bool
1461 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1462 {
1463         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1464 }
1465
1466 bool
1467 Region::equivalent (boost::shared_ptr<const Region> other) const
1468 {
1469         return _start == other->_start &&
1470                 _position == other->_position &&
1471                 _length == other->_length;
1472 }
1473
1474 bool
1475 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1476 {
1477         return _start == other->_start &&
1478                 _length == other->_length;
1479 }
1480
1481 bool
1482 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1483 {
1484         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1485 }
1486
1487 void
1488 Region::source_deleted (boost::shared_ptr<Source>)
1489 {
1490         _sources.clear ();
1491         drop_references ();
1492 }
1493
1494 vector<string>
1495 Region::master_source_names ()
1496 {
1497         SourceList::iterator i;
1498
1499         vector<string> names;
1500         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1501                 names.push_back((*i)->name());
1502         }
1503
1504         return names;
1505 }
1506
1507 void
1508 Region::set_master_sources (const SourceList& srcs)
1509 {
1510         _master_sources = srcs;
1511 }
1512
1513 bool
1514 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1515 {
1516         if (!other)
1517                 return false;
1518
1519         SourceList::const_iterator i;
1520         SourceList::const_iterator io;
1521
1522         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1523                 if ((*i)->id() != (*io)->id()) {
1524                         return false;
1525                 }
1526         }
1527
1528         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1529                 if ((*i)->id() != (*io)->id()) {
1530                         return false;
1531                 }
1532         }
1533
1534         return true;
1535 }
1536
1537 bool
1538 Region::verify_length (nframes_t len)
1539 {
1540         if (source() && (source()->destructive() || source()->length_mutable())) {
1541                 return true;
1542         }
1543
1544         nframes_t maxlen = 0;
1545
1546         for (uint32_t n=0; n < _sources.size(); ++n) {
1547                 maxlen = max (maxlen, _sources[n]->length() - _start);
1548         }
1549         
1550         len = min (len, maxlen);
1551         
1552         return true;
1553 }
1554
1555 bool
1556 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1557 {
1558         if (source() && (source()->destructive() || source()->length_mutable())) {
1559                 return true;
1560         }
1561
1562         nframes_t maxlen = 0;
1563
1564         for (uint32_t n=0; n < _sources.size(); ++n) {
1565                 maxlen = max (maxlen, _sources[n]->length() - new_start);
1566         }
1567
1568         new_length = min (new_length, maxlen);
1569
1570         return true;
1571 }
1572
1573 bool
1574 Region::verify_start (nframes_t pos)
1575 {
1576         if (source() && (source()->destructive() || source()->length_mutable())) {
1577                 return true;
1578         }
1579
1580         for (uint32_t n=0; n < _sources.size(); ++n) {
1581                 if (pos > _sources[n]->length() - _length) {
1582                         return false;
1583                 }
1584         }
1585         return true;
1586 }
1587
1588 bool
1589 Region::verify_start_mutable (nframes_t& new_start)
1590 {
1591         if (source() && (source()->destructive() || source()->length_mutable())) {
1592                 return true;
1593         }
1594
1595         for (uint32_t n=0; n < _sources.size(); ++n) {
1596                 if (new_start > _sources[n]->length() - _length) {
1597                         new_start = _sources[n]->length() - _length;
1598                 }
1599         }
1600         return true;
1601 }
1602
1603 boost::shared_ptr<Region>
1604 Region::get_parent() const
1605 {
1606         boost::shared_ptr<Playlist> pl (playlist());
1607
1608         if (pl) {
1609                 boost::shared_ptr<Region> r;
1610                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1611                 
1612                 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1613                         return boost::static_pointer_cast<Region> (r);
1614                 }
1615         }
1616         
1617         return boost::shared_ptr<Region>();
1618 }
1619
1620 int
1621 Region::apply (Filter& filter)
1622 {
1623         return filter.run (shared_from_this());
1624 }
1625
1626
1627 void
1628 Region::invalidate_transients ()
1629 {
1630         _valid_transients = false;
1631         _transients.clear ();
1632 }
1633