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>
41 using namespace ARDOUR;
44 Change Region::FadeChanged = ARDOUR::new_change ();
45 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
46 Change Region::MuteChanged = ARDOUR::new_change ();
47 Change Region::OpacityChanged = ARDOUR::new_change ();
48 Change Region::LockChanged = ARDOUR::new_change ();
49 Change Region::LayerChanged = ARDOUR::new_change ();
50 Change Region::HiddenChanged = ARDOUR::new_change ();
52 /** Basic Region constructor (single source) */
53 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)
60 , _sync_position(_start)
62 , _first_edit(EditChangesNothing)
65 , _pending_changed(Change (0))
68 _sources.push_back (src);
69 _master_sources.push_back (src);
70 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
72 assert(_sources.size() > 0);
75 /** Basic Region constructor (many sources) */
76 Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
83 , _sync_position(_start)
85 , _first_edit(EditChangesNothing)
88 , _pending_changed(Change (0))
92 set<boost::shared_ptr<Source> > unique_srcs;
94 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
95 _sources.push_back (*i);
96 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
97 unique_srcs.insert (*i);
100 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
101 _master_sources.push_back (*i);
102 if (unique_srcs.find (*i) == unique_srcs.end()) {
103 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
107 assert(_sources.size() > 0);
110 /** Create a new Region from part of an existing one */
111 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
113 , _type(other->data_type())
114 , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
115 , _start(other->_start + offset)
118 , _sync_position(_start)
120 , _first_edit(EditChangesNothing)
122 , _read_data_count(0)
123 , _pending_changed(Change (0))
126 if (other->_sync_position < offset)
127 _sync_position = other->_sync_position;
129 set<boost::shared_ptr<Source> > unique_srcs;
131 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
132 _sources.push_back (*i);
133 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
134 unique_srcs.insert (*i);
137 if (other->_sync_position < offset) {
138 _sync_position = other->_sync_position;
141 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
142 if (unique_srcs.find (*i) == unique_srcs.end()) {
143 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
145 _master_sources.push_back (*i);
148 assert(_sources.size() > 0);
151 /** Pure copy constructor */
152 Region::Region (boost::shared_ptr<const Region> other)
153 : _name(other->_name)
154 , _type(other->data_type())
155 , _flags(Flag(other->_flags & ~Locked))
156 , _start(other->_start)
157 , _length(other->_length)
158 , _position(other->_position)
159 , _sync_position(other->_sync_position)
160 , _layer(other->_layer)
161 , _first_edit(EditChangesID)
163 , _read_data_count(0)
164 , _pending_changed(Change(0))
165 , _last_layer_op(other->_last_layer_op)
167 other->_first_edit = EditChangesName;
169 if (other->_extra_xml) {
170 _extra_xml = new XMLNode (*other->_extra_xml);
175 set<boost::shared_ptr<Source> > unique_srcs;
177 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
178 _sources.push_back (*i);
179 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
180 unique_srcs.insert (*i);
183 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
184 _master_sources.push_back (*i);
185 if (unique_srcs.find (*i) == unique_srcs.end()) {
186 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
190 assert(_sources.size() > 0);
193 Region::Region (SourceList& srcs, const XMLNode& node)
194 : _name(X_("error: XML did not reset this"))
195 , _type(DataType::NIL) // to be loaded from XML
200 , _sync_position(_start)
202 , _first_edit(EditChangesNothing)
204 , _read_data_count(0)
205 , _pending_changed(Change(0))
208 set<boost::shared_ptr<Source> > unique_srcs;
210 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
211 _sources.push_back (*i);
212 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
213 unique_srcs.insert (*i);
216 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
217 _master_sources.push_back (*i);
218 if (unique_srcs.find (*i) == unique_srcs.end()) {
219 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
223 if (set_state (node)) {
224 throw failed_constructor();
227 assert(_type != DataType::NIL);
228 assert(_sources.size() > 0);
231 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
232 : _name(X_("error: XML did not reset this"))
233 , _type(DataType::NIL)
238 , _sync_position(_start)
240 , _first_edit(EditChangesNothing)
242 , _read_data_count(0)
243 , _pending_changed(Change(0))
246 _sources.push_back (src);
249 if (set_state (node)) {
250 throw failed_constructor();
253 assert(_type != DataType::NIL);
254 assert(_sources.size() > 0);
259 boost::shared_ptr<Playlist> pl (playlist());
262 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
263 (*i)->remove_playlist (pl);
268 GoingAway (); /* EMIT SIGNAL */
272 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
274 boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
276 boost::shared_ptr<Playlist> pl (wpl.lock());
278 if (old_playlist == pl) {
286 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
287 (*i)->remove_playlist (_playlist);
288 (*i)->add_playlist (pl);
291 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
292 (*i)->add_playlist (pl);
297 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
298 (*i)->remove_playlist (old_playlist);
305 Region::set_name (string str)
309 send_change (NameChanged);
314 Region::set_length (nframes_t len, void *src)
316 if (_flags & Locked) {
320 if (_length != len && len != 0) {
322 /* check that the current _position wouldn't make the new
326 if (max_frames - len < _position) {
330 if (!verify_length (len)) {
336 _flags = Region::Flag (_flags & ~WholeFile);
345 send_change (LengthChanged);
350 Region::maybe_uncopy ()
355 Region::first_edit ()
357 boost::shared_ptr<Playlist> pl (playlist());
359 if (_first_edit != EditChangesNothing && pl) {
361 _name = pl->session().new_region_name (_name);
362 _first_edit = EditChangesNothing;
364 send_change (NameChanged);
365 RegionFactory::CheckNewRegion (shared_from_this());
370 Region::at_natural_position () const
372 boost::shared_ptr<Playlist> pl (playlist());
378 boost::shared_ptr<Region> whole_file_region = get_parent();
380 if (whole_file_region) {
381 if (_position == whole_file_region->position() + _start) {
390 Region::move_to_natural_position (void *src)
392 boost::shared_ptr<Playlist> pl (playlist());
398 boost::shared_ptr<Region> whole_file_region = get_parent();
400 if (whole_file_region) {
401 set_position (whole_file_region->position() + _start, src);
406 Region::special_set_position (nframes_t pos)
408 /* this is used when creating a whole file region as
409 a way to store its "natural" or "captured" position.
416 Region::set_position (nframes_t pos, void *src)
418 if (_flags & Locked) {
422 if (_position != pos) {
425 /* check that the new _position wouldn't make the current
426 length impossible - if so, change the length.
428 XXX is this the right thing to do?
431 if (max_frames - _length < _position) {
432 _length = max_frames - _position;
436 /* do this even if the position is the same. this helps out
437 a GUI that has moved its representation already.
440 send_change (PositionChanged);
444 Region::set_position_on_top (nframes_t pos, void *src)
446 if (_flags & Locked) {
450 if (_position != pos) {
454 boost::shared_ptr<Playlist> pl (playlist());
457 pl->raise_region_to_top (shared_from_this ());
460 /* do this even if the position is the same. this helps out
461 a GUI that has moved its representation already.
464 send_change (PositionChanged);
468 Region::nudge_position (long n, void *src)
470 if (_flags & Locked) {
479 if (_position > max_frames - n) {
480 _position = max_frames;
485 if (_position < (nframes_t) -n) {
492 send_change (PositionChanged);
496 Region::set_start (nframes_t pos, void *src)
498 if (_flags & Locked) {
501 /* This just sets the start, nothing else. It effectively shifts
502 the contents of the Region within the overall extent of the Source,
503 without changing the Region's position or length
508 if (!verify_start (pos)) {
513 _flags = Region::Flag (_flags & ~WholeFile);
516 send_change (StartChanged);
521 Region::trim_start (nframes_t new_position, void *src)
523 if (_flags & Locked) {
529 if (new_position > _position) {
530 start_shift = new_position - _position;
532 start_shift = -(_position - new_position);
535 if (start_shift > 0) {
537 if (_start > max_frames - start_shift) {
538 new_start = max_frames;
540 new_start = _start + start_shift;
543 if (!verify_start (new_start)) {
547 } else if (start_shift < 0) {
549 if (_start < (nframes_t) -start_shift) {
552 new_start = _start + start_shift;
558 if (new_start == _start) {
563 _flags = Region::Flag (_flags & ~WholeFile);
566 send_change (StartChanged);
570 Region::trim_front (nframes_t new_position, void *src)
572 if (_flags & Locked) {
576 nframes_t end = last_frame();
577 nframes_t source_zero;
579 if (_position > _start) {
580 source_zero = _position - _start;
582 source_zero = 0; // its actually negative, but this will work for us
585 if (new_position < end) { /* can't trim it zero or negative length */
589 /* can't trim it back passed where source position zero is located */
591 new_position = max (new_position, source_zero);
594 if (new_position > _position) {
595 newlen = _length - (new_position - _position);
597 newlen = _length + (_position - new_position);
600 trim_to_internal (new_position, newlen, src);
602 recompute_at_start ();
608 Region::trim_end (nframes_t new_endpoint, void *src)
610 if (_flags & Locked) {
614 if (new_endpoint > _position) {
615 trim_to_internal (_position, new_endpoint - _position, this);
623 Region::trim_to (nframes_t position, nframes_t length, void *src)
625 if (_flags & Locked) {
629 trim_to_internal (position, length, src);
632 recompute_at_start ();
638 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
643 if (_flags & Locked) {
647 if (position > _position) {
648 start_shift = position - _position;
650 start_shift = -(_position - position);
653 if (start_shift > 0) {
655 if (_start > max_frames - start_shift) {
656 new_start = max_frames;
658 new_start = _start + start_shift;
662 } else if (start_shift < 0) {
664 if (_start < (nframes_t) -start_shift) {
667 new_start = _start + start_shift;
673 if (!verify_start_and_length (new_start, length)) {
677 Change what_changed = Change (0);
679 if (_start != new_start) {
681 what_changed = Change (what_changed|StartChanged);
683 if (_length != length) {
685 what_changed = Change (what_changed|LengthChanged);
687 if (_position != position) {
688 _position = position;
689 what_changed = Change (what_changed|PositionChanged);
692 _flags = Region::Flag (_flags & ~WholeFile);
694 if (what_changed & (StartChanged|LengthChanged)) {
699 send_change (what_changed);
704 Region::set_hidden (bool yn)
706 if (hidden() != yn) {
709 _flags = Flag (_flags|Hidden);
711 _flags = Flag (_flags & ~Hidden);
714 send_change (HiddenChanged);
719 Region::set_muted (bool yn)
724 _flags = Flag (_flags|Muted);
726 _flags = Flag (_flags & ~Muted);
729 send_change (MuteChanged);
734 Region::set_opaque (bool yn)
736 if (opaque() != yn) {
738 _flags = Flag (_flags|Opaque);
740 _flags = Flag (_flags & ~Opaque);
742 send_change (OpacityChanged);
747 Region::set_locked (bool yn)
749 if (locked() != yn) {
751 _flags = Flag (_flags|Locked);
753 _flags = Flag (_flags & ~Locked);
755 send_change (LockChanged);
760 Region::set_sync_position (nframes_t absolute_pos)
764 file_pos = _start + (absolute_pos - _position);
766 if (file_pos != _sync_position) {
768 _sync_position = file_pos;
769 _flags = Flag (_flags|SyncMarked);
774 send_change (SyncOffsetChanged);
779 Region::clear_sync_position ()
781 if (_flags & SyncMarked) {
782 _flags = Flag (_flags & ~SyncMarked);
787 send_change (SyncOffsetChanged);
792 Region::sync_offset (int& dir) const
794 /* returns the sync point relative the first frame of the region */
796 if (_flags & SyncMarked) {
797 if (_sync_position > _start) {
799 return _sync_position - _start;
802 return _start - _sync_position;
811 Region::adjust_to_sync (nframes_t pos)
814 nframes_t offset = sync_offset (sync_dir);
817 if (max_frames - pos > offset) {
832 Region::sync_position() const
834 if (_flags & SyncMarked) {
835 return _sync_position;
845 boost::shared_ptr<Playlist> pl (playlist());
847 pl->raise_region (shared_from_this ());
854 boost::shared_ptr<Playlist> pl (playlist());
856 pl->lower_region (shared_from_this ());
861 Region::raise_to_top ()
863 boost::shared_ptr<Playlist> pl (playlist());
865 pl->raise_region_to_top (shared_from_this());
870 Region::lower_to_bottom ()
872 boost::shared_ptr<Playlist> pl (playlist());
874 pl->lower_region_to_bottom (shared_from_this());
879 Region::set_layer (layer_t l)
884 send_change (LayerChanged);
889 Region::state (bool full_state)
891 XMLNode *node = new XMLNode ("Region");
895 _id.print (buf, sizeof (buf));
896 node->add_property ("id", buf);
897 node->add_property ("name", _name);
898 node->add_property ("type", _type.to_string());
899 snprintf (buf, sizeof (buf), "%u", _start);
900 node->add_property ("start", buf);
901 snprintf (buf, sizeof (buf), "%u", _length);
902 node->add_property ("length", buf);
903 snprintf (buf, sizeof (buf), "%u", _position);
904 node->add_property ("position", buf);
906 switch (_first_edit) {
907 case EditChangesNothing:
910 case EditChangesName:
916 default: /* should be unreachable but makes g++ happy */
917 cerr << "Odd region property found\n";
922 node->add_property ("first_edit", fe);
924 /* note: flags are stored by derived classes */
926 snprintf (buf, sizeof (buf), "%d", (int) _layer);
927 node->add_property ("layer", buf);
928 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
929 node->add_property ("sync-position", buf);
941 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
943 const XMLNodeList& nlist = node.children();
944 const XMLProperty *prop;
947 /* this is responsible for setting those aspects of Region state
948 that are mutable after construction.
951 if ((prop = node.property ("name")) == 0) {
952 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
956 _name = prop->value();
958 if ((prop = node.property ("type")) == 0) {
959 _type = DataType::AUDIO;
961 _type = DataType(prop->value());
964 if ((prop = node.property ("start")) != 0) {
965 sscanf (prop->value().c_str(), "%" PRIu32, &val);
967 what_changed = Change (what_changed|StartChanged);
974 if ((prop = node.property ("length")) != 0) {
975 sscanf (prop->value().c_str(), "%" PRIu32, &val);
976 if (val != _length) {
977 what_changed = Change (what_changed|LengthChanged);
984 if ((prop = node.property ("position")) != 0) {
985 sscanf (prop->value().c_str(), "%" PRIu32, &val);
986 if (val != _position) {
987 what_changed = Change (what_changed|PositionChanged);
994 if ((prop = node.property ("layer")) != 0) {
996 x = (layer_t) atoi (prop->value().c_str());
998 what_changed = Change (what_changed|LayerChanged);
1005 if ((prop = node.property ("sync-position")) != 0) {
1006 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1007 if (val != _sync_position) {
1008 what_changed = Change (what_changed|SyncOffsetChanged);
1009 _sync_position = val;
1012 _sync_position = _start;
1015 /* XXX FIRST EDIT !!! */
1017 /* note: derived classes set flags */
1024 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1030 if (child->name () == "extra") {
1031 _extra_xml = new XMLNode (*child);
1037 send_change (what_changed);
1044 Region::set_state (const XMLNode& node)
1046 const XMLProperty *prop;
1047 Change what_changed = Change (0);
1049 /* ID is not allowed to change, ever */
1051 if ((prop = node.property ("id")) == 0) {
1052 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1056 _id = prop->value();
1058 _first_edit = EditChangesNothing;
1060 set_live_state (node, what_changed, true);
1072 Region::thaw (const string& why)
1074 Change what_changed = Change (0);
1077 Glib::Mutex::Lock lm (_lock);
1079 if (_frozen && --_frozen > 0) {
1083 if (_pending_changed) {
1084 what_changed = _pending_changed;
1085 _pending_changed = Change (0);
1089 if (what_changed == Change (0)) {
1093 if (what_changed & LengthChanged) {
1094 if (what_changed & PositionChanged) {
1095 recompute_at_start ();
1097 recompute_at_end ();
1100 StateChanged (what_changed);
1104 Region::send_change (Change what_changed)
1107 Glib::Mutex::Lock lm (_lock);
1109 _pending_changed = Change (_pending_changed|what_changed);
1114 StateChanged (what_changed);
1118 Region::set_last_layer_op (uint64_t when)
1120 _last_layer_op = when;
1124 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1126 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1130 Region::equivalent (boost::shared_ptr<const Region> other) const
1132 return _start == other->_start &&
1133 _position == other->_position &&
1134 _length == other->_length;
1138 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1140 return _start == other->_start &&
1141 _length == other->_length;
1145 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1147 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1151 Region::source_deleted (boost::shared_ptr<Source>)
1157 Region::master_source_names ()
1159 SourceList::iterator i;
1161 vector<string> names;
1162 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1163 names.push_back((*i)->name());
1170 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1175 SourceList::const_iterator i;
1176 SourceList::const_iterator io;
1178 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1179 if ((*i)->id() != (*io)->id()) {
1184 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1185 if ((*i)->id() != (*io)->id()) {
1194 Region::verify_length (jack_nframes_t len)
1196 for (uint32_t n=0; n < _sources.size(); ++n) {
1197 if (_start > _sources[n]->length() - len) {
1205 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1207 for (uint32_t n=0; n < _sources.size(); ++n) {
1208 if (new_length > _sources[n]->length() - new_start) {
1215 Region::verify_start (jack_nframes_t pos)
1217 for (uint32_t n=0; n < _sources.size(); ++n) {
1218 if (pos > _sources[n]->length() - _length) {
1226 Region::verify_start_mutable (jack_nframes_t& new_start)
1228 for (uint32_t n=0; n < _sources.size(); ++n) {
1229 if (new_start > _sources[n]->length() - _length) {
1230 new_start = _sources[n]->length() - _length;
1236 boost::shared_ptr<Region>
1237 Region::get_parent() const
1239 boost::shared_ptr<Playlist> pl (playlist());
1242 boost::shared_ptr<Region> r;
1243 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1245 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1246 return boost::static_pointer_cast<Region> (r);
1250 return boost::shared_ptr<Region>();