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