X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fregion.cc;h=1f4d6f0f912221d78d3fc4739ea8840a82d13af4;hb=7d96960b162d25da87c388a3083775e8770bba56;hp=ecc7b5e30550b5fa0a035bd6d947798b8f677294;hpb=badc087263990ecf360792c10e4d9f2d60828d43;p=ardour.git diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index ecc7b5e305..1f4d6f0f91 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 @@ -21,20 +21,24 @@ #include #include #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/region.h" +#include "ardour/playlist.h" +#include "ardour/session.h" +#include "ardour/source.h" +#include "ardour/tempo.h" +#include "ardour/region_factory.h" +#include "ardour/filter.h" +#include "ardour/profile.h" +#include "ardour/utils.h" #include "i18n.h" @@ -50,152 +54,215 @@ Change Region::LockChanged = ARDOUR::new_change (); Change Region::LayerChanged = ARDOUR::new_change (); Change Region::HiddenChanged = ARDOUR::new_change (); +PBD::Signal1 > Region::RegionPropertyChanged; /* derived-from-derived constructor (no sources in constructor) */ Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) - : Automatable(s, name) + : SessionObject(s, name) , _type(type) - , _flags(flags) - , _start(start) - , _length(length) - , _position(0) + , _flags(Flag (flags|DoNotSendPropertyChanges)) + , _start(start) + , _length(length) + , _position(0) + , _last_position(0) + , _positional_lock_style(AudioTime) , _sync_position(_start) , _layer(layer) , _first_edit(EditChangesNothing) , _frozen(0) + , _ancestral_start (0) + , _ancestral_length (0) + , _stretch(1.0) + , _shift(1.0) , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) + , _pending_explicit_relayer (false) { /* no sources at this point */ } - /** Basic Region constructor (single source) */ Region::Region (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) - : Automatable(src->session(), name) + : SessionObject(src->session(), name) , _type(type) - , _flags(flags) - , _start(start) - , _length(length) - , _position(0) + , _flags(Flag (flags|DoNotSendPropertyChanges)) + , _start(start) + , _length(length) + , _position(0) + , _last_position(0) + , _positional_lock_style(AudioTime) , _sync_position(_start) , _layer(layer) , _first_edit(EditChangesNothing) , _frozen(0) - , _ancestral_start (start) - , _ancestral_length (length) + , _ancestral_start (0) + , _ancestral_length (0) , _stretch (1.0) + , _shift (1.0) + , _valid_transients(false) , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) + , _pending_explicit_relayer (false) { _sources.push_back (src); _master_sources.push_back (src); - src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src)); + src->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr(src))); assert(_sources.size() > 0); + _positional_lock_style = AudioTime; } /** Basic Region constructor (many sources) */ -Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) - : Automatable(srcs.front()->session(), name) +Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags) + : SessionObject(srcs.front()->session(), name) , _type(type) - , _flags(flags) - , _start(start) - , _length(length) - , _position(0) + , _flags(Flag (flags|DoNotSendPropertyChanges)) + , _start(start) + , _length(length) + , _position(0) + , _last_position(0) + , _positional_lock_style(AudioTime) , _sync_position(_start) , _layer(layer) , _first_edit(EditChangesNothing) , _frozen(0) + , _ancestral_start (0) + , _ancestral_length (0) + , _stretch(1.0) + , _shift(1.0) , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) + , _pending_explicit_relayer (false) { - - set > unique_srcs; + use_sources (srcs); + assert(_sources.size() > 0); +} - for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { - _sources.push_back (*i); - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - unique_srcs.insert (*i); +/** Create a new Region from part of an existing one */ +Region::Region (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) + : SessionObject(other->session(), name) + , _type (other->data_type()) + , _pending_explicit_relayer (false) + +{ + _start = other->_start + offset; + copy_stuff (other, offset, length, name, layer, flags); + + _flags = Flag (_flags | DoNotSendPropertyChanges); + + /* 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->flags() & SyncMarked) { + if (other->_sync_position < _start) { + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; + } else { + _sync_position = other->_sync_position; + } + } else { + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; } - for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) { - _master_sources.push_back (*i); - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); + if (Profile->get_sae()) { + /* reset sync point to start if its ended up + outside region bounds. + */ + + if (_sync_position < _start || _sync_position >= _start + _length) { + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; } } - - assert(_sources.size() > 0); } -/** Create a new Region from part of an existing one */ -Region::Region (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) - : Automatable(other->session(), name) - , _type(other->data_type()) - , _flags(Flag(flags & ~(Locked|PositionLocked|WholeFile|Hidden))) - , _start(other->_start + offset) - , _length(length) - , _position(0) - , _sync_position(_start) - , _layer(layer) - , _first_edit(EditChangesNothing) - , _frozen(0) - , _ancestral_start (other->_ancestral_start + offset) - , _ancestral_length (length) - , _stretch (1.0) - , _read_data_count(0) - , _pending_changed(Change (0)) - , _last_layer_op(0) +Region::Region (boost::shared_ptr other, nframes_t length, const string& name, layer_t layer, Flag flags) + : SessionObject(other->session(), name) + , _type (other->data_type()) + , _pending_explicit_relayer (false) { - if (other->_sync_position < offset) - _sync_position = other->_sync_position; + /* create a new Region exactly like another but starting at 0 in its sources */ - set > unique_srcs; + _start = 0; + copy_stuff (other, 0, length, name, layer, flags); - for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) { - _sources.push_back (*i); - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - unique_srcs.insert (*i); - } - - if (other->_sync_position < offset) { - _sync_position = other->_sync_position; + _flags = Flag (_flags | DoNotSendPropertyChanges); + + /* 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->flags() & SyncMarked) { + 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. */ + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; + } + } else { + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; } - for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) { - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); + if (Profile->get_sae()) { + /* reset sync point to start if its ended up + outside region bounds. + */ + + if (_sync_position < _start || _sync_position >= _start + _length) { + _flags = Flag (_flags & ~SyncMarked); + _sync_position = _start; } - _master_sources.push_back (*i); } - - assert(_sources.size() > 0); + + /* reset a couple of things that copy_stuff() gets wrong in this particular case */ + + _positional_lock_style = other->_positional_lock_style; + _first_edit = other->_first_edit; } /** Pure copy constructor */ Region::Region (boost::shared_ptr other) - : Automatable(other->session(), other->name()) + : SessionObject(other->session(), other->name()) , _type(other->data_type()) , _flags(Flag(other->_flags & ~(Locked|PositionLocked))) - , _start(other->_start) - , _length(other->_length) - , _position(other->_position) + , _start(other->_start) + , _length(other->_length) + , _position(other->_position) + , _last_position(other->_last_position) + , _positional_lock_style(other->_positional_lock_style) , _sync_position(other->_sync_position) , _layer(other->_layer) , _first_edit(EditChangesID) , _frozen(0) - , _ancestral_start (_start) - , _ancestral_length (_length) - , _stretch (1.0) + , _ancestral_start (other->_ancestral_start) + , _ancestral_length (other->_ancestral_length) + , _stretch (other->_stretch) + , _shift (other->_shift) + , _valid_transients(false) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(other->_last_layer_op) + , _pending_explicit_relayer (false) { + _flags = Flag (_flags | DoNotSendPropertyChanges); + other->_first_edit = EditChangesName; if (other->_extra_xml) { @@ -204,55 +271,33 @@ Region::Region (boost::shared_ptr other) _extra_xml = 0; } - set > unique_srcs; - - for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) { - _sources.push_back (*i); - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - unique_srcs.insert (*i); - } - - for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) { - _master_sources.push_back (*i); - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - } - } - + use_sources (other->_sources); assert(_sources.size() > 0); } -Region::Region (SourceList& srcs, const XMLNode& node) - : Automatable(srcs.front()->session(), X_("error: XML did not reset this")) +Region::Region (const SourceList& srcs, const XMLNode& node) + : SessionObject(srcs.front()->session(), X_("error: XML did not reset this")) , _type(DataType::NIL) // to be loaded from XML - , _flags(Flag(0)) - , _start(0) - , _length(0) - , _position(0) + , _flags(DoNotSendPropertyChanges) + , _start(0) + , _length(0) + , _position(0) + , _last_position(0) + , _positional_lock_style(AudioTime) , _sync_position(_start) , _layer(0) , _first_edit(EditChangesNothing) , _frozen(0) + , _stretch(1.0) + , _shift(1.0) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(0) + , _pending_explicit_relayer (false) { - set > unique_srcs; - - for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { - _sources.push_back (*i); - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - unique_srcs.insert (*i); - } + use_sources (srcs); - for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) { - _master_sources.push_back (*i); - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i))); - } - } - - if (set_state (node)) { + if (set_state (node, Stateful::loading_state_version)) { throw failed_constructor(); } @@ -261,75 +306,71 @@ Region::Region (SourceList& srcs, const XMLNode& node) } Region::Region (boost::shared_ptr src, const XMLNode& node) - : Automatable(src->session(), X_("error: XML did not reset this")) + : SessionObject(src->session(), X_("error: XML did not reset this")) , _type(DataType::NIL) - , _flags(Flag(0)) - , _start(0) - , _length(0) - , _position(0) + , _flags(DoNotSendPropertyChanges) + , _start(0) + , _length(0) + , _position(0) + , _last_position(0) + , _positional_lock_style(AudioTime) , _sync_position(_start) , _layer(0) , _first_edit(EditChangesNothing) , _frozen(0) + , _stretch(1.0) + , _shift(1.0) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(0) + , _pending_explicit_relayer (false) { _sources.push_back (src); - if (set_state (node)) { + if (set_state (node, Stateful::loading_state_version)) { throw failed_constructor(); } - + assert(_type != DataType::NIL); assert(_sources.size() > 0); } Region::~Region () { - boost::shared_ptr pl (playlist()); - - if (pl) { - for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (pl); - } - } - - notify_callbacks (); - GoingAway (); /* EMIT SIGNAL */ + DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this)); } void -Region::set_playlist (boost::weak_ptr wpl) +Region::copy_stuff (boost::shared_ptr other, nframes_t /*offset*/, nframes_t length, const string& name, layer_t layer, Flag flags) { - boost::shared_ptr old_playlist = (_playlist.lock()); - - boost::shared_ptr pl (wpl.lock()); - - if (old_playlist == pl) { - return; - } + _frozen = 0; + _pending_changed = Change (0); + _read_data_count = 0; + _valid_transients = false; + + _length = length; + _last_length = length; + _sync_position = other->_sync_position; + _ancestral_start = other->_ancestral_start; + _ancestral_length = other->_ancestral_length; + _stretch = other->_stretch; + _shift = other->_shift; + _name = name; + _last_position = 0; + _position = 0; + _layer = layer; + _flags = Flag (flags & ~(Locked|WholeFile|Hidden)); + _first_edit = EditChangesNothing; + _last_layer_op = 0; + _positional_lock_style = AudioTime; - _playlist = pl; + use_sources (other->_sources); +} - if (pl) { - if (old_playlist) { - for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (_playlist); - (*i)->add_playlist (pl); - } - } else { - for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->add_playlist (pl); - } - } - } else { - if (old_playlist) { - for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (old_playlist); - } - } - } +void +Region::set_playlist (boost::weak_ptr wpl) +{ + _playlist = wpl.lock(); } bool @@ -337,7 +378,7 @@ Region::set_name (const std::string& str) { if (_name != str) { SessionObject::set_name(str); // EMIT SIGNAL NameChanged() - assert(_name == str); + assert(_name == str); send_change (ARDOUR::NameChanged); } @@ -345,15 +386,16 @@ Region::set_name (const std::string& str) } void -Region::set_length (nframes_t len, void *src) +Region::set_length (nframes_t len, void */*src*/) { + //cerr << "Region::set_length() len = " << len << endl; if (_flags & Locked) { return; } if (_length != len && len != 0) { - /* check that the current _position wouldn't make the new + /* check that the current _position wouldn't make the new length impossible. */ @@ -364,13 +406,16 @@ Region::set_length (nframes_t len, void *src) if (!verify_length (len)) { return; } - + + + _last_length = _length; _length = len; _flags = Region::Flag (_flags & ~WholeFile); first_edit (); maybe_uncopy (); + invalidate_transients (); if (!_frozen) { recompute_at_end (); @@ -392,7 +437,7 @@ Region::first_edit () if (_first_edit != EditChangesNothing && pl) { - _name = pl->session().new_region_name (_name); + _name = _session.new_region_name (_name); _first_edit = EditChangesNothing; send_change (ARDOUR::NameChanged); @@ -408,7 +453,7 @@ Region::at_natural_position () const if (!pl) { return false; } - + boost::shared_ptr whole_file_region = get_parent(); if (whole_file_region) { @@ -428,43 +473,89 @@ Region::move_to_natural_position (void *src) if (!pl) { return; } - + boost::shared_ptr whole_file_region = get_parent(); if (whole_file_region) { set_position (whole_file_region->position() + _start, src); } } - + void Region::special_set_position (nframes_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 (nframes_t pos, void *src) +Region::set_position_lock_style (PositionLockStyle ps) +{ + boost::shared_ptr pl (playlist()); + + if (!pl) { + return; + } + + _positional_lock_style = ps; + + if (_positional_lock_style == MusicTime) { + _session.tempo_map().bbt_time (_position, _bbt_time); + } + +} + +void +Region::update_position_after_tempo_map_change () +{ + boost::shared_ptr pl (playlist()); + + if (!pl || _positional_lock_style != MusicTime) { + return; + } + + TempoMap& map (_session.tempo_map()); + nframes_t pos = map.frame_time (_bbt_time); + set_position_internal (pos, false); +} + +void +Region::set_position (nframes_t pos, void* /*src*/) { if (!can_move()) { return; } + set_position_internal (pos, true); +} + +void +Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute) +{ if (_position != pos) { + _last_position = _position; _position = pos; /* check that the new _position wouldn't make the current - length impossible - if so, change the length. + length impossible - if so, change the length. XXX is this the right thing to do? */ if (max_frames - _length < _position) { + _last_length = _length; _length = max_frames - _position; } + + if (allow_bbt_recompute) { + recompute_position_from_lock_style (); + } + + invalidate_transients (); } /* do this even if the position is the same. this helps out @@ -475,13 +566,14 @@ Region::set_position (nframes_t pos, void *src) } void -Region::set_position_on_top (nframes_t pos, void *src) +Region::set_position_on_top (nframes_t pos, void* /*src*/) { if (_flags & Locked) { return; } if (_position != pos) { + _last_position = _position; _position = pos; } @@ -494,12 +586,20 @@ Region::set_position_on_top (nframes_t pos, void *src) /* do this even if the position is the same. this helps out a GUI that has moved its representation already. */ - + send_change (PositionChanged); } void -Region::nudge_position (long n, void *src) +Region::recompute_position_from_lock_style () +{ + if (_positional_lock_style == MusicTime) { + _session.tempo_map().bbt_time (_position, _bbt_time); + } +} + +void +Region::nudge_position (nframes64_t n, void* /*src*/) { if (_flags & Locked) { return; @@ -508,7 +608,9 @@ Region::nudge_position (long n, void *src) if (n == 0) { return; } - + + _last_position = _position; + if (n > 0) { if (_position > max_frames - n) { _position = max_frames; @@ -527,15 +629,16 @@ Region::nudge_position (long n, void *src) } void -Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st) +Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh) { _ancestral_length = l; _ancestral_start = s; _stretch = st; + _shift = sh; } void -Region::set_start (nframes_t pos, void *src) +Region::set_start (nframes_t pos, void* /*src*/) { if (_flags & (Locked|PositionLocked)) { return; @@ -554,20 +657,21 @@ Region::set_start (nframes_t pos, void *src) _start = pos; _flags = Region::Flag (_flags & ~WholeFile); first_edit (); + invalidate_transients (); send_change (StartChanged); } } void -Region::trim_start (nframes_t new_position, void *src) +Region::trim_start (nframes_t new_position, void */*src*/) { if (_flags & (Locked|PositionLocked)) { return; } nframes_t new_start; int32_t start_shift; - + if (new_position > _position) { start_shift = new_position - _position; } else { @@ -600,7 +704,7 @@ Region::trim_start (nframes_t new_position, void *src) if (new_start == _start) { return; } - + _start = new_start; _flags = Region::Flag (_flags & ~WholeFile); first_edit (); @@ -625,20 +729,20 @@ Region::trim_front (nframes_t new_position, void *src) } if (new_position < end) { /* can't trim it zero or negative length */ - + nframes_t newlen; /* can't trim it back passed where source position zero is located */ - + new_position = max (new_position, source_zero); - - + + if (new_position > _position) { newlen = _length - (new_position - _position); } else { newlen = _length + (_position - new_position); } - + trim_to_internal (new_position, newlen, src); if (!_frozen) { recompute_at_start (); @@ -646,15 +750,19 @@ Region::trim_front (nframes_t new_position, void *src) } } +/** @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 (nframes_t new_endpoint, void *src) +Region::trim_end (nframes_t new_endpoint, void */*src*/) { if (_flags & Locked) { return; } if (new_endpoint > _position) { - trim_to_internal (_position, new_endpoint - _position, this); + trim_to_internal (_position, new_endpoint - _position + 1, this); if (!_frozen) { recompute_at_end (); } @@ -677,7 +785,7 @@ Region::trim_to (nframes_t position, nframes_t length, void *src) } void -Region::trim_to_internal (nframes_t position, nframes_t length, void *src) +Region::trim_to_internal (nframes_t position, nframes_t length, void */*src*/) { int32_t start_shift; nframes_t new_start; @@ -723,24 +831,30 @@ Region::trim_to_internal (nframes_t position, nframes_t length, void *src) what_changed = Change (what_changed|StartChanged); } if (_length != length) { + if (!_frozen) { + _last_length = _length; + } _length = length; what_changed = Change (what_changed|LengthChanged); } if (_position != position) { + if (!_frozen) { + _last_position = _position; + } _position = position; what_changed = Change (what_changed|PositionChanged); } - + _flags = Region::Flag (_flags & ~WholeFile); if (what_changed & (StartChanged|LengthChanged)) { first_edit (); - } + } if (what_changed) { send_change (what_changed); } -} +} void Region::set_hidden (bool yn) @@ -814,12 +928,10 @@ Region::set_position_locked (bool yn) void Region::set_sync_position (nframes_t absolute_pos) { - nframes_t file_pos; - - file_pos = _start + (absolute_pos - _position); + nframes_t const file_pos = _start + (absolute_pos - _position); if (file_pos != _sync_position) { - + _sync_position = file_pos; _flags = Flag (_flags|SyncMarked); @@ -851,7 +963,7 @@ Region::sync_offset (int& dir) const if (_flags & SyncMarked) { if (_sync_position > _start) { dir = 1; - return _sync_position - _start; + return _sync_position - _start; } else { dir = -1; return _start - _sync_position; @@ -862,22 +974,24 @@ Region::sync_offset (int& dir) const } } -nframes_t -Region::adjust_to_sync (nframes_t pos) +nframes_t +Region::adjust_to_sync (nframes_t pos) const { int sync_dir; nframes_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_frames - pos > offset) { + pos += offset; + } } return pos; @@ -887,12 +1001,30 @@ nframes_t Region::sync_position() const { if (_flags & SyncMarked) { - return _sync_position; + return _sync_position; } else { return _start; } } +void +Region::raise () +{ + boost::shared_ptr pl (playlist()); + if (pl) { + pl->raise_region (shared_from_this ()); + } +} + +void +Region::lower () +{ + boost::shared_ptr pl (playlist()); + if (pl) { + pl->lower_region (shared_from_this ()); + } +} + void Region::raise_to_top () @@ -917,13 +1049,13 @@ Region::set_layer (layer_t l) { if (_layer != l) { _layer = l; - + send_change (LayerChanged); } } XMLNode& -Region::state (bool full_state) +Region::state (bool /*full_state*/) { XMLNode *node = new XMLNode ("Region"); char buf[64]; @@ -939,13 +1071,15 @@ Region::state (bool full_state) node->add_property ("length", buf); snprintf (buf, sizeof (buf), "%u", _position); node->add_property ("position", buf); - snprintf (buf, sizeof (buf), "%lu", _ancestral_start); + snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start); node->add_property ("ancestral-start", buf); - snprintf (buf, sizeof (buf), "%lu", _ancestral_length); + snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length); node->add_property ("ancestral-length", buf); snprintf (buf, sizeof (buf), "%.12g", _stretch); node->add_property ("stretch", buf); - + snprintf (buf, sizeof (buf), "%.12g", _shift); + node->add_property ("shift", buf); + switch (_first_edit) { case EditChangesNothing: fe = X_("nothing"); @@ -961,7 +1095,7 @@ Region::state (bool full_state) break; } - node->add_property ("first_edit", fe); + node->add_property ("first-edit", fe); /* note: flags are stored by derived classes */ @@ -970,6 +1104,13 @@ Region::state (bool full_state) snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position); node->add_property ("sync-position", buf); + if (_positional_lock_style != AudioTime) { + node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style)); + stringstream str; + str << _bbt_time; + node->add_property ("bbt-position", str.str()); + } + return *node; } @@ -980,13 +1121,13 @@ Region::get_state () } int -Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) +Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_changed, bool send) { const XMLNodeList& nlist = node.children(); const XMLProperty *prop; nframes_t val; - /* this is responsible for setting those aspects of Region state + /* this is responsible for setting those aspects of Region state that are mutable after construction. */ @@ -996,7 +1137,7 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) } _name = prop->value(); - + if ((prop = node.property ("type")) == 0) { _type = DataType::AUDIO; } else { @@ -1006,7 +1147,8 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) if ((prop = node.property ("start")) != 0) { sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _start) { - what_changed = Change (what_changed|StartChanged); + what_changed = Change (what_changed|StartChanged); + cerr << _name << " start changed\n"; _start = val; } } else { @@ -1017,9 +1159,12 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _length) { what_changed = Change (what_changed|LengthChanged); + cerr << _name << " length changed\n"; + _last_length = _length; _length = val; } } else { + _last_length = _length; _length = 1; } @@ -1027,9 +1172,12 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _position) { what_changed = Change (what_changed|PositionChanged); + cerr << _name << " position changed\n"; + _last_position = _position; _position = val; } } else { + _last_position = _position; _position = 0; } @@ -1038,6 +1186,7 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) x = (layer_t) atoi (prop->value().c_str()); if (x != _layer) { what_changed = Change (what_changed|LayerChanged); + cerr << _name << " layer changed\n"; _layer = x; } } else { @@ -1048,54 +1197,96 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _sync_position) { what_changed = Change (what_changed|SyncOffsetChanged); + cerr << _name << " sync changed\n"; _sync_position = val; } } else { _sync_position = _start; } + if ((prop = node.property ("positional-lock-style")) != 0) { + _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style)); + + if (_positional_lock_style == MusicTime) { + if ((prop = node.property ("bbt-position")) == 0) { + /* missing BBT info, revert to audio time locking */ + _positional_lock_style = AudioTime; + } else { + if (sscanf (prop->value().c_str(), "%d|%d|%d", + &_bbt_time.bars, + &_bbt_time.beats, + &_bbt_time.ticks) != 3) { + _positional_lock_style = AudioTime; + } + } + } + + } else { + _positional_lock_style = AudioTime; + } + /* XXX FIRST EDIT !!! */ - + /* these 3 properties never change as a result of any editing */ if ((prop = node.property ("ancestral-start")) != 0) { - _ancestral_start = atoi (prop->value()); + _ancestral_start = strtoll (prop->value().c_str(), 0, 10); } else { _ancestral_start = _start; } if ((prop = node.property ("ancestral-length")) != 0) { - _ancestral_length = atoi (prop->value()); + _ancestral_length = strtoll (prop->value().c_str(), 0, 10); } else { _ancestral_length = _length; } if ((prop = node.property ("stretch")) != 0) { _stretch = atof (prop->value()); + + /* fix problem with old sessions corrupted by an impossible + value for _stretch + */ + if (_stretch == 0.0) { + _stretch = 1.0; + } } else { _stretch = 1.0; } - /* note: derived classes set flags */ + if ((prop = node.property ("shift")) != 0) { + _shift = atof (prop->value()); - if (_extra_xml) { - delete _extra_xml; - _extra_xml = 0; + /* fix problem with old sessions corrupted by an impossible + value for _shift + */ + if (_shift == 0.0) { + _shift = 1.0; + } + } else { + _shift = 1.0; } + + /* note: derived classes set flags */ + + delete _extra_xml; + _extra_xml = 0; + for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { - + XMLNode *child; - + child = (*niter); - - if (child->name () == "extra") { + + if (child->name () == "Extra") { _extra_xml = new XMLNode (*child); break; } } if (send) { + cerr << _name << ": final change to be sent: " << hex << what_changed << dec << endl; send_change (what_changed); } @@ -1103,7 +1294,7 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) } int -Region::set_state (const XMLNode& node) +Region::set_state (const XMLNode& node, int version) { const XMLProperty *prop; Change what_changed = Change (0); @@ -1116,10 +1307,10 @@ Region::set_state (const XMLNode& node) } _id = prop->value(); - + _first_edit = EditChangesNothing; - - set_live_state (node, what_changed, true); + + set_live_state (node, version, what_changed, true); return 0; } @@ -1128,10 +1319,12 @@ void Region::freeze () { _frozen++; + _last_length = _length; + _last_position = _position; } void -Region::thaw (const string& why) +Region::thaw (const string& /*why*/) { Change what_changed = Change (0); @@ -1155,25 +1348,45 @@ Region::thaw (const string& why) if (what_changed & LengthChanged) { if (what_changed & PositionChanged) { recompute_at_start (); - } + } recompute_at_end (); } - - StateChanged (what_changed); + + send_change (what_changed); } void Region::send_change (Change what_changed) { + { Glib::Mutex::Lock lm (_lock); if (_frozen) { _pending_changed = Change (_pending_changed|what_changed); return; - } + } } + cerr << _name << " actually sends " << hex << what_changed << dec << " @" << get_microseconds() << endl; StateChanged (what_changed); + cerr << _name << " done with " << hex << what_changed << dec << " @" << get_microseconds() << endl; + + if (!(_flags & DoNotSendPropertyChanges)) { + + /* 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(); + cerr << _name << " actually sends prop change " << hex << what_changed << dec << " @ " << get_microseconds() << endl; + RegionPropertyChanged (rptr); + cerr << _name << " done with prop change @ " << get_microseconds() << endl; + } catch (...) { + /* no shared_ptr available, relax; */ + } + } + } void @@ -1210,9 +1423,21 @@ Region::region_list_equivalent (boost::shared_ptr other) const } void -Region::source_deleted (boost::shared_ptr) +Region::source_deleted (boost::weak_ptr) { - delete this; + _sources.clear (); + + 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 (); + } } vector @@ -1229,9 +1454,10 @@ Region::master_source_names () } void -Region::set_master_sources (SourceList& srcs) +Region::set_master_sources (const SourceList& srcs) { _master_sources = srcs; + assert (_sources.size() == _master_sources.size()); } bool @@ -1258,32 +1484,68 @@ Region::source_equivalent (boost::shared_ptr other) const return true; } +bool +Region::uses_source (boost::shared_ptr source) const +{ + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + if (*i == source) { + return true; + } + } + return false; +} + +sframes_t +Region::source_length(uint32_t n) const +{ + return _sources[n]->length(_position - _start); +} + bool Region::verify_length (nframes_t len) { + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + + nframes_t maxlen = 0; + for (uint32_t n=0; n < _sources.size(); ++n) { - if (_start > _sources[n]->length() - len) { - return false; - } + maxlen = max (maxlen, (nframes_t)source_length(n) - _start); } + + len = min (len, maxlen); + return true; } bool -Region::verify_start_and_length (nframes_t new_start, nframes_t new_length) +Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length) { + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + + nframes_t maxlen = 0; + for (uint32_t n=0; n < _sources.size(); ++n) { - if (new_length > _sources[n]->length() - new_start) { - return false; - } + maxlen = max (maxlen, (nframes_t)source_length(n) - new_start); } + + new_length = min (new_length, maxlen); + return true; } + bool Region::verify_start (nframes_t pos) { + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + for (uint32_t n=0; n < _sources.size(); ++n) { - if (pos > _sources[n]->length() - _length) { + if (pos > source_length(n) - _length) { return false; } } @@ -1293,9 +1555,13 @@ Region::verify_start (nframes_t pos) bool Region::verify_start_mutable (nframes_t& new_start) { + if (source() && (source()->destructive() || source()->length_mutable())) { + return true; + } + for (uint32_t n=0; n < _sources.size(); ++n) { - if (new_start > _sources[n]->length() - _length) { - new_start = _sources[n]->length() - _length; + if (new_start > source_length(n) - _length) { + new_start = source_length(n) - _length; } } return true; @@ -1309,12 +1575,12 @@ Region::get_parent() const if (pl) { boost::shared_ptr r; boost::shared_ptr grrr2 = boost::dynamic_pointer_cast (shared_from_this()); - - if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) { + + if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) { return boost::static_pointer_cast (r); } } - + return boost::shared_ptr(); } @@ -1325,3 +1591,30 @@ Region::apply (Filter& filter) } +void +Region::invalidate_transients () +{ + _valid_transients = false; + _transients.clear (); +} + + +void +Region::use_sources (SourceList const & s) +{ + set > unique_srcs; + + for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) { + _sources.push_back (*i); + (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr(*i))); + unique_srcs.insert (*i); + } + + for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) { + _master_sources.push_back (*i); + if (unique_srcs.find (*i) == unique_srcs.end()) { + (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr(*i))); + } + } +} +