Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Not valid in C++. Ah, that was it. Back to char we go. We have some choices: 1. your original uint8_t - good because you can still do (event-data)[offset], bad because it breaks sizeof somewhat, making it totally misleading (this particular clock doesn't show the right time even once per 12 hours ;) ) 2. uint8_t[1] - variant of solution 1, just *slightly* better because you can write event-data[offset] instead of (event-data)[offset] 3. uint8_t[0] - gcc extension, possibly incompatible with non-gcc compilers (I don't care, I bet someone else does) 4. uint8_t[4] - variant of solution 1 with somewhat better sizeof behaviour (at least it returns the proper size for payload length = 4) 5. separate header structure and event-type specific event structure (with header as first field), equivalent to what Nedko proposed. Not as awkward as you probably imagine. You'll find examples in my previous mails if you have any doubt about what I mean. I'm fine with either choice. 4 and 5 look somewhat attractive, 3 is probably out of the question. Anyway, we're arguing about changing one or two lines of code in a plugin here, so if you think a single uint8_t member is a way to go, it's fine IMHO. The URI passing thing is much more interesting, as it affects 5 lines of plugin code, not 2, so it'd be better to concentrate on that one. I'd still prefer to have new URI mappings delivered to plugins (which you don't like), and URI-int mapping to be implemented in a host (which you like, IIRC). No matter what calling convention is - C++-style object or a structure with function pointer and instance pointer. At least as long as it doesn't lead to GObject-style insanity :) this struct in that case would be handy), but I guess that could just be another event type? Absolutely. char* does cause confusion though, as this thread made painfully clear earlier... Yes, and how would you refer to in-place char data? ((char *)(event-data))[0]? Do you like it? Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Is this what we have for the event/buffer header, then? http://svn.drobilla.net/lad/lv2/extensions/events/lv2_events.h char data? why not char data[0]? Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Krzysztof Foltman [EMAIL PROTECTED] writes: Dave Robillard wrote: Is this what we have for the event/buffer header, then? http://svn.drobilla.net/lad/lv2/extensions/events/lv2_events.h char data? why not char data[0]? just header please -- Nedko Arnaudov GnuPG KeyID: DE1716B0 pgpV4XRXS0CZd.pgp Description: PGP signature ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-09 at 15:06 +, Krzysztof Foltman wrote: Dave Robillard wrote: Is this what we have for the event/buffer header, then? http://svn.drobilla.net/lad/lv2/extensions/events/lv2_events.h char data? why not char data[0]? Not standard C AFAIK. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-09 at 18:08 +0200, Nedko Arnaudov wrote: Krzysztof Foltman [EMAIL PROTECTED] writes: Dave Robillard wrote: Is this what we have for the event/buffer header, then? http://svn.drobilla.net/lad/lv2/extensions/events/lv2_events.h char data? why not char data[0]? just header please How would you access the data in code? (it'd be ugly/annoying) -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-09 at 17:09 +, Krzysztof Foltman wrote: Dave Robillard wrote: How would you access the data in code? (it'd be ugly/annoying) Control byte of MIDI - data[0] First argument of MIDI - data[1] Second argument of MIDI - data[2] . duh. Try to note what something is a reply to /before/ responding ;) -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Is this what we have for the event/buffer header, then? http://svn.drobilla.net/lad/lv2/extensions/events/lv2_events.h (Ignoring the URIs, references to nonexistant symbol stuff, etc.) Cheers, -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: There are times when 'GoF Patterns in Java' style is appropriate for discussion solutions. This isn't one of them. And that's because? Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: That sounds OK. I suggest something like this: struct URI_Mapper { // call this with your URI and the host_data member - not RT safe uint16_t (map_function*)(const char* uri, void* host_data); // pass this as the second parameter to map_function() void* host_data; }; It doesn't provide a way for the host to say I'm out of IDs! though. Will that be needed? What about using int32_t and returning -1 as out of IDs? There's no difference in CPU time spent anyway, because uint16_t is returned in the same 32-bit register as int32_t is (eax, on x86). Or you can use 0 (or 65535) for error. It doesn't look like something that requires lots of thinking. Also, a reverse mapping mechanism would be helpful at times (for debugging/data monitors). I put it in my proposal for that specific reason. It shouldn't be hard to implement, either (might be a waste of memory, but of acceptable kind). Or it might be a separate feature, so that certain people won't get too confused ;) Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Not really 'nicely'... essentially implementing the exact same thing n times. Why bother? Huh??? Instantiating n times, not implementing. I know there are certain bad programmers around who would implement it multiple times or use copy-paste for code reuse, but I don't think they make LV2 hosts. Also, I would be for a requirement that numbers are always assigned (by host) sequentially starting from 0, not by - for example - hashing, because then some plugins could use small arrays to store event-type-to-handler mapping (or something-id-to-whatever-data-structure). Is that OK? The interface for the dynamic part would have to be figured out before this can be decided. ??? Well, just proposed one. I don't know if it's the best one, as nobody proposed any alternative, but it certainly works. ... did I stumble on to the Java 101 mailing list by mistake? ;) Do you think interfaces are a Java thing? Hint: IDL. Makes some explanations shorter. Also, allows us not to get distracted by details like if user data should be passed as void * or struct * or integer cookie or anything else. If there are *really* people who don't get it (shouldn't have been sleeping through OOP courses :P), let me translate it, say, to C: /*** functions in IURIRegistryObserver_funcs are only implemented by the plugin when it wants notifications about new types / struct IURIRegistryObserver; struct IURIRegistryObserver_funcs { // function in a plugin called by host on new URI registration void (*mapping_added)(struct IURIRegistryObserver *observer, int id, const char *uri); }; struct IURIRegistryObserver { struct IURIRegistryObserver_funcs *functions; // plugin may place it in a larger structure, and cast a pointer }; /* functions implemented by host, used for mapping URIs and numbers */ struct IURIRegistry_funcs { // returns -1 if new registrations are not supported int (*uri_to_id)(struct IURIRegistry *registry, const char *uri, bool create_if_absent); // returns pointer to the URI (host-owned), or NULL const char *(*id_to_uri)(struct IURIRegistry *registry, int id); // might be a do-nothing if the host doesn't support registering uris on the fly void (*add_observer)(struct IURIRegistryObserver *observer); // might be a do-nothing if the host doesn't support registering uris on the fly void (*remove_observer)(struct IURIRegistryObserver *observer); }; /* structure returned by get_registry */ struct IURIRegistry { struct IURIRegistry_funcs *functions; // host may place it in a larger structure, and cast a pointer }; // structure on host side struct IURIRegistries_funcs { struct IURIRegistry *get_registry(const char *uri); }; // structure on host side struct IURIRegistries { IURIRegistries_funcs *functions; // host may place it in a larger structure, and cast a pointer }; Or to C++: class IURIRegistryObserver { virtual void mapping_added(int id, const char *uri) = 0; }; class IURIRegistry { virtual int uri_to_id(const char *uri, bool create_if_absent) = 0; virtual const char *id_to_uri(int id) = 0; virtual void add_observer(IURIRegistryObserver *observer) {} virtual remove_observer(IURIRegistryObserver *observer) {} }; class IURIRegistries { virtual IURIRegistry *get_registry(const char *registry_uri) = 0; }; In fact, instead of IURIRegistries* you may have separate feature per registry. Doesn't matter much. Perhaps you want reference implementation, too? :D Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: I don't like having something this complicated in an extension that is going to be required if you just want to write a simple synth with a MIDI input. Hey, it's simpler than it looks like. Especially when you're writing a plugin. All you need to do is do this during activation (C++ code): // assume you have the void* for the registries feature already IURIRegistries *registries = (IURIRegistries *)feature_value; IURIRegistry *registry = registries-get_registry(http://example.com/event_types_registry;); if (registry) { midi_event_id = registry-uri_to_id(http://example.com/midi_event_type;, true); } And that's all. With C you'd have registries-functions-get_registry(registries, http://example.com/event_types_registry;) instead, but that's still 6 lines of code or less. Done on activation. Nothing truly bad. Is there really any need for adding and removing mappings dynamically? I think it is (especially adding). For inter-plugin communication - even when host doesn't support a specific plugin type, two connected plugins may. And Dave will probably agree with me here. The big question here is memory management when host doesn't support the type of data which is passed between two plugins (ie. if one plugin produces an event which is of a type which has a pointer inside, then who does free the memory? receiver just after reception, or sender during next process() call?) Just passing an array in the LV2_Feature data would be enough for me. But then, how would you search the array if it had, say, 1000 items? Linear search with strcmp on each item? Exposing the uri-id in the host lets you reuse host's lookup implementation, which may be more efficient (say, using a hash table or a tree like std::map). Not that expect that many items in an array, especially if it was restricted to event types only, and not a general mapping. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-12-05 at 09:45 +, Krzysztof Foltman wrote: Lars Luthman wrote: I don't like having something this complicated in an extension that is going to be required if you just want to write a simple synth with a MIDI input. Hey, it's simpler than it looks like. Especially when you're writing a plugin. All you need to do is do this during activation (C++ code): // assume you have the void* for the registries feature already IURIRegistries *registries = (IURIRegistries *)feature_value; IURIRegistry *registry = registries-get_registry(http://example.com/event_types_registry;); if (registry) { midi_event_id = registry-uri_to_id(http://example.com/midi_event_type;, true); } And with an array you'd have const char* const* uris = feature_data; for (uint16_t i = 0; uris[i]; ++i) { if (!strcmp(uris[i], midi_type_uri)) { midi_event_id = i; break; } } The difference is not in the number of lines but in how many new functions and types are used. The fewer the better. Sure, in practice this will probably be done with a single function call to some plugin support library, but the actual spec should still be as simple as possible. Is there really any need for adding and removing mappings dynamically? I think it is (especially adding). For inter-plugin communication - even when host doesn't support a specific plugin type, two connected plugins may. If a plugin supports an event type (I assume that's what you meant) it can list it in its RDF file, and then the host can map it and that plugin will get an ID for that event type URI, regardless of whether the host or any other loaded plugin supports that event type or not. Though I don't think it's such a great idea to load plugins that need event types that the host doesn't know anything about, for the reasons you list below. If you want some sort of generic event where the host doesn't need to bother with the actual content you could define a single event type for that - maybe something like the refcounted objects you talked about earlier. Just passing an array in the LV2_Feature data would be enough for me. But then, how would you search the array if it had, say, 1000 items? Linear search with strcmp on each item? Sure. It's just on instantiation. If you wanted to be smart you could have the host sort the array and pass the size as well and let the plugin do binary searches. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: The difference is not in the number of lines but in how many new functions and types are used. I think the cost-benefit ratio is important here. The added complexity may be worth it for me, but perhaps not for you. I hope we're still at pretty low complexity level, but that is a very subjective thing. If a plugin supports an event type (I assume that's what you meant) it can list it in its RDF file, and then the host can map it and that plugin will get an ID for that event type URI, The point is, how a previously loaded plugin (a transparent bridge or something) will be informed about the new type? In most cases it would work, though. It's just that old loaded plugins will not know the mapping for new types, which might be OK for many applications. Not that I think it's a good solution, just an acceptable one. We can ignore the issue for now, but maybe it should be picked up later at some point, when deciding about runtime extensibility. Question to Dave, do you see any way to invalidate a particular RDF triplet, then announce another one? say: PluginX is no longer a ReverbPlugin, PluginX is now a FilterPlugin. Or port 4 is no longer the plugin X's port. It's unrelated to our current discussion, I know. Perhaps some kind of rdf triplet event type, that would carry information about knowledge update, if you know what I mean? Not timestamped, perhaps :) Sure. It's just on instantiation. If you wanted to be smart you could have the host sort the array and pass the size as well and let the plugin do binary searches. So it would have to be an array of pairs (uri, number), right? Because otherwise adding any new number will mess up the numbering. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-12-05 at 16:34 +, Krzysztof Foltman wrote: Lars Luthman wrote: The point is, how a previously loaded plugin (a transparent bridge or something) will be informed about the new type? In most cases it would work, though. It's just that old loaded plugins will not know the mapping for new types. My idea is that if the plugin knows how to handle an event type it should list that type in its RDF data, and the host will assign it an integer value. A plugin that doesn't care about the event types, e.g. a generic quantizer, doesn't care about the event types, so any new mappings wouldn't matter to it. Sure. It's just on instantiation. If you wanted to be smart you could have the host sort the array and pass the size as well and let the plugin do binary searches. So it would have to be an array of pairs (uri, number), right? Because otherwise adding any new number will mess up the numbering. Yeah, if it's sorted. I don't really think it's needed though, iterating through a couple of hundreds of strings (in extreme cases) isn't that much work to do at instantiation time. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-12-05 at 04:35 +0100, Lars Luthman wrote: On Tue, 2007-12-04 at 09:42 +, Krzysztof Foltman wrote: What about this (translate it to C in your heads :) ): interface IURIRegistryObserver { // function in plugin etc. called by host whenever new URI is registered void mapping_added(int id, const char *uri); }; interface IURIRegistry { int uri_to_id(const char *uri, bool create_if_absent); const char *id_to_uri(int id); void add_observer(IURIRegistryObserver *observer); void remove_observer(IURIRegistryObserver *observer); }; interface IURIRegistries { IURIRegistry *get_registry(const char *registry_uri); }; I don't like having something this complicated in an extension that is going to be required if you just want to write a simple synth with a MIDI input. Is there really any need for adding and removing mappings dynamically? Definitely agreed. Just passing an array in the LV2_Feature data would be enough for me. Simple is nice, but somewhat closed ended. How about simply passing a function pointer which converts a string to an int? (in a Feature struct so things can be added for a more advanced symbol system in the future) Easier on the plugin author to just call a function than have to implement that search all over the place anyway. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-12-05 at 22:50 -0500, Dave Robillard wrote: On Wed, 2007-12-05 at 04:35 +0100, Lars Luthman wrote: On Tue, 2007-12-04 at 09:42 +, Krzysztof Foltman wrote: What about this (translate it to C in your heads :) ): interface IURIRegistryObserver { // function in plugin etc. called by host whenever new URI is registered void mapping_added(int id, const char *uri); }; interface IURIRegistry { int uri_to_id(const char *uri, bool create_if_absent); const char *id_to_uri(int id); void add_observer(IURIRegistryObserver *observer); void remove_observer(IURIRegistryObserver *observer); }; interface IURIRegistries { IURIRegistry *get_registry(const char *registry_uri); }; I don't like having something this complicated in an extension that is going to be required if you just want to write a simple synth with a MIDI input. Is there really any need for adding and removing mappings dynamically? Definitely agreed. Just passing an array in the LV2_Feature data would be enough for me. Simple is nice, but somewhat closed ended. How about simply passing a function pointer which converts a string to an int? (in a Feature struct so things can be added for a more advanced symbol system in the future) Easier on the plugin author to just call a function than have to implement that search all over the place anyway. That sounds OK. I suggest something like this: struct URI_Mapper { // call this with your URI and the host_data member - not RT safe uint16_t (map_function*)(const char* uri, void* host_data); // pass this as the second parameter to map_function() void* host_data; }; It doesn't provide a way for the host to say I'm out of IDs! though. Will that be needed? --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Using a uint32_t (which is a reasonable limit for number of symbols in pretty much any system) and just not using out of range numbers for the event ones doesn't seem so bad, just in case. It's one line in a comment somewhere, BFD. I'm thinking of something else - like having different domains for mappings. Like http://whatever/parameter-value gets number 10 in URI-to-event-type mapping, and number 16777216 in URI-to-global-numbers mapping. Basically, same class (URI-to-int mapper), different objects (event type URI registry, plugin port URI registry, plugin URI registry, and so on). Different dictionaries, same dictionary interface. I'm not insisting this is a good idea - just that it could solve the max 65536 types thing nicely while preserving some (not all) generality. It could also lead to a mess if somebody used wrong type ID (when the same URI is in two registries). Also, I would be for a requirement that numbers are always assigned (by host) sequentially starting from 0, not by - for example - hashing, because then some plugins could use small arrays to store event-type-to-handler mapping (or something-id-to-whatever-data-structure). Is that OK? Seems that we don't have as many ideas for design of URI-int mapping as we had for binary layout ;) What about this (translate it to C in your heads :) ): interface IURIRegistryObserver { // function in plugin etc. called by host whenever new URI is registered void mapping_added(int id, const char *uri); }; interface IURIRegistry { int uri_to_id(const char *uri, bool create_if_absent); const char *id_to_uri(int id); void add_observer(IURIRegistryObserver *observer); void remove_observer(IURIRegistryObserver *observer); }; interface IURIRegistries { IURIRegistry *get_registry(const char *registry_uri); }; Where IURIRegistries pointer is passed as a feature to a plugin. Either that, or expose individual IURIRegistry objects as separate features (perhaps deriving from http://something/uri-registry, RDF allows that, right?). Note that there is no delete function, as is doesn't seem very useful for our purposes. Otherwise, it should be fine. Thoughts? Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Tue, 2007-12-04 at 09:42 +, Krzysztof Foltman wrote: What about this (translate it to C in your heads :) ): interface IURIRegistryObserver { // function in plugin etc. called by host whenever new URI is registered void mapping_added(int id, const char *uri); }; interface IURIRegistry { int uri_to_id(const char *uri, bool create_if_absent); const char *id_to_uri(int id); void add_observer(IURIRegistryObserver *observer); void remove_observer(IURIRegistryObserver *observer); }; interface IURIRegistries { IURIRegistry *get_registry(const char *registry_uri); }; I don't like having something this complicated in an extension that is going to be required if you just want to write a simple synth with a MIDI input. Is there really any need for adding and removing mappings dynamically? Just passing an array in the LV2_Feature data would be enough for me. --ll PS. Who keeps breaking the list headers? About 50% of the time Reply to list doesn't work on mails in this thread. signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Mon, 2007-12-03 at 03:32 +0100, Lars Luthman wrote: On Sun, 2007-12-02 at 21:08 -0500, Dave Robillard wrote: On Mon, 2007-12-03 at 02:29 +0100, Lars Luthman wrote: For the URI-int mapping, I've changed my mind and vote for a separate Feature that you have to explicitly list in the RDF file. It may be useful for other extensions where you need to have arbitrarily extendable stuff that you want to match to your known data reasonably fast. The actual mapping would be done by the host by passing a NULL-terminated URI array as the data for that Feature, where the index of an URI in the array is the associated integer value. Or maybe we should pass the size of the array as well and require that it's sorted in lexicographical order, so a clever plugin can do a binary search? Not really important, it will only happen at instantiation time anyway. Fully agreed. int-URI mapping is generic and widely useful. This does have me thinking about range though.. you can have an awful lot of URIs in a system. I guess the generic extension can map to, say, uint32_t, but the event extension can specifically say the event type URIs need to fit in uint16_t? Maybe doesn't matter but I think allowing a huge number of URIs is a good idea if it's just a single (or half) word, just in case. I doubt anyone is ever going to use more than a couple of tens of URIs with this. What if, say, symbols are used as identifiers to shared data structures? If you've got a symbol per sample or something, it could get high fast. OSC has a specific symbol type we could use to get super fast comparison/lookup of things keyed by symbol. Parameters in each message being a symbol? That can definitely get high fast... Using a uint32_t (which is a reasonable limit for number of symbols in pretty much any system) and just not using out of range numbers for the event ones doesn't seem so bad, just in case. It's one line in a comment somewhere, BFD. It's really only for things that will be compared and processed in a realtime thread, non-time-critical code might as well use strings directly. I'm fine with limiting the total number of URIs (or symbols or names or whatever) to 2^16 in the URI mapping extension itself to avoid annoying and inelegant constraints in the event port extension. If it is just for URIs, we could maybe take advantage of prefixes and save some space in that table? lv2:Plugin is a lot nicer to deal with in all cases than http://lv2plug.in/ns/spec/lv2core#Plugin Hm. Feels a bit hacky. Memory isn't really an issue unless you use tens of thousands of these which seems a bit unlikely, and if it's just for code prettification you might as well use a macro. #define LV2(sfx) http://lv2plug.in/ns/spec/lv2core#; sfx const char* my_uri_map[] = { LV2(Plugin), LV2(Baaah), NULL }; Yeah, this extension probably shouldn't be concerned with the actual contents of the strings/symbols anyway (they can still be prefixed names in some cases). Just a thought.. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
I think the version you (probably) proposed is: struct LV2_EVENT_HDR { uint32_t frame, subframe; unsigned int size:24; unsigned int type:8; // put data here, but header size+data size must be a multiple of 8 }; plus 8-byte alignment requirement (ie. header size+payload size must be an integer multiple of 8), is going to work. Either that, or 16 bits for both size and type. Or 16 bits for size and 8 bits for type, and the remaining byte may be used for char field in payload (5 byte payload for free). All three are more or less equally good/bad. MIDI would look like this: 12 bytes header, (up to) 3 bytes content, 1 byte padding - which is fine. Same for pointer-only data on 32-bit architecture, for 64-bit, there would be a padding of 4 bytes followed by 8-byte pointer. Not very elegant, but perfectly acceptable. Plus, the compiler takes care of generating padding. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Mon, 2007-12-03 at 02:29 +0100, Lars Luthman wrote: On Sun, 2007-12-02 at 19:39 -0500, Dave Robillard wrote: On Sun, 2007-12-02 at 22:25 +, Krzysztof Foltman wrote: Lars Luthman wrote: with data padded to 4+N*16 bytes 4 + N*8. 16 is excessive, while 4 is not enough (to be able to store aligned doubles or 64-bit pointers). But, yes, I think header is ready. Right, Dave? Works for me, I get my timestamp :) On to the buffer header (same as MIDI? more clever?) and the URI-int mechanism.. Aligned to 8 bytes sounds good to me too. Done. I'm happy with a port buffer header that has the same layout as the current MIDI one (I should be...). For the URI-int mapping, I've changed my mind and vote for a separate Feature that you have to explicitly list in the RDF file. It may be useful for other extensions where you need to have arbitrarily extendable stuff that you want to match to your known data reasonably fast. The actual mapping would be done by the host by passing a NULL-terminated URI array as the data for that Feature, where the index of an URI in the array is the associated integer value. Or maybe we should pass the size of the array as well and require that it's sorted in lexicographical order, so a clever plugin can do a binary search? Not really important, it will only happen at instantiation time anyway. Fully agreed. int-URI mapping is generic and widely useful. This does have me thinking about range though.. you can have an awful lot of URIs in a system. I guess the generic extension can map to, say, uint32_t, but the event extension can specifically say the event type URIs need to fit in uint16_t? Maybe doesn't matter but I think allowing a huge number of URIs is a good idea if it's just a single (or half) word, just in case. Though that's starting to sound like we're implementing a symbol system... probably going to be wanting one of those at some point regardless. Is that what this really is? (probably doesn't affect implementation much, just a hypothetical question). If it is just for URIs, we could maybe take advantage of prefixes and save some space in that table? lv2:Plugin is a lot nicer to deal with in all cases than http://lv2plug.in/ns/spec/lv2core#Plugin -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-02 at 21:08 -0500, Dave Robillard wrote: On Mon, 2007-12-03 at 02:29 +0100, Lars Luthman wrote: For the URI-int mapping, I've changed my mind and vote for a separate Feature that you have to explicitly list in the RDF file. It may be useful for other extensions where you need to have arbitrarily extendable stuff that you want to match to your known data reasonably fast. The actual mapping would be done by the host by passing a NULL-terminated URI array as the data for that Feature, where the index of an URI in the array is the associated integer value. Or maybe we should pass the size of the array as well and require that it's sorted in lexicographical order, so a clever plugin can do a binary search? Not really important, it will only happen at instantiation time anyway. Fully agreed. int-URI mapping is generic and widely useful. This does have me thinking about range though.. you can have an awful lot of URIs in a system. I guess the generic extension can map to, say, uint32_t, but the event extension can specifically say the event type URIs need to fit in uint16_t? Maybe doesn't matter but I think allowing a huge number of URIs is a good idea if it's just a single (or half) word, just in case. I doubt anyone is ever going to use more than a couple of tens of URIs with this. It's really only for things that will be compared and processed in a realtime thread, non-time-critical code might as well use strings directly. I'm fine with limiting the total number of URIs (or symbols or names or whatever) to 2^16 in the URI mapping extension itself to avoid annoying and inelegant constraints in the event port extension. If it is just for URIs, we could maybe take advantage of prefixes and save some space in that table? lv2:Plugin is a lot nicer to deal with in all cases than http://lv2plug.in/ns/spec/lv2core#Plugin Hm. Feels a bit hacky. Memory isn't really an issue unless you use tens of thousands of these which seems a bit unlikely, and if it's just for code prettification you might as well use a macro. #define LV2(sfx) http://lv2plug.in/ns/spec/lv2core#; sfx const char* my_uri_map[] = { LV2(Plugin), LV2(Baaah), NULL }; --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 09:45 +, Krzysztof Foltman wrote: Dave Robillard wrote: We could use float I guess to save a bit of space, but I definitely prefer floating point. Fixed point is just a PITA, modern CPUs are much faster at FP anyway, why bother? 1. The modern CPUs are much faster at FP thing is a myth that shows up here and there and is usually taken as a gospel by people who don't know what they're doing (or what their CPU is doing). The Fixed point is faster than FP thing is a myth that shows up here and there and is usually taken as gospel by people who don't know what they're doing (or what their CPU is doing) ;) Fixed point is a PITA. Time based effects and whatnot do math on time stamps, and doing math on fixed point is a massive nuisance compared to floating. With floating, if you want to time stretch by a factor of 2 multiply by 2. Fixed? Well, first write your fixed point arithmetic library, then. Unless there's very /significant/ advantages to bothering with it, well.. why bother with it. I've advocated high precision floating point in the past because 'groove' effects and such are obviously much better off with such a representation. At the end of the day, for basic synths etc. timestamp things aren't going to be a remotely significant part of the CPU time spent by a plugin; I doubt the fixed point nuisance is worth it. Premature optimisation is the root of all evil... -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 11:23 +0100, David Olofson wrote: On Friday 30 November 2007, Krzysztof Foltman wrote: [...several points that I totally agree with...] If you use integers, perhaps the timestamps should be stored as delta values. That would seem to add complexity with little gain, though I haven't really thought hard about that... It does have the significant advantage of eliminating the hard upper bound on the range of time that can be present in a buffer (and with 'null' events, eliminates any such limit entirely, ala SMF). More annoying to work with though.. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 10:42 +, Krzysztof Foltman wrote: Dave Robillard wrote: Sigh. In /this case/ they are the same, because the data directly follows Sort it out with gcc, not with me :) The struct does not actually exist, its sizeof is irrelevant. And if you meant that event-buf is still a pointer, buf the value of event-buf will always be (buf)+1 [snip] The data is at buf. That's all. The sole significance of that struct member is that the data is at buf. It doesn't matter in any way whatsoever what that struct member is. Honest. ;) Read the comment... the buffer format couldn't be more clear. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 11:10 +, Krzysztof Foltman wrote: Dave Robillard wrote: MIDI. In short: don't rock the boat; all this Jack/LV2 MIDI stuff is still getting off the ground... Well, I see your point. Again, I'll start worrying (or defining extensions) when it'll be necessary. Don't you think that there should be a sort of extension TODO list? As you see, I already have certain things in mind, and so do others. If you really think it would make a difference to compile a list of what people have in mind, go nuts. That's right. On the other hand, sometimes extension designed by some guy and then adopted because there was nothing better available, limits growth of the scene/market (there's enough resistance to a change to prevent better standards from being adopted). True, but these things are generally discussed on the list anyway (as they are right now). If that does happen, you can just make something better. Generally, host authors won't support things they think are stupid/short-sighted/whatever, so the problem takes care of itself for the most part. Maybe all it lacks is a set of well-thought-out well-defined extensions to start things up? Or maybe the invisible hand of the scene will sort things out by itself. You speak as if these two things are different, somehow? :) The invisible hand of the scene created all this... Quite a bit of thought has gone into hypothetical extensions to make sure the core spec is sound, but the complete independence of extensions from the core spec is a fundamentally good thing. Everything LV2 should be tackled in terms of the smallest sub-problems possible (hence e.g. my oppositing to cramming event definitions into the generic event transport stuff). Read the archives of this list (or, even better, the GMPI one..) enough and it's pretty clear that's the only way we can actually get things done 'round here... and, hey, because of it we're getting things done around here right now. We have working plugins, MIDI, dynamic parameters, OSC, toolkit agnostic embeddable GUIs; we're soon getting generic extensible events, control ramps, message-based stuff GMPI has a few years worth of pointless bickering and arguing on a mailing list archive somewhere. QED. Love or hate all this extensible talk and RDF stuff, the proof is in the pudding. I love it when things work out. :) -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 14:03 -0500, Dave Robillard wrote: We have working plugins, MIDI, dynamic parameters, OSC, toolkit agnostic embeddable GUIs; Clarification: The GUI extension is toolkit agnostic, the GUIs are not. Not that it matters to me. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Read the comment... the buffer format couldn't be more clear. That was the problem - the comment contradicted the code, but - as human language is usually more ambiguous than C code - I assumed that the true intention is in the code, not in the comment. Just to clear it up. And as for fixed point timestamps - yes, I admit(ted) the fixed point is *usually* more PITA than floating point. But it all depends on context. I can't say fixed point is always bad, because for some things it's really good :) Say, wavetable oscillators - you can't really get good performance when you store sample position as floating point. Just try it :) However, I'm not advocating for using fixed point numbers everywhere. Let me give a concrete example, so that you know *why* I'm so stubborn about this issue. Perhaps I'm making a wrong assumption here. uint32_t optr = 0; while(icount) { uint32_t timestamp = events[i].timestamp; // just saving on typing here :) uint32_t nsamples = (timestamp 16) - optr; // fixed point case uint32_t nsamples = fast_f2i(timestamp) - optr; // floating point case if (nsamples) { process_samples(optr, nsamples); // buffer offset, nsamples optr += nsamples; } float frac_pos = (timestamp 0x) * (1.0 / 65536.0); // fixed point case float frac_pos = timestamp - floor(timestamp); // floating point case process_event(events[i], frac_pos); i += (events[i].size + 7)~7; } process_samples(optr, output_size - optr); This is how I imagine an event processing loop (main process function) in an average plugin. Note that there's not a lot of difference between fixed point and floating point (assuming we want fractional position as float number, which is a fair assumption, IMHO). However, floor is slow (though you need it only if you need fractional position). Float-to-int is slow, even if implemented via bit-manipulation trickery. Shift is fast. AND is fast. float*int is fast, at least faster than floor() :) As you see, I'm not pushing on fixed point use everywhere. Just in that particular case, because - in my opinion - it's worth it. The loss of precision is pretty acceptable (1/65536th of a sample? that's 0,34 nanosecond resolution with sr = 44100, pretty impressive in my opinion, I think DRAM access times are more than 0,34 nanoseconds, just to give you some point of reference :) ). The code is very similar, only a bit faster. Come on, we're arguing about changing two lines of code here (with the rest - process_samples, process_event etc - being exactly the same!). Don't present it as if I wanted everyone to code thousands of lines of fixed point handling libraries. THAT would be both stupid and evil. I obviously don't want that. I'd gladly sacrifice very large runs to not have to use the horribly inefficient things like float-to-int - and you don't really have to sacrifice them (just add a 65536 sample time period have pased event type). I think it's not premature optimization, it's more of not forcing everyone waste CPU cycles. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 14:09 -0500, Dave Robillard wrote: On Fri, 2007-11-30 at 16:21 +0200, Nedko Arnaudov wrote: Krzysztof Foltman [EMAIL PROTECTED] writes: Lars Luthman wrote: non-standard hacks in a specification. But with the current event header proposal we don't have a pointer _or_ a flexible array member in it, so this discussion is sort of pointless. So, basically, we have a choice between: struct LV2_EVENT_HEADER_LLKF { uint32_t timestamp; // 16:16 uint16_t payload_size; uint16_t event_type; }; Might as well break the time stamp into two separate uint16_t's and make life easy. Agreed. Most plugins won't care about the fractional part. The only drawback I can think of is that on a platform that aligns struct members to 32 bits you won't be able to load the complete timestamp as a uint32_t without some shifting and |ing, but that will probably be a special case anyway. Having the timestamp as two separate members makes everything completely self-documented. Please, please let this silly 'type of the data member' angle of conversation die... :) Hereby humbly requesting that: // data follows here be the last thing in the event struct for the purposes of this conversation, since it's irrelevant and not a point of debate Agreed. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 20:13 +0100, Lars Luthman wrote: On Sat, 2007-12-01 at 14:03 -0500, Dave Robillard wrote: We have working plugins, MIDI, dynamic parameters, OSC, toolkit agnostic embeddable GUIs; Clarification: The GUI extension is toolkit agnostic, the GUIs are not. Not that it matters to me. Ah, right. I was debating even mentioning that bit :) Has the world beyond IRC seen your videos? You should post one for the list maybe.. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Krzysztof Foltman wrote: i += (events[i].size + 7)~7; Oops, that should be: i += (event[i].size + 7) 3; :) Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 21:49 +0100, David Olofson wrote: On Saturday 01 December 2007, Dave Robillard wrote: [...] Taking a step back, it would be nice to have these events (being generic) able to use something other than frame timestamps, for future dispatching/scheduled type event systems. [...] I'm not sure about the scope of this LV2 event system, but the idea of using timestamps related to anything other than audio time (ie buffers, sample frames etc) seems to be a violation of the idea that events are essentially structured control data or similar - ie anything that's locked to the audio stream. Obviously MIDI-like events to be handled in-band in the audio stream would use frame time stamps, yes. You can't do everything with in-band realtime events though... say, firing around video frames in events for processing (not at all unheard of in pd etc). You can't be doing complex image processing in the audio thread, that's idiotic. ie yes this is a wider scope for events, but it's necessary. I'd rather not go on this digression to be honest, multi-context plugins/patcher environments with events crossing contexts is not a trivial subject. The time stamps being up to the task would sure be nice though... All I ask for is a few measley bits :) That I can point to widely accepted standards (one of which is very, very close in domain to this event stuff) that use timestamps of this sort is telling.. Anyway, making the frames part 16 bits screws up parity with Jack MIDI. We already have a uint32_t frame timestamp. We want fractional, so stick a fractional part in there, voila. The host just sets the fractional part to 0 from Jack, things can use the fractional bit (or not) as they please. Simple. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 22:19 +0100, David Olofson wrote: On Saturday 01 December 2007, Dave Robillard wrote: [...non audio time timestamps...] All I ask for is a few measley bits :) That I can point to widely accepted standards (one of which is very, very close in domain to this event stuff) that use timestamps of this sort is telling.. Sure, I have no problem with that. I just don't want to make sure we're not confusing this with types of event communication that just doesn't fit in the model of this particular event system. I mean, it's kind of pointless to pollute *this* event system with features that you need a completely separate LV2 extension to actually use. :-) (Well, unless that other LV2 extension can use this one as a transport layer in some sensible way, maybe.) Understandable. I'm thinking the events themselves can be the same (they're so open-ended, why not..) but the transport mechanism would just be different. Some kind of one-event-at-a-time thing instead of a big flat buffer. There's also the sharing of transport layer and 'crossing' contexts, yes. The way I see it in both cases the generic event is just a time stamp, size, and some data, so might as well make this one good enough. Precise timing resolution never hurt anyone anyway. Anyway, making the frames part 16 bits screws up parity with Jack MIDI. We already have a uint32_t frame timestamp. We want fractional, so stick a fractional part in there, voila. The host just sets the fractional part to 0 from Jack, things can use the fractional bit (or not) as they please. Simple. Yes, that makes sense to me. It seems like the general case (even when making use of sub-sample timing) favors separate integer and fractional parts, and then, why not just use an int for each? Yep. Something ala: struct LV2Event { uint32_t frames; uint32_t subframes; foo_ttype; uint32_t size; // data follows here }; Best choice for order and foo_t for alignment? Making it smaller than 32 makes size's (and data) alignment suck, but 2^32 types of events at one time is pretty nuts. Cheers, -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 14:56 -0500, Dave Robillard wrote: No argument that it's faster. With separate int fractional parts it's even probably cleaner. I may be convinced... Taking a step back, it would be nice to have these events (being generic) able to use something other than frame timestamps, for future dispatching/scheduled type event systems. OSC uses 64 big fixed point absolute time stamps, giving a resolution of about 200 picoseconds absolute time (epoch January 1, 1900). This is equivalent to NTP time stamps apparently - ie huge precedent. Whether stamps are in frames or absolute time could be a property of the port. Maybe we should up it to 64 bits fixed point? The additional 32 bits aside, doing that gives us all the range or precision in frames needed, and the ability to map to a widespread absolute timestamp format, and parity with OSC. That's a lot of pros... (This may sound a bit esoteric, but to do something like Max right, you need absolute time stamps). Currently in MIDI we have 64 bits in there anyway.. I never thought of using fixed point, but the OSC/NTP parity and potential for very precise absolute time stamps is very, very tasty to me. Also, the frame part would be a uint32_t - equivalent to Jack MIDI timestamps, another win (and cutting out more conversion overhead). Another thought on 32:32 fixed point with a 'timestamp type' concept: tempo time. You could use these stamps at beats:ticks time stamps with excellent range/resolution. Actually now that I think of it I've run into exactly this problem in Ardour MIDI with floating point tempo-time-stamps. 32:32 stamps like this would be excellent in a sequencer, I would definitely use them in Ardour. With the same event struct we can get: - Sub-sample accurate frame timestamps (Jack MIDI stamps + resolution) - Absolute timestamps with sub-nanosecond resolution (OSC equivalent) - Tempo relative timestamps with much higher resolution than any existing MIDI/sequencer/etc gear I know of ++ -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sat, 2007-12-01 at 23:23 +0100, Krzysztof Foltman wrote: Dave Robillard wrote: Taking a step back, it would be nice to have these events (being generic) able to use something other than frame timestamps, for future dispatching/scheduled type event systems. I think there's certain advantage to using sample frames for events in the typical audio plugin situations. On the other hand, it doesn't cover all weird stuff people will want to do. Agreed I could even think of two event ports - one for typical frame-based stuff, other for absolute nanosecond time or what not. Keep in mind that converting nanoseconds to frames in typical case would be very very slow (64-bit divisions, or at least multiplications) What is the point of having two separate event definitions when one will do for both, though? So, if we need something else than sample-based timing, we should do it elsewhere (by defining another event type, or something). Trying to fit everything in - audio, video, networking, industrial equipment, car engine control etc - is highly likely to fail. As listed in my previous mail, any audio plugin situation I can conceive of is covered. Specifically where and why will it fail? This logic applies for things that try to be universal by being complex, not things that do it by being simple. OSC uses 64 big fixed point absolute time stamps, giving a resolution of about 200 picoseconds absolute time (epoch January 1, 1900). This is equivalent to NTP time stamps apparently - ie huge precedent. Also, huge overkill, in my opinion :) It might be needed in some cases, but demanding everyone to use this resolution is bad. You can freely ignore this and just consider it a frame stamp for your purposes. Whether stamps are in frames or absolute time could be a property of the port. You may not like it, but I would actually like two kinds of ports: Why? The cons are obvious, what are the pros? A few bits? I'm more comfortable with 8-byte header, just because it lets us use headers as unit of data (payload is always padded to 8 bytes, think pointers on 64-bit machines). As in the code example I've posted (minus the bug). With 12-byte headers, we get some ugly pointer arithmetic (and pointer alignment problems on 64-bit machines). With 16-byte headers, we either count in 16-byte units (just think of 1-byte MIDI messages here :) ), or align to 8 and get ugly pointer arithmetic. You need to traverse the actual event data anyway, there's no difference. Noone will ever be skipping through an array of just headers like this (flat buffer thing again..). Everything that allows plugin loops to be simple and readable and doesn't waste CPU/bus cycles or (whether on misalignment penalties, long divisions, float-to-int, 32 bytes per average event) is going to be fine to me. It's just that... well, you can see for yourself, there are not many solutions that satisfy all of those criteria. A byte here and a byte there in the header makes no difference to any of this. We should try to keep it small as possible, yes, but it doesn't affect what the using code looks like at all. Currently in MIDI we have 64 bits in there anyway.. I never thought of using fixed point, but the OSC/NTP parity and potential for very precise absolute time stamps is very, very tasty to me. Tastes like 64-bit division, which as far as I know involves a function call on average x86 platform ;) Good for some stuff (networking, audio plus video), absolutely horrible for others (think granular synthesis which has dense events). Well... good thing you don't have to use those stamps for the 'others'. Again, I'm not proposing anyone use OSC style stamps in place of frame stamps... Also, the frame part would be a uint32_t - equivalent to Jack MIDI timestamps, another win (and cutting out more conversion overhead). I like this - just don't like the consequences on header size :) Hard to find a clear winner here. I think being directly compatible with Jack, with higher resolution, while also capable of being an absolute or tempo based time stamp is a pretty clear winner when the only downside is 4 bytes. 16:16 fixed /decreases/ the stamp range compared to Jack's by a factor of 2^16 remember. That's a big decrease. I've directly run into problems with the MIDI timestamp type in Ardour (I was wrong, floating point stamps were stupid, didn't realise it before this conversation though). 32:32 fixed stamps solve all the problems. Ardour will be able to use the same event struct from Jack through ringbuffers, to tempo based time, through plugins, right down to the file writing (the tempo stuff is going to have to use stamps like this anyway). It would be perfect... Ingen could transparently read, schedule, process, and output absolute stamped OSC messages with minimal jitter and latency compensation. It would be perfect... In short; I've hit a lot of
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-02 at 00:15 +, Krzysztof Foltman wrote: Dave Robillard wrote: What is the point of having two separate event definitions when one will do for both, though? Perhaps, if we go 64-bit (although I still think it's overkill!) it might make sense to make a timestamp a sort of an union, so some event types will use one int64_t member (say, 200 picosecond units) instead of integer+fractional? Just cosmetics. So we'd have something like: struct LV2_EVENT_HEADER { union { int64_t timestamp64; // for event streams that use one large number as timestamp, like OSC??? struct { int32_t timestamp; int32_t timestamp_fract; }; }; uint32_t type; // don't see any other choice but int, opaque pointer won't fit on x86-64, plus, index is all we need, given a good URI mapping mechanism uint32_t size; }; Guess we could. I don't really want to mess up the struct, but whatever. The number of bytes is all that actually matters, some URI somewhere will define what they are. union can make things nicer, so why not... not really relevant. With 16-byte granularity for payload (ie. size is rounded up to nearest multiple of 16 to determine next header address). Or perhaps 8-byte, winning some memory at cost of messier code. I still don't see where you're getting all this messy code stuff. Adding 8 to a pointer isn't any more or less messy than adding 16 to a pointer. Or 4, or 2, or 3, or 17, or whatever. pointer + number. There will surely be a macro to round any value up to whatever that number should be. Alignment is purely a performance issue. Why? The cons are obvious, what are the pros? A few bits? Using a small structure when a small structure is just fine. Ignoring my 'extended' uses, halving the stamp size vs. Jack MIDI isn't really fine. Dealing with that properly would be a PITA (a real one, not a 'adding a different number' one ;) ) When you have lots of events, this may be very important. In other situations, no clear advantages of 8-byte struct over 16-byte. A byte here and a byte there in the header makes no difference to any of this. We should try to keep it small as possible, yes, but it doesn't affect what the using code looks like at all. Well, let me try to rephrase it, because I sense a huge miscommunication here. When payload is always aligned to header size (8 bytes in my case), the loop can look just like this: for (size_t i = 0; i count; i += (events[i].size+7) 3) { // use events[i] here, cast to event type struct if needed } or (events[i].size+15)4 with 16 byte header Yeah, because everyone is going to write the loop with (events[i].size +7) 3 in it. Geeze. :P A bitshift versus an addition is hardly significant. Short, simple, efficient. , weird looking, insignificant anyway. Well... good thing you don't have to use those stamps for the 'others'. Again, I'm not proposing anyone use OSC style stamps in place of frame stamps... So we end up with two-three event stream types, each using different timing scheme. Not as bad as it sounds, certainly, because each of these types will be used for different things. What I proposed was to use different event structure layout for those event streams. As I said above, the differences between those streams are so huge, that translation between 8-byte and 16-byte headers will be the least of the problems :) 3 separate and incompatible extensions and event structs everyone has to deal with so you can have a bit shift in a for loop and save a tiny fraction of a nanosecond processing time per event... somewhere is not quite a convincing enough argument for me to be happy dealing with 5 different event structs (and all the translation) instead of 1 ;) 2 structs, not 5. And the translation will have to be there anyway, unless you expect sample-based plugins to read OSC-style timestamps. Frankly if anyone knows this would be a PITA, it's me, and it would be a PITA. It would make the overall thing more complicated for no good reason. Ringbuffering events around and changing the time base and such would be extremely annoying (not to mention significantly more expensive due to all the copying from struct type A to struct type B) versus just using the same struct everywhere. Then you just change the time stamp in place. Easy, fast. In apps that do have to do things like this the performance hit of copying structs around is way, way more significant than a shift here vs an add there - in both space and time. Bit... odd. Sure, saves 2 bytes, but at the cost of throwing out that OSC stamp compatibility (which I guarantee will be actually useful). Plus... well, 2 bytes. Recentish chips can keep a few million of them in cache. :) By saving 2 bytes, it saves 8-16 bytes. Magic, isn't it? :) Only because of your excessive padding and irrational desire to apply the operator
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: I still don't see where you're getting all this messy code stuff. Adding 8 to a pointer isn't any more or less messy than adding 16 to a pointer. uint32_t *p = some_int_array[0]; p += 7; Q: Where does p point now? A: 28 bytes ahead of its previous value. That's where the elegance-related problem lies (IMO). If the struct size is 16, and you want to increase the pointer by 8 bytes, you need to cast to char*, increase by 8, and cast back to LV2_EVENT_HEADER. That's what was (and is) bugging me. i += (events[i].size+7) 3; is a bit nicer than: p = (LV2_EVENT_HDR *)((char *)p + ((p-size + 7) ~7)); to me. Still, it's just one line of code, and can be put inside of a macro or something, so the messiness might be perfectly bearable. I might also keep the current pointer as char * for easy incrementing, and cast to LV2_EVENT_HEADER * (or whatever) when needed. Putting things in perspective, it's not even nearly as ugly as what goes on inside plugins' inner loops, so it might be perfectly acceptable ;) Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Sun, 2007-12-02 at 01:26 +, Krzysztof Foltman wrote: Dave Robillard wrote: I still don't see where you're getting all this messy code stuff. Adding 8 to a pointer isn't any more or less messy than adding 16 to a pointer. uint32_t *p = some_int_array[0]; p += 7; Q: Where does p point now? A: 28 bytes ahead of its previous value. Congratulations, you can add and multiply. ? That's where the elegance-related problem lies (IMO). heh. If the struct size is 16, and you want to increase the pointer by 8 bytes, you need to cast to char*, increase by 8, and cast back to LV2_EVENT_HEADER. That's what was (and is) bugging me. i += (events[i].size+7) 3; is a bit nicer than: p = (LV2_EVENT_HDR *)((char *)p + ((p-size + 7) ~7)); Yeah, that or p = (LV2_EVENT*)(p.data + PAD(p.size)) you have an unhealthy bit manipulation fetish or something :P Anyway, I agree the alignment sucks. Not for these... colourful reasons, but because accessing things that aren't aligned is slow. The real problem (relative to the old original events) is the type field. It doesn't need to be very large, so it takes up a little bit of space and throws everything out of alignment regardless of whether we choose 32 or 64 bit stamps. struct LV2EventA { uintn_t frames; // good alignment uintn_t subframes; // good alignment uint32_t size; // good alignment uint8_t type; // bad alignment // data // bad alignment } // 9 or 13 bytes (ick) How do we fix that? The only solution is to cram type into the space taken up by size or subframes. If I had to pick, I'd choose doing it to size, but OSC blobs (among other things) have a 32 bit length so that sucks a bit. The max length if we use uint16_t is not even a meg Taking over subframes space means tempo time wouldn't be sample accurate, which is a show-stopper. It's a big chunk for time, sure, but time is the most important information, the rest is framework. struct LV2EventB { uintn_t frames; uintn_t subframes; uint16_t size; uint16_t type; // data (32-bit aligned) } // 8 or 12 bytes is the best so far IMO, but that's an awful lot of space wasted on type which isn't even likely to be 10. Is doing something really weird like 24 bytes for size and 8 bytes for type worth it for the much nicer payload size limit (16 megs)? Then again, UDP packet size (which OSC typically travels in) is typically well within uint16_t's range, and if messages are actually getting that big, they should probably be pointing at some shared resource to avoid the potential copying. All things considered LV2EventB with n=32 seems best to me.. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 10:42 +, Krzysztof Foltman wrote: In other words - just remove the pointer from the generic header, or replace it with buf[], a zero-length in-place array, as opposed to 4- or 8-byte pointer - and nobody's hurt ;) If you have a flexible array member in a struct that struct is an incomplete type and you can not put it in an array, according to the C99 standard. Not to mention that flexible array members aren't even allowed in C++. Yes, GCC allows it anyway unless you compile with -std=c99 (or c++98) -pedantic-errors, but it still wouldn't be very nice to use non-standard hacks in a specification. But with the current event header proposal we don't have a pointer _or_ a flexible array member in it, so this discussion is sort of pointless. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Sigh. In /this case/ they are the same, because the data directly follows Sort it out with gcc, not with me :) And if you meant that event-buf is still a pointer, buf the value of event-buf will always be (buf)+1, then we will always have a pointer dereference. Which is (slightly) less efficient and usually unnecessary. If you define a type that passes large data, put the pointer in payload, it will only have to be dereferenced for that particular event type. And if you meant that it's usually (buf)+1 but not if data is large then we also need to check if we need to skip the (possibly non-existent) data block or not. As in: if (event-buf == (char *)(1 + event-buf)) { // data are in-place, skip then event += some_function_of(event-size); } else { // data are somewhere else event += some_function_of(header_size); } In other words - just remove the pointer from the generic header, or replace it with buf[], a zero-length in-place array, as opposed to 4- or 8-byte pointer - and nobody's hurt ;) And by the way - maybe it would make sense to use some sort of performance testing framework to check which event headers are faster and how much impact does it really have? If the difference on a gain plugin (or say, wave playback - to test cache-intensive stuff) is within, say, 10%, with 256-sample buffers and event density of 8 samples/event, then there's not much to argue about. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Nedko Arnaudov wrote: And supplying things to plugins that they should not (and do not) care about is not confusing? And we assume that plugins would be more than hosts after all... Honestly, I prefer to have unnecessary information which I need to ignore, than fields with context-dependent meaning. Context-dependent meaning is usually more evil. Buffer header is not the place to squeeze bits. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Friday 30 November 2007, Krzysztof Foltman wrote: [...several points that I totally agree with...] If you use integers, perhaps the timestamps should be stored as delta values. That would seem to add complexity with little gain, though I haven't really thought hard about that... It seems more straightforward to just use sample frame offsets when sending; you just grab the loop counter/sample index. However, in the specific case of my instant dispath architechture, you'd need to look at the last event in the queue to calculate the delta - but then again, you need to touch that event anyway, to set the 'next' field... (Linked lists.) No showstopper issues either way, I think. When receiving, OTOH, deltas would be brilliant! You'd just process events until you get one with a non-zero delta - and then you process the number of sample frames indicated by that delta. (Obviously, end-of-buffer stop condition must be dealt with somewhere. Adding a dummy stop event scheduled for right after the buffer would eliminate the per-audio-fragment check for fragment_frames remaining_buffer_frames.) Perhaps fractional parts could be just stored in events that demand fractional timing (ie. grain start event), removing that part from generic protocol. That's another idea I might steal! ;-) I'm not sure, but it seems that you'd normally not want to drive a sub-sample timestamped input from an integer timestamped output or vice versa. An output intended for generating grain timing would be concerned about generating events at the exact right times, whereas a normal control output would be value oriented. This may not seem to matter much at first, but it makes all the difference in the world if you consider event processors. With pure values, you might want to add extra events or even regenerate the signal completely, but this would break down when controlling something that relies on event timing. Might be worth considering even in non modular synth environments, as you might want to edit these events with in sequencer. This is starting to sound like highly experimental stuff, though. :-) Perhaps we're still overlooking something. I'd want to try actually implementing some different, sensible plugins using this before I really decide what makes sense and what doesn't. Granular synthesis is about the only application I can think of right now that *really* needs sub-sample accurate timing, so that's the scenario I'm considering, obviously - along with all the normal code that doesn't need or want to mess with anything below sample frames. //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman [EMAIL PROTECTED] writes: According to the core spec, hosts are only required to pass the features to a plugin that the plugin lists as required in its RDF data. In practice many hosts will probably keep one static feature array with all the features they supports that they pass to every plugin instance but it's not necessary. This is what zynjacku does, allocate list of all available host features and then supply them to all plugins. -- Nedko Arnaudov GnuPG KeyID: DE1716B0 pgpUmftcrlGj9.pgp Description: PGP signature ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: We could use float I guess to save a bit of space, but I definitely prefer floating point. Fixed point is just a PITA, modern CPUs are much faster at FP anyway, why bother? 1. The modern CPUs are much faster at FP thing is a myth that shows up here and there and is usually taken as a gospel by people who don't know what they're doing (or what their CPU is doing). It depends. Sure, implementing an IIR filter with 4:28 fixed point is not going to be efficient, or even easy. That's one of many tasks where floating point just works better. But we were talking about comparing numbers or calculating an integer difference (with fixed point, it's basically shift and subtract, with floating point you have float-to-int conversion which is slow and unelegant unless you use SSE-based conversion or tricks on IEEE representation, which are not portable). One could compare an integer loop index to a floating point event timestamp instead of converting the timestamp to integer, but that would mean that perhaps every inner loop would have to be written this way. Bad idea, IMO. 2. With floating point, the precision of the fractional part decreases with log2(sample_number). This is not going to be a practical problem, but you're either wasting bits or losing precision here. 3. See: page C-24 in Intel® 64 and IA-32 Architectures Optimization Reference Manual (keep in mind that the tables show latency/throughput numbers for different CPUs, so compare apples with apples!), instructions ADD/SUB vs FADD/FSUB. Intel doesn't seem to support your claim that modern CPUs are much faster at floating point than at fixed point (assuming same bit allocation) - certainly not for that particular operation pair :) And they will probably never be, because the complexity of adding two floats is simply higher than of adding two integers (because one of the mantissa values needs to be shifted and exponent has to be set properly). Of course, it's not just ADD vs FADD. In the reality it's ADD+SHR vs FADD+float to int conversion (or SUBSS+CVTSS2SI). Still, fixed point seems to win. 4. I would agree that floating point numbers are usually much better for representing rational numbers than fixed point numbers are. But usually doesn't mean always. This particular situation is a textbook example of where a fixed point values should be used! Anyway, keep thinking. You already have three solutions to pick from (floats, 16:16 fixed point, or an integer). If you use integers, perhaps the timestamps should be stored as delta values. Perhaps fractional parts could be just stored in events that demand fractional timing (ie. grain start event), removing that part from generic protocol. Perhaps we're still overlooking something. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: _Any_ structure that isn't just a dumb array of bytes will be unsafe to move between machines because of endianness. A bridge can compare the architectures of the bridged machines, and refuse to continue if they're different. That kind of bridge could be written by a well trained chimpanzee and work for (perhaps) majority of cases. Of course, to work for *all* cases, the serialization would be needed. Also, if the buffer is simple (as in: no pointers or handles whatsoever), the serialization doesn't need to be implemented or invoked! :) struct Event_Port_Buffer { uint32_t capacity;// number of elements in the array uint32_t used_size; // number of _used_ elements uint32_t event_count; // number of events (different from // used_size if there are large events - // would this really be needed?) struct Event* events; // an array allocated by the host }; I would argue for additional Event_Port_Buffer pointer, as in next. This way, the host could allocate event buffers in (say) fixed-size chunks, and the buffer is too full, another is allocated and linked to previous one. What do you think? struct Event { uint32_t timestamp; uint16_t size; uint16_t event_type; uint8_t data[8]; // or a union or whatever, as long // as it's 8 bytes }; So we agree on that one. The host won't instantiate the plugin unless it knows how to handle event ports and MIDI events, and the plugin will fail to instantiate unless the host passes a URI - integer map to instantiate using a LV2_Feature with the URI http://lv2.example.com/uri-map and a NULL-terminated array of event type URIs as data. How will it fail? (how will the host be notified about failure?) Is there any way to communicate the reason for the failure to the user? Just asking. The integer associated to each URI is simply the array index (my earlier suggestion was just a brain dump from a thought-in-progress, a simple array seems a lot cleaner). A NULL-terminated array is good. Assuming that the host only supports MIDI events the data passed for this feature will be { http://lv2.example.com/midi-event-type;, NULL }, the plugin will store the index 0 as the MIDI event identifier somewhere in its state, and everything is good to go. A basic loop for processing input events in a plugin could look something like this: Looks good! handle_event(events[index]); index += 1 + (events[index].size - 8) / 16; index += (events[index].size + 15) 4; right? Should plugins have to list http://lv2.example.com/uri-map as a required feature, or should that be implicit whenever a plugin has an event port? I guess, sometimes the event port isn't *required* to be connected? Depends on a plugin, I guess. MIDI arpeggiator will need event support, so will a synthesizer, but a reverb with MIDI support might do without the event port support. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: IP doesn't dictate packet contents whatsoever. It does what it needs to do quite well, I don't think it's crufy at all. It would be crufty if it tried to define, say HTTP error codes... That's right. Lars addressed that criticism pretty well, I think. The evolved proposal is both keeping headers and data in the same place and keeping event semantics on separate layer from event transport. Please look at the definition of the LV2_MIDI struct in lv2-midiport.h. Note the char* parameter, and read it's comment. The buffer is flat, there is no cache thrashing or any such issues here whatsoever. Here it comes: char* buf; /// raw event data Maybe you meant to write this? char buf[]; /// raw event data directly follow here Because char* usually means, you know, a pointer, not a variable length array :) Take this, and solve alignment and URI numbering issues, and both proposals (LarsLKF and yours) are actually the same one. Which is a good thing. The only problem that needs to be handled is how to get the type in there. I would like to find a good solution to this problem that's as extensible as URIs but doesn't actually stick a URI in the event struct (there are a few other future extensions that have the same problem. Lars solved that one pretty well. strcmp of URIs in the audio thread is, as you say, completely out of the question, but so is handing out a flat numeric space. Unless the handing out is done on the fly, based on URIs. Like, say, file descriptors :) We still have central authority, but this time it's the host loading the plugins, not a person. Should give a much better roundtrip and less authority abuse ;) Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 05:59 +0200, Nedko Arnaudov wrote: Also, i doubt we need three count members in Event_Port_Buffer structure. used_size - number of used events is perfectly fine by itself. I dont see why plugin should know whether buffer is actually larger. It needs to know that for an output buffer. Is midi event type semantics a broad or narrow one? I'd prefer narrow one, i.e. one type for note on/offs, one for pitch bend, and for midi cc, etc. Reasoning behind this is to indicate to user (informational) or maybe to host for runtime optimizations too, that only certain types of midi events will be actually processed. Read this as lv2zynadd does not respond to MIDI CC events (zynjacku however maps (will) those to actual parameter changes, through separate ports). I'd prefer to just have one MIDI event type and pass the status bytes as part of the event data. That way you can have generic MIDI processors or channel filters or whatever without having to list every event type in the RDF file. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 09:19 +, Krzysztof Foltman wrote: Lars Luthman wrote: _Any_ structure that isn't just a dumb array of bytes will be unsafe to move between machines because of endianness. A bridge can compare the architectures of the bridged machines, and refuse to continue if they're different. That kind of bridge could be written by a well trained chimpanzee and work for (perhaps) majority of cases. Of course, to work for *all* cases, the serialization would be needed. OK, maybe. It's trivial to add a safe RDF indicator in a separate extension and just assume that an event type is not safe if it doesn't have it. struct Event_Port_Buffer { uint32_t capacity;// number of elements in the array uint32_t used_size; // number of _used_ elements uint32_t event_count; // number of events (different from // used_size if there are large events - // would this really be needed?) struct Event* events; // an array allocated by the host }; I would argue for additional Event_Port_Buffer pointer, as in next. This way, the host could allocate event buffers in (say) fixed-size chunks, and the buffer is too full, another is allocated and linked to previous one. What do you think? For that case I think it would be cleaner if the host just splits the processing period in smaller parts if it needs to fit in more events per time unit. The plugin doesn't need to worry about it. The host won't instantiate the plugin unless it knows how to handle event ports and MIDI events, and the plugin will fail to instantiate unless the host passes a URI - integer map to instantiate using a LV2_Feature with the URI http://lv2.example.com/uri-map and a NULL-terminated array of event type URIs as data. How will it fail? (how will the host be notified about failure?) Is there any way to communicate the reason for the failure to the user? The same way the core spec says that a plugin should always fail to instantiate when a required feature is missing - by returning NULL from the instantiate() callback. There is no error message passing. handle_event(events[index]); index += 1 + (events[index].size - 8) / 16; index += (events[index].size + 15) 4; right? No, that doesn't work. If the event size is 16 it will need to use the 8 free bytes in this event header and 8 bytes in the next one, so the index needs to be increased by 2, but (16 + 15) 4 == 1. And mine is wrong too. Embarassing. This works: index += (events[index].size + 23) / 16; Should plugins have to list http://lv2.example.com/uri-map as a required feature, or should that be implicit whenever a plugin has an event port? I guess, sometimes the event port isn't *required* to be connected? Depends on a plugin, I guess. MIDI arpeggiator will need event support, so will a synthesizer, but a reverb with MIDI support might do without the event port support. Right, if the event port has the lv2:connectionOptional hint (from the core spec) the host doesn't need to know about the event extension at all. So I guess the cleanest way would be to not list the uri-map thing as a separate lv2:Feature in the RDF data but require that a host that handles events passes that LV2_Feature to the plugin's instantiate callback if it is going to connect a non-NULL buffer to any event ports. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 14:55 +0200, Nedko Arnaudov wrote: Lars Luthman [EMAIL PROTECTED] writes: On Thu, 2007-11-29 at 05:59 +0200, Nedko Arnaudov wrote: Also, i doubt we need three count members in Event_Port_Buffer structure. used_size - number of used events is perfectly fine by itself. I dont see why plugin should know whether buffer is actually larger. It needs to know that for an output buffer. Well, port is either input or output, why not use same variable/member (with appropriate naming)? You could, but there isn't really much to gain from it. The host will want to keep track of the buffer capacity in some way so those 4 bytes have to go somewhere, and I think having the same variable mean different things for input and output buffers makes things more confusing. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 13:56 +0100, Lars Luthman wrote: On Thu, 2007-11-29 at 09:19 +, Krzysztof Foltman wrote: Lars Luthman wrote: The host won't instantiate the plugin unless it knows how to handle event ports and MIDI events, and the plugin will fail to instantiate unless the host passes a URI - integer map to instantiate using a LV2_Feature with the URI http://lv2.example.com/uri-map and a NULL-terminated array of event type URIs as data. How will it fail? (how will the host be notified about failure?) Is there any way to communicate the reason for the failure to the user? The same way the core spec says that a plugin should always fail to instantiate when a required feature is missing - by returning NULL from the instantiate() callback. There is no error message passing. ...except that if all event ports are lv2:connectionOptional the plugin should _not_ fail to instantiate. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: OK, maybe. It's trivial to add a safe RDF indicator in a separate extension and just assume that an event type is not safe if it doesn't have it. Fair enough. For that case I think it would be cleaner if the host just splits the processing period in smaller parts if it needs to fit in more events per time unit. The plugin doesn't need to worry about it. Yes. But that makes it impossible to use a fixed size buffer. If we already have a fixed size buffer extension, why break it this way? (note: I'm not saying that fixed size buffers are always a nice thing, but let's think about it before it's too late) And mine is wrong too. Embarassing. This works: index += (events[index].size + 23) / 16; Yes, that's the correct one. Mine would only be correct if 'size' meant size of the whole event including 8-byte header, not the size of the payload. all. So I guess the cleanest way would be to not list the uri-map thing as a separate lv2:Feature in the RDF data but require that a host that handles events passes that LV2_Feature to the plugin's instantiate callback if it is going to connect a non-NULL buffer to any event ports. Why not use lv2:optionalFeature? Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: Yes. But that makes it impossible to use a fixed size buffer. If we already have a fixed size buffer extension, why break it this way? I just don't like to add complexity to the port buffer struct. Sure, worrying about one extra pointer may seem silly, but it's yet another thing that the plugin needs to check. The simpler the better. It's not just extra pointer, it's also extra outer loop in every plugin, so the complexity is definitely there. A host doesn't necessarily have to allocate one fixed-size memory block for every plugin event port, it can use one large shared buffer and dole out portions of it to each input port in each period, What about plugin-to-host communication, then? Before getting the events from the host we set the capacity to maximum, and after the plugin returns, we just reuse the part that wasn't used by the plugin for other events? Why not use lv2:optionalFeature? Because it isn't really a separate optional feature, it depends on whether 1) all event ports have the lv2:connectionOptional hint and 2) the host doesn't plan to connect to any of those ports. Are hosts forced to reveal all features that they implement for instantiate call, or just the ones mentioned by the particular plugin in RDF? In theory, passing some features to a plugin despite if they use the features or not, might require the host to create additional objects, often unnecessarily. That's not a huge problem though. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Krzysztof Foltman wrote: What about plugin-to-host communication, then? Before getting the events from the host we set the capacity to maximum, and after the plugin From the plugin, of course, not the host :) In other words, the host sets capacity to max, calls the plugin, and resets the capacity to size of data received. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 14:03 +, Krzysztof Foltman wrote: Lars Luthman wrote: A host doesn't necessarily have to allocate one fixed-size memory block for every plugin event port, it can use one large shared buffer and dole out portions of it to each input port in each period, What about plugin-to-host communication, then? Before getting the events from the host we set the capacity to maximum, and after the plugin returns, we just reuse the part that wasn't used by the plugin for other events? It's up to the host I suppose. If you pass the maximum capacity you get maximum flexibility, but also risk that a greedy plugin fills your entire buffer with 12412 MIDI CC's per audio frame. But at some point I guess you just have to assume that plugin writers aren't going to be maliciously stupid. =) Why not use lv2:optionalFeature? Because it isn't really a separate optional feature, it depends on whether 1) all event ports have the lv2:connectionOptional hint and 2) the host doesn't plan to connect to any of those ports. Are hosts forced to reveal all features that they implement for instantiate call, or just the ones mentioned by the particular plugin in RDF? According to the core spec, hosts are only required to pass the features to a plugin that the plugin lists as required in its RDF data. In practice many hosts will probably keep one static feature array with all the features they supports that they pass to every plugin instance but it's not necessary. If we decide that plugins with event ports do _not_ have to list the URI map thing as an optional or required feature but still pass the LV2_Feature struct to the plugin at instantiation that's OK too - that's what extensions are for, extending and modifying the core spec. --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: It's up to the host I suppose. If you pass the maximum capacity you get maximum flexibility, but also risk that a greedy plugin fills your entire buffer with 12412 MIDI CC's per audio frame. But at some point I guess you just have to assume that plugin writers aren't going to be maliciously stupid. =) Judging from my Buzz experience, I *have* to assume that plugin writers aren't going to know what they're doing - like a certain plugin developer who couldn't get the basic pitch-to-Hz formula right for several years, and many others (myself included). But, unfortunately, there's little one can do to remedy that. You could draft the specs in such a way that nobody with IQ under 170 could write plugins (DirectShow, I'm looking at you ;) ), but then you'd end up with no plugins. According to the core spec, hosts are only required to pass the features to a plugin that the plugin lists as required in its RDF data. Unless certain features will need passing personalized pointers to plugins (separate host's objects per plugin). And by the way - seems that the char array/union part creates more controversies than necessary. So we may get back to Dave's idea of payload being a zero-length array (no minimum length), and to 8 bytes of alignment instead of 16. If LV2_EVENT_HEADER is defined this way: struct LV2_EVENT_HEADER { uint32_t timestamp; // still wondering if fractional addresses should be a part of generic even transport spec, by the way uint16_t event_type; uint16_t size; // of payload, with space occupied rounded up to nearest 8 }; it's practically as convenient to use as the previous version, and may be preferable in some cases (when bit 3 in payload size is 0). Then we may define (as another extension, perhaps) a MIDI event like this: struct LV2_MIDI_EVENT { LV2_EVENT_HEADER hdr; // or use as a base class in C++ uint8_t command, arg1, arg2, subchannel; uint8_t padding[4]; }; Or maybe instead of dedicating one byte to subchannel we might allow dynamic arbitrary assignment of extra fields (host passes an offset-and-length-to-struct-uri mapping). Well, maybe not. Or other possibility (WARNING: bit squeezing paranoia ahead): struct LV2_EVENT_HEADER // 4 bytes :) { uint16_t timestamp; // no fractional timestamps here uint8_t event_type; // 256 events are enough for everybody uint8_t size; // 256 bytes are enough for everybody }; struct LV2_MIDI_EVENT { LV2_EVENT_HEADER hdr; // or use as a base class in C++ uint8_t command, arg1, arg2, padding; }; // separate event type for those 3 instruments with per-note control changes struct LV2_MIDI_EVENT_SUBCHANNEL { LV2_EVENT_HEADER hdr; // or use as a base class in C++ uint8_t command, arg1, arg2, subchannel; }; // separate event type for those 3 specific granular synthesis hosts and plugins struct LV2_MIDI_EVENT_FRACTIONAL { LV2_EVENT_HEADER hdr; // or use as a base class in C++ uint8_t command, arg1, arg2, subsample; }; (1/256 of a sample should be good enough for everybody, too!) Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 19:43 +, Krzysztof Foltman wrote: Lars Luthman wrote: And by the way - seems that the char array/union part creates more controversies than necessary. So we may get back to Dave's idea of payload being a zero-length array (no minimum length), and to 8 bytes of alignment instead of 16. If LV2_EVENT_HEADER is defined this way: struct LV2_EVENT_HEADER { uint32_t timestamp; // still wondering if fractional addresses should be a part of generic even transport spec, by the way uint16_t event_type; uint16_t size; // of payload, with space occupied rounded up to nearest 8 }; Sure, that's fine by me. It has the added benefit that you can have 0 byte events where only the event type carries any meaning (though that may not be much of a benefit). Then we may define (as another extension, perhaps) a MIDI event like this: Another extension, yes. =) --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 17:19 +0100, Lars Luthman wrote: On Wed, 2007-11-28 at 15:56 +, Krzysztof Foltman wrote: Serialization is necessary because large events can potentially refer to some foreign objects, like handles to OpenGL textures in video memory and what not :) You cannot assume that all of your large event data will be conveniently placed in a single contiguous buffer in RAM, because it might not be the most practical way of dealing with them. No argument with any of this. Passing around reference counted opaque objects can certainly be useful and I can imagine lots of sexy applications, I just don't think it needs to be in the event transport specification when it works just as well outside it. ++ Extensions should be kept as small and limited in scope as possible. Do one thing, and do it well... -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 12:55 +, Krzysztof Foltman wrote: Lars Luthman wrote: I'd prefer to just have one MIDI event type and pass the status bytes as part of the event data. That way you can have generic MIDI processors or channel filters or whatever without having to list every event type in the RDF file. Yes. MIDI Implementation Chart LV2 extension, anyone? ;) In fact, MIDI Map extension serves exactly that purpose - however, it only deals with CC, not other messages. A totally unrelated note: static RDF files in LV2 might be fine for now, but sometimes I'd like to generate the same kind of metadata on the fly. Think dynamic parameters, dynamic event types, editable CC maps... Nedko has tried to solve the first problem by his dynamic parameters extension, but I guess that a general solution could be better, because the same problem he had with params, is practically guaranteed to appear everywhere else. Think of plugin standard adapters (VST-to-LV2) and the like. Or any adapters, bridges and what not. Or plugin-side MIDI learn which sends controller information via dynamic CC map. Any thoughts how to attack that problem? A function to send an updated RDF to the host? A function to send incremental information? We will want that eventually, but it's officially Hard(TM) :) Oh, and by the way - Nedko has mentioned that a *host* may want to send updates of URI number allocations - how are we going to solve that? I suppose a host object with a function to subscribe/unsubscribe URI-number allocation update information could be a way to go - a classic Observer pattern. A plugin that doesn't make use of it would just ignore unknown event types in the event buffer, which is fine, as long as the only URIs that may appear in updates are new URIs (there are no reassignments or deletions). Dynamic URI-int mapping is probably a good thing. Raises issues though - maybe an acceptable compromise is that the existing ones never change, but ones can be added? Really no reason an already designated number needs to change URIs that I can think of, just tack new ones on the end. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 05:59 +0200, Nedko Arnaudov wrote: Lars Luthman [EMAIL PROTECTED] writes: struct Event_Port_Buffer { uint32_t capacity;// number of elements in the array uint32_t used_size; // number of _used_ elements uint32_t event_count; // number of events (different from // used_size if there are large events - // would this really be needed?) struct Event* events; // an array allocated by the host }; struct Event { uint32_t timestamp; uint16_t size; uint16_t event_type; uint8_t data[8]; // or a union or whatever, as long // as it's 8 bytes }; snip Should plugins have to list http://lv2.example.com/uri-map as a required feature, or should that be implicit whenever a plugin has an event port? Both methods have some drawbacks - if plugins are required to list it there is some redundancy, if they are not we have a required feature that isn't listed as one which can be a bit confusing. I think uri-map feature should be part of event extension itself. what would be use without it? Shared data structures? Maybe even things inside events themselves (ie the type of various message arguments). I think a URI-int mapping mechanism will probably be very useful in several places, since it's the only way of getting URI extensibility with DSP appropriate performance. Doesn't hurt to keep it separate anyway, it's not really event specific at all. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 13:25 +0100, Lars Luthman wrote: On Thu, 2007-11-29 at 05:59 +0200, Nedko Arnaudov wrote: Also, i doubt we need three count members in Event_Port_Buffer structure. used_size - number of used events is perfectly fine by itself. I dont see why plugin should know whether buffer is actually larger. It needs to know that for an output buffer. Is midi event type semantics a broad or narrow one? I'd prefer narrow one, i.e. one type for note on/offs, one for pitch bend, and for midi cc, etc. Reasoning behind this is to indicate to user (informational) or maybe to host for runtime optimizations too, that only certain types of midi events will be actually processed. Read this as lv2zynadd does not respond to MIDI CC events (zynjacku however maps (will) those to actual parameter changes, through separate ports). I'd prefer to just have one MIDI event type and pass the status bytes as part of the event data. That way you can have generic MIDI processors or channel filters or whatever without having to list every event type in the RDF file. ++ Event extension should keep it hands out of event contents entirely. MIDI is already well-defined. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: We will want that eventually, but it's officially Hard(TM) :) No doubt it is. But it's the result of your own choice, so better don't complain too much ;) Dynamic URI-int mapping is probably a good thing. Raises issues though - maybe an acceptable compromise is that the existing ones never change, but ones can be added? Look at the part you've just quoted (or see below) as long as the only URIs that may appear in updates are new URIs (there are no reassignments or deletions). No reassignments or deletions. Just add, no change/remove. But, actually, I can see some purpose for deletion - when a host stops supporting some event type coming out of a plugin. Which tells me we've concentrated too much on host-to-plugin direction, and not enough on plugin-to-host or plugin-to-plugin (basically: communicating what event types are actually *wanted* on an event output port!) But, worst case is that host would have to ignore the event types that are not valid anymore. It would be inefficient for the plugins, sure. But somehow, I don't see anyone will care. Not with current temperatures in hell. Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: I /really/ don't like screwing around with MIDI. Just make the events pure, raw MIDI. Jack MIDI events are 'just n bytes of MIDI', Alsa has functions to get at 'just n bytes of MIDI', and... well, it's just MIDI. However, it also has its own age. And limitations. In particular, the amount of per-note control is pitiful. I can always use hacks to get around the limitations, or introduce a per-note control via separate set note parameter event type. But hacks are ... hacky, and the extended extension of extension for every single feature is a bit inelegant too. Anyway - so far, I have no code that would make use of this, so we might keep it as plain MIDI. And then we have next 5 years to decide the details of the feature. In the meantime, maybe the MIDI guys will decide for us :D Krzysztof ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 00:23 +, Krzysztof Foltman wrote: Dave Robillard wrote: Because char* usually means, you know, a pointer, not a variable length array :) char buf[] is, you know, equivalent to char* buf. You do know C, yes? ;) You *do* know C, yes? Well enough to judge others? Hint: try this little proggy (gcc should compile it fine): #include stdio.h int main(int argc, char *argv[]) { struct X { int a; char buf[]; }; struct Y { int a; char *buf; }; printf(%d %d\n, sizeof(struct X), sizeof(struct Y)); } Sigh. In /this case/ they are the same, because the data directly follows (the char* member doesn't even really have to be there, it's just convenient. It could literally be just 'char', for example). The data always did directly follow, and this has all been working just fine for ages now. You just didn't bother to understand the existing LV2 MIDI before trying to extend it, and an awful lot of pointless ranting about cache and optimisation or whatever was the result. Touche, smartass :P -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 19:43 +, Krzysztof Foltman wrote: Lars Luthman wrote: It's up to the host I suppose. If you pass the maximum capacity you get maximum flexibility, but also risk that a greedy plugin fills your entire buffer with 12412 MIDI CC's per audio frame. But at some point I guess you just have to assume that plugin writers aren't going to be maliciously stupid. =) Judging from my Buzz experience, I *have* to assume that plugin writers aren't going to know what they're doing - like a certain plugin developer who couldn't get the basic pitch-to-Hz formula right for several years, and many others (myself included). But, unfortunately, there's little one can do to remedy that. You could draft the specs in such a way that nobody with IQ under 170 could write plugins (DirectShow, I'm looking at you ;) ), but then you'd end up with no plugins. According to the core spec, hosts are only required to pass the features to a plugin that the plugin lists as required in its RDF data. Unless certain features will need passing personalized pointers to plugins (separate host's objects per plugin). And by the way - seems that the char array/union part creates more controversies than necessary. So we may get back to Dave's idea of payload being a zero-length array (no minimum length), and to 8 bytes of alignment instead of 16. If LV2_EVENT_HEADER is defined this way: struct LV2_EVENT_HEADER { uint32_t timestamp; // still wondering if fractional addresses should be a part of generic even transport spec, by the way uint16_t event_type; uint16_t size; // of payload, with space occupied rounded up to nearest 8 }; ++ FWIW OSC (and by extension, my LV2 OSC stuff) does this too, except with 4 bytes instead of 8. Then we may define (as another extension, perhaps) a MIDI event like this: struct LV2_MIDI_EVENT { LV2_EVENT_HEADER hdr; // or use as a base class in C++ uint8_t command, arg1, arg2, subchannel; uint8_t padding[4]; }; I /really/ don't like screwing around with MIDI. Just make the events pure, raw MIDI. Jack MIDI events are 'just n bytes of MIDI', Alsa has functions to get at 'just n bytes of MIDI', and... well, it's just MIDI. Everyone knows MIDI, it's probably the most ubiquitous audio standard in existence. Keeping our hands out of it means trivial interoperability with everything: past, present, and future. We should just leave it be. It's working this way in Jack right now, it's working this way in LV2 right now. If it ain't broke -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thu, 2007-11-29 at 01:49 +0100, David Olofson wrote: On Thursday 29 November 2007, Dave Robillard wrote: [...] Well, sure, but big data is big data. In the typical case plugin buffers are much smaller than the cache [...] Of course, but that's exactly what I'm talking about - large buffers, and why it doesn't make sense to support them. :-) It makes a lot less sense to explicitly deny them for no particular reason. You can't use events larger than x bytes Why? Because some Dave or another said it could possibly be slow in certain cases? Sure, there's a performance hit running on really massive buffers. There's a performance hit running on tiny buffers too. So what? There are times when running on single sample buffers is what you want to do, even though it's slow. There are other times when running on massive buffers is what you want to do, even though it's slow. If you don't want to do that, well... don't do that. Currently the only limit on sizes of things in LV2 is the range of uint32_t, and this is a Good Thing. Not explicitly making things verboten != supporting I do agree we should not be adding crufty features to support massive buffers, if that's what you mean. It's easier to just split the cycle anyway. Last time I looked into this, a reasonably optimized resampler with cubic interpolation and some ramped parameters was memory bound even on a lowly P-III CPU, at least with integer processing. (Haven't actually tested this on my AMD64...) I think floating point should be as fast or faster in most cases, at least on P-III CPUs and better - and with SIMD, you may get another 2x-4x higher throughput at that. A clever host can just use the same, say, 2 buffers (stereo audio), so running a bunch of plugins on it will be in the cache the entire time. Ardour, for example, (usually) runs the entire route's chain of plugins on the same buffers in-place. For any reasonable Jack buffer size on any reasonable modern CPU, those buffers are going to be in the cache for the duration of that entire chain's processing. For non-in-place chains, add a factor of 2 (and it's still going to all fit in cache in many cases). In other places, that's not the case. Point being this is the host author's - not the plugin specification's - business. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 00:30 +, Krzysztof Foltman wrote: Dave Robillard wrote: I /really/ don't like screwing around with MIDI. Just make the events pure, raw MIDI. Jack MIDI events are 'just n bytes of MIDI', Alsa has functions to get at 'just n bytes of MIDI', and... well, it's just MIDI. However, it also has its own age. And limitations. In particular, the amount of per-note control is pitiful. Definitely. This is why event type agnostic event ports are a good thing. You're free to make a better MIDI if you want (I'd say use OSC and leave MIDI on the trash pile of computing history where it belongs, but that's just me), but the normal MIDI events need to be plain old MIDI. In short: don't rock the boat; all this Jack/LV2 MIDI stuff is still getting off the ground... I can always use hacks to get around the limitations, or introduce a per-note control via separate set note parameter event type. But hacks are ... hacky, and the extended extension of extension for every single feature is a bit inelegant too. No it isn't, it's extremely elegant. What's inelegant is cramming too much garbage into an extension when that garbage is a separate problem. You want to define a new event type? Define it! No consensus, no debates, no fuss, no muss. You can get the whole thing working and implement it in a host and plugins both before even telling anyone about it at all (if you want), and /nothing breaks/. Your plugins can even be run in another host that doesn't even know what the heck these events/ports even are, and everything will work fine (except, of course, those ports/events). If that's not elegant, I don't know what is ;) -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 03:15 +0100, David Olofson wrote: On Friday 30 November 2007, Dave Robillard wrote: [...] I do agree we should not be adding crufty features to support massive buffers, if that's what you mean. It's easier to just split the cycle anyway. Yes, that's exactly what I mean. Sure, one *could* have a use for really huge buffers (say, running large FFTs without intermediate buffering), but to me, that seems too far out that one should have everyone deal with 32:32 event timestamps for that reason alone. The current version of LV2 MIDI just uses double. All the precision you could ask for, or an insane range if you'd prefer. It's a bit big maybe, but hey, why not? We could use float I guess to save a bit of space, but I definitely prefer floating point. Fixed point is just a PITA, modern CPUs are much faster at FP anyway, why bother? -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Fri, 2007-11-30 at 02:39 +0100, David Olofson wrote: On Friday 30 November 2007, Dave Robillard wrote: That's why I'm using a Port as the smallest connection unit, much like LADSPA ports, so there is no need for an event type field of any kind at all, let alone a URI. Ports /are/ the smallest connection unit. But ports can /contain/ events, and if we want multiple kinds of events in a single port, then the events themselves need a type field. Sounds like there's a fundamental difference there, then. I'm using a model where a port is nothing more than something that deals with a value of some sort. There are no channels, voices, different events or anything inside a port - just a value. An output port can operate that value of a compatible input port. LV2 ports can contain /anything/. It's a void*, completely 100% opaque in every way. There might be 'channels' or 'voices' or a 'value' or whatever in there, it's just data. The data in the events *could* be MIDI or whatever (the host doesn't even have to understand any of it), but normally, in the case of Audiality 2, it'll be modular synth style ramped control events. That is, one port controls exactly one value - just like in LADSPA, only using timestamped events with ramping info instead of one value per buffer. The host might not have to (though in practise it usually does), but other plugins certainly do. You can't process events if you don't even know what they are. Yes, obviously. I don't quite see what you think I'm trying to say here. :-) [snip] Me neither :) There are of course infinite ways to do 'events' for plugins (and an infinite number of interpretations of what 'events' means). Nice thing about LV2 is you can do them all. Something like your non-flat event stuff (with queueing and linked lists and dispatching and such, rather than plain old directly connected buffers) may find a place in LV2 as well - may have to for certain message-based (ahem) programming modular stuff ala Max. What we have now is the sample accurate hard realtime sort of 'events' (ala Jack MIDI). Havn't quite figured out the bridging of those two worlds yet -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On 28 Nov 2007, at 00:05, Marc-Olivier Barre wrote: On Nov 27, 2007 11:24 PM, Dave Robillard [EMAIL PROTECTED] wrote: I have said this a lot, and I will continue saying it more until the end of time because it's important: the fact that ports can contain /anything/ is the fundamental core idea behind LV2, and it's a good one. A good generic event extension must do this as well. Dave, I love it when you go crazy over a piece of spec. Just don't change anything :-) And for what it's worth, I agree with you on most parts... Yeah, right - no big steps backwards please. - Steve ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 10:47 +, 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 signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman wrote: 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. 256 types is more than enough, in my opinion. Especially if MIDI takes just 1 type, not 128 :) 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/ . How does the host know if the buffer has or has not been acquired (owned) by the plugin? With my approach, a plugin can either ignore data completely, or copy it into safe place, or increase reference count so that host doesn't free it until plugin has finished with it. 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, It surely is a potential problem. While remapping can be really trivial, it's not efficient and perhaps should be avoided. On the other hand, how often do we send exactly the *same* event buffer to different plugins (think Set Parameter messages!)? Is that a typical or untypical case? What are example scenarios where that problem might arise? But, if we put whole MIDI stuff into a single event type, I'm fine with host-assigned numbers (which would be given to plugin on instantiation). 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), Actually, a correct behaviour for the host would be to ignore the fact that plugin handles some events that host doesn't. The fact that plugin supports - say - video, doesn't mean that host must have anything to do with that video. It's a bit different if we're talking abot plugin's _output_ buffer here, but I guess there can be a way to make a plugin not send unwanted event types. Or to skip unrecognized event types. 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). We might. But then we lose generality and reserve a huge block of event identifiers for an outdated crappy standard ;) Who uses polyphonic aftertouch these days, anyway? :) I thought of, at least, type ranges (like, 0x80-0x8F is note off ch1..ch16). That might reduce RDF bloat, but don't know what others will think about it. Anyway, single event type for MIDI is OK for me. 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. Losing those 2 bytes on 1-byte events is really acceptable to me :) We need padding anyway, don't we? To clarify the issue: shortData means that the event data may be up to 3 bytes long, not that it must be 3 bytes. In case it's less than 3 bytes, the remaining bytes are used for padding/alignment. But, anyway, the shortData/mediumData thing in my proposal could have been done in a much better way (see below!). 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 }; A small modification: what about struct LV2_EVENT_HEADER { uint32_t timestamp; uint16_t size; uint16_t event_type; union { uint8_t data[8]; float dataf[2]; int32_t datai[2]; IDataBuffer *ptr; } }; We don't need 65535 bytes for size, because copying THAT large blocks in processing thread (no matter if host or plugin does it) is a bad idea, just pass a pointer/object! 8 bytes of data is better than 7 bytes, because you can fit a 64-bit pointer there (on 64-bit machines). Or an int32_t and a float (set parameter event). I'm *slightly* against the size field, because it is another value that must be inspected in event processing loop, even with trivial events like MIDI. But it's not worth arguing, I'm fine with keeping size there. 12 bytes to inspect+4 to skip for average MIDI event is already slightly better than Dave's 16 bytes + 3 bytes on the side. And memory management gets easier. Everybody wins :) 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. So you've basically improved the mediumData thing and merged it with smallData, by introducing size
Re: [LAD] enhanced event port LV2 extension proposal
On Wednesday 28 November 2007, Krzysztof Foltman wrote: [...] I don't think it's a serious problem. Huge processing buffers are not very useful in practice. [...] Actually, they're directly harmful most of the time! For any graph with more than one plugin, and with plugins that use internal buffers of the I/O buffer size, large buffers means you'll get a gigantic cache footprint. It gets worse in high latency real time situations (games, media players etc), where you have other threads fighting for the cache as well. Obviously, this effect is less visible with non-trivial plugins - but even how many plugins in a normal graph are actually CPU bound? In my experience, the effect is very pronounced (several times higher CPU load with 512 samples/buffer than with 32) when using a bunch of sampleplayer voices (cubic interpolation + oversampling) and some simple effects. You have to do some pretty serious processing to go CPU bound on a modern PC... //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wednesday 28 November 2007, Krzysztof Foltman wrote: [...] - or introducing a 65536 samples milestone kind of event similar to clear message in LZ compression format, separating events from different 65536-sample eras :) Why would you need to do this? Timestamps in Audiality 0.1.x are 16 bit and based on a continuously running, wrapping timer. Audiality 2, VST, DSSI and others use offsets from the first sample in the current buffer. Either way works just fine as long as the timestamps cover the buffers safely. Now, this is assuming that we consider events real time data, just like audio. That is, plugins are not allowed to send events for future buffers, and they'll only ever see events for the current buffer. I don't see the point in supporting anything else, unless you go all the way and provide a random access sequencer API, where plugins can just browse around as they wish. If the plugin does not implement this extension, it cannot handle buffers of more than 65536 samples - and that should be perfectly fine in most cases. Well, it's kind of nasty that the base API supports larger buffers if the events don't, but IMNSHO, the mistake is in supporting 65536 sample buffers *at all*... Hell, max buffer size in Buzz was 256 samples, pitiful by today's standards, and it was still quite efficient. I'd say it's efficient *because* of this. It may not matter much on a quiescent system with a CPU with megabytes of cache, but if you have serious GUI stuff going on - or a game engine - your audio thread will wake up and find 100% cold memory every time it fires! In that situation, every byte of the graph's footprint will have to be fetched from cold memory at least once per engine cycle. Of course, if your graph footprint is larger than the cache, things get even worse, as you'll have cache misses over and over until the audio thread is done with the buffer. //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wednesday 28 November 2007, Dave Robillard wrote: [...] The only problem that needs to be handled is how to get the type in there. I would like to find a good solution to this problem that's as extensible as URIs but doesn't actually stick a URI in the event struct (there are a few other future extensions that have the same problem. strcmp of URIs in the audio thread is, as you say, completely out of the question, but so is handing out a flat numeric space. This is /the/ problem that needs solving here, and I'm desperately trying to guide the conversation in a direction that will get it solved nicely ;) I don't know if this is applicable here, but for Audiality 2 I'm dealing with this on the connection level. Each control is a port like any other, meaning it has a name, a protocol URI and a few other parameters that the host needs to know what can and cannot be connected. If two ports have the same URI, they can be connected, and that's it, basically. Event semantics (structured stream, commands etc) and data fields are left to the plugins that implement the ports, so the host doesn't even need to know what the plugins are talking about. (This is a direct connection model; data is not normally piped through the host.) On the physical level, I still have ports share event buffers (or rather, queues in this case) so plugins don't have to sort/merge or poll umpteen queues all the time. What event goes where is decided by means of filling an address field with an opaque cookie value that the plugin generates upon connection. The cookie can be ignored if there's one queue per port, or it can be a fully specified plugin-wide port index if the plugin uses a single event queue, or anything in between. Multiple queues...? Yes, A2 plugins can use multiple queues when that suits the implementation better. (Multiple inner loops, rather than running the whole synth, all voices, or whatever, one sample at a time.) Thus, a plugin doesn't have to mess around with the events to get them to the right places in the right order. It just creates one queue per voice/strip/section/whatever loop and hands the right queues out when connections are made. This means an event target also needs to contain a queue pointer. Of course, one could just use one queue per plugin and use only cookie addressing, but I decided to allow multiple queues to eliminate most of the event dispatching complexity you'll otherwise have in any non-trivial plugin. It seems to be a simple and efficient solution, but I could be missing something, of course. Remains to see when I have some more serious code running. :-) //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 16:45 +, Krzysztof Foltman wrote: There is also a possible middle ground - an event type declaration in rdf would say not just what URI it corresponds to, but also if it contains any unsafe data (like pointers, handles etc). Just to possibly prevent a hypothetical transparent network/interprocess bridge from trying to smuggle pointers between process boundaries, which could lead to unexplained crashes :) _Any_ structure that isn't just a dumb array of bytes will be unsafe to move between machines because of endianness. If the host doesn't know exactly what the internal structure of the event buffer is it can't do anything like that (unless it has a serialisation callback like in your object event proposal) and if it does it can, so having a separate flag for that is probably redundant. No argument with any of this. Passing around reference counted opaque objects can certainly be useful and I can imagine lots of sexy applications, I just don't think it needs to be in the event transport specification when it works just as well outside it. Good. So let's postpone that part for now, it will go into different extension(s). OK, so what we have now is something like this: struct Event_Port_Buffer { uint32_t capacity;// number of elements in the array uint32_t used_size; // number of _used_ elements uint32_t event_count; // number of events (different from // used_size if there are large events - // would this really be needed?) struct Event* events; // an array allocated by the host }; struct Event { uint32_t timestamp; uint16_t size; uint16_t event_type; uint8_t data[8]; // or a union or whatever, as long // as it's 8 bytes }; A pointer to an Event_Port_Buffer is passed to connect_port() for every event port etc. And in the RDF file for the plugin there would be something like this: http://myplugin.example.com a lv2:Plugin; lv2:requiredFeature http://lv2.example.com/midi-event-type; lv2:port [ a lv2:InputPort, http://lv2.example.com/event-port; ... ]; ... The host won't instantiate the plugin unless it knows how to handle event ports and MIDI events, and the plugin will fail to instantiate unless the host passes a URI - integer map to instantiate using a LV2_Feature with the URI http://lv2.example.com/uri-map and a NULL-terminated array of event type URIs as data. The integer associated to each URI is simply the array index (my earlier suggestion was just a brain dump from a thought-in-progress, a simple array seems a lot cleaner). Assuming that the host only supports MIDI events the data passed for this feature will be { http://lv2.example.com/midi-event-type;, NULL }, the plugin will store the index 0 as the MIDI event identifier somewhere in its state, and everything is good to go. A basic loop for processing input events in a plugin could look something like this: Event_Port_Buffer* buf = ports[EVENT_PORT]; uint32_t index; uint32_t next_frame; uint32_t frames_done = 0 while (index buf-used_size) { next_frame = events[index].timestamp 16; render_audio(frames_done, next_frame); frames_done = next_frame; handle_event(events[index]); index += 1 + (events[index].size - 8) / 16; } render_audio(frames_done, nframes); Should plugins have to list http://lv2.example.com/uri-map as a required feature, or should that be implicit whenever a plugin has an event port? Both methods have some drawbacks - if plugins are required to list it there is some redundancy, if they are not we have a required feature that isn't listed as one which can be a bit confusing. Does anyone else see any other problems with this type of event port? Steve, Dave, Nedko? --ll signature.asc Description: This is a digitally signed message part ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 23:39 +0100, David Olofson wrote: On Wednesday 28 November 2007, Dave Robillard wrote: [...] The only problem that needs to be handled is how to get the type in there. I would like to find a good solution to this problem that's as extensible as URIs but doesn't actually stick a URI in the event struct (there are a few other future extensions that have the same problem. strcmp of URIs in the audio thread is, as you say, completely out of the question, but so is handing out a flat numeric space. This is /the/ problem that needs solving here, and I'm desperately trying to guide the conversation in a direction that will get it solved nicely ;) I don't know if this is applicable here, but for Audiality 2 I'm dealing with this on the connection level. Each control is a port like any other, meaning it has a name, a protocol URI and a few other parameters that the host needs to know what can and cannot be connected. If two ports have the same URI, they can be connected, and that's it, basically. Event semantics (structured stream, commands etc) and data fields are left to the plugins that implement the ports, so the host doesn't even need to know what the plugins are talking about. (This is a direct connection model; data is not normally piped through the host.) Same with LV2 ports; works perfectly for port types. Problem is, sticking a URI in each /event/ is far too bloated/slow. -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 19:07 -0500, Dave Robillard wrote: Same with LV2 ports; works perfectly for port types. Problem is, sticking a URI in each /event/ is far too bloated/slow. I am coming horribly late to this discussion, so I might be being thick, but what happens if each event contains a pointer to a C style string being the URI... Bloated and slow right? Now here is the trick, we convert that to a unique identifier at run time by registering each unique event URI as a string in some data structure (Map, linked list of parameters, whatever), then at event creation time we set the pointer to point to our single common instance of the URI. Now all events of a given type have the same value stored in that pointer which will serve as a unique ID for this event for run of the programme. If saving event data to disk, we just replace the pointer with the string it pointed to. struct event_t { char * uri; size_t data_length; char data[1] }; On loading a set of events build a list of every unique uri and patch each events uri pointer to point to it. struct known_events { char *uri; int (*handler)(struct event_t * ev, void *param); struct known_events *next; } Or something like that. Now using the fact that the address of a struct is the address of the first element of the struct, running an event is simply ((struct known_events *) event.uri)-handler (event,whatever); Or something like that, I didn't try to compile it. If we wish to check if two events are of the same type then if ((char*) event1.uri == (char*) event2.uri){ is sufficient and is a single 32 or 64 bit integer comparison. Just a thought. Regards, Dan. ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thursday 29 November 2007, Dave Robillard wrote: [...] Same with LV2 ports; works perfectly for port types. Problem is, sticking a URI in each /event/ is far too bloated/slow. That's why I'm using a Port as the smallest connection unit, much like LADSPA ports, so there is no need for an event type field of any kind at all, let alone a URI. The data in the events *could* be MIDI or whatever (the host doesn't even have to understand any of it), but normally, in the case of Audiality 2, it'll be modular synth style ramped control events. That is, one port controls exactly one value - just like in LADSPA, only using timestamped events with ramping info instead of one value per buffer. Extensibility is a non-issue on this level. What you do if you want more stuff is just grab another URI for a new event based protocol, and you get to start over with a fresh event struct to use in whatever way you like. (In fact, as it is, the host doesn't even have to know you'll be using events. It just provides a LIFO pool of events for any plugins that might need it.) //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Thursday 29 November 2007, Dave Robillard wrote: [...] Well, sure, but big data is big data. In the typical case plugin buffers are much smaller than the cache [...] Of course, but that's exactly what I'm talking about - large buffers, and why it doesn't make sense to support them. :-) If you're using 65536 samples per buffer, it just takes a plugin with four audio inputs and you're up to 1 MB of intermediate buffers. Even if that does fit in the cache, in a real life situation, with other threads working, most of it will be cold again every time the audio thread starts. So, your processing speed is potentially capped at the memory bandwidth throughout the buffer cycle, or at least until you start reusing buffers in the graph. And what is supposed to be gained by this...? I don't see why a plugin API of this type should support nonsense like that at all, and thus, it shouldn't affect event timestamps either - but well, now it's there, and there isn't really any Right Thing(TM) to do here, I guess. crunching away on plain old audio here is definitely CPU bound (with properly written RT safe host+plugins anyway). Last time I looked into this, a reasonably optimized resampler with cubic interpolation and some ramped parameters was memory bound even on a lowly P-III CPU, at least with integer processing. (Haven't actually tested this on my AMD64...) I think floating point should be as fast or faster in most cases, at least on P-III CPUs and better - and with SIMD, you may get another 2x-4x higher throughput at that. Could be way off here, though. Do you have benchmark figures? //David Olofson - Programmer, Composer, Open Source Advocate .--- http://olofson.net - Games, SDL examples ---. |http://zeespace.net - 2.5D rendering engine | | http://audiality.org - Music/audio engine | | http://eel.olofson.net - Real time scripting | '-- http://www.reologica.se - Rheology instrumentation --' ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Lars Luthman [EMAIL PROTECTED] writes: struct Event_Port_Buffer { uint32_t capacity;// number of elements in the array uint32_t used_size; // number of _used_ elements uint32_t event_count; // number of events (different from // used_size if there are large events - // would this really be needed?) struct Event* events; // an array allocated by the host }; struct Event { uint32_t timestamp; uint16_t size; uint16_t event_type; uint8_t data[8]; // or a union or whatever, as long // as it's 8 bytes }; snip Should plugins have to list http://lv2.example.com/uri-map as a required feature, or should that be implicit whenever a plugin has an event port? Both methods have some drawbacks - if plugins are required to list it there is some redundancy, if they are not we have a required feature that isn't listed as one which can be a bit confusing. I think uri-map feature should be part of event extension itself. what would be use without it? I think using untyped 8 byte array instead of union is more appropriate, we can map those 8 bytes to whethever is needed based on type uri. However, the needs additional marshaling flag is good thing imho. this can include endian swaping, and dereferencing external objects. It would be appropriate to have marshaling feature lv2 extension in future (for cross machine events). Since value of this flag is known at compile/deploy stage, there is no need it to be runtime variable. Having it specified in RDF is the way to go. Also, i doubt we need three count members in Event_Port_Buffer structure. used_size - number of used events is perfectly fine by itself. I dont see why plugin should know whether buffer is actually larger. Is midi event type semantics a broad or narrow one? I'd prefer narrow one, i.e. one type for note on/offs, one for pitch bend, and for midi cc, etc. Reasoning behind this is to indicate to user (informational) or maybe to host for runtime optimizations too, that only certain types of midi events will be actually processed. Read this as lv2zynadd does not respond to MIDI CC events (zynjacku however maps (will) those to actual parameter changes, through separate ports). -- Nedko Arnaudov GnuPG KeyID: DE1716B0 pgpoyJHUMAEGa.pgp Description: PGP signature ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dan Mills [EMAIL PROTECTED] writes: Now here is the trick, we convert that to a unique identifier at run time by registering each unique event URI as a string in some data structure (Map, linked list of parameters, whatever), then at event creation time we set the pointer to point to our single common instance of the URI. Now all events of a given type have the same value stored in that pointer which will serve as a unique ID for this event for run of the programme. If saving event data to disk, we just replace the pointer with the string it pointed to. struct event_t { char * uri; size_t data_length; char data[1] }; On loading a set of events build a list of every unique uri and patch each events uri pointer to point to it. I somewhat like the idea, but how would this work for a host dispatching opaque/unknown events? Maybe such host would load plugin event type uris From RDF and just use them in opaque way. However, I dont see benefits over the uri-map approach proposed in the other subthread. using uri map indexes looks quite more cleaner and intuitive to me than assuming uri pointers are constant. IMHO later can be quite confisung for not that experienced plugin writters. struct known_events { char *uri; int (*handler)(struct event_t * ev, void *param); struct known_events *next; } I don't get why this handler callback is needed at all. In LV2 such events are supposed to be processed during run(). It is worth to discuss how host dispatching opaque events will notify already instantiated plugins about new event type introduced by loading new plugin. -- Nedko Arnaudov GnuPG KeyID: DE1716B0 pgpHv0aKEGCkw.pgp Description: PGP signature ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
Dave Robillard wrote: Why not make it satisfy most everyone by being extensible? It *is* extensible. Note that commands 0x00-0x6F and 0x73-0x7F are unused, so further extensions are free to define them (perhaps we need a scheme for binding extension URIs to command numbers, to make it more LV2-ey). And 0x72 command can be used for pretty much any data larger than 8+2 octets. While it's efficient first and not generic first, so to speak, it should be fine for the intended uses. The idea of a generic event port is not a bad one, I think it's not just not a bad one. The other possibility (multiple event ports) is less efficient, and speed is crucial here. It's also more complex, looking from plugin author's perspective. So I had little choice. idea at all (no matter what, someone is going to want to put something in there you didn't think of. Please don't jump to conclusions, and take more time to read and analyse the proposal. Of course, it is possible to add new event types with arbitrary length data, and the limitation of 8 octets per extended block is not that bad, because you can always fit an interface pointer (32-bit or 64-bit) there. Just look how binary data extension is implemented. Notice that I just took the approach of optimizing for most common case (MIDI events), and tried to maximize functionality while keeping block size small and constant (to avoid pointer arithmetic that was complicating Lars' proposal a bit). Trying to pre-define things from the top down like this is un-lv2-ey). Well, sometimes you need to find the right tradeoff between being efficient (memory- and speed-wise) and generic. I think I've found an acceptable tradeoff (definitely favouring speed, but not losing generality and not very memory-hungry). However, I had to make some assumptions about how it will be used (mostly implemented by inexperienced people, mostly used for MIDI and float parameters, seldom used for advanced stuff). Oh well, I'm repeating myself here :) I think those are correct assumptions, but you seem to have a different angle for looking at those things. Well, it took me years (and failed/inadequate designs) to grow out of the everything should be as generic as possible approach, so I understand why you're doing that, but I still prefer the priority-based optimization approach that I've used. I still think my proposal could be improved, and I don't like some decisions that I made (basically, I made them because the alternatives looked even more nasty), but stripping off optimizations is not the way to go, IMO. Something more appropriate (IMO) might be like: struct LV2_EVENT { ev_stamp_t time; /// (ignoring the timestamp type issue) ev_type_t type; /// (again ignoring type issue) size_t buf_size; /// size of buf in bytes char* buf; /// raw event data } You're suggesting a classic textbook chunked data approach, which works, no doubt. However, it has some problems with it, which might not be considered very major, but seem to make my approach slightly more favourable: - too much data to be accessed in the most common use case (in 32-bit environment, 16 bytes of header plus event data possibly in distant memory); we don't need to save every byte of RAM, but when you need to read and write twice as much RAM as you could, then maybe it's worth rethinking it - separation of event header and event data in the most common case; it would be better not to cause cache thrashing too much - it encourages memory fragmentation (experienced people will allocate event data for all events in the same buffer, wonder about inexperienced ones, one malloc per event data? :) ) - it doesn't deal with large data properly (because the plugin cannot start owning the raw event data instead of copying it from the buffer provided); imagine copying a video buffer in the process() function of a plugin! I'm not saying that approach is Really Bad - just that it's kind of a pre-optimization version of my proposal (I made MIDI data very efficient, float parameter data slightly less efficient, float parameter data with deltas even less efficient, and binary data are pretty inefficient :) ). The fact that event has to be handled is annoying enough on its own :) - I have to end the inner loop, store state information somewhere etc. - I don't want some additional, unnecessary memory accesses which may throw sample data and buffers out of the cache. (Obviously just a quick generic knock-off to get the idea across). In networkey terms, this is separating transport from contents, which is pretty firmly established as a Good Idea. In network context, yes. However, _optimizing_ for uncommon case is not a preferable approach to me. The arbitrary binary data command (0x72) mentioned in my proposal can give you practically everything you need, and can be used in a network-transparent way, as long as data in the binary data chunks are self-contained (don't
Re: [LAD] enhanced event port LV2 extension proposal
On Tue, 2007-11-27 at 10:46 +, Krzysztof Foltman wrote: Well, sometimes you need to find the right tradeoff between being efficient (memory- and speed-wise) and generic. ++ KzF I think I've found an acceptable tradeoff (definitely favouring speed, but not losing generality and not very memory-hungry). ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Tue, 2007-11-27 at 10:46 +, Krzysztof Foltman wrote: Dave Robillard wrote: Why not make it satisfy most everyone by being extensible? It *is* extensible. Note that commands 0x00-0x6F and 0x73-0x7F are unused, so further extensions are free to define them (perhaps we need a scheme for binding extension URIs to command numbers, to make it more LV2-ey). And 0x72 command can be used for pretty much any data larger than 8+2 octets. extensible like SYSEX. It isn't 1980 anymore... I think it's not just not a bad one. The other possibility (multiple event ports) is less efficient, and speed is crucial here. It's also more complex, looking from plugin author's perspective. So I had little choice. Your extension is too complex, this is primarily why I don't like it. Why are there two uint8_t's in the header that are probably completely useless for essentially everything but MIDI? It's crufty. A stamped chunk of some sort of event is simple to understand, guaranteed 100% extensible and generic without any weird sysexey 1980'sness, and is very, very similar to how LV2 ports themselves work (and therefore obvious and easy to understand for anyone who knows LV2). Notice that I just took the approach of optimizing for most common case (MIDI events), and tried to maximize functionality while keeping block size small and constant (to avoid pointer arithmetic that was complicating Lars' proposal a bit). Trying to pre-define things from the top down like this is un-lv2-ey). Well, sometimes you need to find the right tradeoff between being efficient (memory- and speed-wise) and generic. I think I've found an acceptable tradeoff (definitely favouring speed, but not losing generality and not very memory-hungry). Your efficiency claims make no sense, frankly. Put everything in a flat buffer and voila, it's exactly how MIDI events work right now. Your proposal has no inherent performance advantage over a more obvious and cleaner one. However, I had to make some assumptions about how it will be used (mostly implemented by inexperienced people, mostly used for MIDI and float parameters, seldom used for advanced stuff). Oh well, I'm repeating myself here :) Exactly. You are making assumptions about what people want to put in there. This is a Bad Thing(TM). If it can be avoided, it should be (and it can definitely be avoided). I have said this a lot, and I will continue saying it more until the end of time because it's important: the fact that ports can contain /anything/ is the fundamental core idea behind LV2, and it's a good one. A good generic event extension must do this as well. Event transport and event contents are orthogonal problems and they should be solved separately, otherwise they won't be solved well because too much is on the table to be figured out at one time (how far would the Internet have come if every high level protocol was defined in the IP specification?) Your optimisation comments (the bulk of your email) are based on a misunderstanding. I don't mean the data pointer to point somewhere else in memory, it's a flat buffer (ie almost identical to how MIDI works right now). For large data, that flat buffer can contain a pointer or whatever (again, see LV2 port extensions that can e.g. contain structs with whatever you feel like cramming in there). In short: if generic events is the goal, then lets make the events generic! I'm adamant about this for pragmatic reasons: There are some more difficult problems here if we really want to crank up the performance of this, especially random event access and constant time buffer traversal which needs an lookup table to be around somewhere. I want to make the event contents part of this discussion go away so we can focus on the real problem at hand and ignore the (completely orthogonal) details of event types. The transport part alone is a problem that needs solving /first/. Don't get me wrong, I think it's great someone is tackling this one, and would implement it in a heartbeat, but it needs to be done more in the spirit of LV2. Note that commands 0x00-0x6F and 0x73-0x7F are unused is more the spirit of MIDI circa 30 years ago. It might be kinda sorta extensible for some limited definition of extensible, but we can do better.. having to appeal to some (nonexistant) central authority for doling out a numeric range for features is completely out of the question (we learned that one with LADSPA IDs). Cheers, -DR- P.S. I tried to see this (generic events) happen with Jack MIDI too but it was shot down for silly reasons. Real extensibility means we're free to hack without having to convince a bunch of sticks in the mud on mailing lists that we're right (or tell them anything at all for that matter), which IMO has held back LAD progress in several areas for years. I absolutely guarantee that people will do new and interesting things with events if we /completely/ eliminate the diplomatic overhead of
Re: [LAD] enhanced event port LV2 extension proposal
On Nov 27, 2007 11:24 PM, Dave Robillard [EMAIL PROTECTED] wrote: I have said this a lot, and I will continue saying it more until the end of time because it's important: the fact that ports can contain /anything/ is the fundamental core idea behind LV2, and it's a good one. A good generic event extension must do this as well. Dave, I love it when you go crazy over a piece of spec. Just don't change anything :-) And for what it's worth, I agree with you on most parts... Cheers, __ Marc-Olivier Barre, MarcO'Chapeau. ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev
Re: [LAD] enhanced event port LV2 extension proposal
On Wed, 2007-11-28 at 01:05 +0100, Marc-Olivier Barre wrote: On Nov 27, 2007 11:24 PM, Dave Robillard [EMAIL PROTECTED] wrote: I have said this a lot, and I will continue saying it more until the end of time because it's important: the fact that ports can contain /anything/ is the fundamental core idea behind LV2, and it's a good one. A good generic event extension must do this as well. Dave, I love it when you go crazy over a piece of spec. Just don't change anything :-) I try :) -DR- ___ Linux-audio-dev mailing list Linux-audio-dev@lists.linuxaudio.org http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev