diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index c2de836097..07a6e99dec 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -171,6 +171,8 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable bool remove_plugin (std::shared_ptr); void reorder_plugins (RegionFxList const&); + timecnt_t tail () const; + /* automation */ std::shared_ptr @@ -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); 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 _invalidated; protected: diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 8405fd197c..4533a8336c 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -402,7 +402,7 @@ protected: void _set_sort_id (); - std::shared_ptr regions_touched_locked (timepos_t const & start, timepos_t const & end); + std::shared_ptr regions_touched_locked (timepos_t const & start, timepos_t const & end, bool with_tail); void notify_region_removed (std::shared_ptr); void notify_region_added (std::shared_ptr); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index a66dc036f0..ea842a3f40 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -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; @@ -564,6 +566,7 @@ protected: protected: virtual bool _add_plugin (std::shared_ptr, std::shared_ptr, 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 _sync_marked; diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index d751152d4c..67b391c384 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -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 all = regions_touched_locked (start, start + cnt); + std::shared_ptr 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 ..." diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index ab7bf64766..4ef78b659e 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -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 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 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 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 (0, min (cnt, esamples)); + samplecnt_t const can_read = max (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_array; boost::scoped_array 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 (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(this)->apply_region_fx (_readcache, offset, offset + n_proc, n_proc); + const_cast(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 rfx, std::shared_ptrLatencyChanged.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 rfx, std::shared_ptrset_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 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 (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) { diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 5389d30cb7..4b78a677d6 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -1989,16 +1989,16 @@ std::shared_ptr 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 -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 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); } } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 6393eac02e..ec982c1529 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -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 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 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 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 (t, rfx->plugin()->effective_tail ()); + } + if (t == _fx_tail) { + return; + } + _fx_tail = t; +}