Add proper API to expose plugin tailtime

This is in preparation to display current tail time,
and allow a user to override it.

This is similar to existing processor latency API.
This commit is contained in:
Robin Gareus 2024-08-30 21:21:42 +02:00
parent 79fcb3d0ba
commit 520bbfe515
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
11 changed files with 191 additions and 30 deletions

View file

@ -40,6 +40,7 @@
#include "ardour/midi_ring_buffer.h" #include "ardour/midi_ring_buffer.h"
#include "ardour/midi_state_tracker.h" #include "ardour/midi_state_tracker.h"
#include "ardour/parameter_descriptor.h" #include "ardour/parameter_descriptor.h"
#include "ardour/tailtime.h"
#include "ardour/types.h" #include "ardour/types.h"
#include "ardour/variant.h" #include "ardour/variant.h"
@ -74,7 +75,7 @@ typedef std::set<uint32_t> PluginOutputConfiguration;
* *
* Plugins are not used directly in Ardour but always wrapped by a PluginInsert. * Plugins are not used directly in Ardour but always wrapped by a PluginInsert.
*/ */
class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public HasLatency class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public HasLatency, public HasTailTime
{ {
public: public:
Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&); Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&);
@ -172,12 +173,14 @@ public:
return plugin_latency (); return plugin_latency ();
} }
samplecnt_t signal_tailtime () const
{
return plugin_tailtime ();
}
/** the max possible latency a plugin will have */ /** the max possible latency a plugin will have */
virtual samplecnt_t max_latency () const { return 0; } virtual samplecnt_t max_latency () const { return 0; }
samplecnt_t effective_tail() const;
PBD::Signal0<void> TailChanged;
virtual int set_block_size (pframes_t nframes) = 0; virtual int set_block_size (pframes_t nframes) = 0;
virtual bool requires_fixed_sized_buffers () const { return false; } virtual bool requires_fixed_sized_buffers () const { return false; }
virtual bool inplace_broken () const { return false; } virtual bool inplace_broken () const { return false; }
@ -429,7 +432,7 @@ private:
/** tail duration in samples. e.g. for reverb or delay plugins. /** tail duration in samples. e.g. for reverb or delay plugins.
* *
* The default when unknown is 2 sec */ * The default when unknown is 2 sec */
virtual samplecnt_t plugin_tail () const; virtual samplecnt_t plugin_tailtime () const;
/** Fill _presets with our presets */ /** Fill _presets with our presets */

View file

