outline a "NoSampleAccurateControl" LV2 feature:
authorRobin Gareus <robin@gareus.org>
Sun, 6 Dec 2015 12:49:16 +0000 (13:49 +0100)
committerRobin Gareus <robin@gareus.org>
Sun, 6 Dec 2015 13:27:24 +0000 (14:27 +0100)
Since control-ports have a fixed value for the current process-block,
Ardour splits a plugin's run() process cycle on every automation event
to facilitate sample-accurate automation.

Since automation is interpolated between events, this ensures that each
explicit automation point is reached (not interpolated).

Plugins where this is not required and which favor a fixed block-size,
can now specify an optional Feature: NoSampleAccurateControl.

One example: a convolution plugin with smoothed gain control.

libs/ardour/ardour/lv2_plugin.h
libs/ardour/lv2_plugin.cc

index fcbf8b90cc84ef7be0ce4306194f2753efbd1914..a0572b5af73c11846721452c9ae50424c56eef37 100644 (file)
@@ -97,6 +97,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        void cleanup ();
 
        int set_block_size (pframes_t);
+       bool requires_fixed_sized_buffers () const;
 
        int connect_and_run (BufferSet& bufs,
                             ChanMapping in, ChanMapping out,
@@ -178,6 +179,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        uint32_t      _patch_port_in_index;
        uint32_t      _patch_port_out_index;
        URIMap&       _uri_map;
+       bool          _no_sample_accurate_ctrl;
 
        friend const void* lv2plugin_get_port_value(const char* port_symbol,
                                                    void*       user_data,
index 47e1053a2cd36aa55f39d9d0329b0e16e9f49811..940ec8ae0389ac49a47057a89da98f5550fb2f0a 100644 (file)
@@ -155,6 +155,7 @@ public:
        LilvNode* units_midiNote;
        LilvNode* patch_writable;
        LilvNode* patch_Message;
+       LilvNode* lv2_noSampleAccurateCtrl;
 #ifdef HAVE_LV2_1_2_0
        LilvNode* bufz_powerOf2BlockLength;
        LilvNode* bufz_fixedBlockLength;
@@ -286,6 +287,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
        , _uri_map(URIMap::instance())
+       , _no_sample_accurate_ctrl (false)
 {
        init(c_plugin, rate);
 }
@@ -300,6 +302,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
        , _uri_map(URIMap::instance())
+       , _no_sample_accurate_ctrl (false)
 {
        init(other._impl->plugin, other._sample_rate);
 
@@ -481,6 +484,12 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                throw failed_constructor();
        }
        lilv_nodes_free(required_features);
+
+       LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin);
+       if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) {
+               _no_sample_accurate_ctrl = true;
+       }
+       lilv_nodes_free(optional_features);
 #endif
 
 #ifdef HAVE_LILV_0_16_0
@@ -685,6 +694,25 @@ LV2Plugin::set_block_size (pframes_t nframes)
        return 0;
 }
 
+bool
+LV2Plugin::requires_fixed_sized_buffers () const
+{
+       /* This controls if Ardour will split the plugin's run()
+        * on automation events in order to pass sample-accurate automation
+        * via standard control-ports.
+        *
+        * When returning true Ardour will *not* sub-divide the process-cycle.
+        * Automation events that happen between cycle-start and cycle-end will be
+        * ignored (ctrl values are interpolated to cycle-start).
+        * NB. Atom Sequences are still sample accurate.
+        *
+        * Note: This does not guarantee a fixed block-size.
+        * e.g The process cycle may be split when looping, also
+        * the period-size may change any time: see set_block_size()
+        */
+       return _no_sample_accurate_ctrl;
+}
+
 LV2Plugin::~LV2Plugin ()
 {
        DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name()));
@@ -2448,6 +2476,7 @@ LV2World::LV2World()
        units_db           = lilv_new_uri(world, LV2_UNITS__db);
        patch_writable     = lilv_new_uri(world, LV2_PATCH__writable);
        patch_Message      = lilv_new_uri(world, LV2_PATCH__Message);
+       lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "NoSampleAccurateCtrl"); // XXX
 #ifdef HAVE_LV2_1_2_0
        bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
        bufz_fixedBlockLength    = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
@@ -2463,6 +2492,7 @@ LV2World::~LV2World()
        lilv_node_free(bufz_fixedBlockLength);
        lilv_node_free(bufz_powerOf2BlockLength);
 #endif
+       lilv_node_free(lv2_noSampleAccurateCtrl);
        lilv_node_free(patch_Message);
        lilv_node_free(patch_writable);
        lilv_node_free(units_hz);