On Wed, 2007-11-28 at 10:47 +0000, Krzysztof Foltman wrote:
I already gave you one example how it can be handled
beyond what SysEx
offers (by using interfaces). And another (by binding command numbers to
URIs, preferably on plugin load). Read my previous mail more carefully.
Using a number -> URI mapping host feature is a good idea. With one byte
for the event type in each event header you get 256 different types per
plugin instance which is probably more than enough. With two bytes you
get 65536 different types per plugin instance which is certainly more
than enough. It's not really an argument against having a completely
generic event transport though.
As I said, you didn't even provide any way to
transfer the ownership of
the buffer to the plugin, so that it doesn't have to copy it.
Actually he did. Just pass a pointer and a byte size in the buffer and
type it with the URI
http://this.buffer.is.yours.dude/ .
Anyway... Let's try to LV2ize my proposal, just as
some starting point:
struct LV2_EVENT_HEADER
{
uint32_t timestamp; // timestamp
uint8_t event_type; // event type number
uint8_t args[3]; // short-form arguments or padding
};
Event types are defined by the plugin like this:
@prefix ep: <http://foltman.com/lv2/ep> .
<http://someevent> ep:eventType [
ep:eventNumber 128 ;
ep:eventName "CH1:Note off" ;
ep:eventData ep:shortData ;
ep:eventContent
"http://www.midi.org/about-midi/specshome.shtml#ch1-noteoff" ;
].
I'd prefer to have the host define the URI -> number mappings itself so
it doesn't have to remap them for every different plugin it has loaded,
e.g. the plugin requires a host feature for every event type it uses
(
http://lv2.example.com/midi-event,
http://lv2.example.com/osc-event
etc) so the host knows which events it needs to be able to handle (and
can refuse to load the plugin if it doesn't recognise an event type),
and then passes a pointer to something like this to instantiate() for
the host feature
http://lv2.example.com/event-uri-map :
struct {
uint32_t num_mappings;
struct uri2num* mappings;
};
where uri2num is defined as
struct uri2num {
const char* event_type_uri;
uint16_t event_type_number;
};
I'd also prefer to have a single event type for MIDI and have the status
byte as part of the event data instead of having one event type for each
MIDI message type (note on, note off, aftertouch, what have you).
Also, in your proposal a single event type always has to have the same
size and there is no way to say that an event is smaller than 3 bytes
without possibly using the longData thing. I'd really prefer to have the
event size explicitly in the event header. Something like this:
struct LV2_EVENT_HEADER {
uint32_t timestamp; // timestamp
uint32_t size; // event size
uint8_t event_type; // event type number
uint8_t data[7]; // event data
};
The port buffer could contain a pointer to an array of these, and if
size > 7 the subsequent array element is used to store data, if it's
larger then 7 + 16 the one after that is also used to store data etc.
This means that each event will need at least 16 bytes instead of 8, but
I don't think that's a huge loss. If we really wanted to we could make
the data array 3 bytes instead - would a 12 byte alignment be OK on all
platforms?
Another thing I've thought about since your earlier mails is the
timestamp. I think I agree that fixed point is better than floating
point (assuming that we want subsample precision) but I'd prefer 32.32
to 16.16. 16.16 would effectively limit the audio buffer size to 65536
samples since you can't address events in buffers larger than that. It's
not a huge practical problem to have to break your processing up in 1/3
second blocks at 192kHz, but it is a bit inelegant when the core spec
allows for 2^32 samples per buffer (~6 hour blocks at 192kHz). With a
32.32 fixed point timestamp the event struct could look like this:
struct LV2_EVENT_HEADER {
uint32_t timestamp_int; // timestamp, integer part
uint32_t timestamp_frc; // timestamp, fractional part (probably
// ignored by almost everyone)
uint32_t size; // event size
uint8_t event_type; // event type number
uint8_t data[3]; // event data
};
which would be 16 bytes per event with size <= 3.
--ll