[linux-audio-dev] PTAF link and comments

David Olofson david at olofson.net
Thu Feb 6 17:13:01 UTC 2003


On Thursday 06 February 2003 12.58, Laurent de Soras [Ohm Force] 
wrote:
> Late reply, I was quite busy and english writing takes me
> time. I commented various people quotes here. However I
> haven't read recent posts yet, I'm a bit overhelmed by
> the reply avalanche. :)

Well, we're crazy about plugin APIs around here! ;-)


[...]
> > I'm questioning whether
> > having a simpler query based system may be easier.  I don't like
> > the idea that you have to instantiate to query, though.
>
> Many plug-in standards require instanciation before fetching
> properties. PTAF must be able to make good wrappers for existing
> plug-ins, industry requires it.

That's one point, although I think you could have the plugin factory 
of a wrapper do the instantiation "secretly". A wrapper *is* a plugin 
host anyway, so this shouldn't be a major issue.

Either way, for various other reasons, I'm beginning to think that 
there are other reasons to use lightweight plugin instances for 
metadata requests. We have already mentioned plugins that can 
"mutate", and generic wrappers (ie wrappers that have a control for 
selecting the alien plugin to wrap) belong in that category.


> > Yeah, I've thought about that too. Rather hairy stuff. The only
> > easy way to avoid it is to strictly define what the host should
> > tell the plugin before asking anything, and then just leave it at
> > that.
>
> We can also add somewhere a watchdog limiting the recursion.

Yeah, but it doesn't really solve the problem, does it? If you get 
endless recusion (or rather ping-pong, if we're talking about events; 
not much better though), it's because there's something wrong with 
the host, the plugin or both.

There has to be a way to guarantee that dependencies don't become 
circular... Can we have some actual examples to break down? I've 
never written a host that changes it's parameters based on plugin 
properties, so I've never seen the problem in real life.


> > [...the "sequencer" thread...]
>
> As suggested we can call it "host", or "main" thread. In my opinion
> generic forms of hosts have several "virtual" threads :
>
> - Audio: handles audio processing and related sequencing
> - Main/Background: handles generic operations, state transitions,

What state transitions?


> as well as background disk streaming, data preparing for audio
> thread, etc.
> - GUI thread (at least on Win and Mac OSs), can theoretically
> be merged with the main thread, but it's not recommended (Mac
> programmers have the bad habit to poll the mouse within a
> blocking loop when user is holds a click).

Well, they didn't have much choise in the old days... Win16 had the 
same problem. Provided we're talking about Mac OS X, it certainly is 
nothing but a bad habit, though.

Anyway, I know what you mean by these threads - but I still don't see 
it as relevant to a plugin API. A GUI-less audio server may well have 
only one thread, and either way, plugins should not be required to be 
thread safe, unless they explicitly ask for it (trouble, that is ;-) 
themselves.


[...]
> > Well, you might still be able to comment on an idea: Splitting up
> > the GUI in two parts; one that runs in the same process as the
> > DSP plugin, and one that runs in another process, possibly on
> > another machine.
>
> Yeah, I had this in mind when i was talking about lauching the
> GUI from the plug-in.

