RegionFX: include plugin tail with pre-fade Fx

This commit is contained in:
Robin Gareus 2024-08-14 04:13:25 +02:00
parent c2169d6d51
commit c16e31012b
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
7 changed files with 136 additions and 41 deletions

View file

@ -171,6 +171,8 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
bool remove_plugin (std::shared_ptr<RegionFxPlugin>); bool remove_plugin (std::shared_ptr<RegionFxPlugin>);
void reorder_plugins (RegionFxList const&); void reorder_plugins (RegionFxList const&);
timecnt_t tail () const;
/* automation */ /* automation */
std::shared_ptr<Evoral::Control> 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 apply_region_fx (BufferSet&, samplepos_t, samplepos_t, samplecnt_t);
void fx_latency_changed (bool no_emit); void fx_latency_changed (bool no_emit);
void fx_tail_changed (bool no_emit);
void copy_plugin_state (std::shared_ptr<const AudioRegion>); void copy_plugin_state (std::shared_ptr<const AudioRegion>);
mutable samplepos_t _fx_pos; mutable samplepos_t _fx_pos;
@ -274,6 +277,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
mutable BufferSet _readcache; mutable BufferSet _readcache;
mutable samplepos_t _cache_start; mutable samplepos_t _cache_start;
mutable samplepos_t _cache_end; mutable samplepos_t _cache_end;
mutable samplecnt_t _cache_tail;
mutable std::atomic<bool> _invalidated; mutable std::atomic<bool> _invalidated;
protected: protected:

View file

@ -402,7 +402,7 @@ protected:
void _set_sort_id (); 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_removed (std::shared_ptr<Region>);
void notify_region_added (std::shared_ptr<Region>); void notify_region_added (std::shared_ptr<Region>);

View file

@ -145,6 +145,8 @@ public:
timepos_t end() const; timepos_t end() const;
timepos_t nt_last() const { return end().decrement(); } timepos_t nt_last() const { return end().decrement(); }
virtual timecnt_t tail () const { return timecnt_t (0); }
timepos_t source_position () const; timepos_t source_position () const;
timecnt_t source_relative_position (Temporal::timepos_t const &) const; timecnt_t source_relative_position (Temporal::timepos_t const &) const;
timecnt_t region_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. * OverlapEnd: the range overlaps the end of this region.
* OverlapExternal: the range overlaps all of this region. * OverlapExternal: the range overlaps all of this region.
*/ */
Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end) const { Temporal::OverlapType coverage (timepos_t const & start, timepos_t const & end, bool with_tail = false) const {
return Temporal::coverage_exclusive_ends (position(), nt_last(), start, end); return Temporal::coverage_exclusive_ends (position(), with_tail ? nt_last() + tail() : nt_last(), start, end);
} }
bool exact_equivalent (std::shared_ptr<const Region>) const; bool exact_equivalent (std::shared_ptr<const Region>) const;
@ -564,6 +566,7 @@ protected:
protected: protected:
virtual bool _add_plugin (std::shared_ptr<RegionFxPlugin>, std::shared_ptr<RegionFxPlugin>, bool) { return false; } 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_latency_changed (bool no_emit);
virtual void fx_tail_changed (bool no_emit);
virtual void send_change (const PBD::PropertyChange&); virtual void send_change (const PBD::PropertyChange&);
virtual int _set_state (const XMLNode&, int version, PBD::PropertyChange& what_changed, bool send_signal); 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; mutable Glib::Threads::RWLock _fx_lock;
uint32_t _fx_latency; uint32_t _fx_latency;
uint32_t _fx_tail;
RegionFxList _plugins; RegionFxList _plugins;
PBD::Property<bool> _sync_marked; PBD::Property<bool> _sync_marked;

View file