@ -43,7 +43,7 @@ namespace ARDOUR
{ {
class ReadOnlyControl; class ReadOnlyControl;
class LIBARDOUR_API RegionFxPlugin : public SessionObject, public PlugInsertBase, public Latent, public Temporal::TimeDomainProvider class LIBARDOUR_API RegionFxPlugin : public SessionObject, public PlugInsertBase, public Latent, public TailTime, public Temporal::TimeDomainProvider
{ {
public: public:
RegionFxPlugin (Session&, Temporal::TimeDomain const, std::shared_ptr<Plugin> = std::shared_ptr<Plugin> ()); RegionFxPlugin (Session&, Temporal::TimeDomain const, std::shared_ptr<Plugin> = std::shared_ptr<Plugin> ());
@ -61,6 +61,8 @@ public:
/* Latent */ /* Latent */
samplecnt_t signal_latency () const; samplecnt_t signal_latency () const;
/* TailTime */
samplecnt_t signal_tailtime () const;
/* PlugInsertBase */ /* PlugInsertBase */
uint32_t get_count () const uint32_t get_count () const
@ -153,10 +155,6 @@ public:
return _required_buffers; return _required_buffers;
} }
/* wrapped Plugin API */
PBD::Signal0<void> TailChanged;
samplecnt_t effective_tail () const;
private: private:
/* disallow copy construction */ /* disallow copy construction */
RegionFxPlugin (RegionFxPlugin const&); RegionFxPlugin (RegionFxPlugin const&);
@ -178,7 +176,8 @@ private:
/** details of the match currently being used */ /** details of the match currently being used */
Match _match; Match _match;
uint32_t _plugin_signal_latency; samplecnt_t _plugin_signal_latency;
samplecnt_t _plugin_signal_tailtime;
typedef std::vector<std::shared_ptr<Plugin>> Plugins; typedef std::vector<std::shared_ptr<Plugin>> Plugins;
Plugins _plugins; Plugins _plugins;

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __ardour_tailtime_h__
#define __ardour_tailtime_h__
#include "pbd/signals.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"
namespace ARDOUR {
class LIBARDOUR_API HasTailTime {
public:
virtual ~HasTailTime () {}
virtual samplecnt_t signal_tailtime () const = 0;
};
class LIBARDOUR_API TailTime : public HasTailTime {
public:
TailTime ();
TailTime (TailTime const&);
virtual ~TailTime() {}
samplecnt_t effective_tailtime () const;
samplecnt_t user_latency () const {
if (_use_user_tailtime) {
return _user_tailtime;
} else {
return 0;
}
}
void unset_user_tailtime ();
void set_user_tailtime (samplecnt_t val);
PBD::Signal0<void> TailTimeChanged;
protected:
int set_state (const XMLNode& node, int version);
void add_state (XMLNode*) const;
private:
samplecnt_t _use_user_tailtime;
samplecnt_t _user_tailtime;
};
} /* namespace */
#endif /* __ardour_tailtime_h__*/

View file

@ -182,7 +182,7 @@ public:
/* API for Ardour -- Setup/Processing */ /* API for Ardour -- Setup/Processing */
uint32_t plugin_latency (); uint32_t plugin_latency ();
uint32_t plugin_tail (); uint32_t plugin_tailtime ();
bool set_block_size (int32_t); bool set_block_size (int32_t);
bool activate (); bool activate ();
bool deactivate (); bool deactivate ();
@ -444,7 +444,7 @@ public:
private: private:
samplecnt_t plugin_latency () const; samplecnt_t plugin_latency () const;
samplecnt_t plugin_tail () const; samplecnt_t plugin_tailtime () const;
void init (); void init ();
void find_presets (); void find_presets ();
void forward_resize_view (int w, int h); void forward_resize_view (int w, int h);

View file

@ -2488,7 +2488,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->TailChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fx_tail_changed, this, false)); rfx->TailTimeChanged.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) {
@ -2576,7 +2576,7 @@ AudioRegion::fx_tail_changed (bool no_emit)
{ {
uint32_t t = 0; uint32_t t = 0;
for (auto const& rfx : _plugins) { for (auto const& rfx : _plugins) {
t = max<uint32_t> (t, rfx->effective_tail ()); t = max<uint32_t> (t, rfx->effective_tailtime ());
} }
if (t == _fx_tail) { if (t == _fx_tail) {
return; return;

View file

@ -318,14 +318,7 @@ Plugin::input_streams () const
} }
samplecnt_t samplecnt_t
Plugin::effective_tail () const Plugin::plugin_tailtime () const
{
/* consider adding a user-override per plugin; compare to HasLatency, Latent */
return max<samplecnt_t> (0, min<samplecnt_t> (plugin_tail (), Config->get_max_tail_samples ()));
}
samplecnt_t
Plugin::plugin_tail () const
{ {
return _session.sample_rate () * Config->get_tail_duration_sec (); return _session.sample_rate () * Config->get_tail_duration_sec ();
} }

View file

@ -2475,7 +2475,7 @@ Region::fx_tail_changed (bool)
{ {
uint32_t t = 0; uint32_t t = 0;
for (auto const& rfx : _plugins) { for (auto const& rfx : _plugins) {
t = max<uint32_t> (t, rfx->plugin()->effective_tail ()); t = max<uint32_t> (t, rfx->effective_tailtime ());
} }
if (t == _fx_tail) { if (t == _fx_tail) {
return; return;

View file

@ -250,6 +250,7 @@ RegionFxPlugin::get_state () const
XMLNode* node = new XMLNode (/*state_node_name*/ "RegionFXPlugin"); XMLNode* node = new XMLNode (/*state_node_name*/ "RegionFXPlugin");
Latent::add_state (node); Latent::add_state (node);
TailTime::add_state (node);
node->set_property ("type", _plugins[0]->state_node_name ()); node->set_property ("type", _plugins[0]->state_node_name ());
node->set_property ("unique-id", _plugins[0]->unique_id ()); node->set_property ("unique-id", _plugins[0]->unique_id ());
@ -383,6 +384,10 @@ RegionFxPlugin::set_state (const XMLNode& node, int version)
ac->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ ac->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
} }
} }
Latent::set_state (node, version);
TailTime::set_state (node, version);
return 0; return 0;
} }
@ -422,7 +427,6 @@ RegionFxPlugin::add_plugin (std::shared_ptr<Plugin> plugin)
plugin->ParameterChangedExternally.connect_same_thread (*this, boost::bind (&RegionFxPlugin::parameter_changed_externally, this, _1, _2)); plugin->ParameterChangedExternally.connect_same_thread (*this, boost::bind (&RegionFxPlugin::parameter_changed_externally, this, _1, _2));
plugin->StartTouch.connect_same_thread (*this, boost::bind (&RegionFxPlugin::start_touch, this, _1)); plugin->StartTouch.connect_same_thread (*this, boost::bind (&RegionFxPlugin::start_touch, this, _1));
plugin->EndTouch.connect_same_thread (*this, boost::bind (&RegionFxPlugin::end_touch, this, _1)); plugin->EndTouch.connect_same_thread (*this, boost::bind (&RegionFxPlugin::end_touch, this, _1));
plugin->TailChanged.connect_same_thread (*this, [this](){ TailChanged (); });
} }
plugin->set_insert (this, _plugins.size ()); plugin->set_insert (this, _plugins.size ());
@ -503,12 +507,12 @@ RegionFxPlugin::signal_latency () const
} }
ARDOUR::samplecnt_t ARDOUR::samplecnt_t
RegionFxPlugin::effective_tail () const RegionFxPlugin::signal_tailtime () const
{ {
if (_plugins.empty ()) { if (_plugins.empty ()) {
return 0; return 0;
} }
return _plugins.front ()->effective_tail (); return _plugins.front ()->signal_tailtime ();
} }
PlugInsertBase::UIElements PlugInsertBase::UIElements
@ -1440,6 +1444,11 @@ RegionFxPlugin::connect_and_run (BufferSet& bufs, samplepos_t start, samplepos_t
_plugin_signal_latency= l; _plugin_signal_latency= l;
LatencyChanged (); /* EMIT SIGNAL */ LatencyChanged (); /* EMIT SIGNAL */
} }
const samplecnt_t t = effective_latency ();
if (_plugin_signal_tailtime != l) {
_plugin_signal_tailtime = t;
TailTimeChanged (); /* EMIT SIGNAL */
}
return true; return true;
} }

