What the World Needs: a New Plug-in Format!

DSP, Plugin and Host development discussion.

Does the World Need a New Plug-in Standard?

Poll ended at Sun Jan 25, 2009 7:38 pm

No! There are too many already!
62
40%
Yes! VST 3 isn't the answer!
41
26%
That's the wrong question.
26
17%
That's the wrong question.
26
17%
 
Total votes: 155

RELATED
PRODUCTS

Post

aciddose wrote:you should forget about supporting "lame" languages like basic, java and the like. forget about languages that do not support pointers, typing, type-casting (voids, etc) and memory management. if you write an interface that can support these languages it will HAVE to be bloated to deal with the limitations of those languages - as i said, it becomes hard to implement in ALL languages. if the code is made to work with basic and java, using c the same bloat will be present.
This is just so very wrong. If you need pointers (as opposed to object references) and voids, you'll never manage proper API design - unless you think Object Oriented Design is bad and the cause of bloat, in which case there's no helping you.

Post

in order to do object oriented design in a procedural language you need void pointers and memory management. c++ uses void pointers and all the structures i describe for it's objects, only you don't see that. rather than passing void pointers to structs, you pass a "this" pointer - it's the same thing.

we can either have a dynamic object oriented interface by using the methods i suggest, or we can have a flat procedural interface using hundreds of c functions with tens of parameters each.

your problem is simply that you don't recognize OO programming at a systems level. do some OO in assembly then come back and talk.

using the methods i propose, it is possible to write clean vbasic, java and c# wrappers while maintaining clean OO c code for the core.

Post

aciddose wrote:in order to do object oriented design in a procedural language you need void pointers and memory management. c++ uses void pointers and all the structures i describe for it's objects, only you don't see that. rather than passing void pointers to structs, you pass a "this" pointer - it's the same thing.

we can either have a dynamic object oriented interface by using the methods i suggest, or we can have a flat procedural interface using hundreds of c functions with tens of parameters each.

your problem is simply that you don't recognize OO programming at a systems level. do some OO in assembly then come back and talk.

using the methods i propose, it is possible to write clean vbasic, java and c# wrappers while maintaining clean OO c code for the core.
I fully understand OO at systems level. We're talking about an API here though, and APIs - those intended to be used by others, especially - shouldn't need any visibility of direct pointers or voids.

Yes, the language may still be using them under the covers, but the API is a document-level specification and should be as high-level as possible. Pointers to arrays of pointers, and dynamic structures are implementation-level decisions, and not for APIs.

Your dismissal of Java as 'lame' in this regard, demonstrates the confusion. As soon as you get caught up in 'efficiency' of the implementation, you've already compromised the API design.

Post

we don't care about efficiency, the flat c functions are the highest efficiency method. after that, the ordered opcodes with predefined parameters (vst dispatcher) is second place for efficiency. the method i'm suggesting is the least efficient - however it solves EVERY limitation of the other methods and it also happens to be the smallest and least complex. in languages like c++ and java, the wrapper would handle translation between the native object format and the pointers/structures. such wrappers would face the limitations of their languages - however the specification of the interface would be free from these limitations.

we're not talking about an sdk here, we're talking about an interface. the wrapper is an sdk.

when you use TCP/IP functions in java, do you care about the interface for TCP/IP, do you care about the firmware on the network card? do you care about the individual bits being encoded serially on the wires or being transmitted wireless? No.

you only care about the wrapper to the interface, not about the interface itself. you call "tcp.sendpacket(...)", that's all.

there are many layers beneath the java layer - even java itself has many layers which hide implementation from the java programmer. once it's out of the virtual machine, you have O/S drivers. then you have more and more layers. if you're writing plugins in java for this interface you ought to care about writing the java wrapper (a lib) and then care about writing java code. you are not a systems coder if you don't already realize this stuff.

Post

Hmm..

I'm kind of wondering if we're saying the same thing here from different angles.

I was (trying) to say that the API should be simple and high-level, not worrying about implementation.

Something like:

MakeNoise(Time when, Pitch what, WarbleFactor seven, HostInfo information)

instead of:

MakeNoise(Void **Parameters, Void *callback)

[excuse the poor pseudo-code]

Post

of course it should, the api looks like this:

class myapi
{
int getsomething() { return interfacelib.getsomething(); }
}

