Remove unused full parameter to Region::state
[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 (other->_muted) \
186         , _opaque (other->_opaque) \
187         , _locked (other->_locked) \
188         , _automatic (other->_automatic) \
189         , _whole_file (other->_whole_file) \
190         , _import (other->_import) \
191         , _external (other->_external) \
192         , _sync_marked (other->_sync_marked) \
193         , _left_of_split (other->_left_of_split) \
194         , _right_of_split (other->_right_of_split) \
195         , _hidden (other->_hidden) \
196         , _position_locked (other->_position_locked) \
197         , _valid_transients (other->_valid_transients) \
198         , _start(other->_start) \
199         , _length(other->_length) \
200         , _position(other->_position) \
201         , _sync_position(other->_sync_position) \
202         , _layer (other->_layer) \
203         , _ancestral_start (other->_ancestral_start) \
204         , _ancestral_length (other->_ancestral_length) \
205         , _stretch (other->_stretch) \
206         , _shift (other->_shift) \
207         , _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 @param offset_relative is true, then the start within @param other is given by @param offset
251     (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
252
253     if @param offset_relative is false, then the start within the source is given @param 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_frames - 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_frames - _length < _position) {
606                         _last_length = _length;
607                         _length = max_frames - _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_frames - n) {
671                         _position = max_frames;
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_frames - start_shift) {
739                         new_start = max_frames;
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_frames - start_shift) {
899                         new_start = max_frames;
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 void
1017 Region::set_sync_position (framepos_t absolute_pos)
1018 {
1019         framepos_t const file_pos = _start + (absolute_pos - _position);
1020
1021         if (file_pos != _sync_position) {
1022                 _sync_marked = true;
1023                 _sync_position = file_pos;
1024                 if (!property_changes_suspended()) {
1025                         maybe_uncopy ();
1026                 }
1027                 send_change (Properties::sync_position);
1028         }
1029 }
1030
1031 void
1032 Region::clear_sync_position ()
1033 {
1034         if (sync_marked()) {
1035                 _sync_marked = false;
1036                 if (!property_changes_suspended()) {
1037                         maybe_uncopy ();
1038                 }
1039                 send_change (Properties::sync_position);
1040         }
1041 }
1042
1043 framepos_t
1044 Region::sync_offset (int& dir) const
1045 {
1046         /* returns the sync point relative the first frame of the region */
1047
1048         if (sync_marked()) {
1049                 if (_sync_position > _start) {
1050                         dir = 1;
1051                         return _sync_position - _start;
1052                 } else {
1053                         dir = -1;
1054                         return _start - _sync_position;
1055                 }
1056         } else {
1057                 dir = 0;
1058                 return 0;
1059         }
1060 }
1061
1062 framepos_t
1063 Region::adjust_to_sync (framepos_t pos) const
1064 {
1065         int sync_dir;
1066         frameoffset_t offset = sync_offset (sync_dir);
1067
1068         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1069
1070         if (sync_dir > 0) {
1071                 if (pos > offset) {
1072                         pos -= offset;
1073                 } else {
1074                         pos = 0;
1075                 }
1076         } else {
1077                 if (max_frames - pos > offset) {
1078                         pos += offset;
1079                 }
1080         }
1081
1082         return pos;
1083 }
1084
1085 framepos_t
1086 Region::sync_position() const
1087 {
1088         if (sync_marked()) {
1089                 return _sync_position;
1090         } else {
1091                 return _start;
1092         }
1093 }
1094
1095 void
1096 Region::raise ()
1097 {
1098         boost::shared_ptr<Playlist> pl (playlist());
1099         if (pl) {
1100                 pl->raise_region (shared_from_this ());
1101         }
1102 }
1103
1104 void
1105 Region::lower ()
1106 {
1107         boost::shared_ptr<Playlist> pl (playlist());
1108         if (pl) {
1109                 pl->lower_region (shared_from_this ());
1110         }
1111 }
1112
1113
1114 void
1115 Region::raise_to_top ()
1116 {
1117         boost::shared_ptr<Playlist> pl (playlist());
1118         if (pl) {
1119                 pl->raise_region_to_top (shared_from_this());
1120         }
1121 }
1122
1123 void
1124 Region::lower_to_bottom ()
1125 {
1126         boost::shared_ptr<Playlist> pl (playlist());
1127         if (pl) {
1128                 pl->lower_region_to_bottom (shared_from_this());
1129         }
1130 }
1131
1132 void
1133 Region::set_layer (layer_t l)
1134 {
1135         if (_layer != l) {
1136                 _layer = l;
1137
1138                 send_change (Properties::layer);
1139         }
1140 }
1141
1142 XMLNode&
1143 Region::state ()
1144 {
1145         XMLNode *node = new XMLNode ("Region");
1146         char buf[64];
1147         char buf2[64];
1148         LocaleGuard lg (X_("POSIX"));
1149         const char* fe = NULL;
1150
1151         add_properties (*node);
1152
1153         _id.print (buf, sizeof (buf));
1154         node->add_property ("id", buf);
1155         node->add_property ("type", _type.to_string());
1156
1157         switch (_first_edit) {
1158         case EditChangesNothing:
1159                 fe = X_("nothing");
1160                 break;
1161         case EditChangesName:
1162                 fe = X_("name");
1163                 break;
1164         case EditChangesID:
1165                 fe = X_("id");
1166                 break;
1167         default: /* should be unreachable but makes g++ happy */
1168                 fe = X_("nothing");
1169                 break;
1170         }
1171
1172         node->add_property ("first-edit", fe);
1173
1174         /* note: flags are stored by derived classes */
1175
1176         if (_position_lock_style != AudioTime) {
1177                 stringstream str;
1178                 str << _bbt_time;
1179                 node->add_property ("bbt-position", str.str());
1180         }
1181
1182         for (uint32_t n=0; n < _sources.size(); ++n) {
1183                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1184                 _sources[n]->id().print (buf, sizeof(buf));
1185                 node->add_property (buf2, buf);
1186         }
1187
1188         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1189                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1190                 _master_sources[n]->id().print (buf, sizeof (buf));
1191                 node->add_property (buf2, buf);
1192         }
1193
1194         if (_extra_xml) {
1195                 node->add_child_copy (*_extra_xml);
1196         }
1197
1198         return *node;
1199 }
1200
1201 XMLNode&
1202 Region::get_state ()
1203 {
1204         return state ();
1205 }
1206
1207 int
1208 Region::set_state (const XMLNode& node, int version)
1209 {
1210         PropertyChange what_changed;
1211         return _set_state (node, version, what_changed, true);
1212 }
1213
1214 int
1215 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1216 {
1217         const XMLProperty* prop;
1218
1219         what_changed = set_properties (node);
1220
1221         if ((prop = node.property (X_("id")))) {
1222                 _id = prop->value();
1223         }
1224
1225         if (_position_lock_style == MusicTime) {
1226                 if ((prop = node.property ("bbt-position")) == 0) {
1227                         /* missing BBT info, revert to audio time locking */
1228                         _position_lock_style = AudioTime;
1229                 } else {
1230                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1231                                     &_bbt_time.bars,
1232                                     &_bbt_time.beats,
1233                                     &_bbt_time.ticks) != 3) {
1234                                 _position_lock_style = AudioTime;
1235                         }
1236                 }
1237         }
1238
1239         /* fix problems with old sessions corrupted by impossible
1240            values for _stretch or _shift
1241         */
1242         if (_stretch == 0.0f) {
1243                 _stretch = 1.0f;
1244         }
1245         
1246         if (_shift == 0.0f) {
1247                 _shift = 1.0f;
1248         }
1249
1250         const XMLNodeList& nlist = node.children();
1251
1252         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1253
1254                 XMLNode *child;
1255
1256                 child = (*niter);
1257
1258                 if (child->name () == "Extra") {
1259                         delete _extra_xml;
1260                         _extra_xml = new XMLNode (*child);
1261                         break;
1262                 }
1263         }
1264
1265         if (send) {
1266                 send_change (what_changed);
1267         }
1268         
1269         /* Quick fix for 2.x sessions when region is muted */
1270         if ((prop = node.property (X_("flags")))) {
1271                 if (string::npos != prop->value().find("Muted")){
1272                         set_muted (true);
1273                 }
1274         }
1275         
1276
1277         return 0;
1278 }
1279
1280 void
1281 Region::suspend_property_changes ()
1282 {
1283         Stateful::suspend_property_changes ();
1284         _last_length = _length;
1285         _last_position = _position;
1286 }
1287
1288 void
1289 Region::mid_thaw (const PropertyChange& what_changed)
1290 {
1291         if (what_changed.contains (Properties::length)) {
1292                 if (what_changed.contains (Properties::position)) {
1293                         recompute_at_start ();
1294                 }
1295                 recompute_at_end ();
1296         }
1297 }
1298
1299 void
1300 Region::send_change (const PropertyChange& what_changed)
1301 {
1302         if (what_changed.empty()) {
1303                 return;
1304         }
1305
1306         Stateful::send_change (what_changed);
1307
1308         if (!_no_property_changes) {
1309                 
1310                 /* Try and send a shared_pointer unless this is part of the constructor.
1311                    If so, do nothing.
1312                 */
1313
1314                 try {
1315                         boost::shared_ptr<Region> rptr = shared_from_this();
1316                         RegionPropertyChanged (rptr, what_changed);
1317                 } catch (...) {
1318                         /* no shared_ptr available, relax; */
1319                 }
1320         }
1321 }
1322
1323 void
1324 Region::set_last_layer_op (uint64_t when)
1325 {
1326         _last_layer_op = when;
1327 }
1328
1329 bool
1330 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1331 {
1332         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1333 }
1334
1335 bool
1336 Region::equivalent (boost::shared_ptr<const Region> other) const
1337 {
1338         return _start == other->_start &&
1339                 _position == other->_position &&
1340                 _length == other->_length;
1341 }
1342
1343 bool
1344 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1345 {
1346         return _start == other->_start &&
1347                 _length == other->_length;
1348 }
1349
1350 bool
1351 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1352 {
1353         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1354 }
1355
1356 void
1357 Region::source_deleted (boost::weak_ptr<Source>)
1358 {
1359         drop_sources ();
1360
1361         if (!_session.deletion_in_progress()) {
1362                 /* this is a very special case: at least one of the region's
1363                    sources has bee deleted, so invalidate all references to
1364                    ourselves. Do NOT do this during session deletion, because
1365                    then we run the risk that this will actually result
1366                    in this object being deleted (as refcnt goes to zero)
1367                    while emitting DropReferences.
1368                 */
1369
1370                 drop_references ();
1371         }
1372 }
1373
1374 vector<string>
1375 Region::master_source_names ()
1376 {
1377         SourceList::iterator i;
1378
1379         vector<string> names;
1380         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1381                 names.push_back((*i)->name());
1382         }
1383
1384         return names;
1385 }
1386
1387 void
1388 Region::set_master_sources (const SourceList& srcs)
1389 {
1390         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1391                 (*i)->dec_use_count ();
1392         }
1393
1394         _master_sources = srcs;
1395         assert (_sources.size() == _master_sources.size());
1396
1397         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1398                 (*i)->inc_use_count ();
1399         }
1400 }
1401
1402 bool
1403 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1404 {
1405         if (!other)
1406                 return false;
1407
1408         SourceList::const_iterator i;
1409         SourceList::const_iterator io;
1410
1411         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1412                 if ((*i)->id() != (*io)->id()) {
1413                         return false;
1414                 }
1415         }
1416
1417         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1418                 if ((*i)->id() != (*io)->id()) {
1419                         return false;
1420                 }
1421         }
1422
1423         return true;
1424 }
1425
1426 bool
1427 Region::uses_source (boost::shared_ptr<const Source> source) const
1428 {
1429         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1430                 if (*i == source) {
1431                         return true;
1432                 }
1433         }
1434         return false;
1435 }
1436
1437 sframes_t
1438 Region::source_length(uint32_t n) const
1439 {
1440         assert (n < _sources.size());
1441         return _sources[n]->length(_position - _start);
1442 }
1443
1444 bool
1445 Region::verify_length (framecnt_t len)
1446 {
1447         if (source() && (source()->destructive() || source()->length_mutable())) {
1448                 return true;
1449         }
1450
1451         framecnt_t maxlen = 0;
1452
1453         for (uint32_t n = 0; n < _sources.size(); ++n) {
1454                 maxlen = max (maxlen, source_length(n) - _start);
1455         }
1456
1457         len = min (len, maxlen);
1458
1459         return true;
1460 }
1461
1462 bool
1463 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1464 {
1465         if (source() && (source()->destructive() || source()->length_mutable())) {
1466                 return true;
1467         }
1468
1469         framecnt_t maxlen = 0;
1470
1471         for (uint32_t n = 0; n < _sources.size(); ++n) {
1472                 maxlen = max (maxlen, source_length(n) - new_start);
1473         }
1474
1475         new_length = min (new_length, maxlen);
1476
1477         return true;
1478 }
1479
1480 bool
1481 Region::verify_start (framepos_t pos)
1482 {
1483         if (source() && (source()->destructive() || source()->length_mutable())) {
1484                 return true;
1485         }
1486
1487         for (uint32_t n = 0; n < _sources.size(); ++n) {
1488                 if (pos > source_length(n) - _length) {
1489                         return false;
1490                 }
1491         }
1492         return true;
1493 }
1494
1495 bool
1496 Region::verify_start_mutable (framepos_t& new_start)
1497 {
1498         if (source() && (source()->destructive() || source()->length_mutable())) {
1499                 return true;
1500         }
1501
1502         for (uint32_t n = 0; n < _sources.size(); ++n) {
1503                 if (new_start > source_length(n) - _length) {
1504                         new_start = source_length(n) - _length;
1505                 }
1506         }
1507         return true;
1508 }
1509
1510 boost::shared_ptr<Region>
1511 Region::get_parent() const
1512 {
1513         boost::shared_ptr<Playlist> pl (playlist());
1514
1515         if (pl) {
1516                 boost::shared_ptr<Region> r;
1517                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1518
1519                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1520                         return boost::static_pointer_cast<Region> (r);
1521                 }
1522         }
1523
1524         return boost::shared_ptr<Region>();
1525 }
1526
1527 int
1528 Region::apply (Filter& filter)
1529 {
1530         return filter.run (shared_from_this());
1531 }
1532
1533
1534 void
1535 Region::invalidate_transients ()
1536 {
1537         _valid_transients = false;
1538         _transients.clear ();
1539         
1540         send_change (PropertyChange (Properties::valid_transients));
1541 }
1542
1543 void
1544 Region::drop_sources ()
1545 {
1546         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1547                 (*i)->dec_use_count ();
1548         }
1549
1550         _sources.clear ();
1551
1552         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1553                 (*i)->dec_use_count ();
1554         }
1555
1556         _master_sources.clear ();
1557 }
1558
1559 void
1560 Region::use_sources (SourceList const & s)
1561 {
1562         set<boost::shared_ptr<Source> > unique_srcs;
1563
1564         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1565
1566                 _sources.push_back (*i);
1567                 (*i)->inc_use_count ();
1568                 _master_sources.push_back (*i);
1569                 (*i)->inc_use_count ();
1570
1571                 /* connect only once to DropReferences, even if sources are replicated
1572                  */
1573
1574                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1575                         unique_srcs.insert (*i);
1576                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1577                 }
1578         }
1579 }
1580
1581 PropertyList*
1582 Region::property_factory (const XMLNode& history_node) const
1583 {
1584         PropertyList* prop_list = new PropertyList;
1585
1586         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
1587                 PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node);
1588
1589                 if (prop) {
1590                         prop_list->add (prop);
1591                 }
1592         }
1593
1594         return prop_list;
1595 }