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>
31 #include <pbd/stacktrace.h>
33 #include <ardour/region.h>
34 #include <ardour/playlist.h>
35 #include <ardour/session.h>
36 #include <ardour/source.h>
37 #include <ardour/region_factory.h>
42 using namespace ARDOUR;
45 Change Region::FadeChanged = ARDOUR::new_change ();
46 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
47 Change Region::MuteChanged = ARDOUR::new_change ();
48 Change Region::OpacityChanged = ARDOUR::new_change ();
49 Change Region::LockChanged = ARDOUR::new_change ();
50 Change Region::LayerChanged = ARDOUR::new_change ();
51 Change Region::HiddenChanged = ARDOUR::new_change ();
53 /** Basic Region constructor (single source) */
54 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)
61 , _sync_position(_start)
63 , _first_edit(EditChangesNothing)
66 , _pending_changed(Change (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))
95 set<boost::shared_ptr<Source> > unique_srcs;
97 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
98 _sources.push_back (*i);
99 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
100 unique_srcs.insert (*i);
103 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
104 _master_sources.push_back (*i);
105 if (unique_srcs.find (*i) == unique_srcs.end()) {
106 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
110 assert(_sources.size() > 0);
113 /** Create a new Region from part of an existing one */
114 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
116 , _type(other->data_type())
117 , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
118 , _start(other->_start + offset)
121 , _sync_position(_start)
123 , _first_edit(EditChangesNothing)
125 , _read_data_count(0)
126 , _pending_changed(Change (0))
130 if (other->_sync_position < offset)
131 _sync_position = other->_sync_position;
133 set<boost::shared_ptr<Source> > unique_srcs;
135 for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
136 _sources.push_back (*i);
137 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
138 unique_srcs.insert (*i);
141 if (other->_sync_position < offset) {
142 _sync_position = other->_sync_position;
145 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
146 if (unique_srcs.find (*i) == unique_srcs.end()) {
147 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
149 _master_sources.push_back (*i);
152 assert(_sources.size() > 0);
155 /** Pure copy constructor */
156 Region::Region (boost::shared_ptr<const Region> other)
157 : _name(other->_name)
158 , _type(other->data_type())
159 , _flags(Flag(other->_flags & ~Locked))
160 , _start(other->_start)
161 , _length(other->_length)
162 , _position(other->_position)
163 , _sync_position(other->_sync_position)
164 , _layer(other->_layer)
165 , _first_edit(EditChangesID)
167 , _read_data_count(0)
168 , _pending_changed(Change(0))
169 , _last_layer_op(other->_last_layer_op)
172 other->_first_edit = EditChangesName;
174 if (other->_extra_xml) {
175 _extra_xml = new XMLNode (*other->_extra_xml);
180 set<boost::shared_ptr<Source> > unique_srcs;
182 for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
183 _sources.push_back (*i);
184 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
185 unique_srcs.insert (*i);
188 for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
189 _master_sources.push_back (*i);
190 if (unique_srcs.find (*i) == unique_srcs.end()) {
191 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
195 assert(_sources.size() > 0);
198 Region::Region (SourceList& srcs, const XMLNode& node)
199 : _name(X_("error: XML did not reset this"))
200 , _type(DataType::NIL) // to be loaded from XML
205 , _sync_position(_start)
207 , _first_edit(EditChangesNothing)
209 , _read_data_count(0)
210 , _pending_changed(Change(0))
216 set<boost::shared_ptr<Source> > unique_srcs;
218 for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
219 _sources.push_back (*i);
220 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
221 unique_srcs.insert (*i);
224 for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
225 _master_sources.push_back (*i);
226 if (unique_srcs.find (*i) == unique_srcs.end()) {
227 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
231 if (set_state (node)) {
232 throw failed_constructor();
235 assert(_type != DataType::NIL);
236 assert(_sources.size() > 0);
239 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
240 : _name(X_("error: XML did not reset this"))
241 , _type(DataType::NIL)
246 , _sync_position(_start)
248 , _first_edit(EditChangesNothing)
250 , _read_data_count(0)
251 , _pending_changed(Change(0))
255 _sources.push_back (src);
258 if (set_state (node)) {
259 throw failed_constructor();
262 assert(_type != DataType::NIL);
263 assert(_sources.size() > 0);
268 /* derived classes must call notify_callbacks() and then emit GoingAway */
272 Region::set_playlist (Playlist* pl)
278 Region::set_name (string str)
282 send_change (NameChanged);
287 Region::set_length (nframes_t len, void *src)
289 if (_flags & Locked) {
293 if (_length != len && len != 0) {
295 /* check that the current _position wouldn't make the new
299 if (max_frames - len < _position) {
303 if (!verify_length (len)) {
309 _flags = Region::Flag (_flags & ~WholeFile);
318 send_change (LengthChanged);
323 Region::maybe_uncopy ()
328 Region::first_edit ()
330 if (_first_edit != EditChangesNothing && _playlist) {
332 _name = _playlist->session().new_region_name (_name);
333 _first_edit = EditChangesNothing;
335 send_change (NameChanged);
336 RegionFactory::CheckNewRegion (shared_from_this());
341 Region::move_to_natural_position (void *src)
347 boost::shared_ptr<Region> whole_file_region = get_parent();
349 if (whole_file_region) {
350 set_position (whole_file_region->position() + _start, src);
355 Region::special_set_position (nframes_t pos)
357 /* this is used when creating a whole file region as
358 a way to store its "natural" or "captured" position.
365 Region::set_position (nframes_t pos, void *src)
367 if (_flags & Locked) {
371 if (_position != pos) {
374 /* check that the new _position wouldn't make the current
375 length impossible - if so, change the length.
377 XXX is this the right thing to do?
380 if (max_frames - _length < _position) {
381 _length = max_frames - _position;
385 /* do this even if the position is the same. this helps out
386 a GUI that has moved its representation already.
389 send_change (PositionChanged);
393 Region::set_position_on_top (nframes_t pos, void *src)
395 if (_flags & Locked) {
399 if (_position != pos) {
403 _playlist->raise_region_to_top (shared_from_this ());
405 /* do this even if the position is the same. this helps out
406 a GUI that has moved its representation already.
409 send_change (PositionChanged);
413 Region::nudge_position (long n, void *src)
415 if (_flags & Locked) {
424 if (_position > max_frames - n) {
425 _position = max_frames;
430 if (_position < (nframes_t) -n) {
437 send_change (PositionChanged);
441 Region::set_start (nframes_t pos, void *src)
443 if (_flags & Locked) {
446 /* This just sets the start, nothing else. It effectively shifts
447 the contents of the Region within the overall extent of the Source,
448 without changing the Region's position or length
453 if (!verify_start (pos)) {
458 _flags = Region::Flag (_flags & ~WholeFile);
461 send_change (StartChanged);
466 Region::trim_start (nframes_t new_position, void *src)
468 if (_flags & Locked) {
474 if (new_position > _position) {
475 start_shift = new_position - _position;
477 start_shift = -(_position - new_position);
480 if (start_shift > 0) {
482 if (_start > max_frames - start_shift) {
483 new_start = max_frames;
485 new_start = _start + start_shift;
488 if (!verify_start (new_start)) {
492 } else if (start_shift < 0) {
494 if (_start < (nframes_t) -start_shift) {
497 new_start = _start + start_shift;
503 if (new_start == _start) {
508 _flags = Region::Flag (_flags & ~WholeFile);
511 send_change (StartChanged);
515 Region::trim_front (nframes_t new_position, void *src)
517 if (_flags & Locked) {
521 nframes_t end = last_frame();
522 nframes_t source_zero;
524 if (_position > _start) {
525 source_zero = _position - _start;
527 source_zero = 0; // its actually negative, but this will work for us
530 if (new_position < end) { /* can't trim it zero or negative length */
534 /* can't trim it back passed where source position zero is located */
536 new_position = max (new_position, source_zero);
539 if (new_position > _position) {
540 newlen = _length - (new_position - _position);
542 newlen = _length + (_position - new_position);
545 trim_to_internal (new_position, newlen, src);
547 recompute_at_start ();
553 Region::trim_end (nframes_t new_endpoint, void *src)
555 if (_flags & Locked) {
559 if (new_endpoint > _position) {
560 trim_to_internal (_position, new_endpoint - _position, this);
568 Region::trim_to (nframes_t position, nframes_t length, void *src)
570 if (_flags & Locked) {
574 trim_to_internal (position, length, src);
577 recompute_at_start ();
583 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
588 if (_flags & Locked) {
592 if (position > _position) {
593 start_shift = position - _position;
595 start_shift = -(_position - position);
598 if (start_shift > 0) {
600 if (_start > max_frames - start_shift) {
601 new_start = max_frames;
603 new_start = _start + start_shift;
607 } else if (start_shift < 0) {
609 if (_start < (nframes_t) -start_shift) {
612 new_start = _start + start_shift;
618 if (!verify_start_and_length (new_start, length)) {
622 Change what_changed = Change (0);
624 if (_start != new_start) {
626 what_changed = Change (what_changed|StartChanged);
628 if (_length != length) {
630 what_changed = Change (what_changed|LengthChanged);
632 if (_position != position) {
633 _position = position;
634 what_changed = Change (what_changed|PositionChanged);
637 _flags = Region::Flag (_flags & ~WholeFile);
639 if (what_changed & (StartChanged|LengthChanged)) {
644 send_change (what_changed);
649 Region::set_hidden (bool yn)
651 if (hidden() != yn) {
654 _flags = Flag (_flags|Hidden);
656 _flags = Flag (_flags & ~Hidden);
659 send_change (HiddenChanged);
664 Region::set_muted (bool yn)
669 _flags = Flag (_flags|Muted);
671 _flags = Flag (_flags & ~Muted);
674 send_change (MuteChanged);
679 Region::set_opaque (bool yn)
681 if (opaque() != yn) {
683 _flags = Flag (_flags|Opaque);
685 _flags = Flag (_flags & ~Opaque);
687 send_change (OpacityChanged);
692 Region::set_locked (bool yn)
694 if (locked() != yn) {
696 _flags = Flag (_flags|Locked);
698 _flags = Flag (_flags & ~Locked);
700 send_change (LockChanged);
705 Region::set_sync_position (nframes_t absolute_pos)
709 file_pos = _start + (absolute_pos - _position);
711 if (file_pos != _sync_position) {
713 _sync_position = file_pos;
714 _flags = Flag (_flags|SyncMarked);
719 send_change (SyncOffsetChanged);
724 Region::clear_sync_position ()
726 if (_flags & SyncMarked) {
727 _flags = Flag (_flags & ~SyncMarked);
732 send_change (SyncOffsetChanged);
737 Region::sync_offset (int& dir) const
739 /* returns the sync point relative the first frame of the region */
741 if (_flags & SyncMarked) {
742 if (_sync_position > _start) {
744 return _sync_position - _start;
747 return _start - _sync_position;
756 Region::adjust_to_sync (nframes_t pos)
759 nframes_t offset = sync_offset (sync_dir);
762 if (max_frames - pos > offset) {
777 Region::sync_position() const
779 if (_flags & SyncMarked) {
780 return _sync_position;
790 if (_playlist == 0) {
794 _playlist->raise_region (shared_from_this ());
800 if (_playlist == 0) {
804 _playlist->lower_region (shared_from_this ());
808 Region::raise_to_top ()
811 if (_playlist == 0) {
815 _playlist->raise_region_to_top (shared_from_this());
819 Region::lower_to_bottom ()
821 if (_playlist == 0) {
825 _playlist->lower_region_to_bottom (shared_from_this());
829 Region::set_layer (layer_t l)
834 send_change (LayerChanged);
839 Region::state (bool full_state)
841 XMLNode *node = new XMLNode ("Region");
845 _id.print (buf, sizeof (buf));
846 node->add_property ("id", buf);
847 node->add_property ("name", _name);
848 node->add_property ("type", _type.to_string());
849 snprintf (buf, sizeof (buf), "%u", _start);
850 node->add_property ("start", buf);
851 snprintf (buf, sizeof (buf), "%u", _length);
852 node->add_property ("length", buf);
853 snprintf (buf, sizeof (buf), "%u", _position);
854 node->add_property ("position", buf);
856 switch (_first_edit) {
857 case EditChangesNothing:
860 case EditChangesName:
868 node->add_property ("first_edit", fe);
870 /* note: flags are stored by derived classes */
872 snprintf (buf, sizeof (buf), "%d", (int) _layer);
873 node->add_property ("layer", buf);
874 snprintf (buf, sizeof (buf), "%u", _sync_position);
875 node->add_property ("sync-position", buf);
887 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
889 const XMLNodeList& nlist = node.children();
890 const XMLProperty *prop;
893 /* this is responsible for setting those aspects of Region state
894 that are mutable after construction.
897 if ((prop = node.property ("name")) == 0) {
898 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
902 _name = prop->value();
904 if ((prop = node.property ("type")) == 0) {
905 _type = DataType::AUDIO;
907 _type = DataType(prop->value());
910 if ((prop = node.property ("start")) != 0) {
911 sscanf (prop->value().c_str(), "%" PRIu32, &val);
913 what_changed = Change (what_changed|StartChanged);
920 if ((prop = node.property ("length")) != 0) {
921 sscanf (prop->value().c_str(), "%" PRIu32, &val);
922 if (val != _length) {
923 what_changed = Change (what_changed|LengthChanged);
930 if ((prop = node.property ("position")) != 0) {
931 sscanf (prop->value().c_str(), "%" PRIu32, &val);
932 if (val != _position) {
933 what_changed = Change (what_changed|PositionChanged);
940 if ((prop = node.property ("layer")) != 0) {
942 x = (layer_t) atoi (prop->value().c_str());
944 what_changed = Change (what_changed|LayerChanged);
951 if ((prop = node.property ("sync-position")) != 0) {
952 sscanf (prop->value().c_str(), "%" PRIu32, &val);
953 if (val != _sync_position) {
954 what_changed = Change (what_changed|SyncOffsetChanged);
955 _sync_position = val;
958 _sync_position = _start;
961 /* XXX FIRST EDIT !!! */
963 /* note: derived classes set flags */
970 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
976 if (child->name () == "extra") {
977 _extra_xml = new XMLNode (*child);
983 send_change (what_changed);
990 Region::set_state (const XMLNode& node)
992 const XMLProperty *prop;
993 Change what_changed = Change (0);
995 /* ID is not allowed to change, ever */
997 if ((prop = node.property ("id")) == 0) {
998 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1002 _id = prop->value();
1004 _first_edit = EditChangesNothing;
1006 set_live_state (node, what_changed, true);
1018 Region::thaw (const string& why)
1020 Change what_changed = Change (0);
1023 Glib::Mutex::Lock lm (_lock);
1025 if (_frozen && --_frozen > 0) {
1029 if (_pending_changed) {
1030 what_changed = _pending_changed;
1031 _pending_changed = Change (0);
1035 if (what_changed == Change (0)) {
1039 if (what_changed & LengthChanged) {
1040 if (what_changed & PositionChanged) {
1041 recompute_at_start ();
1043 recompute_at_end ();
1046 StateChanged (what_changed);
1050 Region::send_change (Change what_changed)
1053 Glib::Mutex::Lock lm (_lock);
1055 _pending_changed = Change (_pending_changed|what_changed);
1060 StateChanged (what_changed);
1064 Region::set_last_layer_op (uint64_t when)
1066 _last_layer_op = when;
1070 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1072 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1076 Region::equivalent (boost::shared_ptr<const Region> other) const
1078 return _start == other->_start &&
1079 _position == other->_position &&
1080 _length == other->_length;
1084 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1086 return _start == other->_start &&
1087 _length == other->_length;
1091 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1093 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1097 Region::source_deleted (boost::shared_ptr<Source>)
1103 Region::master_source_names ()
1105 SourceList::iterator i;
1107 vector<string> names;
1108 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1109 names.push_back((*i)->name());
1116 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1121 SourceList::const_iterator i;
1122 SourceList::const_iterator io;
1124 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1125 if ((*i)->id() != (*io)->id()) {
1130 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1131 if ((*i)->id() != (*io)->id()) {
1140 Region::verify_length (jack_nframes_t len)
1142 for (uint32_t n=0; n < _sources.size(); ++n) {
1143 if (_start > _sources[n]->length() - len) {
1151 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1153 for (uint32_t n=0; n < _sources.size(); ++n) {
1154 if (new_length > _sources[n]->length() - new_start) {
1161 Region::verify_start (jack_nframes_t pos)
1163 for (uint32_t n=0; n < _sources.size(); ++n) {
1164 if (pos > _sources[n]->length() - _length) {
1172 Region::verify_start_mutable (jack_nframes_t& new_start)
1174 for (uint32_t n=0; n < _sources.size(); ++n) {
1175 if (new_start > _sources[n]->length() - _length) {
1176 new_start = _sources[n]->length() - _length;
1182 boost::shared_ptr<Region>
1183 Region::get_parent()
1185 boost::shared_ptr<Region> r;
1188 r = _playlist->session().find_whole_file_parent (*this);