and in the wrapper lib, we'd have

int interfacelib::getsomething()
{
int returnvalue = 0;
void *p[] = {&returnvalue,};
host.send("getsomething", void *p);
return returnvalue;
}

then the interface itself looks like:

struct host
{
some data...
function pointers...
}

struct lotsofstructdefinitions
{
}

...

the advantage being that the minimal number of definitions can be used if you write your plugin in c, the wrappers only need to become big in languages like c++, java, vbasic and so on. the whole issue of wrapper vs. interface only exists if we first assume somebody will even write a java wrapper in the first place.. which although entirely possible with this type of interface (unlike vst3) is unlikely to be done anyways. that's why i'm saying, the interface shouldn't take note of languages like java when it's unlikely they'll be used without a wrapper anyway. (well, to use this or really anything in java you MUST write a wrapper.. so it's moot.)

Post

Then we're pretty much agreeing, from an API point of view, although it should just be the header file with no implementation.

Must be a Monday :wink:

Post

i think i should have ranted less about some things and just pointed out issues with interface formats instead:

- flat c functions

this places a lot of weight on the shoulders of host and plugin developers as well as makes the interface definition very large. the host _must_ implement all functions defined while plugins must do likewise. this seems ok for very minimal interfaces but it becomes a huge bloat with any reasonable interface.

- structs of function pointers

while this solves bloat on both sides, it severely limits expansion of the interface. each version will require additional functions to be placed in new structs of pointers with new calls to populate and pass them between host and plugin. while bloat is initially warded off, spaghetti code will quickly result if additions are made to the interface.

- static dispatcher function

this method solves the issue of expansion, however it does not prevent spaghetti code from forming. as additions are made related functions might end up being numbered 100s of places away from each other and perhaps in completely different files, as in vst.

- dynamic dispatcher function

this method solves all the above issues, however it presents a new one; the opcodes, namespaces, pointer arrays and structures must be implemented by the hosts and plugins. although always cleanly defined, divided and completely optional, the core implementation required is slightly larger than with a static dispatcher. the most significant issue is the handling of strings, and the format of strings for the namespace and opcode encodings.

should namespaces be in english? plaintext? unicode? upper/lower case? what names should be used? what type of separators should be used between namespaces? how should pointer arrays be defined, what should the order of variables be, how should return values be handled, how can opcodes be flagged as accepted or declined? how can structures and opcodes be designed to prevent redundancies?



- old interfaces used flat c interfaces.. generally they were very small. i think this method is unacceptable for the scope and size of a audio processing and real-time messaging / synthesis interface.

- structs of function pointers have been used by winamp, for example. these also must be very small, although they are cleaner than flat c function interfaces.

- the static dispatcher method has been quite popular for those who did not wish to implement a dynamic one or did not see the purpose in doing so.

- i've never seen a dynamic dispatcher implemented exactly as i describe with only two functions, although i've glanced at similar interfaces with a mixture of the other formats.

- i did not mention one other method: a messaging system like that used in xwindows xlib. this is very similar to a static dispatcher using void pointers, perhaps half-way between what vst does and what i think should be done. this method is MESSY.

Post

jwatte wrote:The Simple Audio Plugin Specification
Preview release 2009-01-11
http://www.enchantedage.com/saps


Given that a lot of people are unhappy with VST 3.0 being so complex to develop even a simple effect for, and the lack of hosting documentation for most of the plug-in standards out there, I sat down for a number of week-ends and evenings and came up with my own proposal for a plug-in standard. The guiding principles were:

1) Define the hosting API as well as the plug-in API.
2) Use plain C function pointers for the interface, to allow multiple languages.
3) Define a system that is easy to extend in a compatible way in the future.
4) Clearly define threading.
5) Use a declarative plug-in GUI that is rich enough to let hosts generate good GUIs that integrate with the host, rather than putting that on the plug-in.
6) Support the integration and timing of MIDI with audio.
7) Multiple input and output busses, each with multiple channels of audio.
8) A working reference implementation of most kinds of plug-ins (effects, file and device I/O, MIDI I/O and synth).
9) Working Code!!!

