8625b17a8a8276b11c14bea9a852f5ecc68ffe1b
[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         if (locked()) {
768                 return;
769         }
770
771         framepos_t end = last_frame();
772         framepos_t source_zero;
773
774         if (_position > _start) {
775                 source_zero = _position - _start;
776         } else {
777                 source_zero = 0; // its actually negative, but this will work for us
778         }
779
780         if (new_position < end) { /* can't trim it zero or negative length */
781
782                 framecnt_t newlen;
783
784                 /* can't trim it back passed where source position zero is located */
785
786                 new_position = max (new_position, source_zero);
787
788
789                 if (new_position > _position) {
790                         newlen = _length - (new_position - _position);
791                 } else {
792                         newlen = _length + (_position - new_position);
793                 }
794
795                 trim_to_internal (new_position, newlen, src);
796                 if (!property_changes_suspended()) {
797                         recompute_at_start ();
798                 }
799         }
800 }
801
802 /** @param new_endpoint New region end point, such that, for example,
803  *  a region at 0 of length 10 has an endpoint of 9.
804  */
805
806 void
807 Region::trim_end (framepos_t new_endpoint, void */*src*/)
808 {
809         if (locked()) {
810                 return;
811         }
812
813         if (new_endpoint > _position) {
814                 trim_to_internal (_position, new_endpoint - _position + 1, this);
815                 if (!property_changes_suspended()) {
816                         recompute_at_end ();
817                 }
818         }
819 }
820
821 void
822 Region::trim_to (framepos_t position, framecnt_t length, void *src)
823 {
824         if (locked()) {
825                 return;
826         }
827
828         trim_to_internal (position, length, src);
829
830         if (!property_changes_suspended()) {
831                 recompute_at_start ();
832                 recompute_at_end ();
833         }
834 }
835
836 void
837 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
838 {
839         frameoffset_t start_shift;
840         framepos_t new_start;
841
842         if (locked()) {
843                 return;
844         }
845
846         if (position > _position) {
847                 start_shift = position - _position;
848         } else {
849                 start_shift = -(_position - position);
850         }
851
852         if (start_shift > 0) {
853
854                 if (_start > max_frames - start_shift) {
855                         new_start = max_frames;
856                 } else {
857                         new_start = _start + start_shift;
858                 }
859
860
861         } else if (start_shift < 0) {
862
863                 if (_start < -start_shift) {
864                         new_start = 0;
865                 } else {
866                         new_start = _start + start_shift;
867                 }
868         } else {
869                 new_start = _start;
870         }
871
872         if (!verify_start_and_length (new_start, length)) {
873                 return;
874         }
875
876         PropertyChange what_changed;
877
878         if (_start != new_start) {
879                 _start = new_start;
880                 what_changed.add (Properties::start);
881         }
882         if (_length != length) {
883                 if (!property_changes_suspended()) {
884                         _last_length = _length;
885                 }
886                 _length = length;
887                 what_changed.add (Properties::length);
888         }
889         if (_position != position) {
890                 if (!property_changes_suspended()) {
891                         _last_position = _position;
892                 }
893                 _position = position;
894                 what_changed.add (Properties::position);
895         }
896
897         _whole_file = false;
898
899         PropertyChange start_and_length;
900
901         start_and_length.add (Properties::start);
902         start_and_length.add (Properties::length);
903
904         if (what_changed.contains (start_and_length)) {
905                 first_edit ();
906         }
907
908         if (!what_changed.empty()) {
909                 send_change (what_changed);
910         }
911 }
912
913 void
914 Region::set_hidden (bool yn)
915 {
916         if (hidden() != yn) {
917                 _hidden = yn;
918                 send_change (Properties::hidden);
919         }
920 }
921
922 void
923 Region::set_whole_file (bool yn)
924 {
925         _whole_file = yn;
926         /* no change signal */
927 }
928
929 void
930 Region::set_automatic (bool yn)
931 {
932         _automatic = yn;
933         /* no change signal */
934 }
935
936 void
937 Region::set_muted (bool yn)
938 {
939         if (muted() != yn) {
940                 _muted = yn;
941                 send_change (Properties::muted);
942         }
943 }
944
945 void
946 Region::set_opaque (bool yn)
947 {
948         if (opaque() != yn) {
949                 _opaque = yn;
950                 send_change (Properties::opaque);
951         }
952 }
953
954 void
955 Region::set_locked (bool yn)
956 {
957         if (locked() != yn) {
958                 _locked = yn;
959                 send_change (Properties::locked);
960         }
961 }
962
963 void
964 Region::set_position_locked (bool yn)
965 {
966         if (position_locked() != yn) {
967                 _position_locked = yn;
968                 send_change (Properties::locked);
969         }
970 }
971
972 void
973 Region::set_sync_position (framepos_t absolute_pos)
974 {
975         framepos_t const file_pos = _start + (absolute_pos - _position);
976
977         if (file_pos != _sync_position) {
978                 _sync_marked = true;
979                 _sync_position = file_pos;
980                 if (!property_changes_suspended()) {
981                         maybe_uncopy ();
982                 }
983                 send_change (Properties::sync_position);
984         }
985 }
986
987 void
988 Region::clear_sync_position ()
989 {
990         if (sync_marked()) {
991                 _sync_marked = false;
992                 if (!property_changes_suspended()) {
993                         maybe_uncopy ();
994                 }
995                 send_change (Properties::sync_position);
996         }
997 }
998
999 framepos_t
1000 Region::sync_offset (int& dir) const
1001 {
1002         /* returns the sync point relative the first frame of the region */
1003
1004         if (sync_marked()) {
1005                 if (_sync_position > _start) {
1006                         dir = 1;
1007                         return _sync_position - _start;
1008                 } else {
1009                         dir = -1;
1010                         return _start - _sync_position;
1011                 }
1012         } else {
1013                 dir = 0;
1014                 return 0;
1015         }
1016 }
1017
1018 framepos_t
1019 Region::adjust_to_sync (framepos_t pos) const
1020 {
1021         int sync_dir;
1022         frameoffset_t offset = sync_offset (sync_dir);
1023
1024         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1025
1026         if (sync_dir > 0) {
1027                 if (pos > offset) {
1028                         pos -= offset;
1029                 } else {
1030                         pos = 0;
1031                 }
1032         } else {
1033                 if (max_frames - pos > offset) {
1034                         pos += offset;
1035                 }
1036         }
1037
1038         return pos;
1039 }
1040
1041 framepos_t
1042 Region::sync_position() const
1043 {
1044         if (sync_marked()) {
1045                 return _sync_position;
1046         } else {
1047                 return _start;
1048         }
1049 }
1050
1051 void
1052 Region::raise ()
1053 {
1054         boost::shared_ptr<Playlist> pl (playlist());
1055         if (pl) {
1056                 pl->raise_region (shared_from_this ());
1057         }
1058 }
1059
1060 void
1061 Region::lower ()
1062 {
1063         boost::shared_ptr<Playlist> pl (playlist());
1064         if (pl) {
1065                 pl->lower_region (shared_from_this ());
1066         }
1067 }
1068
1069
1070 void
1071 Region::raise_to_top ()
1072 {
1073         boost::shared_ptr<Playlist> pl (playlist());
1074         if (pl) {
1075                 pl->raise_region_to_top (shared_from_this());
1076         }
1077 }
1078
1079 void
1080 Region::lower_to_bottom ()
1081 {
1082         boost::shared_ptr<Playlist> pl (playlist());
1083         if (pl) {
1084                 pl->lower_region_to_bottom (shared_from_this());
1085         }
1086 }
1087
1088 void
1089 Region::set_layer (layer_t l)
1090 {
1091         if (_layer != l) {
1092                 _layer = l;
1093
1094                 send_change (Properties::layer);
1095         }
1096 }
1097
1098 XMLNode&
1099 Region::state (bool /*full_state*/)
1100 {
1101         XMLNode *node = new XMLNode ("Region");
1102         char buf[64];
1103         const char* fe = NULL;
1104
1105         add_properties (*node);
1106
1107         _id.print (buf, sizeof (buf));
1108         node->add_property ("id", buf);
1109         node->add_property ("type", _type.to_string());
1110
1111         switch (_first_edit) {
1112         case EditChangesNothing:
1113                 fe = X_("nothing");
1114                 break;
1115         case EditChangesName:
1116                 fe = X_("name");
1117                 break;
1118         case EditChangesID:
1119                 fe = X_("id");
1120                 break;
1121         default: /* should be unreachable but makes g++ happy */
1122                 fe = X_("nothing");
1123                 break;
1124         }
1125
1126         node->add_property ("first-edit", fe);
1127
1128         /* note: flags are stored by derived classes */
1129
1130         if (_positional_lock_style != AudioTime) {
1131                 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1132                 stringstream str;
1133                 str << _bbt_time;
1134                 node->add_property ("bbt-position", str.str());
1135         }
1136
1137         return *node;
1138 }
1139
1140 XMLNode&
1141 Region::get_state ()
1142 {
1143         return state (true);
1144 }
1145
1146 int
1147 Region::set_state (const XMLNode& node, int version)
1148 {
1149         PropertyChange what_changed;
1150         return _set_state (node, version, what_changed, true);
1151 }
1152
1153 int
1154 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1155 {
1156         const XMLProperty* prop;
1157
1158         what_changed = set_properties (node);
1159
1160         if ((prop = node.property (X_("id")))) {
1161                 _id = prop->value();
1162         }
1163
1164         if ((prop = node.property ("positional-lock-style")) != 0) {
1165                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1166
1167                 if (_positional_lock_style == MusicTime) {
1168                         if ((prop = node.property ("bbt-position")) == 0) {
1169                                 /* missing BBT info, revert to audio time locking */
1170                                 _positional_lock_style = AudioTime;
1171                         } else {
1172                                 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1173                                             &_bbt_time.bars,
1174                                             &_bbt_time.beats,
1175                                             &_bbt_time.ticks) != 3) {
1176                                         _positional_lock_style = AudioTime;
1177                                 }
1178                         }
1179                 }
1180
1181         }
1182
1183         /* fix problems with old sessions corrupted by impossible
1184            values for _stretch or _shift
1185         */
1186         if (_stretch == 0.0f) {
1187                 _stretch = 1.0f;
1188         }
1189         
1190         if (_shift == 0.0f) {
1191                 _shift = 1.0f;
1192         }
1193
1194         const XMLNodeList& nlist = node.children();
1195
1196         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1197
1198                 XMLNode *child;
1199
1200                 child = (*niter);
1201
1202                 if (child->name () == "Extra") {
1203                         delete _extra_xml;
1204                         _extra_xml = new XMLNode (*child);
1205                         break;
1206                 }
1207         }
1208
1209         if (send) {
1210                 cerr << _name << ": final change to be sent: ";
1211                 for (PropertyChange::iterator i = what_changed.begin(); i != what_changed.end(); ++i) {
1212                         cerr << g_quark_to_string ((GQuark) *i) << ' ';
1213                 }
1214                 cerr << endl;
1215                 send_change (what_changed);
1216         }
1217         
1218         /* Quick fix for 2.x sessions when region is muted */
1219         if ((prop = node.property (X_("flags")))) {
1220                 if (string::npos != prop->value().find("Muted")){
1221                         set_muted (true);
1222                 }
1223         }
1224         
1225
1226         return 0;
1227 }
1228
1229 void
1230 Region::suspend_property_changes ()
1231 {
1232         Stateful::suspend_property_changes ();
1233         _last_length = _length;
1234         _last_position = _position;
1235 }
1236
1237 void
1238 Region::mid_thaw (const PropertyChange& what_changed)
1239 {
1240         if (what_changed.contains (Properties::length)) {
1241                 if (what_changed.contains (Properties::position)) {
1242                         recompute_at_start ();
1243                 }
1244                 recompute_at_end ();
1245         }
1246 }
1247
1248 void
1249 Region::send_change (const PropertyChange& what_changed)
1250 {
1251         if (what_changed.empty()) {
1252                 return;
1253         }
1254
1255         Stateful::send_change (what_changed);
1256
1257         if (!_no_property_changes) {
1258                 
1259                 /* Try and send a shared_pointer unless this is part of the constructor.
1260                    If so, do nothing.
1261                 */
1262
1263                 try {
1264                         boost::shared_ptr<Region> rptr = shared_from_this();
1265                         RegionPropertyChanged (rptr, what_changed);
1266                 } catch (...) {
1267                         /* no shared_ptr available, relax; */
1268                 }
1269         }
1270 }
1271
1272 void
1273 Region::set_last_layer_op (uint64_t when)
1274 {
1275         _last_layer_op = when;
1276 }
1277
1278 bool
1279 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1280 {
1281         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1282 }
1283
1284 bool
1285 Region::equivalent (boost::shared_ptr<const Region> other) const
1286 {
1287         return _start == other->_start &&
1288                 _position == other->_position &&
1289                 _length == other->_length;
1290 }
1291
1292 bool
1293 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1294 {
1295         return _start == other->_start &&
1296                 _length == other->_length;
1297 }
1298
1299 bool
1300 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1301 {
1302         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1303 }
1304
1305 void
1306 Region::source_deleted (boost::weak_ptr<Source>)
1307 {
1308         _sources.clear ();
1309
1310         if (!_session.deletion_in_progress()) {
1311                 /* this is a very special case: at least one of the region's
1312                    sources has bee deleted, so invalidate all references to
1313                    ourselves. Do NOT do this during session deletion, because
1314                    then we run the risk that this will actually result
1315                    in this object being deleted (as refcnt goes to zero)
1316                    while emitting DropReferences.
1317                 */
1318
1319                 drop_references ();
1320         }
1321 }
1322
1323 vector<string>
1324 Region::master_source_names ()
1325 {
1326         SourceList::iterator i;
1327
1328         vector<string> names;
1329         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1330                 names.push_back((*i)->name());
1331         }
1332
1333         return names;
1334 }
1335
1336 void
1337 Region::set_master_sources (const SourceList& srcs)
1338 {
1339         _master_sources = srcs;
1340         assert (_sources.size() == _master_sources.size());
1341 }
1342
1343 bool
1344 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1345 {
1346         if (!other)
1347                 return false;
1348
1349         SourceList::const_iterator i;
1350         SourceList::const_iterator io;
1351
1352         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1353                 if ((*i)->id() != (*io)->id()) {
1354                         return false;
1355                 }
1356         }
1357
1358         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1359                 if ((*i)->id() != (*io)->id()) {
1360                         return false;
1361                 }
1362         }
1363
1364         return true;
1365 }
1366
1367 bool
1368 Region::uses_source (boost::shared_ptr<const Source> source) const
1369 {
1370         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1371                 if (*i == source) {
1372                         return true;
1373                 }
1374         }
1375         return false;
1376 }
1377
1378 sframes_t
1379 Region::source_length(uint32_t n) const
1380 {
1381         return _sources[n]->length(_position - _start);
1382 }
1383
1384 bool
1385 Region::verify_length (framecnt_t len)
1386 {
1387         if (source() && (source()->destructive() || source()->length_mutable())) {
1388                 return true;
1389         }
1390
1391         framecnt_t maxlen = 0;
1392
1393         for (uint32_t n=0; n < _sources.size(); ++n) {
1394                 maxlen = max (maxlen, source_length(n) - _start);
1395         }
1396
1397         len = min (len, maxlen);
1398
1399         return true;
1400 }
1401
1402 bool
1403 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1404 {
1405         if (source() && (source()->destructive() || source()->length_mutable())) {
1406                 return true;
1407         }
1408
1409         framecnt_t maxlen = 0;
1410
1411         for (uint32_t n=0; n < _sources.size(); ++n) {
1412                 maxlen = max (maxlen, source_length(n) - new_start);
1413         }
1414
1415         new_length = min (new_length, maxlen);
1416
1417         return true;
1418 }
1419
1420 bool
1421 Region::verify_start (framepos_t pos)
1422 {
1423         if (source() && (source()->destructive() || source()->length_mutable())) {
1424                 return true;
1425         }
1426
1427         for (uint32_t n=0; n < _sources.size(); ++n) {
1428                 if (pos > source_length(n) - _length) {
1429                         return false;
1430                 }
1431         }
1432         return true;
1433 }
1434
1435 bool
1436 Region::verify_start_mutable (framepos_t& new_start)
1437 {
1438         if (source() && (source()->destructive() || source()->length_mutable())) {
1439                 return true;
1440         }
1441
1442         for (uint32_t n=0; n < _sources.size(); ++n) {
1443                 if (new_start > source_length(n) - _length) {
1444                         new_start = source_length(n) - _length;
1445                 }
1446         }
1447         return true;
1448 }
1449
1450 boost::shared_ptr<Region>
1451 Region::get_parent() const
1452 {
1453         boost::shared_ptr<Playlist> pl (playlist());
1454
1455         if (pl) {
1456                 boost::shared_ptr<Region> r;
1457                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1458
1459                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1460                         return boost::static_pointer_cast<Region> (r);
1461                 }
1462         }
1463
1464         return boost::shared_ptr<Region>();
1465 }
1466
1467 int
1468 Region::apply (Filter& filter)
1469 {
1470         return filter.run (shared_from_this());
1471 }
1472
1473
1474 void
1475 Region::invalidate_transients ()
1476 {
1477         _valid_transients = false;
1478         _transients.clear ();
1479 }
1480
1481
1482 void
1483 Region::use_sources (SourceList const & s)
1484 {
1485         set<boost::shared_ptr<Source> > unique_srcs;
1486
1487         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1488                 _sources.push_back (*i);
1489                 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1490                 unique_srcs.insert (*i);
1491         }
1492
1493         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1494                 _master_sources.push_back (*i);
1495                 if (unique_srcs.find (*i) == unique_srcs.end()) {
1496                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1497                 }
1498         }
1499 }
1500
1501 PropertyList*
1502 Region::property_factory (const XMLNode& history_node) const
1503 {
1504         PropertyList* prop_list = new PropertyList;
1505
1506         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
1507                 PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node);
1508
1509                 if (prop) {
1510                         prop_list->add (prop);
1511                 }
1512         }
1513
1514         return prop_list;
1515 }