X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fregion.cc;h=66c0b84ae9c201f66264614ab1760a6f32627e2d;hb=ecae912da9f6805d4939e657e3a591f79b0ae542;hp=91d5704db20477c3f7fd70285e5bf2fa3f43c58d;hpb=4c509656223d3ed1f0fab504cb483090d38972f9;p=ardour.git diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 91d5704db2..66c0b84ae9 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2003 Paul Davis + Copyright (C) 2000-2003 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,383 +15,664 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include #include #include #include - -#include -#include - -#include -#include - -#include -#include -#include +#include + +#include +#include "pbd/xml++.h" +#include "pbd/stacktrace.h" +#include "pbd/enumwriter.h" + +#include "ardour/debug.h" +#include "ardour/file_source.h" +#include "ardour/filter.h" +#include "ardour/playlist.h" +#include "ardour/playlist_source.h" +#include "ardour/profile.h" +#include "ardour/region.h" +#include "ardour/region_factory.h" +#include "ardour/session.h" +#include "ardour/source.h" +#include "ardour/source_factory.h" +#include "ardour/tempo.h" +#include "ardour/utils.h" #include "i18n.h" using namespace std; using namespace ARDOUR; +using namespace PBD; + +namespace ARDOUR { + namespace Properties { + PBD::PropertyDescriptor muted; + PBD::PropertyDescriptor opaque; + PBD::PropertyDescriptor locked; + PBD::PropertyDescriptor automatic; + PBD::PropertyDescriptor whole_file; + PBD::PropertyDescriptor import; + PBD::PropertyDescriptor external; + PBD::PropertyDescriptor sync_marked; + PBD::PropertyDescriptor left_of_split; + PBD::PropertyDescriptor right_of_split; + PBD::PropertyDescriptor hidden; + PBD::PropertyDescriptor position_locked; + PBD::PropertyDescriptor valid_transients; + PBD::PropertyDescriptor start; + PBD::PropertyDescriptor length; + PBD::PropertyDescriptor position; + PBD::PropertyDescriptor sync_position; + PBD::PropertyDescriptor layer; + PBD::PropertyDescriptor ancestral_start; + PBD::PropertyDescriptor ancestral_length; + PBD::PropertyDescriptor stretch; + PBD::PropertyDescriptor shift; + PBD::PropertyDescriptor position_lock_style; + PBD::PropertyDescriptor layering_index; + } +} -Change Region::FadeChanged = ARDOUR::new_change (); -Change Region::SyncOffsetChanged = ARDOUR::new_change (); -Change Region::MuteChanged = ARDOUR::new_change (); -Change Region::OpacityChanged = ARDOUR::new_change (); -Change Region::LockChanged = ARDOUR::new_change (); -Change Region::LayerChanged = ARDOUR::new_change (); -Change Region::HiddenChanged = ARDOUR::new_change (); +PBD::Signal2,const PropertyChange&> Region::RegionPropertyChanged; -sigc::signal Region::CheckNewRegion; +void +Region::make_property_quarks () +{ + Properties::muted.property_id = g_quark_from_static_string (X_("muted")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id)); + Properties::opaque.property_id = g_quark_from_static_string (X_("opaque")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id)); + Properties::locked.property_id = g_quark_from_static_string (X_("locked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id)); + Properties::automatic.property_id = g_quark_from_static_string (X_("automatic")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id)); + Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id)); + Properties::import.property_id = g_quark_from_static_string (X_("import")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id)); + Properties::external.property_id = g_quark_from_static_string (X_("external")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id)); + Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id)); + Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id)); + Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id)); + Properties::hidden.property_id = g_quark_from_static_string (X_("hidden")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); + Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id)); + Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id)); + Properties::start.property_id = g_quark_from_static_string (X_("start")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id)); + Properties::length.property_id = g_quark_from_static_string (X_("length")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id)); + Properties::position.property_id = g_quark_from_static_string (X_("position")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id)); + Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id)); + Properties::layer.property_id = g_quark_from_static_string (X_("layer")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id)); + Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id)); + Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id)); + Properties::stretch.property_id = g_quark_from_static_string (X_("stretch")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id)); + Properties::shift.property_id = g_quark_from_static_string (X_("shift")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id)); + Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id)); + Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id)); +} -Region::Region (jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Region::Flag flags) +void +Region::register_properties () { - /* basic Region constructor */ + _xml_node_name = X_("Region"); + + add_property (_muted); + add_property (_opaque); + add_property (_locked); + add_property (_automatic); + add_property (_whole_file); + add_property (_import); + add_property (_external); + add_property (_sync_marked); + add_property (_left_of_split); + add_property (_right_of_split); + add_property (_hidden); + add_property (_position_locked); + add_property (_valid_transients); + add_property (_start); + add_property (_length); + add_property (_position); + add_property (_sync_position); + add_property (_ancestral_start); + add_property (_ancestral_length); + add_property (_stretch); + add_property (_shift); + add_property (_position_lock_style); + add_property (_layering_index); +} - _id = ARDOUR::new_id(); - _flags = flags; - _playlist = 0; - _read_data_count = 0; - _frozen = 0; - pending_changed = Change (0); +#define REGION_DEFAULT_STATE(s,l) \ + _sync_marked (Properties::sync_marked, false) \ + , _left_of_split (Properties::left_of_split, false) \ + , _right_of_split (Properties::right_of_split, false) \ + , _valid_transients (Properties::valid_transients, false) \ + , _start (Properties::start, (s)) \ + , _length (Properties::length, (l)) \ + , _position (Properties::position, 0) \ + , _sync_position (Properties::sync_position, (s)) \ + , _muted (Properties::muted, false) \ + , _opaque (Properties::opaque, true) \ + , _locked (Properties::locked, false) \ + , _automatic (Properties::automatic, false) \ + , _whole_file (Properties::whole_file, false) \ + , _import (Properties::import, false) \ + , _external (Properties::external, false) \ + , _hidden (Properties::hidden, false) \ + , _position_locked (Properties::position_locked, false) \ + , _ancestral_start (Properties::ancestral_start, (s)) \ + , _ancestral_length (Properties::ancestral_length, (l)) \ + , _stretch (Properties::stretch, 1.0) \ + , _shift (Properties::shift, 1.0) \ + , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \ + , _layering_index (Properties::layering_index, 0) + +#define REGION_COPY_STATE(other) \ + _sync_marked (Properties::sync_marked, other->_sync_marked) \ + , _left_of_split (Properties::left_of_split, other->_left_of_split) \ + , _right_of_split (Properties::right_of_split, other->_right_of_split) \ + , _valid_transients (Properties::valid_transients, other->_valid_transients) \ + , _start(Properties::start, other->_start) \ + , _length(Properties::length, other->_length) \ + , _position(Properties::position, other->_position) \ + , _sync_position(Properties::sync_position, other->_sync_position) \ + , _muted (Properties::muted, other->_muted) \ + , _opaque (Properties::opaque, other->_opaque) \ + , _locked (Properties::locked, other->_locked) \ + , _automatic (Properties::automatic, other->_automatic) \ + , _whole_file (Properties::whole_file, other->_whole_file) \ + , _import (Properties::import, other->_import) \ + , _external (Properties::external, other->_external) \ + , _hidden (Properties::hidden, other->_hidden) \ + , _position_locked (Properties::position_locked, other->_position_locked) \ + , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \ + , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \ + , _stretch (Properties::stretch, other->_stretch) \ + , _shift (Properties::shift, other->_shift) \ + , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \ + , _layering_index (Properties::layering_index, other->_layering_index) + +/* derived-from-derived constructor (no sources in constructor) */ +Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type) + : SessionObject(s, name) + , _type(type) + , REGION_DEFAULT_STATE(start,length) + , _last_length (length) + , _last_position (0) + , _first_edit (EditChangesNothing) + , _layer (0) +{ + register_properties (); - _name = name; - _start = start; - _sync_position = _start; - _length = length; - _position = 0; - _layer = layer; - _current_state_id = 0; - _read_data_count = 0; - _first_edit = EditChangesNothing; - _last_layer_op = 0; + /* no sources at this point */ } -Region::Region (const Region& other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags) +/** Basic Region constructor (many sources) */ +Region::Region (const SourceList& srcs) + : SessionObject(srcs.front()->session(), "toBeRenamed") + , _type (srcs.front()->type()) + , REGION_DEFAULT_STATE(0,0) + , _last_length (0) + , _last_position (0) + , _first_edit (EditChangesNothing) + , _layer (0) { - /* create a new Region from part of an existing one */ + register_properties (); - _id = ARDOUR::new_id(); - _frozen = 0; - pending_changed = Change (0); - _playlist = 0; - _read_data_count = 0; + _type = srcs.front()->type(); - _start = other._start + offset; - if (other._sync_position < offset) { - _sync_position = other._sync_position; - } else { - _sync_position = _start; - } - _length = length; - _name = name; - _position = 0; - _layer = layer; - _flags = Flag (flags & ~(Locked|WholeFile|Hidden)); - _current_state_id = 0; - _first_edit = EditChangesNothing; - _last_layer_op = 0; + use_sources (srcs); + + assert(_sources.size() > 0); + assert (_type == srcs.front()->type()); } -Region::Region (const Region &other) +/** Create a new Region from an existing one */ +Region::Region (boost::shared_ptr other) + : SessionObject(other->session(), other->name()) + , _type (other->data_type()) + , REGION_COPY_STATE (other) + , _last_length (other->_last_length) + , _last_position(other->_last_position) \ + , _first_edit (EditChangesNothing) + , _layer (other->_layer) { - /* Pure copy constructor */ + register_properties (); - _id = ARDOUR::new_id(); - _frozen = 0; - pending_changed = Change (0); - _playlist = 0; - _read_data_count = 0; + /* override state that may have been incorrectly inherited from the other region + */ - _first_edit = EditChangesID; - other._first_edit = EditChangesName; + _position = 0; + _locked = false; + _whole_file = false; + _hidden = false; - if (other._extra_xml) { - _extra_xml = new XMLNode (*other._extra_xml); + use_sources (other->_sources); + + _position_lock_style = other->_position_lock_style; + _first_edit = other->_first_edit; + + _start = 0; // It seems strange _start is not inherited here? + + /* sync pos is relative to start of file. our start-in-file is now zero, + so set our sync position to whatever the the difference between + _start and _sync_pos was in the other region. + + result is that our new sync pos points to the same point in our source(s) + as the sync in the other region did in its source(s). + + since we start at zero in our source(s), it is not possible to use a sync point that + is before the start. reset it to _start if that was true in the other region. + */ + + if (other->sync_marked()) { + if (other->_start < other->_sync_position) { + /* sync pos was after the start point of the other region */ + _sync_position = other->_sync_position - other->_start; + } else { + /* sync pos was before the start point of the other region. not possible here. */ + _sync_marked = false; + _sync_position = _start; + } } else { - _extra_xml = 0; + _sync_marked = false; + _sync_position = _start; } - _start = other._start; - _sync_position = other._sync_position; - _length = other._length; - _name = other._name; - _position = other._position; - _layer = other._layer; - _flags = Flag (other._flags & ~Locked); - _current_state_id = 0; - _last_layer_op = other._last_layer_op; -} - -Region::Region (const XMLNode& node) -{ - _id = 0; - _frozen = 0; - pending_changed = Change (0); - _playlist = 0; - _read_data_count = 0; - _start = 0; - _sync_position = _start; - _length = 0; - _name = X_("error: XML did not reset this"); - _position = 0; - _layer = 0; - _flags = Flag (0); - _current_state_id = 0; - _first_edit = EditChangesNothing; + if (Profile->get_sae()) { + /* reset sync point to start if its ended up + outside region bounds. + */ - if (set_state (node)) { - throw failed_constructor(); + if (_sync_position < _start || _sync_position >= _start + _length) { + _sync_marked = false; + _sync_position = _start; + } } -} -Region::~Region () -{ + assert (_type == other->data_type()); } -void -Region::set_playlist (Playlist* pl) -{ - _playlist = pl; -} +/** Create a new Region from part of an existing one. -void -Region::store_state (RegionState& state) const + the start within \a other is given by \a offset + (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start() +*/ +Region::Region (boost::shared_ptr other, frameoffset_t offset) + : SessionObject(other->session(), other->name()) + , _type (other->data_type()) + , REGION_COPY_STATE (other) + , _last_length (other->_last_length) + , _last_position(other->_last_position) \ + , _first_edit (EditChangesNothing) + , _layer (other->_layer) { - state._start = _start; - state._length = _length; - state._position = _position; - state._flags = _flags; - state._sync_position = _sync_position; - state._layer = _layer; - state._name = _name; - state._first_edit = _first_edit; -} + register_properties (); -Change -Region::restore_and_return_flags (RegionState& state) -{ - Change what_changed = Change (0); + /* override state that may have been incorrectly inherited from the other region + */ - { - LockMonitor lm (lock, __LINE__, __FILE__); - - if (_start != state._start) { - what_changed = Change (what_changed|StartChanged); - _start = state._start; - } - if (_length != state._length) { - what_changed = Change (what_changed|LengthChanged); - _length = state._length; - } - if (_position != state._position) { - what_changed = Change (what_changed|PositionChanged); - _position = state._position; - } - if (_sync_position != state._sync_position) { - _sync_position = state._sync_position; - what_changed = Change (what_changed|SyncOffsetChanged); - } - if (_layer != state._layer) { - what_changed = Change (what_changed|LayerChanged); - _layer = state._layer; - } + _position = 0; + _locked = false; + _whole_file = false; + _hidden = false; - uint32_t old_flags = _flags; - _flags = Flag (state._flags); - - if ((old_flags ^ state._flags) & Muted) { - what_changed = Change (what_changed|MuteChanged); - } - if ((old_flags ^ state._flags) & Opaque) { - what_changed = Change (what_changed|OpacityChanged); + use_sources (other->_sources); + + _start = other->_start + offset; + + /* if the other region had a distinct sync point + set, then continue to use it as best we can. + otherwise, reset sync point back to start. + */ + + if (other->sync_marked()) { + if (other->_sync_position < _start) { + _sync_marked = false; + _sync_position = _start; + } else { + _sync_position = other->_sync_position; } - if ((old_flags ^ state._flags) & Locked) { - what_changed = Change (what_changed|LockChanged); + } else { + _sync_marked = false; + _sync_position = _start; + } + + if (Profile->get_sae()) { + /* reset sync point to start if its ended up + outside region bounds. + */ + + if (_sync_position < _start || _sync_position >= _start + _length) { + _sync_marked = false; + _sync_position = _start; } + } - _first_edit = state._first_edit; + assert (_type == other->data_type()); +} + +/** Create a copy of @param other but with different sources. Used by filters */ +Region::Region (boost::shared_ptr other, const SourceList& srcs) + : SessionObject (other->session(), other->name()) + , _type (srcs.front()->type()) + , REGION_COPY_STATE (other) + , _last_length (other->_last_length) + , _last_position (other->_last_position) + , _first_edit (EditChangesID) + , _layer (other->_layer) +{ + register_properties (); + + _locked = false; + _position_locked = false; + + other->_first_edit = EditChangesName; + + if (other->_extra_xml) { + _extra_xml = new XMLNode (*other->_extra_xml); + } else { + _extra_xml = 0; } - return what_changed; + use_sources (srcs); + assert(_sources.size() > 0); +} + +Region::~Region () +{ + DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this)); + drop_sources (); } void -Region::set_name (string str) +Region::set_playlist (boost::weak_ptr wpl) +{ + _playlist = wpl.lock(); +} +bool +Region::set_name (const std::string& str) { if (_name != str) { - _name = str; - send_change (NameChanged); + SessionObject::set_name(str); // EMIT SIGNAL NameChanged() + assert(_name == str); + + send_change (Properties::name); } + + return true; } void -Region::set_length (jack_nframes_t len, void *src) +Region::set_length (framecnt_t len) { - if (_flags & Locked) { + //cerr << "Region::set_length() len = " << len << endl; + if (locked()) { return; } if (_length != len && len != 0) { + /* check that the current _position wouldn't make the new + length impossible. + */ + + if (max_framepos - len < _position) { + return; + } + if (!verify_length (len)) { return; } - - _length = len; - _flags = Region::Flag (_flags & ~WholeFile); + _last_length = _length; + set_length_internal (len); + _whole_file = false; first_edit (); maybe_uncopy (); + invalidate_transients (); - if (!_frozen) { + if (!property_changes_suspended()) { recompute_at_end (); - - char buf[64]; - snprintf (buf, sizeof (buf), "length set to %u", len); - save_state (buf); } - send_change (LengthChanged); + send_change (Properties::length); } } +void +Region::set_length_internal (framecnt_t len) +{ + _length = len; +} + void Region::maybe_uncopy () { + /* this does nothing but marked a semantic moment once upon a time */ } void Region::first_edit () { - if (_first_edit != EditChangesNothing && _playlist) { + boost::shared_ptr pl (playlist()); - _name = _playlist->session().new_region_name (_name); + if (_first_edit != EditChangesNothing && pl) { + + _name = RegionFactory::new_region_name (_name); _first_edit = EditChangesNothing; - send_change (NameChanged); - CheckNewRegion (this); + send_change (Properties::name); + + RegionFactory::CheckNewRegion (shared_from_this()); } } +bool +Region::at_natural_position () const +{ + boost::shared_ptr pl (playlist()); + + if (!pl) { + return false; + } + + boost::shared_ptr whole_file_region = get_parent(); + + if (whole_file_region) { + if (_position == whole_file_region->position() + _start) { + return true; + } + } + + return false; +} + void -Region::move_to_natural_position (void *src) +Region::move_to_natural_position () { - if (!_playlist) { + boost::shared_ptr pl (playlist()); + + if (!pl) { return; } - Region* whole_file_region = get_parent(); + boost::shared_ptr whole_file_region = get_parent(); if (whole_file_region) { - set_position (whole_file_region->position() + _start, src); + set_position (whole_file_region->position() + _start); } } - + void -Region::special_set_position (jack_nframes_t pos) +Region::special_set_position (framepos_t pos) { - /* this is used when creating a whole file region as + /* this is used when creating a whole file region as a way to store its "natural" or "captured" position. */ + _position = _position; _position = pos; } void -Region::set_position (jack_nframes_t pos, void *src) +Region::set_position_lock_style (PositionLockStyle ps) { - if (_flags & Locked) { - return; - } + if (_position_lock_style != ps) { - if (_position != pos) { - _position = pos; + boost::shared_ptr pl (playlist()); + + _position_lock_style = ps; - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "position set to %u", pos); - save_state (buf); + if (_position_lock_style == MusicTime) { + _session.bbt_time (_position, _bbt_time); } + + send_change (Properties::position_lock_style); + } +} + +void +Region::update_after_tempo_map_change () +{ + boost::shared_ptr pl (playlist()); + + if (!pl || _position_lock_style != MusicTime) { + return; } + TempoMap& map (_session.tempo_map()); + framepos_t pos = map.frame_time (_bbt_time); + set_position_internal (pos, false); + /* do this even if the position is the same. this helps out a GUI that has moved its representation already. */ - - send_change (PositionChanged); + send_change (Properties::position); } void -Region::set_position_on_top (jack_nframes_t pos, void *src) +Region::set_position (framepos_t pos) { - if (_flags & Locked) { + if (!can_move()) { return; } + set_position_internal (pos, true); + + /* do this even if the position is the same. this helps out + a GUI that has moved its representation already. + */ + send_change (Properties::position); + +} + +void +Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute) +{ + /* We emit a change of Properties::position even if the position hasn't changed + (see Region::set_position), so we must always set this up so that + e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position. + */ + _last_position = _position; + if (_position != pos) { _position = pos; - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "position set to %u", pos); - save_state (buf); + /* check that the new _position wouldn't make the current + length impossible - if so, change the length. + + XXX is this the right thing to do? + */ + + if (max_framepos - _length < _position) { + _last_length = _length; + _length = max_framepos - _position; } - } - _playlist->raise_region_to_top (*this); + if (allow_bbt_recompute) { + recompute_position_from_lock_style (); + } - /* do this even if the position is the same. this helps out - a GUI that has moved its representation already. - */ - - send_change (PositionChanged); + //invalidate_transients (); + } } void -Region::nudge_position (long n, void *src) +Region::recompute_position_from_lock_style () { - if (_flags & Locked) { + if (_position_lock_style == MusicTime) { + _session.bbt_time (_position, _bbt_time); + } +} + +void +Region::nudge_position (frameoffset_t n) +{ + if (locked()) { return; } if (n == 0) { return; } - + + framepos_t new_position = _position; + if (n > 0) { - if (_position > max_frames - n) { - _position = max_frames; + if (_position > max_framepos - n) { + new_position = max_framepos; } else { - _position += n; + new_position += n; } } else { - if (_position < (jack_nframes_t) -n) { - _position = 0; + if (_position < -n) { + new_position = 0; } else { - _position += n; + new_position += n; } } - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "position set to %u", _position); - save_state (buf); - } + set_position_internal (new_position, true); - send_change (PositionChanged); + send_change (Properties::position); +} + +void +Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh) +{ + _ancestral_length = l; + _ancestral_start = s; + _stretch = st; + _shift = sh; } void -Region::set_start (jack_nframes_t pos, void *src) +Region::set_start (framepos_t pos) { - if (_flags & Locked) { + if (locked() || position_locked()) { return; } /* This just sets the start, nothing else. It effectively shifts @@ -406,38 +687,27 @@ Region::set_start (jack_nframes_t pos, void *src) } _start = pos; - _flags = Region::Flag (_flags & ~WholeFile); + _whole_file = false; first_edit (); + invalidate_transients (); - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "start set to %u", pos); - save_state (buf); - } - - send_change (StartChanged); + send_change (Properties::start); } } void -Region::trim_start (jack_nframes_t new_position, void *src) +Region::trim_start (framepos_t new_position) { - if (_flags & Locked) { + if (locked() || position_locked()) { return; } - jack_nframes_t new_start; - int32_t start_shift; - - if (new_position > _position) { - start_shift = new_position - _position; - } else { - start_shift = -(_position - new_position); - } + framepos_t new_start; + frameoffset_t const start_shift = new_position - _position; if (start_shift > 0) { - if (_start > max_frames - start_shift) { - new_start = max_frames; + if (_start > max_framepos - start_shift) { + new_start = max_framepos; } else { new_start = _start + start_shift; } @@ -448,11 +718,12 @@ Region::trim_start (jack_nframes_t new_position, void *src) } else if (start_shift < 0) { - if (_start < (jack_nframes_t) -start_shift) { + if (_start < -start_shift) { new_start = 0; } else { new_start = _start + start_shift; } + } else { return; } @@ -460,29 +731,41 @@ Region::trim_start (jack_nframes_t new_position, void *src) if (new_start == _start) { return; } - + _start = new_start; - _flags = Region::Flag (_flags & ~WholeFile); + _whole_file = false; first_edit (); - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "slipped start to %u", _start); - save_state (buf); - } + send_change (Properties::start); +} + +void +Region::trim_front (framepos_t new_position) +{ + modify_front (new_position, false); +} - send_change (StartChanged); +void +Region::cut_front (framepos_t new_position) +{ + modify_front (new_position, true); } void -Region::trim_front (jack_nframes_t new_position, void *src) +Region::cut_end (framepos_t new_endpoint) { - if (_flags & Locked) { + modify_end (new_endpoint, true); +} + +void +Region::modify_front (framepos_t new_position, bool reset_fade) +{ + if (locked()) { return; } - jack_nframes_t end = _position + _length - 1; - jack_nframes_t source_zero; + framepos_t end = last_frame(); + framepos_t source_zero; if (_position > _start) { source_zero = _position - _start; @@ -491,89 +774,109 @@ Region::trim_front (jack_nframes_t new_position, void *src) } if (new_position < end) { /* can't trim it zero or negative length */ - - jack_nframes_t newlen; - - /* can't trim it back passed where source position zero is located */ - - new_position = max (new_position, source_zero); - - + + framecnt_t newlen = 0; + framepos_t delta = 0; + + if (!can_trim_start_before_source_start ()) { + /* can't trim it back past where source position zero is located */ + new_position = max (new_position, source_zero); + } + if (new_position > _position) { newlen = _length - (new_position - _position); + delta = -1 * (new_position - _position); } else { newlen = _length + (_position - new_position); + delta = _position - new_position; } - - trim_to_internal (new_position, newlen, src); - if (!_frozen) { + + trim_to_internal (new_position, newlen); + + if (reset_fade) { + _right_of_split = true; + } + + if (!property_changes_suspended()) { recompute_at_start (); } + + if (_transients.size() > 0){ + adjust_transients(delta); + } } } void -Region::trim_end (jack_nframes_t new_endpoint, void *src) +Region::modify_end (framepos_t new_endpoint, bool reset_fade) { - if (_flags & Locked) { + if (locked()) { return; } if (new_endpoint > _position) { - trim_to_internal (_position, new_endpoint - _position, this); - if (!_frozen) { + trim_to_internal (_position, new_endpoint - _position); + if (reset_fade) { + _left_of_split = true; + } + if (!property_changes_suspended()) { recompute_at_end (); } } } +/** @param new_endpoint New region end point, such that, for example, + * a region at 0 of length 10 has an endpoint of 9. + */ + +void +Region::trim_end (framepos_t new_endpoint) +{ + modify_end (new_endpoint, false); +} + void -Region::trim_to (jack_nframes_t position, jack_nframes_t length, void *src) +Region::trim_to (framepos_t position, framecnt_t length) { - if (_flags & Locked) { + if (locked()) { return; } - trim_to_internal (position, length, src); + trim_to_internal (position, length); - if (!_frozen) { + if (!property_changes_suspended()) { recompute_at_start (); recompute_at_end (); } } void -Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void *src) +Region::trim_to_internal (framepos_t position, framecnt_t length) { - int32_t start_shift; - jack_nframes_t new_start; + framepos_t new_start; - if (_flags & Locked) { + if (locked()) { return; } - if (position > _position) { - start_shift = position - _position; - } else { - start_shift = -(_position - position); - } + frameoffset_t const start_shift = position - _position; if (start_shift > 0) { - if (_start > max_frames - start_shift) { - new_start = max_frames; + if (_start > max_framepos - start_shift) { + new_start = max_framepos; } else { new_start = _start + start_shift; } - } else if (start_shift < 0) { - if (_start < (jack_nframes_t) -start_shift) { + if (_start < -start_shift && !can_trim_start_before_source_start ()) { new_start = 0; } else { new_start = _start + start_shift; } + } else { new_start = _start; } @@ -582,76 +885,82 @@ Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void * return; } - Change what_changed = Change (0); + PropertyChange what_changed; if (_start != new_start) { _start = new_start; - what_changed = Change (what_changed|StartChanged); - } - if (_length != length) { - _length = length; - what_changed = Change (what_changed|LengthChanged); + what_changed.add (Properties::start); } + + /* Set position before length, otherwise for MIDI regions this bad thing happens: + * 1. we call set_length_internal; length in beats is computed using the region's current + * (soon-to-be old) position + * 2. we call set_position_internal; position is set and length in frames re-computed using + * length in beats from (1) but at the new position, which is wrong if the region + * straddles a tempo/meter change. + */ + if (_position != position) { - _position = position; - what_changed = Change (what_changed|PositionChanged); + if (!property_changes_suspended()) { + _last_position = _position; + } + set_position_internal (position, true); + what_changed.add (Properties::position); } - - _flags = Region::Flag (_flags & ~WholeFile); - if (what_changed & (StartChanged|LengthChanged)) { - first_edit (); - } - - if (what_changed) { - - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1); - save_state (buf); + if (_length != length) { + if (!property_changes_suspended()) { + _last_length = _length; } + set_length_internal (length); + what_changed.add (Properties::length); + } + _whole_file = false; + + PropertyChange start_and_length; + + start_and_length.add (Properties::start); + start_and_length.add (Properties::length); + + if (what_changed.contains (start_and_length)) { + first_edit (); + } + + if (!what_changed.empty()) { send_change (what_changed); } -} +} void Region::set_hidden (bool yn) { if (hidden() != yn) { + _hidden = yn; + send_change (Properties::hidden); + } +} - if (yn) { - _flags = Flag (_flags|Hidden); - } else { - _flags = Flag (_flags & ~Hidden); - } +void +Region::set_whole_file (bool yn) +{ + _whole_file = yn; + /* no change signal */ +} - send_change (HiddenChanged); - } +void +Region::set_automatic (bool yn) +{ + _automatic = yn; + /* no change signal */ } void Region::set_muted (bool yn) { if (muted() != yn) { - - if (yn) { - _flags = Flag (_flags|Muted); - } else { - _flags = Flag (_flags & ~Muted); - } - - if (!_frozen) { - char buf[64]; - if (yn) { - snprintf (buf, sizeof (buf), "muted"); - } else { - snprintf (buf, sizeof (buf), "unmuted"); - } - save_state (buf); - } - - send_change (MuteChanged); + _muted = yn; + send_change (Properties::muted); } } @@ -659,18 +968,8 @@ void Region::set_opaque (bool yn) { if (opaque() != yn) { - if (!_frozen) { - char buf[64]; - if (yn) { - snprintf (buf, sizeof (buf), "opaque"); - _flags = Flag (_flags|Opaque); - } else { - snprintf (buf, sizeof (buf), "translucent"); - _flags = Flag (_flags & ~Opaque); - } - save_state (buf); - } - send_change (OpacityChanged); + _opaque = yn; + send_change (Properties::opaque); } } @@ -678,66 +977,61 @@ void Region::set_locked (bool yn) { if (locked() != yn) { - if (!_frozen) { - char buf[64]; - if (yn) { - snprintf (buf, sizeof (buf), "locked"); - _flags = Flag (_flags|Locked); - } else { - snprintf (buf, sizeof (buf), "unlocked"); - _flags = Flag (_flags & ~Locked); - } - save_state (buf); - } - send_change (LockChanged); + _locked = yn; + send_change (Properties::locked); } } void -Region::set_sync_position (jack_nframes_t absolute_pos) +Region::set_position_locked (bool yn) { - jack_nframes_t file_pos; + if (position_locked() != yn) { + _position_locked = yn; + send_change (Properties::locked); + } +} - file_pos = _start + (absolute_pos - _position); +/** Set the region's sync point. + * @param absolute_pos Session time. + */ +void +Region::set_sync_position (framepos_t absolute_pos) +{ + /* position within our file */ + framepos_t const file_pos = _start + (absolute_pos - _position); if (file_pos != _sync_position) { - + _sync_marked = true; _sync_position = file_pos; - _flags = Flag (_flags|SyncMarked); - - if (!_frozen) { - char buf[64]; + if (!property_changes_suspended()) { maybe_uncopy (); - snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position); - save_state (buf); } - send_change (SyncOffsetChanged); + + send_change (Properties::sync_position); } } void Region::clear_sync_position () { - if (_flags & SyncMarked) { - _flags = Flag (_flags & ~SyncMarked); - - if (!_frozen) { + if (sync_marked()) { + _sync_marked = false; + if (!property_changes_suspended()) { maybe_uncopy (); - save_state ("sync point removed"); } - send_change (SyncOffsetChanged); + + send_change (Properties::sync_position); } } -jack_nframes_t +/* @return the sync point relative the first frame of the region */ +frameoffset_t Region::sync_offset (int& dir) const { - /* returns the sync point relative the first frame of the region */ - - if (_flags & SyncMarked) { + if (sync_marked()) { if (_sync_position > _start) { dir = 1; - return _sync_position - _start; + return _sync_position - _start; } else { dir = -1; return _start - _sync_position; @@ -748,117 +1042,161 @@ Region::sync_offset (int& dir) const } } -jack_nframes_t -Region::adjust_to_sync (jack_nframes_t pos) +framepos_t +Region::adjust_to_sync (framepos_t pos) const { int sync_dir; - jack_nframes_t offset = sync_offset (sync_dir); - + frameoffset_t offset = sync_offset (sync_dir); + + // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl; + if (sync_dir > 0) { - if (max_frames - pos > offset) { - pos += offset; - } - } else { if (pos > offset) { pos -= offset; } else { pos = 0; } + } else { + if (max_framepos - pos > offset) { + pos += offset; + } } return pos; } -jack_nframes_t +/** @return Sync position in session time */ +framepos_t Region::sync_position() const { - if (_flags & SyncMarked) { - return _sync_position; + if (sync_marked()) { + return _position - _start + _sync_position; } else { - return _start; + /* if sync has not been marked, use the start of the region */ + return _position; } } - void Region::raise () { - if (_playlist == 0) { - return; + boost::shared_ptr pl (playlist()); + if (pl) { + pl->raise_region (shared_from_this ()); } - - _playlist->raise_region (*this); } void Region::lower () { - if (_playlist == 0) { - return; + boost::shared_ptr pl (playlist()); + if (pl) { + pl->lower_region (shared_from_this ()); } - - _playlist->lower_region (*this); } + void Region::raise_to_top () { - - if (_playlist == 0) { - return; + boost::shared_ptr pl (playlist()); + if (pl) { + pl->raise_region_to_top (shared_from_this()); } - - _playlist->raise_region_to_top (*this); } void Region::lower_to_bottom () { - if (_playlist == 0) { - return; + boost::shared_ptr pl (playlist()); + if (pl) { + pl->lower_region_to_bottom (shared_from_this()); } - - _playlist->lower_region_to_bottom (*this); } void Region::set_layer (layer_t l) { - if (_layer != l) { - _layer = l; - - if (!_frozen) { - char buf[64]; - snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer); - save_state (buf); - } - - send_change (LayerChanged); - } + _layer = l; } XMLNode& -Region::state (bool full_state) +Region::state () { XMLNode *node = new XMLNode ("Region"); char buf[64]; - - snprintf (buf, sizeof (buf), "%" PRIu64, _id); + char buf2[64]; + LocaleGuard lg (X_("POSIX")); + const char* fe = NULL; + + add_properties (*node); + + id().print (buf, sizeof (buf)); node->add_property ("id", buf); - node->add_property ("name", _name); - snprintf (buf, sizeof (buf), "%u", _start); - node->add_property ("start", buf); - snprintf (buf, sizeof (buf), "%u", _length); - node->add_property ("length", buf); - snprintf (buf, sizeof (buf), "%u", _position); - node->add_property ("position", buf); + node->add_property ("type", _type.to_string()); + + switch (_first_edit) { + case EditChangesNothing: + fe = X_("nothing"); + break; + case EditChangesName: + fe = X_("name"); + break; + case EditChangesID: + fe = X_("id"); + break; + default: /* should be unreachable but makes g++ happy */ + fe = X_("nothing"); + break; + } + + node->add_property ("first-edit", fe); /* note: flags are stored by derived classes */ - snprintf (buf, sizeof (buf), "%d", (int) _layer); - node->add_property ("layer", buf); - snprintf (buf, sizeof (buf), "%u", _sync_position); - node->add_property ("sync-position", buf); + if (_position_lock_style != AudioTime) { + stringstream str; + str << _bbt_time; + node->add_property ("bbt-position", str.str()); + } + + for (uint32_t n=0; n < _sources.size(); ++n) { + snprintf (buf2, sizeof(buf2), "source-%d", n); + _sources[n]->id().print (buf, sizeof(buf)); + node->add_property (buf2, buf); + } + + for (uint32_t n=0; n < _master_sources.size(); ++n) { + snprintf (buf2, sizeof(buf2), "master-source-%d", n); + _master_sources[n]->id().print (buf, sizeof (buf)); + node->add_property (buf2, buf); + } + + /* Only store nested sources for the whole-file region that acts + as the parent/root of all regions using it. + */ + + if (_whole_file && max_source_level() > 0) { + + XMLNode* nested_node = new XMLNode (X_("NestedSource")); + + /* region is compound - get its playlist and + store that before we list the region that + needs it ... + */ + + for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) { + nested_node->add_child_nocopy ((*s)->get_state ()); + } + + if (nested_node) { + node->add_child_nocopy (*nested_node); + } + } + + if (_extra_xml) { + node->add_child_copy (*_extra_xml); + } return *node; } @@ -866,130 +1204,447 @@ Region::state (bool full_state) XMLNode& Region::get_state () { - return state (true); + return state (); } int -Region::set_state (const XMLNode& node) +Region::set_state (const XMLNode& node, int version) { - const XMLNodeList& nlist = node.children(); - const XMLProperty *prop; + PropertyChange what_changed; + return _set_state (node, version, what_changed, true); +} - if (_extra_xml) { - delete _extra_xml; - _extra_xml = 0; +int +Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send) +{ + const XMLProperty* prop; + + Stateful::save_extra_xml (node); + + what_changed = set_values (node); + + set_id (node); + + if (_position_lock_style == MusicTime) { + if ((prop = node.property ("bbt-position")) == 0) { + /* missing BBT info, revert to audio time locking */ + _position_lock_style = AudioTime; + } else { + if (sscanf (prop->value().c_str(), "%d|%d|%d", + &_bbt_time.bars, + &_bbt_time.beats, + &_bbt_time.ticks) != 3) { + _position_lock_style = AudioTime; + } + } } - if ((prop = node.property ("id")) == 0) { - error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg; - return -1; + /* fix problems with old sessions corrupted by impossible + values for _stretch or _shift + */ + if (_stretch == 0.0f) { + _stretch = 1.0f; } - sscanf (prop->value().c_str(), "%" PRIu64, &_id); + if (_shift == 0.0f) { + _shift = 1.0f; + } - if ((prop = node.property ("name")) == 0) { - error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg; - return -1; + if (send) { + send_change (what_changed); } - _name = prop->value(); + /* Quick fix for 2.x sessions when region is muted */ + if ((prop = node.property (X_("flags")))) { + if (string::npos != prop->value().find("Muted")){ + set_muted (true); + } + } - if ((prop = node.property ("start")) != 0) { - _start = (jack_nframes_t) atoi (prop->value().c_str()); + + return 0; +} + +void +Region::suspend_property_changes () +{ + Stateful::suspend_property_changes (); + _last_length = _length; + _last_position = _position; +} + +void +Region::mid_thaw (const PropertyChange& what_changed) +{ + if (what_changed.contains (Properties::length)) { + if (what_changed.contains (Properties::position)) { + recompute_at_start (); + } + recompute_at_end (); } +} - if ((prop = node.property ("length")) != 0) { - _length = (jack_nframes_t) atoi (prop->value().c_str()); +void +Region::send_change (const PropertyChange& what_changed) +{ + if (what_changed.empty()) { + return; } - if ((prop = node.property ("position")) != 0) { - _position = (jack_nframes_t) atoi (prop->value().c_str()); + Stateful::send_change (what_changed); + + if (!Stateful::property_changes_suspended()) { + + /* Try and send a shared_pointer unless this is part of the constructor. + If so, do nothing. + */ + + try { + boost::shared_ptr rptr = shared_from_this(); + RegionPropertyChanged (rptr, what_changed); + } catch (...) { + /* no shared_ptr available, relax; */ + } } +} + +bool +Region::overlap_equivalent (boost::shared_ptr other) const +{ + return coverage (other->first_frame(), other->last_frame()) != OverlapNone; +} + +bool +Region::equivalent (boost::shared_ptr other) const +{ + return _start == other->_start && + _position == other->_position && + _length == other->_length; +} + +bool +Region::size_equivalent (boost::shared_ptr other) const +{ + return _start == other->_start && + _length == other->_length; +} + +bool +Region::region_list_equivalent (boost::shared_ptr other) const +{ + return size_equivalent (other) && source_equivalent (other) && _name == other->_name; +} + +void +Region::source_deleted (boost::weak_ptr) +{ + drop_sources (); - if ((prop = node.property ("layer")) != 0) { - _layer = (layer_t) atoi (prop->value().c_str()); + if (!_session.deletion_in_progress()) { + /* this is a very special case: at least one of the region's + sources has bee deleted, so invalidate all references to + ourselves. Do NOT do this during session deletion, because + then we run the risk that this will actually result + in this object being deleted (as refcnt goes to zero) + while emitting DropReferences. + */ + + drop_references (); } +} - /* note: derived classes set flags */ +vector +Region::master_source_names () +{ + SourceList::iterator i; - if ((prop = node.property ("sync-position")) != 0) { - _sync_position = (jack_nframes_t) atoi (prop->value().c_str()); - } else { - _sync_position = _start; + vector names; + for (i = _master_sources.begin(); i != _master_sources.end(); ++i) { + names.push_back((*i)->name()); } - - for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { - - XMLNode *child; - - child = (*niter); - - if (child->name () == "extra") { - _extra_xml = new XMLNode (*child); - break; + + return names; +} + +void +Region::set_master_sources (const SourceList& srcs) +{ + for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { + (*i)->dec_use_count (); + } + + _master_sources = srcs; + assert (_sources.size() == _master_sources.size()); + + for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { + (*i)->inc_use_count (); + } +} + +bool +Region::source_equivalent (boost::shared_ptr other) const +{ + if (!other) + return false; + + if ((_sources.size() != other->_sources.size()) || + (_master_sources.size() != other->_master_sources.size())) { + return false; + } + + SourceList::const_iterator i; + SourceList::const_iterator io; + + for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) { + if ((*i)->id() != (*io)->id()) { + return false; } } - _first_edit = EditChangesNothing; + for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) { + if ((*i)->id() != (*io)->id()) { + return false; + } + } - return 0; + return true; } -void -Region::freeze () +std::string +Region::source_string () const { - _frozen++; + //string res = itos(_sources.size()); + + stringstream res; + res << _sources.size() << ":"; + + SourceList::const_iterator i; + + for (i = _sources.begin(); i != _sources.end(); ++i) { + res << (*i)->id() << ":"; + } + + for (i = _master_sources.begin(); i != _master_sources.end(); ++i) { + res << (*i)->id() << ":"; + } + + return res.str(); } -void -Region::thaw (const string& why) +bool +Region::uses_source (boost::shared_ptr source) const { - Change what_changed = Change (0); + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + if (*i == source) { + return true; + } - { - LockMonitor lm (lock, __LINE__, __FILE__); + boost::shared_ptr ps = boost::dynamic_pointer_cast (*i); - if (_frozen && --_frozen > 0) { - return; + if (ps) { + if (ps->playlist()->uses_source (source)) { + return true; + } } + } - if (pending_changed) { - what_changed = pending_changed; - pending_changed = Change (0); + return false; +} + +framecnt_t +Region::source_length(uint32_t n) const +{ + assert (n < _sources.size()); + return _sources[n]->length (_position - _start); +} + +bool +Region::verify_length (framecnt_t len) +{ + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + + framecnt_t maxlen = 0; + + for (uint32_t n = 0; n < _sources.size(); ++n) { + maxlen = max (maxlen, source_length(n) - _start); + } + + len = min (len, maxlen); + + return true; +} + +bool +Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length) +{ + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + + framecnt_t maxlen = 0; + + for (uint32_t n = 0; n < _sources.size(); ++n) { + maxlen = max (maxlen, source_length(n) - new_start); + } + + new_length = min (new_length, maxlen); + + return true; +} + +bool +Region::verify_start (framepos_t pos) +{ + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + + for (uint32_t n = 0; n < _sources.size(); ++n) { + if (pos > source_length(n) - _length) { + return false; } } + return true; +} - if (what_changed == Change (0)) { - return; +bool +Region::verify_start_mutable (framepos_t& new_start) +{ + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; } - if (what_changed & LengthChanged) { - if (what_changed & PositionChanged) { - recompute_at_start (); - } - recompute_at_end (); + for (uint32_t n = 0; n < _sources.size(); ++n) { + if (new_start > source_length(n) - _length) { + new_start = source_length(n) - _length; + } } - - save_state (why); - StateChanged (what_changed); + return true; } +boost::shared_ptr +Region::get_parent() const +{ + boost::shared_ptr pl (playlist()); + + if (pl) { + boost::shared_ptr r; + boost::shared_ptr grrr2 = boost::dynamic_pointer_cast (shared_from_this()); + + if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) { + return boost::static_pointer_cast (r); + } + } + + return boost::shared_ptr(); +} + +int +Region::apply (Filter& filter, Progress* progress) +{ + return filter.run (shared_from_this(), progress); +} + + void -Region::send_change (Change what_changed) +Region::invalidate_transients () { - { - LockMonitor lm (lock, __LINE__, __FILE__); - if (_frozen) { - pending_changed = Change (pending_changed|what_changed); - return; - } + _valid_transients = false; + _transients.clear (); + + send_change (PropertyChange (Properties::valid_transients)); +} + +void +Region::drop_sources () +{ + for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) { + (*i)->dec_use_count (); + } + + _sources.clear (); + + for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { + (*i)->dec_use_count (); } - StateManager::send_state_changed (what_changed); + _master_sources.clear (); } void -Region::set_last_layer_op (uint64_t when) +Region::use_sources (SourceList const & s) { - _last_layer_op = when; + set > unique_srcs; + + for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) { + + _sources.push_back (*i); + (*i)->inc_use_count (); + _master_sources.push_back (*i); + (*i)->inc_use_count (); + + /* connect only once to DropReferences, even if sources are replicated + */ + + if (unique_srcs.find (*i) == unique_srcs.end ()) { + unique_srcs.insert (*i); + (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr(*i))); + } + } } + +Trimmable::CanTrim +Region::can_trim () const +{ + CanTrim ct = CanTrim (0); + + if (locked()) { + return ct; + } + + /* if not locked, we can always move the front later, and the end earlier + */ + + ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier); + + if (start() != 0 || can_trim_start_before_source_start ()) { + ct = CanTrim (ct | FrontTrimEarlier); + } + + if (!_sources.empty()) { + if ((start() + length()) < _sources.front()->length (0)) { + ct = CanTrim (ct | EndTrimLater); + } + } + + return ct; +} + +uint32_t +Region::max_source_level () const +{ + uint32_t lvl = 0; + + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + lvl = max (lvl, (*i)->level()); + } + + return lvl; +} + +bool +Region::is_compound () const +{ + return max_source_level() > 0; +} + +void +Region::post_set (const PropertyChange& pc) +{ + if (pc.contains (Properties::position)) { + recompute_position_from_lock_style (); + } +} +