I think I have something interesting so far. At this point, I'd like to hear some comments from potential users, be they plug-in authors or host authors.
While I enjoyed reading this interesting draft, I keep thinking this is the wrong approach to the situation with VST3. I believe there wasn't really a "need" for a new plugin format, rather than areas to improve inside of what's already there. IMO it would be more fruitful to start collecting mandatory features (VST 2.4, AU, RTAS, LV2 etc.) and try to provide something with the fundamental requirements (what is expected to transition smoothly from whatever existing plugin standard), and work from there on.

Sorry, I don't have much more time to go into details right now, but probably will comment later on. I only thought I'd throw this into the discussion. It's what seemed to me the most important conditions why someone would ever think about doing this extra work of choosing such a new plugin standard (right now it looks more like an academic study with a test implementation, but without the connection to the current end users).
The implementation details I'd leave for later once the use cases ("what is needed ?") has been worked out.

And then, it also remains to be figured out what the status of VST 2.4 is at the moment. It seems like VST3 is the way that Steinberg is following, but I still secretly think that maybe it could be possible to convince Steinberg to allow VST 2.x (after 2.4) to be under the control of an independant consortium and let it branch into a VST 2.5 release.

Enough said from me for now, please carry on.

Post

plastique wrote:
And then, it also remains to be figured out what the status of VST 2.4 is at the moment. It seems like VST3 is the way that Steinberg is following, but I still secretly think that maybe it could be possible to convince Steinberg to allow VST 2.x (after 2.4) to be under the control of an independant consortium and let it branch into a VST 2.5 release.

Enough said from me for now, please carry on.
That's what I think would be brilliant and logical, too. ;)

Cheers!
It is no measure of health to be well adjusted to a profoundly sick society. - Jiddu Krishnamurti

Post

'll give you both xhip, my effects, and all source code under gpl
Thanks! I don't like the GPL, though, because of its viral nature. It's not even clear that a GPL plug-in could be dynamically linked to a non-GPL host! (If you ask the FSF if you could write a GPL plug-in to Photoshop, for example, they would say "probably not!")
C functions, structs of pointers, dispatchers
I don't like dispatchers, because they add overhead, and when you have zillions of little plug-ins, that overhead matters. Making the host responsible for recognizing NULL pointers in the plug-in is OK, though -- the host could just smash default do-nothing pointers into the structure when it's first returned.

I wonder if the plug-in structure could be made shorter by using indirection? A pointer to a list of parameter functions, a pointer to a list of Audio Bus functions, a pointer to a list of MIDI functions, ... Perhaps here is where the dynamic dispatch comes in? GetInterface("MIDI") returns the MIDI interface, which is a struct of function pointers. GetInterface("SKINI") returns the SKINI interface.

Personally, I think that an "escape hatch" is always going to be necessary. While fully nailed-down interfaces are great (and a goal), there will always be some special use in some special host/plug-in combination that you can't think up in advance. COM has IUnknown with QueryInterface() for this; UNIX device drivers have ioctl() for this; etc. For myself, the long-term, unlikely-to-be-finished use case is a multi-track recorder. I could implement my file handling, say, as a plug-in, but use a custom interface for the parts that make it integrate really well into my app.

When it comes to MIDI versus events, I first wanted to provide separate events for each MIDI message (key on, key off, controller change, etc) -- but then I realized that was just mirroring the already-existing MIDI specification! It turns out that decoding MIDI bytes is not that hard, and there's already a lot of code out there to deal with MIDI, so unless I decide I want to revolutionize the music controller business, I don't see the value-add in trying to replace it.

Here's a question: I purposefully decided not to use double precision for audio data. The thinking is that double precision helps for IIR filter implementations, but that's really a plug-in internal problem, and blowing the caches by doubling the buffer sizes for everything doesn't seem like it would gain you anything (other than a marketing checklist item), while it would have a noticeable cost. Opinions?
Apparently, there are some legal, mostly non-controverisal subjects you can't talk about on KVR, or even mention in your sig. Unfortunately, the guidelines don't actually talk about them, so the only way to find out is trial and error.

Post

I vehemently oppose this standard as it is an unauthorized use of the initials S.A.P... :tantrum:

that said...

I would like to see a plugin standard that was not controlled by any company myself...

Post

