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>
39 using namespace ARDOUR;
42 Change Region::FadeChanged = ARDOUR::new_change ();
43 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
44 Change Region::MuteChanged = ARDOUR::new_change ();
45 Change Region::OpacityChanged = ARDOUR::new_change ();
46 Change Region::LockChanged = ARDOUR::new_change ();
47 Change Region::LayerChanged = ARDOUR::new_change ();
48 Change Region::HiddenChanged = ARDOUR::new_change ();
50 Region::Region (jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Region::Flag flags)
52 /* basic Region constructor */
58 pending_changed = Change (0);
62 _sync_position = _start;
66 _current_state_id = 0;
68 _first_edit = EditChangesNothing;
72 Region::Region (boost::shared_ptr<const Region> other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
74 /* create a new Region from part of an existing one */
77 pending_changed = Change (0);
81 _start = other->_start + offset;
82 if (other->_sync_position < offset) {
83 _sync_position = other->_sync_position;
85 _sync_position = _start;
91 _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
92 _current_state_id = 0;
93 _first_edit = EditChangesNothing;
97 Region::Region (boost::shared_ptr<const Region> other)
99 /* Pure copy constructor */
102 pending_changed = Change (0);
104 _read_data_count = 0;
106 _first_edit = EditChangesID;
107 other->_first_edit = EditChangesName;
109 if (other->_extra_xml) {
110 _extra_xml = new XMLNode (*other->_extra_xml);
115 _start = other->_start;
116 _sync_position = other->_sync_position;
117 _length = other->_length;
118 _name = other->_name;
119 _position = other->_position;
120 _layer = other->_layer;
121 _flags = Flag (other->_flags & ~Locked);
122 _current_state_id = 0;
123 _last_layer_op = other->_last_layer_op;
126 Region::Region (const XMLNode& node)
129 pending_changed = Change (0);
131 _read_data_count = 0;
133 _sync_position = _start;
135 _name = X_("error: XML did not reset this");
139 _current_state_id = 0;
140 _first_edit = EditChangesNothing;
142 if (set_state (node)) {
143 throw failed_constructor();
151 /* derived classes must emit GoingAway */
155 Region::set_playlist (Playlist* pl)
161 Region::store_state (RegionState& state) const
163 state._start = _start;
164 state._length = _length;
165 state._position = _position;
166 state._flags = _flags;
167 state._sync_position = _sync_position;
168 state._layer = _layer;
170 state._first_edit = _first_edit;
174 Region::restore_and_return_flags (RegionState& state)
176 Change what_changed = Change (0);
179 Glib::Mutex::Lock lm (lock);
181 if (_start != state._start) {
182 what_changed = Change (what_changed|StartChanged);
183 _start = state._start;
185 if (_length != state._length) {
186 what_changed = Change (what_changed|LengthChanged);
187 _length = state._length;
189 if (_position != state._position) {
190 what_changed = Change (what_changed|PositionChanged);
191 _position = state._position;
193 if (_sync_position != state._sync_position) {
194 _sync_position = state._sync_position;
195 what_changed = Change (what_changed|SyncOffsetChanged);
197 if (_layer != state._layer) {
198 what_changed = Change (what_changed|LayerChanged);
199 _layer = state._layer;
202 uint32_t old_flags = _flags;
203 _flags = Flag (state._flags);
205 if ((old_flags ^ state._flags) & Muted) {
206 what_changed = Change (what_changed|MuteChanged);
208 if ((old_flags ^ state._flags) & Opaque) {
209 what_changed = Change (what_changed|OpacityChanged);
211 if ((old_flags ^ state._flags) & Locked) {
212 what_changed = Change (what_changed|LockChanged);
215 _first_edit = state._first_edit;
222 Region::set_name (string str)
227 send_change (NameChanged);
232 Region::set_length (jack_nframes_t len, void *src)
234 if (_flags & Locked) {
238 if (_length != len && len != 0) {
240 if (!verify_length (len)) {
246 _flags = Region::Flag (_flags & ~WholeFile);
255 snprintf (buf, sizeof (buf), "length set to %u", len);
259 send_change (LengthChanged);
264 Region::maybe_uncopy ()
269 Region::first_edit ()
271 if (_first_edit != EditChangesNothing && _playlist) {
273 _name = _playlist->session().new_region_name (_name);
274 _first_edit = EditChangesNothing;
276 send_change (NameChanged);
277 /// XXX CheckNewRegion (boost::shared_ptr<Region>(this));
282 Region::move_to_natural_position (void *src)
288 boost::shared_ptr<Region> whole_file_region = get_parent();
290 if (whole_file_region) {
291 set_position (whole_file_region->position() + _start, src);
296 Region::special_set_position (jack_nframes_t pos)
298 /* this is used when creating a whole file region as
299 a way to store its "natural" or "captured" position.
306 Region::set_position (jack_nframes_t pos, void *src)
308 if (_flags & Locked) {
312 if (_position != pos) {
317 snprintf (buf, sizeof (buf), "position set to %u", pos);
322 /* do this even if the position is the same. this helps out
323 a GUI that has moved its representation already.
326 send_change (PositionChanged);
330 Region::set_position_on_top (jack_nframes_t pos, void *src)
332 if (_flags & Locked) {
336 if (_position != pos) {
341 snprintf (buf, sizeof (buf), "position set to %u", pos);
346 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
348 /* do this even if the position is the same. this helps out
349 a GUI that has moved its representation already.
352 send_change (PositionChanged);
356 Region::nudge_position (long n, void *src)
358 if (_flags & Locked) {
367 if (_position > max_frames - n) {
368 _position = max_frames;
373 if (_position < (jack_nframes_t) -n) {
382 snprintf (buf, sizeof (buf), "position set to %u", _position);
386 send_change (PositionChanged);
390 Region::set_start (jack_nframes_t pos, void *src)
392 if (_flags & Locked) {
395 /* This just sets the start, nothing else. It effectively shifts
396 the contents of the Region within the overall extent of the Source,
397 without changing the Region's position or length
402 if (!verify_start (pos)) {
407 _flags = Region::Flag (_flags & ~WholeFile);
412 snprintf (buf, sizeof (buf), "start set to %u", pos);
416 send_change (StartChanged);
421 Region::trim_start (jack_nframes_t new_position, void *src)
423 if (_flags & Locked) {
426 jack_nframes_t new_start;
429 if (new_position > _position) {
430 start_shift = new_position - _position;
432 start_shift = -(_position - new_position);
435 if (start_shift > 0) {
437 if (_start > max_frames - start_shift) {
438 new_start = max_frames;
440 new_start = _start + start_shift;
443 if (!verify_start (new_start)) {
447 } else if (start_shift < 0) {
449 if (_start < (jack_nframes_t) -start_shift) {
452 new_start = _start + start_shift;
458 if (new_start == _start) {
463 _flags = Region::Flag (_flags & ~WholeFile);
468 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
472 send_change (StartChanged);
476 Region::trim_front (jack_nframes_t new_position, void *src)
478 if (_flags & Locked) {
482 jack_nframes_t end = _position + _length - 1;
483 jack_nframes_t source_zero;
485 if (_position > _start) {
486 source_zero = _position - _start;
488 source_zero = 0; // its actually negative, but this will work for us
491 if (new_position < end) { /* can't trim it zero or negative length */
493 jack_nframes_t newlen;
495 /* can't trim it back passed where source position zero is located */
497 new_position = max (new_position, source_zero);
500 if (new_position > _position) {
501 newlen = _length - (new_position - _position);
503 newlen = _length + (_position - new_position);
506 trim_to_internal (new_position, newlen, src);
508 recompute_at_start ();
514 Region::trim_end (jack_nframes_t new_endpoint, void *src)
516 if (_flags & Locked) {
520 if (new_endpoint > _position) {
521 trim_to_internal (_position, new_endpoint - _position, this);
529 Region::trim_to (jack_nframes_t position, jack_nframes_t length, void *src)
531 if (_flags & Locked) {
535 trim_to_internal (position, length, src);
538 recompute_at_start ();
544 Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void *src)
547 jack_nframes_t new_start;
549 if (_flags & Locked) {
553 if (position > _position) {
554 start_shift = position - _position;
556 start_shift = -(_position - position);
559 if (start_shift > 0) {
561 if (_start > max_frames - start_shift) {
562 new_start = max_frames;
564 new_start = _start + start_shift;
568 } else if (start_shift < 0) {
570 if (_start < (jack_nframes_t) -start_shift) {
573 new_start = _start + start_shift;
579 if (!verify_start_and_length (new_start, length)) {
583 Change what_changed = Change (0);
585 if (_start != new_start) {
587 what_changed = Change (what_changed|StartChanged);
589 if (_length != length) {
591 what_changed = Change (what_changed|LengthChanged);
593 if (_position != position) {
594 _position = position;
595 what_changed = Change (what_changed|PositionChanged);
598 _flags = Region::Flag (_flags & ~WholeFile);
600 if (what_changed & (StartChanged|LengthChanged)) {
608 snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
612 send_change (what_changed);
617 Region::set_hidden (bool yn)
619 if (hidden() != yn) {
622 _flags = Flag (_flags|Hidden);
624 _flags = Flag (_flags & ~Hidden);
627 send_change (HiddenChanged);
632 Region::set_muted (bool yn)
637 _flags = Flag (_flags|Muted);
639 _flags = Flag (_flags & ~Muted);
645 snprintf (buf, sizeof (buf), "muted");
647 snprintf (buf, sizeof (buf), "unmuted");
652 send_change (MuteChanged);
657 Region::set_opaque (bool yn)
659 if (opaque() != yn) {
663 snprintf (buf, sizeof (buf), "opaque");
664 _flags = Flag (_flags|Opaque);
666 snprintf (buf, sizeof (buf), "translucent");
667 _flags = Flag (_flags & ~Opaque);
671 send_change (OpacityChanged);
676 Region::set_locked (bool yn)
678 if (locked() != yn) {
682 snprintf (buf, sizeof (buf), "locked");
683 _flags = Flag (_flags|Locked);
685 snprintf (buf, sizeof (buf), "unlocked");
686 _flags = Flag (_flags & ~Locked);
690 send_change (LockChanged);
695 Region::set_sync_position (jack_nframes_t absolute_pos)
697 jack_nframes_t file_pos;
699 file_pos = _start + (absolute_pos - _position);
701 if (file_pos != _sync_position) {
703 _sync_position = file_pos;
704 _flags = Flag (_flags|SyncMarked);
709 snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
712 send_change (SyncOffsetChanged);
717 Region::clear_sync_position ()
719 if (_flags & SyncMarked) {
720 _flags = Flag (_flags & ~SyncMarked);
724 save_state ("sync point removed");
726 send_change (SyncOffsetChanged);
731 Region::sync_offset (int& dir) const
733 /* returns the sync point relative the first frame of the region */
735 if (_flags & SyncMarked) {
736 if (_sync_position > _start) {
738 return _sync_position - _start;
741 return _start - _sync_position;
750 Region::adjust_to_sync (jack_nframes_t pos)
753 jack_nframes_t offset = sync_offset (sync_dir);
756 if (max_frames - pos > offset) {
771 Region::sync_position() const
773 if (_flags & SyncMarked) {
774 return _sync_position;
784 if (_playlist == 0) {
788 _playlist->raise_region (boost::shared_ptr<Region>(this));
794 if (_playlist == 0) {
798 _playlist->lower_region (boost::shared_ptr<Region>(this));
802 Region::raise_to_top ()
805 if (_playlist == 0) {
809 _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
813 Region::lower_to_bottom ()
815 if (_playlist == 0) {
819 _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
823 Region::set_layer (layer_t l)
830 snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
834 send_change (LayerChanged);
839 Region::state (bool full_state)
841 XMLNode *node = new XMLNode ("Region");
845 node->add_property ("id", buf);
846 node->add_property ("name", _name);
847 snprintf (buf, sizeof (buf), "%u", _start);
848 node->add_property ("start", buf);
849 snprintf (buf, sizeof (buf), "%u", _length);
850 node->add_property ("length", buf);
851 snprintf (buf, sizeof (buf), "%u", _position);
852 node->add_property ("position", buf);
854 /* note: flags are stored by derived classes */
856 snprintf (buf, sizeof (buf), "%d", (int) _layer);
857 node->add_property ("layer", buf);
858 snprintf (buf, sizeof (buf), "%u", _sync_position);
859 node->add_property ("sync-position", buf);
871 Region::set_state (const XMLNode& node)
873 const XMLNodeList& nlist = node.children();
874 const XMLProperty *prop;
881 if ((prop = node.property ("id")) == 0) {
882 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
888 if ((prop = node.property ("name")) == 0) {
889 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
893 _name = prop->value();
895 if ((prop = node.property ("start")) != 0) {
896 _start = (jack_nframes_t) atoi (prop->value().c_str());
899 if ((prop = node.property ("length")) != 0) {
900 _length = (jack_nframes_t) atoi (prop->value().c_str());
903 if ((prop = node.property ("position")) != 0) {
904 _position = (jack_nframes_t) atoi (prop->value().c_str());
907 if ((prop = node.property ("layer")) != 0) {
908 _layer = (layer_t) atoi (prop->value().c_str());
911 /* note: derived classes set flags */
913 if ((prop = node.property ("sync-position")) != 0) {
914 _sync_position = (jack_nframes_t) atoi (prop->value().c_str());
916 _sync_position = _start;
919 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
925 if (child->name () == "extra") {
926 _extra_xml = new XMLNode (*child);
931 _first_edit = EditChangesNothing;
943 Region::thaw (const string& why)
945 Change what_changed = Change (0);
948 Glib::Mutex::Lock lm (lock);
950 if (_frozen && --_frozen > 0) {
954 if (pending_changed) {
955 what_changed = pending_changed;
956 pending_changed = Change (0);
960 if (what_changed == Change (0)) {
964 if (what_changed & LengthChanged) {
965 if (what_changed & PositionChanged) {
966 recompute_at_start ();
972 StateChanged (what_changed);
976 Region::send_change (Change what_changed)
979 Glib::Mutex::Lock lm (lock);
981 pending_changed = Change (pending_changed|what_changed);
986 StateManager::send_state_changed (what_changed);
990 Region::set_last_layer_op (uint64_t when)
992 _last_layer_op = when;
996 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
998 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1002 Region::equivalent (boost::shared_ptr<const Region> other) const
1004 return _start == other->_start &&
1005 _position == other->_position &&
1006 _length == other->_length;
1010 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1012 return _start == other->_start &&
1013 _length == other->_length;
1017 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1019 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;