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());
275 boost::shared_ptr<Playlist> pl (wpl.lock());
277 if (old_playlist == pl) {
283 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
284 (*i)->remove_playlist (_playlist);
285 (*i)->add_playlist (pl);
288 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
289 (*i)->add_playlist (pl);
294 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
295 (*i)->remove_playlist (_playlist);
302 Region::set_name (string str)
306 send_change (NameChanged);
311 Region::set_length (nframes_t len, void *src)
313 if (_flags & Locked) {
317 if (_length != len && len != 0) {
319 /* check that the current _position wouldn't make the new
323 if (max_frames - len < _position) {
327 if (!verify_length (len)) {
333 _flags = Region::Flag (_flags & ~WholeFile);
342 send_change (LengthChanged);
347 Region::maybe_uncopy ()
352 Region::first_edit ()
354 boost::shared_ptr<Playlist> pl (playlist());
356 if (_first_edit != EditChangesNothing && pl) {
358 _name = pl->session().new_region_name (_name);
359 _first_edit = EditChangesNothing;
361 send_change (NameChanged);
362 RegionFactory::CheckNewRegion (shared_from_this());
367 Region::at_natural_position () const
369 boost::shared_ptr<Playlist> pl (playlist());
375 boost::shared_ptr<Region> whole_file_region = get_parent();
377 if (whole_file_region) {
378 if (_position == whole_file_region->position() + _start) {
387 Region::move_to_natural_position (void *src)
389 boost::shared_ptr<Playlist> pl (playlist());
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 boost::shared_ptr<Playlist> pl (playlist());
454 pl->raise_region_to_top (shared_from_this ());
457 /* do this even if the position is the same. this helps out
458 a GUI that has moved its representation already.
461 send_change (PositionChanged);
465 Region::nudge_position (long n, void *src)
467 if (_flags & Locked) {
476 if (_position > max_frames - n) {
477 _position = max_frames;
482 if (_position < (nframes_t) -n) {
489 send_change (PositionChanged);
493 Region::set_start (nframes_t pos, void *src)
495 if (_flags & Locked) {
498 /* This just sets the start, nothing else. It effectively shifts
499 the contents of the Region within the overall extent of the Source,
500 without changing the Region's position or length
505 if (!verify_start (pos)) {
510 _flags = Region::Flag (_flags & ~WholeFile);
513 send_change (StartChanged);
518 Region::trim_start (nframes_t new_position, void *src)
520 if (_flags & Locked) {
526 if (new_position > _position) {
527 start_shift = new_position - _position;
529 start_shift = -(_position - new_position);
532 if (start_shift > 0) {
534 if (_start > max_frames - start_shift) {
535 new_start = max_frames;
537 new_start = _start + start_shift;
540 if (!verify_start (new_start)) {
544 } else if (start_shift < 0) {
546 if (_start < (nframes_t) -start_shift) {
549 new_start = _start + start_shift;
555 if (new_start == _start) {
560 _flags = Region::Flag (_flags & ~WholeFile);
563 send_change (StartChanged);
567 Region::trim_front (nframes_t new_position, void *src)
569 if (_flags & Locked) {
573 nframes_t end = last_frame();
574 nframes_t source_zero;
576 if (_position > _start) {
577 source_zero = _position - _start;
579 source_zero = 0; // its actually negative, but this will work for us
582 if (new_position < end) { /* can't trim it zero or negative length */
586 /* can't trim it back passed where source position zero is located */
588 new_position = max (new_position, source_zero);
591 if (new_position > _position) {
592 newlen = _length - (new_position - _position);
594 newlen = _length + (_position - new_position);
597 trim_to_internal (new_position, newlen, src);
599 recompute_at_start ();
605 Region::trim_end (nframes_t new_endpoint, void *src)
607 if (_flags & Locked) {
611 if (new_endpoint > _position) {
612 trim_to_internal (_position, new_endpoint - _position, this);
620 Region::trim_to (nframes_t position, nframes_t length, void *src)
622 if (_flags & Locked) {
626 trim_to_internal (position, length, src);
629 recompute_at_start ();
635 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
640 if (_flags & Locked) {
644 if (position > _position) {
645 start_shift = position - _position;
647 start_shift = -(_position - position);
650 if (start_shift > 0) {
652 if (_start > max_frames - start_shift) {
653 new_start = max_frames;
655 new_start = _start + start_shift;
659 } else if (start_shift < 0) {
661 if (_start < (nframes_t) -start_shift) {
664 new_start = _start + start_shift;
670 if (!verify_start_and_length (new_start, length)) {
674 Change what_changed = Change (0);
676 if (_start != new_start) {
678 what_changed = Change (what_changed|StartChanged);
680 if (_length != length) {
682 what_changed = Change (what_changed|LengthChanged);
684 if (_position != position) {
685 _position = position;
686 what_changed = Change (what_changed|PositionChanged);
689 _flags = Region::Flag (_flags & ~WholeFile);
691 if (what_changed & (StartChanged|LengthChanged)) {
696 send_change (what_changed);
701 Region::set_hidden (bool yn)
703 if (hidden() != yn) {
706 _flags = Flag (_flags|Hidden);
708 _flags = Flag (_flags & ~Hidden);
711 send_change (HiddenChanged);
716 Region::set_muted (bool yn)
721 _flags = Flag (_flags|Muted);
723 _flags = Flag (_flags & ~Muted);
726 send_change (MuteChanged);
731 Region::set_opaque (bool yn)
733 if (opaque() != yn) {
735 _flags = Flag (_flags|Opaque);
737 _flags = Flag (_flags & ~Opaque);
739 send_change (OpacityChanged);
744 Region::set_locked (bool yn)
746 if (locked() != yn) {
748 _flags = Flag (_flags|Locked);
750 _flags = Flag (_flags & ~Locked);
752 send_change (LockChanged);
757 Region::set_sync_position (nframes_t absolute_pos)
761 file_pos = _start + (absolute_pos - _position);
763 if (file_pos != _sync_position) {
765 _sync_position = file_pos;
766 _flags = Flag (_flags|SyncMarked);
771 send_change (SyncOffsetChanged);
776 Region::clear_sync_position ()
778 if (_flags & SyncMarked) {
779 _flags = Flag (_flags & ~SyncMarked);
784 send_change (SyncOffsetChanged);
789 Region::sync_offset (int& dir) const
791 /* returns the sync point relative the first frame of the region */
793 if (_flags & SyncMarked) {
794 if (_sync_position > _start) {
796 return _sync_position - _start;
799 return _start - _sync_position;
808 Region::adjust_to_sync (nframes_t pos)
811 nframes_t offset = sync_offset (sync_dir);
814 if (max_frames - pos > offset) {
829 Region::sync_position() const
831 if (_flags & SyncMarked) {
832 return _sync_position;
842 boost::shared_ptr<Playlist> pl (playlist());
844 pl->raise_region (shared_from_this ());
851 boost::shared_ptr<Playlist> pl (playlist());
853 pl->lower_region (shared_from_this ());
858 Region::raise_to_top ()
860 boost::shared_ptr<Playlist> pl (playlist());
862 pl->raise_region_to_top (shared_from_this());
867 Region::lower_to_bottom ()
869 boost::shared_ptr<Playlist> pl (playlist());
871 pl->lower_region_to_bottom (shared_from_this());
876 Region::set_layer (layer_t l)
881 send_change (LayerChanged);
886 Region::state (bool full_state)
888 XMLNode *node = new XMLNode ("Region");
892 _id.print (buf, sizeof (buf));
893 node->add_property ("id", buf);
894 node->add_property ("name", _name);
895 node->add_property ("type", _type.to_string());
896 snprintf (buf, sizeof (buf), "%u", _start);
897 node->add_property ("start", buf);
898 snprintf (buf, sizeof (buf), "%u", _length);
899 node->add_property ("length", buf);
900 snprintf (buf, sizeof (buf), "%u", _position);
901 node->add_property ("position", buf);
903 switch (_first_edit) {
904 case EditChangesNothing:
907 case EditChangesName:
913 default: /* should be unreachable but makes g++ happy */
914 cerr << "Odd region property found\n";
919 node->add_property ("first_edit", fe);
921 /* note: flags are stored by derived classes */
923 snprintf (buf, sizeof (buf), "%d", (int) _layer);
924 node->add_property ("layer", buf);
925 snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
926 node->add_property ("sync-position", buf);
938 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
940 const XMLNodeList& nlist = node.children();
941 const XMLProperty *prop;
944 /* this is responsible for setting those aspects of Region state
945 that are mutable after construction.
948 if ((prop = node.property ("name")) == 0) {
949 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
953 _name = prop->value();
955 if ((prop = node.property ("type")) == 0) {
956 _type = DataType::AUDIO;
958 _type = DataType(prop->value());
961 if ((prop = node.property ("start")) != 0) {
962 sscanf (prop->value().c_str(), "%" PRIu32, &val);
964 what_changed = Change (what_changed|StartChanged);
971 if ((prop = node.property ("length")) != 0) {
972 sscanf (prop->value().c_str(), "%" PRIu32, &val);
973 if (val != _length) {
974 what_changed = Change (what_changed|LengthChanged);
981 if ((prop = node.property ("position")) != 0) {
982 sscanf (prop->value().c_str(), "%" PRIu32, &val);
983 if (val != _position) {
984 what_changed = Change (what_changed|PositionChanged);
991 if ((prop = node.property ("layer")) != 0) {
993 x = (layer_t) atoi (prop->value().c_str());
995 what_changed = Change (what_changed|LayerChanged);
1002 if ((prop = node.property ("sync-position")) != 0) {
1003 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1004 if (val != _sync_position) {
1005 what_changed = Change (what_changed|SyncOffsetChanged);
1006 _sync_position = val;
1009 _sync_position = _start;
1012 /* XXX FIRST EDIT !!! */
1014 /* note: derived classes set flags */
1021 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1027 if (child->name () == "extra") {
1028 _extra_xml = new XMLNode (*child);
1034 send_change (what_changed);
1041 Region::set_state (const XMLNode& node)
1043 const XMLProperty *prop;
1044 Change what_changed = Change (0);
1046 /* ID is not allowed to change, ever */
1048 if ((prop = node.property ("id")) == 0) {
1049 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1053 _id = prop->value();
1055 _first_edit = EditChangesNothing;
1057 set_live_state (node, what_changed, true);
1069 Region::thaw (const string& why)
1071 Change what_changed = Change (0);
1074 Glib::Mutex::Lock lm (_lock);
1076 if (_frozen && --_frozen > 0) {
1080 if (_pending_changed) {
1081 what_changed = _pending_changed;
1082 _pending_changed = Change (0);
1086 if (what_changed == Change (0)) {
1090 if (what_changed & LengthChanged) {
1091 if (what_changed & PositionChanged) {
1092 recompute_at_start ();
1094 recompute_at_end ();
1097 StateChanged (what_changed);
1101 Region::send_change (Change what_changed)
1104 Glib::Mutex::Lock lm (_lock);
1106 _pending_changed = Change (_pending_changed|what_changed);
1111 StateChanged (what_changed);
1115 Region::set_last_layer_op (uint64_t when)
1117 _last_layer_op = when;
1121 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1123 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1127 Region::equivalent (boost::shared_ptr<const Region> other) const
1129 return _start == other->_start &&
1130 _position == other->_position &&
1131 _length == other->_length;
1135 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1137 return _start == other->_start &&
1138 _length == other->_length;
1142 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1144 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1148 Region::source_deleted (boost::shared_ptr<Source>)
1154 Region::master_source_names ()
1156 SourceList::iterator i;
1158 vector<string> names;
1159 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1160 names.push_back((*i)->name());
1167 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1172 SourceList::const_iterator i;
1173 SourceList::const_iterator io;
1175 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1176 if ((*i)->id() != (*io)->id()) {
1181 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1182 if ((*i)->id() != (*io)->id()) {
1191 Region::verify_length (jack_nframes_t len)
1193 for (uint32_t n=0; n < _sources.size(); ++n) {
1194 if (_start > _sources[n]->length() - len) {
1202 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1204 for (uint32_t n=0; n < _sources.size(); ++n) {
1205 if (new_length > _sources[n]->length() - new_start) {
1212 Region::verify_start (jack_nframes_t pos)
1214 for (uint32_t n=0; n < _sources.size(); ++n) {
1215 if (pos > _sources[n]->length() - _length) {
1223 Region::verify_start_mutable (jack_nframes_t& new_start)
1225 for (uint32_t n=0; n < _sources.size(); ++n) {
1226 if (new_start > _sources[n]->length() - _length) {
1227 new_start = _sources[n]->length() - _length;
1233 boost::shared_ptr<Region>
1234 Region::get_parent() const
1236 boost::shared_ptr<Playlist> pl (playlist());
1239 boost::shared_ptr<Region> r;
1240 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1242 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1243 return boost::static_pointer_cast<Region> (r);
1247 return boost::shared_ptr<Region>();