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, nframes_t start, 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, nframes_t start, 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|PositionLocked|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|PositionLocked)))
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)
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|PositionLocked)) {
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|PositionLocked)) {
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_position_locked (bool yn)
762 if (position_locked() != yn) {
764 _flags = Flag (_flags|PositionLocked);
766 _flags = Flag (_flags & ~PositionLocked);
768 send_change (LockChanged);
773 Region::set_sync_position (nframes_t absolute_pos)
777 file_pos = _start + (absolute_pos - _position);
779 if (file_pos != _sync_position) {
781 _sync_position = file_pos;
782 _flags = Flag (_flags|SyncMarked);
787 send_change (SyncOffsetChanged);
792 Region::clear_sync_position ()
794 if (_flags & SyncMarked) {
795 _flags = Flag (_flags & ~SyncMarked);
800 send_change (SyncOffsetChanged);
805 Region::sync_offset (int& dir) const
807 /* returns the sync point relative the first frame of the region */
809 if (_flags & SyncMarked) {
810 if (_sync_position > _start) {
812 return _sync_position - _start;
815 return _start - _sync_position;
824 Region::adjust_to_sync (nframes_t pos)
827 nframes_t offset = sync_offset (sync_dir);
830 if (max_frames - pos > offset) {
845 Region::sync_position() const
847 if (_flags & SyncMarked) {
848 return _sync_position;
858 boost::shared_ptr<Playlist> pl (playlist());
860 pl->raise_region (shared_from_this ());
867 boost::shared_ptr<Playlist> pl (playlist());
869 pl->lower_region (shared_from_this ());
874 Region::raise_to_top ()
876 boost::shared_ptr<Playlist> pl (playlist());
878 pl->raise_region_to_top (shared_from_this());
883 Region::lower_to_bottom ()
885 boost::shared_ptr<Playlist> pl (playlist());
887 pl->lower_region_to_bottom (shared_from_this());
892 Region::set_layer (layer_t l)
897 send_change (LayerChanged);
902 Region::state (bool full_state)
904 XMLNode *node = new XMLNode ("Region");
908 _id.print (buf, sizeof (buf));
909 node->add_property ("id", buf);
910 node->add_property ("name", _name);
911 node->add_property ("type", _type.to_string());
912 snprintf (buf, sizeof (buf), "%u", _start);
913 node->add_property ("start", buf);
914 snprintf (buf, sizeof (buf), "%u", _length);
915 node->add_property ("length", buf);
916 snprintf (buf, sizeof (buf), "%u", _position);
917 node->add_property ("position", buf);
919 switch (_first_edit) {
920 case EditChangesNothing:
923 case EditChangesName:
929 default: /* should be unreachable but makes g++ happy */
930 cerr << "Odd region property found\n";
935 node->add_property ("first_edit", fe);
937 /* note: flags are stored by derived classes */
939 snprintf (buf, sizeof (buf), "%d", (int) _layer);
940 node->add_property ("layer", buf);
941 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
942 node->add_property ("sync-position", buf);
954 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
956 const XMLNodeList& nlist = node.children();
957 const XMLProperty *prop;
960 /* this is responsible for setting those aspects of Region state
961 that are mutable after construction.
964 if ((prop = node.property ("name")) == 0) {
965 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
969 _name = prop->value();
971 if ((prop = node.property ("type")) == 0) {
972 _type = DataType::AUDIO;
974 _type = DataType(prop->value());
977 if ((prop = node.property ("start")) != 0) {
978 sscanf (prop->value().c_str(), "%" PRIu32, &val);
980 what_changed = Change (what_changed|StartChanged);
987 if ((prop = node.property ("length")) != 0) {
988 sscanf (prop->value().c_str(), "%" PRIu32, &val);
989 if (val != _length) {
990 what_changed = Change (what_changed|LengthChanged);
997 if ((prop = node.property ("position")) != 0) {
998 sscanf (prop->value().c_str(), "%" PRIu32, &val);
999 if (val != _position) {
1000 what_changed = Change (what_changed|PositionChanged);
1007 if ((prop = node.property ("layer")) != 0) {
1009 x = (layer_t) atoi (prop->value().c_str());
1011 what_changed = Change (what_changed|LayerChanged);
1018 if ((prop = node.property ("sync-position")) != 0) {
1019 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1020 if (val != _sync_position) {
1021 what_changed = Change (what_changed|SyncOffsetChanged);
1022 _sync_position = val;
1025 _sync_position = _start;
1028 /* XXX FIRST EDIT !!! */
1030 /* note: derived classes set flags */
1037 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1043 if (child->name () == "extra") {
1044 _extra_xml = new XMLNode (*child);
1050 send_change (what_changed);
1057 Region::set_state (const XMLNode& node)
1059 const XMLProperty *prop;
1060 Change what_changed = Change (0);
1062 /* ID is not allowed to change, ever */
1064 if ((prop = node.property ("id")) == 0) {
1065 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1069 _id = prop->value();
1071 _first_edit = EditChangesNothing;
1073 set_live_state (node, what_changed, true);
1085 Region::thaw (const string& why)
1087 Change what_changed = Change (0);
1090 Glib::Mutex::Lock lm (_lock);
1092 if (_frozen && --_frozen > 0) {
1096 if (_pending_changed) {
1097 what_changed = _pending_changed;
1098 _pending_changed = Change (0);
1102 if (what_changed == Change (0)) {
1106 if (what_changed & LengthChanged) {
1107 if (what_changed & PositionChanged) {
1108 recompute_at_start ();
1110 recompute_at_end ();
1113 StateChanged (what_changed);
1117 Region::send_change (Change what_changed)
1120 Glib::Mutex::Lock lm (_lock);
1122 _pending_changed = Change (_pending_changed|what_changed);
1127 StateChanged (what_changed);
1131 Region::set_last_layer_op (uint64_t when)
1133 _last_layer_op = when;
1137 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1139 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1143 Region::equivalent (boost::shared_ptr<const Region> other) const
1145 return _start == other->_start &&
1146 _position == other->_position &&
1147 _length == other->_length;
1151 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1153 return _start == other->_start &&
1154 _length == other->_length;
1158 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1160 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1164 Region::source_deleted (boost::shared_ptr<Source>)
1170 Region::master_source_names ()
1172 SourceList::iterator i;
1174 vector<string> names;
1175 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1176 names.push_back((*i)->name());
1183 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1188 SourceList::const_iterator i;
1189 SourceList::const_iterator io;
1191 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1192 if ((*i)->id() != (*io)->id()) {
1197 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1198 if ((*i)->id() != (*io)->id()) {
1207 Region::verify_length (nframes_t len)
1209 for (uint32_t n=0; n < _sources.size(); ++n) {
1210 if (_start > _sources[n]->length() - len) {
1218 Region::verify_start_and_length (nframes_t new_start, nframes_t new_length)
1220 for (uint32_t n=0; n < _sources.size(); ++n) {
1221 if (new_length > _sources[n]->length() - new_start) {
1228 Region::verify_start (nframes_t pos)
1230 for (uint32_t n=0; n < _sources.size(); ++n) {
1231 if (pos > _sources[n]->length() - _length) {
1239 Region::verify_start_mutable (nframes_t& new_start)
1241 for (uint32_t n=0; n < _sources.size(); ++n) {
1242 if (new_start > _sources[n]->length() - _length) {
1243 new_start = _sources[n]->length() - _length;
1249 boost::shared_ptr<Region>
1250 Region::get_parent() const
1252 boost::shared_ptr<Playlist> pl (playlist());
1255 boost::shared_ptr<Region> r;
1256 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1258 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1259 return boost::static_pointer_cast<Region> (r);
1263 return boost::shared_ptr<Region>();