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>
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 */
59 pending_changed = Change (0);
63 _sync_position = _start;
67 _current_state_id = 0;
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 _current_state_id = 0;
94 _first_edit = EditChangesNothing;
98 Region::Region (boost::shared_ptr<const Region> other)
100 /* Pure copy constructor */
103 pending_changed = Change (0);
105 _read_data_count = 0;
107 _first_edit = EditChangesID;
108 other->_first_edit = EditChangesName;
110 if (other->_extra_xml) {
111 _extra_xml = new XMLNode (*other->_extra_xml);
116 _start = other->_start;
117 _sync_position = other->_sync_position;
118 _length = other->_length;
119 _name = other->_name;
120 _position = other->_position;
121 _layer = other->_layer;
122 _flags = Flag (other->_flags & ~Locked);
123 _current_state_id = 0;
124 _last_layer_op = other->_last_layer_op;
127 Region::Region (const XMLNode& node)
130 pending_changed = Change (0);
132 _read_data_count = 0;
134 _sync_position = _start;
136 _name = X_("error: XML did not reset this");
140 _current_state_id = 0;
141 _first_edit = EditChangesNothing;
143 if (set_state (node)) {
144 throw failed_constructor();
150 /* derived classes must call notify_callbacks() and then emit GoingAway */
154 Region::set_playlist (Playlist* pl)
160 Region::store_state (RegionState& state) const
162 state._start = _start;
163 state._length = _length;
164 state._position = _position;
165 state._flags = _flags;
166 state._sync_position = _sync_position;
167 state._layer = _layer;
169 state._first_edit = _first_edit;
173 Region::restore_and_return_flags (RegionState& state)
175 Change what_changed = Change (0);
178 Glib::Mutex::Lock lm (lock);
180 if (_start != state._start) {
181 what_changed = Change (what_changed|StartChanged);
182 _start = state._start;
184 if (_length != state._length) {
185 what_changed = Change (what_changed|LengthChanged);
186 _length = state._length;
188 if (_position != state._position) {
189 what_changed = Change (what_changed|PositionChanged);
190 _position = state._position;
192 if (_sync_position != state._sync_position) {
193 _sync_position = state._sync_position;
194 what_changed = Change (what_changed|SyncOffsetChanged);
196 if (_layer != state._layer) {
197 what_changed = Change (what_changed|LayerChanged);
198 _layer = state._layer;
201 uint32_t old_flags = _flags;
202 _flags = Flag (state._flags);
204 if ((old_flags ^ state._flags) & Muted) {
205 what_changed = Change (what_changed|MuteChanged);
207 if ((old_flags ^ state._flags) & Opaque) {
208 what_changed = Change (what_changed|OpacityChanged);
210 if ((old_flags ^ state._flags) & Locked) {
211 what_changed = Change (what_changed|LockChanged);
214 _first_edit = state._first_edit;
221 Region::set_name (string str)
226 send_change (NameChanged);
231 Region::set_length (nframes_t len, void *src)
233 if (_flags & Locked) {
237 if (_length != len && len != 0) {
239 /* check that the current _position wouldn't make the new
243 if (max_frames - len < _position) {
247 if (!verify_length (len)) {
253 _flags = Region::Flag (_flags & ~WholeFile);
262 snprintf (buf, sizeof (buf), "length set to %u", len);
266 send_change (LengthChanged);
271 Region::maybe_uncopy ()
276 Region::first_edit ()
278 if (_first_edit != EditChangesNothing && _playlist) {
280 _name = _playlist->session().new_region_name (_name);
281 _first_edit = EditChangesNothing;
283 send_change (NameChanged);
284 RegionFactory::CheckNewRegion (shared_from_this());
289 Region::move_to_natural_position (void *src)
295 boost::shared_ptr<Region> whole_file_region = get_parent();
297 if (whole_file_region) {
298 set_position (whole_file_region->position() + _start, src);
303 Region::special_set_position (nframes_t pos)
305 /* this is used when creating a whole file region as
306 a way to store its "natural" or "captured" position.
313 Region::set_position (nframes_t pos, void *src)
315 if (_flags & Locked) {
319 if (_position != pos) {
322 /* check that the new _position wouldn't make the current
323 length impossible - if so, change the length.
325 XXX is this the right thing to do?
328 if (max_frames - _length < _position) {
329 _length = max_frames - _position;
334 snprintf (buf, sizeof (buf), "position set to %u", pos);
339 /* do this even if the position is the same. this helps out
340 a GUI that has moved its representation already.
343 send_change (PositionChanged);
347 Region::set_position_on_top (nframes_t pos, void *src)
349 if (_flags & Locked) {
353 if (_position != pos) {
358 snprintf (buf, sizeof (buf), "position set to %u", pos);
363 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
365 /* do this even if the position is the same. this helps out
366 a GUI that has moved its representation already.
369 send_change (PositionChanged);
373 Region::nudge_position (long n, void *src)
375 if (_flags & Locked) {
384 if (_position > max_frames - n) {
385 _position = max_frames;
390 if (_position < (nframes_t) -n) {
399 snprintf (buf, sizeof (buf), "position set to %u", _position);
403 send_change (PositionChanged);
407 Region::set_start (nframes_t pos, void *src)
409 if (_flags & Locked) {
412 /* This just sets the start, nothing else. It effectively shifts
413 the contents of the Region within the overall extent of the Source,
414 without changing the Region's position or length
419 if (!verify_start (pos)) {
424 _flags = Region::Flag (_flags & ~WholeFile);
429 snprintf (buf, sizeof (buf), "start set to %u", pos);
433 send_change (StartChanged);
438 Region::trim_start (nframes_t new_position, void *src)
440 if (_flags & Locked) {
446 if (new_position > _position) {
447 start_shift = new_position - _position;
449 start_shift = -(_position - new_position);
452 if (start_shift > 0) {
454 if (_start > max_frames - start_shift) {
455 new_start = max_frames;
457 new_start = _start + start_shift;
460 if (!verify_start (new_start)) {
464 } else if (start_shift < 0) {
466 if (_start < (nframes_t) -start_shift) {
469 new_start = _start + start_shift;
475 if (new_start == _start) {
480 _flags = Region::Flag (_flags & ~WholeFile);
485 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
489 send_change (StartChanged);
493 Region::trim_front (nframes_t new_position, void *src)
495 if (_flags & Locked) {
499 nframes_t end = last_frame();
500 nframes_t source_zero;
502 if (_position > _start) {
503 source_zero = _position - _start;
505 source_zero = 0; // its actually negative, but this will work for us
508 if (new_position < end) { /* can't trim it zero or negative length */
512 /* can't trim it back passed where source position zero is located */
514 new_position = max (new_position, source_zero);
517 if (new_position > _position) {
518 newlen = _length - (new_position - _position);
520 newlen = _length + (_position - new_position);
523 trim_to_internal (new_position, newlen, src);
525 recompute_at_start ();
531 Region::trim_end (nframes_t new_endpoint, void *src)
533 if (_flags & Locked) {
537 if (new_endpoint > _position) {
538 trim_to_internal (_position, new_endpoint - _position, this);
546 Region::trim_to (nframes_t position, nframes_t length, void *src)
548 if (_flags & Locked) {
552 trim_to_internal (position, length, src);
555 recompute_at_start ();
561 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
566 if (_flags & Locked) {
570 if (position > _position) {
571 start_shift = position - _position;
573 start_shift = -(_position - position);
576 if (start_shift > 0) {
578 if (_start > max_frames - start_shift) {
579 new_start = max_frames;
581 new_start = _start + start_shift;
585 } else if (start_shift < 0) {
587 if (_start < (nframes_t) -start_shift) {
590 new_start = _start + start_shift;
596 if (!verify_start_and_length (new_start, length)) {
600 Change what_changed = Change (0);
602 if (_start != new_start) {
604 what_changed = Change (what_changed|StartChanged);
606 if (_length != length) {
608 what_changed = Change (what_changed|LengthChanged);
610 if (_position != position) {
611 _position = position;
612 what_changed = Change (what_changed|PositionChanged);
615 _flags = Region::Flag (_flags & ~WholeFile);
617 if (what_changed & (StartChanged|LengthChanged)) {
625 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
629 send_change (what_changed);
634 Region::set_hidden (bool yn)
636 if (hidden() != yn) {
639 _flags = Flag (_flags|Hidden);
641 _flags = Flag (_flags & ~Hidden);
644 send_change (HiddenChanged);
649 Region::set_muted (bool yn)
654 _flags = Flag (_flags|Muted);
656 _flags = Flag (_flags & ~Muted);
662 snprintf (buf, sizeof (buf), "muted");
664 snprintf (buf, sizeof (buf), "unmuted");
669 send_change (MuteChanged);
674 Region::set_opaque (bool yn)
676 if (opaque() != yn) {
680 snprintf (buf, sizeof (buf), "opaque");
681 _flags = Flag (_flags|Opaque);
683 snprintf (buf, sizeof (buf), "translucent");
684 _flags = Flag (_flags & ~Opaque);
688 send_change (OpacityChanged);
693 Region::set_locked (bool yn)
695 if (locked() != yn) {
699 snprintf (buf, sizeof (buf), "locked");
700 _flags = Flag (_flags|Locked);
702 snprintf (buf, sizeof (buf), "unlocked");
703 _flags = Flag (_flags & ~Locked);
707 send_change (LockChanged);
712 Region::set_sync_position (nframes_t absolute_pos)
716 file_pos = _start + (absolute_pos - _position);
718 if (file_pos != _sync_position) {
720 _sync_position = file_pos;
721 _flags = Flag (_flags|SyncMarked);
726 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
729 send_change (SyncOffsetChanged);
734 Region::clear_sync_position ()
736 if (_flags & SyncMarked) {
737 _flags = Flag (_flags & ~SyncMarked);
741 save_state ("sync point removed");
743 send_change (SyncOffsetChanged);
748 Region::sync_offset (int& dir) const
750 /* returns the sync point relative the first frame of the region */
752 if (_flags & SyncMarked) {
753 if (_sync_position > _start) {
755 return _sync_position - _start;
758 return _start - _sync_position;
767 Region::adjust_to_sync (nframes_t pos)
770 nframes_t offset = sync_offset (sync_dir);
773 if (max_frames - pos > offset) {
788 Region::sync_position() const
790 if (_flags & SyncMarked) {
791 return _sync_position;
801 if (_playlist == 0) {
805 _playlist->raise_region (boost::shared_ptr<Region>(this));
811 if (_playlist == 0) {
815 _playlist->lower_region (boost::shared_ptr<Region>(this));
819 Region::raise_to_top ()
822 if (_playlist == 0) {
826 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
830 Region::lower_to_bottom ()
832 if (_playlist == 0) {
836 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
840 Region::set_layer (layer_t l)
847 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
851 send_change (LayerChanged);
856 Region::state (bool full_state)
858 XMLNode *node = new XMLNode ("Region");
861 _id.print (buf, sizeof (buf));
862 node->add_property ("id", buf);
863 node->add_property ("name", _name);
864 snprintf (buf, sizeof (buf), "%u", _start);
865 node->add_property ("start", buf);
866 snprintf (buf, sizeof (buf), "%u", _length);
867 node->add_property ("length", buf);
868 snprintf (buf, sizeof (buf), "%u", _position);
869 node->add_property ("position", buf);
871 /* note: flags are stored by derived classes */
873 snprintf (buf, sizeof (buf), "%d", (int) _layer);
874 node->add_property ("layer", buf);
875 snprintf (buf, sizeof (buf), "%u", _sync_position);
876 node->add_property ("sync-position", buf);
888 Region::set_state (const XMLNode& node)
890 const XMLNodeList& nlist = node.children();
891 const XMLProperty *prop;
898 if ((prop = node.property ("id")) == 0) {
899 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
905 if ((prop = node.property ("name")) == 0) {
906 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
910 _name = prop->value();
912 if ((prop = node.property ("start")) != 0) {
913 sscanf (prop->value().c_str(), "%" PRIu32, &_start);
916 if ((prop = node.property ("length")) != 0) {
917 sscanf (prop->value().c_str(), "%" PRIu32, &_length);
920 if ((prop = node.property ("position")) != 0) {
921 sscanf (prop->value().c_str(), "%" PRIu32, &_position);
924 if ((prop = node.property ("layer")) != 0) {
925 _layer = (layer_t) atoi (prop->value().c_str());
928 /* note: derived classes set flags */
930 if ((prop = node.property ("sync-position")) != 0) {
931 sscanf (prop->value().c_str(), "%" PRIu32, &_sync_position);
933 _sync_position = _start;
936 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
942 if (child->name () == "extra") {
943 _extra_xml = new XMLNode (*child);
948 _first_edit = EditChangesNothing;
960 Region::thaw (const string& why)
962 Change what_changed = Change (0);
965 Glib::Mutex::Lock lm (lock);
967 if (_frozen && --_frozen > 0) {
971 if (pending_changed) {
972 what_changed = pending_changed;
973 pending_changed = Change (0);
977 if (what_changed == Change (0)) {
981 if (what_changed & LengthChanged) {
982 if (what_changed & PositionChanged) {
983 recompute_at_start ();
989 StateChanged (what_changed);
993 Region::send_change (Change what_changed)
996 Glib::Mutex::Lock lm (lock);
998 pending_changed = Change (pending_changed|what_changed);
1003 StateManager::send_state_changed (what_changed);
1007 Region::set_last_layer_op (uint64_t when)
1009 _last_layer_op = when;
1013 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1015 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1019 Region::equivalent (boost::shared_ptr<const Region> other) const
1021 return _start == other->_start &&
1022 _position == other->_position &&
1023 _length == other->_length;
1027 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1029 return _start == other->_start &&
1030 _length == other->_length;
1034 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1036 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;