RegionFX: include plugin tail with pre-fade Fx
This commit is contained in:
parent
c2169d6d51
commit
c16e31012b
@ -171,6 +171,8 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
bool remove_plugin (std::shared_ptr<RegionFxPlugin>);
|
||||
void reorder_plugins (RegionFxList const&);
|
||||
|
||||
timecnt_t tail () const;
|
||||
|
||||
/* automation */
|
||||
|
||||
std::shared_ptr<Evoral::Control>
|
||||
@ -264,6 +266,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
|
||||
void apply_region_fx (BufferSet&, samplepos_t, samplepos_t, samplecnt_t);
|
||||
void fx_latency_changed (bool no_emit);
|
||||
void fx_tail_changed (bool no_emit);
|
||||
void copy_plugin_state (std::shared_ptr<const AudioRegion>);
|
||||
|
||||
mutable samplepos_t _fx_pos;
|
||||
@ -274,6 +277,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
|
||||
mutable BufferSet _readcache;
|
||||
mutable samplepos_t _cache_start;
|
||||
mutable samplepos_t _cache_end;
|
||||
mutable samplecnt_t _cache_tail;
|
||||
mutable std::atomic<bool> _invalidated;
|
||||
|
||||
protected:
|
||||
|
@ -402,7 +402,7 @@ protected:
|
||||
|
||||
void _set_sort_id ();
|
||||
|
||||
std::shared_ptr<RegionList> regions_touched_locked (timepos_t const & start, timepos_t const & end);
|
||||
std::shared_ptr<RegionList> regions_touched_locked (timepos_t const & start, timepos_t const & end, bool with_tail);
|
||||
|
||||
void notify_region_removed (std::shared_ptr<Region>);
|
||||
void notify_region_added (std::shared_ptr<Region>);
|
||||
|
@ -145,6 +145,8 @@ public:
|
||||
timepos_t end() const;
|
||||
timepos_t nt_last() const { return end().decrement(); }
|
||||
|
||||
virtual timecnt_t tail () const { return timecnt_t (0); }
|
||||
|
||||
timepos_t source_position () const;
|
||||
timecnt_t source_relative_position (Temporal::timepos_t const &) const;
|
||||
timecnt_t region_relative_position (Temporal::timepos_t const &) const;
|
||||
@ -301,8 +303,8 @@ public:
|
||||
* OverlapEnd: the range overlaps the end of this region.
|
||||
* OverlapExternal: the range overlaps all of this region.
|
||||
*/
|
||||
Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end) const {
|
||||
return Temporal::coverage_exclusive_ends (position(), nt_last(), start, end);
|
||||
Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end, bool with_tail = false) const {
|
||||
return Temporal::coverage_exclusive_ends (position(), with_tail ? nt_last() + tail() : nt_last(), start, end);
|
||||
}
|
||||
|
||||
bool exact_equivalent (std::shared_ptr<const Region>) const;
|
||||
@ -564,6 +566,7 @@ protected:
|
||||
protected:
|
||||
virtual bool _add_plugin (std::shared_ptr<RegionFxPlugin>, std::shared_ptr<RegionFxPlugin>, bool) { return false; }
|
||||
virtual void fx_latency_changed (bool no_emit);
|
||||
virtual void fx_tail_changed (bool no_emit);
|
||||
|
||||
virtual void send_change (const PBD::PropertyChange&);
|
||||
virtual int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal);
|
||||
@ -584,6 +587,7 @@ protected:
|
||||
|
||||
mutable Glib::Threads::RWLock _fx_lock;
|
||||
uint32_t _fx_latency;
|
||||
uint32_t _fx_tail;
|
||||
RegionFxList _plugins;
|
||||
|
||||
PBD::Property<bool> _sync_marked;
|
||||
|
@ -176,7 +176,7 @@ ARDOUR::timecnt_t
|
||||
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, timepos_t const & start, timecnt_t const & cnt, uint32_t chan_n)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
|
||||
name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
|
||||
name(), start.samples(), cnt.samples(), chan_n, regions.size(), mixdown_buffer, gain_buffer));
|
||||
|
||||
samplecnt_t const scnt (cnt.samples ());
|
||||
|
||||
@ -204,7 +204,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
/* Find all the regions that are involved in the bit we are reading,
|
||||
and sort them by descending layer and ascending position.
|
||||
*/
|
||||
std::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt);
|
||||
std::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt, true);
|
||||
all->sort (ReadSorter ());
|
||||
|
||||
/* This will be a list of the bits of our read range that we have
|
||||
@ -236,7 +236,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
*/
|
||||
Temporal::Range rrange = ar->range_samples ();
|
||||
Temporal::Range region_range (max (rrange.start(), start),
|
||||
min (rrange.end(), start + cnt));
|
||||
min (rrange.end() + ar->tail (), start + cnt));
|
||||
|
||||
/* ... and then remove the bits that are already done */
|
||||
|
||||
@ -256,9 +256,9 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
/* Cut this range down to just the body and mark it done */
|
||||
Temporal::Range body = ar->body_range ();
|
||||
|
||||
if (body.start() < d.end() && body.end() > d.start()) {
|
||||
if (body.start() < d.end().earlier (ar->tail ()) && body.end() > d.start()) {
|
||||
d.set_start (max (d.start(), body.start()));
|
||||
d.set_end (min (d.end(), body.end()));
|
||||
d.set_end (min (d.end().earlier (ar->tail ()), body.end()));
|
||||
done.add (d);
|
||||
}
|
||||
}
|
||||
@ -285,8 +285,13 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
|
||||
assert (soffset + read_cnt <= scnt);
|
||||
samplecnt_t nread = i->region->read_at (buf + soffset, mixdown_buffer, gain_buffer, read_pos, read_cnt, chan_n);
|
||||
if (nread != read_cnt) {
|
||||
std::cerr << name() << " tried to read " << read_cnt << " from " << nread << " in " << i->region->name() << " using range "
|
||||
<< i->range.start() << " .. " << i->range.end() << " len " << i->range.length() << std::endl;
|
||||
std::cerr << name() << " tried to read " << read_cnt
|
||||
<< " got " << nread
|
||||
<< " in " << i->region->name()
|
||||
<< " for chn " << chan_n
|
||||
<< " to offset " << soffset
|
||||
<< " using range " << i->range.start().samples() << " .. " << i->range.end().samples()
|
||||
<< " len " << i->range.length().samples() << std::endl;
|
||||
#ifndef NDEBUG
|
||||
/* forward error to DiskReader::audio_read. This does 2 things:
|
||||
* - error "DiskReader %1: when refilling, cannot read ..."
|
||||
|
@ -254,6 +254,7 @@ AudioRegion::init ()
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
}
|
||||
@ -347,6 +348,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other)
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -375,6 +377,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, timecnt_t co
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -401,6 +404,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, const Source
|
||||
connect_to_header_position_offset_changed ();
|
||||
|
||||
_fx_pos = _cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
_fx_block_size = 0;
|
||||
_fx_latent_read = false;
|
||||
|
||||
@ -521,6 +525,16 @@ AudioRegion::set_fade_before_fx (bool yn)
|
||||
}
|
||||
}
|
||||
|
||||
timecnt_t
|
||||
AudioRegion::tail () const
|
||||
{
|
||||
if (_fade_before_fx && has_region_fx ()) {
|
||||
return timecnt_t (_session.sample_rate ()); // TODO use plugin API
|
||||
} else {
|
||||
return timecnt_t (0);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param buf Buffer to put peak data in.
|
||||
* @param npeaks Number of peaks to read (ie the number of PeakDatas in buf)
|
||||
* @param offset Start position, as an offset from the start of this region's source.
|
||||
@ -614,24 +628,33 @@ AudioRegion::read_at (Sample* buf,
|
||||
|
||||
/* WORK OUT WHERE TO GET DATA FROM */
|
||||
|
||||
samplecnt_t to_read;
|
||||
const samplepos_t psamples = position().samples();
|
||||
const samplecnt_t lsamples = _length.val().samples();
|
||||
const samplecnt_t tsamples = tail ().samples ();
|
||||
|
||||
assert (pos >= psamples);
|
||||
sampleoffset_t const internal_offset = pos - psamples;
|
||||
sampleoffset_t internal_offset = pos - psamples;
|
||||
sampleoffset_t suffix = 0;
|
||||
|
||||
if (internal_offset >= lsamples) {
|
||||
if (internal_offset >= lsamples + tsamples) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
if (internal_offset > lsamples) {
|
||||
suffix = internal_offset - lsamples;
|
||||
internal_offset = lsamples;
|
||||
}
|
||||
|
||||
const samplecnt_t esamples = lsamples - internal_offset;
|
||||
assert (esamples >= 0);
|
||||
|
||||
if ((to_read = min (cnt, esamples)) == 0) {
|
||||
if (min (cnt, esamples + tsamples) <= 0) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
/* does not include tail */
|
||||
samplecnt_t const to_read = max<samplecnt_t> (0, min (cnt, esamples));
|
||||
samplecnt_t const can_read = max<samplecnt_t> (0, min (cnt, esamples + tsamples));
|
||||
|
||||
/* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ
|
||||
*
|
||||
@ -699,16 +722,17 @@ AudioRegion::read_at (Sample* buf,
|
||||
Glib::Threads::Mutex::Lock cl (_cache_lock);
|
||||
if (chan_n == 0 && _invalidated.exchange (false)) {
|
||||
_cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
}
|
||||
|
||||
boost::scoped_array<gain_t> gain_array;
|
||||
boost::scoped_array<Sample> mixdown_array;
|
||||
|
||||
// TODO optimize mono reader, w/o plugins -> old code
|
||||
if (n_chn > 1 && _cache_start < _cache_end && internal_offset >= _cache_start && internal_offset + to_read <= _cache_end) {
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 copy from cache %3 - %4 to_read: %5\n",
|
||||
name(), chan_n, internal_offset, internal_offset + to_read, to_read));
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (internal_offset - _cache_start), to_read);
|
||||
if (n_chn > 1 && _cache_start < _cache_end && internal_offset + suffix >= _cache_start && internal_offset + suffix + can_read <= _cache_end) {
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 copy from cache %3 - %4 to_read: %5 can_read: %6\n",
|
||||
name(), chan_n, internal_offset + suffix, internal_offset + suffix + can_read, to_read, can_read));
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (internal_offset + suffix - _cache_start), can_read);
|
||||
cl.release ();
|
||||
} else {
|
||||
Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
|
||||
@ -716,24 +740,21 @@ AudioRegion::read_at (Sample* buf,
|
||||
uint32_t fx_latency = _fx_latency;
|
||||
lm.release ();
|
||||
|
||||
ChanCount cc (DataType::AUDIO, n_channels ());
|
||||
_readcache.ensure_buffers (cc, to_read + _fx_latency);
|
||||
|
||||
samplecnt_t n_read = to_read; //< data to read from disk
|
||||
samplecnt_t n_proc = to_read; //< silence pad data to process
|
||||
samplepos_t n_tail = 0; // further silence pad, read tail from FX
|
||||
samplepos_t readat = pos;
|
||||
sampleoffset_t offset = internal_offset;
|
||||
|
||||
//printf ("READ Cache end %ld pos %ld\n", _cache_end, readat);
|
||||
if (_cache_end != readat && fx_latency > 0) {
|
||||
if (tsamples > 0 && cnt >= esamples) {
|
||||
n_tail = can_read - n_read;
|
||||
n_proc += n_tail;
|
||||
}
|
||||
|
||||
if (_cache_end != internal_offset + suffix && fx_latency > 0) {
|
||||
_fx_latent_read = true;
|
||||
n_proc += fx_latency;
|
||||
n_read = min (to_read + fx_latency, esamples);
|
||||
|
||||
mixdown_array.reset (new Sample[n_proc]);
|
||||
mixdown_buffer = mixdown_array.get ();
|
||||
gain_array.reset (new gain_t[n_proc]);
|
||||
gain_buffer = gain_array.get ();
|
||||
}
|
||||
|
||||
if (!_fx_latent_read && fx_latency > 0) {
|
||||
@ -742,13 +763,20 @@ AudioRegion::read_at (Sample* buf,
|
||||
n_read = max<samplecnt_t> (0, min (to_read, lsamples - offset));
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read: %3 - %4 (%5) to_read: %6 offset: %7 with fx: %8 fx_latency: %9\n",
|
||||
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency));
|
||||
if (n_proc > to_read) {
|
||||
mixdown_array.reset (new Sample[n_proc]);
|
||||
mixdown_buffer = mixdown_array.get ();
|
||||
gain_array.reset (new gain_t[n_proc]);
|
||||
gain_buffer = gain_array.get ();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read: %3 - %4 (%5) to_read: %6 offset: %7 with fx: %8 fx_latency: %9 fx_tail %10\n",
|
||||
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency, n_tail));
|
||||
|
||||
ChanCount cc (DataType::AUDIO, n_channels ());
|
||||
_readcache.ensure_buffers (cc, n_proc);
|
||||
|
||||
if (n_read < n_proc) {
|
||||
//printf ("SILENCE PAD rd: %ld proc: %ld\n", n_read, n_proc);
|
||||
/* silence pad, process tail of latent effects */
|
||||
memset (&mixdown_buffer[n_read], 0, sizeof (Sample)* (n_proc - n_read));
|
||||
_readcache.silence (n_proc - n_read, n_read);
|
||||
@ -756,6 +784,7 @@ AudioRegion::read_at (Sample* buf,
|
||||
|
||||
/* reset in case read fails we return early */
|
||||
_cache_start = _cache_end = -1;
|
||||
_cache_tail = 0;
|
||||
|
||||
for (uint32_t chn = 0; chn < n_chn; ++chn) {
|
||||
/* READ DATA FROM THE SOURCE INTO mixdown_buffer.
|
||||
@ -838,26 +867,27 @@ AudioRegion::read_at (Sample* buf,
|
||||
|
||||
/* apply region FX to all channels */
|
||||
if (have_fx) {
|
||||
const_cast<AudioRegion*>(this)->apply_region_fx (_readcache, offset, offset + n_proc, n_proc);
|
||||
const_cast<AudioRegion*>(this)->apply_region_fx (_readcache, offset + suffix, offset + suffix + n_proc, n_proc);
|
||||
}
|
||||
|
||||
/* for mono regions without plugins, mixdown_buffer is valid as-is */
|
||||
if (n_chn > 1 || have_fx) {
|
||||
/* copy data for current channel */
|
||||
if (chan_n < n_channels()) {
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read);
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read + n_tail);
|
||||
} else {
|
||||
if (Config->get_replicate_missing_region_channels()) {
|
||||
chan_n = chan_n % n_channels ();
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read);
|
||||
copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (), to_read + n_tail);
|
||||
} else {
|
||||
memset (mixdown_buffer, 0, sizeof (Sample) * to_read);
|
||||
memset (mixdown_buffer, 0, sizeof (Sample) * (to_read + n_tail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_cache_start = internal_offset;
|
||||
_cache_end = internal_offset + to_read;
|
||||
_cache_start = internal_offset + suffix;
|
||||
_cache_end = internal_offset + suffix + to_read + n_tail;
|
||||
_cache_tail = n_tail;
|
||||
cl.release ();
|
||||
}
|
||||
|
||||
@ -974,8 +1004,16 @@ AudioRegion::read_at (Sample* buf,
|
||||
mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N);
|
||||
}
|
||||
}
|
||||
samplecnt_t T = _cache_tail;
|
||||
if (T > 0) {
|
||||
T = min (T, can_read);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 adding FX tail of %2 cut to_read %3 at %4 total len = %5 cnt was %6\n",
|
||||
name (), _cache_tail, T, to_read, to_read + T, cnt));
|
||||
/* AudioPlaylist::read reads regions in reverse order, so we can add the tail here */
|
||||
mix_buffers_no_gain (buf + to_read, mixdown_buffer + to_read, T);
|
||||
}
|
||||
|
||||
return to_read;
|
||||
return to_read + T;
|
||||
}
|
||||
|
||||
/** Read data directly from one of our sources, accounting for the situation when the track has a different channel
|
||||
@ -2445,6 +2483,7 @@ AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<R
|
||||
}
|
||||
|
||||
rfx->LatencyChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_latency_changed, this, false));
|
||||
rfx->plugin()->TailChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_tail_changed, this, false));
|
||||
rfx->set_block_size (_session.get_block_size ());
|
||||
|
||||
if (from_set_state) {
|
||||
@ -2463,6 +2502,8 @@ AudioRegion::_add_plugin (std::shared_ptr<RegionFxPlugin> rfx, std::shared_ptr<R
|
||||
rfx->set_default_automation (len_as_tpos ());
|
||||
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
}
|
||||
@ -2484,6 +2525,7 @@ AudioRegion::remove_plugin (std::shared_ptr<RegionFxPlugin> fx)
|
||||
|
||||
fx->drop_references ();
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
@ -2523,6 +2565,27 @@ AudioRegion::fx_latency_changed (bool no_emit)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegion::fx_tail_changed (bool no_emit)
|
||||
{
|
||||
uint32_t t = 0;
|
||||
for (auto const& rfx : _plugins) {
|
||||
t = max<uint32_t> (t, rfx->plugin()->effective_tail ());
|
||||
}
|
||||
if (t == _fx_tail) {
|
||||
return;
|
||||
}
|
||||
_fx_tail = t;
|
||||
|
||||
if (no_emit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_invalidated.exchange (true)) {
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, samplecnt_t n_samples)
|
||||
{
|
||||
|
@ -1989,16 +1989,16 @@ std::shared_ptr<RegionList>
|
||||
Playlist::regions_touched (timepos_t const & start, timepos_t const & end)
|
||||
{
|
||||
RegionReadLock rlock (this);
|
||||
return regions_touched_locked (start, end);
|
||||
return regions_touched_locked (start, end, false);
|
||||
}
|
||||
|
||||
std::shared_ptr<RegionList>
|
||||
Playlist::regions_touched_locked (timepos_t const & start, timepos_t const & end)
|
||||
Playlist::regions_touched_locked (timepos_t const & start, timepos_t const & end, bool with_tail)
|
||||
{
|
||||
std::shared_ptr<RegionList> rlist (new RegionList);
|
||||
|
||||
for (auto & r : regions) {
|
||||
if (r->coverage (start, end) != Temporal::OverlapNone) {
|
||||
if (r->coverage (start, end, with_tail) != Temporal::OverlapNone) {
|
||||
rlist->push_back (r);
|
||||
}
|
||||
}
|
||||
|
@ -296,6 +296,7 @@ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, c
|
||||
: SessionObject(s, name)
|
||||
, _type (type)
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_DEFAULT_STATE (start,length)
|
||||
, _last_length (length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -312,6 +313,7 @@ Region::Region (const SourceList& srcs)
|
||||
: SessionObject(srcs.front()->session(), "toBeRenamed")
|
||||
, _type (srcs.front()->type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_DEFAULT_STATE(_type == DataType::MIDI ? timepos_t (Temporal::Beats()) : timepos_t::from_superclock (0),
|
||||
_type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0))
|
||||
, _last_length (_type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0))
|
||||
@ -332,6 +334,7 @@ Region::Region (std::shared_ptr<const Region> other)
|
||||
: SessionObject(other->session(), other->name())
|
||||
, _type (other->data_type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -391,6 +394,7 @@ Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
|
||||
: SessionObject(other->session(), other->name())
|
||||
, _type (other->data_type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesNothing)
|
||||
@ -437,6 +441,7 @@ Region::Region (std::shared_ptr<const Region> other, const SourceList& srcs)
|
||||
: SessionObject (other->session(), other->name())
|
||||
, _type (srcs.front()->type())
|
||||
, _fx_latency (0)
|
||||
, _fx_tail (0)
|
||||
, REGION_COPY_STATE (other)
|
||||
, _last_length (other->_last_length)
|
||||
, _first_edit (EditChangesID)
|
||||
@ -1589,6 +1594,7 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
|
||||
}
|
||||
if (changed) {
|
||||
fx_latency_changed (true);
|
||||
fx_tail_changed (true);
|
||||
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
|
||||
RegionFxChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
@ -2449,3 +2455,16 @@ Region::fx_latency_changed (bool)
|
||||
}
|
||||
_fx_latency = l;
|
||||
}
|
||||
|
||||
void
|
||||
Region::fx_tail_changed (bool)
|
||||
{
|
||||
uint32_t t = 0;
|
||||
for (auto const& rfx : _plugins) {
|
||||
t = max<uint32_t> (t, rfx->plugin()->effective_tail ());
|
||||
}
|
||||
if (t == _fx_tail) {
|
||||
return;
|
||||
}
|
||||
_fx_tail = t;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user