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