jwatte wrote:Thanks! I don't like the GPL, though, because of its viral nature. It's not even clear that a GPL plug-in could be dynamically linked to a non-GPL host! (If you ask the FSF if you could write a GPL plug-in to Photoshop, for example, they would say "probably not!")
But that's what LGPL is for! It's a bit less viral than GPL, and you can link it with whatever you want.
jwatte wrote:I wonder if the plug-in structure could be made shorter by using indirection? A pointer to a list of parameter functions, a pointer to a list of Audio Bus functions, a pointer to a list of MIDI functions, ... Perhaps here is where the dynamic dispatch comes in? GetInterface("MIDI") returns the MIDI interface, which is a struct of function pointers. GetInterface("SKINI") returns the SKINI interface.
Am I becoming the LGPL Guy here? Sorry! Just coincidence! :P *prepares anti-rotten-tomato shield anyway*

What you're describing looks a lot like LV2, which can also keep 'extensions' as optional structures of function pointers. If you don't like its LGPL licensing (which is not viral, you can make your plugin closed-source), you could at least take a few ideas from it; I think LV2 is full of great ideas (but that is just an opinion, of course).

Post

I'm familiar with LGPL, and vastly prefer it over GPL, for sure.

LV2, huh? I can't quite make up my mind as for whether it's a good idea or not. Things like this make me instinctively shy away:
LV2 separates the static data into RDF files in the easy to read and hand-write Turtle syntax
The sample plug-ins use GCC extensions (such as initializing structures using field names) that won't compile on other platforms. It also doesn't seem to deal with such things as timecode, or plug-ins that can reconfigure themselves (a real problem when the description is separate from the plug-in).
Apparently, there are some legal, mostly non-controverisal subjects you can't talk about on KVR, or even mention in your sig. Unfortunately, the guidelines don't actually talk about them, so the only way to find out is trial and error.

Post

aciddose wrote:... the flat c functions are the highest efficiency method.
True, flat C is efficient, but plugins are written in C++, so each 'C' function ends up 'wrapped' in a C++ method. This doubles the overhead.

More efficent is the list-of-function-pointers (arranged like a vtable). Plugin uses C++ calls directly with no intermediate layer. API's writen this way are 2-3 times faster.

Works fine with other languages too, they treat the interface as a list of function pointers, also efficient and easy to use.

Technical merits aside, Plugin Programmers deal with the highest layer of the SDK, so the underlying technology isn't what drives it's popularity.

What is interesting to me is seeing how an SKD implements a simple plugin (like VST's AGain). Is the code less complicated?, is it easy to understand?

Can any plugin SDK beat this? (GMPI) AGain plugin in 31 lines of code, plus an XML description.

Jeff

Code: Select all

#include ".\AGain.h"

REGISTER_PLUGIN( AGain, L"Gain" );

AGain::AGain( IMpUnknown* host ) : MpBase( host )
{
	// Register pins.
	initializePin( 0, input1_ );
	initializePin( 1, input2_ );
	initializePin( 2, output1_ );
	initializePin( 3, output2_ );
	initializePin( 4, gain_ );
}

void AGain::subProcess( int bufferOffset, int sampleFrames )
{
	// get parameter value.
	float gain = gain_;

	// get pointers to in/output buffers.
	float* input1	= bufferOffset + input1_.getBuffer();
	float* input2	= bufferOffset + input2_.getBuffer();
	float* output1	= bufferOffset + output1_.getBuffer();
	float* output2	= bufferOffset + output2_.getBuffer();

    while( --sampleFrames >= 0 )
    {
        (*output1++) = (*input1++) * gain;
        (*output2++) = (*input2++) * gain;
    }
}

Code: Select all

<?xml version="1.0" encoding="utf-8" ?>

<PluginList>
	<Plugin id="Gain" category="SDK Examples" vendor="Jeff McClintock">
    <Parameters>
      <Parameter id="0" name="Gain" datatype="float" default="0.8"/>
    </Parameters>
    <Audio>
			<Pin id="0" name="Input1" direction="in" datatype="float" rate="audio"/>
			<Pin id="1" name="Input2" direction="in" datatype="float" rate="audio"/>
      <Pin id="2" name="Output1" direction="out" datatype="float" rate="audio"/>
      <Pin id="3" name="Output2" direction="out" datatype="float" rate="audio"/>
      <Pin id="4" name="Gain" direction="in" datatype="float" parameterId="0"/>
    </Audio>
	</Plugin>
</PluginList>

Post Reply

Return to “DSP and Plugin Development”