<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>drobilla.net - Hacking</title><link href="https://drobilla.net/" rel="alternate"/><link href="https://drobilla.net/category/hacking/feed/atom" rel="self"/><id>https://drobilla.net/</id><updated>2025-11-26T21:36:00-05:00</updated><entry><title>A More Modern Gtk3 Jalv Frontend</title><link href="https://drobilla.net/2025/11/26/a-more-modern-gtk3-jalv-frontend.html" rel="alternate"/><published>2025-11-26T21:36:00-05:00</published><updated>2025-11-26T21:36:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2025-11-26:/2025/11/26/a-more-modern-gtk3-jalv-frontend.html</id><content type="html">&lt;p&gt;My simple single-plugin LV2 host, &lt;a href="http://drobilla.net/software/jalv"&gt;Jalv&lt;/a&gt;, isn't quite sure whether it's a developer utility or polished user program, but in any case, it had become stale in the past few years and needed an update.&lt;/p&gt;
&lt;p&gt;Most of those changes are internal and only interesting for those who use it as a basis for larger systems.  The internals have been largely rewritten to support various things, but this post isn't about that.  This post is about a more obviously stale thing: the Gtk2 interface.&lt;/p&gt;
&lt;p&gt;In keeping with the free desktop tradition of constant breakage with reduced functionality, that toolkit is now EOLed, and soon the ability to embed GUIs whatsoever will probably go away.  Luckily though, we're not quite there yet, and it's still possible/feasible to embed GUIs in Gtk3 (at least on X11), so things can continue roughly as they were for a while.  Gtk2 &lt;em&gt;is&lt;/em&gt; EOLed though, which is a problem for distributions, and I have no interest in maintaining code for a dead toolkit, so that frontend is gone entirely in the latest release.  This does mean that some plugin GUIs written in Gtk2 will no longer work, but that's inherent to the situation (and why general plugin GUIs shouldn't use Gtk).&lt;/p&gt;
&lt;p&gt;This seemed like a good time to update the UI to be a bit more “modern”, particularly since a menu bar has never really made much sense here anyway.
I replaced this with a header bar, which I think does suit plugins better.  For example, here's the custom GUI for the &lt;a href="https://lsp-plug.in/"&gt;LSP&lt;/a&gt; Compressor:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/lsp_compressor_in_jalv.png"&gt;&lt;img alt="Custom UI for LSP Compressor" src="/images/lsp_compressor_in_jalv-357x222.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As always, there's also generic controls, with a few refinements but still using the same boring stock widgets:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/amsynth_in_jalv.png"&gt;&lt;img alt="Generic UI for amsynth" src="/images/amsynth_in_jalv-208x239.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All of the menu items have been moved into a single menu button, which is a pattern I'm sceptical of in general, but it works fine for a very simple application like this.  The preset menu can be unwieldy, but that's a whole topic unto itself that I hope to tackle more comprehensively later.&lt;/p&gt;
&lt;p&gt;Code-wise, it's long been a problem that the rudimentary (lack of) architecture couldn't easily support the more advanced features people wanted from it.  So, I've reworked everything into a more serious application, with a more explicit architecture and communication patterns that make adding new features much easier.  As far as the Gtk frontend goes, I've also switched to using more modern APIs like &lt;code&gt;GtkApplication&lt;/code&gt;, &lt;code&gt;GAction&lt;/code&gt;, and so on.  To be fair, these parts are quite nice.  Actions are a pretty good model for building accessible GUI applications, and these new APIs encourage doing the right thing.
There's still some areas that need work, but &lt;code&gt;jalv.gtk3&lt;/code&gt; (the version which has a &lt;code&gt;.desktop&lt;/code&gt; file and all that) is much closer to being a proper application that integrates with the desktop environment now, and smells less like a hackey program that developers just use to check if their plugin works.&lt;/p&gt;
&lt;p&gt;That aside, Jalv is still frequently used from the command-line, and there's a
major QoL improvement there as well: the positional argument now accepts files
and directories, not just plugin URIs.  The code will try to figure out what to
do automatically, for example, if a bundle or data file only describes a
single plugin, then that plugin is loaded.  Presets can also be passed (by path
or by URI), which will load the appropriate plugin with that preset initially applied.  In short, it's more like the “do what I mean” interface many people expect.&lt;/p&gt;
&lt;p&gt;It's been entirely too long since the last release, but now that the host libraries and Jalv are up to date with most issues resolved, I'm going to try to do some broader cross-project efforts to address a few things that are a mess across the LV2 ecosystem as a whole, with Jalv serving as a sort of reference implementation.
For now, though, it's just a much better implementation of the same old features.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Lilv “Object Index” Option Revised</title><link href="https://drobilla.net/2025/11/12/lilv-object-index-option-revised.html" rel="alternate"/><published>2025-11-12T13:58:00-05:00</published><updated>2025-11-12T13:58:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2025-11-12:/2025/11/12/lilv-object-index-option-revised.html</id><content type="html">&lt;p&gt;My previous post, &lt;a href="https://drobilla.net/2025/09/29/improving-lv2-discovery-performance.html"&gt;“Improving LV2 Discovery Performance”&lt;/a&gt;, described adding an option to &lt;a href="https://drobilla.net/software/lilv/"&gt;lilv&lt;/a&gt; that controls whether an additional index is maintained for loaded LV2 data.
While testing those changes for release, I discovered a few things (like &lt;a href="https://git.open-music-kontrollers.ch/~hp/lv2lint"&gt;lv2lint&lt;/a&gt;) that break without the index, which convinced me that disabling it by default wasn't the right decision.&lt;/p&gt;
&lt;p&gt;So, I'm instead enabling it by default, which avoids this “break by default” situation.
The downside is that hosts will need to add code in order to enjoy the performance improvements.
To make this change as simple as possible, I also made &lt;code&gt;lilv_world_set_option&lt;/code&gt; accept a null value to disable boolean options, so most hosts can simply add this line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;lilv_world_set_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LILV_OPTION_OBJECT_INDEX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This requires the new version, but the same change can be made without bumping the required version by avoiding the niceties:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;LilvNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false_node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lilv_new_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;lilv_world_set_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://drobilla.net/ns/lilv#object-index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;false_node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;lilv_node_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;false_node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will log a warning about the unsupported option on older versions, but otherwise have no effect.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/><category term="Lilv"/><category term="RDF"/></entry><entry><title>Improving LV2 Discovery Performance</title><link href="https://drobilla.net/2025/09/29/improving-lv2-discovery-performance.html" rel="alternate"/><published>2025-09-29T10:25:00-04:00</published><updated>2025-09-29T10:25:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2025-09-29:/2025/09/29/improving-lv2-discovery-performance.html</id><content type="html">&lt;p&gt;LV2 plugins (and other data, like presets) are discovered by parsing &lt;a href="https://www.w3.org/TR/turtle/"&gt;Turtle&lt;/a&gt; files installed in various bundle directories.
If the host only needs to know &lt;em&gt;which&lt;/em&gt; things are installed, then only &lt;code&gt;manifest.ttl&lt;/code&gt; files need to be loaded.
That takes only a fraction of a second (60 ms on this machine, which has 897 plugins installed), but if the host needs more information (like the plugin's label or what ports it has), then the data files need to be loaded as well, which can take several seconds.
The parser (from &lt;a href="https://drobilla.net/software/serd/"&gt;serd&lt;/a&gt;) is very fast, so the overwhelming majority of this time is spent inserting data into a model.&lt;/p&gt;
&lt;p&gt;I was recently doing some work on &lt;a href="https://drobilla.net/software/lilv/"&gt;lilv&lt;/a&gt; related to discovery, and got sidetracked into investigating how much of this overhead could be eliminated.
Quite a bit, as it turns out, but before explaining how, I'll give a brief summary of the relevant fundamentals so people who don't spend their days in the triple mines can understand what I'm talking about.&lt;/p&gt;
&lt;p&gt;LV2 data is written in the &lt;a href="https://www.w3.org/TR/turtle/"&gt;Turtle&lt;/a&gt; syntax, which can have a lot of structure, but is ultimately just a shorthand for data that is simply a “flat” series of triples.
For example, the abbreviated Turtle&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;&amp;lt;http://example.org/amp&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;a&lt;/span&gt; &lt;span class="nn"&gt;lv2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;Plugin&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nn"&gt;doap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Amplifier&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;represents the two triples&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;&amp;lt;http://example.org/amp&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;  &lt;span class="nn"&gt;lv2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;Plugin&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;&amp;lt;http://example.org/amp&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;doap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Amplifier&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which describe a plugin with the name “Amplifier”.&lt;/p&gt;
&lt;p&gt;Two implications of this format are important to understand here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;All data is a set of triples which can be stored in some lexicographical order to be quickly searchable (for example, a set ordered subject-first can be used to quickly find triples that start with a given subject).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All data can be streamed via a simple “flat” interface (for example a function with three parameters), and is trivial to inspect and/or filter on the fly (much like line-based text in POSIX pipelines).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The data may not be stored as a “literal” ordered set of triples (they're actually &lt;em&gt;quads&lt;/em&gt; in memory for one thing), but this simplified way of thinking about it is good enough for this explanation.&lt;/p&gt;
&lt;p&gt;Which order is best depends on the query.
For example (borrowing a bit of syntax from &lt;a href="https://www.w3.org/TR/sparql11-query/"&gt;SPARQL&lt;/a&gt; where &lt;code&gt;?name&lt;/code&gt; represents a query variable), if the host asks something like “what's the name of this plugin” or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;&amp;lt;http://example.org/amp&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;doap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt; &lt;span class="nv"&gt;?name&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then it needs to find triples that start with the given subject and predicate (&lt;code&gt;&amp;lt;http://example.org/amp&amp;gt;&lt;/code&gt; and &lt;code&gt;doap:name&lt;/code&gt;) to see what object (&lt;code&gt;?name&lt;/code&gt;) they have.
So, a subject-predicate-object or “SPO” index will work well.
If, however, the host asks something like “which things are plugins” or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;?plugin&lt;/span&gt; &lt;span class="nn"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt; &lt;span class="nn"&gt;lv2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;Plugin&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then an SPO index doesn't help because there's no known subject to search for.
Most queries are like this, with either a subject or object wildcard (querying relatively “fixed” predicates is rare), so lilv has always had both an SPO and OPS index to support them.&lt;/p&gt;
&lt;p&gt;This is convenient, but twice the indexing means roughly twice the overhead.
The SPO index is the natural order used in the syntax, and supports most code (which mainly looks up properties of known things), but the OPS index isn't used so much.
Can it be removed entirely to speed things up?
It &lt;em&gt;is&lt;/em&gt; used for many important things, but conveniently, there's only a few fixed cases of those (like finding plugins as above).
So, we can take advantage of the streaming nature of the data to record this information while it's being read, instead of inserting it into an index only to query it out later.&lt;/p&gt;
&lt;p&gt;To do this in the implementation, I introduced the concept of a “skimmer”.
A skimmer inserts triples into a model as usual, but first “skims” them and records items of interest for later use.
For example, a skimmer checks whether a triple has &lt;code&gt;rdf:type lv2:Plugin&lt;/code&gt;, and if so, records the subject in a result set that stores only plugin URIs.
Some cases are a bit trickier, and actually pulling this off cleanly took quite an overhaul, but details aside, it turns out that this approach can be used to eliminate the OPS index entirely without losing any functionality.&lt;/p&gt;
&lt;p&gt;How much of an improvement does this make?
On this machine, using &lt;code&gt;lv2ls -n&lt;/code&gt; as a crude benchmark, the time goes from 3.11 to 1.72 seconds, or about a 45% improvement.
The memory consumption goes down a bit as well, from (even more crudely) a max-RSS of about 138 to 112 MiB, or about a 19% improvement (everything here is an average of three runs).
The improvement will vary dramatically based on what's installed, for example in some earlier tests with a different configuration I saw drops from around 7 to 4 seconds, but in general, not bad!&lt;/p&gt;
&lt;p&gt;Are there any downsides to this?
Yes, primarily two:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;As with any significant change, there is the possibility of regression.
   Discovery is a little more restricted than before, so although I've done my best to test things, there is a possibility of things not being discovered anymore.
   Usually the fix for this is adding information to the manifest that should have been there anyway.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Though the implementation itself doesn't make queries that require the OPS index, hosts themselves can since lilv provides generic query functions.
   To support this, there's a new boolean option, &lt;code&gt;LILV_OPTION_OBJECT_INDEX&lt;/code&gt;, which can be used to enable or disable the OPS index.
   Since most hosts don't need it, I decided to disable this by default.
   That will be a regression for hosts that do, however, who need to explicitly opt in to the old behaviour by enabling this option.
   A warning is printed if a query will be slow because of the missing index, so it should at least be obvious if this happens.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These changes are now in the &lt;code&gt;main&lt;/code&gt; branch in git, which will be released soon, probably as version 0.26.0.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/><category term="Lilv"/><category term="RDF"/></entry><entry><title>Software Pages Removed</title><link href="https://drobilla.net/2025/02/16/software-pages-removed.html" rel="alternate"/><published>2025-02-16T11:32:00-05:00</published><updated>2025-02-16T11:32:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2025-02-16:/2025/02/16/software-pages-removed.html</id><content type="html">&lt;p&gt;I haven't been sure what to do about the software pages here for quite a while.
Most of them were essentially just stale versions of the README files from their projects,
and for better and worse, most of the projects I maintain are libraries that don't have as much of a need for a homepage as user-facing software.
It's easy to just ignore things that don't really matter in day-to-day work,
but the embarrassingly bad state of things became really clear when I sat down to actually poke through this site.&lt;/p&gt;
&lt;p&gt;Since it's much more important for me right now to streamline maintenance duties and eliminate as much overhead as possible,
I've simply removed all of the software pages, and redirected those addresses to the corresponding Gitlab projects where possible.
I might bring them back at some point, but for now, no pages are better than stale pages that really only serve to make things look bad.
I don't have traffic metrics here, but I seriously doubt anyone will either notice or care.&lt;/p&gt;
&lt;p&gt;I'm not sure about the utility of the software release posts or tarballs either.
Ideally, the effort required to make a release could be reduced to simply pushing a git tag,
and cross-domain posting hugely complicates that.
Besides, the tarballs are made manually on my personal machine, so they're absolutely &lt;em&gt;less&lt;/em&gt; trustworthy than the signed tags in git anyway, and, I assume, not reproducible.
At the same time, for many reasons I'm wary of fully investing in some git forge or another,
the automatic tarballs provided by all of them leave much to be desired (the silly “v” names for example),
and I don't want to disrupt things for packagers.
We'll see, but for now I'll leave the mechanics of actual releases as they are.&lt;/p&gt;
&lt;p&gt;Ultimately, pages and posts are largely a waste of time for libraries and similar things that only support other projects anyway.
So, a more radical simplification of the release process would be a good idea,
but for now I'll just take out the trash and reduce the amount of things I need to consider in that process.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Intermission</title><link href="https://drobilla.net/2025/02/04/intermission.html" rel="alternate"/><published>2025-02-04T14:40:00-05:00</published><updated>2025-02-04T14:40:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2025-02-04:/2025/02/04/intermission.html</id><content type="html">&lt;p&gt;I don't suppose inactivity from me will be terribly surprising to anyone after the past several years.
Still, since I was working on emptying my bug-fix and maintenance queue, making releases, and finally getting to some significant forward progress again, an update:&lt;/p&gt;
&lt;p&gt;Unfortunately I got violently ill last week with some horrible flu-like thing, the worst I've ever had (and I'm a COVID casualty, so that's saying quite a lot).
Somehow, it's still going strong.
So, aside from maybe a few minutes a day of idle tinkering, everything I was doing is on pause for a while.
Hopefully a short while, but so far so not good, so we'll see.&lt;/p&gt;
&lt;p&gt;As an added bonus, Google just bricked my phone with a botched forced update, so I'm locked out of 2FA and many other things besides (I suppose I had to learn the hard way that I've gotten lazy and too dependent on that horrible device).
So, yeah, things aren't going great, to put it lightly.&lt;/p&gt;
&lt;p&gt;On the bright side, I do have some exciting things in the queue, but since I don't do vapourware or hype (to a fault, really), and have a huge amount of “infrastructure” work to do first anyway, you'll have to stay tuned for that.
Assuming I don't die first, anyway.&lt;/p&gt;
&lt;p&gt;... if I do, it &lt;em&gt;would&lt;/em&gt; be pretty funny that this was my last post though, so I've got that going for me, which is nice.&lt;/p&gt;
&lt;p&gt;[Edited on 2025-02-16 to remove broken link]&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Meson Features and Packaging</title><link href="https://drobilla.net/2022/08/16/on-meson-features.html" rel="alternate"/><published>2022-08-16T15:42:00-04:00</published><updated>2022-08-16T15:42:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2022-08-16:/2022/08/16/on-meson-features.html</id><content type="html">&lt;p&gt;Recently, I have been porting all of the software I maintain to &lt;a href="//mesonbuild.com"&gt;meson&lt;/a&gt;.
For the most part, this has been a nice improvement.
Meson strives to be a generally sensible and coherent build system that addresses the needs of both upstream developers and packagers,
and it shows.  It isn't perfect (like all software), but it's a pretty big improvement over what came before in many ways.&lt;/p&gt;
&lt;p&gt;One disagreement has come up, however, and I think it - at best - betrays a misunderstanding of meson feature options, and - at worst - threatens to effectively destroy their utility entirely for no good reason.
This disagreement originates from &lt;code&gt;arch-meson&lt;/code&gt;, which adds &lt;code&gt;--auto-features=enabled&lt;/code&gt; by default.
This isn't technically &lt;em&gt;wrong&lt;/em&gt;, but it's problematic at scale.
To explain why, I first need to explain how meson feature options work.&lt;/p&gt;
&lt;p&gt;Meson feature options are a better solution to the &lt;code&gt;--enable-this&lt;/code&gt; and &lt;code&gt;--disable-that&lt;/code&gt; style options of autoconf and similar.  They make all the possible states of a feature clear and explicit.  Those states are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;disabled&lt;/code&gt;, which unconditionally disables the feature.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;, which enables the feature if possible (typically if dependencies are found).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enabled&lt;/code&gt;, which unconditionally enables the feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The default value for an option can be any of the three, and is given by the developer in the option definition.
This is a better system because there are three states with clear meanings.
They allow the builder to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure that a feature is definitely not included.&lt;/li&gt;
&lt;li&gt;Ask that a feature be included if possible.&lt;/li&gt;
&lt;li&gt;Ensure that a feature is definitely included (failing to configure otherwise).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these states (and desires) are distinct and, importantly, their definitions are not conditional.
There is no “unless” in the definition of &lt;code&gt;disabled&lt;/code&gt; or &lt;code&gt;enabled&lt;/code&gt;,
and adding one would destroy their utility.
It's not “enabled unless someone &lt;em&gt;feels&lt;/em&gt; like it's fine to disable”, it's just “enabled”.&lt;/p&gt;
&lt;p&gt;Concretely, for &lt;code&gt;enabled&lt;/code&gt; in particular, this means that, as a builder of whatever meson project, you can:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;meson setup build -Dsomething=enabled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, you know that if configuration succeeded, the &lt;code&gt;something&lt;/code&gt; feature is enabled.
This is important.
It is also &lt;em&gt;the only way&lt;/em&gt; to reliably/mechanically ensure that a build contains a feature.
“Destroying” it (as something meson users can generally expect) would therefore mean that there &lt;em&gt;there is no way&lt;/em&gt; to ensure that a build contains a feature in general.
Surely everyone - especially packagers - can agree that's bad.&lt;/p&gt;
&lt;p&gt;Hopefully that's a decent enough explanation of why meson feature options are a good, coherent, and reliable system.&lt;/p&gt;
&lt;p&gt;What's the problem then?
Well, there is that convenience option, &lt;code&gt;--auto-features=enabled&lt;/code&gt;,
which enables all features for convenience.
This allows the builder to override the developer-chosen defaults,
and force the build script to be explicit about all non-enabled features.
This is, again, not technically wrong, and is indeed convenient at times.&lt;/p&gt;
&lt;p&gt;The problem is more of a social one.
Because Arch chose to make this the default (unlike most other distributions, as far as I can tell),
people have apparently adopted the mentality that &lt;code&gt;--auto-features=enabled&lt;/code&gt; not building is an upstream bug.
It isn't.
The shorthand &lt;code&gt;--auto-features&lt;/code&gt; is just that - a shorthand for enabling all features.
It is equivalent to explicitly enabling every feature independently,
so doing so is quite literally &lt;em&gt;asking&lt;/em&gt; for the build to fail if &lt;code&gt;any&lt;/code&gt; feature is not present.&lt;/p&gt;
&lt;p&gt;Where this gets weird is when features seem unlikely,
or even impossible,
to build on a given system.
The bug filed against my project was about Cocoa on Linux.
I get it, Cocoa is &lt;em&gt;mostly&lt;/em&gt; a MacOS thing, and is &lt;em&gt;almost certainly&lt;/em&gt; unavailable elsewhere,
but my argument is that this doesn't matter at all.
Even if it were literally impossible for Cocoa to exist on top of, say, FreeBSD somewhere (which it isn't, and cross-compiling is a thing anyway),
it still wouldn't matter.
A similar case can be made for optional dependencies that are not publicly available at all (something I have done in the past).
Feature options working as expected in general is much more important than any of this subjective case-specific thinking.&lt;/p&gt;
&lt;p&gt;“This doesn't work for me, so the package should not build when this feature is enabled” is not a good argument,
regardless of &lt;em&gt;why&lt;/em&gt; it doesn't work.
It doesn't matter if you imagine a feature “can't” be built on some system
(anyone who has been in this game long enough should be able to see where that slippery slope leads...).
The power of feature options depend on the states meaning just what they say,
and making &lt;code&gt;enabled&lt;/code&gt; &lt;em&gt;sometimes&lt;/em&gt; effectively equivalent to &lt;code&gt;auto&lt;/code&gt; destroys that power.
There's not really any point in using features at that point,
you might as well just use a boolean flag.&lt;/p&gt;
&lt;p&gt;Put another way, the proposed “fix” here would make it so that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;meson setup build -Dsomething=enabled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Can successfully configure, but &lt;em&gt;without&lt;/em&gt; the &lt;code&gt;something&lt;/code&gt; feature being included.
Et voila, we've made the feature states effectively meaningless.
&lt;em&gt;That&lt;/em&gt; would be a bug, and letting this mentality take over would resign us back to manually wading through logs to see if what we asked for &lt;em&gt;actually&lt;/em&gt; happened.&lt;/p&gt;
&lt;p&gt;It's also worth noting that feature options are deeply integrated all over the place in meson,
for example, you can pass them to as “required” parameters and the right thing will happen.
Diluting the meaning of &lt;code&gt;enabled&lt;/code&gt; would cause deeper problems than the builder-visible ones explained here.
What does it even mean for a feature to be enabled, except not &lt;em&gt;actually&lt;/em&gt; enabled,
when that feature status is being used to dictate that another dependency is required?
Everything falls apart.
The feature option system, when used as intended, is designed well,
but messing around with the meaning of &lt;code&gt;enabled&lt;/code&gt; like this ruins it.
I don't think it's hyperbole to say it “destroys” the whole thing:
it turns a sensible and coherent system into some confused boolean,
which is both objectively less powerful, and, well... confusing.&lt;/p&gt;
&lt;p&gt;This may seem trivial when you're zoomed in to a single project or handful of options,
but globally it is a much bigger deal than it might seem at first.
Even in my relatively tidy third-party source directory,
there's nearly a &lt;em&gt;thousand&lt;/em&gt; meson options.
I can't imagine how many there are globally.
The basic way that features work can't depend on the builder understanding details about every single option,
that would be a complete disaster.
It is crucial that &lt;code&gt;disabled&lt;/code&gt; and &lt;code&gt;enabled&lt;/code&gt; mean exactly what they say, always.&lt;/p&gt;
&lt;p&gt;I have never seen this problem come up from a user configuring the project themselves.
Presumably, people realize that explicitly requesting a potentially failed build,
then complaining that it did what they ask,
is... rather silly.
This gets obscured when a tool (&lt;code&gt;arch-meson&lt;/code&gt;) does it automatically,
but remains just as silly.
It makes no difference from the perspective of a meson package or its maintainer:
by enabling all features, the builder has &lt;em&gt;explicitly requested&lt;/em&gt; ignoring the defaults and failing to build if &lt;em&gt;any feature&lt;/em&gt; was not present.&lt;/p&gt;
&lt;p&gt;Yet, here we are, with upstream developers getting “bug” reports &lt;em&gt;for feature options working exactly how they are supposed to work&lt;/em&gt;.
This, unfortunately, means that Arch is putting toxic pressure on the meson ecosystem,
pushing upstream developers to make things objectively worse.&lt;/p&gt;
&lt;p&gt;I don't know what the solution to that is, and I'm not an Arch developer (though I'm typing this on an Arch-derived distribution and have nothing in particular against it).
Breaking all the existing packages that use &lt;code&gt;arch-meson&lt;/code&gt; is probably not going to happen,
but perhaps improving the documentation would be good enough.&lt;/p&gt;
&lt;p&gt;All I know is that &lt;code&gt;--auto-features=enabled&lt;/code&gt; not building is &lt;em&gt;very definitely&lt;/em&gt; not a bug.
Quite the opposite, that is &lt;em&gt;literally the whole point&lt;/em&gt; of feature options.
If there's something out there that's leading people to thinking that enabled features not building is a bug,
then it is misleading people in a bad way,
and needs to be fixed.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="Software"/><category term="LAD"/></entry><entry><title>Beautiful C and C++ Documentation with Sphinx</title><link href="https://drobilla.net/2020/11/26/beautiful-c-and-cxx-documentation-with-sphinx.html" rel="alternate"/><published>2020-11-26T17:12:00-05:00</published><updated>2020-11-26T17:12:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2020-11-26:/2020/11/26/beautiful-c-and-cxx-documentation-with-sphinx.html</id><content type="html">&lt;p&gt;Like many, I've long suffered under the antiquated and inflexible HTML
documentation generated by Doxygen.  Having recently worked on some Python
documentation using Sphinx, though, I found it powerful and pleasant enough to
use.  It also has a way of encouraging actually &lt;em&gt;writing&lt;/em&gt; documentation, rather
than just generating a dump of glorified comments, which is a good thing.
Though I'm not at all a fan of ReStructuredText syntax (which at times seems
like it's trying to be cryptic on purpose), Sphinx is undeniably powerful, and
I like the “assemble a bunch of plainish text files” approach in general.  The
support for multiple languages is also very appealing, though not without its
problems, as we'll get to.&lt;/p&gt;
&lt;p&gt;So, is it possible to use Sphinx to generate documentation for C and C++
libraries?  Yes!  As explained somewhat recently in a &lt;a href="https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/"&gt;post by Sy Brand&lt;/a&gt;,
there is a project called Breathe that integrates Doxygen (for extracting
documentation) with Sphinx (for generating output).  That sounded promising, so
I attempted to migrate a library to using Breathe instead of Doxygen's HTML
support.  Unfortunately, though, I encountered quite a few roadblocks where I
couldn't quite get output that I was happy with.  Worse, the project itself is
very complicated, and as I poked around in swaths of originally generated but
manually modified code, I decided that Breathe was not for me.  That would feel
like just exchanging one inflexible and unhackable system for another.&lt;/p&gt;
&lt;p&gt;What, then, to do?  Though I realize that deep integration via modules like
Breathe is usually the way things are done with Sphinx, I am a KISS sort of
person, so I like to think of it as something more like a Static Site
Generator: it reads a bunch of plainish text input files, and outputs HTML (or
whatever other presentation format).  How do we describe C and C++ things in
Sphinx?  It turns out that recent versions have built-in support for these
“domains” now, which define markup for describing everything in these
languages.  This means that everything to do with nicely formatting and
cross-referencing C and C++ is already dealt with out of the box.  Excellent.&lt;/p&gt;
&lt;p&gt;So, taking a step back and assessing the situation: we have some XML files that
describe the documentation, and we have a tool that reads text files and
produces nice documentation.  This strikes me as a relatively straightforward
task for a nice and simple “files in, files out” script, not somewhere a
Goldbergian contraption that mashes Doxygen into Sphinx is required.  So, after
investigating any other promising options (no such luck), I resigned myself to
trying to write such a thing, at the very least to see if it's feasible.  I
certainly have no time or interest in writing and maintaining a Documentation
System, but a self-contained script to convert one thing to another seems
reasonable enough.&lt;/p&gt;
&lt;p&gt;As it turns out, I wouldn't call it trivial, but it's certainly feasible.  I
ended up with a &lt;a href="https://gitlab.com/lv2/pugl/-/blob/6a530fcaf22effb8f54f290996c02e9e90d58a7a/scripts/dox_to_sphinx.py"&gt;~700 line Python script&lt;/a&gt; that does everything I need
(though this is of course not the same as everything possible).  It's a bit
“gluey” and makes some assumptions about the structure and so on, but it does
the job and is something I feel I can maintain as necessary.  I won't be
publishing or supporting this as an independent project any time soon, and make
no claims about it being general purpose, but feel free to steal it if any of
this sounds appealing.&lt;/p&gt;
&lt;p&gt;With this, I was able to get around some long-standing gripes I have with
Doxygen, and easily make whatever I wanted to happen a reality, so I'm pretty
happy with this approach.  Everything is nicely decoupled, so I don't feel
over-invested in any of the tools involved.  If, for example, someone finally
writes a good clang-based extractor that gains traction (JSON please, I did not
enjoy this revisitation of the horrors of XML at all), I should be able to
switch to using that easily enough.  I've actually found this somewhat crude
and UNIXey approach quite convenient: you can simply look at the ReST files to
understand what is happening, or tweak them a bit and run Sphinx to test what
you're aiming for, and so on.  Text files are good.&lt;/p&gt;
&lt;p&gt;So, after however many years, I think I've found an approach to documentation
I'm actually quite happy with, that can support all of the languages that I
use, and in general doesn't seem to get in my way.  Hooray.  For starters, I
did my window system portability layer, &lt;a href="https://gitlab.com/lv2/pugl"&gt;Pugl&lt;/a&gt;.  The generated documentation
for the C API can be seen at &lt;a href="https://lv2.gitlab.io/pugl/c/singlehtml/"&gt;https://lv2.gitlab.io/pugl/c/singlehtml/&lt;/a&gt;, and
the C++ at &lt;a href="https://lv2.gitlab.io/pugl/cpp/singlehtml/"&gt;https://lv2.gitlab.io/pugl/cpp/singlehtml/&lt;/a&gt;.  This is more or less
the standard Alabaster theme with a few tweaks, which I'm not sure feels
appropriate for API documentation (and is &lt;em&gt;much&lt;/em&gt; more bloated with a bunch of
Javascript than I'd like), but it's pretty enough, at least.  I'll tinker with
themes later when I feel like jumping down that rabbit hole.&lt;/p&gt;
&lt;p&gt;The slightly cumbersome links are an artifact of the one problem I encountered
using Sphinx domains: you can't really document C &lt;em&gt;and&lt;/em&gt; C++ APIs nicely in the
same documentation set.  If you use the &lt;code&gt;cpp&lt;/code&gt; domain everywhere, you get name
mangling in links even for C symbols, which is really unfortunate, and you
can't really mix them.  To take a contrived example, if you have a &lt;code&gt;struct
MylibThing&lt;/code&gt; in C, then a type alias in C++ like &lt;code&gt;using Thing = MylibThing&lt;/code&gt;,
Sphinx isn't clever enough to figure out that &lt;code&gt;MylibThing&lt;/code&gt; is from C, and will
generate warnings and not link correctly.  Perhaps someday it will, which would
be nice, but for now I opted to simply generate completely separate
documentation sets.  This means the C documentation is duplicated in the C++
documentation so that things can be hyperlinked, which isn't ideal, but I can
live with it.  A certain amount of redundancy is inherent in multi-language
documentation anyway.&lt;/p&gt;
&lt;p&gt;As I add Python bindings to most libraries, having a unified documentation
system for all of these languages will be very nice.  There is one additional
thing I'll need at some point for the LV2 documentation in particular: a domain
for RDF properties and classes.  The LV2 documentation really suffers from an
unnatural code (via Doxygen) and data (via lv2specgen) documentation split, and
my hope is that Sphinx can provide a nice environment for writing documentation
that refers to both worlds freely.  That, unfortunately, will be much more
work, but hopefully writing a custom Sphinx domain isn't too hard...&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="C"/><category term="Cpp"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Git migration</title><link href="https://drobilla.net/2015/11/14/git-migration.html" rel="alternate"/><published>2015-11-14T16:46:00-05:00</published><updated>2015-11-14T16:46:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2015-11-14:/2015/11/14/git-migration.html</id><content type="html">&lt;p&gt;I have finally migrated all of my software to git. This was not a very
fun process due to the nested nature of my “drobillad” repository, but
now all projects live in their own git repositories with history and
tags from SVN preserved.&lt;/p&gt;
&lt;p&gt;It is still possible to build all my audio software in one step, the
top-level repository &lt;a href="http://git.drobilla.net/drobillad.git"&gt;http://git.drobilla.net/drobillad.git&lt;/a&gt; is now a
skeleton with git submodules for each project. Anyone using SVN should
switch immediately, the SVN repositories will remain in their current
state for the foreseeable future but all development activity will move
to git.&lt;/p&gt;
&lt;p&gt;All the repositories are available in &lt;a href="http://git.drobilla.net/"&gt;cgit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[Edited on 2025-02-16 to remove broken links]&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>LV2 Plugin Control Units in Ardour</title><link href="https://drobilla.net/2014/11/03/lv2-plugin-control-units-in-ardour.html" rel="alternate"/><published>2014-11-03T20:09:00-05:00</published><updated>2014-11-03T20:09:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2014-11-03:/2014/11/03/lv2-plugin-control-units-in-ardour.html</id><content type="html">&lt;p&gt;LV2 has had a “units” extension since the beginning, which allows
plugins to specify units like Hz or dB for their controls. To date this
information has not been used very widely by hosts, so I've done some
work in Ardour to use this information for better UI generation and
intelligent plugin control.&lt;/p&gt;
&lt;p&gt;Units can specify a format string which describes how to print a value
in that unit. This is now used to draw the text on control sliders:&lt;/p&gt;
&lt;p&gt;&lt;img alt="An Ardour control dialog for an LV2 plugin." src="/images/control_dialog-300x152.png"&gt;&lt;/p&gt;
&lt;p&gt;The same controls are used in automation lane headers. If the control is
a note number, then right-clicking will present a menu with an option to
pop up a note selector dialog where the value can be chosen on a piano
keyboard:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The select note menu on a note number port." src="/images/select_note_menu-300x286.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Ardour note selector dialog." src="/images/note_choose_dialog-300x51.png"&gt;&lt;/p&gt;
&lt;p&gt;Similarly, numeric frequency controls have a context menu which can set
the value to a specific number of beats in the current tempo, if the
frequency range is low:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting a low frequency port in beats." src="/images/set_beats_menu-272x300.png"&gt;&lt;/p&gt;
&lt;p&gt;If the frequency range is high, then numeric frequency ports can be set
with the note selector dialog just like note numbers:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting an audible frequency port by note." src="/images/set_freq_menu-300x83.png"&gt;&lt;/p&gt;
&lt;p&gt;In the future, it would be nice to have this idea applied more
extensively so automation lanes can “pretend” a port is in the desired
unit, for example allowing the user to automate an LFO frequency in
beats, or a cutoff frequency in notes. Until then, being able to at
least easily set controls to musically sensible values makes many
production tasks easier, particularly in electronic music where it's
often desirable to set plugin controls based on key or tempo.&lt;/p&gt;
&lt;p&gt;Up next is setting time ports based on tempo, for cases like setting
delay lines to a certain number of beats, but many plugins are missing
the unit information required to make this possible. Hopefully better
and more widespread host support will provide some incentive for plugin
authors to specify the units of their ports. It is very simple to do so,
see the &lt;a href="http://lv2plug.in/ns/extensions/units"&gt;LV2 units&lt;/a&gt; documentation
for examples.&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Pretty names in Patchage via Jack Metadata</title><link href="https://drobilla.net/2014/04/09/pretty-names-in-patchage-via-jack-metadata.html" rel="alternate"/><published>2014-04-09T21:26:00-04:00</published><updated>2014-04-09T21:26:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2014-04-09:/2014/04/09/pretty-names-in-patchage-via-jack-metadata.html</id><content type="html">&lt;p&gt;The much-awaited (by me, at least) Jack metadata API has arrived. This
will allow us to easily achieve many new things with minimal/nonexistent
API friction. One of the simplest and most obvious is pretty names for
Jack clients and ports, so I've chosen this as the first thing to tackle
(as part of a drive to get Patchage polished up for a much overdue
release).&lt;/p&gt;
&lt;p&gt;Unfortunately, as far as I can tell, there is a chicken &amp;amp; egg scenario
here since nothing is setting this metadata yet. So, I've made Jalv and
Ingen both set pretty names for their ports. In the case of Jalv, the
“pretty name” is the label given in the plugin data (distinct from the
LV2 “symbol” which is restricted and unique).&lt;/p&gt;
&lt;p&gt;Firing up Jalv with the Tal Dub III plugin (LV2 port courtesy KXStudio),
we can see the port symbols, which are a bit awkward for end users with
their prefixes and underscores. Conflating strong identifiers with
human-readable labels is a serious design error I learned of the hard
way, but that's a discussion for another time...&lt;/p&gt;
&lt;p&gt;Enable “Human Names” in the view menu, or press C-h, and voilà, we see
the pretty names set in Jack metadata (if present) instead.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/patchage_symbols.png"&gt;&lt;img alt="Tal Dub III in Jalv as shown by Patchage with human names off." src="/images/patchage_symbols.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/patchage_human_names.png"&gt;&lt;img alt="Tal Dub III in Jalv as shown by Patchage with human names enabled." src="/images/patchage_human_names.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The metadata API is very simple to use for ports, though there seems to
be a hole in the API which makes it difficult to get the UUID for your
client to set metadata (I want a simple jack_client_uuid, like
jack_port_uuid, but it seems you have to get a string UUID and parse
it to a jack_uuid_t, clunky enough that I just didn't bother). In any
case, I am happy to see a low-friction mechanism in Jack which apps can
use to share metadata towards making a better user experience.&lt;/p&gt;
&lt;p&gt;It will be interesting to see what sort of information proves useful and
becomes established/standard. For those of us of a mad scientist bent
who live in a nest of patch cables, a CV tag seems like another obvious
simple step, but for everyone, a big step is finally having meaningful
port grouping and channel roles. I have always liked to joke that Jack
(like LADSPA) doesn't &lt;em&gt;really&lt;/em&gt; even do stereo, but with metadata, we can
mark up stereo, 5.1, Ambisonics, etc., and other clients will be able to
make sense of the channel assignments without resorting to dirty kludges
based on guessing from names. All without changing the ABI one bit. Good
stuff.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Labeled MIDI controller values in Ardour</title><link href="https://drobilla.net/2014/01/06/labeled-midi-controller-values-in-ardour.html" rel="alternate"/><published>2014-01-06T17:09:00-05:00</published><updated>2014-01-06T17:09:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2014-01-06:/2014/01/06/labeled-midi-controller-values-in-ardour.html</id><content type="html">&lt;p&gt;A while ago, I implemented &lt;a href="/2013/01/20/ardour-midi-patch-controller-and-note-names.html"&gt;MIDI patch, controller, and note name
support for
Ardour&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There was one thing missing from that work: labelled controller values.
This is particularly useful for controllers that don't represent a
continuous numeric parameter, but instead have a set of specific values.
For example, the Moog Minitaur supports CC #86 to control the LFO sync
clock division, but it's not obvious how a 0-127 number maps to a clock
division. From the manual, we can learn that 61-67 is “1/4”, 68-73 is
“1/8 Dot”, and so on.&lt;/p&gt;
&lt;p&gt;Translate that information into standard MIDINameDocument format, and a
few hours of hacking later... voila, Ardour displays something sensible
when hovering over an automation point instead of a meaningless number.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ardour_midi_cc_cursor.png"&gt;&lt;img alt="Ardour displaying a labelled MIDI controller value when hovering over an automation point." src="/images/ardour_midi_cc_cursor-150x85.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to add support for your MIDI device or program, my .midnam
documents for the Moog Minitaur, MF-104M, and MF-108M, included in the
Ardour distribution all contain examples of labelled values. It should
be relatively obvious how to copy and modify these for other devices.&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Interactive Force-Directed Graph Layout in Ingen</title><link href="https://drobilla.net/2013/12/20/interactive-force-directed-graph-layout-in-ingen.html" rel="alternate"/><published>2013-12-20T00:51:00-05:00</published><updated>2013-12-20T00:51:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2013-12-20:/2013/12/20/interactive-force-directed-graph-layout-in-ingen.html</id><content type="html">&lt;p&gt;GraphViz has, unfortunately, broken reverse compatibility of their
library API, which has been nagging as a maintenance headache for
FlowCanvas and its successor
&lt;a href="/software/ganv"&gt;Ganv&lt;/a&gt; for a while now. It would be
nice to just get rid of that dependency altogether...&lt;/p&gt;
&lt;p&gt;Meanwhile, I had to write some force-directed graph layout (FDGL) code
for another project...&lt;/p&gt;
&lt;p&gt;... You can probably see where this is going. In the spirit of it
theoretically being a holiday (there are no holidays in practice in grad
school), I spent some time implementing live FDGL in Ganv, and recorded
a &lt;a href="/videos/ganv_fdgl.ogv"&gt;video of interactive layout in Ingen&lt;/a&gt;
to show progress.&lt;/p&gt;
&lt;p&gt;One nice thing about using live FDGL in a program like
&lt;a href="/software/ingen"&gt;Ingen&lt;/a&gt; is that the user can
customize the layout in an intuitive tactile way, since objects behave
physical laws. This video demonstrates some manual rearranging of a
patch, and a total reconfiguration by switching the signal flow
direction. Manual tweaking of the layout like this was not possible with
the previous GraphViz-based layout code.&lt;/p&gt;
&lt;p&gt;It took quite a bit of tweaking to get the physics working well, so
layouts looked good but the patch didn't explode if there are
disconnected components. It's still not quite ideal, but usable.&lt;/p&gt;
&lt;p&gt;This is implemented at the Ganv level and thus works in Ingen (as
shown), Patchage, and Machina. I'm still not confident enough in this
implementation to enable it by default and/or drop Graphviz quite yet,
but the adventurous who follow svn, &lt;code&gt;./waf configure --debug --fdgl&lt;/code&gt;. If
you're hit by the graphviz breakage (and I still haven't fixed the
check...), add &lt;code&gt;--no-graphviz&lt;/code&gt; to work around the problem.&lt;/p&gt;
&lt;p&gt;[Edited on 2025-02-16 to remove broken link]&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Better Stock LV2 Plugin UIs</title><link href="https://drobilla.net/2013/02/10/better-stock-lv2-plugin-uis.html" rel="alternate"/><published>2013-02-10T22:11:00-05:00</published><updated>2013-02-10T22:11:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2013-02-10:/2013/02/10/better-stock-lv2-plugin-uis.html</id><content type="html">&lt;p&gt;Today I implemented better UI generation for LV2 plugin controls in
&lt;a href="/software/jalv"&gt;Jalv&lt;/a&gt;, particularly grouping
controls under headings which makes a big difference. Unfortunately few
plugins group their controls currently, but hopefully more host support
will provide the incentive to do so.&lt;/p&gt;
&lt;p&gt;Other improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added a spinbutton to slider controls for precisely setting values
   numerically&lt;/li&gt;
&lt;li&gt;Much more efficient layout to pack more controls on the screen at
   once&lt;/li&gt;
&lt;li&gt;Support for named scale points (“ticks”) on non-enumeration ports&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the UI generated for
&lt;a href="http://code.google.com/p/amsynth/"&gt;Amsynth&lt;/a&gt; (after I added the
necessary metadata):&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/jalv_amsynth.png"&gt;&lt;img alt="Generic Jalv UI for Amsynth LV2" src="/images/jalv_amsynth-112x150.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Still pretty utilitarian, but much more usable, which is the important
thing. Of course, this plugin is quite large, and has a pretty good
custom UI, but I happened to be polishing up its metadata anyway and the
controls group nicely so I used it as my test case.&lt;/p&gt;
&lt;p&gt;Maybe some day this code will get smarter and evolve into a library that
can be used by other Gtk hosts. Better group layout is the obvious “next
level” advancement, a flat linear list of controls is pretty limited.
Unfortunately there's no stock Gtk container which can do text-like
reflow, which would be ideal. Perhaps a simpler scheme based on a
maximum reasonable width for a controller would do, where the table will
expand to have more columns if the window is wide enough for several.&lt;/p&gt;
&lt;p&gt;Most of the metadata required to generate a good UI is also useful for
other purposes, for example groups make building a decent menu of many
controls feasible (e.g. to add automation lanes in
&lt;a href="http://ardour.org"&gt;Ardour&lt;/a&gt;), and some types of host UIs like a &lt;a href="/software/ingen"&gt;modular
patcher&lt;/a&gt; inherently need to generate
their own “UI” of sorts.&lt;/p&gt;
&lt;p&gt;Good metadata is not at odds with custom UIs, they each have their uses,
but it is important to remove the burden of custom UIs for simple
plugins with just a few controls that really don't need them. It's a
waste of that most precious of all resources - developer time - to have
to code UIs for a few sliders and toggles. Hopefully better host
generated UIs and support for more advanced controls like &lt;a href="/2013/01/10/host-generated-lv2-file-choosers.html"&gt;file
selectors&lt;/a&gt;
free up developers to spend more time making useful plugins and less
time wrestling with GUI toolkits.&lt;/p&gt;
&lt;p&gt;It is, after all, all about the sound.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Ardour MIDI Patch, Controller, and Note Names</title><link href="https://drobilla.net/2013/01/20/ardour-midi-patch-controller-and-note-names.html" rel="alternate"/><published>2013-01-20T00:50:00-05:00</published><updated>2013-01-20T00:50:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2013-01-20:/2013/01/20/ardour-midi-patch-controller-and-note-names.html</id><content type="html">&lt;p&gt;Ardour has supported displaying MIDI patch names loaded from a
&lt;a href="http://www.midi.org/dtds/midi_xml.php"&gt;MIDINameDocument&lt;/a&gt; (or “midnam”
file) for a while, though only patches, and even that was pretty flaky.
Since this has become an itch for me and it's time for Ardour MIDI to
reach release quality, I took some time to give it a serious overhaul.
In this case, a picture or two is indeed worth a thousand words:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ardour_midi_note_names.png"&gt;&lt;img alt="Human readable MIDI note names in Ardour 3" src="/images/ardour_midi_note_names-150x119.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ardour_midi_control_names.png"&gt;&lt;img alt="Human readable MIDI controller menu in Ardour" src="/images/ardour_midi_control_names-150x104.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The note names are mainly useful for doing percussion. Several of the
bundled midnam files have note names defined, and I have added one for
General MIDI drums, which more or less corresponds to a lot of
instruments, including &lt;a href="http://www.hydrogen-music.org/"&gt;Hydrogen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Controller names, to my surprise, were not present in any of the
existing midnam files at all. I wanted to sequence a hardware synth (the
Moog Minitaur) without constantly referring to the manual and trying to
remember which CC is which, so I wrote a new midnam file for the device
and implemented support in Ardour. I am very disappointed to learn that
there is no ability to group controllers, which really is the best way
to do things, so I may embrace and extend (in a completely backwards
compatible way) the format in the future to provide a better interface.&lt;/p&gt;
&lt;p&gt;The quality of midnam files scattered around the 'net is atrocious. The
MMA actually hosts a DTD for it, but most files use the wrong DOCTYPE
(usually with a broken link), among other problems. I fixed and cleaned
up all the bundled ones in Ardour, validated them against the DTD, and
left a README in that directory about how to do so. Hopefully this,
along with incentive added by these features, encourages the community
to grow a nice set of quality midnam files.&lt;/p&gt;
&lt;p&gt;So, MIDI name support in Ardour is now in pretty good shape. Now we just
need to add the corresponding LV2 support...&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/><category term="MIDI"/></entry><entry><title>Reviving Machina</title><link href="https://drobilla.net/2013/01/14/reviving-machina.html" rel="alternate"/><published>2013-01-14T03:26:00-05:00</published><updated>2013-01-14T03:26:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2013-01-14:/2013/01/14/reviving-machina.html</id><content type="html">&lt;p&gt;In the past few years of hammering on my repository while moulding
&lt;a href="/software/ingen"&gt;Ingen&lt;/a&gt; into the modular of my
dreams, my poor little generative sequencer
&lt;a href="/software/machina"&gt;Machina&lt;/a&gt; was often left to rot.
That's a shame, because it's a fun tool to play with if you're into
generative gadgets and pretty graphs.&lt;/p&gt;
&lt;p&gt;So, I spent the weekend working on turning Machina back into a usable
app. It's come a very long way in two days, from a mess that doesn't
even compile to most functionality working pretty well:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/machina.png"&gt;&lt;img alt="A small Machina machine created by step recording" src="/images/machina.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Aside from rewriting a lot of the code, testing, and fixing things that
were broken, I've changed the conceptual model a bit. There is now
always just one start node for a machine. This tends to create more
manageable structures, especially when recording several takes into the
same machine.&lt;/p&gt;
&lt;p&gt;Step recording has also been significantly improved. You can build
machines entirely in the UI, but this is pretty tedious. It's much more
effective to record, or step record if you want regular timing and no
delays in-between notes. The above pictured machine was created by step
recording MIDI, though I have tweaked the edge probabilities with the
mouse. You can also import a MIDI file, which is an easy way to get a
big machine to work with.&lt;/p&gt;
&lt;p&gt;Hopefully I can find the time to turn this into an LV2 plugin and
release it, but for now it is an unreleased Jack application. Give it a
try if you're feeling adventurous, I would appreciate any feedback.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Host-Generated LV2 File Choosers</title><link href="https://drobilla.net/2013/01/10/host-generated-lv2-file-choosers.html" rel="alternate"/><published>2013-01-10T00:29:00-05:00</published><updated>2013-01-10T00:29:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2013-01-10:/2013/01/10/host-generated-lv2-file-choosers.html</id><content type="html">&lt;p&gt;LV2 has been slowly but surely moving towards message-based control to
overcome the limitations of static ports. The required low level
facilities have been available for a while now, but the lack of a
standard way to document message-based parameters and host UI support
has been a sticking point. One particularly pressing need is file
parameters. The current sample example has a stop-gap UI to enable
sample loading, but parameters really should be host controllable and
simple plugins should not need a custom UI to be usable. Today I did
some work towards resolving that situation.&lt;/p&gt;
&lt;p&gt;Here is &lt;a href="https://github.com/x42/convoLV2"&gt;ConvoLV2&lt;/a&gt; running in
&lt;a href="/software/jalv"&gt;Jalv&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A file chooser in Jalv" src="/images/jalv_file_chooser.png"&gt;&lt;/p&gt;
&lt;p&gt;Note this is the built-in UI generated by Jalv and not a custom plugin
UI. Unlike traditional LADSPA style controls, the impulse parameter is
not a port, but is set by sending a message to an event port. The plugin
lists parameters with the new patch:writable property, so the host knows
which controls to display (or which parameters it can automatically
set). Parameters are described as normal RDF properties, so any existing
property can be used, or plugins can define them as needed in their data
files (don't let the “RDF” scare you, the description of a Property is
as simple as LV2 ports). One nice advantage of this over ports is that a
project can describe a parameter once and use it in many plugins,
without having to describe the parameter every time. This really adds up
when you have hundreds of plugins with, say, an identical gain
parameter.&lt;/p&gt;
&lt;p&gt;These conventions will be established in the next LV2 release (1.4.0),
and the sampler example will serve as a test case. When host support
becomes established, we can also move towards using messages for numeric
parameters, which will finally allow for dynamic parameters, control
ramps, and so on.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Ingen as Remote Server or LV2 Plugin</title><link href="https://drobilla.net/2012/12/22/ingen-as-remote-server-or-lv2-plugin.html" rel="alternate"/><published>2012-12-22T13:52:00-05:00</published><updated>2012-12-22T13:52:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-12-22:/2012/12/22/ingen-as-remote-server-or-lv2-plugin.html</id><content type="html">&lt;p&gt;I have been working on the control protocol aspect of
&lt;a href="/software/ingen"&gt;Ingen&lt;/a&gt;, towards a few goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Working GUI communicating with the server via a socket&lt;/li&gt;
&lt;li&gt;Decent Python bindings for socket interface&lt;/li&gt;
&lt;li&gt;Ingen as an LV2 plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Python bindings got fleshed out to support a script for loading
&lt;a href="http://alsamodular.sourceforge.net/"&gt;AMS&lt;/a&gt; patches into Ingen I have
been working on (very preliminary, but in the repository and installed
as ‘ingenams’). The socket interface is now sensibly synchronous, e.g.
you send a command, and recv on the socket to get the response, which
will include a status as well as whatever patch updates happened as a
result of the command. I am happy with this route to language bindings,
it's much more pleasant than a mess of direct bindings (e.g. via swig)
and being protocol controlled has many other advantages. The Python
interface is implemented purely in Python, it should be simple to do the
same in any language that can use sockets.&lt;/p&gt;
&lt;p&gt;The Ingen GUI can communicate with the engine via the same protocol, so
you can build patches over the network (e.g. if Ingen is running on a
headless machine as part of an audio rig). It can also run in the same
process and use direct function calls for maximum performance.&lt;/p&gt;
&lt;p&gt;The Ingen protocol uses the &lt;a href="http://lv2plug.in/ns/ext/patch/"&gt;LV2 Patch&lt;/a&gt;
vocabulary to describe changes. For the socket interface, this is
represented entirely in Turtle. When running as an LV2 plugin, Ingen
uses conceptually the same protocol, except in a binary format using LV2
&lt;a href="http://lv2plug.in/ns/ext/atom/"&gt;atoms&lt;/a&gt;. This is a nice example of the
benefits of having things defined in a good abstract model which can be
encoded in many ways. For example, it would be relatively simple to
implement control via JSON for a browser-based Ingen UI, which seems
like the best way to go for separate control panels, but that's a topic
for another day.&lt;/p&gt;
&lt;p&gt;Since Ingen control can be expressed as atoms, it can run as an LV2
plugin with a full patcher UI and communicate only via LV2 ports. The UI
is embeddable, and there is no memory sharing between engine and UI or
hidden communication, everything is host managed. Here is a screenshot
of Ingen running in &lt;a href="http://ardour.org/"&gt;Ardour&lt;/a&gt; 3. The MIDI in Ardour
is played through an Ingen synth plugin, and the output has been
recorded to the audio track. The patch can be manipulated live while
running in Ardour, except for top level ports.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ingen_in_ardour.png"&gt;&lt;img alt="Ingen running in Ardour as an LV2 plugin" src="/images/ingen_in_ardour-150x113.png"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>LV2 Plugin Transport Synchronisation</title><link href="https://drobilla.net/2012/11/17/lv2-plugin-transport-synchronisation.html" rel="alternate"/><published>2012-11-17T01:20:00-05:00</published><updated>2012-11-17T01:20:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-11-17:/2012/11/17/lv2-plugin-transport-synchronisation.html</id><content type="html">&lt;p&gt;I have been working on getting accurate transport information provided
to &lt;a href="http://lv2plug.in/"&gt;LV2&lt;/a&gt; plugins. Until now, there was the ability
to specially designate e.g. BPM control ports, but this has limitations,
the most obvious being that it is is limited to conveying simple numeric
values at the start of a cycle.&lt;/p&gt;
&lt;p&gt;In general, things are headed in an event-based direction, and this is
no exception. The &lt;a href="http://lv2plug.in/ns/ext/time"&gt;LV2 Time&lt;/a&gt; extension
defines all the properties needed to describe a transport
position/speed/tempo/meter and the &lt;a href="http://lv2plug.in/ns/ext/atom"&gt;LV2
Atom&lt;/a&gt; extension defines a dictionary type
that can be used to send several properties at once in a single event.
The host sends updates whenever the transport has changed in a way the
plugin can not anticipate (anything but rolling along at the same
speed/tempo/meter). Since events are time stamped, this makes it
possible to give plugins a sample accurate tempo map, even when there
are changes in the middle of a cycle.&lt;/p&gt;
&lt;p&gt;There is a new example plugin in the LV2 repository, eg-metro, which is
a simple metronome that follows this scheme. If the host implements the
above, the metronome will click in sync with the host transport.&lt;/p&gt;
&lt;p&gt;I have implemented this in &lt;a href="/software/jalv"&gt;Jalv&lt;/a&gt;
(which uses Jack transport), &lt;a href="http://ardour.org"&gt;Ardour&lt;/a&gt; 3, and
&lt;a href="/software/ingen"&gt;Ingen&lt;/a&gt; (via a new internal "Time"
block). Ardour will send an update at the start of the cycle if a
relocate has happened, and one on the exact sample of any meter or tempo
change within the cycle. Due to the limitations of Jack transport, Jalv
and Ingen will only send updates at the start of a cycle. The metronome
should tick correctly when loaded in Jalv or Ingen when there is a Jack
transport master active (I have tested with Ardour), and when loaded
directly in Ardour.&lt;/p&gt;
&lt;p&gt;This functionality is pretty important for many musical plugins, so
hopefully other host authors implement it as well. Depending on feedback
(and available time) I may document the above rules as a feature and add
it to the time extension, and release LV2 1.2.2. I think a well-defined
way to achieve this is worthy of a point release, and it will get the
news out there to implementers.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Benchmarking LV2 plugins</title><link href="https://drobilla.net/2012/08/22/benchmarking-lv2-plugins.html" rel="alternate"/><published>2012-08-22T23:29:00-04:00</published><updated>2012-08-22T23:29:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-08-22:/2012/08/22/benchmarking-lv2-plugins.html</id><content type="html">&lt;p&gt;A new utility has been added to
&lt;a href="/software/lilv"&gt;Lilv&lt;/a&gt; for benchmarking LV2 plugins.
As you might expect, it is called &lt;code&gt;lv2bench&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Simple, but useful, &lt;code&gt;lv2bench&lt;/code&gt; runs all discovered plugins for a
specified number of frames split into a specified block size, and
records the time each plugin took to execute. It prints a simple table
like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Block&lt;/th&gt;
&lt;th&gt;Samples&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;524288&lt;/td&gt;
&lt;td&gt;0.005872&lt;/td&gt;
&lt;td&gt;http://calf.sourceforge.net/plugins/Filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;524288&lt;/td&gt;
&lt;td&gt;0.006461&lt;/td&gt;
&lt;td&gt;http://drobilla.net/plugins/blop/amp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;524288&lt;/td&gt;
&lt;td&gt;0.008772&lt;/td&gt;
&lt;td&gt;http://calf.sourceforge.net/plugins/Equalizer8Band&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;524288&lt;/td&gt;
&lt;td&gt;0.008959&lt;/td&gt;
&lt;td&gt;http://drobilla.net/plugins/fomp/autowah&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This makes it easy to get a good overall feel for how expensive plugins
on the system are relative to each other. Of course, a more expressive
view of this information with several parameters would be useful...
&lt;a href="http://gareus.org/"&gt;Robin Gareus&lt;/a&gt; to the rescue! Robin made a script
currently called &lt;a href="http://robin.linuxaudio.org/lilvplot.sh"&gt;lilvplot&lt;/a&gt;
which does several runs with various parameters and plots the data,
which makes the relative performance more clear and shows the variance
of a plugin's run time with error bars. This is important information
because run-time variance is a good indicator of how real-time
appropriate a plugin is.&lt;/p&gt;
&lt;p&gt;Here is an example of lilvplot output generated from lv2bench data (as a
massive SVG, you may want to open this image in a separate window and
zoom in to see the plugin labels):
&lt;img alt="LV2 Benchmark" src="http://robin.linuxaudio.org/lv2stats1_stats.svg"&gt;&lt;/p&gt;
&lt;p&gt;Both of these tools are very fresh and there are certainly improvements
to be made (normalizing the y-axis so it showed where real-time is would
be more informative, and lv2bench currently supports very few features),
but they already make a useful addition to the LV2 developer's toolkit.
I found them useful for evaluating the improvements had by vectorizing
the arithmetic plugins in my soon-to-be-released port of
&lt;a href="http://blop.sourceforge.net/"&gt;blop&lt;/a&gt;. It will be interesting to see how
things stack up in more thorough runs that include more plugins.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>LV2 OpenGL UIs on Windows</title><link href="https://drobilla.net/2012/08/02/lv2-opengl-uis-on-windows.html" rel="alternate"/><published>2012-08-02T21:54:00-04:00</published><updated>2012-08-02T21:54:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-08-02:/2012/08/02/lv2-opengl-uis-on-windows.html</id><content type="html">&lt;p&gt;I have been working on &lt;a href="/software/suil"&gt;Suil&lt;/a&gt; support
for wrapping native Windows plugin UIs in Gtk. Here is a plugin with a
&lt;a href="/software/pugl"&gt;Pugl&lt;/a&gt; UI embedded in the Gtk version
of &lt;a href="/software/jalv"&gt;Jalv&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/lv2_gl_ui_windows1.png"&gt;&lt;img alt="LV2 OpenGL UI in Windows" src="/images/lv2_gl_ui_windows1.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Very fresh work, but the basics (display, and mouse/keyboard input) are
working well. This work is not actually OpenGL specific, any native
Windows UI will work equally well. Apologies for the boring screenshot,
this is just my test GL plugin that does nothing. However, it is a
working example of a plugin with a portable custom UI; hopefully we'll
soon see production plugins with UIs that work equally well across
toolkits and platforms.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Portable OpenGL Plugin UIs</title><link href="https://drobilla.net/2012/04/30/portable-opengl-plugin-uis.html" rel="alternate"/><published>2012-04-30T22:40:00-04:00</published><updated>2012-04-30T22:40:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-04-30:/2012/04/30/portable-opengl-plugin-uis.html</id><content type="html">&lt;p&gt;&lt;a href="http://lv2plug.in/"&gt;LV2&lt;/a&gt; allows plugins to implement UIs in any
toolkit. This has led to UIs being implemented in several (which is a
Good Thing, and works fine in all hosts via
&lt;a href="/software/suil"&gt;Suil&lt;/a&gt;), but mostly Gtk.&lt;/p&gt;
&lt;p&gt;Unfortunately, Gtk is not really suitable for use in plugins on
platforms where a “system Gtk” can't be relied on. Some toolkits are
suitable for static linking, but personally, I am somewhat disillusioned
with “toolkits” lately, and massive libraries in general. Sometimes all
you want or need is a straightforward standard graphics API and some
keyboard/mouse events.&lt;/p&gt;
&lt;p&gt;When it comes to standard cross-platform graphics APIs, the undisputed
heavyweight king is OpenGL. While not perfect (what is?), no other API
is &lt;em&gt;already there&lt;/em&gt; on almost any platform you'd care about (heck, most
modern &lt;em&gt;phones&lt;/em&gt; have hardware accelerated OpenGL). Unfortunately, OpenGL
deals only with rendering, and not user input or windowing issues. What
is needed is a minimal framework to get an OpenGL view to draw to, and
receive keyboard and mouse events. Enter
&lt;a href="/software/pugl"&gt;Pugl&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pugl handles all the platform specific business behind an API very
similar to GLUT, but much smaller and appropriate for plugins (which
GLUT unfortunately is not). In terms of size, this is a few hundred
lines of C per platform (on a personal note, this fits in well with my
ever-increasing distaste in dealing with bloated junk with tons of
dependencies... give me a Couple Hundred Lines of C™ any day). The
breakdown on Pugl is on its homepage, but suffice to say after a few
days' work it does the job it was designed to do on X11, Mac OS X, and
Windows. Since embedding X11 works in LV2 land, that means an OpenGL
plugin UI can be embedded in the host, and I have the pretty pictures to
prove it:&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ardour_lv2_gl.png"&gt;&lt;img alt="Pugl embedded in Ardour" src="/images/ardour_lv2_gl-109x150.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/ingen_lv2_gl.png"&gt;&lt;img alt="Pugl embedded in Ingen" src="/images/ingen_lv2_gl-135x150.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a simple test plugin (complete with gratuitous use of 3D)
embedded in Ingen and Ardour. Both are Gtk based programs, but this
works in Qt as well. Testing so far on other platforms has only been
top-level since I have no programs to embed in, but the bulk of the work
is done. This includes full keyboard and mouse support, with
significantly more complete keyboard support than GLUT.&lt;/p&gt;
&lt;p&gt;Naturally I can't predict the future, so it remains to be seen how much
OpenGL UI action we'll see for plugins. Being just a low level drawing
API and not a set of boxed widgets, it's a bit open ended with a bit of
a learning curve, but on the other hand there is &lt;em&gt;lots&lt;/em&gt; of existing
OpenGL code out there. Perhaps someone will throw together a library of
audio appropriate widgets, if one doesn't already exist. Either way, I
think an easy to use API for writing truly portable LV2 plugin UIs is a
very good thing, which hopefully eliminates a barrier for some plugin
developers and helps LV2 invade the territory of its proprietary
adversaries... or, at the very least, makes for a really cool 3D panner
GUI :)&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>LV2 Atom and State Serialisation</title><link href="https://drobilla.net/2012/02/23/lv2-atom-and-state-serialisation.html" rel="alternate"/><published>2012-02-23T01:42:00-05:00</published><updated>2012-02-23T01:42:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2012-02-23:/2012/02/23/lv2-atom-and-state-serialisation.html</id><content type="html">&lt;p&gt;I have been working on full round-trip serialisation for everything in
the &lt;a href="http://lv2plug.in/ns/ext/atom"&gt;LV2 Atom&lt;/a&gt; extension (which also
applies for implementing state). I am doing this as a small library with
a simple API, with the goal that it be simple to include in any project.&lt;/p&gt;
&lt;p&gt;Currently this only writes (using
&lt;a href="/software/serd"&gt;Serd&lt;/a&gt;), I still need to move the
reading stuff into it (which will probably use a model and thus require
using &lt;a href="/software/sord"&gt;Sord&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The Atom extension defines a simple data model for use in LV2 plugins
and hosts (or elsewhere). The general Big Idea here is to have a
smallish set of primitive types, a few collections, and out of that one
can build arbitrarily complex structures. Everything (including
containers) is a simple and compact chunk of POD data, but serialises
to/from (a subset of) RDF, so it can nicely be described in a plugin's
Turtle file, among other advantages.&lt;/p&gt;
&lt;p&gt;An easy to adopt serialisation API is important to making these
advantages a reality for many implementations, so I have decided to
provide one before marking these extensions stable. It also serves as a
nice test case with complete coverage. Here is an example of an Atom
that contains every currently defined Atom type, as well as MIDI,
serialised to Turtle by sratom:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;three&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;four&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;urid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;langlit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bonjour&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fra&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;typelit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Vector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;xsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;eg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nl"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;frameTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;901A01&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;midi&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;MidiEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nl"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;frameTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nl"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;902B02&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;^^&lt;/span&gt;&lt;span class="n"&gt;midi&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;MidiEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I anticipate/intend for all plugin control to happen via such messages,
since this approach has a few important qualities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Typically no need to define new binary formats for things (and be
    held back waiting for others to implement them).&lt;/li&gt;
&lt;li&gt;Everything has a portable serialization for free (meaning network
    transparency, saving/loading, and for developers or power users the
    ability to dump any message to see what is going on).&lt;/li&gt;
&lt;li&gt;The convention is to use “objects” (resources, i.e. things with
    properties) as messages, which are inherently extensible. No “oops I
    needed to add a parameter so now compatibility is broken”.&lt;/li&gt;
&lt;li&gt;Easy to bind to other languages or syntaxes, so e.g. Python or
    browser-based UI frameworks should be possible.&lt;/li&gt;
&lt;li&gt;Any RDF vocabulary can be used, meaning millions of well-defined and
    documented predicates (“keys”) are available right now (though it is
    perfectly okay to create one-off objects - compatibility with RDF is
    a benefit, not a burden).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The atom extension includes an API that makes it relatively simple to
build such objects in C, so plugins can write them directly to an output
port or a ring buffer. See the “forge” API in the Atom extension for
details. There are also iterators for all the collections and a “get”
function for objects to make reading data simple.&lt;/p&gt;
&lt;p&gt;Just in case it's not crystal clear, the above is only the &lt;em&gt;external
representation&lt;/em&gt; of the corresponding atom. At run-time, an atom (i.e.
what plugins work with) is just a blob of data with an integer type and
size header. 100% of the API provided for reading and writing atoms is
real-time safe and suitable for use in an audio processing thread.&lt;/p&gt;
&lt;p&gt;For an example, see the &lt;a href="https://gitlab.com/lv2/lv2/-/tree/main/plugins/eg-sampler.lv2"&gt;LV2 sampler
example&lt;/a&gt;, which
has a UI that loads samples via such messages. It currently works in
&lt;a href="/software/jalv"&gt;Jalv&lt;/a&gt;,
&lt;a href="http://ardour.org"&gt;Ardour&lt;/a&gt; support is coming soon. This is the way
forward for more powerful LV2 plugin control, and hopefully will end the
worrying practice of abusing direct instance access to provide such
functionality.&lt;/p&gt;
&lt;p&gt;This work isn't finished yet, but the important parts are done and not
likely to change significantly. I am interested in hearing any developer
feedback, feel free to comment on this post or at the &lt;a href="http://lists.lv2plug.in/listinfo.cgi/devel-lv2plug.in"&gt;LV2 mailing
list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[Edited on 2025-02-16 to update broken links]&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/><category term="Semantic Web"/></entry><entry><title>New LV2 Host Implementation Stack</title><link href="https://drobilla.net/2011/05/25/new-lv2-host-implementation-stack.html" rel="alternate"/><published>2011-05-25T00:40:00-04:00</published><updated>2011-05-25T00:40:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2011-05-25:/2011/05/25/new-lv2-host-implementation-stack.html</id><content type="html">&lt;p&gt;I have released my new stack of libraries for implementing LV2 in hosts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/software/serd"&gt;Serd&lt;/a&gt;, a fast, lightweight,
   dependency-free Turtle syntax library&lt;/li&gt;
&lt;li&gt;&lt;a href="/software/sord"&gt;Sord&lt;/a&gt;, an in-memory RDF quad
   store&lt;/li&gt;
&lt;li&gt;&lt;a href="/software/lilv"&gt;Lilv&lt;/a&gt;, an LV2 host library
   (replaces SLV2)&lt;/li&gt;
&lt;li&gt;&lt;a href="/software/suil"&gt;Suil&lt;/a&gt;, an LV2 UI
   loading/embedding library&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These libraries collectively replace SLV2, and have no dependencies
except amongst themselves, and Glib (by Lilv and Sord, but this
dependency will likely be removed in the near future). Serd and Sord
replace Redland, making for a dramatically smaller implementation more
appropriate for audio software and embedded applications.&lt;/p&gt;
&lt;p&gt;Overall, Lilv is dramatically faster and leaner than SLV2, enough that
the improvement should be quite noticeable from a user point of view
(typically in a lag when the host first loads all LV2 data). Anyone
using SLV2 is highly encouraged to migrate to Lilv.&lt;/p&gt;
&lt;p&gt;These libraries are well tested, each (except Suil) with a test suite
covering over 90% of the code, which runs without memory errors or
leaks. They are new, however, so (as always) there may be problems;
feedback is most welcome.&lt;/p&gt;
&lt;p&gt;[Edited on 2025-02-16 to remove broken links]&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/><category term="Semantic Web"/></entry><entry><title>SLV2 Is Dead, Long Live SLV2</title><link href="https://drobilla.net/2011/04/29/slv2-is-dead-long-live-slv2.html" rel="alternate"/><published>2011-04-29T14:22:00-04:00</published><updated>2011-04-29T14:22:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2011-04-29:/2011/04/29/slv2-is-dead-long-live-slv2.html</id><content type="html">&lt;p&gt;(See below for important information for anyone following my SVN
repository)&lt;/p&gt;
&lt;p&gt;I've decided to replace SLV2 with a new library (named “Lilv”) instead
of breaking the API which has been causing a lot of hassle, and would
only cause more when it gets released and packaged. Among other things,
this gives me the opportunity to rework the installation to support
parallel installs of different major versions to prevent this kind of
problem in the future. It also gives me a chance to improve the API and
remove some cruft that has accumulated to avoid breaking the API (SLV2
has been around for many years now, and LV2 (and myself) have come a
long way since then, it's a good time to do some laundry).&lt;/p&gt;
&lt;p&gt;Anyone who has an SVN version of SLV2 installed, please uninstall it
immediately and pretend it never existed. SLV2 0.6.6 is the last version
of SLV2. The first Lilv release will be out shortly.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>Unified LV2 Core/Extensions Code Documentation</title><link href="https://drobilla.net/2010/09/22/unified-lv2-core-extensions-code-documentation.html" rel="alternate"/><published>2010-09-22T15:37:00-04:00</published><updated>2010-09-22T15:37:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2010-09-22:/2010/09/22/unified-lv2-core-extensions-code-documentation.html</id><content type="html">&lt;p&gt;I've reworked the &lt;a href="http://lv2plug.in/ns/doc/html/"&gt;LV2 documentation&lt;/a&gt;
generation system to generate all code documentation for
&lt;a href="http://lv2plug.in"&gt;lv2plug.in&lt;/a&gt; in a single
&lt;a href="http://doxygen.org"&gt;Doxygen&lt;/a&gt; run. This means the core and all
extensions' documentation are hyperlinked between each other, and there
are handy global indices of everything.&lt;/p&gt;
&lt;p&gt;See &lt;a href="http://lv2plug.in/ns/doc/html/"&gt;http://lv2plug.in/ns/doc/html/&lt;/a&gt; for an index.&lt;/p&gt;
&lt;p&gt;As before, you can go directly to the URI of any lv2plug.in extension in
your browser for documentation, which links to the code documentation
for that particular extension.&lt;/p&gt;
&lt;p&gt;Hopefully this reference proves useful for implementers.&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>GStreamer LV2 Surround</title><link href="https://drobilla.net/2009/08/12/gstreamer-lv2-surround.html" rel="alternate"/><published>2009-08-12T15:06:00-04:00</published><updated>2009-08-12T15:06:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2009-08-12:/2009/08/12/gstreamer-lv2-surround.html</id><content type="html">&lt;p&gt;My last post was mostly about adding multi-channel support to GStreamer.&lt;/p&gt;
&lt;p&gt;That was only partially implemented though - many elements in GStreamer
(and pretty much all of the ones that support more than stereo) require
each channel to be explicitly labeled with its position. This
information wasn't bridged until now.&lt;/p&gt;
&lt;p&gt;The two sides of this situation are:&lt;/p&gt;
&lt;p&gt;The LV2 Port Groups extension:
http://lv2plug.in/ns/dev/port-groups.lv2/port-groups.html&lt;br&gt;
GStreamer multi-channel:
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstmultichannel.html&lt;/p&gt;
&lt;p&gt;LV2 “roles” correspond to GstAudioChannelPosition. Unfortunately, the
mapping was a bit strained with the Port Groups extension as it was at
midterm time. Fortunately, that extension is not finalized yet, and in
the process I found some other problems with it, so I decided to bring
it closer to the way GStreamer does things, which seems to be common in
other software that does this sort of stuff. Formerly, the port groups
extension used the channel names from the relevant surround spec (see
the &lt;a href="http://en.wikipedia.org/wiki/Surround_sound"&gt;Wikipedia page on Surround
Sound&lt;/a&gt;). This resulted in a
lot of roles that were really the same thing, e.g. the “Surround Left”
channel in 5.1 is really the same as the “Left Back” channel in 7.1.
This was pretty confusing in practice. GStreamer instead always uses
positional names like “Left Back”, which is unambiguous regardless of
context (you don't have to know if it's 17.2 or whatever to know what
“Left Back” probably means...). So, I changed the LV2 port groups
extension to be similar (though not identical; munging an LV2 extension
to match GStreamer API wouldn't be appropriate).&lt;/p&gt;
&lt;p&gt;This information is now queried out from LV2 plugins that have it,
mapped to GStreamer channel positions, and set as capabilities on
mulit-channel pads. This means multi-channel surround LV2 plugins should
now work fine with surround sound GStreamer set-ups, for example playing
a Theora video through some audio processing and out through Alsa.&lt;/p&gt;
&lt;p&gt;I updated &lt;a href="http://plugin.org.uk/lv2/"&gt;swh-plugins&lt;/a&gt; for the minor
changes, and added port group information to Invada Studio Plugins.&lt;/p&gt;
&lt;p&gt;More to come...&lt;/p&gt;</content><category term="misc"/><category term="GStreamer"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry><entry><title>GStreamer SoC Midterm Summary</title><link href="https://drobilla.net/2009/07/05/gstreamer-soc-midterm-summary.html" rel="alternate"/><published>2009-07-05T19:30:00-04:00</published><updated>2009-07-05T19:30:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2009-07-05:/2009/07/05/gstreamer-soc-midterm-summary.html</id><content type="html">&lt;p&gt;Apologies for not blogging along the way for those who are interested in
this project. I'm more into actually doing things rather than blogging
about doing things ;)&lt;/p&gt;
&lt;p&gt;So, LV2 in Gst, where are we? Well, I've had LV2 plugins working with
the same functionality as LADSPA plugins for a while. The main downside
was shared by LADSPA: no support for multi-channel streams.&lt;/p&gt;
&lt;p&gt;Some background for those unfamiliar with LADSPA or LV2: LADSPA and LV2
plugins are very simple in terms of I/O. A plugin has a collection of
“ports” which contain either a single floating point value (control) or
an array of floating point values (audio). LV2 extends this to any types
but that's not important here. This is simple but causes problems in the
face of stereo and other multi-channel streams: if a plugin has 3 audio
inputs and 2 audio outputs, for example, what is what? Some kind of
3-channel audio input with stereo output? 5 completely unrelated ports?
A stereo input and stereo output with a “sidechain” (common with e.g.
compressors)? This information is not available in LADSPA, the best you
can do is guess (which is practice means it just doesn't work).&lt;/p&gt;
&lt;p&gt;GStreamer works with multi-channel streams as a single interleaved
stream, so this is a problem. Thankfully, LV2 makes it trivial to add
whatever information you like about plugins without having to touch a
line of code since plugins are described in RDF (see &lt;a href="http://lv2plug.in"&gt;the LV2
site&lt;/a&gt; for more information). All that needs to be
decided is how to actually model that information. A specification for
this is called an “ontology” in general/theory, and an “extension” in
the LV2 community.&lt;/p&gt;
&lt;p&gt;So, the problem is we need(ed) a good multi-channel ontology for LV2
plugins to work well inside Gst, since most things in Gst are at least
stereo. The difficult thing with creating ontologies is making sure
anything anyone might want to describe in the future is accounted for.
Here's my best shot at this so far: &lt;a href="http://lv2plug.in/ns/dev/port-groups.lv2/port-groups.html"&gt;LV2 Port
Groups&lt;/a&gt;,
based largely on earlier work by Lars Luthman and some input from the
LV2 community. This extension isn't final, but expresses all the
information needed by Gst for multi-channel (and more). (I also wrote
the documentation generator that created the aforementioned
specification page in the hopes that more user friendly documentation
will encourage adoption by plugin and host authors).&lt;/p&gt;
&lt;p&gt;I've created patches for the popular &lt;a href="http://plugin.org.uk/"&gt;SWH&lt;/a&gt; and
&lt;a href="http://calf.sourceforge.net/"&gt;Calf&lt;/a&gt; plugin packages to add this
information. When the extension goes final they will be included in
these projects, but in the mean time are included in my git branch of
gst-plugins-bad (see
&lt;a href="http://git.drobilla.net/"&gt;http://git.drobilla.net&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This is, as far as I know, the first time coherent multi-channel
information has been available about plugins from the “LAD” community
(e.g. LADSPA, DSSI, LV2). Coincidentally, this information is required
for recent work on &lt;a href="http://ardour.org"&gt;Ardour&lt;/a&gt;, among other things. In
hindsight, this was a pretty glaring hole in the general state of LAD
plugins, but back to GStreamer...&lt;/p&gt;
&lt;p&gt;I've rewritten quite a bit of the GstSignalProcessor class (used by the
LADSPA and now LV2 wrapper elements) to support multi-channel plugins.
In code terms this means creating a set of buffers for non-interleaved
data used by the plugin, and interleaving/deinterleaving buffers to/from
Gst, respectively.&lt;/p&gt;
&lt;p&gt;In summary: many LV2 plugins now exist with useful multi-channel
information, and stereo LV2 plugins now work correctly in GStreamer. It
should now be simple to add support for other audio plugin interfaces
(VST? AudioUnits?) that also works with multi-channel streams.&lt;/p&gt;
&lt;p&gt;Next up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get the “role” information from the plugin data and use it to
   support surround streams correctly&lt;/li&gt;
&lt;li&gt;Finalize and publish the LV2 Port Groups extension, and contribute
   patches for all major plugin collections&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Where to go from there is pretty open-ended. Unlike LADSPA, LV2 can
theoretically support any kind of data, or any feature (non-realtime and
non-audio things included). Extensions just need to be made to bridge
the gap. What sort of functionality would &lt;em&gt;you&lt;/em&gt; like to see bridge the
GStreamer/LV2 gap?&lt;/p&gt;</content><category term="misc"/><category term="GStreamer"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/><category term="Semantic Web"/></entry><entry><title>DOAP replacing AUTHORS/MAINTAINERS/etc</title><link href="https://drobilla.net/2008/01/18/doap-replacing-authorsmaintainersetc.html" rel="alternate"/><published>2008-01-18T12:25:00-05:00</published><updated>2008-01-18T12:25:00-05:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2008-01-18:/2008/01/18/doap-replacing-authorsmaintainersetc.html</id><content type="html">&lt;p&gt;There's been a bit of talk in the GNOME camp lately about using DOAP
instead of the unstructured text files that are the current norm for
source packages. On the one hand, people want the benefits of having
machine readable data in projects, OTOH, RDF/XML is a nightmare (“I'll
never maintain such bloat!” - “That is one hell of an ugly file.”).&lt;/p&gt;
&lt;p&gt;This is how RDF/XML hurts RDF. The original loathed file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stylesheet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text/xsl&amp;quot;&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;RDF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://usefulinc.com/ns/doap#&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;asfext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://projects.apache.org/ns/asfext#&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;about&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://ant.apache.org/&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2006&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://usefulinc.com/doap/licenses/asl20&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Apache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ant&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://ant.apache.org&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;asfext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;pmc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://ant.apache.org&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;shortdesc&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;based&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;shortdesc&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Apache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;based&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Make&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;without&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Make&lt;/span&gt;\&lt;span class="s1"&gt;&amp;#39;s wrinkles.&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bug&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://issues.apache.org/bugzilla/buglist.cgi?product=Ant&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;mailing&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://ant.apache.org/mail.html&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://ant.apache.org/bindownload.cgi&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;programming&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Java&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;programming&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://projects.apache.org/category/build-management&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Apache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2006&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;1.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SVNRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://svn.apache.org/repos/asf/ant&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;browse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://svn.apache.org/viewcvs.cgi/ant&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;SVNRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;RDF&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and the equivalent in Turtle (a subset of N3) (automatically generated
with rapper -o turtle doap_Ant.rdf):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rdf&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//www.w3.org/1999/02/22-rdf-syntax-ns#&amp;gt; .&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//usefulinc.com/ns/doap#&amp;gt; .&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asfext&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//projects.apache.org/ns/asfext#&amp;gt; .&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2006-02-17&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Apache Ant&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//ant.apache.org&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;asfext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;pmc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//ant.apache.org&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;shortdesc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Java-based build tool&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Apache Ant is a Java-based build tool. In theory, it is kind of like Make, but without Make&amp;#39;s wrinkles.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bug&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//issues.apache.org/bugzilla/buglist.cgi?product=Ant&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;mailing&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//ant.apache.org/mail.html&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//ant.apache.org/bindownload.cgi&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;programming&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Java&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//projects.apache.org/category/build-management&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;release&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Apache Ant 1.7.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2006-12-13&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.7.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;SVNRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//svn.apache.org/repos/asf/ant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;browse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//svn.apache.org/viewcvs.cgi/ant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I wouldn't want to hand maintain for RDF/XML version either, but the
Turtle version? Sure. It's the exact same information, far more human
readable, and about as terse as it could be while representing the same
information.&lt;/p&gt;
&lt;p&gt;The best thing about a syntax independent model like RDF is.. well, it's
syntax independent. Choose one that doesn't suck :)&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="Semantic Web"/></entry><entry><title>Ardour: Back On Track. Maybe.</title><link href="https://drobilla.net/2007/06/24/ardour-back-on-track-maybe.html" rel="alternate"/><published>2007-06-24T12:00:00-04:00</published><updated>2007-06-24T12:00:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2007-06-24:/2007/06/24/ardour-back-on-track-maybe.html</id><content type="html">&lt;p&gt;Well, I've been working on Ardour but not directly on my SoC project..
finally got around to fixing that whole mixer-strip-element thing that's
been on the table since last year. Things are more open to extensibility
now (and a unified bus implementation should appear soon, so we'll have
MIDI busses or even MIDI/audio (instrument) busses). Maybe we'll see
things like &lt;a href="http://ardour.org/node/1043"&gt;this&lt;/a&gt; actually happen some
time soonish...&lt;/p&gt;
&lt;p&gt;It would be nice to figure out a really good MIDI meter (something more
clever than just an audio peak meter abused), and a MIDI fader that can
work in various ways, or even instrument plugin support, but it's
probably time to get on the actual piano roll editing part of the
project and leave that stuff until afterwards.&lt;/p&gt;
&lt;p&gt;Kinda ruins my &lt;em&gt;flow&lt;/em&gt; though, you know?&lt;/p&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/><category term="LAD"/></entry><entry><title>Back to the Hack</title><link href="https://drobilla.net/2007/05/29/back-to-the-hack.html" rel="alternate"/><published>2007-05-29T13:58:00-04:00</published><updated>2007-05-29T13:58:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2007-05-29:/2007/05/29/back-to-the-hack.html</id><content type="html">&lt;p&gt;Well.. finally fully moved; I guess it's time to stop screwing around
and get back into hacking. Specifically Ardour, since I'm being payed to
do so and all.&lt;/p&gt;
&lt;p&gt;Looks like we don't have a fancy new cairo canvas to play with. Maybe it
will be worth my time to do that first, but I'm going to do some
fiddling with displaying MIDI data with gnomecanvas first to get a grasp
on things. Wouldn't hurt to have some visually obvious clue that I am,
in fact, actually doing something. This summer's project should be more
rewarding than last in that sense; most of what I need to do is visual
stuff which tends to be more fun since you have something nice and
tangible at the end of the day.&lt;/p&gt;
&lt;p&gt;Of course, displaying data is one thing. One relatively easy thing.
Actually editing it on the other hand.......... that's where the “fun”
(i.e. hard) part comes in.&lt;/p&gt;
&lt;p&gt;I think a top-down (GUI-&amp;gt;implementation) strategy is best here. The
Grand Battle Plan(TM) goes something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get MIDI data displayed in regions, notes visible as one canvas item
   (rect) per note&lt;/li&gt;
&lt;li&gt;Attach event signals from note canvas items (move, click, etc) to a
   set of stub methods that encompass all the editing operations&lt;/li&gt;
&lt;li&gt;Now there's a nice centralized area where all the editing operations
   need to be implemented&lt;/li&gt;
&lt;li&gt;Figure out how the hell to implement them (ie ???????????)&lt;/li&gt;
&lt;li&gt;Implement them (ie Profit!)&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/><category term="Ardour"/><category term="Hacking"/></entry><entry><title>Unix Modular Synth</title><link href="https://drobilla.net/2007/04/28/unix-modular-synth.html" rel="alternate"/><published>2007-04-28T17:09:00-04:00</published><updated>2007-04-28T17:09:00-04:00</updated><author><name>drobilla</name></author><id>tag:drobilla.net,2007-04-28:/2007/04/28/unix-modular-synth.html</id><content type="html">&lt;p&gt;I've decided to use my blog to archive random ideas until I figure
something better out (on that note, anyone know of any good &lt;em&gt;RDF based&lt;/em&gt;
mind-map software, or something generic that can easily be used as one?)&lt;/p&gt;
&lt;p&gt;Until then:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;drobilla&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;crazy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;thought&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;day&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LV2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;extension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;
&lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trivial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;
&lt;span class="nv"&gt;utility&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;wrapper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;plugin&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;drobilla&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;anyone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;convert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;midi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;through&lt;/span&gt;
&lt;span class="nv"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;convert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;back&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;midi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;modular&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;gordonjcp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ahahaha&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;gordonjcp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;man&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;drobilla&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;modular&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;best&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;thing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ever&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ingix? Really wouldn't be that hard. Could even make it parallelize
forkey pipelines, if the LV2 contexts extension is powerful enough. More
on that later...&lt;/p&gt;</content><category term="misc"/><category term="Hacking"/><category term="LAD"/><category term="LV2"/></entry></feed>