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>
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))
69 _current_state_id = 0;
71 _sources.push_back (src);
72 _master_sources.push_back (src);
73 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
75 assert(_sources.size() > 0);
78 /** Basic Region constructor (many sources) */
79 Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
86 , _sync_position(_start)
88 , _first_edit(EditChangesNothing)
91 , _pending_changed(Change (0))
95 _current_state_id = 0;
97 set<boost::shared_ptr<Source> > unique_srcs;
99 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
100 _sources.push_back (*i);
101 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
102 unique_srcs.insert (*i);
105 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
106 _master_sources.push_back (*i);
107 if (unique_srcs.find (*i) == unique_srcs.end()) {
108 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
112 assert(_sources.size() > 0);
115 /** Create a new Region from part of an existing one */
116 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
118 , _type(other->data_type())
119 , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
120 , _start(other->_start + offset)
123 , _sync_position(_start)
125 , _first_edit(EditChangesNothing)
127 , _read_data_count(0)
128 , _pending_changed(Change (0))
132 _current_state_id = 0;
134 if (other->_sync_position < offset)
135 _sync_position = other->_sync_position;
137 set<boost::shared_ptr<Source> > unique_srcs;
139 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
140 _sources.push_back (*i);
141 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
142 unique_srcs.insert (*i);
145 if (other->_sync_position < offset) {
146 _sync_position = other->_sync_position;
149 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
150 if (unique_srcs.find (*i) == unique_srcs.end()) {
151 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
153 _master_sources.push_back (*i);
156 assert(_sources.size() > 0);
159 /** Pure copy constructor */
160 Region::Region (boost::shared_ptr<const Region> other)
161 : _name(other->_name)
162 , _type(other->data_type())
163 , _flags(Flag(other->_flags & ~Locked))
164 , _start(other->_start)
165 , _length(other->_length)
166 , _position(other->_position)
167 , _sync_position(other->_sync_position)
168 , _layer(other->_layer)
169 , _first_edit(EditChangesID)
171 , _read_data_count(0)
172 , _pending_changed(Change(0))
173 , _last_layer_op(other->_last_layer_op)
176 _current_state_id = 0;
178 other->_first_edit = EditChangesName;
180 if (other->_extra_xml) {
181 _extra_xml = new XMLNode (*other->_extra_xml);
186 set<boost::shared_ptr<Source> > unique_srcs;
188 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
189 _sources.push_back (*i);
190 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
191 unique_srcs.insert (*i);
194 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
195 _master_sources.push_back (*i);
196 if (unique_srcs.find (*i) == unique_srcs.end()) {
197 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
201 assert(_sources.size() > 0);
204 Region::Region (SourceList& srcs, const XMLNode& node)
205 : _name(X_("error: XML did not reset this"))
206 , _type(DataType::NIL) // to be loaded from XML
211 , _sync_position(_start)
213 , _first_edit(EditChangesNothing)
215 , _read_data_count(0)
216 , _pending_changed(Change(0))
221 _current_state_id = 0;
223 set<boost::shared_ptr<Source> > unique_srcs;
225 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
226 _sources.push_back (*i);
227 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
228 unique_srcs.insert (*i);
231 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
232 _master_sources.push_back (*i);
233 if (unique_srcs.find (*i) == unique_srcs.end()) {
234 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
238 if (set_state (node)) {
239 throw failed_constructor();
242 assert(_type != DataType::NIL);
243 assert(_sources.size() > 0);
246 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
247 : _name(X_("error: XML did not reset this"))
248 , _type(DataType::NIL)
253 , _sync_position(_start)
255 , _first_edit(EditChangesNothing)
257 , _read_data_count(0)
258 , _pending_changed(Change(0))
262 _sources.push_back (src);
264 _current_state_id = 0;
266 if (set_state (node)) {
267 throw failed_constructor();
270 assert(_type != DataType::NIL);
271 assert(_sources.size() > 0);
276 /* derived classes must call notify_callbacks() and then emit GoingAway */
280 Region::set_playlist (Playlist* pl)
286 Region::store_state (RegionState& state) const
288 state._start = _start;
289 state._length = _length;
290 state._position = _position;
291 state._flags = _flags;
292 state._sync_position = _sync_position;
293 state._layer = _layer;
295 state._first_edit = _first_edit;
299 Region::restore_and_return_flags (RegionState& state)
301 Change what_changed = Change (0);
304 Glib::Mutex::Lock lm (_lock);
306 if (_start != state._start) {
307 what_changed = Change (what_changed|StartChanged);
308 _start = state._start;
310 if (_length != state._length) {
311 what_changed = Change (what_changed|LengthChanged);
312 _length = state._length;
314 if (_position != state._position) {
315 what_changed = Change (what_changed|PositionChanged);
316 _position = state._position;
318 if (_sync_position != state._sync_position) {
319 _sync_position = state._sync_position;
320 what_changed = Change (what_changed|SyncOffsetChanged);
322 if (_layer != state._layer) {
323 what_changed = Change (what_changed|LayerChanged);
324 _layer = state._layer;
327 uint32_t old_flags = _flags;
328 _flags = Flag (state._flags);
330 if ((old_flags ^ state._flags) & Muted) {
331 what_changed = Change (what_changed|MuteChanged);
333 if ((old_flags ^ state._flags) & Opaque) {
334 what_changed = Change (what_changed|OpacityChanged);
336 if ((old_flags ^ state._flags) & Locked) {
337 what_changed = Change (what_changed|LockChanged);
340 _first_edit = state._first_edit;
347 Region::set_name (string str)
352 send_change (NameChanged);
357 Region::set_length (nframes_t len, void *src)
359 if (_flags & Locked) {
363 if (_length != len && len != 0) {
365 /* check that the current _position wouldn't make the new
369 if (max_frames - len < _position) {
373 if (!verify_length (len)) {
379 _flags = Region::Flag (_flags & ~WholeFile);
388 snprintf (buf, sizeof (buf), "length set to %u", len);
392 send_change (LengthChanged);
397 Region::maybe_uncopy ()
402 Region::first_edit ()
404 if (_first_edit != EditChangesNothing && _playlist) {
406 _name = _playlist->session().new_region_name (_name);
407 _first_edit = EditChangesNothing;
409 send_change (NameChanged);
410 RegionFactory::CheckNewRegion (shared_from_this());
415 Region::move_to_natural_position (void *src)
421 boost::shared_ptr<Region> whole_file_region = get_parent();
423 if (whole_file_region) {
424 set_position (whole_file_region->position() + _start, src);
429 Region::special_set_position (nframes_t pos)
431 /* this is used when creating a whole file region as
432 a way to store its "natural" or "captured" position.
439 Region::set_position (nframes_t pos, void *src)
441 if (_flags & Locked) {
445 if (_position != pos) {
448 /* check that the new _position wouldn't make the current
449 length impossible - if so, change the length.
451 XXX is this the right thing to do?
454 if (max_frames - _length < _position) {
455 _length = max_frames - _position;
460 snprintf (buf, sizeof (buf), "position set to %u", pos);
465 /* do this even if the position is the same. this helps out
466 a GUI that has moved its representation already.
469 send_change (PositionChanged);
473 Region::set_position_on_top (nframes_t pos, void *src)
475 if (_flags & Locked) {
479 if (_position != pos) {
484 snprintf (buf, sizeof (buf), "position set to %u", pos);
489 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
491 /* do this even if the position is the same. this helps out
492 a GUI that has moved its representation already.
495 send_change (PositionChanged);
499 Region::nudge_position (long n, void *src)
501 if (_flags & Locked) {
510 if (_position > max_frames - n) {
511 _position = max_frames;
516 if (_position < (nframes_t) -n) {
525 snprintf (buf, sizeof (buf), "position set to %u", _position);
529 send_change (PositionChanged);
533 Region::set_start (nframes_t pos, void *src)
535 if (_flags & Locked) {
538 /* This just sets the start, nothing else. It effectively shifts
539 the contents of the Region within the overall extent of the Source,
540 without changing the Region's position or length
545 if (!verify_start (pos)) {
550 _flags = Region::Flag (_flags & ~WholeFile);
555 snprintf (buf, sizeof (buf), "start set to %u", pos);
559 send_change (StartChanged);
564 Region::trim_start (nframes_t new_position, void *src)
566 if (_flags & Locked) {
572 if (new_position > _position) {
573 start_shift = new_position - _position;
575 start_shift = -(_position - new_position);
578 if (start_shift > 0) {
580 if (_start > max_frames - start_shift) {
581 new_start = max_frames;
583 new_start = _start + start_shift;
586 if (!verify_start (new_start)) {
590 } else if (start_shift < 0) {
592 if (_start < (nframes_t) -start_shift) {
595 new_start = _start + start_shift;
601 if (new_start == _start) {
606 _flags = Region::Flag (_flags & ~WholeFile);
611 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
615 send_change (StartChanged);
619 Region::trim_front (nframes_t new_position, void *src)
621 if (_flags & Locked) {
625 nframes_t end = last_frame();
626 nframes_t source_zero;
628 if (_position > _start) {
629 source_zero = _position - _start;
631 source_zero = 0; // its actually negative, but this will work for us
634 if (new_position < end) { /* can't trim it zero or negative length */
638 /* can't trim it back passed where source position zero is located */
640 new_position = max (new_position, source_zero);
643 if (new_position > _position) {
644 newlen = _length - (new_position - _position);
646 newlen = _length + (_position - new_position);
649 trim_to_internal (new_position, newlen, src);
651 recompute_at_start ();
657 Region::trim_end (nframes_t new_endpoint, void *src)
659 if (_flags & Locked) {
663 if (new_endpoint > _position) {
664 trim_to_internal (_position, new_endpoint - _position, this);
672 Region::trim_to (nframes_t position, nframes_t length, void *src)
674 if (_flags & Locked) {
678 trim_to_internal (position, length, src);
681 recompute_at_start ();
687 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
692 if (_flags & Locked) {
696 if (position > _position) {
697 start_shift = position - _position;
699 start_shift = -(_position - position);
702 if (start_shift > 0) {
704 if (_start > max_frames - start_shift) {
705 new_start = max_frames;
707 new_start = _start + start_shift;
711 } else if (start_shift < 0) {
713 if (_start < (nframes_t) -start_shift) {
716 new_start = _start + start_shift;
722 if (!verify_start_and_length (new_start, length)) {
726 Change what_changed = Change (0);
728 if (_start != new_start) {
730 what_changed = Change (what_changed|StartChanged);
732 if (_length != length) {
734 what_changed = Change (what_changed|LengthChanged);
736 if (_position != position) {
737 _position = position;
738 what_changed = Change (what_changed|PositionChanged);
741 _flags = Region::Flag (_flags & ~WholeFile);
743 if (what_changed & (StartChanged|LengthChanged)) {
751 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
755 send_change (what_changed);
760 Region::set_hidden (bool yn)
762 if (hidden() != yn) {
765 _flags = Flag (_flags|Hidden);
767 _flags = Flag (_flags & ~Hidden);
770 send_change (HiddenChanged);
775 Region::set_muted (bool yn)
780 _flags = Flag (_flags|Muted);
782 _flags = Flag (_flags & ~Muted);
788 snprintf (buf, sizeof (buf), "muted");
790 snprintf (buf, sizeof (buf), "unmuted");
795 send_change (MuteChanged);
800 Region::set_opaque (bool yn)
802 if (opaque() != yn) {
806 snprintf (buf, sizeof (buf), "opaque");
807 _flags = Flag (_flags|Opaque);
809 snprintf (buf, sizeof (buf), "translucent");
810 _flags = Flag (_flags & ~Opaque);
814 send_change (OpacityChanged);
819 Region::set_locked (bool yn)
821 if (locked() != yn) {
825 snprintf (buf, sizeof (buf), "locked");
826 _flags = Flag (_flags|Locked);
828 snprintf (buf, sizeof (buf), "unlocked");
829 _flags = Flag (_flags & ~Locked);
833 send_change (LockChanged);
838 Region::set_sync_position (nframes_t absolute_pos)
842 file_pos = _start + (absolute_pos - _position);
844 if (file_pos != _sync_position) {
846 _sync_position = file_pos;
847 _flags = Flag (_flags|SyncMarked);
852 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
855 send_change (SyncOffsetChanged);
860 Region::clear_sync_position ()
862 if (_flags & SyncMarked) {
863 _flags = Flag (_flags & ~SyncMarked);
867 save_state ("sync point removed");
869 send_change (SyncOffsetChanged);
874 Region::sync_offset (int& dir) const
876 /* returns the sync point relative the first frame of the region */
878 if (_flags & SyncMarked) {
879 if (_sync_position > _start) {
881 return _sync_position - _start;
884 return _start - _sync_position;
893 Region::adjust_to_sync (nframes_t pos)
896 nframes_t offset = sync_offset (sync_dir);
899 if (max_frames - pos > offset) {
914 Region::sync_position() const
916 if (_flags & SyncMarked) {
917 return _sync_position;
927 if (_playlist == 0) {
931 _playlist->raise_region (boost::shared_ptr<Region>(this));
937 if (_playlist == 0) {
941 _playlist->lower_region (boost::shared_ptr<Region>(this));
945 Region::raise_to_top ()
948 if (_playlist == 0) {
952 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
956 Region::lower_to_bottom ()
958 if (_playlist == 0) {
962 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
966 Region::set_layer (layer_t l)
973 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
977 send_change (LayerChanged);
982 Region::state (bool full_state)
984 XMLNode *node = new XMLNode ("Region");
987 _id.print (buf, sizeof (buf));
988 node->add_property ("id", buf);
989 node->add_property ("name", _name);
990 node->add_property ("type", _type.to_string());
991 snprintf (buf, sizeof (buf), "%u", _start);
992 node->add_property ("start", buf);
993 snprintf (buf, sizeof (buf), "%u", _length);
994 node->add_property ("length", buf);
995 snprintf (buf, sizeof (buf), "%u", _position);
996 node->add_property ("position", buf);
998 /* note: flags are stored by derived classes */
1000 snprintf (buf, sizeof (buf), "%d", (int) _layer);
1001 node->add_property ("layer", buf);
1002 snprintf (buf, sizeof (buf), "%u", _sync_position);
1003 node->add_property ("sync-position", buf);
1009 Region::get_state ()
1011 return state (true);
1015 Region::set_state (const XMLNode& node)
1017 const XMLNodeList& nlist = node.children();
1018 const XMLProperty *prop;
1025 if ((prop = node.property ("id")) == 0) {
1026 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1030 _id = prop->value();
1032 if ((prop = node.property ("name")) == 0) {
1033 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
1037 _name = prop->value();
1039 if ((prop = node.property ("type")) == 0) {
1040 _type = DataType::AUDIO;
1042 _type = DataType(prop->value());
1045 if ((prop = node.property ("start")) != 0) {
1046 sscanf (prop->value().c_str(), "%" PRIu32, &_start);
1049 if ((prop = node.property ("length")) != 0) {
1050 sscanf (prop->value().c_str(), "%" PRIu32, &_length);
1053 if ((prop = node.property ("position")) != 0) {
1054 sscanf (prop->value().c_str(), "%" PRIu32, &_position);
1057 if ((prop = node.property ("layer")) != 0) {
1058 _layer = (layer_t) atoi (prop->value().c_str());
1061 /* note: derived classes set flags */
1063 if ((prop = node.property ("sync-position")) != 0) {
1064 sscanf (prop->value().c_str(), "%" PRIu32, &_sync_position);
1066 _sync_position = _start;
1069 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1075 if (child->name () == "extra") {
1076 _extra_xml = new XMLNode (*child);
1081 _first_edit = EditChangesNothing;
1093 Region::thaw (const string& why)
1095 Change what_changed = Change (0);
1098 Glib::Mutex::Lock lm (_lock);
1100 if (_frozen && --_frozen > 0) {
1104 if (_pending_changed) {
1105 what_changed = _pending_changed;
1106 _pending_changed = Change (0);
1110 if (what_changed == Change (0)) {
1114 if (what_changed & LengthChanged) {
1115 if (what_changed & PositionChanged) {
1116 recompute_at_start ();
1118 recompute_at_end ();
1122 StateChanged (what_changed);
1126 Region::send_change (Change what_changed)
1129 Glib::Mutex::Lock lm (_lock);
1131 _pending_changed = Change (_pending_changed|what_changed);
1136 StateManager::send_state_changed (what_changed);
1140 Region::set_last_layer_op (uint64_t when)
1142 _last_layer_op = when;
1146 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1148 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1152 Region::equivalent (boost::shared_ptr<const Region> other) const
1154 return _start == other->_start &&
1155 _position == other->_position &&
1156 _length == other->_length;
1160 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1162 return _start == other->_start &&
1163 _length == other->_length;
1167 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1169 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1173 Region::source_deleted (boost::shared_ptr<Source>)
1179 Region::master_source_names ()
1181 SourceList::iterator i;
1183 vector<string> names;
1184 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1185 names.push_back((*i)->name());
1192 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1197 SourceList::const_iterator i;
1198 SourceList::const_iterator io;
1200 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1201 if ((*i)->id() != (*io)->id()) {
1206 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1207 if ((*i)->id() != (*io)->id()) {
1216 Region::verify_length (jack_nframes_t len)
1218 for (uint32_t n=0; n < _sources.size(); ++n) {
1219 if (_start > _sources[n]->length() - len) {
1227 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1229 for (uint32_t n=0; n < _sources.size(); ++n) {
1230 if (new_length > _sources[n]->length() - new_start) {
1237 Region::verify_start (jack_nframes_t pos)
1239 for (uint32_t n=0; n < _sources.size(); ++n) {
1240 if (pos > _sources[n]->length() - _length) {
1248 Region::verify_start_mutable (jack_nframes_t& new_start)
1250 for (uint32_t n=0; n < _sources.size(); ++n) {
1251 if (new_start > _sources[n]->length() - _length) {
1252 new_start = _sources[n]->length() - _length;
1258 boost::shared_ptr<Region>
1259 Region::get_parent()
1261 boost::shared_ptr<Region> r;
1264 r = _playlist->session().find_whole_file_parent (*this);