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::at_natural_position () const
226 boost::shared_ptr<Region> whole_file_region = get_parent();
228 if (whole_file_region) {
229 if (_position == whole_file_region->position() + _start) {
238 Region::move_to_natural_position (void *src)
244 boost::shared_ptr<Region> whole_file_region = get_parent();
246 if (whole_file_region) {
247 set_position (whole_file_region->position() + _start, src);
252 Region::special_set_position (nframes_t pos)
254 /* this is used when creating a whole file region as
255 a way to store its "natural" or "captured" position.
262 Region::set_position (nframes_t pos, void *src)
264 if (_flags & Locked) {
268 if (_position != pos) {
271 /* check that the new _position wouldn't make the current
272 length impossible - if so, change the length.
274 XXX is this the right thing to do?
277 if (max_frames - _length < _position) {
278 _length = max_frames - _position;
282 /* do this even if the position is the same. this helps out
283 a GUI that has moved its representation already.
286 send_change (PositionChanged);
290 Region::set_position_on_top (nframes_t pos, void *src)
292 if (_flags & Locked) {
296 if (_position != pos) {
300 _playlist->raise_region_to_top (shared_from_this ());
302 /* do this even if the position is the same. this helps out
303 a GUI that has moved its representation already.
306 send_change (PositionChanged);
310 Region::nudge_position (long n, void *src)
312 if (_flags & Locked) {
321 if (_position > max_frames - n) {
322 _position = max_frames;
327 if (_position < (nframes_t) -n) {
334 send_change (PositionChanged);
338 Region::set_start (nframes_t pos, void *src)
340 if (_flags & Locked) {
343 /* This just sets the start, nothing else. It effectively shifts
344 the contents of the Region within the overall extent of the Source,
345 without changing the Region's position or length
350 if (!verify_start (pos)) {
355 _flags = Region::Flag (_flags & ~WholeFile);
358 send_change (StartChanged);
363 Region::trim_start (nframes_t new_position, void *src)
365 if (_flags & Locked) {
371 if (new_position > _position) {
372 start_shift = new_position - _position;
374 start_shift = -(_position - new_position);
377 if (start_shift > 0) {
379 if (_start > max_frames - start_shift) {
380 new_start = max_frames;
382 new_start = _start + start_shift;
385 if (!verify_start (new_start)) {
389 } else if (start_shift < 0) {
391 if (_start < (nframes_t) -start_shift) {
394 new_start = _start + start_shift;
400 if (new_start == _start) {
405 _flags = Region::Flag (_flags & ~WholeFile);
408 send_change (StartChanged);
412 Region::trim_front (nframes_t new_position, void *src)
414 if (_flags & Locked) {
418 nframes_t end = last_frame();
419 nframes_t source_zero;
421 if (_position > _start) {
422 source_zero = _position - _start;
424 source_zero = 0; // its actually negative, but this will work for us
427 if (new_position < end) { /* can't trim it zero or negative length */
431 /* can't trim it back passed where source position zero is located */
433 new_position = max (new_position, source_zero);
436 if (new_position > _position) {
437 newlen = _length - (new_position - _position);
439 newlen = _length + (_position - new_position);
442 trim_to_internal (new_position, newlen, src);
444 recompute_at_start ();
450 Region::trim_end (nframes_t new_endpoint, void *src)
452 if (_flags & Locked) {
456 if (new_endpoint > _position) {
457 trim_to_internal (_position, new_endpoint - _position, this);
465 Region::trim_to (nframes_t position, nframes_t length, void *src)
467 if (_flags & Locked) {
471 trim_to_internal (position, length, src);
474 recompute_at_start ();
480 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
485 if (_flags & Locked) {
489 if (position > _position) {
490 start_shift = position - _position;
492 start_shift = -(_position - position);
495 if (start_shift > 0) {
497 if (_start > max_frames - start_shift) {
498 new_start = max_frames;
500 new_start = _start + start_shift;
504 } else if (start_shift < 0) {
506 if (_start < (nframes_t) -start_shift) {
509 new_start = _start + start_shift;
515 if (!verify_start_and_length (new_start, length)) {
519 Change what_changed = Change (0);
521 if (_start != new_start) {
523 what_changed = Change (what_changed|StartChanged);
525 if (_length != length) {
527 what_changed = Change (what_changed|LengthChanged);
529 if (_position != position) {
530 _position = position;
531 what_changed = Change (what_changed|PositionChanged);
534 _flags = Region::Flag (_flags & ~WholeFile);
536 if (what_changed & (StartChanged|LengthChanged)) {
541 send_change (what_changed);
546 Region::set_hidden (bool yn)
548 if (hidden() != yn) {
551 _flags = Flag (_flags|Hidden);
553 _flags = Flag (_flags & ~Hidden);
556 send_change (HiddenChanged);
561 Region::set_muted (bool yn)
566 _flags = Flag (_flags|Muted);
568 _flags = Flag (_flags & ~Muted);
571 send_change (MuteChanged);
576 Region::set_opaque (bool yn)
578 if (opaque() != yn) {
580 _flags = Flag (_flags|Opaque);
582 _flags = Flag (_flags & ~Opaque);
584 send_change (OpacityChanged);
589 Region::set_locked (bool yn)
591 if (locked() != yn) {
593 _flags = Flag (_flags|Locked);
595 _flags = Flag (_flags & ~Locked);
597 send_change (LockChanged);
602 Region::set_sync_position (nframes_t absolute_pos)
606 file_pos = _start + (absolute_pos - _position);
608 if (file_pos != _sync_position) {
610 _sync_position = file_pos;
611 _flags = Flag (_flags|SyncMarked);
616 send_change (SyncOffsetChanged);
621 Region::clear_sync_position ()
623 if (_flags & SyncMarked) {
624 _flags = Flag (_flags & ~SyncMarked);
629 send_change (SyncOffsetChanged);
634 Region::sync_offset (int& dir) const
636 /* returns the sync point relative the first frame of the region */
638 if (_flags & SyncMarked) {
639 if (_sync_position > _start) {
641 return _sync_position - _start;
644 return _start - _sync_position;
653 Region::adjust_to_sync (nframes_t pos)
656 nframes_t offset = sync_offset (sync_dir);
659 if (max_frames - pos > offset) {
674 Region::sync_position() const
676 if (_flags & SyncMarked) {
677 return _sync_position;
687 if (_playlist == 0) {
691 _playlist->raise_region (shared_from_this ());
697 if (_playlist == 0) {
701 _playlist->lower_region (shared_from_this ());
705 Region::raise_to_top ()
708 if (_playlist == 0) {
712 _playlist->raise_region_to_top (shared_from_this());
716 Region::lower_to_bottom ()
718 if (_playlist == 0) {
722 _playlist->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:
764 node->add_property ("first_edit", fe);
766 /* note: flags are stored by derived classes */
768 snprintf (buf, sizeof (buf), "%d", (int) _layer);
769 node->add_property ("layer", buf);
770 snprintf (buf, sizeof (buf), "%u", _sync_position);
771 node->add_property ("sync-position", buf);
783 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
785 const XMLNodeList& nlist = node.children();
786 const XMLProperty *prop;
789 /* this is responsible for setting those aspects of Region state
790 that are mutable after construction.
793 if ((prop = node.property ("name")) == 0) {
794 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
798 _name = prop->value();
800 if ((prop = node.property ("start")) != 0) {
801 sscanf (prop->value().c_str(), "%" PRIu32, &val);
803 what_changed = Change (what_changed|StartChanged);
810 if ((prop = node.property ("length")) != 0) {
811 sscanf (prop->value().c_str(), "%" PRIu32, &val);
812 if (val != _length) {
813 what_changed = Change (what_changed|LengthChanged);
820 if ((prop = node.property ("position")) != 0) {
821 sscanf (prop->value().c_str(), "%" PRIu32, &val);
822 if (val != _position) {
823 what_changed = Change (what_changed|PositionChanged);
830 if ((prop = node.property ("layer")) != 0) {
832 x = (layer_t) atoi (prop->value().c_str());
834 what_changed = Change (what_changed|LayerChanged);
841 if ((prop = node.property ("sync-position")) != 0) {
842 sscanf (prop->value().c_str(), "%" PRIu32, &val);
843 if (val != _sync_position) {
844 what_changed = Change (what_changed|SyncOffsetChanged);
845 _sync_position = val;
848 _sync_position = _start;
851 /* XXX FIRST EDIT !!! */
853 /* note: derived classes set flags */
860 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
866 if (child->name () == "extra") {
867 _extra_xml = new XMLNode (*child);
873 send_change (what_changed);
880 Region::set_state (const XMLNode& node)
882 const XMLProperty *prop;
883 Change what_changed = Change (0);
885 /* ID is not allowed to change, ever */
887 if ((prop = node.property ("id")) == 0) {
888 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
894 _first_edit = EditChangesNothing;
896 set_live_state (node, what_changed, true);
908 Region::thaw (const string& why)
910 Change what_changed = Change (0);
913 Glib::Mutex::Lock lm (lock);
915 if (_frozen && --_frozen > 0) {
919 if (pending_changed) {
920 what_changed = pending_changed;
921 pending_changed = Change (0);
925 if (what_changed == Change (0)) {
929 if (what_changed & LengthChanged) {
930 if (what_changed & PositionChanged) {
931 recompute_at_start ();
936 StateChanged (what_changed);
940 Region::send_change (Change what_changed)
943 Glib::Mutex::Lock lm (lock);
945 pending_changed = Change (pending_changed|what_changed);
950 StateChanged (what_changed);
954 Region::set_last_layer_op (uint64_t when)
956 _last_layer_op = when;
960 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
962 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
966 Region::equivalent (boost::shared_ptr<const Region> other) const
968 return _start == other->_start &&
969 _position == other->_position &&
970 _length == other->_length;
974 Region::size_equivalent (boost::shared_ptr<const Region> other) const
976 return _start == other->_start &&
977 _length == other->_length;
981 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
983 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;