Re: [linux-audio-dev] changing control port values with LADSPA: a serious issue?

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: Re: [linux-audio-dev] changing control port values with LADSPA: a serious issue?
From: David Olofson (david_AT_gardena.net)
Date: Sat Mar 16 2002 - 23:35:46 EET


On Saturday 16 March 2002 13.59, Paul Davis wrote:
[...]
> (1) might work if we made it part of the semantics of how plugins
> are supposed to operate. Fortunately at this time there are only 3
> sets of plugins (CMT, swh and the guy that wrote a suite of them
> including recorders, ALSA/OSS plugins etc). We could change this
> and it wouldn't be impossible for the change to take place. On the
> other hand, its not a very nice thing to do, it puts a great burden
> on plugin authors,

IMHO, any plugins for serious use should interpolate all control
ports that are sensitive to transients (which means practically all
of them). This is of course provided the control ports are really
meant to be changed during processing - but I guess we can just
assume that that is the idea. (Or we'd have to differentiate between
real time control ports and init time control ports... Probably a
nice feature, put not for LADSPA, I guess.)

Anyway, my point is that I'm not sure it would actually make a big
difference, as something like this is required anyway, to achieve
seriously usable sound quality.

> and it prevents the host from controlling the
> shape of the parameter changes.

"Approximation" is probably the keyword here. The only way a host
could have *totaly* control is to pass all control data as audio rate
arrays of data.

Linear interpolation (or rather, ramping) will probably do for most
things, and if the accuracy is insufficient, one can either switch to
cubic curves, as proposed by "n++k", or just split buffers into
smaller fragments, to get more "nodes". Of course, the linear/cubic
choice is an API level one, whereas samples/"node" is a run time
decision to be made by the host.

As to cost, we're talking about one or two adds per port and sample,
which cannot really be avoided anyhow, without making plugins useless
with automation.

Complexity: Unless I'm missing something, having LADSPA *requiring*
plugins to implement linear or cubic ramping will actually make life
*easier* on plugin coders, as they can just throw in the ramping adds
for each port, and be done with it.

The host has to do the actual y, dy and (for cubic ramping) ddy
calculations and hand the values to the plugin, one way or another.
Obviously, cubic will require significantly heavier calculations -
but then again, a host may chose to simply set ddy to 0 at all times,
to use linear ramping.

Implementation wise, one could make it as simple as extending the
single element "array" of the current control ports with one (linear)
or two (cubic) elements, arranged as <y, dy[, ddy]>.

IMHO, linear should do the trick (considering that hosts may split
buffers as they like, but there may be DSP algorithms that will react
audibly on the transients you get when changing the linear slope -
not enough experience with that to tell if it's a real problem.

There is one problem, though: Plugins that need to do heavy
recalculations when certain controls are changed. The dy and ddy
parameters would be of little use to them, as if they were to perform
interpolation internally, it would be done on a different level. As a
result, the plugin would have to translate the <y, dy[, ddy]> input
into y1, y2 - which would involve some math to undo the work of the
host...

> (2) this works is the step size is large enough. but over on the
> vst-plugins list, it seems de rigeur to interpolate at the sample
> level, something like (for linear interpolation, for example):
>
> float inc = new_control_port_value -
> last_control_port_value; inc /= nframes;
>
> while (nframes--) {
> ... generate a frame ...
> parameter_value += inc;
> }
>
> You cannot reasonably simulate this by calling process() with an
> argument of 1 frame over and over again - the cost is way to high
> and you destroy any optimization in the plugin.

Right.

> From where I stand right now it seems impossible to implement
> plugin parameter automation with this scheme. The VST approach is
> to feed a list of timestamped (parameter change) events to the
> plugin before the call to process(), and expect the plugin to
> respond to these events during process(). This takes us back to all
> the event-centered discussions we had here about MAIA.

Keep in mind that a timestamped event system does not automatically
solve the control "smoothing" problem. In theory, you *could* pass
one event for each sample for each control port that needs to me
ramped - but that's still pretty expensive. (The only gain would be
that the plugin would recalculate only the controls actually changed,
as opposed to recalculating *all* controls for each call.)

That is, an event system is an optimization that eliminates the need
for buffer splitting, and as a result, also drastically reduces the
amount of "inner loop state rebuilding" plugins have to do. Consider
this pseudo code example:

        process(..., int frames)
        {
                remaining = frames;
                ...
                prepare for inner loop
                ...
                while(remaining)
                {
                        while((fragment = next_event()) == 0)
                        {
                                event = get_event();
                                switch(event->type)
                                {
                                  ...
                                  Various cases that directly
                                  manipulate the inner loop state
                                  ...
                                }
                        }
                        if(fragment > remaining)
                                fragment = remaining;
                        for(s = 0; s < fragment; ++s)
                        {
                                ...
                                DSP code
                                ...
                        }
                        remaining -= fragment;
                }
        }

(next_event() returns the frames until the next event is to be
processed. Returns 0 if there are events to process *now*.)

It does not eliminate the need for interpolation - although it *does*
allow interpolated controls to be used more efficiently. (For
example, implementing envelopes with fast transients can be done with
sample accurate timing without requiring a buffer split for each
node.)

[...]
> Does anybody have any concrete proposals or ideas?

Well, if it wasn't for the problem with plugins that need to
recalculate stuff when controls change, I'd suggest the <y, dy[,
ddy]> controls (see above) as that's probably the simplest solution
there is.

I'm not sure, but I think explicitly specifying in the API that
control ports should be interpolated, and how, would be the simplest
solution that doesn't bring in issues that require further extensions.

> Did I miss some
> glaringly obvious method for doing this?

I've had many ideas for this kind of things, but I've failed to see
any easier ways. (Still open to suggestions, although my hopes aren't
high...)

All methods have their problems, and although a VST/MAIA style event
system seems to be the best complexity/cost/flexibility tradeoff
discovered yet, it doesn't really solve the right problem here. It's
also more complex than any per-buffer control interpolation
solutions. Just have a look at the example above, or read the source
(if you can get some... *heh*) of some VST synth that actually does
sample accurate timing right. (*)

(*) People seem to have problems with that. Questions like "What
   do I do with the event timestamps?", and examples of VST plugins
   that just ignore the timestamps and implement all changes right
   in process_events(), indicate this. That's why I'm not even
   considering using a separate function for events in my designs,
   but rather propose a standard model (like the above) for the
   process() function. (You're still free to screw up event timing
   if you like, of course... :-)

//David

.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
`----------------------> http://www.linuxaudiodev.com/maia -'
.- David Olofson -------------------------------------------.
| Audio Hacker - Open Source Advocate - Singer - Songwriter |
`-------------------------------------> http://olofson.net -'


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : Sat Mar 16 2002 - 23:40:40 EET