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;
67 _first_edit = EditChangesNothing;
71 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
73 /* create a new Region from part of an existing one */
76 pending_changed = Change (0);
79 _start = other->_start + offset;
80 if (other->_sync_position < offset) {
81 _sync_position = other->_sync_position;
83 _sync_position = _start;
89 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
90 _first_edit = EditChangesNothing;
94 Region::Region (boost::shared_ptr<const Region> other)
96 /* Pure copy constructor */
99 pending_changed = Change (0);
100 _read_data_count = 0;
102 _first_edit = EditChangesID;
103 other->_first_edit = EditChangesName;
105 if (other->_extra_xml) {
106 _extra_xml = new XMLNode (*other->_extra_xml);
111 _start = other->_start;
112 _sync_position = other->_sync_position;
113 _length = other->_length;
114 _name = other->_name;
115 _position = other->_position;
116 _layer = other->_layer;
117 _flags = Flag (other->_flags & ~Locked);
118 _last_layer_op = other->_last_layer_op;
121 Region::Region (const XMLNode& node)
124 pending_changed = Change (0);
125 _read_data_count = 0;
127 _sync_position = _start;
129 _name = X_("error: XML did not reset this");
133 _first_edit = EditChangesNothing;
135 if (set_state (node)) {
136 throw failed_constructor();
142 /* derived classes must call notify_callbacks() and then emit GoingAway */
146 Region::set_playlist (boost::weak_ptr<Playlist> pl)
152 Region::set_name (string str)
156 send_change (NameChanged);
161 Region::set_length (nframes_t len, void *src)
163 if (_flags & Locked) {
167 if (_length != len && len != 0) {
169 /* check that the current _position wouldn't make the new
173 if (max_frames - len < _position) {
177 if (!verify_length (len)) {
183 _flags = Region::Flag (_flags & ~WholeFile);
192 send_change (LengthChanged);
197 Region::maybe_uncopy ()
202 Region::first_edit ()
204 boost::shared_ptr<Playlist> pl (playlist());
206 if (_first_edit != EditChangesNothing && pl) {
208 _name = pl->session().new_region_name (_name);
209 _first_edit = EditChangesNothing;
211 send_change (NameChanged);
212 RegionFactory::CheckNewRegion (shared_from_this());
217 Region::at_natural_position () const
219 boost::shared_ptr<Playlist> pl (playlist());
225 boost::shared_ptr<Region> whole_file_region = get_parent();
227 if (whole_file_region) {
228 if (_position == whole_file_region->position() + _start) {
237 Region::move_to_natural_position (void *src)
239 boost::shared_ptr<Playlist> pl (playlist());
245 boost::shared_ptr<Region> whole_file_region = get_parent();
247 if (whole_file_region) {
248 set_position (whole_file_region->position() + _start, src);
253 Region::special_set_position (nframes_t pos)
255 /* this is used when creating a whole file region as
256 a way to store its "natural" or "captured" position.
263 Region::set_position (nframes_t pos, void *src)
265 if (_flags & Locked) {
269 if (_position != pos) {
272 /* check that the new _position wouldn't make the current
273 length impossible - if so, change the length.
275 XXX is this the right thing to do?
278 if (max_frames - _length < _position) {
279 _length = max_frames - _position;
283 /* do this even if the position is the same. this helps out
284 a GUI that has moved its representation already.
287 send_change (PositionChanged);
291 Region::set_position_on_top (nframes_t pos, void *src)
293 if (_flags & Locked) {
297 if (_position != pos) {
301 boost::shared_ptr<Playlist> pl (playlist());
304 pl->raise_region_to_top (shared_from_this ());
307 /* do this even if the position is the same. this helps out
308 a GUI that has moved its representation already.
311 send_change (PositionChanged);
315 Region::nudge_position (long n, void *src)
317 if (_flags & Locked) {
326 if (_position > max_frames - n) {
327 _position = max_frames;
332 if (_position < (nframes_t) -n) {
339 send_change (PositionChanged);
343 Region::set_start (nframes_t pos, void *src)
345 if (_flags & Locked) {
348 /* This just sets the start, nothing else. It effectively shifts
349 the contents of the Region within the overall extent of the Source,
350 without changing the Region's position or length
355 if (!verify_start (pos)) {
360 _flags = Region::Flag (_flags & ~WholeFile);
363 send_change (StartChanged);
368 Region::trim_start (nframes_t new_position, void *src)
370 if (_flags & Locked) {
376 if (new_position > _position) {
377 start_shift = new_position - _position;
379 start_shift = -(_position - new_position);
382 if (start_shift > 0) {
384 if (_start > max_frames - start_shift) {
385 new_start = max_frames;
387 new_start = _start + start_shift;
390 if (!verify_start (new_start)) {
394 } else if (start_shift < 0) {
396 if (_start < (nframes_t) -start_shift) {
399 new_start = _start + start_shift;
405 if (new_start == _start) {
410 _flags = Region::Flag (_flags & ~WholeFile);
413 send_change (StartChanged);
417 Region::trim_front (nframes_t new_position, void *src)
419 if (_flags & Locked) {
423 nframes_t end = last_frame();
424 nframes_t source_zero;
426 if (_position > _start) {
427 source_zero = _position - _start;
429 source_zero = 0; // its actually negative, but this will work for us
432 if (new_position < end) { /* can't trim it zero or negative length */
436 /* can't trim it back passed where source position zero is located */
438 new_position = max (new_position, source_zero);
441 if (new_position > _position) {
442 newlen = _length - (new_position - _position);
444 newlen = _length + (_position - new_position);
447 trim_to_internal (new_position, newlen, src);
449 recompute_at_start ();
455 Region::trim_end (nframes_t new_endpoint, void *src)
457 if (_flags & Locked) {
461 if (new_endpoint > _position) {
462 trim_to_internal (_position, new_endpoint - _position, this);
470 Region::trim_to (nframes_t position, nframes_t length, void *src)
472 if (_flags & Locked) {
476 trim_to_internal (position, length, src);
479 recompute_at_start ();
485 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
490 if (_flags & Locked) {
494 if (position > _position) {
495 start_shift = position - _position;
497 start_shift = -(_position - position);
500 if (start_shift > 0) {
502 if (_start > max_frames - start_shift) {
503 new_start = max_frames;
505 new_start = _start + start_shift;
509 } else if (start_shift < 0) {
511 if (_start < (nframes_t) -start_shift) {
514 new_start = _start + start_shift;
520 if (!verify_start_and_length (new_start, length)) {
524 Change what_changed = Change (0);
526 if (_start != new_start) {
528 what_changed = Change (what_changed|StartChanged);
530 if (_length != length) {
532 what_changed = Change (what_changed|LengthChanged);
534 if (_position != position) {
535 _position = position;
536 what_changed = Change (what_changed|PositionChanged);
539 _flags = Region::Flag (_flags & ~WholeFile);
541 if (what_changed & (StartChanged|LengthChanged)) {
546 send_change (what_changed);
551 Region::set_hidden (bool yn)
553 if (hidden() != yn) {
556 _flags = Flag (_flags|Hidden);
558 _flags = Flag (_flags & ~Hidden);
561 send_change (HiddenChanged);
566 Region::set_muted (bool yn)
571 _flags = Flag (_flags|Muted);
573 _flags = Flag (_flags & ~Muted);
576 send_change (MuteChanged);
581 Region::set_opaque (bool yn)
583 if (opaque() != yn) {
585 _flags = Flag (_flags|Opaque);
587 _flags = Flag (_flags & ~Opaque);
589 send_change (OpacityChanged);
594 Region::set_locked (bool yn)
596 if (locked() != yn) {
598 _flags = Flag (_flags|Locked);
600 _flags = Flag (_flags & ~Locked);
602 send_change (LockChanged);
607 Region::set_sync_position (nframes_t absolute_pos)
611 file_pos = _start + (absolute_pos - _position);
613 if (file_pos != _sync_position) {
615 _sync_position = file_pos;
616 _flags = Flag (_flags|SyncMarked);
621 send_change (SyncOffsetChanged);
626 Region::clear_sync_position ()
628 if (_flags & SyncMarked) {
629 _flags = Flag (_flags & ~SyncMarked);
634 send_change (SyncOffsetChanged);
639 Region::sync_offset (int& dir) const
641 /* returns the sync point relative the first frame of the region */
643 if (_flags & SyncMarked) {
644 if (_sync_position > _start) {
646 return _sync_position - _start;
649 return _start - _sync_position;
658 Region::adjust_to_sync (nframes_t pos)
661 nframes_t offset = sync_offset (sync_dir);
664 if (max_frames - pos > offset) {
679 Region::sync_position() const
681 if (_flags & SyncMarked) {
682 return _sync_position;
692 boost::shared_ptr<Playlist> pl (playlist());
694 pl->raise_region (shared_from_this ());
701 boost::shared_ptr<Playlist> pl (playlist());
703 pl->lower_region (shared_from_this ());
708 Region::raise_to_top ()
710 boost::shared_ptr<Playlist> pl (playlist());
712 pl->raise_region_to_top (shared_from_this());
717 Region::lower_to_bottom ()
719 boost::shared_ptr<Playlist> pl (playlist());
721 pl->lower_region_to_bottom (shared_from_this());
726 Region::set_layer (layer_t l)
731 send_change (LayerChanged);
736 Region::state (bool full_state)
738 XMLNode *node = new XMLNode ("Region");
742 _id.print (buf, sizeof (buf));
743 node->add_property ("id", buf);
744 node->add_property ("name", _name);
745 snprintf (buf, sizeof (buf), "%u", _start);
746 node->add_property ("start", buf);
747 snprintf (buf, sizeof (buf), "%u", _length);
748 node->add_property ("length", buf);
749 snprintf (buf, sizeof (buf), "%u", _position);
750 node->add_property ("position", buf);
752 switch (_first_edit) {
753 case EditChangesNothing:
756 case EditChangesName:
762 default: /* should be unreachable but makes g++ happy */
763 cerr << "Odd region property found\n";
768 node->add_property ("first_edit", fe);
770 /* note: flags are stored by derived classes */
772 snprintf (buf, sizeof (buf), "%d", (int) _layer);
773 node->add_property ("layer", buf);
774 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
775 node->add_property ("sync-position", buf);
787 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
789 const XMLNodeList& nlist = node.children();
790 const XMLProperty *prop;
793 /* this is responsible for setting those aspects of Region state
794 that are mutable after construction.
797 if ((prop = node.property ("name")) == 0) {
798 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
802 _name = prop->value();
804 if ((prop = node.property ("start")) != 0) {
805 sscanf (prop->value().c_str(), "%" PRIu32, &val);
807 what_changed = Change (what_changed|StartChanged);
814 if ((prop = node.property ("length")) != 0) {
815 sscanf (prop->value().c_str(), "%" PRIu32, &val);
816 if (val != _length) {
817 what_changed = Change (what_changed|LengthChanged);
824 if ((prop = node.property ("position")) != 0) {
825 sscanf (prop->value().c_str(), "%" PRIu32, &val);
826 if (val != _position) {
827 what_changed = Change (what_changed|PositionChanged);
834 if ((prop = node.property ("layer")) != 0) {
836 x = (layer_t) atoi (prop->value().c_str());
838 what_changed = Change (what_changed|LayerChanged);
845 if ((prop = node.property ("sync-position")) != 0) {
846 sscanf (prop->value().c_str(), "%" PRIu32, &val);
847 if (val != _sync_position) {
848 what_changed = Change (what_changed|SyncOffsetChanged);
849 _sync_position = val;
852 _sync_position = _start;
855 /* XXX FIRST EDIT !!! */
857 /* note: derived classes set flags */
864 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
870 if (child->name () == "extra") {
871 _extra_xml = new XMLNode (*child);
877 send_change (what_changed);
884 Region::set_state (const XMLNode& node)
886 const XMLProperty *prop;
887 Change what_changed = Change (0);
889 /* ID is not allowed to change, ever */
891 if ((prop = node.property ("id")) == 0) {
892 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
898 _first_edit = EditChangesNothing;
900 set_live_state (node, what_changed, true);
912 Region::thaw (const string& why)
914 Change what_changed = Change (0);
917 Glib::Mutex::Lock lm (lock);
919 if (_frozen && --_frozen > 0) {
923 if (pending_changed) {
924 what_changed = pending_changed;
925 pending_changed = Change (0);
929 if (what_changed == Change (0)) {
933 if (what_changed & LengthChanged) {
934 if (what_changed & PositionChanged) {
935 recompute_at_start ();
940 StateChanged (what_changed);
944 Region::send_change (Change what_changed)
947 Glib::Mutex::Lock lm (lock);
949 pending_changed = Change (pending_changed|what_changed);
954 StateChanged (what_changed);
958 Region::set_last_layer_op (uint64_t when)
960 _last_layer_op = when;
964 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
966 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
970 Region::equivalent (boost::shared_ptr<const Region> other) const
972 return _start == other->_start &&
973 _position == other->_position &&
974 _length == other->_length;
978 Region::size_equivalent (boost::shared_ptr<const Region> other) const
980 return _start == other->_start &&
981 _length == other->_length;
985 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
987 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;