@ -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) 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", 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 ()); 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, /* Find all the regions that are involved in the bit we are reading,
and sort them by descending layer and ascending position. 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 ()); all->sort (ReadSorter ());
/* This will be a list of the bits of our read range that we have /* 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 rrange = ar->range_samples ();
Temporal::Range region_range (max (rrange.start(), start), 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 */ /* ... 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 */ /* Cut this range down to just the body and mark it done */
Temporal::Range body = ar->body_range (); 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_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); done.add (d);
} }
} }
@ -285,8 +285,13 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ti
assert (soffset + read_cnt <= scnt); assert (soffset + read_cnt <= scnt);
samplecnt_t nread = i->region->read_at (buf + soffset, mixdown_buffer, gain_buffer, read_pos, read_cnt, chan_n); samplecnt_t nread = i->region->read_at (buf + soffset, mixdown_buffer, gain_buffer, read_pos, read_cnt, chan_n);
if (nread != read_cnt) { if (nread != read_cnt) {
std::cerr << name() << " tried to read " << read_cnt << " from " << nread << " in " << i->region->name() << " using range " std::cerr << name() << " tried to read " << read_cnt
<< i->range.start() << " .. " << i->range.end() << " len " << i->range.length() << std::endl; << " 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 #ifndef NDEBUG
/* forward error to DiskReader::audio_read. This does 2 things: /* forward error to DiskReader::audio_read. This does 2 things:
* - error "DiskReader %1: when refilling, cannot read ..." * - error "DiskReader %1: when refilling, cannot read ..."

View file

@ -254,6 +254,7 @@ AudioRegion::init ()
connect_to_header_position_offset_changed (); connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1; _fx_pos = _cache_start = _cache_end = -1;
_cache_tail = 0;
_fx_block_size = 0; _fx_block_size = 0;
_fx_latent_read = false; _fx_latent_read = false;
} }
@ -347,6 +348,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other)
connect_to_header_position_offset_changed (); connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1; _fx_pos = _cache_start = _cache_end = -1;
_cache_tail = 0;
_fx_block_size = 0; _fx_block_size = 0;
_fx_latent_read = false; _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 (); connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1; _fx_pos = _cache_start = _cache_end = -1;
_cache_tail = 0;
_fx_block_size = 0; _fx_block_size = 0;
_fx_latent_read = false; _fx_latent_read = false;
@ -401,6 +404,7 @@ AudioRegion::AudioRegion (std::shared_ptr<const AudioRegion> other, const Source
connect_to_header_position_offset_changed (); connect_to_header_position_offset_changed ();
_fx_pos = _cache_start = _cache_end = -1; _fx_pos = _cache_start = _cache_end = -1;
_cache_tail = 0;
_fx_block_size = 0; _fx_block_size = 0;
_fx_latent_read = false; _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 buf Buffer to put peak data in.
* @param npeaks Number of peaks to read (ie the number of PeakDatas in buf) * @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. * @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 */ /* WORK OUT WHERE TO GET DATA FROM */
samplecnt_t to_read;
const samplepos_t psamples = position().samples(); const samplepos_t psamples = position().samples();
const samplecnt_t lsamples = _length.val().samples(); const samplecnt_t lsamples = _length.val().samples();
const samplecnt_t tsamples = tail ().samples ();
assert (pos >= psamples); 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 */ return 0; /* read nothing */
} }
if (internal_offset > lsamples) {
suffix = internal_offset - lsamples;
internal_offset = lsamples;
}
const samplecnt_t esamples = lsamples - internal_offset; const samplecnt_t esamples = lsamples - internal_offset;
assert (esamples >= 0); assert (esamples >= 0);
if ((to_read = min (cnt, esamples)) == 0) { if (min (cnt, esamples + tsamples) <= 0) {
return 0; /* read nothing */ 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 /* 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); Glib::Threads::Mutex::Lock cl (_cache_lock);
if (chan_n == 0 && _invalidated.exchange (false)) { if (chan_n == 0 && _invalidated.exchange (false)) {
_cache_start = _cache_end = -1; _cache_start = _cache_end = -1;
_cache_tail = 0;
} }
boost::scoped_array<gain_t> gain_array; boost::scoped_array<gain_t> gain_array;
boost::scoped_array<Sample> mixdown_array; boost::scoped_array<Sample> mixdown_array;
// TODO optimize mono reader, w/o plugins -> old code // 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) { 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\n", 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, internal_offset + to_read, to_read)); 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 - _cache_start), to_read); copy_vector (mixdown_buffer, _readcache.get_audio (chan_n).data (internal_offset + suffix - _cache_start), can_read);
cl.release (); cl.release ();
} else { } else {
Glib::Threads::RWLock::ReaderLock lm (_fx_lock); Glib::Threads::RWLock::ReaderLock lm (_fx_lock);
@ -716,24 +740,21 @@ AudioRegion::read_at (Sample* buf,
uint32_t fx_latency = _fx_latency; uint32_t fx_latency = _fx_latency;
lm.release (); 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_read = to_read; //< data to read from disk
samplecnt_t n_proc = to_read; //< silence pad data to process 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; samplepos_t readat = pos;
sampleoffset_t offset = internal_offset; sampleoffset_t offset = internal_offset;
//printf ("READ Cache end %ld pos %ld\n", _cache_end, readat); if (tsamples > 0 && cnt >= esamples) {
if (_cache_end != readat && fx_latency > 0) { n_tail = can_read - n_read;
n_proc += n_tail;
}
if (_cache_end != internal_offset + suffix && fx_latency > 0) {
_fx_latent_read = true; _fx_latent_read = true;
n_proc += fx_latency; n_proc += fx_latency;
n_read = min (to_read + fx_latency, esamples); 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) { 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)); 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", if (n_proc > to_read) {
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency)); 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); _readcache.ensure_buffers (cc, n_proc);
if (n_read < 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 */ /* silence pad, process tail of latent effects */
memset (&mixdown_buffer[n_read], 0, sizeof (Sample)* (n_proc - n_read)); memset (&mixdown_buffer[n_read], 0, sizeof (Sample)* (n_proc - n_read));
_readcache.silence (n_proc - n_read, 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 */ /* reset in case read fails we return early */
_cache_start = _cache_end = -1; _cache_start = _cache_end = -1;
_cache_tail = 0;
for (uint32_t chn = 0; chn < n_chn; ++chn) { for (uint32_t chn = 0; chn < n_chn; ++chn) {
/* READ DATA FROM THE SOURCE INTO mixdown_buffer. /* READ DATA FROM THE SOURCE INTO mixdown_buffer.
@ -838,26 +867,27 @@ AudioRegion::read_at (Sample* buf,
/* apply region FX to all channels */ /* apply region FX to all channels */
if (have_fx) { 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 */ /* for mono regions without plugins, mixdown_buffer is valid as-is */
if (n_chn > 1 || have_fx) { if (n_chn > 1 || have_fx) {
/* copy data for current channel */ /* copy data for current channel */
if (chan_n < n_channels()) { 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 { } else {
if (Config->get_replicate_missing_region_channels()) { if (Config->get_replicate_missing_region_channels()) {
chan_n = chan_n % n_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 { } else {
memset (mixdown_buffer, 0, sizeof (Sample) * to_read); memset (mixdown_buffer, 0, sizeof (Sample) * (to_read + n_tail));
} }
} }
} }
_cache_start = internal_offset; _cache_start = internal_offset + suffix;
_cache_end = internal_offset + to_read; _cache_end = internal_offset + suffix + to_read + n_tail;
_cache_tail = n_tail;
cl.release (); 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); 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 /** 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->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 ()); rfx->set_block_size (_session.get_block_size ());
if (from_set_state) { 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 ()); rfx->set_default_automation (len_as_tpos ());
fx_latency_changed (true); fx_latency_changed (true);
fx_tail_changed (true);
if (!_invalidated.exchange (true)) { if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite 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->drop_references ();
fx_latency_changed (true); fx_latency_changed (true);
fx_tail_changed (true);
if (!_invalidated.exchange (true)) { if (!_invalidated.exchange (true)) {
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite 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 void
AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, samplecnt_t n_samples) AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, samplecnt_t n_samples)
{ {

View file

@ -1989,16 +1989,16 @@ std::shared_ptr<RegionList>
Playlist::regions_touched (timepos_t const & start, timepos_t const & end) Playlist::regions_touched (timepos_t const & start, timepos_t const & end)
{ {
RegionReadLock rlock (this); RegionReadLock rlock (this);
return regions_touched_locked (start, end); return regions_touched_locked (start, end, false);
} }
std::shared_ptr<RegionList> 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); std::shared_ptr<RegionList> rlist (new RegionList);
for (auto & r : regions) { for (auto & r : regions) {
if (r->coverage (start, end) != Temporal::OverlapNone) { if (r->coverage (start, end, with_tail) != Temporal::OverlapNone) {
rlist->push_back (r); rlist->push_back (r);
} }
} }

View file

@ -296,6 +296,7 @@ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, c
: SessionObject(s, name) : SessionObject(s, name)
, _type (type) , _type (type)
, _fx_latency (0) , _fx_latency (0)
, _fx_tail (0)
, REGION_DEFAULT_STATE (start,length) , REGION_DEFAULT_STATE (start,length)
, _last_length (length) , _last_length (length)
, _first_edit (EditChangesNothing) , _first_edit (EditChangesNothing)
@ -312,6 +313,7 @@ Region::Region (const SourceList& srcs)
: SessionObject(srcs.front()->session(), "toBeRenamed") : SessionObject(srcs.front()->session(), "toBeRenamed")
, _type (srcs.front()->type()) , _type (srcs.front()->type())
, _fx_latency (0) , _fx_latency (0)
, _fx_tail (0)
, REGION_DEFAULT_STATE(_type == DataType::MIDI ? timepos_t (Temporal::Beats()) : timepos_t::from_superclock (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)) _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)) , _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()) : SessionObject(other->session(), other->name())
, _type (other->data_type()) , _type (other->data_type())
, _fx_latency (0) , _fx_latency (0)
, _fx_tail (0)
, REGION_COPY_STATE (other) , REGION_COPY_STATE (other)
, _last_length (other->_last_length) , _last_length (other->_last_length)
, _first_edit (EditChangesNothing) , _first_edit (EditChangesNothing)
@ -391,6 +394,7 @@ Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
: SessionObject(other->session(), other->name()) : SessionObject(other->session(), other->name())
, _type (other->data_type()) , _type (other->data_type())
, _fx_latency (0) , _fx_latency (0)
, _fx_tail (0)
, REGION_COPY_STATE (other) , REGION_COPY_STATE (other)
, _last_length (other->_last_length) , _last_length (other->_last_length)
, _first_edit (EditChangesNothing) , _first_edit (EditChangesNothing)
@ -437,6 +441,7 @@ Region::Region (std::shared_ptr<const Region> other, const SourceList& srcs)
: SessionObject (other->session(), other->name()) : SessionObject (other->session(), other->name())
, _type (srcs.front()->type()) , _type (srcs.front()->type())
, _fx_latency (0) , _fx_latency (0)
, _fx_tail (0)
, REGION_COPY_STATE (other) , REGION_COPY_STATE (other)
, _last_length (other->_last_length) , _last_length (other->_last_length)
, _first_edit (EditChangesID) , _first_edit (EditChangesID)
@ -1589,6 +1594,7 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
} }
if (changed) { if (changed) {
fx_latency_changed (true); fx_latency_changed (true);
fx_tail_changed (true);
send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite
RegionFxChanged (); /* EMIT SIGNAL */ RegionFxChanged (); /* EMIT SIGNAL */
} }
@ -2449,3 +2455,16 @@ Region::fx_latency_changed (bool)
} }
_fx_latency = l; _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;
}