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.
25 #include <sigc++/bind.h>
26 #include <sigc++/class_slot.h>
28 #include <glibmm/thread.h>
29 #include <pbd/xml++.h>
30 #include <pbd/stacktrace.h>
32 #include <ardour/region.h>
33 #include <ardour/playlist.h>
34 #include <ardour/session.h>
35 #include <ardour/region_factory.h>
40 using namespace ARDOUR;
43 Change Region::FadeChanged = ARDOUR::new_change ();
44 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
45 Change Region::MuteChanged = ARDOUR::new_change ();
46 Change Region::OpacityChanged = ARDOUR::new_change ();
47 Change Region::LockChanged = ARDOUR::new_change ();
48 Change Region::LayerChanged = ARDOUR::new_change ();
49 Change Region::HiddenChanged = ARDOUR::new_change ();
51 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
53 /* basic Region constructor */
58 pending_changed = Change (0);
62 _sync_position = _start;
64 _last_length = length;
65 _ancestral_start = start;
66 _ancestral_length = length;
73 _first_edit = EditChangesNothing;
77 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
79 /* create a new Region from part of an existing one */
82 pending_changed = Change (0);
85 _start = other->_start + offset;
86 if (other->_sync_position < offset) {
87 _sync_position = other->_sync_position;
89 _sync_position = _start;
92 _last_length = length;
93 _ancestral_start = other->_ancestral_start + offset;
94 _ancestral_length = length;
101 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
102 _first_edit = EditChangesNothing;
106 Region::Region (boost::shared_ptr<const Region> other)
108 /* Pure copy constructor */
111 pending_changed = Change (0);
112 _read_data_count = 0;
114 _first_edit = EditChangesID;
115 other->_first_edit = EditChangesName;
117 if (other->_extra_xml) {
118 _extra_xml = new XMLNode (*other->_extra_xml);
123 _start = other->_start;
124 _sync_position = other->_sync_position;
125 _length = other->_length;
126 _last_length = other->_length;
127 _ancestral_start = _start;
128 _ancestral_length = _length;
131 _name = other->_name;
132 _last_position = other->_position;
133 _position = other->_position;
134 _layer = other->_layer;
135 _flags = Flag (other->_flags & ~Locked);
136 _last_layer_op = other->_last_layer_op;
139 Region::Region (const XMLNode& node)
142 pending_changed = Change (0);
143 _read_data_count = 0;
145 _sync_position = _start;
148 _name = X_("error: XML did not reset this");
153 _first_edit = EditChangesNothing;
155 if (set_state (node)) {
156 throw failed_constructor();
162 /* derived classes must call notify_callbacks() and then emit GoingAway */
166 Region::set_playlist (boost::weak_ptr<Playlist> pl)
172 Region::set_name (string str)
176 send_change (NameChanged);
181 Region::set_length (nframes_t len, void *src)
183 if (_flags & Locked) {
187 if (_length != len && len != 0) {
189 /* check that the current _position wouldn't make the new
193 if (max_frames - len < _position) {
197 if (!verify_length (len)) {
202 _last_length = _length;
205 _flags = Region::Flag (_flags & ~WholeFile);
214 send_change (LengthChanged);
219 Region::maybe_uncopy ()
224 Region::first_edit ()
226 boost::shared_ptr<Playlist> pl (playlist());
228 if (_first_edit != EditChangesNothing && pl) {
230 _name = pl->session().new_region_name (_name);
231 _first_edit = EditChangesNothing;
233 send_change (NameChanged);
234 RegionFactory::CheckNewRegion (shared_from_this());
239 Region::at_natural_position () const
241 boost::shared_ptr<Playlist> pl (playlist());
247 boost::shared_ptr<Region> whole_file_region = get_parent();
249 if (whole_file_region) {
250 if (_position == whole_file_region->position() + _start) {
259 Region::move_to_natural_position (void *src)
261 boost::shared_ptr<Playlist> pl (playlist());
267 boost::shared_ptr<Region> whole_file_region = get_parent();
269 if (whole_file_region) {
270 set_position (whole_file_region->position() + _start, src);
275 Region::special_set_position (nframes_t pos)
277 /* this is used when creating a whole file region as
278 a way to store its "natural" or "captured" position.
281 _position = _position;
286 Region::set_position (nframes_t pos, void *src)
288 if (_flags & Locked) {
292 if (_position != pos) {
293 _last_position = _position;
296 /* check that the new _position wouldn't make the current
297 length impossible - if so, change the length.
299 XXX is this the right thing to do?
302 if (max_frames - _length < _position) {
303 _last_length = _length;
304 _length = max_frames - _position;
308 /* do this even if the position is the same. this helps out
309 a GUI that has moved its representation already.
312 send_change (PositionChanged);
316 Region::set_position_on_top (nframes_t pos, void *src)
318 if (_flags & Locked) {
322 if (_position != pos) {
323 _last_position = _position;
327 boost::shared_ptr<Playlist> pl (playlist());
330 pl->raise_region_to_top (shared_from_this ());
333 /* do this even if the position is the same. this helps out
334 a GUI that has moved its representation already.
337 send_change (PositionChanged);
341 Region::nudge_position (nframes64_t n, void *src)
343 if (_flags & Locked) {
351 _last_position = _position;
354 if (_position > max_frames - n) {
355 _position = max_frames;
360 if (_position < (nframes_t) -n) {
367 send_change (PositionChanged);
371 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
373 _ancestral_length = l;
374 _ancestral_start = s;
380 Region::set_start (nframes_t pos, void *src)
382 if (_flags & Locked) {
385 /* This just sets the start, nothing else. It effectively shifts
386 the contents of the Region within the overall extent of the Source,
387 without changing the Region's position or length
392 if (!verify_start (pos)) {
397 _flags = Region::Flag (_flags & ~WholeFile);
400 send_change (StartChanged);
405 Region::trim_start (nframes_t new_position, void *src)
407 if (_flags & Locked) {
413 if (new_position > _position) {
414 start_shift = new_position - _position;
416 start_shift = -(_position - new_position);
419 if (start_shift > 0) {
421 if (_start > max_frames - start_shift) {
422 new_start = max_frames;
424 new_start = _start + start_shift;
427 if (!verify_start (new_start)) {
431 } else if (start_shift < 0) {
433 if (_start < (nframes_t) -start_shift) {
436 new_start = _start + start_shift;
442 if (new_start == _start) {
447 _flags = Region::Flag (_flags & ~WholeFile);
450 send_change (StartChanged);
454 Region::trim_front (nframes_t new_position, void *src)
456 if (_flags & Locked) {
460 nframes_t end = last_frame();
461 nframes_t source_zero;
463 if (_position > _start) {
464 source_zero = _position - _start;
466 source_zero = 0; // its actually negative, but this will work for us
469 if (new_position < end) { /* can't trim it zero or negative length */
473 /* can't trim it back passed where source position zero is located */
475 new_position = max (new_position, source_zero);
478 if (new_position > _position) {
479 newlen = _length - (new_position - _position);
481 newlen = _length + (_position - new_position);
484 trim_to_internal (new_position, newlen, src);
486 recompute_at_start ();
492 Region::trim_end (nframes_t new_endpoint, void *src)
494 if (_flags & Locked) {
498 if (new_endpoint > _position) {
499 trim_to_internal (_position, new_endpoint - _position, this);
507 Region::trim_to (nframes_t position, nframes_t length, void *src)
509 if (_flags & Locked) {
513 trim_to_internal (position, length, src);
516 recompute_at_start ();
522 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
527 if (_flags & Locked) {
531 if (position > _position) {
532 start_shift = position - _position;
534 start_shift = -(_position - position);
537 if (start_shift > 0) {
539 if (_start > max_frames - start_shift) {
540 new_start = max_frames;
542 new_start = _start + start_shift;
546 } else if (start_shift < 0) {
548 if (_start < (nframes_t) -start_shift) {
551 new_start = _start + start_shift;
557 if (!verify_start_and_length (new_start, length)) {
561 Change what_changed = Change (0);
563 if (_start != new_start) {
565 what_changed = Change (what_changed|StartChanged);
567 if (_length != length) {
569 _last_length = _length;
572 what_changed = Change (what_changed|LengthChanged);
574 if (_position != position) {
576 _last_position = _position;
578 _position = position;
579 what_changed = Change (what_changed|PositionChanged);
582 _flags = Region::Flag (_flags & ~WholeFile);
584 if (what_changed & (StartChanged|LengthChanged)) {
589 send_change (what_changed);
594 Region::set_hidden (bool yn)
596 if (hidden() != yn) {
599 _flags = Flag (_flags|Hidden);
601 _flags = Flag (_flags & ~Hidden);
604 send_change (HiddenChanged);
609 Region::set_muted (bool yn)
614 _flags = Flag (_flags|Muted);
616 _flags = Flag (_flags & ~Muted);
619 send_change (MuteChanged);
624 Region::set_opaque (bool yn)
626 if (opaque() != yn) {
628 _flags = Flag (_flags|Opaque);
630 _flags = Flag (_flags & ~Opaque);
632 send_change (OpacityChanged);
637 Region::set_locked (bool yn)
639 if (locked() != yn) {
641 _flags = Flag (_flags|Locked);
643 _flags = Flag (_flags & ~Locked);
645 send_change (LockChanged);
650 Region::set_sync_position (nframes_t absolute_pos)
654 file_pos = _start + (absolute_pos - _position);
656 if (file_pos != _sync_position) {
658 _sync_position = file_pos;
659 _flags = Flag (_flags|SyncMarked);
664 send_change (SyncOffsetChanged);
669 Region::clear_sync_position ()
671 if (_flags & SyncMarked) {
672 _flags = Flag (_flags & ~SyncMarked);
677 send_change (SyncOffsetChanged);
682 Region::sync_offset (int& dir) const
684 /* returns the sync point relative the first frame of the region */
686 if (_flags & SyncMarked) {
687 if (_sync_position > _start) {
689 return _sync_position - _start;
692 return _start - _sync_position;
701 Region::adjust_to_sync (nframes_t pos)
704 nframes_t offset = sync_offset (sync_dir);
706 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
709 if (max_frames - pos > offset) {
724 Region::sync_position() const
726 if (_flags & SyncMarked) {
727 return _sync_position;
737 boost::shared_ptr<Playlist> pl (playlist());
739 pl->raise_region (shared_from_this ());
746 boost::shared_ptr<Playlist> pl (playlist());
748 pl->lower_region (shared_from_this ());
753 Region::raise_to_top ()
755 boost::shared_ptr<Playlist> pl (playlist());
757 pl->raise_region_to_top (shared_from_this());
762 Region::lower_to_bottom ()
764 boost::shared_ptr<Playlist> pl (playlist());
766 pl->lower_region_to_bottom (shared_from_this());
771 Region::set_layer (layer_t l)
776 send_change (LayerChanged);
781 Region::state (bool full_state)
783 XMLNode *node = new XMLNode ("Region");
785 const char* fe = NULL;
787 _id.print (buf, sizeof (buf));
788 node->add_property ("id", buf);
789 node->add_property ("name", _name);
790 snprintf (buf, sizeof (buf), "%u", _start);
791 node->add_property ("start", buf);
792 snprintf (buf, sizeof (buf), "%u", _length);
793 node->add_property ("length", buf);
794 snprintf (buf, sizeof (buf), "%u", _position);
795 node->add_property ("position", buf);
796 snprintf (buf, sizeof (buf), "%Ld", _ancestral_start);
797 node->add_property ("ancestral-start", buf);
798 snprintf (buf, sizeof (buf), "%Ld", _ancestral_length);
799 node->add_property ("ancestral-length", buf);
800 snprintf (buf, sizeof (buf), "%.12g", _stretch);
801 node->add_property ("stretch", buf);
802 snprintf (buf, sizeof (buf), "%.12g", _shift);
803 node->add_property ("shift", buf);
805 switch (_first_edit) {
806 case EditChangesNothing:
809 case EditChangesName:
815 default: /* should be unreachable but makes g++ happy */
820 node->add_property ("first_edit", fe);
822 /* note: flags are stored by derived classes */
824 snprintf (buf, sizeof (buf), "%d", (int) _layer);
825 node->add_property ("layer", buf);
826 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
827 node->add_property ("sync-position", buf);
839 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
841 const XMLNodeList& nlist = node.children();
842 const XMLProperty *prop;
845 /* this is responsible for setting those aspects of Region state
846 that are mutable after construction.
849 if ((prop = node.property ("name")) == 0) {
850 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
854 _name = prop->value();
856 if ((prop = node.property ("start")) != 0) {
857 sscanf (prop->value().c_str(), "%" PRIu32, &val);
859 what_changed = Change (what_changed|StartChanged);
866 if ((prop = node.property ("length")) != 0) {
867 sscanf (prop->value().c_str(), "%" PRIu32, &val);
868 if (val != _length) {
869 what_changed = Change (what_changed|LengthChanged);
870 _last_length = _length;
874 _last_length = _length;
878 if ((prop = node.property ("position")) != 0) {
879 sscanf (prop->value().c_str(), "%" PRIu32, &val);
880 if (val != _position) {
881 what_changed = Change (what_changed|PositionChanged);
882 _last_position = _position;
886 _last_position = _position;
890 if ((prop = node.property ("layer")) != 0) {
892 x = (layer_t) atoi (prop->value().c_str());
894 what_changed = Change (what_changed|LayerChanged);
901 if ((prop = node.property ("sync-position")) != 0) {
902 sscanf (prop->value().c_str(), "%" PRIu32, &val);
903 if (val != _sync_position) {
904 what_changed = Change (what_changed|SyncOffsetChanged);
905 _sync_position = val;
908 _sync_position = _start;
911 /* XXX FIRST EDIT !!! */
913 /* these 3 properties never change as a result of any editing */
915 if ((prop = node.property ("ancestral-start")) != 0) {
916 _ancestral_start = atoi (prop->value());
918 _ancestral_start = _start;
921 if ((prop = node.property ("ancestral-length")) != 0) {
922 _ancestral_length = atoi (prop->value());
924 _ancestral_length = _length;
927 if ((prop = node.property ("stretch")) != 0) {
928 _stretch = atof (prop->value());
933 if ((prop = node.property ("shift")) != 0) {
934 _shift = atof (prop->value());
939 /* note: derived classes set flags */
946 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
952 if (child->name () == "extra") {
953 _extra_xml = new XMLNode (*child);
959 send_change (what_changed);
966 Region::set_state (const XMLNode& node)
968 const XMLProperty *prop;
969 Change what_changed = Change (0);
971 /* ID is not allowed to change, ever */
973 if ((prop = node.property ("id")) == 0) {
974 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
980 _first_edit = EditChangesNothing;
982 set_live_state (node, what_changed, true);
991 _last_length = _length;
992 _last_position = _position;
996 Region::thaw (const string& why)
998 Change what_changed = Change (0);
1001 Glib::Mutex::Lock lm (lock);
1003 if (_frozen && --_frozen > 0) {
1007 if (pending_changed) {
1008 what_changed = pending_changed;
1009 pending_changed = Change (0);
1013 if (what_changed == Change (0)) {
1017 if (what_changed & LengthChanged) {
1018 if (what_changed & PositionChanged) {
1019 recompute_at_start ();
1021 recompute_at_end ();
1024 StateChanged (what_changed);
1028 Region::send_change (Change what_changed)
1031 Glib::Mutex::Lock lm (lock);
1033 pending_changed = Change (pending_changed|what_changed);
1038 StateChanged (what_changed);
1042 Region::set_last_layer_op (uint64_t when)
1044 _last_layer_op = when;
1048 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1050 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1054 Region::equivalent (boost::shared_ptr<const Region> other) const
1056 return _start == other->_start &&
1057 _position == other->_position &&
1058 _length == other->_length;
1062 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1064 return _start == other->_start &&
1065 _length == other->_length;
1069 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1071 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;