Hello everybody,
I'm one of the PTAF authors and I've just subscribed to
this list, seeing the standard is discussed here. I will
try to answer your questions and to collect feedback.
I apologize for this long mail.
WELCOME! I'm excited to see one of the more well-known plugin companies
chiming in. I'm also excited that XAP and PTAF are not too far from each
other. Hopefully we can reconcile our efforts and just have one API, and
not two very similar ones.
I prefered to keep the factory API quite simple. In
most
cases, the factory would produce only one kind of plug-in.
Detaching the plug-in capabilities from the plug-in itself
may become a pain. With factory-provided metadata and only
two states, the trivial solution is to instanciate internally
the plug-in to get its properties. If it brings it directly
in the Initialized states, it will likely increase the
overhead.
We store all the metadata external to the plugin. Getting metadata is a
pointer to a plugin-owned struct. You only instantiate when you want to use
a plugin. This leads us to have a lot of metadata, or a static meta-data
query method. We currently have extensive structs. 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.
Lastly, plug-in properties may depends on contextual
data,
like OS or host versions, date, CPU, environment variable,
etc. - things sometimes useful for copy protections.
hmm, I'm not sure I buy this - how does this matter?
word "Sequencer" is badly choosen, someone
already told me
that too. If anyone has a better idea for its name...
"host"?
I'm presonally not familiar with Linux GUI
toolkits (I'm
confused with Gnome, KDE, X, Berlin, etc, sorry for
my ignorance), is there a problem with them ? In this
case wouldn't be possible to launch the GUI module from
the plug-in ? What would you recommend ?
Defining a proper cross-platform GUI system will be fun. I see four levels
of UI from plugins:
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)
One solution is to make the host define implicit
priority
levels for these clients, for example 1 = automation,
2 = remote controls, 3 = main GUI. This is good enough
for parameter changes arriving simultaneously, but is
not appropriate for "interleaved" changes.
Actually, I prefer this situation to appear broken - you should NOT have
interleaved control sources. Tokens need to be forecefully grabbed with
that model. 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.
It was for the structured naming facilities offered by
C++
(namespaces). It makes possible to use short names when
using the right scope and to be sure that these names
wouldn't collide with other libraries.
I agree that it's not essential and everything could be
written in C.
I think that if you want to push a true cross platform API it should be pure
C. 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 :)
* I think the
VST style dispatcher idea is silly
But it's the easiest way to :
1) Ensure the bidirectional compatibility across API versions
I agree that it is simplest for host developers. Do we want to make life
simpler for host developers or plugin developers. I've advocated staying
simple in the past on this subject.
I could be convinced that a slimmed down system that was somewhat in the
middle of our two proposals is best. I don't like using variable arguments
in an API, however.
plug->run_func(enum cmd func, ...);
That scares me as a source for big bad runtime bugs, and just passing a void
pointer doesn't naturally handle all cases.
And no, C++ is not required for PTAF plug-in, because
it
defines a communication protocol at the lowest possible
level. C/C++ syntax is just an easy and common way to
write it.
but by writing it in C++, you require a C++ compiler.
* UTF-8,
rather than ASCII or UNICODE.
Actually it IS Unicode, packed in UTF-8. This is the
best of both worlds, because strings restricted to the
Actually, I don't see much point in anything complicated. Localization
should be separated from the plugin. I much prefer something like
getttext(). The plugin provides keys (generally an english string) and the
key is looked up for translations. It requires very simple processing by
the host, and frees the plugin from worrying about it. UTF8 is fine because
it tastes like ASCII to me, so I don't have to deal with it. I don't know
if it is useful to spec UTF8
* Hosts assume
all plugins to be in-place broken. Why?
* No mix output mode; only replace. More overhead...
1) Copy/mix/etc overhead on plug-in pins is *very* light
on today's GHz computers. Think of computers in 5, 10
you'll get a lot of arguments about that on this list - linux people tend
to have a 486 or pentium stashed somewhere. :)
2) This is the most important reason: programmer
failure.
This I might buy. I'm really unsure.
= are massively prone to bugs, and it has been
confirmed
in commercial products released by major companies.
As a producing company of several respected plugins, I'll have to take your
word for this. I am a big fan of simplicity.
3) Why no in-place processing ? Same reasons as
above,
especially when using block-processing. More, allowing
"in-place" modes can lead to weird configurations like
crossed-channels, without considering the multi-pins /
multi-channels configurations with different numbers
of inputs and outputs. Also, leaving intact the input
buffers is useful to make internal bypass or dry mix.
I'm leaning more towards the idea that if a plugin can do in-place
processing, it may be asked to, but it should have the ability to specifiy
for itself.
bugs when coding even simple programs. Final user
will
attach value to reliable software, fore sure.
Agreed - but they also attach value to efficient software.
* Buffers 16
byte aligned. Sufficient?
I don't know. We could extend it to 64 ? For what I've
read about 64-bit architecture of incoming CPUs, 128-bit
registers are still the widers. Enforcing 16-byte
I think this is at MOST a suggestion to host developers. It should not be
part of the API in any way.
I think about replacing all these redundant functions
by just one, callable only in Initialized state. It
would be kinda similar to the audio processing opcode,
but would only receive events, which wouldn't be dated.
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.
* Ramping API
seems awkward...
What would you propose ? I designed it to let plug-in
developpers bypass the ramp correctly. Because there are
chances that ramps would be completly ignored by many
of them. Indeed it is often preferable to smooth the
transitions according to internal plug-in rules.
Unlike some people here (I haven't gotten into it yet with them :) I think
ramping is an add-on to discrete values. I think all controls should
support discrete SET operations, and RAMP opertaions are an optional
extension that the host can use IFF the control says it supports it.
Internally, all SET operations should be ramped, even if it is a fast linear
ramp.
* It is not
specified whether ramping stops automatically
at the end value, although one would assume it should,
considering the design of the interface.
Hmmm... I start to see your point. You mean that it
is impossible for the plug-in to know if the block ramp
is part of a bigger ramp, extending over several blocks ?
Right and wrong. We've agreed that a RAMP operation is a target and
duration, but that does not imply a stop. A control must reach the target
at (or slightly before) the end-point. Beyond that, it is undefined. It
may keep ramping, or stop, or whatever. The host should send a SET event
(which cancels any pending ramps) to stop a ramp. Now it may instead be
useful to just say that ramping WILL continue, and get rid of the undefined
BS.
Yes. It is easy to implement and still fast as long
as
the polyphony is not too high, but I agree, there is
probably a more convenient solution. You were talking
about VVIDs... what is the concept ?
A VVID is a virtual voice ID. It is an unsigned int. It is owned by the
host and is global to a plugin (and optionally to the host, but plugins
don't care).
When a host wants to turn a voice on, it tells the plugin to allocate a
VVID. The plugin does what it needs to do to allocate space. The host can
then send events for that VVID. The plugin does NOT make noise for the VVID
yet, until a voice-on. When the host has sent all the initial values it
wants, it sends an event to the VOICE control, saying VOICE_ON. The plugin
can then make sound. This gives the plugin a chance to have parameters for
a voice BEFORE starting it. Which is good for things like init-velocity,
etc.
The host sends VOICE_OFF to end the voice, but the VVID is still alive. The
voice enters the release phase, and events are still tracked (as in your
model, I'm glad to see). When the voice is done, the host can de-allocate
the VVID. Same as your model.
Where we differ is these two things. Once a VOICE_OFF has been delivered,
the host may call VOICE_ON again. This re-using of a VVID provides a way to
do closer MIDI emulation, as well as portamento, possibly. The second
difference is that a VVID can be de-allocated without VOICE_OFF. A simple
step-sequencer might do this. Send initial parameters and then 'detach'
from the voice.
* Why [0, 2]
ranges for Velocity and Pressure?
As Steve stated, 1.0 is for medium, default intensity,
the middle of the parameter course as specified in MIDI
specs. But I don't get why it is "wrong" ? IMHO it isn't
more wrong than the 0..64..127 MIDI scales. There is just
a constant factor between both representation.
ick - if there is a balanced (or even unbalanced) spectrum of values, center
it at 0, please :)
*
TransportJump: sample pos, beat, bar. (Why not just ticks?)
But musically speaking, what is tick, and to what is it
this has been well explained in another message :)
* Why have
normalized parameter values at all?
Normalized parameter value is intended to show something
like the potentiometer course, in order to have significant
I rather like normalized values. I've been contemplating this problem,
myself. I don't like having to call normalize functions for every
translation, though. Can the translation overhead be minimized?
* The
"save state chunk" call seems cool, but what's
the point, really?
This is not mandatory at all, there is a lot of plug-in
which can live without. It is intended to store the
plug-in instance state when you save the host document.
I actually have this in my own notes for XAP - an optional way for the
plugin to save 'extra stuff' with a preset or document.
This is getting fun! Hopefully my DSL will be up soon, so my latency won't
be so high.
Tim