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>
33 #include <ardour/region.h>
34 #include <ardour/playlist.h>
35 #include <ardour/session.h>
36 #include <ardour/region_factory.h>
41 using namespace ARDOUR;
44 Change Region::FadeChanged = ARDOUR::new_change ();
45 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
46 Change Region::MuteChanged = ARDOUR::new_change ();
47 Change Region::OpacityChanged = ARDOUR::new_change ();
48 Change Region::LockChanged = ARDOUR::new_change ();
49 Change Region::LayerChanged = ARDOUR::new_change ();
50 Change Region::HiddenChanged = ARDOUR::new_change ();
52 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
54 /* basic Region constructor */
60 pending_changed = Change (0);
64 _sync_position = _start;
69 _first_edit = EditChangesNothing;
73 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
75 /* create a new Region from part of an existing one */
78 pending_changed = Change (0);
82 _start = other->_start + offset;
83 if (other->_sync_position < offset) {
84 _sync_position = other->_sync_position;
86 _sync_position = _start;
92 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
93 _first_edit = EditChangesNothing;
97 Region::Region (boost::shared_ptr<const Region> other)
99 /* Pure copy constructor */
102 pending_changed = Change (0);
104 _read_data_count = 0;
106 _first_edit = EditChangesID;
107 other->_first_edit = EditChangesName;
109 if (other->_extra_xml) {
110 _extra_xml = new XMLNode (*other->_extra_xml);
115 _start = other->_start;
116 _sync_position = other->_sync_position;
117 _length = other->_length;
118 _name = other->_name;
119 _position = other->_position;
120 _layer = other->_layer;
121 _flags = Flag (other->_flags & ~Locked);
122 _last_layer_op = other->_last_layer_op;
125 Region::Region (const XMLNode& node)
128 pending_changed = Change (0);
130 _read_data_count = 0;
132 _sync_position = _start;
134 _name = X_("error: XML did not reset this");
138 _first_edit = EditChangesNothing;
140 if (set_state (node)) {
141 throw failed_constructor();
147 /* derived classes must call notify_callbacks() and then emit GoingAway */
151 Region::set_playlist (Playlist* pl)
157 Region::set_name (string str)
161 send_change (NameChanged);
166 Region::set_length (nframes_t len, void *src)
168 if (_flags & Locked) {
172 if (_length != len && len != 0) {
174 /* check that the current _position wouldn't make the new
178 if (max_frames - len < _position) {
182 if (!verify_length (len)) {
188 _flags = Region::Flag (_flags & ~WholeFile);
197 send_change (LengthChanged);
202 Region::maybe_uncopy ()
207 Region::first_edit ()
209 if (_first_edit != EditChangesNothing && _playlist) {
211 _name = _playlist->session().new_region_name (_name);
212 _first_edit = EditChangesNothing;
214 send_change (NameChanged);
215 RegionFactory::CheckNewRegion (shared_from_this());
220 Region::move_to_natural_position (void *src)
226 boost::shared_ptr<Region> whole_file_region = get_parent();
228 if (whole_file_region) {
229 set_position (whole_file_region->position() + _start, src);
234 Region::special_set_position (nframes_t pos)
236 /* this is used when creating a whole file region as
237 a way to store its "natural" or "captured" position.
244 Region::set_position (nframes_t pos, void *src)
246 if (_flags & Locked) {
250 if (_position != pos) {
253 /* check that the new _position wouldn't make the current
254 length impossible - if so, change the length.
256 XXX is this the right thing to do?
259 if (max_frames - _length < _position) {
260 _length = max_frames - _position;
264 /* do this even if the position is the same. this helps out
265 a GUI that has moved its representation already.
268 send_change (PositionChanged);
272 Region::set_position_on_top (nframes_t pos, void *src)
274 if (_flags & Locked) {
278 if (_position != pos) {
282 _playlist->raise_region_to_top (shared_from_this ());
284 /* do this even if the position is the same. this helps out
285 a GUI that has moved its representation already.
288 send_change (PositionChanged);
292 Region::nudge_position (long n, void *src)
294 if (_flags & Locked) {
303 if (_position > max_frames - n) {
304 _position = max_frames;
309 if (_position < (nframes_t) -n) {
316 send_change (PositionChanged);
320 Region::set_start (nframes_t pos, void *src)
322 if (_flags & Locked) {
325 /* This just sets the start, nothing else. It effectively shifts
326 the contents of the Region within the overall extent of the Source,
327 without changing the Region's position or length
332 if (!verify_start (pos)) {
337 _flags = Region::Flag (_flags & ~WholeFile);
340 send_change (StartChanged);
345 Region::trim_start (nframes_t new_position, void *src)
347 if (_flags & Locked) {
353 if (new_position > _position) {
354 start_shift = new_position - _position;
356 start_shift = -(_position - new_position);
359 if (start_shift > 0) {
361 if (_start > max_frames - start_shift) {
362 new_start = max_frames;
364 new_start = _start + start_shift;
367 if (!verify_start (new_start)) {
371 } else if (start_shift < 0) {
373 if (_start < (nframes_t) -start_shift) {
376 new_start = _start + start_shift;
382 if (new_start == _start) {
387 _flags = Region::Flag (_flags & ~WholeFile);
390 send_change (StartChanged);
394 Region::trim_front (nframes_t new_position, void *src)
396 if (_flags & Locked) {
400 nframes_t end = last_frame();
401 nframes_t source_zero;
403 if (_position > _start) {
404 source_zero = _position - _start;
406 source_zero = 0; // its actually negative, but this will work for us
409 if (new_position < end) { /* can't trim it zero or negative length */
413 /* can't trim it back passed where source position zero is located */
415 new_position = max (new_position, source_zero);
418 if (new_position > _position) {
419 newlen = _length - (new_position - _position);
421 newlen = _length + (_position - new_position);
424 trim_to_internal (new_position, newlen, src);
426 recompute_at_start ();
432 Region::trim_end (nframes_t new_endpoint, void *src)
434 if (_flags & Locked) {
438 if (new_endpoint > _position) {
439 trim_to_internal (_position, new_endpoint - _position, this);
447 Region::trim_to (nframes_t position, nframes_t length, void *src)
449 if (_flags & Locked) {
453 trim_to_internal (position, length, src);
456 recompute_at_start ();
462 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
467 if (_flags & Locked) {
471 if (position > _position) {
472 start_shift = position - _position;
474 start_shift = -(_position - position);
477 if (start_shift > 0) {
479 if (_start > max_frames - start_shift) {
480 new_start = max_frames;
482 new_start = _start + start_shift;
486 } else if (start_shift < 0) {
488 if (_start < (nframes_t) -start_shift) {
491 new_start = _start + start_shift;
497 if (!verify_start_and_length (new_start, length)) {
501 Change what_changed = Change (0);
503 if (_start != new_start) {
505 what_changed = Change (what_changed|StartChanged);
507 if (_length != length) {
509 what_changed = Change (what_changed|LengthChanged);
511 if (_position != position) {
512 _position = position;
513 what_changed = Change (what_changed|PositionChanged);
516 _flags = Region::Flag (_flags & ~WholeFile);
518 if (what_changed & (StartChanged|LengthChanged)) {
523 send_change (what_changed);
528 Region::set_hidden (bool yn)
530 if (hidden() != yn) {
533 _flags = Flag (_flags|Hidden);
535 _flags = Flag (_flags & ~Hidden);
538 send_change (HiddenChanged);
543 Region::set_muted (bool yn)
548 _flags = Flag (_flags|Muted);
550 _flags = Flag (_flags & ~Muted);
553 send_change (MuteChanged);
558 Region::set_opaque (bool yn)
560 if (opaque() != yn) {
562 _flags = Flag (_flags|Opaque);
564 _flags = Flag (_flags & ~Opaque);
566 send_change (OpacityChanged);
571 Region::set_locked (bool yn)
573 if (locked() != yn) {
575 _flags = Flag (_flags|Locked);
577 _flags = Flag (_flags & ~Locked);
579 send_change (LockChanged);
584 Region::set_sync_position (nframes_t absolute_pos)
588 file_pos = _start + (absolute_pos - _position);
590 if (file_pos != _sync_position) {
592 _sync_position = file_pos;
593 _flags = Flag (_flags|SyncMarked);
598 send_change (SyncOffsetChanged);
603 Region::clear_sync_position ()
605 if (_flags & SyncMarked) {
606 _flags = Flag (_flags & ~SyncMarked);
611 send_change (SyncOffsetChanged);
616 Region::sync_offset (int& dir) const
618 /* returns the sync point relative the first frame of the region */
620 if (_flags & SyncMarked) {
621 if (_sync_position > _start) {
623 return _sync_position - _start;
626 return _start - _sync_position;
635 Region::adjust_to_sync (nframes_t pos)
638 nframes_t offset = sync_offset (sync_dir);
641 if (max_frames - pos > offset) {
656 Region::sync_position() const
658 if (_flags & SyncMarked) {
659 return _sync_position;
669 if (_playlist == 0) {
673 _playlist->raise_region (shared_from_this ());
679 if (_playlist == 0) {
683 _playlist->lower_region (shared_from_this ());
687 Region::raise_to_top ()
690 if (_playlist == 0) {
694 _playlist->raise_region_to_top (shared_from_this());
698 Region::lower_to_bottom ()
700 if (_playlist == 0) {
704 _playlist->lower_region_to_bottom (shared_from_this());
708 Region::set_layer (layer_t l)
713 send_change (LayerChanged);
718 Region::state (bool full_state)
720 XMLNode *node = new XMLNode ("Region");
724 _id.print (buf, sizeof (buf));
725 node->add_property ("id", buf);
726 node->add_property ("name", _name);
727 snprintf (buf, sizeof (buf), "%u", _start);
728 node->add_property ("start", buf);
729 snprintf (buf, sizeof (buf), "%u", _length);
730 node->add_property ("length", buf);
731 snprintf (buf, sizeof (buf), "%u", _position);
732 node->add_property ("position", buf);
734 switch (_first_edit) {
735 case EditChangesNothing:
738 case EditChangesName:
746 node->add_property ("first_edit", fe);
748 /* note: flags are stored by derived classes */
750 snprintf (buf, sizeof (buf), "%d", (int) _layer);
751 node->add_property ("layer", buf);
752 snprintf (buf, sizeof (buf), "%u", _sync_position);
753 node->add_property ("sync-position", buf);
765 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
767 const XMLNodeList& nlist = node.children();
768 const XMLProperty *prop;
771 /* this is responsible for setting those aspects of Region state
772 that are mutable after construction.
775 if ((prop = node.property ("name")) == 0) {
776 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
780 _name = prop->value();
782 if ((prop = node.property ("start")) != 0) {
783 sscanf (prop->value().c_str(), "%" PRIu32, &val);
785 what_changed = Change (what_changed|StartChanged);
792 if ((prop = node.property ("length")) != 0) {
793 sscanf (prop->value().c_str(), "%" PRIu32, &val);
794 if (val != _length) {
795 what_changed = Change (what_changed|LengthChanged);
802 if ((prop = node.property ("position")) != 0) {
803 sscanf (prop->value().c_str(), "%" PRIu32, &val);
804 if (val != _position) {
805 what_changed = Change (what_changed|PositionChanged);
812 if ((prop = node.property ("layer")) != 0) {
814 x = (layer_t) atoi (prop->value().c_str());
816 what_changed = Change (what_changed|LayerChanged);
823 if ((prop = node.property ("sync-position")) != 0) {
824 sscanf (prop->value().c_str(), "%" PRIu32, &val);
825 if (val != _sync_position) {
826 what_changed = Change (what_changed|SyncOffsetChanged);
827 _sync_position = val;
830 _sync_position = _start;
833 /* XXX FIRST EDIT !!! */
835 /* note: derived classes set flags */
842 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
848 if (child->name () == "extra") {
849 _extra_xml = new XMLNode (*child);
855 send_change (what_changed);
862 Region::set_state (const XMLNode& node)
864 const XMLProperty *prop;
865 Change what_changed = Change (0);
867 /* ID is not allowed to change, ever */
869 if ((prop = node.property ("id")) == 0) {
870 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
876 _first_edit = EditChangesNothing;
878 set_live_state (node, what_changed, true);
890 Region::thaw (const string& why)
892 Change what_changed = Change (0);
895 Glib::Mutex::Lock lm (lock);
897 if (_frozen && --_frozen > 0) {
901 if (pending_changed) {
902 what_changed = pending_changed;
903 pending_changed = Change (0);
907 if (what_changed == Change (0)) {
911 if (what_changed & LengthChanged) {
912 if (what_changed & PositionChanged) {
913 recompute_at_start ();
918 StateChanged (what_changed);
922 Region::send_change (Change what_changed)
925 Glib::Mutex::Lock lm (lock);
927 pending_changed = Change (pending_changed|what_changed);
932 StateChanged (what_changed);
936 Region::set_last_layer_op (uint64_t when)
938 _last_layer_op = when;
942 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
944 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
948 Region::equivalent (boost::shared_ptr<const Region> other) const
950 return _start == other->_start &&
951 _position == other->_position &&
952 _length == other->_length;
956 Region::size_equivalent (boost::shared_ptr<const Region> other) const
958 return _start == other->_start &&
959 _length == other->_length;
963 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
965 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;