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 && _positional_lock_style == MusicTime) {
356 boost::shared_ptr<Playlist> pl (playlist());
358 pl->session().tempo_map().bbt_time (_position, _bbt_time);
362 invalidate_transients ();
365 /* do this even if the position is the same. this helps out
366 a GUI that has moved its representation already.
369 send_change (PositionChanged);
373 Region::set_position_on_top (nframes_t pos, void *src)
375 if (_flags & Locked) {
379 if (_position != pos) {
380 _last_position = _position;
384 boost::shared_ptr<Playlist> pl (playlist());
387 pl->raise_region_to_top (shared_from_this ());
390 /* do this even if the position is the same. this helps out
391 a GUI that has moved its representation already.
394 send_change (PositionChanged);
398 Region::nudge_position (nframes64_t n, void *src)
400 if (_flags & Locked) {
408 _last_position = _position;
411 if (_position > max_frames - n) {
412 _position = max_frames;
417 if (_position < (nframes_t) -n) {
424 send_change (PositionChanged);
428 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
430 _ancestral_length = l;
431 _ancestral_start = s;
437 Region::set_start (nframes_t pos, void *src)
439 if (_flags & Locked) {
442 /* This just sets the start, nothing else. It effectively shifts
443 the contents of the Region within the overall extent of the Source,
444 without changing the Region's position or length
449 if (!verify_start (pos)) {
454 _flags = Region::Flag (_flags & ~WholeFile);
456 invalidate_transients ();
458 send_change (StartChanged);
463 Region::trim_start (nframes_t new_position, void *src)
465 if (_flags & Locked) {
471 if (new_position > _position) {
472 start_shift = new_position - _position;
474 start_shift = -(_position - new_position);
477 if (start_shift > 0) {
479 if (_start > max_frames - start_shift) {
480 new_start = max_frames;
482 new_start = _start + start_shift;
485 if (!verify_start (new_start)) {
489 } else if (start_shift < 0) {
491 if (_start < (nframes_t) -start_shift) {
494 new_start = _start + start_shift;
500 if (new_start == _start) {
505 _flags = Region::Flag (_flags & ~WholeFile);
508 send_change (StartChanged);
512 Region::trim_front (nframes_t new_position, void *src)
514 if (_flags & Locked) {
518 nframes_t end = last_frame();
519 nframes_t source_zero;
521 if (_position > _start) {
522 source_zero = _position - _start;
524 source_zero = 0; // its actually negative, but this will work for us
527 if (new_position < end) { /* can't trim it zero or negative length */
531 /* can't trim it back passed where source position zero is located */
533 new_position = max (new_position, source_zero);
536 if (new_position > _position) {
537 newlen = _length - (new_position - _position);
539 newlen = _length + (_position - new_position);
542 trim_to_internal (new_position, newlen, src);
544 recompute_at_start ();
550 Region::trim_end (nframes_t new_endpoint, void *src)
552 if (_flags & Locked) {
556 if (new_endpoint > _position) {
557 trim_to_internal (_position, new_endpoint - _position, this);
565 Region::trim_to (nframes_t position, nframes_t length, void *src)
567 if (_flags & Locked) {
571 trim_to_internal (position, length, src);
574 recompute_at_start ();
580 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
585 if (_flags & Locked) {
589 if (position > _position) {
590 start_shift = position - _position;
592 start_shift = -(_position - position);
595 if (start_shift > 0) {
597 if (_start > max_frames - start_shift) {
598 new_start = max_frames;
600 new_start = _start + start_shift;
604 } else if (start_shift < 0) {
606 if (_start < (nframes_t) -start_shift) {
609 new_start = _start + start_shift;
615 if (!verify_start_and_length (new_start, length)) {
619 Change what_changed = Change (0);
621 if (_start != new_start) {
623 what_changed = Change (what_changed|StartChanged);
625 if (_length != length) {
627 _last_length = _length;
630 what_changed = Change (what_changed|LengthChanged);
632 if (_position != position) {
634 _last_position = _position;
636 _position = position;
637 what_changed = Change (what_changed|PositionChanged);
640 _flags = Region::Flag (_flags & ~WholeFile);
642 if (what_changed & (StartChanged|LengthChanged)) {
647 send_change (what_changed);
652 Region::set_hidden (bool yn)
654 if (hidden() != yn) {
657 _flags = Flag (_flags|Hidden);
659 _flags = Flag (_flags & ~Hidden);
662 send_change (HiddenChanged);
667 Region::set_muted (bool yn)
672 _flags = Flag (_flags|Muted);
674 _flags = Flag (_flags & ~Muted);
677 send_change (MuteChanged);
682 Region::set_opaque (bool yn)
684 if (opaque() != yn) {
686 _flags = Flag (_flags|Opaque);
688 _flags = Flag (_flags & ~Opaque);
690 send_change (OpacityChanged);
695 Region::set_locked (bool yn)
697 if (locked() != yn) {
699 _flags = Flag (_flags|Locked);
701 _flags = Flag (_flags & ~Locked);
703 send_change (LockChanged);
708 Region::set_sync_position (nframes_t absolute_pos)
712 file_pos = _start + (absolute_pos - _position);
714 if (file_pos != _sync_position) {
716 _sync_position = file_pos;
717 _flags = Flag (_flags|SyncMarked);
722 send_change (SyncOffsetChanged);
727 Region::clear_sync_position ()
729 if (_flags & SyncMarked) {
730 _flags = Flag (_flags & ~SyncMarked);
735 send_change (SyncOffsetChanged);
740 Region::sync_offset (int& dir) const
742 /* returns the sync point relative the first frame of the region */
744 if (_flags & SyncMarked) {
745 if (_sync_position > _start) {
747 return _sync_position - _start;
750 return _start - _sync_position;
759 Region::adjust_to_sync (nframes_t pos)
762 nframes_t offset = sync_offset (sync_dir);
764 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
773 if (max_frames - pos > offset) {
782 Region::sync_position() const
784 if (_flags & SyncMarked) {
785 return _sync_position;
795 boost::shared_ptr<Playlist> pl (playlist());
797 pl->raise_region (shared_from_this ());
804 boost::shared_ptr<Playlist> pl (playlist());
806 pl->lower_region (shared_from_this ());
811 Region::raise_to_top ()
813 boost::shared_ptr<Playlist> pl (playlist());
815 pl->raise_region_to_top (shared_from_this());
820 Region::lower_to_bottom ()
822 boost::shared_ptr<Playlist> pl (playlist());
824 pl->lower_region_to_bottom (shared_from_this());
829 Region::set_layer (layer_t l)
834 send_change (LayerChanged);
839 Region::state (bool full_state)
841 XMLNode *node = new XMLNode ("Region");
843 const char* fe = NULL;
845 _id.print (buf, sizeof (buf));
846 node->add_property ("id", buf);
847 node->add_property ("name", _name);
848 snprintf (buf, sizeof (buf), "%u", _start);
849 node->add_property ("start", buf);
850 snprintf (buf, sizeof (buf), "%u", _length);
851 node->add_property ("length", buf);
852 snprintf (buf, sizeof (buf), "%u", _position);
853 node->add_property ("position", buf);
854 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
855 node->add_property ("ancestral-start", buf);
856 snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
857 node->add_property ("ancestral-length", buf);
858 snprintf (buf, sizeof (buf), "%.12g", _stretch);
859 node->add_property ("stretch", buf);
860 snprintf (buf, sizeof (buf), "%.12g", _shift);
861 node->add_property ("shift", buf);
863 switch (_first_edit) {
864 case EditChangesNothing:
867 case EditChangesName:
873 default: /* should be unreachable but makes g++ happy */
878 node->add_property ("first_edit", fe);
880 /* note: flags are stored by derived classes */
882 snprintf (buf, sizeof (buf), "%d", (int) _layer);
883 node->add_property ("layer", buf);
884 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
885 node->add_property ("sync-position", buf);
887 if (_positional_lock_style != AudioTime) {
888 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
891 node->add_property ("bbt-position", str.str());
904 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
906 const XMLNodeList& nlist = node.children();
907 const XMLProperty *prop;
910 /* this is responsible for setting those aspects of Region state
911 that are mutable after construction.
914 if ((prop = node.property ("name")) == 0) {
915 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
919 _name = prop->value();
921 if ((prop = node.property ("start")) != 0) {
922 sscanf (prop->value().c_str(), "%" PRIu32, &val);
924 what_changed = Change (what_changed|StartChanged);
931 if ((prop = node.property ("length")) != 0) {
932 sscanf (prop->value().c_str(), "%" PRIu32, &val);
933 if (val != _length) {
934 what_changed = Change (what_changed|LengthChanged);
935 _last_length = _length;
939 _last_length = _length;
943 if ((prop = node.property ("position")) != 0) {
944 sscanf (prop->value().c_str(), "%" PRIu32, &val);
945 if (val != _position) {
946 what_changed = Change (what_changed|PositionChanged);
947 _last_position = _position;
951 _last_position = _position;
955 if ((prop = node.property ("layer")) != 0) {
957 x = (layer_t) atoi (prop->value().c_str());
959 what_changed = Change (what_changed|LayerChanged);
966 if ((prop = node.property ("sync-position")) != 0) {
967 sscanf (prop->value().c_str(), "%" PRIu32, &val);
968 if (val != _sync_position) {
969 what_changed = Change (what_changed|SyncOffsetChanged);
970 _sync_position = val;
973 _sync_position = _start;
976 if ((prop = node.property ("positional-lock-style")) != 0) {
977 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
979 if (_positional_lock_style == MusicTime) {
980 if ((prop = node.property ("bbt-position")) == 0) {
981 /* missing BBT info, revert to audio time locking */
982 _positional_lock_style = AudioTime;
984 if (sscanf (prop->value().c_str(), "%d|%d|%d",
987 &_bbt_time.ticks) != 3) {
988 _positional_lock_style = AudioTime;
994 _positional_lock_style = AudioTime;
997 /* XXX FIRST EDIT !!! */
999 /* these 3 properties never change as a result of any editing */
1001 if ((prop = node.property ("ancestral-start")) != 0) {
1002 _ancestral_start = atoi (prop->value());
1004 _ancestral_start = _start;
1007 if ((prop = node.property ("ancestral-length")) != 0) {
1008 _ancestral_length = atoi (prop->value());
1010 _ancestral_length = _length;
1013 if ((prop = node.property ("stretch")) != 0) {
1014 _stretch = atof (prop->value());
1019 if ((prop = node.property ("shift")) != 0) {
1020 _shift = atof (prop->value());
1025 /* note: derived classes set flags */
1032 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1038 if (child->name () == "extra") {
1039 _extra_xml = new XMLNode (*child);
1045 send_change (what_changed);
1052 Region::set_state (const XMLNode& node)
1054 const XMLProperty *prop;
1055 Change what_changed = Change (0);
1057 /* ID is not allowed to change, ever */
1059 if ((prop = node.property ("id")) == 0) {
1060 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1064 _id = prop->value();
1066 _first_edit = EditChangesNothing;
1068 set_live_state (node, what_changed, true);
1077 _last_length = _length;
1078 _last_position = _position;
1082 Region::thaw (const string& why)
1084 Change what_changed = Change (0);
1087 Glib::Mutex::Lock lm (lock);
1089 if (_frozen && --_frozen > 0) {
1093 if (pending_changed) {
1094 what_changed = pending_changed;
1095 pending_changed = Change (0);
1099 if (what_changed == Change (0)) {
1103 if (what_changed & LengthChanged) {
1104 if (what_changed & PositionChanged) {
1105 recompute_at_start ();
1107 recompute_at_end ();
1110 StateChanged (what_changed);
1114 Region::send_change (Change what_changed)
1117 Glib::Mutex::Lock lm (lock);
1119 pending_changed = Change (pending_changed|what_changed);
1124 StateChanged (what_changed);
1128 Region::set_last_layer_op (uint64_t when)
1130 _last_layer_op = when;
1134 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1136 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1140 Region::equivalent (boost::shared_ptr<const Region> other) const
1142 return _start == other->_start &&
1143 _position == other->_position &&
1144 _length == other->_length;
1148 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1150 return _start == other->_start &&
1151 _length == other->_length;
1155 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1157 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1161 Region::invalidate_transients ()
1163 valid_transients = false;
1164 _transients.clear ();