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/source.h>
37 #include <ardour/region_factory.h>
42 using namespace ARDOUR;
45 Change Region::FadeChanged = ARDOUR::new_change ();
46 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
47 Change Region::MuteChanged = ARDOUR::new_change ();
48 Change Region::OpacityChanged = ARDOUR::new_change ();
49 Change Region::LockChanged = ARDOUR::new_change ();
50 Change Region::LayerChanged = ARDOUR::new_change ();
51 Change Region::HiddenChanged = ARDOUR::new_change ();
53 /** Basic Region constructor (single source) */
54 Region::Region (boost::shared_ptr<Source> src, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
61 , _sync_position(_start)
63 , _first_edit(EditChangesNothing)
66 , _pending_changed(Change (0))
70 _sources.push_back (src);
71 _master_sources.push_back (src);
72 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
74 assert(_sources.size() > 0);
77 /** Basic Region constructor (many sources) */
78 Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
85 , _sync_position(_start)
87 , _first_edit(EditChangesNothing)
90 , _pending_changed(Change (0))
95 set<boost::shared_ptr<Source> > unique_srcs;
97 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
98 _sources.push_back (*i);
99 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
100 unique_srcs.insert (*i);
103 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
104 _master_sources.push_back (*i);
105 if (unique_srcs.find (*i) == unique_srcs.end()) {
106 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
110 assert(_sources.size() > 0);
113 /** Create a new Region from part of an existing one */
114 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
116 , _type(other->data_type())
117 , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
118 , _start(other->_start + offset)
121 , _sync_position(_start)
123 , _first_edit(EditChangesNothing)
125 , _read_data_count(0)
126 , _pending_changed(Change (0))
130 if (other->_sync_position < offset)
131 _sync_position = other->_sync_position;
133 set<boost::shared_ptr<Source> > unique_srcs;
135 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
136 _sources.push_back (*i);
137 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
138 unique_srcs.insert (*i);
141 if (other->_sync_position < offset) {
142 _sync_position = other->_sync_position;
145 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
146 if (unique_srcs.find (*i) == unique_srcs.end()) {
147 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
149 _master_sources.push_back (*i);
152 assert(_sources.size() > 0);
155 /** Pure copy constructor */
156 Region::Region (boost::shared_ptr<const Region> other)
157 : _name(other->_name)
158 , _type(other->data_type())
159 , _flags(Flag(other->_flags & ~Locked))
160 , _start(other->_start)
161 , _length(other->_length)
162 , _position(other->_position)
163 , _sync_position(other->_sync_position)
164 , _layer(other->_layer)
165 , _first_edit(EditChangesID)
167 , _read_data_count(0)
168 , _pending_changed(Change(0))
169 , _last_layer_op(other->_last_layer_op)
172 other->_first_edit = EditChangesName;
174 if (other->_extra_xml) {
175 _extra_xml = new XMLNode (*other->_extra_xml);
180 set<boost::shared_ptr<Source> > unique_srcs;
182 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
183 _sources.push_back (*i);
184 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
185 unique_srcs.insert (*i);
188 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
189 _master_sources.push_back (*i);
190 if (unique_srcs.find (*i) == unique_srcs.end()) {
191 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
195 assert(_sources.size() > 0);
198 Region::Region (SourceList& srcs, const XMLNode& node)
199 : _name(X_("error: XML did not reset this"))
200 , _type(DataType::NIL) // to be loaded from XML
205 , _sync_position(_start)
207 , _first_edit(EditChangesNothing)
209 , _read_data_count(0)
210 , _pending_changed(Change(0))
216 set<boost::shared_ptr<Source> > unique_srcs;
218 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
219 _sources.push_back (*i);
220 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
221 unique_srcs.insert (*i);
224 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
225 _master_sources.push_back (*i);
226 if (unique_srcs.find (*i) == unique_srcs.end()) {
227 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
231 if (set_state (node)) {
232 throw failed_constructor();
235 assert(_type != DataType::NIL);
236 assert(_sources.size() > 0);
239 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
240 : _name(X_("error: XML did not reset this"))
241 , _type(DataType::NIL)
246 , _sync_position(_start)
248 , _first_edit(EditChangesNothing)
250 , _read_data_count(0)
251 , _pending_changed(Change(0))
255 _sources.push_back (src);
258 if (set_state (node)) {
259 throw failed_constructor();
262 assert(_type != DataType::NIL);
263 assert(_sources.size() > 0);
269 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
270 (*i)->remove_playlist (_playlist);
275 GoingAway (); /* EMIT SIGNAL */
279 Region::set_playlist (Playlist* pl)
281 if (pl == _playlist) {
285 Playlist* old_playlist = _playlist;
289 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
290 (*i)->remove_playlist (old_playlist);
291 (*i)->add_playlist (_playlist);
294 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
295 (*i)->add_playlist (_playlist);
300 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
301 (*i)->remove_playlist (old_playlist);
308 Region::set_name (string str)
312 send_change (NameChanged);
317 Region::set_length (nframes_t len, void *src)
319 if (_flags & Locked) {
323 if (_length != len && len != 0) {
325 /* check that the current _position wouldn't make the new
329 if (max_frames - len < _position) {
333 if (!verify_length (len)) {
339 _flags = Region::Flag (_flags & ~WholeFile);
348 send_change (LengthChanged);
353 Region::maybe_uncopy ()
358 Region::first_edit ()
360 if (_first_edit != EditChangesNothing && _playlist) {
362 _name = _playlist->session().new_region_name (_name);
363 _first_edit = EditChangesNothing;
365 send_change (NameChanged);
366 RegionFactory::CheckNewRegion (shared_from_this());
371 Region::at_natural_position () const
377 boost::shared_ptr<Region> whole_file_region = get_parent();
379 if (whole_file_region) {
380 if (_position == whole_file_region->position() + _start) {
389 Region::move_to_natural_position (void *src)
395 boost::shared_ptr<Region> whole_file_region = get_parent();
397 if (whole_file_region) {
398 set_position (whole_file_region->position() + _start, src);
403 Region::special_set_position (nframes_t pos)
405 /* this is used when creating a whole file region as
406 a way to store its "natural" or "captured" position.
413 Region::set_position (nframes_t pos, void *src)
415 if (_flags & Locked) {
419 if (_position != pos) {
422 /* check that the new _position wouldn't make the current
423 length impossible - if so, change the length.
425 XXX is this the right thing to do?
428 if (max_frames - _length < _position) {
429 _length = max_frames - _position;
433 /* do this even if the position is the same. this helps out
434 a GUI that has moved its representation already.
437 send_change (PositionChanged);
441 Region::set_position_on_top (nframes_t pos, void *src)
443 if (_flags & Locked) {
447 if (_position != pos) {
451 _playlist->raise_region_to_top (shared_from_this ());
453 /* do this even if the position is the same. this helps out
454 a GUI that has moved its representation already.
457 send_change (PositionChanged);
461 Region::nudge_position (long n, void *src)
463 if (_flags & Locked) {
472 if (_position > max_frames - n) {
473 _position = max_frames;
478 if (_position < (nframes_t) -n) {
485 send_change (PositionChanged);
489 Region::set_start (nframes_t pos, void *src)
491 if (_flags & Locked) {
494 /* This just sets the start, nothing else. It effectively shifts
495 the contents of the Region within the overall extent of the Source,
496 without changing the Region's position or length
501 if (!verify_start (pos)) {
506 _flags = Region::Flag (_flags & ~WholeFile);
509 send_change (StartChanged);
514 Region::trim_start (nframes_t new_position, void *src)
516 if (_flags & Locked) {
522 if (new_position > _position) {
523 start_shift = new_position - _position;
525 start_shift = -(_position - new_position);
528 if (start_shift > 0) {
530 if (_start > max_frames - start_shift) {
531 new_start = max_frames;
533 new_start = _start + start_shift;
536 if (!verify_start (new_start)) {
540 } else if (start_shift < 0) {
542 if (_start < (nframes_t) -start_shift) {
545 new_start = _start + start_shift;
551 if (new_start == _start) {
556 _flags = Region::Flag (_flags & ~WholeFile);
559 send_change (StartChanged);
563 Region::trim_front (nframes_t new_position, void *src)
565 if (_flags & Locked) {
569 nframes_t end = last_frame();
570 nframes_t source_zero;
572 if (_position > _start) {
573 source_zero = _position - _start;
575 source_zero = 0; // its actually negative, but this will work for us
578 if (new_position < end) { /* can't trim it zero or negative length */
582 /* can't trim it back passed where source position zero is located */
584 new_position = max (new_position, source_zero);
587 if (new_position > _position) {
588 newlen = _length - (new_position - _position);
590 newlen = _length + (_position - new_position);
593 trim_to_internal (new_position, newlen, src);
595 recompute_at_start ();
601 Region::trim_end (nframes_t new_endpoint, void *src)
603 if (_flags & Locked) {
607 if (new_endpoint > _position) {
608 trim_to_internal (_position, new_endpoint - _position, this);
616 Region::trim_to (nframes_t position, nframes_t length, void *src)
618 if (_flags & Locked) {
622 trim_to_internal (position, length, src);
625 recompute_at_start ();
631 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
636 if (_flags & Locked) {
640 if (position > _position) {
641 start_shift = position - _position;
643 start_shift = -(_position - position);
646 if (start_shift > 0) {
648 if (_start > max_frames - start_shift) {
649 new_start = max_frames;
651 new_start = _start + start_shift;
655 } else if (start_shift < 0) {
657 if (_start < (nframes_t) -start_shift) {
660 new_start = _start + start_shift;
666 if (!verify_start_and_length (new_start, length)) {
670 Change what_changed = Change (0);
672 if (_start != new_start) {
674 what_changed = Change (what_changed|StartChanged);
676 if (_length != length) {
678 what_changed = Change (what_changed|LengthChanged);
680 if (_position != position) {
681 _position = position;
682 what_changed = Change (what_changed|PositionChanged);
685 _flags = Region::Flag (_flags & ~WholeFile);
687 if (what_changed & (StartChanged|LengthChanged)) {
692 send_change (what_changed);
697 Region::set_hidden (bool yn)
699 if (hidden() != yn) {
702 _flags = Flag (_flags|Hidden);
704 _flags = Flag (_flags & ~Hidden);
707 send_change (HiddenChanged);
712 Region::set_muted (bool yn)
717 _flags = Flag (_flags|Muted);
719 _flags = Flag (_flags & ~Muted);
722 send_change (MuteChanged);
727 Region::set_opaque (bool yn)
729 if (opaque() != yn) {
731 _flags = Flag (_flags|Opaque);
733 _flags = Flag (_flags & ~Opaque);
735 send_change (OpacityChanged);
740 Region::set_locked (bool yn)
742 if (locked() != yn) {
744 _flags = Flag (_flags|Locked);
746 _flags = Flag (_flags & ~Locked);
748 send_change (LockChanged);
753 Region::set_sync_position (nframes_t absolute_pos)
757 file_pos = _start + (absolute_pos - _position);
759 if (file_pos != _sync_position) {
761 _sync_position = file_pos;
762 _flags = Flag (_flags|SyncMarked);
767 send_change (SyncOffsetChanged);
772 Region::clear_sync_position ()
774 if (_flags & SyncMarked) {
775 _flags = Flag (_flags & ~SyncMarked);
780 send_change (SyncOffsetChanged);
785 Region::sync_offset (int& dir) const
787 /* returns the sync point relative the first frame of the region */
789 if (_flags & SyncMarked) {
790 if (_sync_position > _start) {
792 return _sync_position - _start;
795 return _start - _sync_position;
804 Region::adjust_to_sync (nframes_t pos)
807 nframes_t offset = sync_offset (sync_dir);
810 if (max_frames - pos > offset) {
825 Region::sync_position() const
827 if (_flags & SyncMarked) {
828 return _sync_position;
838 if (_playlist == 0) {
842 _playlist->raise_region (shared_from_this ());
848 if (_playlist == 0) {
852 _playlist->lower_region (shared_from_this ());
856 Region::raise_to_top ()
859 if (_playlist == 0) {
863 _playlist->raise_region_to_top (shared_from_this());
867 Region::lower_to_bottom ()
869 if (_playlist == 0) {
873 _playlist->lower_region_to_bottom (shared_from_this());
877 Region::set_layer (layer_t l)
882 send_change (LayerChanged);
887 Region::state (bool full_state)
889 XMLNode *node = new XMLNode ("Region");
893 _id.print (buf, sizeof (buf));
894 node->add_property ("id", buf);
895 node->add_property ("name", _name);
896 node->add_property ("type", _type.to_string());
897 snprintf (buf, sizeof (buf), "%u", _start);
898 node->add_property ("start", buf);
899 snprintf (buf, sizeof (buf), "%u", _length);
900 node->add_property ("length", buf);
901 snprintf (buf, sizeof (buf), "%u", _position);
902 node->add_property ("position", buf);
904 switch (_first_edit) {
905 case EditChangesNothing:
908 case EditChangesName:
916 node->add_property ("first_edit", fe);
918 /* note: flags are stored by derived classes */
920 snprintf (buf, sizeof (buf), "%d", (int) _layer);
921 node->add_property ("layer", buf);
922 snprintf (buf, sizeof (buf), "%u", _sync_position);
923 node->add_property ("sync-position", buf);
935 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
937 const XMLNodeList& nlist = node.children();
938 const XMLProperty *prop;
941 /* this is responsible for setting those aspects of Region state
942 that are mutable after construction.
945 if ((prop = node.property ("name")) == 0) {
946 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
950 _name = prop->value();
952 if ((prop = node.property ("type")) == 0) {
953 _type = DataType::AUDIO;
955 _type = DataType(prop->value());
958 if ((prop = node.property ("start")) != 0) {
959 sscanf (prop->value().c_str(), "%" PRIu32, &val);
961 what_changed = Change (what_changed|StartChanged);
968 if ((prop = node.property ("length")) != 0) {
969 sscanf (prop->value().c_str(), "%" PRIu32, &val);
970 if (val != _length) {
971 what_changed = Change (what_changed|LengthChanged);
978 if ((prop = node.property ("position")) != 0) {
979 sscanf (prop->value().c_str(), "%" PRIu32, &val);
980 if (val != _position) {
981 what_changed = Change (what_changed|PositionChanged);
988 if ((prop = node.property ("layer")) != 0) {
990 x = (layer_t) atoi (prop->value().c_str());
992 what_changed = Change (what_changed|LayerChanged);
999 if ((prop = node.property ("sync-position")) != 0) {
1000 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1001 if (val != _sync_position) {
1002 what_changed = Change (what_changed|SyncOffsetChanged);
1003 _sync_position = val;
1006 _sync_position = _start;
1009 /* XXX FIRST EDIT !!! */
1011 /* note: derived classes set flags */
1018 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1024 if (child->name () == "extra") {
1025 _extra_xml = new XMLNode (*child);
1031 send_change (what_changed);
1038 Region::set_state (const XMLNode& node)
1040 const XMLProperty *prop;
1041 Change what_changed = Change (0);
1043 /* ID is not allowed to change, ever */
1045 if ((prop = node.property ("id")) == 0) {
1046 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1050 _id = prop->value();
1052 _first_edit = EditChangesNothing;
1054 set_live_state (node, what_changed, true);
1066 Region::thaw (const string& why)
1068 Change what_changed = Change (0);
1071 Glib::Mutex::Lock lm (_lock);
1073 if (_frozen && --_frozen > 0) {
1077 if (_pending_changed) {
1078 what_changed = _pending_changed;
1079 _pending_changed = Change (0);
1083 if (what_changed == Change (0)) {
1087 if (what_changed & LengthChanged) {
1088 if (what_changed & PositionChanged) {
1089 recompute_at_start ();
1091 recompute_at_end ();
1094 StateChanged (what_changed);
1098 Region::send_change (Change what_changed)
1101 Glib::Mutex::Lock lm (_lock);
1103 _pending_changed = Change (_pending_changed|what_changed);
1108 StateChanged (what_changed);
1112 Region::set_last_layer_op (uint64_t when)
1114 _last_layer_op = when;
1118 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1120 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1124 Region::equivalent (boost::shared_ptr<const Region> other) const
1126 return _start == other->_start &&
1127 _position == other->_position &&
1128 _length == other->_length;
1132 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1134 return _start == other->_start &&
1135 _length == other->_length;
1139 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1141 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1145 Region::source_deleted (boost::shared_ptr<Source>)
1151 Region::master_source_names ()
1153 SourceList::iterator i;
1155 vector<string> names;
1156 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1157 names.push_back((*i)->name());
1164 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1169 SourceList::const_iterator i;
1170 SourceList::const_iterator io;
1172 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1173 if ((*i)->id() != (*io)->id()) {
1178 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1179 if ((*i)->id() != (*io)->id()) {
1188 Region::verify_length (jack_nframes_t len)
1190 for (uint32_t n=0; n < _sources.size(); ++n) {
1191 if (_start > _sources[n]->length() - len) {
1199 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1201 for (uint32_t n=0; n < _sources.size(); ++n) {
1202 if (new_length > _sources[n]->length() - new_start) {
1209 Region::verify_start (jack_nframes_t pos)
1211 for (uint32_t n=0; n < _sources.size(); ++n) {
1212 if (pos > _sources[n]->length() - _length) {
1220 Region::verify_start_mutable (jack_nframes_t& new_start)
1222 for (uint32_t n=0; n < _sources.size(); ++n) {
1223 if (new_start > _sources[n]->length() - _length) {
1224 new_start = _sources[n]->length() - _length;
1230 boost::shared_ptr<Region>
1231 Region::get_parent() const
1234 boost::shared_ptr<Region> r;
1235 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1237 if (grrr2 && (r = _playlist->session().find_whole_file_parent (grrr2))) {
1238 return boost::static_pointer_cast<Region> (r);
1242 return boost::shared_ptr<Region>();