Yeah... Though I think all this should be left to the host, 
regardless. Just tell the host where your GUI is. If it's a "local 
half" plugin (that in turn refers to an external GUI), it could be in 
the same binary, as it's still just a plugin with a minimum of 
external dependencies. (Whether or not it wants to run in the audio 
thread is another matter. It can still use the same API - and the 
same rules WRT GUI toolkits still apply; don't use them.)


> > You would need a feature that allows control outputs to be marked
> > as "active" or "passive". This allows hosts to control how things
> > are handled.
> >
> > If I see a knob that is turning via some old automation, I
> > should be able to grab it in the UI and take over.  It is not
> > clear to me, however, whether the host should remember that and
> > leave control with the highest prio controller, or just make it
> > temporary.  This needs more thought.  It may not be the domain of
> > the API at all, but the host.
>
> But the token system is just about the same concept, isn't it ?

Not really. The difference is that my approach doesn't require GUIs 
and hosts to use a special API just for talking to controls.

Also, tokens don't mix with the direct connection system we're using. 
(XAP plugins generally don't send events through the host, but rather 
directly to the input queues of other plugins.)


> For a given parameter, there is at most one active client at a
> time, others are passive and receive only change notifications.
> Arbitration is done by host, who is allowed to deprive a client
> from his token to give it to a client requesting it.

This doesn't work if some clients are out-of process. Control outputs 
with active/passive modes do, since you never have to inform client 
whether or not their data is used.


> For example it makes sense to give less priority to the
> automation system than to user via GUI, so the latter can
> steal its token, just by requesting it. When user has released
> the knob, it give back the token to the host making it
> available for any use (can transmits it implicitly to the
> automation system).

Sure, but it just seems like a more complicated way of doing it, 
especially when threading and out-of-process issues are taken in 
account.


> > I actually think having to try to *call* a function to find out
> > whether it's supported or not is rather nasty...
>
> Good point.
>
> Anyway I would like to minimize the number of functions.

Good idea, of course. And besides, this even further reduces the need 
for "advanced" methods of maintaining supported and unsupported 
calls.


> Functions are handy when the calling conventions
> of both parts are compatible. If the language you use
> doesn't support this convention natively, all functions
> have to be wrapped, which is a real pain.

Really?

Either way, we also have to consider the importance of wrapping the 
API. How many write VST hosts and plugins in other languages than 
C++? (I know that some do. FruityLoops is written in Delphi, IIRC.)

We also have to consider the balance between the work required to 
implement a wrapper/language binding and the impact simplifying that 
has on the native API, in terms of performance and ease of use.


> More, there are still issues with existing plug-ins, reporting
> wrongly if they support some opcode or not (talk to this issue
> to Plogue people :).

Bugs are always bugs. A simple testing host in the SDK would eliminate 
all valid excuses for this particular bug. (And it could of course be 
included in the host SDK as well, to warn every user about broken 
plugins.)

Either way, I don't see a real problem here. Just don't call a NULL 
pointer, ok? :-)


> > I think this separation is a mistake. Of course, we can't have an
> > API for complex monolith synths scale perfectly to modular synth
> > units, but I don't see why one should explicitly prevent some
> > scaling into that range. Not a major design goal, though; just
> > something to keep in mind.
>
> The separation between coarse/fine grained plug-ins was just
> a design goal for the API. Fine grained plug-ins would have
> more issues, like the ability to do fast sample-per-sample
> processing (for feedback loops).

Yeah, but you can't really get around that. You can still implement a 
host that does small block (not the Chevy V8! :-) sub nets for 
feedback loops, but it's going to be rather expensive. OTOH, that's 
*always* expensive...

My point though; expensive is something you could live with. It might 
even matter to you. *Impossible* is a different story entirely.


[...]
> > How about defining buffer alignment as "what works good for
> > whatever extensions that are available on this hardware"...?
>
> Good, but it requires the host to be updated when new hardware
> is released.

Or just make it a setting in "advanced options"...?

(If you're not very interested in users upgrading your software, that 
is! ;-)


> We can add a host property indicating the current
> alignment so the plug-in can check it before continuing.

Very good idea. There *are* platforms where a plugin could crash the 
host if they ignore this.


> > you'll get a lot of arguments about that on this list - linux
> > people tend to have a 486 or pentium stashed somewhere. :)
>
> Professional musicians and studios have recent hardware, at
> least it's what survey results show us.

They don't really have much of a choice, do they?


> Recent audio software
> produced by the industry also tend to be hungry, requiring
> fast hardware - I don't say it's good, it's just a fact, and
> industry is the main target for PTAF.

It's a fact, and it's not as much a result of inefficient APIs as it 
is a result of people wanting "cooler" DSP effects. I don't have a 
problem with this, but I *do* have, and always will have, a problem 
with wasting resources for no good reason. Thus, I tend to look for 
optimal solutions at all times. If it's something I'll have to live 
with for years, I look harder - just in case it turns out that it 
actually matters.


> If you plan to make a standard, it's better to build it for
> having use over years. After all, MIDI is still here and will
> probably last 20 years again. OK things are a bit different
> with pure software protocols, but arguing little performance
> boost against obvious programming safety is pointless for me
> (sadly programmer's brain doesn't follows the Moore law).

