[LV2] Buffersize, Options and Externtion-Data

Filipe Coelho falktx at gmail.com
Wed Sep 9 13:24:42 PDT 2015

On 09-09-2015 21:00, Robin Gareus wrote:
> Now for the long version:
> On 09/09/2015 05:48 PM, Filipe Coelho wrote:
> [..]
>> Note that Carla has always supported reporting buffer size changes to
>> the plugins (similar to a JACK buffersize callback)
>> This is done by using the Options interface, which is provided by the
>> plugin and called by the host.
>> Carla calls options->set() during JACK buffersize callbacks.
> Since Carla reports the current jack-period as "max", a plugin that uses
> this "max" during *instantiation* to pre-allocate buffers will crash, if
> the buffersize is later increased (jack_bufsize).

Mine don't.
Plugins will only crash if then don't implement the full spec.

Carla will call options->set() to inform of the change.

Currently Carla does not check if such thing is supported by the plugin 
or not,
that part I agree is a bug on my side of things.
I suppose I can re-instantiate the broken plugins so they don't crash. ;)

> Plugins that pre-alloccate buffers are a lot more common than plugins
> that need to know the expected "current" (temporary-maximum until
> further notice) buffersize for optimization.
> Hence requiring all plugins to implement dynamic buffer-size changes
> (re-allocate on demand, any time) is the wrong approach.

If the plugin pre-allocates a static amount it doesn't have to care.

>> I think talked with David about this, if options could be used for
>> this or not. I got the idea that yes, that was one of the actual
>> intentions for the Options extension.
> Yeah, Options extension used as extentionData. I'm all for it. Alike the
> plugin in
> https://github.com/DISTRHO/DPF/blob/master/distrho/src/DistrhoPluginLV2.cpp#L660
> does.
> Yet, for the case at hand, we need a new property:
> "nextBlockLength" or "nominalBlockLenght" or "currentBlockLength"
> and not re-use min/max.

Fine by me.

> IMHO the maxBlockLength specification should be amended:
> http://lv2plug.in/ns/ext/buf-size/#maxBlockLength
> "The maximum block length the host will ever request the plugin to
> process at once, that is, the maximum sample_count parameter that will
> ever be passed to LV2_Descriptor::run()."
> Proposal to amend:
> "This value will be valid for complete lifetime of the plugin after
> instantiation and not change dynamically."

I don't agree with this.
There might be cases where the maximum allowed by an engine can change.
For example, JACK has a maximum of 8192 but ALSA (or PulseAudio) does not.

But whatever, if we get a 'nominalBlockLength' property I most likely 
won't care about the 'max' one.

>> For example, in Ardour running JACK in realtime at 1024 buffer size the
>> maximum the engine can do (while realtime) is 1024.
>> Correct me if I'm wrong, but Ardour will never call run() on plugins
>> with more than 1024 frames, will it? (*realtime*)
> Not usually, but Ardour may indeed do so. Plugin analysis (spectrum
> display) is done on rt-data in larger chunks.

I think that's an edge case.

> Seamless loops in some host implementations may also "over-read" (finish
> current cycle, throw away data and continue at the beginning of the loop).

The host can simply process the plugins twice. (first until the end of 
the loop, then the start)
For non-LV2 formats this is required since they only know 1 time 
information per run/process call.

>> What I want to make sure we agree on, is that this "current" value we're
>> after it's the "engine realtime maximum"
> yes.
> To further clarify:  It's the "expected" value, and most of the time
> this value will also be passed as n_samples to run().
> Still, run() can be called with any other buffer-sizes occasionally (as
> long as those are between min and max).

I'd prefer if run() would be called with a bounds of min<->"expected" 
buffer sizes
But I understand that's hard to predict in some cases.

>> PS: Anyone got a suggestion for a "engine realtime maximum" property name?
> see above. I propose: "nominalBlockLength"

Yep, sound good :)

>>> Specs Clarification
>>> -------------------
> [..]
>> When the spec says:
>> "There are two facilities for passing options to an instance:
>> opts:options allows passing options at instantiation time,
>> and the opts:interface interface allows options to be dynamically set
>> and retrieved after instantiation."
>> Seems to me that options:interface and instantiate-time feature serve
>> the same purpose.
>> Giving special meaning to one of them seems wrong.
> Why provide 2 different ways for the same thing then?
> The only reason In can think of would be variable-scope when using pointers.
> The reference passed as Instantiation Option is only valid during
> instantiate. The actual value may be on the stack of the host and leave
> scope after instantiation.  This further implies that all values are
> constant during instantiate (because the value may only change in the
> instantiation thread).

But you need to de-reference the pointer to get the actual value,
if you're going to save the reference to the pointer you're doing it wrong.

The value here is an integer, I see no issues with this.

> Extension-data on the other hand is valid for the lifetime of a plugin
> and allows dynamic opts:interface get/set.
> If both facilities are indeed 100% identical, one of them should be
> deprecated.

No, one of them is used to tell the plugin at instantiate time what the 
host provides.
The other is used as a sort of callback to inform the plugins of changes.

(note that documentation even says this!)

>>> One possible solution would be to require a host to activate/deactivate
>>> plugins to dynamically set the buffersize (2,3) or to re-instantiate the
>>> plugin (1). This could be tied in with fixedBlockLength [3] without any
>>> changes to the spec, but will render some plugins useless.
>> I think for buffer size changes deactivate/activate should be required
>> in between.
>> It's just a few lines added to the spec. :)
> For the min/max buffersize I disagree. They should be instantiation
> parameters and constant.

I don't really agree, but whatever :)

> Proposal:
> "A plugins with or lv2:requiredFeature bufSiz:maxBlockLength MUST be
> re-instantiated if the max blocksize changes."
> "If the nominalBuffersize changes a host may deactivate/activate the
> plugin but is not required to do so."
> Since get()/set() must not be called concurrently with run(), not
> re-activating the plugin is fine.
> This depends on the host/engine. To explain: some audio-systems announce
> buffer-size changes asynchronously without interrupting the actual
> audio-processing (sliding buffers). The LV2-host must in this case
> assure synchronous threading and prevent calling run until the plugin is
> informed (or re-instantiated).

This sounds good to me, but better have other opinions here too.

>> Imagine a plugin that requires fixedBlockLength
>> When the buffer size changes via JACK (or something else) the plugin
>> will no longer work.
>> The host has to either inform the plugin about the change or
>> re-instantiate it.
> correct.
> If the /new/ nominalBufferLength (here jack buffer-size) is below
> bufSiz:maxBlockLength just informing the plugin about the change is fine.
> Most plugins will just see n_samples change in run(.., n_samples),
> Plugins with an opts:interface can be informed out-of-band.

Sounds good too.

Now can we get the "benevolent dictator" to actually dictate something 
please? :)

More information about the Devel mailing list