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 */
59 pending_changed = Change (0);
63 _sync_position = _start;
68 _first_edit = EditChangesNothing;
72 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
74 /* create a new Region from part of an existing one */
77 pending_changed = Change (0);
80 _start = other->_start + offset;
81 if (other->_sync_position < offset) {
82 _sync_position = other->_sync_position;
84 _sync_position = _start;
90 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
91 _first_edit = EditChangesNothing;
95 Region::Region (boost::shared_ptr<const Region> other)
97 /* Pure copy constructor */
100 pending_changed = Change (0);
101 _read_data_count = 0;
103 _first_edit = EditChangesID;
104 other->_first_edit = EditChangesName;
106 if (other->_extra_xml) {
107 _extra_xml = new XMLNode (*other->_extra_xml);
112 _start = other->_start;
113 _sync_position = other->_sync_position;
114 _length = other->_length;
115 _name = other->_name;
116 _position = other->_position;
117 _layer = other->_layer;
118 _flags = Flag (other->_flags & ~Locked);
119 _last_layer_op = other->_last_layer_op;
122 Region::Region (const XMLNode& node)
125 pending_changed = Change (0);
126 _read_data_count = 0;
128 _sync_position = _start;
130 _name = X_("error: XML did not reset this");
134 _first_edit = EditChangesNothing;
136 if (set_state (node)) {
137 throw failed_constructor();
143 /* derived classes must call notify_callbacks() and then emit GoingAway */
147 Region::set_playlist (boost::weak_ptr<Playlist> pl)
153 Region::set_name (string str)
157 send_change (NameChanged);
162 Region::set_length (nframes_t len, void *src)
164 if (_flags & Locked) {
168 if (_length != len && len != 0) {
170 /* check that the current _position wouldn't make the new
174 if (max_frames - len < _position) {
178 if (!verify_length (len)) {
184 _flags = Region::Flag (_flags & ~WholeFile);
193 send_change (LengthChanged);
198 Region::maybe_uncopy ()
203 Region::first_edit ()
205 boost::shared_ptr<Playlist> pl (playlist());
207 if (_first_edit != EditChangesNothing && pl) {
209 _name = pl->session().new_region_name (_name);
210 _first_edit = EditChangesNothing;
212 send_change (NameChanged);
213 RegionFactory::CheckNewRegion (shared_from_this());
218 Region::at_natural_position () const
220 boost::shared_ptr<Playlist> pl (playlist());
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)
240 boost::shared_ptr<Playlist> pl (playlist());
246 boost::shared_ptr<Region> whole_file_region = get_parent();
248 if (whole_file_region) {
249 set_position (whole_file_region->position() + _start, src);
254 Region::special_set_position (nframes_t pos)
256 /* this is used when creating a whole file region as
257 a way to store its "natural" or "captured" position.
264 Region::set_position (nframes_t pos, void *src)
266 if (_flags & Locked) {
270 if (_position != pos) {
273 /* check that the new _position wouldn't make the current
274 length impossible - if so, change the length.
276 XXX is this the right thing to do?
279 if (max_frames - _length < _position) {
280 _length = max_frames - _position;
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::set_position_on_top (nframes_t pos, void *src)
294 if (_flags & Locked) {
298 if (_position != pos) {
302 boost::shared_ptr<Playlist> pl (playlist());
305 pl->raise_region_to_top (shared_from_this ());
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::nudge_position (long n, void *src)
318 if (_flags & Locked) {
327 if (_position > max_frames - n) {
328 _position = max_frames;
333 if (_position < (nframes_t) -n) {
340 send_change (PositionChanged);
344 Region::set_start (nframes_t pos, void *src)
346 if (_flags & Locked) {
349 /* This just sets the start, nothing else. It effectively shifts
350 the contents of the Region within the overall extent of the Source,
351 without changing the Region's position or length
356 if (!verify_start (pos)) {
361 _flags = Region::Flag (_flags & ~WholeFile);
364 send_change (StartChanged);
369 Region::trim_start (nframes_t new_position, void *src)
371 if (_flags & Locked) {
377 if (new_position > _position) {
378 start_shift = new_position - _position;
380 start_shift = -(_position - new_position);
383 if (start_shift > 0) {
385 if (_start > max_frames - start_shift) {
386 new_start = max_frames;
388 new_start = _start + start_shift;
391 if (!verify_start (new_start)) {
395 } else if (start_shift < 0) {
397 if (_start < (nframes_t) -start_shift) {
400 new_start = _start + start_shift;
406 if (new_start == _start) {
411 _flags = Region::Flag (_flags & ~WholeFile);
414 send_change (StartChanged);
418 Region::trim_front (nframes_t new_position, void *src)
420 if (_flags & Locked) {
424 nframes_t end = last_frame();
425 nframes_t source_zero;
427 if (_position > _start) {
428 source_zero = _position - _start;
430 source_zero = 0; // its actually negative, but this will work for us
433 if (new_position < end) { /* can't trim it zero or negative length */
437 /* can't trim it back passed where source position zero is located */
439 new_position = max (new_position, source_zero);
442 if (new_position > _position) {
443 newlen = _length - (new_position - _position);
445 newlen = _length + (_position - new_position);
448 trim_to_internal (new_position, newlen, src);
450 recompute_at_start ();
456 Region::trim_end (nframes_t new_endpoint, void *src)
458 if (_flags & Locked) {
462 if (new_endpoint > _position) {
463 trim_to_internal (_position, new_endpoint - _position, this);
471 Region::trim_to (nframes_t position, nframes_t length, void *src)
473 if (_flags & Locked) {
477 trim_to_internal (position, length, src);
480 recompute_at_start ();
486 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
491 if (_flags & Locked) {
495 if (position > _position) {
496 start_shift = position - _position;
498 start_shift = -(_position - position);
501 if (start_shift > 0) {
503 if (_start > max_frames - start_shift) {
504 new_start = max_frames;
506 new_start = _start + start_shift;
510 } else if (start_shift < 0) {
512 if (_start < (nframes_t) -start_shift) {
515 new_start = _start + start_shift;
521 if (!verify_start_and_length (new_start, length)) {
525 Change what_changed = Change (0);
527 if (_start != new_start) {
529 what_changed = Change (what_changed|StartChanged);
531 if (_length != length) {
533 what_changed = Change (what_changed|LengthChanged);
535 if (_position != position) {
536 _position = position;
537 what_changed = Change (what_changed|PositionChanged);
540 _flags = Region::Flag (_flags & ~WholeFile);
542 if (what_changed & (StartChanged|LengthChanged)) {
547 send_change (what_changed);
552 Region::set_hidden (bool yn)
554 if (hidden() != yn) {
557 _flags = Flag (_flags|Hidden);
559 _flags = Flag (_flags & ~Hidden);
562 send_change (HiddenChanged);
567 Region::set_muted (bool yn)
572 _flags = Flag (_flags|Muted);
574 _flags = Flag (_flags & ~Muted);
577 send_change (MuteChanged);
582 Region::set_opaque (bool yn)
584 if (opaque() != yn) {
586 _flags = Flag (_flags|Opaque);
588 _flags = Flag (_flags & ~Opaque);
590 send_change (OpacityChanged);
595 Region::set_locked (bool yn)
597 if (locked() != yn) {
599 _flags = Flag (_flags|Locked);
601 _flags = Flag (_flags & ~Locked);
603 send_change (LockChanged);
608 Region::set_sync_position (nframes_t absolute_pos)
612 file_pos = _start + (absolute_pos - _position);
614 if (file_pos != _sync_position) {
616 _sync_position = file_pos;
617 _flags = Flag (_flags|SyncMarked);
622 send_change (SyncOffsetChanged);
627 Region::clear_sync_position ()
629 if (_flags & SyncMarked) {
630 _flags = Flag (_flags & ~SyncMarked);
635 send_change (SyncOffsetChanged);
640 Region::sync_offset (int& dir) const
642 /* returns the sync point relative the first frame of the region */
644 if (_flags & SyncMarked) {
645 if (_sync_position > _start) {
647 return _sync_position - _start;
650 return _start - _sync_position;
659 Region::adjust_to_sync (nframes_t pos)
662 nframes_t offset = sync_offset (sync_dir);
665 if (max_frames - pos > offset) {
680 Region::sync_position() const
682 if (_flags & SyncMarked) {
683 return _sync_position;
693 boost::shared_ptr<Playlist> pl (playlist());
695 pl->raise_region (shared_from_this ());
702 boost::shared_ptr<Playlist> pl (playlist());
704 pl->lower_region (shared_from_this ());
709 Region::raise_to_top ()
711 boost::shared_ptr<Playlist> pl (playlist());
713 pl->raise_region_to_top (shared_from_this());
718 Region::lower_to_bottom ()
720 boost::shared_ptr<Playlist> pl (playlist());
722 pl->lower_region_to_bottom (shared_from_this());
727 Region::set_layer (layer_t l)
732 send_change (LayerChanged);
737 Region::state (bool full_state)
739 XMLNode *node = new XMLNode ("Region");
743 _id.print (buf, sizeof (buf));
744 node->add_property ("id", buf);
745 node->add_property ("name", _name);
746 snprintf (buf, sizeof (buf), "%u", _start);
747 node->add_property ("start", buf);
748 snprintf (buf, sizeof (buf), "%u", _length);
749 node->add_property ("length", buf);
750 snprintf (buf, sizeof (buf), "%u", _position);
751 node->add_property ("position", buf);
753 switch (_first_edit) {
754 case EditChangesNothing:
757 case EditChangesName:
765 node->add_property ("first_edit", fe);
767 /* note: flags are stored by derived classes */
769 snprintf (buf, sizeof (buf), "%d", (int) _layer);
770 node->add_property ("layer", buf);
771 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
772 node->add_property ("sync-position", buf);
784 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
786 const XMLNodeList& nlist = node.children();
787 const XMLProperty *prop;
790 /* this is responsible for setting those aspects of Region state
791 that are mutable after construction.
794 if ((prop = node.property ("name")) == 0) {
795 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
799 _name = prop->value();
801 if ((prop = node.property ("start")) != 0) {
802 sscanf (prop->value().c_str(), "%" PRIu32, &val);
804 what_changed = Change (what_changed|StartChanged);
811 if ((prop = node.property ("length")) != 0) {
812 sscanf (prop->value().c_str(), "%" PRIu32, &val);
813 if (val != _length) {
814 what_changed = Change (what_changed|LengthChanged);
821 if ((prop = node.property ("position")) != 0) {
822 sscanf (prop->value().c_str(), "%" PRIu32, &val);
823 if (val != _position) {
824 what_changed = Change (what_changed|PositionChanged);
831 if ((prop = node.property ("layer")) != 0) {
833 x = (layer_t) atoi (prop->value().c_str());
835 what_changed = Change (what_changed|LayerChanged);
842 if ((prop = node.property ("sync-position")) != 0) {
843 sscanf (prop->value().c_str(), "%" PRIu32, &val);
844 if (val != _sync_position) {
845 what_changed = Change (what_changed|SyncOffsetChanged);
846 _sync_position = val;
849 _sync_position = _start;
852 /* XXX FIRST EDIT !!! */
854 /* note: derived classes set flags */
861 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
867 if (child->name () == "extra") {
868 _extra_xml = new XMLNode (*child);
874 send_change (what_changed);
881 Region::set_state (const XMLNode& node)
883 const XMLProperty *prop;
884 Change what_changed = Change (0);
886 /* ID is not allowed to change, ever */
888 if ((prop = node.property ("id")) == 0) {
889 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
895 _first_edit = EditChangesNothing;
897 set_live_state (node, what_changed, true);
909 Region::thaw (const string& why)
911 Change what_changed = Change (0);
914 Glib::Mutex::Lock lm (lock);
916 if (_frozen && --_frozen > 0) {
920 if (pending_changed) {
921 what_changed = pending_changed;
922 pending_changed = Change (0);
926 if (what_changed == Change (0)) {
930 if (what_changed & LengthChanged) {
931 if (what_changed & PositionChanged) {
932 recompute_at_start ();
937 StateChanged (what_changed);
941 Region::send_change (Change what_changed)
944 Glib::Mutex::Lock lm (lock);
946 pending_changed = Change (pending_changed|what_changed);
951 StateChanged (what_changed);
955 Region::set_last_layer_op (uint64_t when)
957 _last_layer_op = when;
961 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
963 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
967 Region::equivalent (boost::shared_ptr<const Region> other) const
969 return _start == other->_start &&
970 _position == other->_position &&
971 _length == other->_length;
975 Region::size_equivalent (boost::shared_ptr<const Region> other) const
977 return _start == other->_start &&
978 _length == other->_length;
982 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
984 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;