Very good points, and of course, I'm not proposing that we should make 
life hard for plugin authors just to save a few cycles.

It's just that the wasted cycles is not the *only* problem I have with 
the dispatcher approach, so if there's a better solution (that also 
happens to be a bit faster), why not use that?


> > Agreed - but they also attach value to efficient software.
>
> Yes, but given my experience with customer support, unsafty
> displeasure is one or two orders of magnitude above efficency
> pleasure.

Very good point! If it *crashes*, who cares if it's slightly faster?


> > That's one way... I thought a lot about that for MAIA, but I'm
> > not sure I like it. Using events becomes somewhat pointless when
> > you change the rules and use another callback. Function calls are
> > cleaner and easier for everyone for this kind of stuff.
>
> The advantage of events is that you can have several of them at
> once, giving the plug-in the possibility to optimise operations.

Yes.

As a matter of fact, I have *exactly* this problem with the FX plugins 
in Audiality. Initializing the parameters of the xdelay or reverb 
effects means they have to recalculate all taps almost once per 
parameter. *heh* They're about the last controls to use function 
calls, and it's a real problem.

I could handle it by implementing the changes in the process() call, 
though, just like with events. It's just that the changes are not RT 
safe, so they have to send the work off to another thread. Dealing 
with that *and* the function call API, well... I'll just leave it 
until I've fixed the plugin API.


> > We've just spoken of calling the process() function with a
> > duration of 0 samples. Events are then processed immediately,
> > and no second API is needed.
>
> Dunno if this factorisation is really good. Different goals,
> different states for call, it should be a different opcode/func.

There's a *big* hidden issue with thinking about different interfaces 
yor different states. States are related to what context a plugin 
runs in, and using different APIs in different states doesn't really 
help. You can't perform non RT operations on a running plugin just 
because it has a separate interface for it! Either the plugin must be 
thread safe, or the host has to take the plugin out of the RT thread 
first.


> > Anyway, I'm nervous about the (supposedly real time) connection
> > event, as it's not obvious that any plugin can easilly handly
> > connections in a real time safe manner.
>
> Don't mix up audio configuration (which is not real-time)
> and connections. In most of cases I see, (de)connections
> is reflected in the plug-in by changing a flag or a variable,
> making the processing a bit different. For example a plug-in
> has a 2-in/2-out configuration. When only one input or output
> channel is connected, it gives the ability to make the
> processing mono, or to (de)activate vu-meters, etc.

You're totally right. I wasn't thinking straight.


[...]
> > This is where it gets hairy. We prefer to think of scale tuning
> > as something that is generally done by specialized plugins, or by
> > whatever sends the events (host/sequencer) *before* the synths.
> > That way, every synth doesn't have to implement scale support to
> > be usable.
>
> That's why Base Pitch is float. But because MIDI is still there,
> existing plug-ins will be happy to round (after scaling if 1/oct)
> Base Pitch to get the note number.

Yeah, I realize that, but there's still a difference between explicit 
1.0/note and 1.0/octave with the scale assumed to be 12tET. If the 
scale *isn't* 12tET, rounding Base Pitch won't work as expected.


> > As it is with VST, most plugins are essentially useless to people
> > that don't use 12tET, unless multiple channels + pitch bend is a
> > sufficient work-around.
>
> VST has also a finetuning delivered with each Note On.

Yes, I remember now that you say it. However, it has very limited 
range (cents; sint8_t), and the MIDI pitch field still doubles as the 
"note ID", so this is in no way a substitute for continous pitch, or 
even sufficient for handling scales that are substantially different 
from 12tET.


[...]
> > The only part I don't like is assuming that Base Pitch isn't
> > linear pitch, but effectively note pith, mapped in unknown ways.
>
> However many (hard/soft) synth can be tuned this way. It is a
> convenient way to play exotic scales using traditional keyboard,
> piano roll or other editors / performance devices.

