2 Copyright (C) 2000-2003 Paul Davis
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.
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.
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.
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31 #include <pbd/stacktrace.h>
32 #include <pbd/enumwriter.h>
34 #include <ardour/region.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/tempo.h>
38 #include <ardour/region_factory.h>
43 using namespace ARDOUR;
46 Change Region::FadeChanged = ARDOUR::new_change ();
47 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
48 Change Region::MuteChanged = ARDOUR::new_change ();
49 Change Region::OpacityChanged = ARDOUR::new_change ();
50 Change Region::LockChanged = ARDOUR::new_change ();
51 Change Region::LayerChanged = ARDOUR::new_change ();
52 Change Region::HiddenChanged = ARDOUR::new_change ();
54 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
56 /* basic Region constructor */
61 pending_changed = Change (0);
62 valid_transients = false;
65 _sync_position = _start;
67 _last_length = length;
68 _ancestral_start = start;
69 _ancestral_length = length;
76 _first_edit = EditChangesNothing;
78 _positional_lock_style = AudioTime;
81 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
83 /* create a new Region from part of an existing one */
86 pending_changed = Change (0);
88 valid_transients = false;
90 _start = other->_start + offset;
91 if (other->_sync_position < offset) {
92 _sync_position = other->_sync_position;
94 _sync_position = _start;
97 _last_length = length;
98 _ancestral_start = other->_ancestral_start + offset;
99 _ancestral_length = length;
106 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
107 _first_edit = EditChangesNothing;
109 _positional_lock_style = AudioTime;
112 Region::Region (boost::shared_ptr<const Region> other)
114 /* Pure copy constructor */
117 pending_changed = Change (0);
118 _read_data_count = 0;
119 valid_transients = false;
121 _first_edit = EditChangesID;
122 other->_first_edit = EditChangesName;
124 if (other->_extra_xml) {
125 _extra_xml = new XMLNode (*other->_extra_xml);
130 _start = other->_start;
131 _sync_position = other->_sync_position;
132 _length = other->_length;
133 _last_length = other->_length;
134 _ancestral_start = _start;
135 _ancestral_length = _length;
138 _name = other->_name;
139 _last_position = other->_position;
140 _position = other->_position;
141 _layer = other->_layer;
142 _flags = Flag (other->_flags & ~Locked);
143 _last_layer_op = other->_last_layer_op;
144 _positional_lock_style = AudioTime;
147 Region::Region (const XMLNode& node)
150 pending_changed = Change (0);
151 valid_transients = false;
152 _read_data_count = 0;
154 _sync_position = _start;
157 _name = X_("error: XML did not reset this");
162 _first_edit = EditChangesNothing;
163 _positional_lock_style = AudioTime;
165 if (set_state (node)) {
166 throw failed_constructor();
172 /* derived classes must call notify_callbacks() and then emit GoingAway */
176 Region::set_playlist (boost::weak_ptr<Playlist> pl)
182 Region::set_name (string str)
186 send_change (NameChanged);
191 Region::set_length (nframes_t len, void *src)
193 if (_flags & Locked) {
197 if (_length != len && len != 0) {
199 /* check that the current _position wouldn't make the new
203 if (max_frames - len < _position) {
207 if (!verify_length (len)) {
212 _last_length = _length;
215 _flags = Region::Flag (_flags & ~WholeFile);
219 invalidate_transients ();
225 send_change (LengthChanged);
230 Region::maybe_uncopy ()
235 Region::first_edit ()
237 boost::shared_ptr<Playlist> pl (playlist());
239 if (_first_edit != EditChangesNothing && pl) {
241 _name = pl->session().new_region_name (_name);
242 _first_edit = EditChangesNothing;
244 send_change (NameChanged);
245 RegionFactory::CheckNewRegion (shared_from_this());
250 Region::at_natural_position () const
252 boost::shared_ptr<Playlist> pl (playlist());
258 boost::shared_ptr<Region> whole_file_region = get_parent();
260 if (whole_file_region) {
261 if (_position == whole_file_region->position() + _start) {
270 Region::move_to_natural_position (void *src)
272 boost::shared_ptr<Playlist> pl (playlist());
278 boost::shared_ptr<Region> whole_file_region = get_parent();
280 if (whole_file_region) {
281 set_position (whole_file_region->position() + _start, src);
286 Region::special_set_position (nframes_t pos)
288 /* this is used when creating a whole file region as
289 a way to store its "natural" or "captured" position.
292 _position = _position;
297 Region::set_position_lock_style (PositionLockStyle ps)
299 boost::shared_ptr<Playlist> pl (playlist());
305 _positional_lock_style = ps;
307 if (_positional_lock_style == MusicTime) {
308 pl->session().tempo_map().bbt_time (_position, _bbt_time);
314 Region::update_position_after_tempo_map_change ()
316 boost::shared_ptr<Playlist> pl (playlist());
318 if (!pl || _positional_lock_style != MusicTime) {
322 TempoMap& map (pl->session().tempo_map());
323 nframes_t pos = map.frame_time (_bbt_time);
324 set_position_internal (pos, false);
328 Region::set_position (nframes_t pos, void *src)
330 if (_flags & Locked) {
334 set_position_internal (pos, true);
338 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
340 if (_position != pos) {
341 _last_position = _position;
344 /* check that the new _position wouldn't make the current
345 length impossible - if so, change the length.
347 XXX is this the right thing to do?
350 if (max_frames - _length < _position) {
351 _last_length = _length;
352 _length = max_frames - _position;
355 if (allow_bbt_recompute) {
356 recompute_position_from_lock_style ();
359 invalidate_transients ();
362 /* do this even if the position is the same. this helps out
363 a GUI that has moved its representation already.
366 send_change (PositionChanged);
370 Region::set_position_on_top (nframes_t pos, void *src)
372 if (_flags & Locked) {
376 if (_position != pos) {
377 _last_position = _position;
381 boost::shared_ptr<Playlist> pl (playlist());
384 pl->raise_region_to_top (shared_from_this ());
387 /* do this even if the position is the same. this helps out
388 a GUI that has moved its representation already.
391 send_change (PositionChanged);
395 Region::recompute_position_from_lock_style ()
397 if (_positional_lock_style == MusicTime) {
398 boost::shared_ptr<Playlist> pl (playlist());
400 pl->session().tempo_map().bbt_time (_position, _bbt_time);
406 Region::nudge_position (nframes64_t n, void *src)
408 if (_flags & Locked) {
416 _last_position = _position;
419 if (_position > max_frames - n) {
420 _position = max_frames;
425 if (_position < (nframes_t) -n) {
432 send_change (PositionChanged);
436 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
438 _ancestral_length = l;
439 _ancestral_start = s;
445 Region::set_start (nframes_t pos, void *src)
447 if (_flags & Locked) {
450 /* This just sets the start, nothing else. It effectively shifts
451 the contents of the Region within the overall extent of the Source,
452 without changing the Region's position or length
457 if (!verify_start (pos)) {
462 _flags = Region::Flag (_flags & ~WholeFile);
464 invalidate_transients ();
466 send_change (StartChanged);
471 Region::trim_start (nframes_t new_position, void *src)
473 if (_flags & Locked) {
479 if (new_position > _position) {
480 start_shift = new_position - _position;
482 start_shift = -(_position - new_position);
485 if (start_shift > 0) {
487 if (_start > max_frames - start_shift) {
488 new_start = max_frames;
490 new_start = _start + start_shift;
493 if (!verify_start (new_start)) {
497 } else if (start_shift < 0) {
499 if (_start < (nframes_t) -start_shift) {
502 new_start = _start + start_shift;
508 if (new_start == _start) {
513 _flags = Region::Flag (_flags & ~WholeFile);
516 send_change (StartChanged);
520 Region::trim_front (nframes_t new_position, void *src)
522 if (_flags & Locked) {
526 nframes_t end = last_frame();
527 nframes_t source_zero;
529 if (_position > _start) {
530 source_zero = _position - _start;
532 source_zero = 0; // its actually negative, but this will work for us
535 if (new_position < end) { /* can't trim it zero or negative length */
539 /* can't trim it back passed where source position zero is located */
541 new_position = max (new_position, source_zero);
544 if (new_position > _position) {
545 newlen = _length - (new_position - _position);
547 newlen = _length + (_position - new_position);
550 trim_to_internal (new_position, newlen, src);
552 recompute_at_start ();
558 Region::trim_end (nframes_t new_endpoint, void *src)
560 if (_flags & Locked) {
564 if (new_endpoint > _position) {
565 trim_to_internal (_position, new_endpoint - _position, this);
573 Region::trim_to (nframes_t position, nframes_t length, void *src)
575 if (_flags & Locked) {
579 trim_to_internal (position, length, src);
582 recompute_at_start ();
588 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
593 if (_flags & Locked) {
597 if (position > _position) {
598 start_shift = position - _position;
600 start_shift = -(_position - position);
603 if (start_shift > 0) {
605 if (_start > max_frames - start_shift) {
606 new_start = max_frames;
608 new_start = _start + start_shift;
612 } else if (start_shift < 0) {
614 if (_start < (nframes_t) -start_shift) {
617 new_start = _start + start_shift;
623 if (!verify_start_and_length (new_start, length)) {
627 Change what_changed = Change (0);
629 if (_start != new_start) {
631 what_changed = Change (what_changed|StartChanged);
633 if (_length != length) {
635 _last_length = _length;
638 what_changed = Change (what_changed|LengthChanged);
640 if (_position != position) {
642 _last_position = _position;
644 _position = position;
645 what_changed = Change (what_changed|PositionChanged);
648 _flags = Region::Flag (_flags & ~WholeFile);
650 if (what_changed & (StartChanged|LengthChanged)) {
655 send_change (what_changed);
660 Region::set_hidden (bool yn)
662 if (hidden() != yn) {
665 _flags = Flag (_flags|Hidden);
667 _flags = Flag (_flags & ~Hidden);
670 send_change (HiddenChanged);
675 Region::set_muted (bool yn)
680 _flags = Flag (_flags|Muted);
682 _flags = Flag (_flags & ~Muted);
685 send_change (MuteChanged);
690 Region::set_opaque (bool yn)
692 if (opaque() != yn) {
694 _flags = Flag (_flags|Opaque);
696 _flags = Flag (_flags & ~Opaque);
698 send_change (OpacityChanged);
703 Region::set_locked (bool yn)
705 if (locked() != yn) {
707 _flags = Flag (_flags|Locked);
709 _flags = Flag (_flags & ~Locked);
711 send_change (LockChanged);
716 Region::set_sync_position (nframes_t absolute_pos)
720 file_pos = _start + (absolute_pos - _position);
722 if (file_pos != _sync_position) {
724 _sync_position = file_pos;
725 _flags = Flag (_flags|SyncMarked);
730 send_change (SyncOffsetChanged);
735 Region::clear_sync_position ()
737 if (_flags & SyncMarked) {
738 _flags = Flag (_flags & ~SyncMarked);
743 send_change (SyncOffsetChanged);
748 Region::sync_offset (int& dir) const
750 /* returns the sync point relative the first frame of the region */
752 if (_flags & SyncMarked) {
753 if (_sync_position > _start) {
755 return _sync_position - _start;
758 return _start - _sync_position;
767 Region::adjust_to_sync (nframes_t pos)
770 nframes_t offset = sync_offset (sync_dir);
772 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
781 if (max_frames - pos > offset) {
790 Region::sync_position() const
792 if (_flags & SyncMarked) {
793 return _sync_position;
803 boost::shared_ptr<Playlist> pl (playlist());
805 pl->raise_region (shared_from_this ());
812 boost::shared_ptr<Playlist> pl (playlist());
814 pl->lower_region (shared_from_this ());
819 Region::raise_to_top ()
821 boost::shared_ptr<Playlist> pl (playlist());
823 pl->raise_region_to_top (shared_from_this());
828 Region::lower_to_bottom ()
830 boost::shared_ptr<Playlist> pl (playlist());
832 pl->lower_region_to_bottom (shared_from_this());
837 Region::set_layer (layer_t l)
842 send_change (LayerChanged);
847 Region::state (bool full_state)
849 XMLNode *node = new XMLNode ("Region");
851 const char* fe = NULL;
853 _id.print (buf, sizeof (buf));
854 node->add_property ("id", buf);
855 node->add_property ("name", _name);
856 snprintf (buf, sizeof (buf), "%u", _start);
857 node->add_property ("start", buf);
858 snprintf (buf, sizeof (buf), "%u", _length);
859 node->add_property ("length", buf);
860 snprintf (buf, sizeof (buf), "%u", _position);
861 node->add_property ("position", buf);
862 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
863 node->add_property ("ancestral-start", buf);
864 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
865 node->add_property ("ancestral-length", buf);
866 snprintf (buf, sizeof (buf), "%.12g", _stretch);
867 node->add_property ("stretch", buf);
868 snprintf (buf, sizeof (buf), "%.12g", _shift);
869 node->add_property ("shift", buf);
871 switch (_first_edit) {
872 case EditChangesNothing:
875 case EditChangesName:
881 default: /* should be unreachable but makes g++ happy */
886 node->add_property ("first_edit", fe);
888 /* note: flags are stored by derived classes */
890 snprintf (buf, sizeof (buf), "%d", (int) _layer);
891 node->add_property ("layer", buf);
892 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
893 node->add_property ("sync-position", buf);
895 if (_positional_lock_style != AudioTime) {
896 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
899 node->add_property ("bbt-position", str.str());
912 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
914 const XMLNodeList& nlist = node.children();
915 const XMLProperty *prop;
918 /* this is responsible for setting those aspects of Region state
919 that are mutable after construction.
922 if ((prop = node.property ("name")) == 0) {
923 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
927 _name = prop->value();
929 if ((prop = node.property ("start")) != 0) {
930 sscanf (prop->value().c_str(), "%" PRIu32, &val);
932 what_changed = Change (what_changed|StartChanged);
939 if ((prop = node.property ("length")) != 0) {
940 sscanf (prop->value().c_str(), "%" PRIu32, &val);
941 if (val != _length) {
942 what_changed = Change (what_changed|LengthChanged);
943 _last_length = _length;
947 _last_length = _length;
951 if ((prop = node.property ("position")) != 0) {
952 sscanf (prop->value().c_str(), "%" PRIu32, &val);
953 if (val != _position) {
954 what_changed = Change (what_changed|PositionChanged);
955 _last_position = _position;
959 _last_position = _position;
963 if ((prop = node.property ("layer")) != 0) {
965 x = (layer_t) atoi (prop->value().c_str());
967 what_changed = Change (what_changed|LayerChanged);
974 if ((prop = node.property ("sync-position")) != 0) {
975 sscanf (prop->value().c_str(), "%" PRIu32, &val);
976 if (val != _sync_position) {
977 what_changed = Change (what_changed|SyncOffsetChanged);
978 _sync_position = val;
981 _sync_position = _start;
984 if ((prop = node.property ("positional-lock-style")) != 0) {
985 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
987 if (_positional_lock_style == MusicTime) {
988 if ((prop = node.property ("bbt-position")) == 0) {
989 /* missing BBT info, revert to audio time locking */
990 _positional_lock_style = AudioTime;
992 if (sscanf (prop->value().c_str(), "%d|%d|%d",
995 &_bbt_time.ticks) != 3) {
996 _positional_lock_style = AudioTime;
1002 _positional_lock_style = AudioTime;
1005 /* XXX FIRST EDIT !!! */
1007 /* these 3 properties never change as a result of any editing */
1009 if ((prop = node.property ("ancestral-start")) != 0) {
1010 _ancestral_start = atoi (prop->value());
1012 _ancestral_start = _start;
1015 if ((prop = node.property ("ancestral-length")) != 0) {
1016 _ancestral_length = atoi (prop->value());
1018 _ancestral_length = _length;
1021 if ((prop = node.property ("stretch")) != 0) {
1022 _stretch = atof (prop->value());
1027 if ((prop = node.property ("shift")) != 0) {
1028 _shift = atof (prop->value());
1033 /* note: derived classes set flags */
1040 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1046 if (child->name () == "extra") {
1047 _extra_xml = new XMLNode (*child);
1053 send_change (what_changed);
1060 Region::set_state (const XMLNode& node)
1062 const XMLProperty *prop;
1063 Change what_changed = Change (0);
1065 /* ID is not allowed to change, ever */
1067 if ((prop = node.property ("id")) == 0) {
1068 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1072 _id = prop->value();
1074 _first_edit = EditChangesNothing;
1076 set_live_state (node, what_changed, true);
1085 _last_length = _length;
1086 _last_position = _position;
1090 Region::thaw (const string& why)
1092 Change what_changed = Change (0);
1095 Glib::Mutex::Lock lm (lock);
1097 if (_frozen && --_frozen > 0) {
1101 if (pending_changed) {
1102 what_changed = pending_changed;
1103 pending_changed = Change (0);
1107 if (what_changed == Change (0)) {
1111 if (what_changed & LengthChanged) {
1112 if (what_changed & PositionChanged) {
1113 recompute_at_start ();
1115 recompute_at_end ();
1118 StateChanged (what_changed);
1122 Region::send_change (Change what_changed)
1125 Glib::Mutex::Lock lm (lock);
1127 pending_changed = Change (pending_changed|what_changed);
1132 StateChanged (what_changed);
1136 Region::set_last_layer_op (uint64_t when)
1138 _last_layer_op = when;
1142 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1144 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1148 Region::equivalent (boost::shared_ptr<const Region> other) const
1150 return _start == other->_start &&
1151 _position == other->_position &&
1152 _length == other->_length;
1156 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1158 return _start == other->_start &&
1159 _length == other->_length;
1163 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1165 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1169 Region::invalidate_transients ()
1171 valid_transients = false;
1172 _transients.clear ();