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>
40 using namespace ARDOUR;
43 Change Region::FadeChanged = ARDOUR::new_change ();
44 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
45 Change Region::MuteChanged = ARDOUR::new_change ();
46 Change Region::OpacityChanged = ARDOUR::new_change ();
47 Change Region::LockChanged = ARDOUR::new_change ();
48 Change Region::LayerChanged = ARDOUR::new_change ();
49 Change Region::HiddenChanged = ARDOUR::new_change ();
51 /** Basic Region constructor (single source) */
52 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)
59 , _sync_position(_start)
61 , _first_edit(EditChangesNothing)
64 , _pending_changed(Change (0))
68 _current_state_id = 0;
70 _sources.push_back (src);
71 _master_sources.push_back (src);
72 src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
74 assert(_sources.size() > 0);
77 /** Basic Region constructor (many sources) */
78 Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
85 , _sync_position(_start)
87 , _first_edit(EditChangesNothing)
90 , _pending_changed(Change (0))
94 _current_state_id = 0;
96 set<boost::shared_ptr<Source> > unique_srcs;
98 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
99 _sources.push_back (*i);
100 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
101 unique_srcs.insert (*i);
104 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
105 _master_sources.push_back (*i);
106 if (unique_srcs.find (*i) == unique_srcs.end()) {
107 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
111 assert(_sources.size() > 0);
114 /** Create a new Region from part of an existing one */
115 Region::Region (boost::shared_ptr<const Region> other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
117 , _type(other->data_type())
118 , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
119 , _start(other->_start + offset)
122 , _sync_position(_start)
124 , _first_edit(EditChangesNothing)
126 , _read_data_count(0)
127 , _pending_changed(Change (0))
131 _current_state_id = 0;
133 if (other->_sync_position < offset)
134 _sync_position = other->_sync_position;
136 set<boost::shared_ptr<Source> > unique_srcs;
138 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
139 _sources.push_back (*i);
140 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
141 unique_srcs.insert (*i);
144 if (other->_sync_position < offset) {
145 _sync_position = other->_sync_position;
148 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
149 if (unique_srcs.find (*i) == unique_srcs.end()) {
150 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
152 _master_sources.push_back (*i);
155 assert(_sources.size() > 0);
158 /** Pure copy constructor */
159 Region::Region (boost::shared_ptr<const Region> other)
160 : _name(other->_name)
161 , _type(other->data_type())
162 , _flags(Flag(other->_flags & ~Locked))
163 , _start(other->_start)
164 , _length(other->_length)
165 , _position(other->_position)
166 , _sync_position(other->_sync_position)
167 , _layer(other->_layer)
168 , _first_edit(EditChangesID)
170 , _read_data_count(0)
171 , _pending_changed(Change(0))
172 , _last_layer_op(other->_last_layer_op)
175 _current_state_id = 0;
177 other->_first_edit = EditChangesName;
179 if (other->_extra_xml) {
180 _extra_xml = new XMLNode (*other->_extra_xml);
185 set<boost::shared_ptr<Source> > unique_srcs;
187 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
188 _sources.push_back (*i);
189 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
190 unique_srcs.insert (*i);
193 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
194 _master_sources.push_back (*i);
195 if (unique_srcs.find (*i) == unique_srcs.end()) {
196 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
200 assert(_sources.size() > 0);
203 Region::Region (SourceList& srcs, const XMLNode& node)
204 : _name(X_("error: XML did not reset this"))
205 , _type(DataType::NIL) // to be loaded from XML
210 , _sync_position(_start)
212 , _first_edit(EditChangesNothing)
214 , _read_data_count(0)
215 , _pending_changed(Change(0))
220 _current_state_id = 0;
222 set<boost::shared_ptr<Source> > unique_srcs;
224 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
225 _sources.push_back (*i);
226 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
227 unique_srcs.insert (*i);
230 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
231 _master_sources.push_back (*i);
232 if (unique_srcs.find (*i) == unique_srcs.end()) {
233 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
237 if (set_state (node)) {
238 throw failed_constructor();
241 assert(_type != DataType::NIL);
242 assert(_sources.size() > 0);
245 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
246 : _name(X_("error: XML did not reset this"))
247 , _type(DataType::NIL)
252 , _sync_position(_start)
254 , _first_edit(EditChangesNothing)
256 , _read_data_count(0)
257 , _pending_changed(Change(0))
261 _sources.push_back (src);
263 _current_state_id = 0;
265 if (set_state (node)) {
266 throw failed_constructor();
269 assert(_type != DataType::NIL);
270 assert(_sources.size() > 0);
277 /* derived classes must emit GoingAway */
281 Region::set_playlist (Playlist* pl)
287 Region::store_state (RegionState& state) const
289 state._start = _start;
290 state._length = _length;
291 state._position = _position;
292 state._flags = _flags;
293 state._sync_position = _sync_position;
294 state._layer = _layer;
296 state._first_edit = _first_edit;
300 Region::restore_and_return_flags (RegionState& state)
302 Change what_changed = Change (0);
305 Glib::Mutex::Lock lm (_lock);
307 if (_start != state._start) {
308 what_changed = Change (what_changed|StartChanged);
309 _start = state._start;
311 if (_length != state._length) {
312 what_changed = Change (what_changed|LengthChanged);
313 _length = state._length;
315 if (_position != state._position) {
316 what_changed = Change (what_changed|PositionChanged);
317 _position = state._position;
319 if (_sync_position != state._sync_position) {
320 _sync_position = state._sync_position;
321 what_changed = Change (what_changed|SyncOffsetChanged);
323 if (_layer != state._layer) {
324 what_changed = Change (what_changed|LayerChanged);
325 _layer = state._layer;
328 uint32_t old_flags = _flags;
329 _flags = Flag (state._flags);
331 if ((old_flags ^ state._flags) & Muted) {
332 what_changed = Change (what_changed|MuteChanged);
334 if ((old_flags ^ state._flags) & Opaque) {
335 what_changed = Change (what_changed|OpacityChanged);
337 if ((old_flags ^ state._flags) & Locked) {
338 what_changed = Change (what_changed|LockChanged);
341 _first_edit = state._first_edit;
348 Region::set_name (string str)
353 send_change (NameChanged);
358 Region::set_length (jack_nframes_t len, void *src)
360 if (_flags & Locked) {
364 if (_length != len && len != 0) {
366 if (!verify_length (len)) {
372 _flags = Region::Flag (_flags & ~WholeFile);
381 snprintf (buf, sizeof (buf), "length set to %u", len);
385 send_change (LengthChanged);
390 Region::maybe_uncopy ()
395 Region::first_edit ()
397 if (_first_edit != EditChangesNothing && _playlist) {
399 _name = _playlist->session().new_region_name (_name);
400 _first_edit = EditChangesNothing;
402 send_change (NameChanged);
403 /// XXX CheckNewRegion (boost::shared_ptr<Region>(this));
408 Region::move_to_natural_position (void *src)
414 boost::shared_ptr<Region> whole_file_region = get_parent();
416 if (whole_file_region) {
417 set_position (whole_file_region->position() + _start, src);
422 Region::special_set_position (jack_nframes_t pos)
424 /* this is used when creating a whole file region as
425 a way to store its "natural" or "captured" position.
432 Region::set_position (jack_nframes_t pos, void *src)
434 if (_flags & Locked) {
438 if (_position != pos) {
443 snprintf (buf, sizeof (buf), "position set to %u", pos);
448 /* do this even if the position is the same. this helps out
449 a GUI that has moved its representation already.
452 send_change (PositionChanged);
456 Region::set_position_on_top (jack_nframes_t pos, void *src)
458 if (_flags & Locked) {
462 if (_position != pos) {
467 snprintf (buf, sizeof (buf), "position set to %u", pos);
472 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
474 /* do this even if the position is the same. this helps out
475 a GUI that has moved its representation already.
478 send_change (PositionChanged);
482 Region::nudge_position (long n, void *src)
484 if (_flags & Locked) {
493 if (_position > max_frames - n) {
494 _position = max_frames;
499 if (_position < (jack_nframes_t) -n) {
508 snprintf (buf, sizeof (buf), "position set to %u", _position);
512 send_change (PositionChanged);
516 Region::set_start (jack_nframes_t pos, void *src)
518 if (_flags & Locked) {
521 /* This just sets the start, nothing else. It effectively shifts
522 the contents of the Region within the overall extent of the Source,
523 without changing the Region's position or length
528 if (!verify_start (pos)) {
533 _flags = Region::Flag (_flags & ~WholeFile);
538 snprintf (buf, sizeof (buf), "start set to %u", pos);
542 send_change (StartChanged);
547 Region::trim_start (jack_nframes_t new_position, void *src)
549 if (_flags & Locked) {
552 jack_nframes_t new_start;
555 if (new_position > _position) {
556 start_shift = new_position - _position;
558 start_shift = -(_position - new_position);
561 if (start_shift > 0) {
563 if (_start > max_frames - start_shift) {
564 new_start = max_frames;
566 new_start = _start + start_shift;
569 if (!verify_start (new_start)) {
573 } else if (start_shift < 0) {
575 if (_start < (jack_nframes_t) -start_shift) {
578 new_start = _start + start_shift;
584 if (new_start == _start) {
589 _flags = Region::Flag (_flags & ~WholeFile);
594 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
598 send_change (StartChanged);
602 Region::trim_front (jack_nframes_t new_position, void *src)
604 if (_flags & Locked) {
608 jack_nframes_t end = _position + _length - 1;
609 jack_nframes_t source_zero;
611 if (_position > _start) {
612 source_zero = _position - _start;
614 source_zero = 0; // its actually negative, but this will work for us
617 if (new_position < end) { /* can't trim it zero or negative length */
619 jack_nframes_t newlen;
621 /* can't trim it back passed where source position zero is located */
623 new_position = max (new_position, source_zero);
626 if (new_position > _position) {
627 newlen = _length - (new_position - _position);
629 newlen = _length + (_position - new_position);
632 trim_to_internal (new_position, newlen, src);
634 recompute_at_start ();
640 Region::trim_end (jack_nframes_t new_endpoint, void *src)
642 if (_flags & Locked) {
646 if (new_endpoint > _position) {
647 trim_to_internal (_position, new_endpoint - _position, this);
655 Region::trim_to (jack_nframes_t position, jack_nframes_t length, void *src)
657 if (_flags & Locked) {
661 trim_to_internal (position, length, src);
664 recompute_at_start ();
670 Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void *src)
673 jack_nframes_t new_start;
675 if (_flags & Locked) {
679 if (position > _position) {
680 start_shift = position - _position;
682 start_shift = -(_position - position);
685 if (start_shift > 0) {
687 if (_start > max_frames - start_shift) {
688 new_start = max_frames;
690 new_start = _start + start_shift;
694 } else if (start_shift < 0) {
696 if (_start < (jack_nframes_t) -start_shift) {
699 new_start = _start + start_shift;
705 if (!verify_start_and_length (new_start, length)) {
709 Change what_changed = Change (0);
711 if (_start != new_start) {
713 what_changed = Change (what_changed|StartChanged);
715 if (_length != length) {
717 what_changed = Change (what_changed|LengthChanged);
719 if (_position != position) {
720 _position = position;
721 what_changed = Change (what_changed|PositionChanged);
724 _flags = Region::Flag (_flags & ~WholeFile);
726 if (what_changed & (StartChanged|LengthChanged)) {
734 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
738 send_change (what_changed);
743 Region::set_hidden (bool yn)
745 if (hidden() != yn) {
748 _flags = Flag (_flags|Hidden);
750 _flags = Flag (_flags & ~Hidden);
753 send_change (HiddenChanged);
758 Region::set_muted (bool yn)
763 _flags = Flag (_flags|Muted);
765 _flags = Flag (_flags & ~Muted);
771 snprintf (buf, sizeof (buf), "muted");
773 snprintf (buf, sizeof (buf), "unmuted");
778 send_change (MuteChanged);
783 Region::set_opaque (bool yn)
785 if (opaque() != yn) {
789 snprintf (buf, sizeof (buf), "opaque");
790 _flags = Flag (_flags|Opaque);
792 snprintf (buf, sizeof (buf), "translucent");
793 _flags = Flag (_flags & ~Opaque);
797 send_change (OpacityChanged);
802 Region::set_locked (bool yn)
804 if (locked() != yn) {
808 snprintf (buf, sizeof (buf), "locked");
809 _flags = Flag (_flags|Locked);
811 snprintf (buf, sizeof (buf), "unlocked");
812 _flags = Flag (_flags & ~Locked);
816 send_change (LockChanged);
821 Region::set_sync_position (jack_nframes_t absolute_pos)
823 jack_nframes_t file_pos;
825 file_pos = _start + (absolute_pos - _position);
827 if (file_pos != _sync_position) {
829 _sync_position = file_pos;
830 _flags = Flag (_flags|SyncMarked);
835 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
838 send_change (SyncOffsetChanged);
843 Region::clear_sync_position ()
845 if (_flags & SyncMarked) {
846 _flags = Flag (_flags & ~SyncMarked);
850 save_state ("sync point removed");
852 send_change (SyncOffsetChanged);
857 Region::sync_offset (int& dir) const
859 /* returns the sync point relative the first frame of the region */
861 if (_flags & SyncMarked) {
862 if (_sync_position > _start) {
864 return _sync_position - _start;
867 return _start - _sync_position;
876 Region::adjust_to_sync (jack_nframes_t pos)
879 jack_nframes_t offset = sync_offset (sync_dir);
882 if (max_frames - pos > offset) {
897 Region::sync_position() const
899 if (_flags & SyncMarked) {
900 return _sync_position;
910 if (_playlist == 0) {
914 _playlist->raise_region (boost::shared_ptr<Region>(this));
920 if (_playlist == 0) {
924 _playlist->lower_region (boost::shared_ptr<Region>(this));
928 Region::raise_to_top ()
931 if (_playlist == 0) {
935 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
939 Region::lower_to_bottom ()
941 if (_playlist == 0) {
945 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
949 Region::set_layer (layer_t l)
956 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
960 send_change (LayerChanged);
965 Region::state (bool full_state)
967 XMLNode *node = new XMLNode ("Region");
971 node->add_property ("id", buf);
972 node->add_property ("name", _name);
973 node->add_property ("type", _type.to_string());
974 snprintf (buf, sizeof (buf), "%u", _start);
975 node->add_property ("start", buf);
976 snprintf (buf, sizeof (buf), "%u", _length);
977 node->add_property ("length", buf);
978 snprintf (buf, sizeof (buf), "%u", _position);
979 node->add_property ("position", buf);
981 /* note: flags are stored by derived classes */
983 snprintf (buf, sizeof (buf), "%d", (int) _layer);
984 node->add_property ("layer", buf);
985 snprintf (buf, sizeof (buf), "%u", _sync_position);
986 node->add_property ("sync-position", buf);
998 Region::set_state (const XMLNode& node)
1000 const XMLNodeList& nlist = node.children();
1001 const XMLProperty *prop;
1008 if ((prop = node.property ("id")) == 0) {
1009 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1013 _id = prop->value();
1015 if ((prop = node.property ("name")) == 0) {
1016 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
1020 _name = prop->value();
1022 if ((prop = node.property ("type")) == 0) {
1023 _type = DataType::AUDIO;
1025 _type = DataType(prop->value());
1028 if ((prop = node.property ("start")) != 0) {
1029 _start = (jack_nframes_t) atoi (prop->value().c_str());
1032 if ((prop = node.property ("length")) != 0) {
1033 _length = (jack_nframes_t) atoi (prop->value().c_str());
1036 if ((prop = node.property ("position")) != 0) {
1037 _position = (jack_nframes_t) atoi (prop->value().c_str());
1040 if ((prop = node.property ("layer")) != 0) {
1041 _layer = (layer_t) atoi (prop->value().c_str());
1044 /* note: derived classes set flags */
1046 if ((prop = node.property ("sync-position")) != 0) {
1047 _sync_position = (jack_nframes_t) atoi (prop->value().c_str());
1049 _sync_position = _start;
1052 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1058 if (child->name () == "extra") {
1059 _extra_xml = new XMLNode (*child);
1064 _first_edit = EditChangesNothing;
1076 Region::thaw (const string& why)
1078 Change what_changed = Change (0);
1081 Glib::Mutex::Lock lm (_lock);
1083 if (_frozen && --_frozen > 0) {
1087 if (_pending_changed) {
1088 what_changed = _pending_changed;
1089 _pending_changed = Change (0);
1093 if (what_changed == Change (0)) {
1097 if (what_changed & LengthChanged) {
1098 if (what_changed & PositionChanged) {
1099 recompute_at_start ();
1101 recompute_at_end ();
1105 StateChanged (what_changed);
1109 Region::send_change (Change what_changed)
1112 Glib::Mutex::Lock lm (_lock);
1114 _pending_changed = Change (_pending_changed|what_changed);
1119 StateManager::send_state_changed (what_changed);
1123 Region::set_last_layer_op (uint64_t when)
1125 _last_layer_op = when;
1129 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1131 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1135 Region::equivalent (boost::shared_ptr<const Region> other) const
1137 return _start == other->_start &&
1138 _position == other->_position &&
1139 _length == other->_length;
1143 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1145 return _start == other->_start &&
1146 _length == other->_length;
1150 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1152 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1156 Region::source_deleted (boost::shared_ptr<Source>)
1162 Region::master_source_names ()
1164 SourceList::iterator i;
1166 vector<string> names;
1167 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1168 names.push_back((*i)->name());
1175 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1180 SourceList::const_iterator i;
1181 SourceList::const_iterator io;
1183 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1184 if ((*i)->id() != (*io)->id()) {
1189 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1190 if ((*i)->id() != (*io)->id()) {
1199 Region::verify_length (jack_nframes_t len)
1201 for (uint32_t n=0; n < _sources.size(); ++n) {
1202 if (_start > _sources[n]->length() - len) {
1210 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1212 for (uint32_t n=0; n < _sources.size(); ++n) {
1213 if (new_length > _sources[n]->length() - new_start) {
1220 Region::verify_start (jack_nframes_t pos)
1222 for (uint32_t n=0; n < _sources.size(); ++n) {
1223 if (pos > _sources[n]->length() - _length) {
1231 Region::verify_start_mutable (jack_nframes_t& new_start)
1233 for (uint32_t n=0; n < _sources.size(); ++n) {
1234 if (new_start > _sources[n]->length() - _length) {
1235 new_start = _sources[n]->length() - _length;
1241 boost::shared_ptr<Region>
1242 Region::get_parent()
1244 boost::shared_ptr<Region> r;
1247 r = _playlist->session().find_whole_file_parent (*this);