What's wrong with using a generic scale converter plugin *before* any 
synth you want to do this with? That's one of the major benefits of 
using continous, linear pitch, IMO. I don't think synths should have 
internal scale converters, unless they have *very* good reasons. (And 
I can't think of any.)


> > Right, but MIDI is integers, and the range defines the
> > resolution. With floats, why have 2.0 if you can have 1.0...?
>
> To make 1.0 the middle, default position.

Yeah, but what is a "default" velocity in real life...?

It's something that was invented for the MIDI protocol, to support 
controllers without velocity sensitivity. As even the cheapest "toy" 
MIDI controllers are velocity sensitive these days, it is essentially 
irrelevant, even to MIDI devices.


[...]
> > Anyway, what I'm suggesting is basically that this event should
> > use the same format as the running musical time "counter" that
> > any tempo or beat sync plugin would maintain. It seems easier to
> > just count ticks, and then convert to beats, bars or whatever
> > when you need those units.
>
> But how do you know on which bar the plug-in is ?

Somewhat like in VST; I ask for a time info struct.


> Tempo and
> time signature can change in the course of the song and it
> is not obvious to deduce the song position from just an
> absolute time reference or a number of ticks or beats.

That's why tempo and beat sync plugins in VST have to ask for time 
info for every block.

The only difference with XAP is that if you have input controls for 
musical time, you'll get timestamped events whenever interesting 
changes occur, so if you just listen, you can have a sample accurate 
view of the musical timeline at all times.

(XAP beats VST in this regard, since VST can only give you time info 
for the first frame in each block. XAP plugins can maintain sample 
accurate beat sync even if there are several *meter* changes within a 
single block. All VST can tell you is the tick time for points in 
audio time, so you could miss meter changes.)


> > Yes. And bars and beats is just *one* valid unit for musical
> > time. There's also SMPTE, hours/minutes/seconds, HDR audio time
> > and other stuff. Why bars and beats of all these, rather than
> > just some linear, single value representation?
> > Could be seconds or ticks, but the latter make more sense as it's
> > locked to the song even if the tempo is changed.
>
> For reasons mentioned above, both time in second an musical
> representation are needed.

I'm not denying that.


> SMPTE etc can be deduce from the
> absolute time in seconds or samples.

Yep, but that's true for musical time as well - although musical time 
*is* indeed of much more interest to many more plugins, so 
special-casing it in the API is probably motivated. (Which is why XAP 
has a set of controls specifically for musical time.)


> > Accuracy is *not* infinite. In fact, you'll have fractions with
> > more decimals that float or double can handle even in lots of
> > hard quantized music, and the tick subdivisions used by most
> > sequencers won't produce "clean" float values for much more than
> > a few values.
>
> But is that so important ? Float or int, we manipulate numbers
> with a finite precision.

Sure, but multiplication of two integers always gives you an exact 
result. (Yes, even if it doesn't fit! It's just that high mid and 
level languages can't make use of the upper half of the result - 
which I find really rather annoying... *heh*)


> And if someone is happy with tick
> scales multiple of 3, someone else will complain about not
> supporting multiples of 19, etc.

Yeah, that's why sequencers use these "weird" numbers... They don't 
cover *everything* of course, but they're high enough that rounding 
times to the nearest tick is ok - and then you can keep calculations 
in the sequencer exact.


> For the drifting question, it is neglictible with double, and
> can be easily resync'd if required. Ohm Force's plug-ins use
> double as positions and steps for the LFO phases and it works
> fine without resync during hours, on any fraction of beat.
>
> If you need exact subdivisions, you can still convert the
> floating point value into the nearest integer tick.

Well, I can't disagree, as I used to argue for your position, and have 
yet to see actual proof of the rounding errors/drift being a problem. 
Accurate musical time calculations belong in the sequencer domain, as 
we can't support them anyway, unless possibly if we switch to musical 
time timestamps.


> > The host would of course know the range of each control, so the
> > only *real* issue is that natural control values mean more work
> > for hosts.
>
> No it just would store and manipulate normalized values.
> Natural values would be needed only for parameter connection
> between plug-ins, if user chooses to connect natural values.

There will be conversion work to do *somewhere* no matter what. 
However, I don't like the idea of making all controls have hard 
ranges. It restricts the users in ways not necessarily intended by 
plugin authors, since there's no way to set controls to values 
outside the range the plugin author decided to use, even if there's 
no technical reason why it couldn't be done. You're basically 
throwing away interesting possibilities.


> > What's wrong with text and/or raw data controls?
>
> They often require a variable amount of memory, making
> them difficult to manage in the real-time thread.

Yes, I'm perfectly aware of that... *heh*


> However there is a need for this kind of stuff for sure.
> I thought more about encapsulating them into specific
> events.

Yeah, that's the approach I have in mind. Chained fixed size blocks or 
something... It's not too much fun working with, but we *are* dealing 
with real time here; not word processing. People wanting to write 
real time software will simply have to accept that it's a different 
world, since there's no way we can changes the laws of nature.


> > 1) none - host can autogenerate from hints
> > 2) layout - plugin provides XML or something suggesting it's UI,
> > host draws it
> > 3) graphics+layout - plugin provides XML or something as well as
> > graphics - host is responsible for animating according to plugin
> > spec 4) total - plugin provides binary UI code (possibly bytecode
> > or lib calls)
>
> I agree with 1) and 4). For my experience, when a plug-in
> has 4), it has also 1), but it's not *always* the case
> because developers don't feel necessary to implement the
> hints correctly (dev time matters). Adding more UI
> description is likely not to be supported by either host
> or plug-in, making them useless.

