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