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.
25 #include <sigc++/bind.h>
26 #include <sigc++/class_slot.h>
28 #include <glibmm/thread.h>
29 #include <pbd/xml++.h>
30 #include <pbd/stacktrace.h>
32 #include <ardour/region.h>
33 #include <ardour/playlist.h>
34 #include <ardour/session.h>
35 #include <ardour/source.h>
36 #include <ardour/region_factory.h>
37 #include <ardour/filter.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 ();
54 /* derived-from-derived constructor (no sources in constructor) */
55 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
56 : Automatable(s, name)
62 , _sync_position(_start)
64 , _first_edit(EditChangesNothing)
67 , _pending_changed(Change (0))
70 /* no sources at this point */
74 /** Basic Region constructor (single source) */
75 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
76 : Automatable(src->session(), name)
82 , _sync_position(_start)
84 , _first_edit(EditChangesNothing)
87 , _pending_changed(Change (0))
90 _sources.push_back (src);
91 _master_sources.push_back (src);
93 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
95 assert(_sources.size() > 0);
98 /** Basic Region constructor (many sources) */
99 Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
100 : Automatable(srcs.front()->session(), name)
106 , _sync_position(_start)
108 , _first_edit(EditChangesNothing)
110 , _read_data_count(0)
111 , _pending_changed(Change (0))
115 set<boost::shared_ptr<Source> > unique_srcs;
117 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
118 _sources.push_back (*i);
119 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
120 unique_srcs.insert (*i);
123 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
124 _master_sources.push_back (*i);
125 if (unique_srcs.find (*i) == unique_srcs.end()) {
126 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
130 assert(_sources.size() > 0);
133 /** Create a new Region from part of an existing one */
134 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
135 : Automatable(other->session(), name)
136 , _type(other->data_type())
137 , _flags(Flag(flags & ~(Locked|PositionLocked|WholeFile|Hidden)))
138 , _start(other->_start + offset)
141 , _sync_position(_start)
143 , _first_edit(EditChangesNothing)
145 , _read_data_count(0)
146 , _pending_changed(Change (0))
149 if (other->_sync_position < offset)
150 _sync_position = other->_sync_position;
152 set<boost::shared_ptr<Source> > unique_srcs;
154 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
155 _sources.push_back (*i);
156 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
157 unique_srcs.insert (*i);
160 if (other->_sync_position < offset) {
161 _sync_position = other->_sync_position;
164 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
165 if (unique_srcs.find (*i) == unique_srcs.end()) {
166 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
168 _master_sources.push_back (*i);
171 assert(_sources.size() > 0);
174 /** Pure copy constructor */
175 Region::Region (boost::shared_ptr<const Region> other)
176 : Automatable(other->session(), other->name())
177 , _type(other->data_type())
178 , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
179 , _start(other->_start)
180 , _length(other->_length)
181 , _position(other->_position)
182 , _sync_position(other->_sync_position)
183 , _layer(other->_layer)
184 , _first_edit(EditChangesID)
186 , _read_data_count(0)
187 , _pending_changed(Change(0))
188 , _last_layer_op(other->_last_layer_op)
190 other->_first_edit = EditChangesName;
192 if (other->_extra_xml) {
193 _extra_xml = new XMLNode (*other->_extra_xml);
198 set<boost::shared_ptr<Source> > unique_srcs;
200 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
201 _sources.push_back (*i);
202 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
203 unique_srcs.insert (*i);
206 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
207 _master_sources.push_back (*i);
208 if (unique_srcs.find (*i) == unique_srcs.end()) {
209 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
213 assert(_sources.size() > 0);
216 Region::Region (SourceList& srcs, const XMLNode& node)
217 : Automatable(srcs.front()->session(), X_("error: XML did not reset this"))
218 , _type(DataType::NIL) // to be loaded from XML
223 , _sync_position(_start)
225 , _first_edit(EditChangesNothing)
227 , _read_data_count(0)
228 , _pending_changed(Change(0))
231 set<boost::shared_ptr<Source> > unique_srcs;
233 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
234 _sources.push_back (*i);
235 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
236 unique_srcs.insert (*i);
239 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
240 _master_sources.push_back (*i);
241 if (unique_srcs.find (*i) == unique_srcs.end()) {
242 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
246 if (set_state (node)) {
247 throw failed_constructor();
250 assert(_type != DataType::NIL);
251 assert(_sources.size() > 0);
254 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
255 : Automatable(src->session(), X_("error: XML did not reset this"))
256 , _type(DataType::NIL)
261 , _sync_position(_start)
263 , _first_edit(EditChangesNothing)
265 , _read_data_count(0)
266 , _pending_changed(Change(0))
269 _sources.push_back (src);
271 if (set_state (node)) {
272 throw failed_constructor();
275 assert(_type != DataType::NIL);
276 assert(_sources.size() > 0);
281 boost::shared_ptr<Playlist> pl (playlist());
284 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
285 (*i)->remove_playlist (pl);
290 GoingAway (); /* EMIT SIGNAL */
294 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
296 boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
298 boost::shared_ptr<Playlist> pl (wpl.lock());
300 if (old_playlist == pl) {
308 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
309 (*i)->remove_playlist (_playlist);
310 (*i)->add_playlist (pl);
313 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
314 (*i)->add_playlist (pl);
319 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
320 (*i)->remove_playlist (old_playlist);
327 Region::set_name (const std::string& str)
330 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
331 assert(_name == str);
332 send_change (ARDOUR::NameChanged);
339 Region::set_length (nframes_t len, void *src)
341 if (_flags & Locked) {
345 if (_length != len && len != 0) {
347 /* check that the current _position wouldn't make the new
351 if (max_frames - len < _position) {
355 if (!verify_length (len)) {
361 _flags = Region::Flag (_flags & ~WholeFile);
370 send_change (LengthChanged);
375 Region::maybe_uncopy ()
380 Region::first_edit ()
382 boost::shared_ptr<Playlist> pl (playlist());
384 if (_first_edit != EditChangesNothing && pl) {
386 _name = pl->session().new_region_name (_name);
387 _first_edit = EditChangesNothing;
389 send_change (ARDOUR::NameChanged);
390 RegionFactory::CheckNewRegion (shared_from_this());
395 Region::at_natural_position () const
397 boost::shared_ptr<Playlist> pl (playlist());
403 boost::shared_ptr<Region> whole_file_region = get_parent();
405 if (whole_file_region) {
406 if (_position == whole_file_region->position() + _start) {
415 Region::move_to_natural_position (void *src)
417 boost::shared_ptr<Playlist> pl (playlist());
423 boost::shared_ptr<Region> whole_file_region = get_parent();
425 if (whole_file_region) {
426 set_position (whole_file_region->position() + _start, src);
431 Region::special_set_position (nframes_t pos)
433 /* this is used when creating a whole file region as
434 a way to store its "natural" or "captured" position.
441 Region::set_position (nframes_t pos, void *src)
447 if (_position != pos) {
450 /* check that the new _position wouldn't make the current
451 length impossible - if so, change the length.
453 XXX is this the right thing to do?
456 if (max_frames - _length < _position) {
457 _length = max_frames - _position;
461 /* do this even if the position is the same. this helps out
462 a GUI that has moved its representation already.
465 send_change (PositionChanged);
469 Region::set_position_on_top (nframes_t pos, void *src)
471 if (_flags & Locked) {
475 if (_position != pos) {
479 boost::shared_ptr<Playlist> pl (playlist());
482 pl->raise_region_to_top (shared_from_this ());
485 /* do this even if the position is the same. this helps out
486 a GUI that has moved its representation already.
489 send_change (PositionChanged);
493 Region::nudge_position (long n, void *src)
495 if (_flags & Locked) {
504 if (_position > max_frames - n) {
505 _position = max_frames;
510 if (_position < (nframes_t) -n) {
517 send_change (PositionChanged);
521 Region::set_start (nframes_t pos, void *src)
523 if (_flags & (Locked|PositionLocked)) {
526 /* This just sets the start, nothing else. It effectively shifts
527 the contents of the Region within the overall extent of the Source,
528 without changing the Region's position or length
533 if (!verify_start (pos)) {
538 _flags = Region::Flag (_flags & ~WholeFile);
541 send_change (StartChanged);
546 Region::trim_start (nframes_t new_position, void *src)
548 if (_flags & (Locked|PositionLocked)) {
554 if (new_position > _position) {
555 start_shift = new_position - _position;
557 start_shift = -(_position - new_position);
560 if (start_shift > 0) {
562 if (_start > max_frames - start_shift) {
563 new_start = max_frames;
565 new_start = _start + start_shift;
568 if (!verify_start (new_start)) {
572 } else if (start_shift < 0) {
574 if (_start < (nframes_t) -start_shift) {
577 new_start = _start + start_shift;
583 if (new_start == _start) {
588 _flags = Region::Flag (_flags & ~WholeFile);
591 send_change (StartChanged);
595 Region::trim_front (nframes_t new_position, void *src)
597 if (_flags & Locked) {
601 nframes_t end = last_frame();
602 nframes_t source_zero;
604 if (_position > _start) {
605 source_zero = _position - _start;
607 source_zero = 0; // its actually negative, but this will work for us
610 if (new_position < end) { /* can't trim it zero or negative length */
614 /* can't trim it back passed where source position zero is located */
616 new_position = max (new_position, source_zero);
619 if (new_position > _position) {
620 newlen = _length - (new_position - _position);
622 newlen = _length + (_position - new_position);
625 trim_to_internal (new_position, newlen, src);
627 recompute_at_start ();
633 Region::trim_end (nframes_t new_endpoint, void *src)
635 if (_flags & Locked) {
639 if (new_endpoint > _position) {
640 trim_to_internal (_position, new_endpoint - _position, this);
648 Region::trim_to (nframes_t position, nframes_t length, void *src)
650 if (_flags & Locked) {
654 trim_to_internal (position, length, src);
657 recompute_at_start ();
663 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
668 if (_flags & Locked) {
672 if (position > _position) {
673 start_shift = position - _position;
675 start_shift = -(_position - position);
678 if (start_shift > 0) {
680 if (_start > max_frames - start_shift) {
681 new_start = max_frames;
683 new_start = _start + start_shift;
687 } else if (start_shift < 0) {
689 if (_start < (nframes_t) -start_shift) {
692 new_start = _start + start_shift;
698 if (!verify_start_and_length (new_start, length)) {
702 Change what_changed = Change (0);
704 if (_start != new_start) {
706 what_changed = Change (what_changed|StartChanged);
708 if (_length != length) {
710 what_changed = Change (what_changed|LengthChanged);
712 if (_position != position) {
713 _position = position;
714 what_changed = Change (what_changed|PositionChanged);
717 _flags = Region::Flag (_flags & ~WholeFile);
719 if (what_changed & (StartChanged|LengthChanged)) {
724 send_change (what_changed);
729 Region::set_hidden (bool yn)
731 if (hidden() != yn) {
734 _flags = Flag (_flags|Hidden);
736 _flags = Flag (_flags & ~Hidden);
739 send_change (HiddenChanged);
744 Region::set_muted (bool yn)
749 _flags = Flag (_flags|Muted);
751 _flags = Flag (_flags & ~Muted);
754 send_change (MuteChanged);
759 Region::set_opaque (bool yn)
761 if (opaque() != yn) {
763 _flags = Flag (_flags|Opaque);
765 _flags = Flag (_flags & ~Opaque);
767 send_change (OpacityChanged);
772 Region::set_locked (bool yn)
774 if (locked() != yn) {
776 _flags = Flag (_flags|Locked);
778 _flags = Flag (_flags & ~Locked);
780 send_change (LockChanged);
785 Region::set_position_locked (bool yn)
787 if (position_locked() != yn) {
789 _flags = Flag (_flags|PositionLocked);
791 _flags = Flag (_flags & ~PositionLocked);
793 send_change (LockChanged);
798 Region::set_sync_position (nframes_t absolute_pos)
802 file_pos = _start + (absolute_pos - _position);
804 if (file_pos != _sync_position) {
806 _sync_position = file_pos;
807 _flags = Flag (_flags|SyncMarked);
812 send_change (SyncOffsetChanged);
817 Region::clear_sync_position ()
819 if (_flags & SyncMarked) {
820 _flags = Flag (_flags & ~SyncMarked);
825 send_change (SyncOffsetChanged);
830 Region::sync_offset (int& dir) const
832 /* returns the sync point relative the first frame of the region */
834 if (_flags & SyncMarked) {
835 if (_sync_position > _start) {
837 return _sync_position - _start;
840 return _start - _sync_position;
849 Region::adjust_to_sync (nframes_t pos)
852 nframes_t offset = sync_offset (sync_dir);
855 if (max_frames - pos > offset) {
870 Region::sync_position() const
872 if (_flags & SyncMarked) {
873 return _sync_position;
881 Region::raise_to_top ()
883 boost::shared_ptr<Playlist> pl (playlist());
885 pl->raise_region_to_top (shared_from_this());
890 Region::lower_to_bottom ()
892 boost::shared_ptr<Playlist> pl (playlist());
894 pl->lower_region_to_bottom (shared_from_this());
899 Region::set_layer (layer_t l)
904 send_change (LayerChanged);
909 Region::state (bool full_state)
911 XMLNode *node = new XMLNode ("Region");
913 const char* fe = NULL;
915 _id.print (buf, sizeof (buf));
916 node->add_property ("id", buf);
917 node->add_property ("name", _name);
918 node->add_property ("type", _type.to_string());
919 snprintf (buf, sizeof (buf), "%u", _start);
920 node->add_property ("start", buf);
921 snprintf (buf, sizeof (buf), "%u", _length);
922 node->add_property ("length", buf);
923 snprintf (buf, sizeof (buf), "%u", _position);
924 node->add_property ("position", buf);
926 switch (_first_edit) {
927 case EditChangesNothing:
930 case EditChangesName:
936 default: /* should be unreachable but makes g++ happy */
941 node->add_property ("first_edit", fe);
943 /* note: flags are stored by derived classes */
945 snprintf (buf, sizeof (buf), "%d", (int) _layer);
946 node->add_property ("layer", buf);
947 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
948 node->add_property ("sync-position", buf);
960 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
962 const XMLNodeList& nlist = node.children();
963 const XMLProperty *prop;
966 /* this is responsible for setting those aspects of Region state
967 that are mutable after construction.
970 if ((prop = node.property ("name")) == 0) {
971 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
975 _name = prop->value();
977 if ((prop = node.property ("type")) == 0) {
978 _type = DataType::AUDIO;
980 _type = DataType(prop->value());
983 if ((prop = node.property ("start")) != 0) {
984 sscanf (prop->value().c_str(), "%" PRIu32, &val);
986 what_changed = Change (what_changed|StartChanged);
993 if ((prop = node.property ("length")) != 0) {
994 sscanf (prop->value().c_str(), "%" PRIu32, &val);
995 if (val != _length) {
996 what_changed = Change (what_changed|LengthChanged);
1003 if ((prop = node.property ("position")) != 0) {
1004 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1005 if (val != _position) {
1006 what_changed = Change (what_changed|PositionChanged);
1013 if ((prop = node.property ("layer")) != 0) {
1015 x = (layer_t) atoi (prop->value().c_str());
1017 what_changed = Change (what_changed|LayerChanged);
1024 if ((prop = node.property ("sync-position")) != 0) {
1025 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1026 if (val != _sync_position) {
1027 what_changed = Change (what_changed|SyncOffsetChanged);
1028 _sync_position = val;
1031 _sync_position = _start;
1034 /* XXX FIRST EDIT !!! */
1036 /* note: derived classes set flags */
1043 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1049 if (child->name () == "extra") {
1050 _extra_xml = new XMLNode (*child);
1056 send_change (what_changed);
1063 Region::set_state (const XMLNode& node)
1065 const XMLProperty *prop;
1066 Change what_changed = Change (0);
1068 /* ID is not allowed to change, ever */
1070 if ((prop = node.property ("id")) == 0) {
1071 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1075 _id = prop->value();
1077 _first_edit = EditChangesNothing;
1079 set_live_state (node, what_changed, true);
1091 Region::thaw (const string& why)
1093 Change what_changed = Change (0);
1096 Glib::Mutex::Lock lm (_lock);
1098 if (_frozen && --_frozen > 0) {
1102 if (_pending_changed) {
1103 what_changed = _pending_changed;
1104 _pending_changed = Change (0);
1108 if (what_changed == Change (0)) {
1112 if (what_changed & LengthChanged) {
1113 if (what_changed & PositionChanged) {
1114 recompute_at_start ();
1116 recompute_at_end ();
1119 StateChanged (what_changed);
1123 Region::send_change (Change what_changed)
1126 Glib::Mutex::Lock lm (_lock);
1128 _pending_changed = Change (_pending_changed|what_changed);
1133 StateChanged (what_changed);
1137 Region::set_last_layer_op (uint64_t when)
1139 _last_layer_op = when;
1143 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1145 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1149 Region::equivalent (boost::shared_ptr<const Region> other) const
1151 return _start == other->_start &&
1152 _position == other->_position &&
1153 _length == other->_length;
1157 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1159 return _start == other->_start &&
1160 _length == other->_length;
1164 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1166 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1170 Region::source_deleted (boost::shared_ptr<Source>)
1176 Region::master_source_names ()
1178 SourceList::iterator i;
1180 vector<string> names;
1181 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1182 names.push_back((*i)->name());
1189 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1194 SourceList::const_iterator i;
1195 SourceList::const_iterator io;
1197 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1198 if ((*i)->id() != (*io)->id()) {
1203 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1204 if ((*i)->id() != (*io)->id()) {
1213 Region::verify_length (nframes_t len)
1215 for (uint32_t n=0; n < _sources.size(); ++n) {
1216 if (_start > _sources[n]->length() - len) {
1224 Region::verify_start_and_length (nframes_t new_start, nframes_t new_length)
1226 for (uint32_t n=0; n < _sources.size(); ++n) {
1227 if (new_length > _sources[n]->length() - new_start) {
1234 Region::verify_start (nframes_t pos)
1236 for (uint32_t n=0; n < _sources.size(); ++n) {
1237 if (pos > _sources[n]->length() - _length) {
1245 Region::verify_start_mutable (nframes_t& new_start)
1247 for (uint32_t n=0; n < _sources.size(); ++n) {
1248 if (new_start > _sources[n]->length() - _length) {
1249 new_start = _sources[n]->length() - _length;
1255 boost::shared_ptr<Region>
1256 Region::get_parent() const
1258 boost::shared_ptr<Playlist> pl (playlist());
1261 boost::shared_ptr<Region> r;
1262 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1264 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1265 return boost::static_pointer_cast<Region> (r);
1269 return boost::shared_ptr<Region>();
1273 Region::apply (Filter& filter)
1275 return filter.run (shared_from_this());