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();
152 /* derived classes must emit GoingAway */
156 Region::set_playlist (Playlist* pl)
162 Region::store_state (RegionState& state) const
164 state._start = _start;
165 state._length = _length;
166 state._position = _position;
167 state._flags = _flags;
168 state._sync_position = _sync_position;
169 state._layer = _layer;
171 state._first_edit = _first_edit;
175 Region::restore_and_return_flags (RegionState& state)
177 Change what_changed = Change (0);
180 Glib::Mutex::Lock lm (lock);
182 if (_start != state._start) {
183 what_changed = Change (what_changed|StartChanged);
184 _start = state._start;
186 if (_length != state._length) {
187 what_changed = Change (what_changed|LengthChanged);
188 _length = state._length;
190 if (_position != state._position) {
191 what_changed = Change (what_changed|PositionChanged);
192 _position = state._position;
194 if (_sync_position != state._sync_position) {
195 _sync_position = state._sync_position;
196 what_changed = Change (what_changed|SyncOffsetChanged);
198 if (_layer != state._layer) {
199 what_changed = Change (what_changed|LayerChanged);
200 _layer = state._layer;
203 uint32_t old_flags = _flags;
204 _flags = Flag (state._flags);
206 if ((old_flags ^ state._flags) & Muted) {
207 what_changed = Change (what_changed|MuteChanged);
209 if ((old_flags ^ state._flags) & Opaque) {
210 what_changed = Change (what_changed|OpacityChanged);
212 if ((old_flags ^ state._flags) & Locked) {
213 what_changed = Change (what_changed|LockChanged);
216 _first_edit = state._first_edit;
223 Region::set_name (string str)
228 send_change (NameChanged);
233 Region::set_length (nframes_t len, void *src)
235 if (_flags & Locked) {
239 if (_length != len && len != 0) {
241 /* check that the current _position wouldn't make the new
245 if (max_frames - len < _position) {
249 if (!verify_length (len)) {
255 _flags = Region::Flag (_flags & ~WholeFile);
264 snprintf (buf, sizeof (buf), "length set to %u", len);
268 send_change (LengthChanged);
273 Region::maybe_uncopy ()
278 Region::first_edit ()
280 if (_first_edit != EditChangesNothing && _playlist) {
282 _name = _playlist->session().new_region_name (_name);
283 _first_edit = EditChangesNothing;
285 send_change (NameChanged);
286 RegionFactory::CheckNewRegion (shared_from_this());
291 Region::move_to_natural_position (void *src)
297 boost::shared_ptr<Region> whole_file_region = get_parent();
299 if (whole_file_region) {
300 set_position (whole_file_region->position() + _start, src);
305 Region::special_set_position (nframes_t pos)
307 /* this is used when creating a whole file region as
308 a way to store its "natural" or "captured" position.
315 Region::set_position (nframes_t pos, void *src)
317 if (_flags & Locked) {
321 if (_position != pos) {
324 /* check that the new _position wouldn't make the current
325 length impossible - if so, change the length.
327 XXX is this the right thing to do?
330 if (max_frames - _length < _position) {
331 _length = max_frames - _position;
336 snprintf (buf, sizeof (buf), "position set to %u", pos);
341 /* do this even if the position is the same. this helps out
342 a GUI that has moved its representation already.
345 send_change (PositionChanged);
349 Region::set_position_on_top (nframes_t pos, void *src)
351 if (_flags & Locked) {
355 if (_position != pos) {
360 snprintf (buf, sizeof (buf), "position set to %u", pos);
365 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
367 /* do this even if the position is the same. this helps out
368 a GUI that has moved its representation already.
371 send_change (PositionChanged);
375 Region::nudge_position (long n, void *src)
377 if (_flags & Locked) {
386 if (_position > max_frames - n) {
387 _position = max_frames;
392 if (_position < (nframes_t) -n) {
401 snprintf (buf, sizeof (buf), "position set to %u", _position);
405 send_change (PositionChanged);
409 Region::set_start (nframes_t pos, void *src)
411 if (_flags & Locked) {
414 /* This just sets the start, nothing else. It effectively shifts
415 the contents of the Region within the overall extent of the Source,
416 without changing the Region's position or length
421 if (!verify_start (pos)) {
426 _flags = Region::Flag (_flags & ~WholeFile);
431 snprintf (buf, sizeof (buf), "start set to %u", pos);
435 send_change (StartChanged);
440 Region::trim_start (nframes_t new_position, void *src)
442 if (_flags & Locked) {
448 if (new_position > _position) {
449 start_shift = new_position - _position;
451 start_shift = -(_position - new_position);
454 if (start_shift > 0) {
456 if (_start > max_frames - start_shift) {
457 new_start = max_frames;
459 new_start = _start + start_shift;
462 if (!verify_start (new_start)) {
466 } else if (start_shift < 0) {
468 if (_start < (nframes_t) -start_shift) {
471 new_start = _start + start_shift;
477 if (new_start == _start) {
482 _flags = Region::Flag (_flags & ~WholeFile);
487 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
491 send_change (StartChanged);
495 Region::trim_front (nframes_t new_position, void *src)
497 if (_flags & Locked) {
501 nframes_t end = last_frame();
502 nframes_t source_zero;
504 if (_position > _start) {
505 source_zero = _position - _start;
507 source_zero = 0; // its actually negative, but this will work for us
510 if (new_position < end) { /* can't trim it zero or negative length */
514 /* can't trim it back passed where source position zero is located */
516 new_position = max (new_position, source_zero);
519 if (new_position > _position) {
520 newlen = _length - (new_position - _position);
522 newlen = _length + (_position - new_position);
525 trim_to_internal (new_position, newlen, src);
527 recompute_at_start ();
533 Region::trim_end (nframes_t new_endpoint, void *src)
535 if (_flags & Locked) {
539 if (new_endpoint > _position) {
540 trim_to_internal (_position, new_endpoint - _position, this);
548 Region::trim_to (nframes_t position, nframes_t length, void *src)
550 if (_flags & Locked) {
554 trim_to_internal (position, length, src);
557 recompute_at_start ();
563 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
568 if (_flags & Locked) {
572 if (position > _position) {
573 start_shift = position - _position;
575 start_shift = -(_position - position);
578 if (start_shift > 0) {
580 if (_start > max_frames - start_shift) {
581 new_start = max_frames;
583 new_start = _start + start_shift;
587 } else if (start_shift < 0) {
589 if (_start < (nframes_t) -start_shift) {
592 new_start = _start + start_shift;
598 if (!verify_start_and_length (new_start, length)) {
602 Change what_changed = Change (0);
604 if (_start != new_start) {
606 what_changed = Change (what_changed|StartChanged);
608 if (_length != length) {
610 what_changed = Change (what_changed|LengthChanged);
612 if (_position != position) {
613 _position = position;
614 what_changed = Change (what_changed|PositionChanged);
617 _flags = Region::Flag (_flags & ~WholeFile);
619 if (what_changed & (StartChanged|LengthChanged)) {
627 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
631 send_change (what_changed);
636 Region::set_hidden (bool yn)
638 if (hidden() != yn) {
641 _flags = Flag (_flags|Hidden);
643 _flags = Flag (_flags & ~Hidden);
646 send_change (HiddenChanged);
651 Region::set_muted (bool yn)
656 _flags = Flag (_flags|Muted);
658 _flags = Flag (_flags & ~Muted);
664 snprintf (buf, sizeof (buf), "muted");
666 snprintf (buf, sizeof (buf), "unmuted");
671 send_change (MuteChanged);
676 Region::set_opaque (bool yn)
678 if (opaque() != yn) {
682 snprintf (buf, sizeof (buf), "opaque");
683 _flags = Flag (_flags|Opaque);
685 snprintf (buf, sizeof (buf), "translucent");
686 _flags = Flag (_flags & ~Opaque);
690 send_change (OpacityChanged);
695 Region::set_locked (bool yn)
697 if (locked() != yn) {
701 snprintf (buf, sizeof (buf), "locked");
702 _flags = Flag (_flags|Locked);
704 snprintf (buf, sizeof (buf), "unlocked");
705 _flags = Flag (_flags & ~Locked);
709 send_change (LockChanged);
714 Region::set_sync_position (nframes_t absolute_pos)
718 file_pos = _start + (absolute_pos - _position);
720 if (file_pos != _sync_position) {
722 _sync_position = file_pos;
723 _flags = Flag (_flags|SyncMarked);
728 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
731 send_change (SyncOffsetChanged);
736 Region::clear_sync_position ()
738 if (_flags & SyncMarked) {
739 _flags = Flag (_flags & ~SyncMarked);
743 save_state ("sync point removed");
745 send_change (SyncOffsetChanged);
750 Region::sync_offset (int& dir) const
752 /* returns the sync point relative the first frame of the region */
754 if (_flags & SyncMarked) {
755 if (_sync_position > _start) {
757 return _sync_position - _start;
760 return _start - _sync_position;
769 Region::adjust_to_sync (nframes_t pos)
772 nframes_t offset = sync_offset (sync_dir);
775 if (max_frames - pos > offset) {
790 Region::sync_position() const
792 if (_flags & SyncMarked) {
793 return _sync_position;
803 if (_playlist == 0) {
807 _playlist->raise_region (boost::shared_ptr<Region>(this));
813 if (_playlist == 0) {
817 _playlist->lower_region (boost::shared_ptr<Region>(this));
821 Region::raise_to_top ()
824 if (_playlist == 0) {
828 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
832 Region::lower_to_bottom ()
834 if (_playlist == 0) {
838 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
842 Region::set_layer (layer_t l)
849 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
853 send_change (LayerChanged);
858 Region::state (bool full_state)
860 XMLNode *node = new XMLNode ("Region");
864 node->add_property ("id", buf);
865 node->add_property ("name", _name);
866 snprintf (buf, sizeof (buf), "%u", _start);
867 node->add_property ("start", buf);
868 snprintf (buf, sizeof (buf), "%u", _length);
869 node->add_property ("length", buf);
870 snprintf (buf, sizeof (buf), "%u", _position);
871 node->add_property ("position", buf);
873 /* note: flags are stored by derived classes */
875 snprintf (buf, sizeof (buf), "%d", (int) _layer);
876 node->add_property ("layer", buf);
877 snprintf (buf, sizeof (buf), "%u", _sync_position);
878 node->add_property ("sync-position", buf);
890 Region::set_state (const XMLNode& node)
892 const XMLNodeList& nlist = node.children();
893 const XMLProperty *prop;
900 if ((prop = node.property ("id")) == 0) {
901 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
907 if ((prop = node.property ("name")) == 0) {
908 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
912 _name = prop->value();
914 if ((prop = node.property ("start")) != 0) {
915 sscanf (prop->value().c_str(), "%" PRIu32, &_start);
918 if ((prop = node.property ("length")) != 0) {
919 sscanf (prop->value().c_str(), "%" PRIu32, &_length);
922 if ((prop = node.property ("position")) != 0) {
923 sscanf (prop->value().c_str(), "%" PRIu32, &_position);
926 if ((prop = node.property ("layer")) != 0) {
927 _layer = (layer_t) atoi (prop->value().c_str());
930 /* note: derived classes set flags */
932 if ((prop = node.property ("sync-position")) != 0) {
933 sscanf (prop->value().c_str(), "%" PRIu32, &_sync_position);
935 _sync_position = _start;
938 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
944 if (child->name () == "extra") {
945 _extra_xml = new XMLNode (*child);
950 _first_edit = EditChangesNothing;
962 Region::thaw (const string& why)
964 Change what_changed = Change (0);
967 Glib::Mutex::Lock lm (lock);
969 if (_frozen && --_frozen > 0) {
973 if (pending_changed) {
974 what_changed = pending_changed;
975 pending_changed = Change (0);
979 if (what_changed == Change (0)) {
983 if (what_changed & LengthChanged) {
984 if (what_changed & PositionChanged) {
985 recompute_at_start ();
991 StateChanged (what_changed);
995 Region::send_change (Change what_changed)
998 Glib::Mutex::Lock lm (lock);
1000 pending_changed = Change (pending_changed|what_changed);
1005 StateManager::send_state_changed (what_changed);
1009 Region::set_last_layer_op (uint64_t when)
1011 _last_layer_op = when;
1015 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1017 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1021 Region::equivalent (boost::shared_ptr<const Region> other) const
1023 return _start == other->_start &&
1024 _position == other->_position &&
1025 _length == other->_length;
1029 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1031 return _start == other->_start &&
1032 _length == other->_length;
1036 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1038 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;