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