megaopus commit: (1) add __STD_(LIMIT|FORMAT)_MACROS to command line flags for cc...
[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
582 void
583 Region::set_position (framepos_t pos, void* /*src*/)
584 {
585         if (!can_move()) {
586                 return;
587         }
588
589         set_position_internal (pos, true);
590 }
591
592 void
593 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
594 {
595         if (_position != pos) {
596                 _last_position = _position;
597                 _position = pos;
598
599                 /* check that the new _position wouldn't make the current
600                    length impossible - if so, change the length.
601
602                    XXX is this the right thing to do?
603                 */
604
605                 if (max_framepos - _length < _position) {
606                         _last_length = _length;
607                         _length = max_framepos - _position;
608                 }
609
610                 if (allow_bbt_recompute) {
611                         recompute_position_from_lock_style ();
612                 }
613
614                 //invalidate_transients ();
615         }
616
617         /* do this even if the position is the same. this helps out
618            a GUI that has moved its representation already.
619         */
620         send_change (Properties::position);
621 }
622
623 void
624 Region::set_position_on_top (framepos_t pos, void* /*src*/)
625 {
626         if (locked()) {
627                 return;
628         }
629
630         if (_position != pos) {
631                 _last_position = _position;
632                 _position = pos;
633         }
634
635         boost::shared_ptr<Playlist> pl (playlist());
636
637         if (pl) {
638                 pl->raise_region_to_top (shared_from_this ());
639         }
640
641         /* do this even if the position is the same. this helps out
642            a GUI that has moved its representation already.
643         */
644
645         send_change (Properties::position);
646 }
647
648 void
649 Region::recompute_position_from_lock_style ()
650 {
651         if (_position_lock_style == MusicTime) {
652                 _session.tempo_map().bbt_time (_position, _bbt_time);
653         }
654 }
655
656 void
657 Region::nudge_position (frameoffset_t n, void* /*src*/)
658 {
659         if (locked()) {
660                 return;
661         }
662
663         if (n == 0) {
664                 return;
665         }
666
667         _last_position = _position;
668
669         if (n > 0) {
670                 if (_position > max_framepos - n) {
671                         _position = max_framepos;
672                 } else {
673                         _position += n;
674                 }
675         } else {
676                 if (_position < -n) {
677                         _position = 0;
678                 } else {
679                         _position += n;
680                 }
681         }
682
683         send_change (Properties::position);
684 }
685
686 void
687 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
688 {
689         _ancestral_length = l;
690         _ancestral_start = s;
691         _stretch = st;
692         _shift = sh;
693 }
694
695 void
696 Region::set_start (framepos_t pos, void* /*src*/)
697 {
698         if (locked() || position_locked()) {
699                 return;
700         }
701         /* This just sets the start, nothing else. It effectively shifts
702            the contents of the Region within the overall extent of the Source,
703            without changing the Region's position or length
704         */
705
706         if (_start != pos) {
707
708                 if (!verify_start (pos)) {
709                         return;
710                 }
711
712                 _start = pos;
713                 _whole_file = false;
714                 first_edit ();
715                 invalidate_transients ();
716
717                 send_change (Properties::start);
718         }
719 }
720
721 void
722 Region::trim_start (framepos_t new_position, void */*src*/)
723 {
724         if (locked() || position_locked()) {
725                 return;
726         }
727         framepos_t new_start;
728         frameoffset_t start_shift;
729
730         if (new_position > _position) {
731                 start_shift = new_position - _position;
732         } else {
733                 start_shift = -(_position - new_position);
734         }
735
736         if (start_shift > 0) {
737
738                 if (_start > max_framepos - start_shift) {
739                         new_start = max_framepos;
740                 } else {
741                         new_start = _start + start_shift;
742                 }
743
744                 if (!verify_start (new_start)) {
745                         return;
746                 }
747
748         } else if (start_shift < 0) {
749
750                 if (_start < -start_shift) {
751                         new_start = 0;
752                 } else {
753                         new_start = _start + start_shift;
754                 }
755         } else {
756                 return;
757         }
758
759         if (new_start == _start) {
760                 return;
761         }
762
763         _start = new_start;
764         _whole_file = false;
765         first_edit ();
766
767         send_change (Properties::start);
768 }
769
770 void
771 Region::trim_front (framepos_t new_position, void *src)
772 {
773         modify_front (new_position, false, src);
774 }
775
776 void
777 Region::cut_front (nframes_t new_position, void *src)
778 {
779         modify_front (new_position, true, src);
780 }
781
782 void
783 Region::cut_end (nframes_t new_endpoint, void *src)
784 {
785         modify_end (new_endpoint, true, src);
786 }
787
788 void
789 Region::modify_front (nframes_t new_position, bool reset_fade, void *src)
790 {
791         if (locked()) {
792                 return;
793         }
794
795         nframes_t end = last_frame();
796         nframes_t source_zero;
797
798         if (_position > _start) {
799                 source_zero = _position - _start;
800         } else {
801                 source_zero = 0; // its actually negative, but this will work for us
802         }
803
804         if (new_position < end) { /* can't trim it zero or negative length */
805                 
806                 nframes_t newlen = 0;
807                 nframes64_t delta = 0;
808
809                 /* can't trim it back passed where source position zero is located */
810                 
811                 new_position = max (new_position, source_zero);
812                 
813                 if (new_position > _position) {
814                         newlen = _length - (new_position - _position);
815                         delta = -1 * (new_position - _position);
816                 } else {
817                         newlen = _length + (_position - new_position);
818                         delta = _position - new_position;
819                 }
820                 
821                 trim_to_internal (new_position, newlen, src);
822                 
823                 if (reset_fade) {
824                         _right_of_split = true;
825                 }
826         
827                 if (!property_changes_suspended()) {
828                         recompute_at_start ();
829                 }
830                 
831                 if (_transients.size() > 0){
832                         adjust_transients(delta);
833                 }
834         }
835 }
836
837 void
838 Region::modify_end (nframes_t new_endpoint, bool reset_fade, void* /*src*/)
839 {
840         if (locked()) {
841                 return;
842         }
843
844         if (new_endpoint > _position) {
845                 trim_to_internal (_position, new_endpoint - _position +1, this);
846                 if (reset_fade) {
847                         _left_of_split = true;
848                 }
849                 if (!property_changes_suspended()) {
850                         recompute_at_end ();
851                 }
852         }
853 }
854
855 /** @param new_endpoint New region end point, such that, for example,
856  *  a region at 0 of length 10 has an endpoint of 9.
857  */
858
859 void
860 Region::trim_end (framepos_t new_endpoint, void* src)
861 {
862         modify_end (new_endpoint, false, src);
863 }
864
865 void
866 Region::trim_to (framepos_t position, framecnt_t length, void *src)
867 {
868         if (locked()) {
869                 return;
870         }
871
872         trim_to_internal (position, length, src);
873
874         if (!property_changes_suspended()) {
875                 recompute_at_start ();
876                 recompute_at_end ();
877         }
878 }
879
880 void
881 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
882 {
883         frameoffset_t start_shift;
884         framepos_t new_start;
885
886         if (locked()) {
887                 return;
888         }
889
890         if (position > _position) {
891                 start_shift = position - _position;
892         } else {
893                 start_shift = -(_position - position);
894         }
895
896         if (start_shift > 0) {
897
898                 if (_start > max_framepos - start_shift) {
899                         new_start = max_framepos;
900                 } else {
901                         new_start = _start + start_shift;
902                 }
903
904         } else if (start_shift < 0) {
905
906                 if (_start < -start_shift) {
907                         new_start = 0;
908                 } else {
909                         new_start = _start + start_shift;
910                 }
911
912         } else {
913                 new_start = _start;
914         }
915
916         if (!verify_start_and_length (new_start, length)) {
917                 return;
918         }
919
920         PropertyChange what_changed;
921
922         if (_start != new_start) {
923                 _start = new_start;
924                 what_changed.add (Properties::start);
925         }
926         if (_length != length) {
927                 if (!property_changes_suspended()) {
928                         _last_length = _length;
929                 }
930                 _length = length;
931                 what_changed.add (Properties::length);
932         }
933         if (_position != position) {
934                 if (!property_changes_suspended()) {
935                         _last_position = _position;
936                 }
937                 _position = position;
938                 what_changed.add (Properties::position);
939         }
940
941         _whole_file = false;
942
943         PropertyChange start_and_length;
944
945         start_and_length.add (Properties::start);
946         start_and_length.add (Properties::length);
947
948         if (what_changed.contains (start_and_length)) {
949                 first_edit ();
950         }
951
952         if (!what_changed.empty()) {
953                 send_change (what_changed);
954         }
955 }
956
957 void
958 Region::set_hidden (bool yn)
959 {
960         if (hidden() != yn) {
961                 _hidden = yn;
962                 send_change (Properties::hidden);
963         }
964 }
965
966 void
967 Region::set_whole_file (bool yn)
968 {
969         _whole_file = yn;
970         /* no change signal */
971 }
972
973 void
974 Region::set_automatic (bool yn)
975 {
976         _automatic = yn;
977         /* no change signal */
978 }
979
980 void
981 Region::set_muted (bool yn)
982 {
983         if (muted() != yn) {
984                 _muted = yn;
985                 send_change (Properties::muted);
986         }
987 }
988
989 void
990 Region::set_opaque (bool yn)
991 {
992         if (opaque() != yn) {
993                 _opaque = yn;
994                 send_change (Properties::opaque);
995         }
996 }
997
998 void
999 Region::set_locked (bool yn)
1000 {
1001         if (locked() != yn) {
1002                 _locked = yn;
1003                 send_change (Properties::locked);
1004         }
1005 }
1006
1007 void
1008 Region::set_position_locked (bool yn)
1009 {
1010         if (position_locked() != yn) {
1011                 _position_locked = yn;
1012                 send_change (Properties::locked);
1013         }
1014 }
1015
1016 /** Set the region's sync point.
1017  *  @param absolute_pos Session time.
1018  */
1019 void
1020 Region::set_sync_position (framepos_t absolute_pos)
1021 {
1022         /* position within our file */
1023         framepos_t const file_pos = _start + (absolute_pos - _position);
1024
1025         if (file_pos != _sync_position) {
1026                 _sync_marked = true;
1027                 _sync_position = file_pos;
1028                 if (!property_changes_suspended()) {
1029                         maybe_uncopy ();
1030                 }
1031                 send_change (Properties::sync_position);
1032         }
1033 }
1034
1035 void
1036 Region::clear_sync_position ()
1037 {
1038         if (sync_marked()) {
1039                 _sync_marked = false;
1040                 if (!property_changes_suspended()) {
1041                         maybe_uncopy ();
1042                 }
1043                 send_change (Properties::sync_position);
1044         }
1045 }
1046
1047 /* @return the sync point relative the first frame of the region */
1048 framepos_t
1049 Region::sync_offset (int& dir) const
1050 {
1051         if (sync_marked()) {
1052                 if (_sync_position > _start) {
1053                         dir = 1;
1054                         return _sync_position - _start;
1055                 } else {
1056                         dir = -1;
1057                         return _start - _sync_position;
1058                 }
1059         } else {
1060                 dir = 0;
1061                 return 0;
1062         }
1063 }
1064
1065 framepos_t
1066 Region::adjust_to_sync (framepos_t pos) const
1067 {
1068         int sync_dir;
1069         frameoffset_t offset = sync_offset (sync_dir);
1070
1071         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1072
1073         if (sync_dir > 0) {
1074                 if (pos > offset) {
1075                         pos -= offset;
1076                 } else {
1077                         pos = 0;
1078                 }
1079         } else {
1080                 if (max_framepos - pos > offset) {
1081                         pos += offset;
1082                 }
1083         }
1084
1085         return pos;
1086 }
1087
1088 /** @return Sync position in session time */
1089 framepos_t
1090 Region::sync_position() const
1091 {
1092         if (sync_marked()) {
1093                 return _position - _start + _sync_position;
1094         } else {
1095                 /* if sync has not been marked, use the start of the region */
1096                 return _position;
1097         }
1098 }
1099
1100 void
1101 Region::raise ()
1102 {
1103         boost::shared_ptr<Playlist> pl (playlist());
1104         if (pl) {
1105                 pl->raise_region (shared_from_this ());
1106         }
1107 }
1108
1109 void
1110 Region::lower ()
1111 {
1112         boost::shared_ptr<Playlist> pl (playlist());
1113         if (pl) {
1114                 pl->lower_region (shared_from_this ());
1115         }
1116 }
1117
1118
1119 void
1120 Region::raise_to_top ()
1121 {
1122         boost::shared_ptr<Playlist> pl (playlist());
1123         if (pl) {
1124                 pl->raise_region_to_top (shared_from_this());
1125         }
1126 }
1127
1128 void
1129 Region::lower_to_bottom ()
1130 {
1131         boost::shared_ptr<Playlist> pl (playlist());
1132         if (pl) {
1133                 pl->lower_region_to_bottom (shared_from_this());
1134         }
1135 }
1136
1137 void
1138 Region::set_layer (layer_t l)
1139 {
1140         if (_layer != l) {
1141                 _layer = l;
1142
1143                 send_change (Properties::layer);
1144         }
1145 }
1146
1147 XMLNode&
1148 Region::state ()
1149 {
1150         XMLNode *node = new XMLNode ("Region");
1151         char buf[64];
1152         char buf2[64];
1153         LocaleGuard lg (X_("POSIX"));
1154         const char* fe = NULL;
1155
1156         add_properties (*node);
1157
1158         _id.print (buf, sizeof (buf));
1159         node->add_property ("id", buf);
1160         node->add_property ("type", _type.to_string());
1161
1162         switch (_first_edit) {
1163         case EditChangesNothing:
1164                 fe = X_("nothing");
1165                 break;
1166         case EditChangesName:
1167                 fe = X_("name");
1168                 break;
1169         case EditChangesID:
1170                 fe = X_("id");
1171                 break;
1172         default: /* should be unreachable but makes g++ happy */
1173                 fe = X_("nothing");
1174                 break;
1175         }
1176
1177         node->add_property ("first-edit", fe);
1178
1179         /* note: flags are stored by derived classes */
1180
1181         if (_position_lock_style != AudioTime) {
1182                 stringstream str;
1183                 str << _bbt_time;
1184                 node->add_property ("bbt-position", str.str());
1185         }
1186
1187         for (uint32_t n=0; n < _sources.size(); ++n) {
1188                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1189                 _sources[n]->id().print (buf, sizeof(buf));
1190                 node->add_property (buf2, buf);
1191         }
1192
1193         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1194                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1195                 _master_sources[n]->id().print (buf, sizeof (buf));
1196                 node->add_property (buf2, buf);
1197         }
1198
1199         if (_extra_xml) {
1200                 node->add_child_copy (*_extra_xml);
1201         }
1202
1203         return *node;
1204 }
1205
1206 XMLNode&
1207 Region::get_state ()
1208 {
1209         return state ();
1210 }
1211
1212 int
1213 Region::set_state (const XMLNode& node, int version)
1214 {
1215         PropertyChange what_changed;
1216         return _set_state (node, version, what_changed, true);
1217 }
1218
1219 int
1220 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1221 {
1222         const XMLProperty* prop;
1223
1224         what_changed = set_values (node);
1225
1226         if ((prop = node.property (X_("id")))) {
1227                 _id = prop->value();
1228         }
1229
1230         if (_position_lock_style == MusicTime) {
1231                 if ((prop = node.property ("bbt-position")) == 0) {
1232                         /* missing BBT info, revert to audio time locking */
1233                         _position_lock_style = AudioTime;
1234                 } else {
1235                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1236                                     &_bbt_time.bars,
1237                                     &_bbt_time.beats,
1238                                     &_bbt_time.ticks) != 3) {
1239                                 _position_lock_style = AudioTime;
1240                         }
1241                 }
1242         }
1243
1244         /* fix problems with old sessions corrupted by impossible
1245            values for _stretch or _shift
1246         */
1247         if (_stretch == 0.0f) {
1248                 _stretch = 1.0f;
1249         }
1250         
1251         if (_shift == 0.0f) {
1252                 _shift = 1.0f;
1253         }
1254
1255         const XMLNodeList& nlist = node.children();
1256
1257         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1258
1259                 XMLNode *child;
1260
1261                 child = (*niter);
1262
1263                 if (child->name () == "Extra") {
1264                         delete _extra_xml;
1265                         _extra_xml = new XMLNode (*child);
1266                         break;
1267                 }
1268         }
1269
1270         if (send) {
1271                 send_change (what_changed);
1272         }
1273         
1274         /* Quick fix for 2.x sessions when region is muted */
1275         if ((prop = node.property (X_("flags")))) {
1276                 if (string::npos != prop->value().find("Muted")){
1277                         set_muted (true);
1278                 }
1279         }
1280         
1281
1282         return 0;
1283 }
1284
1285 void
1286 Region::suspend_property_changes ()
1287 {
1288         Stateful::suspend_property_changes ();
1289         _last_length = _length;
1290         _last_position = _position;
1291 }
1292
1293 void
1294 Region::mid_thaw (const PropertyChange& what_changed)
1295 {
1296         if (what_changed.contains (Properties::length)) {
1297                 if (what_changed.contains (Properties::position)) {
1298                         recompute_at_start ();
1299                 }
1300                 recompute_at_end ();
1301         }
1302 }
1303
1304 void
1305 Region::send_change (const PropertyChange& what_changed)
1306 {
1307         if (what_changed.empty()) {
1308                 return;
1309         }
1310
1311         Stateful::send_change (what_changed);
1312
1313         if (!_no_property_changes) {
1314                 
1315                 /* Try and send a shared_pointer unless this is part of the constructor.
1316                    If so, do nothing.
1317                 */
1318
1319                 try {
1320                         boost::shared_ptr<Region> rptr = shared_from_this();
1321                         RegionPropertyChanged (rptr, what_changed);
1322                 } catch (...) {
1323                         /* no shared_ptr available, relax; */
1324                 }
1325         }
1326 }
1327
1328 void
1329 Region::set_last_layer_op (uint64_t when)
1330 {
1331         _last_layer_op = when;
1332 }
1333
1334 bool
1335 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1336 {
1337         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1338 }
1339
1340 bool
1341 Region::equivalent (boost::shared_ptr<const Region> other) const
1342 {
1343         return _start == other->_start &&
1344                 _position == other->_position &&
1345                 _length == other->_length;
1346 }
1347
1348 bool
1349 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1350 {
1351         return _start == other->_start &&
1352                 _length == other->_length;
1353 }
1354
1355 bool
1356 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1357 {
1358         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1359 }
1360
1361 void
1362 Region::source_deleted (boost::weak_ptr<Source>)
1363 {
1364         drop_sources ();
1365
1366         if (!_session.deletion_in_progress()) {
1367                 /* this is a very special case: at least one of the region's
1368                    sources has bee deleted, so invalidate all references to
1369                    ourselves. Do NOT do this during session deletion, because
1370                    then we run the risk that this will actually result
1371                    in this object being deleted (as refcnt goes to zero)
1372                    while emitting DropReferences.
1373                 */
1374
1375                 drop_references ();
1376         }
1377 }
1378
1379 vector<string>
1380 Region::master_source_names ()
1381 {
1382         SourceList::iterator i;
1383
1384         vector<string> names;
1385         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1386                 names.push_back((*i)->name());
1387         }
1388
1389         return names;
1390 }
1391
1392 void
1393 Region::set_master_sources (const SourceList& srcs)
1394 {
1395         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1396                 (*i)->dec_use_count ();
1397         }
1398
1399         _master_sources = srcs;
1400         assert (_sources.size() == _master_sources.size());
1401
1402         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1403                 (*i)->inc_use_count ();
1404         }
1405 }
1406
1407 bool
1408 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1409 {
1410         if (!other)
1411                 return false;
1412         
1413         if ((_sources.size() != other->_sources.size()) ||
1414             (_master_sources.size() != other->_master_sources.size())) {
1415                 return false;
1416         }
1417
1418         SourceList::const_iterator i;
1419         SourceList::const_iterator io;
1420
1421         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1422                 if ((*i)->id() != (*io)->id()) {
1423                         return false;
1424                 }
1425         }
1426
1427         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1428                 if ((*i)->id() != (*io)->id()) {
1429                         return false;
1430                 }
1431         }
1432
1433         return true;
1434 }
1435
1436 bool
1437 Region::uses_source (boost::shared_ptr<const Source> source) const
1438 {
1439         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1440                 if (*i == source) {
1441                         return true;
1442                 }
1443         }
1444         return false;
1445 }
1446
1447 sframes_t
1448 Region::source_length(uint32_t n) const
1449 {
1450         assert (n < _sources.size());
1451         return _sources[n]->length(_position - _start);
1452 }
1453
1454 bool
1455 Region::verify_length (framecnt_t len)
1456 {
1457         if (source() && (source()->destructive() || source()->length_mutable())) {
1458                 return true;
1459         }
1460
1461         framecnt_t maxlen = 0;
1462
1463         for (uint32_t n = 0; n < _sources.size(); ++n) {
1464                 maxlen = max (maxlen, source_length(n) - _start);
1465         }
1466
1467         len = min (len, maxlen);
1468
1469         return true;
1470 }
1471
1472 bool
1473 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1474 {
1475         if (source() && (source()->destructive() || source()->length_mutable())) {
1476                 return true;
1477         }
1478
1479         framecnt_t maxlen = 0;
1480
1481         for (uint32_t n = 0; n < _sources.size(); ++n) {
1482                 maxlen = max (maxlen, source_length(n) - new_start);
1483         }
1484
1485         new_length = min (new_length, maxlen);
1486
1487         return true;
1488 }
1489
1490 bool
1491 Region::verify_start (framepos_t pos)
1492 {
1493         if (source() && (source()->destructive() || source()->length_mutable())) {
1494                 return true;
1495         }
1496
1497         for (uint32_t n = 0; n < _sources.size(); ++n) {
1498                 if (pos > source_length(n) - _length) {
1499                         return false;
1500                 }
1501         }
1502         return true;
1503 }
1504
1505 bool
1506 Region::verify_start_mutable (framepos_t& new_start)
1507 {
1508         if (source() && (source()->destructive() || source()->length_mutable())) {
1509                 return true;
1510         }
1511
1512         for (uint32_t n = 0; n < _sources.size(); ++n) {
1513                 if (new_start > source_length(n) - _length) {
1514                         new_start = source_length(n) - _length;
1515                 }
1516         }
1517         return true;
1518 }
1519
1520 boost::shared_ptr<Region>
1521 Region::get_parent() const
1522 {
1523         boost::shared_ptr<Playlist> pl (playlist());
1524
1525         if (pl) {
1526                 boost::shared_ptr<Region> r;
1527                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1528
1529                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1530                         return boost::static_pointer_cast<Region> (r);
1531                 }
1532         }
1533
1534         return boost::shared_ptr<Region>();
1535 }
1536
1537 int
1538 Region::apply (Filter& filter)
1539 {
1540         return filter.run (shared_from_this());
1541 }
1542
1543
1544 void
1545 Region::invalidate_transients ()
1546 {
1547         _valid_transients = false;
1548         _transients.clear ();
1549         
1550         send_change (PropertyChange (Properties::valid_transients));
1551 }
1552
1553 void
1554 Region::drop_sources ()
1555 {
1556         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1557                 (*i)->dec_use_count ();
1558         }
1559
1560         _sources.clear ();
1561
1562         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1563                 (*i)->dec_use_count ();
1564         }
1565
1566         _master_sources.clear ();
1567 }
1568
1569 void
1570 Region::use_sources (SourceList const & s)
1571 {
1572         set<boost::shared_ptr<Source> > unique_srcs;
1573
1574         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1575
1576                 _sources.push_back (*i);
1577                 (*i)->inc_use_count ();
1578                 _master_sources.push_back (*i);
1579                 (*i)->inc_use_count ();
1580
1581                 /* connect only once to DropReferences, even if sources are replicated
1582                  */
1583
1584                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1585                         unique_srcs.insert (*i);
1586                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1587                 }
1588         }
1589 }