aboutsummaryrefslogtreecommitdiffstats
path: root/ext/atom.lv2/atom.ttl
diff options
context:
space:
mode:
Diffstat (limited to 'ext/atom.lv2/atom.ttl')
-rw-r--r--ext/atom.lv2/atom.ttl485
1 files changed, 305 insertions, 180 deletions
diff --git a/ext/atom.lv2/atom.ttl b/ext/atom.lv2/atom.ttl
index a7d663b..cf048aa 100644
--- a/ext/atom.lv2/atom.ttl
+++ b/ext/atom.lv2/atom.ttl
@@ -30,255 +30,380 @@
<http://lv2plug.in/ns/ext/atom>
a lv2:Specification ;
- doap:name "LV2 Atom" ;
- doap:maintainer [
- a foaf:Person ;
- foaf:name "David Robillard" ;
- foaf:homepage <http://drobilla.net/> ;
- rdfs:seeAlso <http://drobilla.net/drobilla.rdf>
- ] ;
+ doap:name "LV2 Atom" ;
+ doap:maintainer [
+ a foaf:Person ;
+ foaf:name "David Robillard" ;
+ foaf:homepage <http://drobilla.net/> ;
+ rdfs:seeAlso <http://drobilla.net/drobilla.rdf>
+ ] ;
rdfs:comment """
This extension defines a generic format for a typed piece of data, called an
-"atom" (e.g. integers, strings, buffers, data structures, etc). Atoms allow
-LV2 plugins and host to communicate and store values of any type and size via
-a generic mechanism (e.g. port buffers, event payloads, shared data, etc.).
-Atoms are simple a chunk of memory with a type and a size, and are (with
-one exception) Plain Old Data (POD) and may be safely copied (e.g. with a
-simple call to <code>memcpy</code>). Because they are POD, hosts and plugins
-can communicate atoms of any type, even if they do not understand that type.
-This allows two plugins that both understand some type to be used together in
-a host that does not itself understand that type, or allows a host to send
-atoms "through" a plugin that does not understand them (for e.g. routing,
-delaying, or buffering plugins).
-
-Atoms as defined by this extension can be trivially constructed in-place
-from events as defined by the <a href="http://lv2plug.in/ns/ext/event">LV2
-Event</a> extension. A valid LV2_Atom (see atom.h) is contained within
-any valid LV2_Event (see event.h). An LV2_Event is simply an LV2_Atom
-with a time stamp header prepended. Atoms SHOULD be used anywhere a "value"
-needs to be stored or communicated, to allow implementations to be
-polymorphic and extensible.
-
-Atoms (the beginning of the LV2_Atom header) MUST be 32-bit aligned.
-
-Optionally, the host MAY support "Blobs", which are dynamically allocated
-chunks of memory that (unlike Atoms) are not necessarily POD. Blobs are
-accessed via references, which are a special case of Atom that always have
-type 0, are not POD, and can only be copied using host provided functions.
-This allows plugins and hosts to work with data of any type at all.
-Blob data MUST NOT be used in any way by an implementation that does not
-understand that blob type (unlike other Atoms, meaningful type-oblivious use
-of a Blob is impossible).
+"<a href="#Atom">Atom</a>" (e.g. integers, strings, buffers, data structures,
+etc). Atoms allow LV2 plugins and hosts to communicate, process, serialise,
+and store values of any type via a generic mechanism (e.g. LV2 ports, events,
+disk, shared memory, network). Atoms are, with one exception, Plain
+Old Data (POD) and may be safely copied (e.g. with a simple call to
+<code>memcpy</code>).
+
+Since Atom communication can be implemented generically, plugins that
+understand some type can be used together in a host that does not understand
+that type, and plugins (e.g. routers, delays) can process atoms of unknown
+type.
+
+An Atom can be trivially constructed in-place from an
+<a href="http://lv2plug.in/ns/ext/event#Event">Event</a> as defined by the
+<a href="http://lv2plug.in/ns/ext/event">LV2 Event</a> extension. In other
+words, an Event is simply an Atom with a time stamp header. Atoms SHOULD
+be used anywhere a "value" needs to be stored or communicated, to allow
+implementations to be polymorphic and extensible.
+
+Atoms (the start of the LV2_Atom header) MUST be 32-bit aligned.
+
+Optionally, the host MAY implement <a href="#blobSupport">blob support</a>.
+A <a href="#Blob">Blob</a> is a dynamically allocated chunk of memory
+that (unlike an Atom) is not necessarily POD. Blobs are accessed via a
+<a href="#Reference">Reference</a>, which is a special case of Atom that
+always has <code>type = 0</code>, is not POD, and can only be copied using
+host provided functions. This allows plugins and hosts to work with data
+of any type at all.
+
+Atoms can be communicated in many ways. Since an Atom is the payload of an
+Event, an <a href="http://lv2plug.in/ns/ext/event#EventPort">EventPort</a>
+can be used for communicating Atoms in realtime with sub-sample time stamp
+accuracy. This extension also defines two port types for connecting directly
+to a single Atom: <a href="ValuePort">ValuePort</a> and <a href="#MessagePort"
+>MessagePort</a>, which both have the same buffer format but different
+semantics (with respect to how the run() callback interprets the Atom).
This extension requires the host to support the <a
href="http://lv2plug.in/ns/ext/uri-map">LV2 URI Map</a> extension.
""" .
-atom:AtomType a rdfs:Class ;
- rdfs:label "LV2 Atom Type" ;
+atom:Atom a rdfs:Class ;
+ rdfs:label "Atom" ;
rdfs:comment """
-Base class for all types of LV2 Atom.
-
-All Atom types (instances of this class, which are themselves classes)
-must define a precise binary layout for that type of atom, which dictates
-the format of the data following the LV2_Atom header.
-
-The URIs of subclasses of atom:AtomType are mapped to integers and used as
-the type field of an LV2_Atom. If a plugin or host does not understand
-the type of an LV2_Atom, that atom SHOULD simply be ignored (though it
-MAY be simply copied if it is not a reference).
-
-All atoms are POD by definition, except references, which have type 0.
-An atom MUST NOT contain a reference. It is safe to copy any type of
-atom except type 0 with a simple memcpy using the size field, even if the
-implementation does not understand the actual type of that atom.
+Abstract base class for all atoms. An <a href="urn:struct:LV2_Atom"
+>LV2_Atom</a> has a 16-bit <code>type</code> and <code>size</code> followed by
+a <code>body</code>.
+
+All concrete Atom types (subclasses of this class) MUST define a precise
+binary layout for <code>body</code>.
+
+The <code>type</code> field is the URI of a subclass of Atom mapped to an
+integer using the <a href="http://lv2plug.in/ns/ext/uri-map">URI Map</a>
+extension's <a href="urn:struct:LV2_URI_Map_Feature"
+>LV2_URI_Map_Feature</a>::uri_to_id with
+<code>map = "http://lv2plug.in/ns/ext/event"</code>. If a plugin or host
+does not understand <code>type</code>, that atom SHOULD be gracefully ignored
+(though it MAY be copied if it is not a <a href="#Reference">Reference</a>).
+
+All atoms are POD by definition except references, which have
+<code>type = 0</code>. An Atom MUST NOT contain a Reference. It is safe
+to copy any non-reference Atom with a simple <code>memcpy</code>, even if
+the implementation does not understand <code>type</code>.
""" .
-atom:Reference a atom:AtomType ;
- rdfs:label "Reference" ;
+atom:Reference a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Reference" ;
rdfs:comment """
-Reference to a blob. The actual contents of a reference are opaque and host
-specific, and must not be copied, serialized, or otherwise interpreted by
-a plugin, except by using functions provided by the host.
+Reference to a <a href="#Blob">Blob</a>. The actual contents of a Reference
+are opaque and host specific, and must not be copied, serialized, or otherwise
+interpreted by a plugin, except via functions provided by the host in
+<a href="urn:struct:LV2_Blob_Support">LV2_Blob_Support</a>.
-References are a special case: a reference atom always has type 0.
-The NULL reference is the unique atom with type 0 and size 0.
+A Reference is a special case of Atom with <code>type = 0</code>.
+"Null" is the unique Atom with <code>type = 0</code> and <code>size = 0</code>.
""" .
-atom:String a atom:AtomType ;
- rdfs:label "String" ;
+atom:String a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "String" ;
rdfs:comment """
-A UTF-8 encoded string, where LV2_Atom.size refers to the length of the
-string in bytes (not characters).
+A UTF-8 encoded string, with an optional language tag. An
+<a href="urn:struct:LV2_Atom_String">LV2_Atom_String</a> has an <a href="#ID">ID</a>
+<code>lang</code> followed by the string data in UTF-8 encoding. The length of the
+string data in bytes is <code>size - sizeof(uint32_t)</code>, including the
+terminating NULL character. The <code>lang</code> may be any URI; to
+describe a human language, use http://lexvo.org/id/term/LANG where LANG is
+an <a href="http://www.loc.gov/standards/iso639-2/">ISO 693-2</a> or
+<a href="http://www.loc.gov/standards/iso639-2/">ISO 693-3</a> language code.
+
+For example, "Hello" in English:
+<pre>
+struct LV2_Atom {
+ uint16_t type = uri_to_id(atom:String);
+ uint16_t size = 10;
+}
+uint32_t lang = uri_to_id("http://lexvo.org/id/term/en");
+char str[] = "Hello";
+</pre>
+and French:
+<pre>
+struct LV2_Atom {
+ uint16_t type = uri_to_id(atom:String);
+ uint16_t size = 12;
+}
+uint32_t lang = uri_to_id("http://lexvo.org/id/term/fr");
+char str[] = "Bonjour";
+</pre>
+or a Turtle string:
+<pre>
+struct LV2_Atom {
+ uint16_t type = uri_to_id(atom:String);
+ uint16_t size = 60;
+}
+uint32_t lang = uri_to_id("http://www.w3.org/2008/turtle#turtle");
+char str[] = "&lt;http://example.org/foo&gt; a &lt;http://example.org/Thing&gt; ."
+</pre>
""" .
-atom:URIInt a atom:AtomType ;
- rdfs:label "URI mapped to an integer" ;
+atom:ID a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Integer ID mapped from a URI" ;
rdfs:comment """
-A uint32_t interpreted as a URI mapped to an integer using the LV2
-URI map extension &lt;http://lv2plug.in/ns/ext/uri-map&gt;. Size is
-always 4.
+An unsigned 32-bit integer mapped from a URI using the
+<a href="http://lv2plug.in/ns/ext/uri-map">URI Map</a> extension's
+<a href="urn:struct:LV2_URI_Map_Feature">LV2_URI_Map_Feature</a>::uri_to_id
+with <code>map = NULL</code>.
""" .
-atom:Vector a atom:AtomType ;
- rdfs:label "Vector" ;
+atom:Vector a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Vector" ;
rdfs:comment """
-A POD homogeneous sequence of atoms with equivalent type and size.
+A homogeneous sequence of atoms with equivalent type and size.
-The body of a vector begins with
-<pre>
-uint16_t elem_count; // The number of elements in the vector
-uint16_t elem_type; // The type of each element
-</pre>
-followed by <code>elem_count</code> bodies of atoms of type
-<code>elem_type</code>, each with equivalent size. For variably sized
-content types, this size can be calculated using the total byte size of the
-vector, e.g.
+An <a href="urn:struct:LV2_Atom_Vector">LV2_Atom_Vector</a> is a
+16-bit <code>elem_count</code> and <code>elem_type</code> followed
+by <code>elem_count</code> atom bodies of type <code>elem_type</code>.
+The element type must be a fixed size <a href="#Atom">Atom</a> type, i.e. the
+size of each element is the vector's <code>size / elem_count</code>.
+
+For example, an atom:Vector containing 42 elements of type atom:Float looks
+like this in memory:
<pre>
-uint16_t elem_size = (vector.size - (2 * sizeof(uint16_t))) / vector.count);
+struct LV2_Atom {
+ uint16_t type = uri_to_id(atom:Vector);
+ uint16_t size = sizeof(LV2_Atom) + sizeof(LV2_Atom_Vector) + (42 * sizeof(float);
+}
+struct LV2_Vector {
+ uint16_t elem_count = 42;
+ uint16_t elem_type = uri_to_id(atom:Float);
+}
+float elem_00;
+float elem_01;
+...
+float elem_41;
</pre>
+
Note that it is possible to construct a valid Atom for each element
of the vector, even by an implementation which does not understand
<code>elem_type</code>.
-Note that an Atom is 32-bit aligned by definition, and both the Atom and
-Vector headers are 32-bits each, therefore the first element of a Vector is
-64-bit aligned.
-
-For example, an atom:Vector containing 42 elements of type atom:Int32 looks
-like this in memory (including the Atom header):
-<pre>
-uint16_t atom_type = uri_map(atom:Vector)
-uint16_t atom_size = (2 * sizeof(uint16_t)) + (42 * sizeof(int32_t))
-uint16_t elem_count = 42
-uint16_t elem_type = uri_map(atom:Int32)
-int32_t contents[42] = ...
-</pre>
+A Vector header is 64-bits, thus the first element of a Vector is 64-bit
+aligned if the Vector itself is 64-bit aligned.
""" .
-atom:Tuple a atom:AtomType ;
+atom:Tuple a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
rdfs:label "Tuple" ;
- rdfs:comment """
-A POD sequence of atoms with varying types and sizes.
+ rdfs:comment """
+A sequence of <a href="#Atom">atoms</a> with varying <code>type</code>
+and <code>size</code>.
-The body of a tuple is simply a series of complete atoms, aligned to
+The body of a Tuple is simply a sequence of complete atoms, each aligned to
32 bits.
""" .
-atom:Property a atom:AtomType ;
- rdfs:label "RDF property of some object" ;
+atom:Property a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Property of an Object" ;
rdfs:comment """
-A description of a single property for some object (i.e. an RDF
-statement with only predicate (key) and object (value) defined).
-<pre>
-uint32_t key;
-LV2_Atom value;
-</pre>
+A single property of some <a href="#Object">Object</a>. An
+<a href="urn:struct:LV2_Atom_Property">LV2_Atom_Property</a>
+has an <a href="#ID">ID</a> <code>key</code> and
+<a href="#Atom">Atom</a> <code>value</code>.
""" .
-atom:Triple a atom:AtomType ;
- rdfs:label "RDF triple" ;
+atom:Object a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Object" ;
rdfs:comment """
-A single RDF triple.
-
-The subject and predicate of a Triple are implicitly URIs, thus they are stored
-as URI mapped integers with type and size ommitted (i.e. a single uint32_t).
+Abstract base class for an "Object", i.e. an <a href="#Atom">Atom</a>
+with a number of <a href="#Property">properties</a>. An <a
+href="urn:struct:LV2_Atom_Object" >LV2_Atom_Object</a> is an unsigned 32-bit
+integer <code>context</code> and <code>id</code> followed by a sequence of
+<a href="urn:struct:LV2_Atom_Property" >properties</a>.
+
+The <code>context</code> is mapped using the <a
+href="http://lv2plug.in/ns/ext/uri-map">URI Map</a> extension's <a
+href="urn:struct:LV2_URI_Map_Feature">LV2_URI_Map_Feature</a>::uri_to_id
+with <code>map = NULL</code>, and may be 0 (the default context).
+
+Note this is an abstract class, i.e. no Atom can exist with
+<code>type = uri_to_id(atom:Object)</code>. An Object is
+either a <a href="urn:struct:LV2_Resource">Resource</a> or a <a
+href="urn:struct:Blank">Blank</a>, but the <code>body</code> always has the
+same binary format. Thus, both named and anonymous objects can be handled
+with common code using only a 64-bit header for both.
+""" .
-An atom:Triple is memory is a uint32_t subject immediately followed by the
-body of an atom:Property.
-An atom:Triple in memory is two uint32_t's followed by an LV2_Atom:
-<pre>
-uint32_t subject;
-uint32_t predicate;
-LV2_Atom object;
-</pre>
+atom:Resource a rdfs:Class ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:comment """
+An <a href="#Object">Object</a> where <code>id</code> is the
+URI of the resource mapped to an <a href="#ID">ID</a>.
""" .
-atom:Triples a atom:AtomType ;
- rdfs:label "RDF triple set" ;
+atom:Blank a rdfs:Class ;
+ rdfs:subClassOf atom:Object ;
rdfs:comment """
-A description in RDF (i.e. a set of triples).
+An <a href="#Object">Object</a> where <code>id</code> is the blank node ID of
+the object, which is only meaningful within a certain limited scope
+(e.g. the container of the Blank) and MUST NOT be used as a global ID.
+In particular, <code>id</code> is NOT an <a href="ID">ID</a>.
+""" .
-An atom:Triples contains any number of RDF triples, describing one or
-several resources. The subject and predicate of all triples are implicitly
-URI mapped integers, type tags are omitted. The object of triples may be
-any atom.
-An atom:Triples in memory is a sequence of atom:Triple where each atom:Triple
-is immediately followed by the next (without time stamps or sizes), with
-padding to ensure each subject is 32-bit aligned, e.g.:
-<pre>
-uint32_t subject1;
-uint32_t predicate1;
-LV2_Atom object1;
-uint8_t pad[1]; /* e.g. if object1.size == 3 */
-uint32_t subject2;
-uint32_t predicate2;
-LV2_Atom object2;
-...
-</pre>
+atom:Model a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Model" ;
+ rdfs:comment """
+A description of a set of <a href="#Object">objects</a>. In memory, a Model is
+simply a sequence of objects.
""" .
-
-atom:Blank a atom:AtomType ;
- rdfs:label "Blank (anonymous resource)" ;
+atom:Bang a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Bang (activity) (<code>size = 0</code>)" .
+
+atom:Byte
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Byte (<code>size = 1</code>)" .
+
+atom:Int32 a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Signed 32-bit integer" .
+
+atom:Int64 a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Signed 64-bit integer" .
+
+atom:Bool a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Signed 32-bit integer where 0 is false" .
+
+atom:Float a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "32-bit IEEE-754 floating point number" .
+
+atom:Double a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "64-bit IEEE-754 floating point number" .
+
+atom:blobSupport a lv2:Feature ;
+ rdfs:label "Blob support" ;
rdfs:comment """
-A description of an RDF resource with no URI (a resource with blank node
-ID), e.g. the resource of type ex:Thing in the following Turtle description:
-<code>&lt;&gt; ex:hasThing [ a ex:Thing ]</code>
+Support for dynamically allocated blobs. If a host supports this feature, it
+MUST pass a <a href="urn:struct:LV2_Feature">LV2_Feature</a> with
+<code>URI</code> http://lv2plug.in/ns/ext/atom#blobSupport
+and <code>data</code> pointing to a <a href="urn:struct:LV2_Blob_Support"
+>LV2_Blob_Support</a>.
+""" .
-An atom:Blank is conceptually a dictionary where keys (RDF predicates) are
-URI mapped integers, and values (RDF objects) are any atom.
-An atom:Blank in memory is like an atom:Triples, but with subjects omitted:
-<pre>
-uint32_t predicate1;
-LV2_Atom object1;
-uint32_t predicate2;
-LV2_Atom object2;
-...
-</pre>
+atom:Blob a rdfs:Class ;
+ rdfs:label "Blob" ;
+ rdfs:comment """
+Base class for all dynamically allocated blobs. An <a
+href="urn:struct:LV2_Blob" >LV2_Blob</a> ia an opaque pointer to host
+data. The type and data of a blob can be accessed via host-provided
+functions in <a href="urn:struct:LV2_Blob_Support">LV2_Blob_Support</a>.
+The type of a blob can be any URI that describes a data format. Blobs are
+always allocated by the host, and unlike atoms are not necessarily POD.
+
+Blob data MUST NOT be used in any way by an implementation that does not
+understand that blob type (unlike Atoms, meaningful type-oblivious use
+of a Blob is impossible).
""" .
-atom:Bang a atom:AtomType ; rdfs:label "Bang (generic activity), size=0" .
-atom:Byte a atom:AtomType ; rdfs:label "A byte" .
-atom:Int32 a atom:AtomType ; rdfs:label "Signed 32-bit Integer" .
-atom:Bool a atom:AtomType ; rdfs:label "atom:Int32 where 0=false, 1=true" .
-atom:Float32 a atom:AtomType ; rdfs:label "32-bit Floating Point Number" .
-atom:Float64 a atom:AtomType ; rdfs:label "64-bit Floating Point Number" .
+atom:AtomPort a rdfs:Class ;
+ rdfs:subClassOf lv2:Port ;
+ rdfs:label "Atom Port" ;
+ rdfs:comment """
+A port which contains an <a href="#Atom">Atom</a>. Ports of this type will
+be connected to a 32-bit aligned <a href="urn:struct:LV2_Atom">LV2_Atom</a>
+immediately followed by <code>size</code> bytes of data.
+
+This is an abstract port type, i.e. a port MUST NOT only be an AtomPort,
+but must be a more descriptive type that is a subclass of AtomPort which
+defines the port's semantics (typically <a href="#ValuePort">ValuePort</a>
+or <a href="#MessagePort">MessagePort</a>).
+
+Before calling a method on the plugin that writes to an AtomPort output,
+the host MUST set the size of the Atom in that output to the amount of
+available memory immediately following the Atom header. The plugin MUST
+write a valid Atom to that port (leaving it untouched is illegal). If there
+is no reasonable value to write to the port, the plugin MUST write NULL
+(the Atom with both <code>type = 0</code> and <code>size = 0</code>).
+""" .
-atom:blobSupport a lv2:Feature ;
- rdfs:label "Blob support" ;
- rdfs:comment """
-
-Support for dynamically allocated blobs. If a host supports this feature, it
-MUST pass an LV2_Feature with URI http://lv2plug.in/ns/ext/atom#blobSupport
-and a pointer to LV2_Blob_Support as data to the plugin's instantiate method.
-See atom.h for details.
+atom:ValuePort a rdfs:Class ;
+ rdfs:subClassOf atom:AtomPort ;
+ rdfs:label "Value Port" ;
+ rdfs:comment """
+An AtomPort that interprets its data as a persistent and time-independent
+"value".
+<ul>
+<li>If a plugin has fixed input values for all ports, all ValuePort outputs
+are also fixed regardless of the number of times the plugin is run.</li>
+<li>If a plugin has fixed input values for all ports except a ValuePort,
+each value V of that ValuePort corresponds to a single set of outputs
+for all ports.</li>
+<li>If a ValuePort contains a reference then the blob it refers to is
+constant; plugin MUST NOT modify the blob in any way.</li>
+</ul>
+Value ports can be thought of as purely functional ports: if a plugin
+callback has only value ports, then the plugin callback is a pure function.
""" .
-atom:BlobType a rdfs:Class ;
- rdfs:label "Blob Type" ;
+atom:MessagePort a rdfs:Class ;
+ rdfs:subClassOf atom:AtomPort ;
+ rdfs:label "Message Port" ;
rdfs:comment """
-Base class for all types of dynamically allocated LV2 blobs. Blobs can be of
-any type at all, there are no restrictions on the binary format or contents
-of a blob. Blobs are dynamically allocated by the host (or a plugin via
-the host), and unlike Atoms are not necessarily POD.
+An AtomPort that "receives", "consumes", "executes", or "sends" its value.
+The Atom contained in a MessagePort is considered transient and/or
+time-dependent, and is only valid for a single run invocation. Unlike a
+ValuePort, a MessagePort may be used to manipulate internal plugin state.
-The type of a blob MAY be a atom:AtomType, in which case the start of the
-blob data is the start of the Atom header (LV2_Atom).
+Intuitively, a MessagePort contains a "message" or "command" or "event"
+which is reacted to, NOT a "value" or "signal" (which is computed with).
+""" .
+
+atom:supports a rdf:Property ;
+ rdfs:domain lv2:Port ;
+ rdfs:range atom:Atom ;
+ rdfs:label "supports" ;
+ rdfs:comment """
+Indicates that a Port supports a certain <a href="#Atom">Atom</a> type.
+This is distinct from the port type - e.g. the port type ValuePort can hold
+atoms with many different types. This property is used to describe which
+Atom types a Port expects to receive or send.
""" .