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 (jack_nframes_t start, jack_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, jack_nframes_t offset, jack_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 (jack_nframes_t len, void *src)
235 if (_flags & Locked) {
239 if (_length != len && len != 0) {
241 if (!verify_length (len)) {
247 _flags = Region::Flag (_flags & ~WholeFile);
256 snprintf (buf, sizeof (buf), "length set to %u", len);
260 send_change (LengthChanged);
265 Region::maybe_uncopy ()
270 Region::first_edit ()
272 if (_first_edit != EditChangesNothing && _playlist) {
274 _name = _playlist->session().new_region_name (_name);
275 _first_edit = EditChangesNothing;
277 send_change (NameChanged);
278 RegionFactory::CheckNewRegion (shared_from_this());
283 Region::move_to_natural_position (void *src)
289 boost::shared_ptr<Region> whole_file_region = get_parent();
291 if (whole_file_region) {
292 set_position (whole_file_region->position() + _start, src);
297 Region::special_set_position (jack_nframes_t pos)
299 /* this is used when creating a whole file region as
300 a way to store its "natural" or "captured" position.
307 Region::set_position (jack_nframes_t pos, void *src)
309 if (_flags & Locked) {
313 if (_position != pos) {
318 snprintf (buf, sizeof (buf), "position set to %u", pos);
323 /* do this even if the position is the same. this helps out
324 a GUI that has moved its representation already.
327 send_change (PositionChanged);
331 Region::set_position_on_top (jack_nframes_t pos, void *src)
333 if (_flags & Locked) {
337 if (_position != pos) {
342 snprintf (buf, sizeof (buf), "position set to %u", pos);
347 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
349 /* do this even if the position is the same. this helps out
350 a GUI that has moved its representation already.
353 send_change (PositionChanged);
357 Region::nudge_position (long n, void *src)
359 if (_flags & Locked) {
368 if (_position > max_frames - n) {
369 _position = max_frames;
374 if (_position < (jack_nframes_t) -n) {
383 snprintf (buf, sizeof (buf), "position set to %u", _position);
387 send_change (PositionChanged);
391 Region::set_start (jack_nframes_t pos, void *src)
393 if (_flags & Locked) {
396 /* This just sets the start, nothing else. It effectively shifts
397 the contents of the Region within the overall extent of the Source,
398 without changing the Region's position or length
403 if (!verify_start (pos)) {
408 _flags = Region::Flag (_flags & ~WholeFile);
413 snprintf (buf, sizeof (buf), "start set to %u", pos);
417 send_change (StartChanged);
422 Region::trim_start (jack_nframes_t new_position, void *src)
424 if (_flags & Locked) {
427 jack_nframes_t new_start;
430 if (new_position > _position) {
431 start_shift = new_position - _position;
433 start_shift = -(_position - new_position);
436 if (start_shift > 0) {
438 if (_start > max_frames - start_shift) {
439 new_start = max_frames;
441 new_start = _start + start_shift;
444 if (!verify_start (new_start)) {
448 } else if (start_shift < 0) {
450 if (_start < (jack_nframes_t) -start_shift) {
453 new_start = _start + start_shift;
459 if (new_start == _start) {
464 _flags = Region::Flag (_flags & ~WholeFile);
469 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
473 send_change (StartChanged);
477 Region::trim_front (jack_nframes_t new_position, void *src)
479 if (_flags & Locked) {
483 jack_nframes_t end = _position + _length - 1;
484 jack_nframes_t source_zero;
486 if (_position > _start) {
487 source_zero = _position - _start;
489 source_zero = 0; // its actually negative, but this will work for us
492 if (new_position < end) { /* can't trim it zero or negative length */
494 jack_nframes_t newlen;
496 /* can't trim it back passed where source position zero is located */
498 new_position = max (new_position, source_zero);
501 if (new_position > _position) {
502 newlen = _length - (new_position - _position);
504 newlen = _length + (_position - new_position);
507 trim_to_internal (new_position, newlen, src);
509 recompute_at_start ();
515 Region::trim_end (jack_nframes_t new_endpoint, void *src)
517 if (_flags & Locked) {
521 if (new_endpoint > _position) {
522 trim_to_internal (_position, new_endpoint - _position, this);
530 Region::trim_to (jack_nframes_t position, jack_nframes_t length, void *src)
532 if (_flags & Locked) {
536 trim_to_internal (position, length, src);
539 recompute_at_start ();
545 Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void *src)
548 jack_nframes_t new_start;
550 if (_flags & Locked) {
554 if (position > _position) {
555 start_shift = position - _position;
557 start_shift = -(_position - position);
560 if (start_shift > 0) {
562 if (_start > max_frames - start_shift) {
563 new_start = max_frames;
565 new_start = _start + start_shift;
569 } else if (start_shift < 0) {
571 if (_start < (jack_nframes_t) -start_shift) {
574 new_start = _start + start_shift;
580 if (!verify_start_and_length (new_start, length)) {
584 Change what_changed = Change (0);
586 if (_start != new_start) {
588 what_changed = Change (what_changed|StartChanged);
590 if (_length != length) {
592 what_changed = Change (what_changed|LengthChanged);
594 if (_position != position) {
595 _position = position;
596 what_changed = Change (what_changed|PositionChanged);
599 _flags = Region::Flag (_flags & ~WholeFile);
601 if (what_changed & (StartChanged|LengthChanged)) {
609 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
613 send_change (what_changed);
618 Region::set_hidden (bool yn)
620 if (hidden() != yn) {
623 _flags = Flag (_flags|Hidden);
625 _flags = Flag (_flags & ~Hidden);
628 send_change (HiddenChanged);
633 Region::set_muted (bool yn)
638 _flags = Flag (_flags|Muted);
640 _flags = Flag (_flags & ~Muted);
646 snprintf (buf, sizeof (buf), "muted");
648 snprintf (buf, sizeof (buf), "unmuted");
653 send_change (MuteChanged);
658 Region::set_opaque (bool yn)
660 if (opaque() != yn) {
664 snprintf (buf, sizeof (buf), "opaque");
665 _flags = Flag (_flags|Opaque);
667 snprintf (buf, sizeof (buf), "translucent");
668 _flags = Flag (_flags & ~Opaque);
672 send_change (OpacityChanged);
677 Region::set_locked (bool yn)
679 if (locked() != yn) {
683 snprintf (buf, sizeof (buf), "locked");
684 _flags = Flag (_flags|Locked);
686 snprintf (buf, sizeof (buf), "unlocked");
687 _flags = Flag (_flags & ~Locked);
691 send_change (LockChanged);
696 Region::set_sync_position (jack_nframes_t absolute_pos)
698 jack_nframes_t file_pos;
700 file_pos = _start + (absolute_pos - _position);
702 if (file_pos != _sync_position) {
704 _sync_position = file_pos;
705 _flags = Flag (_flags|SyncMarked);
710 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
713 send_change (SyncOffsetChanged);
718 Region::clear_sync_position ()
720 if (_flags & SyncMarked) {
721 _flags = Flag (_flags & ~SyncMarked);
725 save_state ("sync point removed");
727 send_change (SyncOffsetChanged);
732 Region::sync_offset (int& dir) const
734 /* returns the sync point relative the first frame of the region */
736 if (_flags & SyncMarked) {
737 if (_sync_position > _start) {
739 return _sync_position - _start;
742 return _start - _sync_position;
751 Region::adjust_to_sync (jack_nframes_t pos)
754 jack_nframes_t offset = sync_offset (sync_dir);
757 if (max_frames - pos > offset) {
772 Region::sync_position() const
774 if (_flags & SyncMarked) {
775 return _sync_position;
785 if (_playlist == 0) {
789 _playlist->raise_region (boost::shared_ptr<Region>(this));
795 if (_playlist == 0) {
799 _playlist->lower_region (boost::shared_ptr<Region>(this));
803 Region::raise_to_top ()
806 if (_playlist == 0) {
810 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
814 Region::lower_to_bottom ()
816 if (_playlist == 0) {
820 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
824 Region::set_layer (layer_t l)
831 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
835 send_change (LayerChanged);
840 Region::state (bool full_state)
842 XMLNode *node = new XMLNode ("Region");
846 node->add_property ("id", buf);
847 node->add_property ("name", _name);
848 snprintf (buf, sizeof (buf), "%u", _start);
849 node->add_property ("start", buf);
850 snprintf (buf, sizeof (buf), "%u", _length);
851 node->add_property ("length", buf);
852 snprintf (buf, sizeof (buf), "%u", _position);
853 node->add_property ("position", buf);
855 /* note: flags are stored by derived classes */
857 snprintf (buf, sizeof (buf), "%d", (int) _layer);
858 node->add_property ("layer", buf);
859 snprintf (buf, sizeof (buf), "%u", _sync_position);
860 node->add_property ("sync-position", buf);
872 Region::set_state (const XMLNode& node)
874 const XMLNodeList& nlist = node.children();
875 const XMLProperty *prop;
882 if ((prop = node.property ("id")) == 0) {
883 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
889 if ((prop = node.property ("name")) == 0) {
890 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
894 _name = prop->value();
896 if ((prop = node.property ("start")) != 0) {
897 _start = (jack_nframes_t) atoi (prop->value().c_str());
900 if ((prop = node.property ("length")) != 0) {
901 _length = (jack_nframes_t) atoi (prop->value().c_str());
904 if ((prop = node.property ("position")) != 0) {
905 _position = (jack_nframes_t) atoi (prop->value().c_str());
908 if ((prop = node.property ("layer")) != 0) {
909 _layer = (layer_t) atoi (prop->value().c_str());
912 /* note: derived classes set flags */
914 if ((prop = node.property ("sync-position")) != 0) {
915 _sync_position = (jack_nframes_t) atoi (prop->value().c_str());
917 _sync_position = _start;
920 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
926 if (child->name () == "extra") {
927 _extra_xml = new XMLNode (*child);
932 _first_edit = EditChangesNothing;
944 Region::thaw (const string& why)
946 Change what_changed = Change (0);
949 Glib::Mutex::Lock lm (lock);
951 if (_frozen && --_frozen > 0) {
955 if (pending_changed) {
956 what_changed = pending_changed;
957 pending_changed = Change (0);
961 if (what_changed == Change (0)) {
965 if (what_changed & LengthChanged) {
966 if (what_changed & PositionChanged) {
967 recompute_at_start ();
973 StateChanged (what_changed);
977 Region::send_change (Change what_changed)
980 Glib::Mutex::Lock lm (lock);
982 pending_changed = Change (pending_changed|what_changed);
987 StateManager::send_state_changed (what_changed);
991 Region::set_last_layer_op (uint64_t when)
993 _last_layer_op = when;
997 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
999 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1003 Region::equivalent (boost::shared_ptr<const Region> other) const
1005 return _start == other->_start &&
1006 _position == other->_position &&
1007 _length == other->_length;
1011 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1013 return _start == other->_start &&
1014 _length == other->_length;
1018 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1020 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;