87
libs/ardour/tailtime.cc Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pbd/xml++.h"
#include "ardour/tailtime.h"
#include "ardour/rc_configuration.h"
using namespace ARDOUR;
TailTime::TailTime ()
: HasTailTime ()
, _use_user_tailtime (false)
, _user_tailtime (0)
{}
TailTime::TailTime (TailTime const& other)
: HasTailTime ()
, _use_user_tailtime (other._use_user_tailtime)
, _user_tailtime (other._user_tailtime)
{}
samplecnt_t
TailTime::effective_tailtime () const
{
if (_use_user_tailtime) {
return _user_tailtime;
} else {
return std::max<samplecnt_t> (0, std::min<samplecnt_t> (signal_tailtime (), Config->get_max_tail_samples ()));
}
}
void
TailTime::set_user_tailtime (samplecnt_t val)
{
if (_use_user_tailtime && _user_tailtime == val) {
return;
}
_use_user_tailtime = true;
_user_tailtime = val;
TailTimeChanged (); /* EMIT SIGNAL */
}
void
TailTime::unset_user_tailtime ()
{
if (!_use_user_tailtime) {
return;
}
_use_user_tailtime = false;
_user_tailtime = 0;
TailTimeChanged (); /* EMIT SIGNAL */
}
int
TailTime::set_state (const XMLNode& node, int version)
{
node.get_property ("user-tailtime", _user_tailtime);
if (!node.get_property ("use-user-tailtime", _use_user_tailtime)) {
_use_user_tailtime = _user_tailtime > 0;
}
return 0;
}
void
TailTime::add_state (XMLNode* node) const
{
node->set_property ("user-tailtime", _user_tailtime);
node->set_property ("use-user-tailtime", _use_user_tailtime);
}

View file

@ -678,9 +678,9 @@ VST3Plugin::set_block_size (pframes_t n_samples)
} }
samplecnt_t samplecnt_t
VST3Plugin::plugin_tail () const VST3Plugin::plugin_tailtime () const
{ {
return _plug->plugin_tail (); return _plug->plugin_tailtime ();
} }
samplecnt_t samplecnt_t
@ -1816,7 +1816,7 @@ VST3PI::plugin_latency ()
} }
uint32_t uint32_t
VST3PI::plugin_tail () VST3PI::plugin_tailtime ()
{ {
if (!_plugin_tail) { // XXX this is currently never reset if (!_plugin_tail) { // XXX this is currently never reset
_plugin_tail = _processor->getTailSamples (); _plugin_tail = _processor->getTailSamples ();

View file

@ -256,6 +256,7 @@ libardour_sources = [
'system_exec.cc', 'system_exec.cc',
'revision.cc', 'revision.cc',
'rt_midibuffer.cc', 'rt_midibuffer.cc',
'tailtime.cc',
'template_utils.cc', 'template_utils.cc',
'tempo_map_importer.cc', 'tempo_map_importer.cc',
'thawlist.cc', 'thawlist.cc',