#include
<stdint.h>
I'm thinking about using that as well - but how portable is it these
days? (No big deal, though. Just get one, or hack a fake if you
platform shouldn't support it for some strange reason.)
C99 is here, use it :)
/*
* The current version of the API: read as "major.minor.micro".
*/
#define XAP_VERSION 0x000010
#define XAP_MKVERSION(maj, min, mic) (((mar)<<16) | ((min)<<8) |
(mic))
I generally prefer 8:8:16 format these days, so the minor version
field can be used as something like the serial field below. (Why have
both?)
This is the current XAP API version. Serial is the plugin version.
struct
XAP_descriptor {
[...]
uint32_t xap_id;
Why the xap_ prefix on all the fields in the struct?
Old habit - prefixing struct fields with soemthing to identify the struct.
Makes it trivial to search for all uses of ->xap_label as opposed to ->label
(which exists in several structs). If people REALLY hate it, I can let it
go. I try to always do that in exported code.
I think this belongs in version, as the micro field.
Hosts should
Version is a string - it could be "1.0" or "1.0 rc26" or
"foobar". Host can
not interpret a string. Serial is for multiple builds of the same plugin.
It makes life easier - think of it as a build number for binaries, maybe?
const char
*xap_label;
Why is this needed? (There are unique IDs, I mean...)
LADSPA compat, and it is handy. 'name' may change. It may be localized.
'label' is never localized.
const char
*xap_version;
This sort of doubles the 32 bit version field, so I'm not sure
See above - is it stil unclear?
/* master
controls and I/O */
int xap_n_controls;
XAP_control **xap_controls;
Maybe... I was thinking that you might as well require that the first
Control Port supports "maximum buffer size" (very important!) and
that kind of stuff - but where's the "first" Control Port!? If you
Which is why I threw in master controls - I don't want to have channel 0
always be master. The channel numberspace is reserved for channels.
int
xap_n_ports;
XAP_port **ch_ports;
What are these for? Shortcut to avoid the more complex system below,
when you don't really need it?
Audio ports - master audio ports.
//FIXME: how
to get the num/info currently instantiated channels?
When and why? I mean, isn't the host supposed to remember what it's
done?
That is fair. I can live with that answer
[...]
void *(*xap_create)(XAP_descriptor *descriptor,
XAP_host *host,
int rate);
Why only "rate" here - or rather, why "rate" at all? I think there
are more things that a Plugin might want to know, so it might be
better to pass this kind of info some other way.
As discussed before - setting the rate is something you ought not do often.
//FIXME: how
to enumerate errors from this?
//FIXME: return something better than void *?
If you use the state() thing, you have a *very* simple initialization
I gave thought to the state model, and while it seems nice, there are only
two states that are really needed: INACTIVE and ACTIVE. Before you are
instantiated, you have no state. After you are instantiated you are
INACTIVE. activate() makes you ACTIVE. deactivate() makes you INACTIVE.
Are there really other states that are meaningful?
* set_rate:
set the sample rate
* This method may only be called on plugins that are not active.
(Same thing as
controls that may only be changed in certain states.)
ALL plugins must support set_rate. The way I see it, controls are optional.
There is no control that every plugin HAS to define. OPtional things are
controls. It is a nice way of indicating I do/don't support 'feature'.
Non-optional stuff ought not be controls, IMHO.
int
(*xap_activate)(void *plugin, int quality);
int (*xap_deactivate)(void *plugin);
This is both hairy and incomplete, IMHO. One state() call or
What's missing? I thought about state() and decided this was complete. Did
I miss something?
have hosts calling these functions in the wrong order,
and the order
Which is why I want two states. It's easy to get right :)
XAP_event_port *(*xap_event_port)(void *plugin, int channel, int
index);
I don't think it's a good idea to require that the plugin returns a
struct. This would mean that the plugin needs to allocate memory for
ok, I'll pass a pointer to a struct to fill. I like that better, actually.
I think "now" should be in the host
interface struct. Any plugin that
deals with events will have to mess with the host struct, and either
way, "now" changes only once per block and host; never for each
plugin. (It cannot, because plugins would be out of sync with the
host WRT timestamp conversion requests and the like...)
What about buffer splitting? You then force the host to have separate host
structs for plugins?
struct
XAP_host {
//FIXME: some sort of host ID/version ?
uint32_t host_api;
Yes!
just to clarify - host_api is the XAP_VERSION the host understands. Do you
think we also need a per-host identifier? Is a string good enough? (I don't
want to have to manage host IDs and plugin IDs :)
//FIXME: how
does the host know where an event came from?
XAP_event_queue *host_queue;
It basically doesn't - but it does (hopefully) know which plugin it
What if we have a host->get_event_port(..., XAP_event_port *evport);
The host can then pass the event port it wants to the plugin with a cookie
that it can use to ID a plugin (the plugin address or something else it
wants). It's elegant - it works the same both ways.
void
(*host_alloc_failure)(void);
Why? You can just return a suitable error code...
What if the host has some fallback actions?
* Plugin calls some form of allocation NOT through the host
* allocation fails
* plugin call host->alloc_failed()
* host can abort(), or free stuff, or sleep for a while, or whatever is
appropriate.
* host returns a code to plugin: 'try again' or 'give up'.
It gives the host write a chance to trap a memory allocation failure and do
something useful but consistent with it (print __FILE__:__LINE__ or whatever
it does).
#define
XAP_RTFL_MALLOC 0x02 /* allocates/frees memory */
#define XAP_RTFL_HMALLOC 0x04 /* allocates memory through the host
*/
This should not be optional for normal plugins, IMHO. The host struct
*is* your source of resources - you may not use OS resources directly.
Well, some plugins will want to use library routines that use allocators
other than host->malloc. If a control does this, it should flag itself as
RTFL_MALLOC. HMALLOC can be noticed by smart hosts and if they support an
RT-safe host->malloc, ignored.
Speaking of driver plugins, those will also have to
tell the host
driver plugins?
> #define XAP_RTFL_HFREE 0x08 /* frees memory
through the host */
ok, I'll unify with RTFL_HMALLOC
#define
XAP_RTFL_SLOW 0x20 /* is 'slow' in general */
Well, everything is relative...
I think it would be much more interesting to have a warning hint for
plugins that are significantly nondeterministic in their CPU usage.
That's really what SLOW meant - it may run in non-deterministic time. but
SLOW is shorter than RTFL_NONDETERMINISTIC :) Suggest alternative name?
#define
XAP_CTRLHINT_AFTERTOUCH "AFTERTOUCH" //voice
#define XAP_CTRLHINT_CHPRESSURE "CHPRESSURE" //channel
We should not separate these. They're exactly the same thing, only
one is a channel control and the other is a voice control. (Which
indeed *are* incompatible due to differing addressing needs, but
that's a different story.)
ok, will unify hint
#define
XAP_CTRLHINT_PITCH "PITCH" //channel,voice
...and the optional "NOTE_PITCH" that no one will ever use, of
course. ;-)
I've completey ignored the pitch thread until I had time to digest it :)
[...]
> #define XAP_CTRLHINT_HOLDPEDAL "HOLDPEDAL" //channel,voice: bool
Why boolean? (That's more annoying than helpful
with MIDI, IMHO...
Real instruments rarely have *truly* boolean pedals and stuff, and
sometimes, you actually want to make use of that. Whether synths
check for anything but "value > 0.5" is another matter.)
ok - changed