Some of it can be made optional, of course. In fact, even range and 
default values could be made optional. Just define the default range 
for an unmarked control as [0, 1], and the default value as 0. 

Whether the range should be soft or hard, I'm not sure, but soft seems 
more useful.


> Good solution would
> be using portable UI libs, as someone suggested here,
> but everyone knows it's the Holy Grail.

Yeah... *heh* Even the nicest toolkit/scripting solution will only 
work for *most* plugins; not the ones that want new, cool visual 
effects.

Though SDL is as portable as it gets, and what you can't do with that 
can't be done, basically. I can assure you that an API like that 
*will* work for any plugin GUI, one way or another.

How? All it provides is basic blitting and pixel level access to the 
screen and other surfaces. This is stuff that any platform with 
graphics can support, and most GUI toolkits also have some form of 
low level canvas or other component that can do it. I've even 
implemented the SDL 2D API on top of OpenGL (glSDL), for lightning 
fast rendering where h/w acceleration is available. (Pretty much the 
only way to have fully accelerated SDL on X at this point.)

If we want to spend time on anything along the lines of a portable GUI 
toolkit, SDL's graphics functionality is the *first* thing we should 
implement, or it's just a waste of time. Then we can start thinking 
about building something like VSTGUI and/or other stuff on top of 
that.


> > The spec should not dictate ABI, either.  The ABI is an articat
> > of the platform.  If my platform uses 32 registers to pass C
> > function arguments, it is already binary imcompatible with your
> > PC :)
>
> The only way to guarantee real portability through platforms
> and programming languages is to describe the API at the byte
> level. This include calling convention. IMHO We can assume
> that every target system has a stack, and can pass parameters
> on it.

Good point. I think we can count on standard C calling conventions to 
work and be supported by all languages on every platform. No big deal 
if it isn't as fast as the default conventions - the heavy traffic 
goes through the event system anyway, and there are no per-event 
function calls.

Of course, a language that cannot interface with the event struct 
would be a problem. Requirements: Pointers, 32 bit integers, 32 bit 
floats and 64 bit floats. (We *could* make all integers 64 bit on 64 
bit CPUs, but it seems insane and pointless to me. 64 bit machines 
don't have infinite memory bandwidth...)


[...]
> If you want to generate only ASCII in your program, just write
> only ASCII, it will be supported by UTF-8. However if you have
> to read strings, it's better to be aware that it's UTF-8,
> because incoming strings may contain multi-byte characters.

...and what that means to your average plugin is basically "Don't 
start splitting strings arbitrarily!"

Would be interesting to know which ASCII values are valid inside 
multibyte charatcers, BTW. Is there a risk you'll see false slashes, 
colons and things like that in paths, if you don't parse the UTF-8 
properly? (There isn't IIRC, but I'll have to read up on this.)

[...]


//David Olofson - Programmer, Composer, Open Source Advocate

.- The Return of Audiality! --------------------------------.
| Free/Open Source Audio Engine for use in Games or Studio. |
| RT and off-line synth. Scripting. Sample accurate timing. |
`---------------------------> http://olofson.net/audiality -'
   --- http://olofson.net --- http://www.reologica.se ---




More information about the Linux-audio-dev mailing list