aboutsummaryrefslogtreecommitdiffstats
path: root/lv2/lv2plug.in/ns/ext/atom
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-02-08 04:56:24 +0000
committerDavid Robillard <d@drobilla.net>2012-02-08 04:56:24 +0000
commited78bbe5ba12be1f9bcc736f14c51da6b4f639f3 (patch)
tree653a2dfe33f3923da45a38fc04ed2106f93528f3 /lv2/lv2plug.in/ns/ext/atom
parentb617875c6f3ad439d25ae166da79df839ebfdc71 (diff)
downloadlv2-ed78bbe5ba12be1f9bcc736f14c51da6b4f639f3.tar.xz
Rearrange tree so top level can be used as an include path for standard style LV2 includes.
Diffstat (limited to 'lv2/lv2plug.in/ns/ext/atom')
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/atom-helpers.h310
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/atom-test.c304
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/atom.h203
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/atom.ttl462
l---------lv2/lv2plug.in/ns/ext/atom/ext.pc.in1
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/forge.h445
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/manifest.ttl9
l---------lv2/lv2plug.in/ns/ext/atom/waf1
l---------lv2/lv2plug.in/ns/ext/atom/wscript1
9 files changed, 1736 insertions, 0 deletions
diff --git a/lv2/lv2plug.in/ns/ext/atom/atom-helpers.h b/lv2/lv2plug.in/ns/ext/atom/atom-helpers.h
new file mode 100644
index 0000000..6fff74b
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/atom-helpers.h
@@ -0,0 +1,310 @@
+/*
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file atom-helpers.h Helper functions for the LV2 Atom extension.
+
+ Note these functions are all static inline, do not take their address.
+
+ This header is non-normative, it is provided for convenience.
+*/
+
+#ifndef LV2_ATOM_HELPERS_H
+#define LV2_ATOM_HELPERS_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+/** Pad a size to 64 bits. */
+static inline uint32_t
+lv2_atom_pad_size(uint32_t size)
+{
+ return (size + 7) & (~7);
+}
+
+/** Return true iff @p atom is null. */
+static inline bool
+lv2_atom_is_null(const LV2_Atom* atom)
+{
+ return !atom || (atom->type == 0 && atom->size == 0);
+}
+
+/** Return true iff @p a is equal to @p b. */
+static inline bool
+lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b)
+{
+ return (a == b) || (
+ (a->type == b->type) &&
+ (a->size == b->size) &&
+ !memcmp(LV2_ATOM_CONTENTS(LV2_Atom, a),
+ LV2_ATOM_CONTENTS(LV2_Atom, b),
+ a->size));
+}
+
+/**
+ @name Sequence Iterator
+ @{
+*/
+
+/** An iterator over the elements of an LV2_Atom_Sequence. */
+typedef LV2_Atom_Event* LV2_Atom_Sequence_Iter;
+
+/** Get an iterator pointing to the first element in @p tup. */
+static inline LV2_Atom_Sequence_Iter
+lv2_sequence_begin(const LV2_Atom_Sequence* seq)
+{
+ return (LV2_Atom_Sequence_Iter)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq));
+}
+
+/** Return true iff @p i has reached the end of @p tup. */
+static inline bool
+lv2_sequence_is_end(const LV2_Atom_Sequence* seq, LV2_Atom_Sequence_Iter i)
+{
+ return (uint8_t*)i >= ((uint8_t*)seq + sizeof(LV2_Atom) + seq->atom.size);
+}
+
+/** Return an iterator to the element following @p i. */
+static inline LV2_Atom_Sequence_Iter
+lv2_sequence_iter_next(const LV2_Atom_Sequence_Iter i)
+{
+ return (LV2_Atom_Sequence_Iter)((uint8_t*)i
+ + sizeof(LV2_Atom_Event)
+ + lv2_atom_pad_size(i->body.size));
+}
+
+/** Return the element pointed to by @p i. */
+static inline LV2_Atom_Event*
+lv2_sequence_iter_get(LV2_Atom_Sequence_Iter i)
+{
+ return (LV2_Atom_Event*)i;
+}
+
+/**
+ A macro for iterating over all events in a Sequence.
+ @param sequence The sequence to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), e.g.:
+ @code
+ LV2_SEQUENCE_FOREACH(sequence, i) {
+ LV2_Atom_Event* ev = lv2_sequence_iter_get(i);
+ // Do something with ev here...
+ }
+ @endcode
+*/
+#define LV2_SEQUENCE_FOREACH(sequence, iter) \
+ for (LV2_Atom_Sequence_Iter (iter) = lv2_sequence_begin(sequence); \
+ !lv2_sequence_is_end(sequence, (iter)); \
+ (iter) = lv2_sequence_iter_next(iter))
+
+/**
+ @}
+ @name Tuple Iterator
+ @{
+*/
+
+/** An iterator over the elements of an LV2_Atom_Tuple. */
+typedef LV2_Atom* LV2_Atom_Tuple_Iter;
+
+/** Get an iterator pointing to the first element in @p tup. */
+static inline LV2_Atom_Tuple_Iter
+lv2_tuple_begin(const LV2_Atom_Tuple* tup)
+{
+ return (LV2_Atom_Tuple_Iter)(LV2_ATOM_BODY(tup));
+}
+
+/** Return true iff @p i has reached the end of @p tup. */
+static inline bool
+lv2_tuple_is_end(const LV2_Atom_Tuple* tup, LV2_Atom_Tuple_Iter i)
+{
+ return (uint8_t*)i >= ((uint8_t*)tup + sizeof(LV2_Atom) + tup->atom.size);
+}
+
+/** Return an iterator to the element following @p i. */
+static inline LV2_Atom_Tuple_Iter
+lv2_tuple_iter_next(const LV2_Atom_Tuple_Iter i)
+{
+ return (LV2_Atom_Tuple_Iter)(
+ (uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size));
+}
+
+/** Return the element pointed to by @p i. */
+static inline LV2_Atom*
+lv2_tuple_iter_get(LV2_Atom_Tuple_Iter i)
+{
+ return (LV2_Atom*)i;
+}
+
+/**
+ A macro for iterating over all properties of a Tuple.
+ @param tuple The tuple to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), e.g.:
+ @code
+ LV2_TUPLE_FOREACH(tuple, i) {
+ LV2_Atom_Property* prop = lv2_tuple_iter_get(i);
+ // Do something with prop here...
+ }
+ @endcode
+*/
+#define LV2_TUPLE_FOREACH(tuple, iter) \
+ for (LV2_Atom_Tuple_Iter (iter) = lv2_tuple_begin(tuple); \
+ !lv2_tuple_is_end(tuple, (iter)); \
+ (iter) = lv2_tuple_iter_next(iter))
+
+/**
+ @}
+ @name Object Iterator
+ @{
+*/
+
+/** An iterator over the properties of an LV2_Atom_Object. */
+typedef LV2_Atom_Property_Body* LV2_Atom_Object_Iter;
+
+/** Get an iterator pointing to the first property in @p obj. */
+static inline LV2_Atom_Object_Iter
+lv2_object_begin(const LV2_Atom_Object* obj)
+{
+ return (LV2_Atom_Object_Iter)(LV2_ATOM_CONTENTS(LV2_Atom_Object, obj));
+}
+
+/** Return true iff @p i has reached the end of @p obj. */
+static inline bool
+lv2_object_is_end(const LV2_Atom_Object* obj, LV2_Atom_Object_Iter i)
+{
+ return (uint8_t*)i >= ((uint8_t*)obj + sizeof(LV2_Atom) + obj->atom.size);
+}
+
+/** Return true iff @p l points to the same property as @p r. */
+static inline bool
+lv2_object_iter_equals(const LV2_Atom_Object_Iter l,
+ const LV2_Atom_Object_Iter r)
+{
+ return l == r;
+}
+
+/** Return an iterator to the property following @p i. */
+static inline LV2_Atom_Object_Iter
+lv2_object_iter_next(const LV2_Atom_Object_Iter i)
+{
+ const LV2_Atom* const value = (LV2_Atom*)((uint8_t*)i + sizeof(i));
+ return (LV2_Atom_Object_Iter)((uint8_t*)i
+ + sizeof(LV2_Atom_Property_Body)
+ + lv2_atom_pad_size(value->size));
+}
+
+/** Return the property pointed to by @p i. */
+static inline LV2_Atom_Property_Body*
+lv2_object_iter_get(LV2_Atom_Object_Iter i)
+{
+ return (LV2_Atom_Property_Body*)i;
+}
+
+/**
+ A macro for iterating over all properties of an Object.
+ @param object The object to iterate over
+ @param iter The name of the iterator
+
+ This macro is used similarly to a for loop (which it expands to), e.g.:
+ @code
+ LV2_OBJECT_FOREACH(object, i) {
+ LV2_Atom_Property* prop = lv2_object_iter_get(i);
+ // Do something with prop here...
+ }
+ @endcode
+*/
+#define LV2_OBJECT_FOREACH(object, iter) \
+ for (LV2_Atom_Object_Iter (iter) = lv2_object_begin(object); \
+ !lv2_object_is_end(object, (iter)); \
+ (iter) = lv2_object_iter_next(iter))
+
+/**
+ @}
+ @name Object Query
+ @{
+*/
+
+/** A single entry in an Object query. */
+typedef struct {
+ uint32_t key; /**< Key to query (input set by user) */
+ const LV2_Atom** value; /**< Found value (output set by query function) */
+} LV2_Atom_Object_Query;
+
+static const LV2_Atom_Object_Query LV2_OBJECT_QUERY_END = { 0, NULL };
+
+/**
+ Get an object's values for various keys.
+
+ The value pointer of each item in @p query will be set to the location of
+ the corresponding value in @p object. Every value pointer in @p query MUST
+ be initialised to NULL. This function reads @p object in a single linear
+ sweep. By allocating @p query on the stack, objects can be "queried"
+ quickly without allocating any memory. This function is realtime safe.
+
+ For example:
+ @code
+ const LV2_Atom* name = NULL;
+ const LV2_Atom* age = NULL;
+ LV2_Atom_Object_Query q[] = {
+ { urids.eg_name, &name },
+ { urids.eg_age, &age },
+ LV2_OBJECT_QUERY_END
+ };
+ lv2_object_get(obj, q);
+ // name and age are now set to the appropriate values in obj, or NULL.
+ @endcode
+*/
+static inline int
+lv2_object_get(const LV2_Atom_Object* object, LV2_Atom_Object_Query* query)
+{
+ int matches = 0;
+ int n_queries = 0;
+
+ /* Count number of query keys so we can short-circuit when done */
+ for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
+ ++n_queries;
+ }
+
+ LV2_OBJECT_FOREACH(object, o) {
+ const LV2_Atom_Property_Body* prop = lv2_object_iter_get(o);
+ for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
+ if (q->key == prop->key && !*q->value) {
+ *q->value = &prop->value;
+ if (++matches == n_queries) {
+ return matches;
+ }
+ break;
+ }
+ }
+ }
+ return matches;
+}
+
+/**
+ @}
+*/
+
+#endif /* LV2_ATOM_HELPERS_H */
diff --git a/lv2/lv2plug.in/ns/ext/atom/atom-test.c b/lv2/lv2plug.in/ns/ext/atom/atom-test.c
new file mode 100644
index 0000000..1a88d7d
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/atom-test.c
@@ -0,0 +1,304 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom-helpers.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+
+char** uris = NULL;
+size_t n_uris = 0;
+
+char*
+copy_string(const char* str)
+{
+ const size_t len = strlen(str);
+ char* dup = (char*)malloc(len + 1);
+ memcpy(dup, str, len + 1);
+ return dup;
+}
+
+LV2_URID
+urid_map(LV2_URID_Map_Handle handle, const char* uri)
+{
+ for (size_t i = 0; i < n_uris; ++i) {
+ if (!strcmp(uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
+ uris[n_uris - 1] = copy_string(uri);
+ return n_uris;
+}
+
+int
+test_fail(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ return 1;
+}
+
+int
+main()
+{
+ LV2_URID_Map map = { NULL, urid_map };
+ LV2_Atom_Forge forge;
+ lv2_atom_forge_init(&forge, &map);
+
+ LV2_URID eg_Object = urid_map(NULL, "http://example.org/Object");
+ LV2_URID eg_one = urid_map(NULL, "http://example.org/one");
+ LV2_URID eg_two = urid_map(NULL, "http://example.org/two");
+ LV2_URID eg_three = urid_map(NULL, "http://example.org/three");
+ LV2_URID eg_four = urid_map(NULL, "http://example.org/four");
+ LV2_URID eg_true = urid_map(NULL, "http://example.org/true");
+ LV2_URID eg_false = urid_map(NULL, "http://example.org/false");
+ LV2_URID eg_uri = urid_map(NULL, "http://example.org/uri");
+ LV2_URID eg_string = urid_map(NULL, "http://example.org/string");
+ LV2_URID eg_literal = urid_map(NULL, "http://example.org/literal");
+ LV2_URID eg_tuple = urid_map(NULL, "http://example.org/tuple");
+ LV2_URID eg_vector = urid_map(NULL, "http://example.org/vector");
+ LV2_URID eg_seq = urid_map(NULL, "http://example.org/seq");
+
+#define BUF_SIZE 1024
+#define NUM_PROPS 12
+
+ uint8_t buf[BUF_SIZE];
+ lv2_atom_forge_set_buffer(&forge, buf, BUF_SIZE);
+
+ LV2_Atom* obj = (LV2_Atom*)lv2_atom_forge_object(
+ &forge, NULL, 0, eg_Object);
+
+ // eg_one = (Int32)1
+ lv2_atom_forge_property_head(&forge, obj, eg_one, 0);
+ LV2_Atom_Int32* one = lv2_atom_forge_int32(&forge, obj, 1);
+ if (one->value != 1) {
+ return test_fail("%d != 1\n", one->value);
+ }
+
+ // eg_two = (Int64)2
+ lv2_atom_forge_property_head(&forge, obj, eg_two, 0);
+ LV2_Atom_Int64* two = lv2_atom_forge_int64(&forge, obj, 2);
+ if (two->value != 2) {
+ return test_fail("%ld != 2\n", two->value);
+ }
+
+ // eg_three = (Float)3.0
+ lv2_atom_forge_property_head(&forge, obj, eg_three, 0);
+ LV2_Atom_Float* three = lv2_atom_forge_float(&forge, obj, 3.0f);
+ if (three->value != 3) {
+ return test_fail("%f != 3\n", three->value);
+ }
+
+ // eg_four = (Double)4.0
+ lv2_atom_forge_property_head(&forge, obj, eg_four, 0);
+ LV2_Atom_Double* four = lv2_atom_forge_double(&forge, obj, 4.0);
+ if (four->value != 4) {
+ return test_fail("%ld != 4\n", four->value);
+ }
+
+ // eg_true = (Bool)1
+ lv2_atom_forge_property_head(&forge, obj, eg_true, 0);
+ LV2_Atom_Bool* t = lv2_atom_forge_bool(&forge, obj, true);
+ if (t->value != 1) {
+ return test_fail("%ld != 1 (true)\n", t->value);
+ }
+
+ // eg_false = (Bool)0
+ lv2_atom_forge_property_head(&forge, obj, eg_false, 0);
+ LV2_Atom_Bool* f = lv2_atom_forge_bool(&forge, obj, false);
+ if (f->value != 0) {
+ return test_fail("%ld != 0 (false)\n", f->value);
+ }
+
+ // eg_uri = (URID)"http://example.org/value"
+ LV2_URID eg_value = urid_map(NULL, "http://example.org/value");
+ lv2_atom_forge_property_head(&forge, obj, eg_uri, 0);
+ LV2_Atom_URID* uri = lv2_atom_forge_urid(&forge, obj, eg_value);
+ if (uri->id != eg_value) {
+ return test_fail("%u != %u\n", uri->id, eg_value);
+ }
+
+ // eg_string = (String)"hello"
+ lv2_atom_forge_property_head(&forge, obj, eg_string, 0);
+ LV2_Atom_String* string = lv2_atom_forge_string(
+ &forge, obj, (const uint8_t*)"hello", strlen("hello"));
+ uint8_t* body = (uint8_t*)LV2_ATOM_BODY(string);
+ if (strcmp((const char*)body, "hello")) {
+ return test_fail("%s != \"hello\"\n", (const char*)body);
+ }
+
+ // eg_literal = (Literal)"hello"@fr
+ lv2_atom_forge_property_head(&forge, obj, eg_literal, 0);
+ LV2_Atom_Literal* literal = lv2_atom_forge_literal(
+ &forge, obj, (const uint8_t*)"bonjour", strlen("bonjour"),
+ 0, urid_map(NULL, "http://lexvo.org/id/term/fr"));
+ body = (uint8_t*)LV2_ATOM_CONTENTS(LV2_Atom_Literal, literal);
+ if (strcmp((const char*)body, "bonjour")) {
+ return test_fail("%s != \"bonjour\"\n", (const char*)body);
+ }
+
+ // eg_tuple = "foo",true
+ lv2_atom_forge_property_head(&forge, obj, eg_tuple, 0);
+ LV2_Atom_Tuple* tuple = lv2_atom_forge_tuple(&forge, obj);
+ LV2_Atom_String* tup0 = lv2_atom_forge_string(
+ &forge, (LV2_Atom*)tuple, (const uint8_t*)"foo", strlen("foo"));
+ LV2_Atom_Bool* tup1 = lv2_atom_forge_bool(&forge, (LV2_Atom*)tuple, true);
+ obj->size += ((LV2_Atom*)tuple)->size;
+ LV2_Atom_Tuple_Iter i = lv2_tuple_begin(tuple);
+ if (lv2_tuple_is_end(tuple, i)) {
+ return test_fail("Tuple iterator is empty\n");
+ }
+ LV2_Atom* tup0i = (LV2_Atom*)lv2_tuple_iter_get(i);
+ if (!lv2_atom_equals((LV2_Atom*)tup0, tup0i)) {
+ return test_fail("Corrupt tuple element 0\n");
+ }
+ i = lv2_tuple_iter_next(i);
+ if (lv2_tuple_is_end(tuple, i)) {
+ return test_fail("Premature end of tuple iterator\n");
+ }
+ LV2_Atom* tup1i = lv2_tuple_iter_get(i);
+ if (!lv2_atom_equals((LV2_Atom*)tup1, tup1i)) {
+ return test_fail("Corrupt tuple element 1\n");
+ }
+ i = lv2_tuple_iter_next(i);
+ if (!lv2_tuple_is_end(tuple, i)) {
+ return test_fail("Tuple iter is not at end\n");
+ }
+
+ // eg_vector = (Vector<Int32>)1,2,3,4
+ lv2_atom_forge_property_head(&forge, obj, eg_vector, 0);
+ int32_t elems[] = { 1, 2, 3, 4 };
+ LV2_Atom_Vector* vector = lv2_atom_forge_vector(
+ &forge, obj, 4, forge.Int32, sizeof(int32_t), elems);
+ void* vec_body = LV2_ATOM_CONTENTS(LV2_Atom_Vector, vector);
+ if (memcmp(elems, vec_body, sizeof(elems))) {
+ return test_fail("Corrupt vector\n");
+ }
+
+ // eg_seq = (Sequence)1, 2
+ lv2_atom_forge_property_head(&forge, obj, eg_seq, 0);
+ LV2_Atom_Sequence* seq = lv2_atom_forge_sequence_head(&forge, obj, 0);
+ lv2_atom_forge_audio_time(&forge, (LV2_Atom*)seq, 0, 0);
+ lv2_atom_forge_int32(&forge, (LV2_Atom*)seq, 1);
+ lv2_atom_forge_audio_time(&forge, (LV2_Atom*)seq, 1, 0);
+ lv2_atom_forge_int32(&forge, (LV2_Atom*)seq, 2);
+ obj->size += seq->atom.size - sizeof(LV2_Atom);
+
+ // Test equality
+ LV2_Atom_Int32 itwo = { { forge.Int32, sizeof(int32_t) }, 2 };
+ if (lv2_atom_equals((LV2_Atom*)one, (LV2_Atom*)two)) {
+ return test_fail("1 == 2.0\n");
+ } else if (lv2_atom_equals((LV2_Atom*)one, (LV2_Atom*)&itwo)) {
+ return test_fail("1 == 2\n");
+ }
+
+ unsigned n_events = 0;
+ LV2_SEQUENCE_FOREACH(seq, i) {
+ LV2_Atom_Event* ev = lv2_sequence_iter_get(i);
+ if (ev->time.audio.frames != n_events
+ || ev->time.audio.subframes != 0) {
+ return test_fail("Corrupt event %u has bad time\n", n_events);
+ } else if (ev->body.type != forge.Int32) {
+ return test_fail("Corrupt event %u has bad type\n", n_events);
+ } else if (((LV2_Atom_Int32*)&ev->body)->value != (int)n_events + 1) {
+ return test_fail("Event %u != %d\n", n_events, n_events + 1);
+ }
+ ++n_events;
+ }
+
+ unsigned n_props = 0;
+ LV2_OBJECT_FOREACH((LV2_Atom_Object*)obj, i) {
+ LV2_Atom_Property_Body* prop = lv2_object_iter_get(i);
+ if (!prop->key) {
+ return test_fail("Corrupt property %u has no key\n", n_props);
+ } else if (prop->context) {
+ return test_fail("Corrupt property %u has context\n", n_props);
+ }
+ ++n_props;
+ }
+
+ if (n_props != NUM_PROPS) {
+ return test_fail("Corrupt object has %u properties != %u\n",
+ n_props, NUM_PROPS);
+ }
+
+ const LV2_Atom* one_match = NULL;
+ const LV2_Atom* two_match = NULL;
+ const LV2_Atom* three_match = NULL;
+ const LV2_Atom* four_match = NULL;
+ const LV2_Atom* true_match = NULL;
+ const LV2_Atom* false_match = NULL;
+ const LV2_Atom* uri_match = NULL;
+ const LV2_Atom* string_match = NULL;
+ const LV2_Atom* literal_match = NULL;
+ const LV2_Atom* tuple_match = NULL;
+ const LV2_Atom* vector_match = NULL;
+ const LV2_Atom* seq_match = NULL;
+ LV2_Atom_Object_Query q[] = {
+ { eg_one, &one_match },
+ { eg_two, &two_match },
+ { eg_three, &three_match },
+ { eg_four, &four_match },
+ { eg_true, &true_match },
+ { eg_false, &false_match },
+ { eg_uri, &uri_match },
+ { eg_string, &string_match },
+ { eg_literal, &literal_match },
+ { eg_tuple, &tuple_match },
+ { eg_vector, &vector_match },
+ { eg_seq, &seq_match },
+ LV2_OBJECT_QUERY_END
+ };
+
+ unsigned matches = lv2_object_get((LV2_Atom_Object*)obj, q);
+ if (matches != n_props) {
+ return test_fail("Query failed, %u matches != %u\n", matches, n_props);
+ } else if (!lv2_atom_equals((LV2_Atom*)one, one_match)) {
+ return test_fail("Bad match one\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)two, two_match)) {
+ return test_fail("Bad match two\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)three, three_match)) {
+ return test_fail("Bad match three\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)four, four_match)) {
+ return test_fail("Bad match four\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)t, true_match)) {
+ return test_fail("Bad match true\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)f, false_match)) {
+ return test_fail("Bad match false\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)uri, uri_match)) {
+ return test_fail("Bad match URI\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)string, string_match)) {
+ return test_fail("Bad match string\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)literal, literal_match)) {
+ return test_fail("Bad match literal\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)tuple, tuple_match)) {
+ return test_fail("Bad match tuple\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)vector, vector_match)) {
+ return test_fail("Bad match vector\n");
+ } else if (!lv2_atom_equals((LV2_Atom*)seq, seq_match)) {
+ return test_fail("Bad match sequence\n");
+ }
+
+ printf("All tests passed.\n");
+ return 0;
+}
diff --git a/lv2/lv2plug.in/ns/ext/atom/atom.h b/lv2/lv2plug.in/ns/ext/atom/atom.h
new file mode 100644
index 0000000..bea9197
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/atom.h
@@ -0,0 +1,203 @@
+/*
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file atom.h C header for the LV2 Atom extension
+ <http://lv2plug.in/ns/ext/atom>.
+
+ This header describes the binary layout of various types defined in the
+ atom extension.
+*/
+
+#ifndef LV2_ATOM_H
+#define LV2_ATOM_H
+
+#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom"
+
+#define LV2_ATOM_REFERENCE_TYPE 0
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This expression will fail to compile if double does not fit in 64 bits. */
+typedef char lv2_atom_assert_double_fits_in_64_bits[
+ ((sizeof(double) <= sizeof(uint64_t)) * 2) - 1];
+
+/**
+ Return a pointer to the contents of a variable-sized atom.
+ @param type The type of the atom, e.g. LV2_Atom_String.
+ @param atom A variable-sized atom.
+*/
+#define LV2_ATOM_CONTENTS(type, atom) \
+ ((void*)((uint8_t*)(atom) + sizeof(type)))
+
+/** Return a pointer to the body of @p atom (just past the LV2_Atom head). */
+#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom)
+
+/** The header of an atom:Atom. */
+typedef struct {
+ uint32_t type; /**< Type of this atom (mapped URI). */
+ uint32_t size; /**< Size in bytes, not including type and size. */
+} LV2_Atom;
+
+/** An atom:Int32 or atom:Bool. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom;
+ int32_t value;
+} LV2_Atom_Int32;
+
+/** An atom:Int64. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom;
+ int64_t value;
+} LV2_Atom_Int64;
+
+/** An atom:Float. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom;
+ float value;
+} LV2_Atom_Float;
+
+/** An atom:Double. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom;
+ double value;
+} LV2_Atom_Double;
+
+/** An atom:Bool. May be cast to LV2_Atom. */
+typedef LV2_Atom_Int32 LV2_Atom_Bool;
+
+/** An atom:URID. May be cast to LV2_Atom. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t id; /**< URID. */
+} LV2_Atom_URID;
+
+/** The complete header of an atom:String. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ /* Contents (a null-terminated UTF-8 string) follow here. */
+} LV2_Atom_String;
+
+/** The header of an atom:Literal body. */
+typedef struct {
+ uint32_t datatype; /**< The ID of the datatype of this literal. */
+ uint32_t lang; /**< The ID of the language of this literal. */
+} LV2_Atom_Literal_Head;
+
+/** The complete header of an atom:Literal. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ LV2_Atom_Literal_Head literal; /**< Literal body header. */
+ /* Contents (a null-terminated UTF-8 string) follow here. */
+} LV2_Atom_Literal;
+
+/** The complete header of an atom:Tuple. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ /* Contents (a series of complete atoms) follow here. */
+} LV2_Atom_Tuple;
+
+/** The complete header of an atom:Vector. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t elem_count; /**< The number of elements in the vector */
+ uint32_t elem_type; /**< The type of each element in the vector */
+ /* Contents (a series of packed atom bodies) follow here. */
+} LV2_Atom_Vector;
+
+/** The header of an atom:Property body (e.g. in an atom:Object). */
+typedef struct {
+ uint32_t key; /**< Key (predicate) (mapped URI). */
+ uint32_t context; /**< Context URID (may be, and generally is, 0). */
+ LV2_Atom value; /**< Value atom header. */
+} LV2_Atom_Property_Body;
+
+/** The complete header of an atom:Property. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t key; /**< Key (predicate) (mapped URI). */
+ uint32_t context; /**< Context URID (may be, and generally is, 0). */
+ LV2_Atom value; /**< Value atom header. */
+ /* Value atom body follows here. */
+} LV2_Atom_Property;
+
+/** The complete header of an atom:Object. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t id; /**< URID for atom:Resource, or blank ID for atom:Blank. */
+ uint32_t type; /**< Type URID (same as rdf:type, for fast dispatch). */
+ /* Contents (a series of property bodies) follow here. */
+} LV2_Atom_Object;
+
+/** The complete header of an atom:Response. */
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t source; /**< ID of message this is a response to (may be 0). */
+ uint32_t type; /**< Specific response type URID (may be 0). */
+ uint32_t seq; /**< Response sequence number, 0 for end. */
+ LV2_Atom body; /**< Body atom header (may be empty). */
+ /* Body optionally follows here. */
+} LV2_Atom_Response;
+
+/** A time stamp in frames. Note this type is NOT an LV2_Atom. */
+typedef struct {
+ uint32_t frames; /**< Time in frames relative to this block. */
+ uint32_t subframes; /**< Fractional time in 1/(2^32)ths of a frame. */
+} LV2_Atom_Audio_Time;
+
+/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */
+typedef struct {
+ /** Time stamp. Which type is valid is determined by context. */
+ union {
+ LV2_Atom_Audio_Time audio; /**< Time in audio frames. */
+ double beats; /**< Time in beats. */
+ } time;
+ LV2_Atom body; /**< Event body atom header. */
+ /* Body atom contents follow here. */
+} LV2_Atom_Event;
+
+/**
+ A sequence of events (time-stamped atoms).
+
+ This is used as the contents of an atom:EventPort, but is a generic Atom
+ type which can be used anywhere.
+
+ The contents of a sequence is a series of LV2_Atom_Event, each aligned
+ to 64-bits, e.g.:
+ <pre>
+ | Event 1 (size 6) | Event 2
+ | | | | | | | | |
+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ |FRAMES |SUBFRMS|TYPE |SIZE |DATADATADATAPAD|FRAMES |SUBFRMS|...
+ </pre>
+*/
+typedef struct {
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t capacity; /**< Maximum size of contents. */
+ uint32_t time_type; /**< URID type of event time stamps. */
+ /* Contents (a series of events) follow here. */
+} LV2_Atom_Sequence;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV2_ATOM_H */
diff --git a/lv2/lv2plug.in/ns/ext/atom/atom.ttl b/lv2/lv2plug.in/ns/ext/atom/atom.ttl
new file mode 100644
index 0000000..7d534d0
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/atom.ttl
@@ -0,0 +1,462 @@
+# LV2 Atom Extension
+# Copyright 2007-2012 David Robillard <d@drobilla.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<http://lv2plug.in/ns/ext/atom>
+ a lv2:Specification ;
+ doap:name "LV2 Atom" ;
+ doap:shortdesc "A generic value container and several data types." ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ rdfs:seeAlso <atom-helpers.h> ,
+ <forge.h> ;
+ doap:release [
+ doap:revision "0.4" ;
+ doap:created "2012-02-07"
+ ] ;
+ doap:maintainer [
+ a foaf:Person ;
+ foaf:name "David Robillard" ;
+ foaf:homepage <http://drobilla.net/> ;
+ rdfs:seeAlso <http://drobilla.net/drobilla.rdf>
+ ] ;
+ lv2:documentation """
+<p>This extension defines a generic container for data, called an <q>Atom</q>,
+and several basic Atom types which can be used to express structured data.
+Atoms allow LV2 plugins and hosts to communicate, process, serialise, and store
+values of any type via a generic mechanism (e.g. ports, files, networks,
+ringbuffers, etc.). Atoms are, with one exception, Plain Old Data (POD) which
+may safely be copied (e.g. with a simple call to <code>memcpy</code>).</p>
+
+<p>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.</p>
+
+<p>Atoms can and should be used anywhere values of various types must be stored
+or transmitted. This extension defines port types, atom:ValuePort and
+atom:MessagePort, which are connected to an Atom. The atom:Sequence type in
+conjunction with atom:MessagePort is intended to replace the <a
+href="http://lv2plug.in/ns/ext/event">LV2 event</a> extension.</p>
+
+<p>The types defined in this extension should be powerful enough to express
+almost any structure. Implementers SHOULD build structures out of the types
+provided here, rather than define new binary formats (e.g. use atom:Object
+rather than a new C <code>struct</code> type). New binary formats are an
+implementation burden which harms interoperabilty, and should only be defined
+where absolutely necessary.</p>
+
+<p>Implementing this extension requires a facility for mapping URIs to
+integers, such as the <a href="http://lv2plug.in/ns/ext/urid">LV2 URID</a>
+extension.</p>
+""" .
+
+atom:cType
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "C type" ;
+ rdfs:domain rdfs:Class ;
+ rdfs:range xsd:string ;
+ rdfs:comment """
+The identifier for a C type describing the in-memory representation of
+an instance of this class.
+""" .
+
+atom:Atom
+ a rdfs:Class ;
+ rdfs:label "Atom" ;
+ atom:cType "LV2_Atom" ;
+ lv2:documentation """
+<p>Abstract base class for all atoms. An LV2_Atom has a 32-bit
+<code>type</code> and <code>size</code> followed by a body of <code>size</code>
+bytes. Atoms MUST be 64-bit aligned.</p>
+
+<p>All concrete Atom types (subclasses of this class) MUST define a precise
+binary layout for their body.</p>
+
+<p>The <code>type</code> field is the URI of an Atom type mapped to an integer.
+Implementations SHOULD gracefully ignore, or pass through, atoms with unknown
+types.</p>
+
+<p>All atoms are POD by definition except references, which as a special case
+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>. Though this extension
+reserves the type 0 for references, the details of reference handling are
+currently unspecified. A future revision of this extension, or a different
+extension, may define how to use non-POD data and references. Implementations
+MUST NOT send references to another implementation unless the receiver is
+explicitly known to support references (e.g. by supporting a feature). The
+atom with both <code>type</code> <em>and</em> <code>size</code> 0 is
+<q>null</q>, which is not considered a Reference.</p>
+""" .
+
+atom:Bang
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Bang" ;
+ rdfs:comment "Generic activity or trigger, with no body." .
+
+atom:Number
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Number" .
+
+atom:Int32
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Number ;
+ rdfs:label "Signed 32-bit integer" ;
+ atom:cType "LV2_Atom_Int32" .
+
+atom:Int64
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Number ;
+ rdfs:label "Signed 64-bit integer" ;
+ atom:cType "LV2_Atom_Int64" .
+
+atom:Float
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Number ;
+ rdfs:label "32-bit IEEE-754 floating point number" ;
+ atom:cType "LV2_Atom_Float" .
+
+atom:Double
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Number ;
+ rdfs:label "64-bit IEEE-754 floating point number" ;
+ atom:cType "LV2_Atom_Double" .
+
+atom:Bool
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Boolean" ;
+ atom:cType "LV2_Atom_Bool" ;
+ rdfs:comment "An Int32 where 0 is false and any other value is true." .
+
+atom:String
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "String" ;
+ atom:cType "LV2_Atom_String" ;
+ lv2:documentation """
+<p>A UTF-8 encoded string.</p>
+
+<p>The body of an LV2_Atom_String is a C string in UTF-8 encoding, i.e. an
+array of bytes (<code>uint8_t</code>) terminated with a NULL byte
+(<code>'\\0'</code>).</p>
+
+<p>This type can be used for free-form strings, but in most cases it is better to
+use atom:Literal since this supports a language tag or datatype. Implementations
+SHOULD NOT use atom:String unless translating the string does not make sense and
+the string has no meaningful datatype.</p>
+""" .
+
+atom:Literal
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "String Literal" ;
+ atom:cType "LV2_Atom_Literal" ;
+ lv2:documentation """
+<p>A UTF-8 encoded string literal, with an optional datatype or language.</p>
+
+<p>This type is compatible with rdf:Literal and is capable of expressing a
+string in any language or a value of any type. A Literal has a
+<code>datatype</code> and <code>lang</code> followed by string data in UTF-8
+encoding. The length of the string data in bytes is <code>size -
+sizeof(LV2_Atom_Literal)</code>, including the terminating NULL character. The
+<code>lang</code> field SHOULD be a URI of the form
+&lt;http://lexvo.org/id/term/LANG&gt; 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.</p>
+
+<p>A Literal may have a <code>datatype</code> OR a <code>lang</code>, but never
+both.</p>
+
+<p>For example, a Literal can be "Hello" in English:</p>
+<pre class="c-code">
+void set_to_hello_in_english(LV2_Atom_Literal* lit) {
+ lit->atom.type = map(expand("atom:Literal"));
+ lit->atom.size = 14;
+ lit->datatype = 0;
+ lit->lang = map("http://lexvo.org/id/term/en");
+ memcpy(LV2_ATOM_CONTENTS(LV2_Atom_Literal, lit),
+ "Hello",
+ sizeof("Hello")); // Assumes enough space
+}
+</pre>
+
+<p>or a Turtle string:</p>
+<pre class="c-code">
+void set_to_turtle_string(LV2_Atom_Literal* lit, const char* ttl) {
+ lit->atom.type = map(expand("atom:Literal"));
+ lit->atom.size = 64;
+ lit->datatype = map("http://www.w3.org/2008/turtle#turtle");
+ lit->lang = 0;
+ memcpy(LV2_ATOM_CONTENTS(LV2_Atom_Literal, lit),
+ ttl,
+ strlen(ttl) + 1); // Assumes enough space
+}
+</pre>
+""" .
+
+atom:URID
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Integer ID mapped from a URI" ;
+ atom:cType "LV2_Atom_ID" ;
+ lv2:documentation """
+<p>An unsigned 32-bit integer mapped from a URI (e.g. with LV2_URID_Map).</p>
+""" .
+
+atom:Vector
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Vector" ;
+ atom:cType "LV2_Atom_Vector" ;
+ lv2:documentation """
+<p>A homogeneous series of atom bodies with equivalent type and size.</p>
+
+<p>An LV2_Atom_Vector is a 32-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 atom:Atom type,
+i.e. the size of each element is the vector's <code>size /
+elem_count</code>.</p>
+
+<p>For example, an atom:Vector containing 42 elements of type atom:Float:</p>
+<pre class="c-code">
+struct VectorOf42Floats {
+ uint32_t type; // map(expand("atom:Vector"))
+ uint32_t size; // sizeof(LV2_Atom_Vector) + (42 * sizeof(float);
+ uint32_t elem_count; // 42
+ uint32_t elem_type; // map(expand("atom:Float"))
+ float elems[32];
+};
+</pre>
+
+<p>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>.</p>
+""" .
+
+atom:Tuple
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Tuple" ;
+ lv2:documentation """
+<p>A series of Atoms with varying <code>type</code> and <code>size</code>.</p>
+
+<p>The body of a Tuple is simply a series of complete atoms, each aligned to
+64 bits.</p>
+""" .
+
+atom:Property
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Property" ;
+ atom:cType "LV2_Atom_Property" ;
+ lv2:documentation """
+<p>A property of an atom:Object. An LV2_Atom_Property has a URID
+<code>key</code> and <code>context</code>, and an Atom <code>value</code>.
+This corresponds to an RDF Property, where the <q>key</q> is the <q>predicate</q>
+and the <q>value</q> is the object.</p>
+
+<p>The <code>context</code> field can be used to specify a different context
+for each property, where this is useful. Otherwise, it may be 0.</p>
+""" .
+
+atom:Object
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Object" ;
+ atom:cType "LV2_Atom_Object" ;
+ lv2:documentation """
+<p>An <q>Object</q> is an atom with a set of properties. This corresponds to
+an RDF Resource, and can be thought of as a dictionary with URID keys.</p>
+
+<p>An LV2_Atom_Object has a uint32_t <code>id</code> and uint32_t
+<code>type</code>, followed by a series of atom:Property bodies (without
+headers, i.e. LV2_Atom_Property_Body). The LV2_Atom_Object::type field is
+semantically equivalent to a property with key rdf:type, but is included in the
+structure to allow for fast dispatch.</p>
+
+<p>This is an abstract Atom type, an Object is always either a atom:Resource
+or a atom:Blank.</p>
+""" .
+
+atom:Resource
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:label "Resource" ;
+ atom:cType "LV2_Atom_Object" ;
+ lv2:documentation """
+<p>An atom:Object where the <code>id</code> field is a URID, i.e. an Object
+with a URI.</p>
+""" .
+
+atom:Blank
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:label "Blank" ;
+ atom:cType "LV2_Atom_Object" ;
+ lv2:documentation """
+<p>An atom:Object where the LV2_Atom_Object::id is a blank node ID (NOT a URI).
+The ID of a Blank is valid only within the context the Blank appears in. For
+ports this is the context of the associated run() call, i.e. all ports share
+the same context so outputs can contain IDs that correspond to IDs of blanks in
+the input.</p> """ .
+
+atom:Event
+ a rdfs:Class ;
+ rdfs:label "Event" ;
+ atom:cType "LV2_Atom_Event" ;
+ lv2:documentation """
+<p>An atom with a time stamp header prepended, typically an element of an
+atom:Sequence. Note this is not an Atom type.</p>
+""" .
+
+atom:Sequence
+ a rdfs:Class ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Sequence" ;
+ atom:cType "LV2_Atom_Sequence" ;
+ lv2:documentation """
+<p>A sequence of atom:Event, i.e. a series of time-stamped Atoms.</p>
+""" .
+
+atom:AtomPort
+ a rdfs:Class ;
+ rdfs:subClassOf lv2:Port ;
+ rdfs:label "Atom Port" ;
+ lv2:documentation """
+<p>A port which contains an lv2:Atom. Ports of this type will be connected to
+a 64-bit aligned LV2_Atom immediately followed by <code>size</code> bytes of
+data.</p>
+
+<p>This is an abstract port type with incomplete semantics which can not be
+used directly as a port type. Atom ports should be either a atom:ValuePort or
+a atom:MessagePort.</p>
+
+<p>Before calling a method on a 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</code> and <code>size</code> 0).</p>
+""" .
+
+atom:ValuePort
+ a rdfs:Class ;
+ rdfs:subClassOf atom:AtomPort ;
+ rdfs:label "Value Port" ;
+ lv2:documentation """
+
+<p>An AtomPort that contains a persistent <em>value</em>. A <q>value</q> is
+time-independent and may be used numerous times. A ValuePort is <q>pure</q> in
+the sense that it may affect output but MUST NOT affect persistent plugin state
+in any externally visible way.</p>
+
+<ul>
+<li>If a plugin has fixed values for all inputs, 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 of that port corresponds to a single set of values for all
+ValuePort outputs.</li>
+
+<li>If the plugin saves state other than port values (e.g. using the <a
+href="http://lv2plug.in/ns/ext/state">LV2 State</a> extension), changing only
+the value of a ValuePort input MUST NOT change that state. In other words,
+value port changes MUST NOT trigger a state change that requires a save.</li>
+</ul>
+
+<p>Value ports are essentially purely functional ports: if a plugin has only
+value ports, that plugin is purely functional. Hosts may elect to cache output
+and avoid calling run() if the output is already known according to these
+rules.</p>
+""" .
+
+atom:MessagePort
+ a rdfs:Class ;
+ rdfs:subClassOf atom:AtomPort ;
+ rdfs:label "Message Port" ;
+ lv2:documentation """
+<p>An AtomPort that contains transient data which is <em>consumed</em> or
+<em>sent</em>. The Atom contained in a MessagePort is time-dependent and only
+valid for a single run invocation. Unlike a ValuePort, a MessagePort may be
+used to manipulate internal plugin state.</p>
+
+<p>Intuitively, a MessagePort contains a <q>message</q> or <q>event</q> which
+is reacted to <em>once</em> (not a <q>value</q> which is computed with any
+number of times).</p>
+""" .
+
+atom:bufferType
+ a rdf:Property ;
+ rdfs:domain atom:AtomPort ;
+ rdfs:label "buffer type" ;
+ lv2:documentation """
+<p>Indicates that an AtomPort may be connected to a certain Atom type. A port
+MAY support several buffer types. The host MUST NOT connect a port to an Atom
+with a type not explicitly listed with this property. The value of this
+property MUST be a sub-class of atom:Atom. For example, an input port that is
+connected directly to an LV2_Atom_Double value is described like so:</p>
+
+<pre class="turtle-code">
+&lt;plugin&gt;
+ lv2:port [
+ a lv2:InputPort , atom:ValuePort ;
+ atom:bufferType atom:Double ;
+ ] .
+</pre>
+
+<p>Note this property only indicates the atom types a port may be directly
+connected to, it is not <q>recursive</q>. If a port can be connected to a
+collection, use atom:supports to indicate which element types are understood.
+If a port supports heterogeneous collections (collections that can contain
+several types of elements at once), implementations MUST gracefully handle any
+types that are present in the collection, even if those types are not
+explicitly supported.</p>
+""" .
+
+atom:supports
+ a rdf:Property ;
+ rdfs:label "supports" ;
+ lv2:documentation """
+<p>Indicates that a particular Atom type is supported.</p>
+
+<p>This property is defined loosely, it may be used to indicate that anything
+<q>supports</q> an Atom type, wherever that may be useful. It applies
+<q>recursively</q> where collections are involved.</p>
+
+<p>In particular, this property can be used to describe which event types are
+supported by a port. For example, a port that receives MIDI events is
+described like so:</p>
+
+<pre class="turtle-code">
+&lt;plugin&gt;
+ lv2:port [
+ a lv2:InputPort , atom:MessagePort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports midi:MidiEvent ;
+ ] .
+</pre>
+""" .
diff --git a/lv2/lv2plug.in/ns/ext/atom/ext.pc.in b/lv2/lv2plug.in/ns/ext/atom/ext.pc.in
new file mode 120000
index 0000000..03dd044
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/ext.pc.in
@@ -0,0 +1 @@
+../../../../../ext.pc.in \ No newline at end of file
diff --git a/lv2/lv2plug.in/ns/ext/atom/forge.h b/lv2/lv2plug.in/ns/ext/atom/forge.h
new file mode 100644
index 0000000..2790778
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/forge.h
@@ -0,0 +1,445 @@
+/*
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file forge.h An API for constructing LV2 atoms.
+
+ This file provides a simple API which can be used to create complex nested
+ atoms by calling the provided functions to append atoms (or atom headers) in
+ the correct order. The size of the parent atom is automatically updated,
+ but the caller must handle this situation if atoms are more deeply nested.
+
+ All output is written to a user-provided buffer. This entire API is
+ realtime safe and suitable for writing to output port buffers in the run
+ method.
+
+ Note these functions are all static inline, do not take their address.
+
+ This header is non-normative, it is provided for convenience.
+*/
+
+#ifndef LV2_ATOM_FORGE_H
+#define LV2_ATOM_FORGE_H
+
+#include <assert.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom-helpers.h"
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+/**
+ A "forge" for creating atoms by appending to a buffer.
+*/
+typedef struct {
+ uint8_t* buf;
+ size_t offset;
+ size_t size;
+
+ LV2_URID Bool;
+ LV2_URID Double;
+ LV2_URID Float;
+ LV2_URID Int32;
+ LV2_URID Int64;
+ LV2_URID Literal;
+ LV2_URID Object;
+ LV2_URID Property;
+ LV2_URID Sequence;
+ LV2_URID String;
+ LV2_URID Tuple;
+ LV2_URID URID;
+ LV2_URID Vector;
+} LV2_Atom_Forge;
+
+/** Set the output buffer where @c forge will write atoms. */
+static inline void
+lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size)
+{
+ forge->buf = buf;
+ forge->size = size;
+ forge->offset = 0;
+}
+
+/**
+ Initialise @c forge.
+
+ URIs will be mapped using @c map and stored, a reference to @c map itself is
+ not held.
+*/
+static inline void
+lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)
+{
+ lv2_atom_forge_set_buffer(forge, NULL, 0);
+ forge->Bool = map->map(map->handle, LV2_ATOM_URI "#Bool");
+ forge->Double = map->map(map->handle, LV2_ATOM_URI "#Double");
+ forge->Float = map->map(map->handle, LV2_ATOM_URI "#Float");
+ forge->Int32 = map->map(map->handle, LV2_ATOM_URI "#Int32");
+ forge->Int64 = map->map(map->handle, LV2_ATOM_URI "#Int64");
+ forge->Literal = map->map(map->handle, LV2_ATOM_URI "#Literal");
+ forge->Object = map->map(map->handle, LV2_ATOM_URI "#Object");
+ forge->Property = map->map(map->handle, LV2_ATOM_URI "#Property");
+ forge->Sequence = map->map(map->handle, LV2_ATOM_URI "#Sequence");
+ forge->String = map->map(map->handle, LV2_ATOM_URI "#String");
+ forge->Tuple = map->map(map->handle, LV2_ATOM_URI "#Tuple");
+ forge->URID = map->map(map->handle, LV2_ATOM_URI "#URID");
+ forge->Vector = map->map(map->handle, LV2_ATOM_URI "#Vector");
+}
+
+/**
+ Reserve @c size bytes in the output buffer (used internally).
+ @return The start of the reserved memory, or NULL if out of space.
+*/
+static inline uint8_t*
+lv2_atom_forge_reserve(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t size)
+{
+ uint8_t* const out = forge->buf + forge->offset;
+ const uint32_t padded_size = lv2_atom_pad_size(size);
+ if (forge->offset + padded_size > forge->size) {
+ return NULL;
+ }
+ if (parent) {
+ parent->size += padded_size;
+ }
+ forge->offset += padded_size;
+ return out;
+}
+
+/**
+ Write the header of an atom:Atom.
+
+ Space for the complete atom will be reserved, but uninitialised.
+*/
+static inline LV2_Atom*
+lv2_atom_forge_atom_head(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t type,
+ uint32_t size)
+{
+ LV2_Atom* const out = (LV2_Atom*)lv2_atom_forge_reserve(
+ forge, parent, size);
+ if (out) {
+ out->type = type;
+ out->size = size - sizeof(LV2_Atom);
+ }
+ return out;
+}
+
+/** Write an atom:Int32. */
+static inline LV2_Atom_Int32*
+lv2_atom_forge_int32(LV2_Atom_Forge* forge, LV2_Atom* parent, int32_t val)
+{
+ LV2_Atom_Int32* out = (LV2_Atom_Int32*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Int32, sizeof(LV2_Atom_Int32));
+ if (out) {
+ out->value = val;
+ }
+ return out;
+}
+
+/** Write an atom:Int64. */
+static inline LV2_Atom_Int64*
+lv2_atom_forge_int64(LV2_Atom_Forge* forge, LV2_Atom* parent, int64_t val)
+{
+ LV2_Atom_Int64* out = (LV2_Atom_Int64*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Int64, sizeof(LV2_Atom_Int64));
+ if (out) {
+ out->value = val;
+ }
+ return out;
+}
+
+/** Write an atom:Float. */
+static inline LV2_Atom_Float*
+lv2_atom_forge_float(LV2_Atom_Forge* forge, LV2_Atom* parent, float val)
+{
+ LV2_Atom_Float* out = (LV2_Atom_Float*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Float, sizeof(LV2_Atom_Float));
+ if (out) {
+ out->value = val;
+ }
+ return out;
+}
+
+/** Write an atom:Double. */
+static inline LV2_Atom_Double*
+lv2_atom_forge_double(LV2_Atom_Forge* forge, LV2_Atom* parent, double val)
+{
+ LV2_Atom_Double* out = (LV2_Atom_Double*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Double, sizeof(LV2_Atom_Double));
+ if (out) {
+ out->value = val;
+ }
+ return out;
+}
+
+/** Write an atom:Bool. */
+static inline LV2_Atom_Bool*
+lv2_atom_forge_bool(LV2_Atom_Forge* forge, LV2_Atom* parent, bool val)
+{
+ LV2_Atom_Bool* out = (LV2_Atom_Bool*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Bool, sizeof(LV2_Atom_Bool));
+ if (out) {
+ out->value = val ? 1 : 0;
+ }
+ return out;
+}
+
+/** Write an atom:URID. */
+static inline LV2_Atom_URID*
+lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_Atom* parent, LV2_URID id)
+{
+ LV2_Atom_URID* out = (LV2_Atom_URID*)lv2_atom_forge_reserve(
+ forge, parent, sizeof(LV2_Atom_URID));
+ if (out) {
+ out->atom.type = forge->URID;
+ out->atom.size = sizeof(uint32_t);
+ out->id = id;
+ }
+ return out;
+}
+
+/** Write an atom:String. */
+static inline LV2_Atom_String*
+lv2_atom_forge_string(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ const uint8_t* str,
+ size_t len)
+{
+ LV2_Atom_String* out = (LV2_Atom_String*)lv2_atom_forge_reserve(
+ forge, parent, sizeof(LV2_Atom_String) + len + 1);
+ if (out) {
+ out->atom.type = forge->String;
+ out->atom.size = len + 1;
+ assert(LV2_ATOM_CONTENTS(LV2_Atom_String, out) == LV2_ATOM_BODY(out));
+ uint8_t* buf = LV2_ATOM_CONTENTS(LV2_Atom_String, out);
+ memcpy(buf, str, len);
+ buf[len] = '\0';
+ }
+ return out;
+}
+
+/** Write an atom:Literal. */
+static inline LV2_Atom_Literal*
+lv2_atom_forge_literal(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ const uint8_t* str,
+ size_t len,
+ uint32_t datatype,
+ uint32_t lang)
+{
+ LV2_Atom_Literal* out = (LV2_Atom_Literal*)lv2_atom_forge_reserve(
+ forge, parent, sizeof(LV2_Atom_Literal) + len + 1);
+ if (out) {
+ out->atom.type = forge->Literal;
+ out->atom.size = sizeof(LV2_Atom_Literal_Head) + len + 1;
+ out->literal.datatype = datatype;
+ out->literal.lang = lang;
+ uint8_t* buf = LV2_ATOM_CONTENTS(LV2_Atom_Literal, out);
+ memcpy(buf, str, len);
+ buf[len] = '\0';
+ }
+ return out;
+}
+
+/** Write an atom:Vector header and reserve space for the body. */
+static inline LV2_Atom_Vector*
+lv2_atom_forge_reserve_vector(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t elem_count,
+ uint32_t elem_type,
+ uint32_t elem_size)
+{
+ const size_t size = sizeof(LV2_Atom_Vector) + (elem_size * elem_count);
+ LV2_Atom_Vector* out = (LV2_Atom_Vector*)lv2_atom_forge_reserve(
+ forge, parent, size);
+ if (out) {
+ out->atom.type = forge->Vector;
+ out->atom.size = size - sizeof(LV2_Atom);
+ out->elem_count = elem_count;
+ out->elem_type = elem_type;
+ }
+ return out;
+}
+
+/** Write an atom:Vector. */
+static inline LV2_Atom_Vector*
+lv2_atom_forge_vector(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t elem_count,
+ uint32_t elem_type,
+ uint32_t elem_size,
+ void* elems)
+{
+ LV2_Atom_Vector* out = lv2_atom_forge_reserve_vector(
+ forge, parent, elem_count, elem_type, elem_size);
+ if (out) {
+ uint8_t* buf = LV2_ATOM_CONTENTS(LV2_Atom_Vector, out);
+ memcpy(buf, elems, elem_size * elem_count);
+ }
+ return out;
+}
+
+/**
+ Write the header of an atom:Tuple.
+
+ To complete the tuple, write a sequence of atoms, always passing the
+ returned tuple as the @c parent parameter (or otherwise ensuring the size is
+ updated correctly).
+
+ For example:
+ @code
+ // Write tuple (1, 2.0)
+ LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, NULL);
+ lv2_atom_forge_int32(forge, tup, 1);
+ lv2_atom_forge_float(forge, tup, 2.0);
+ @endcode
+*/
+static inline LV2_Atom_Tuple*
+lv2_atom_forge_tuple(LV2_Atom_Forge* forge,
+ LV2_Atom* parent)
+{
+ return (LV2_Atom_Tuple*)lv2_atom_forge_atom_head(
+ forge, parent, forge->Tuple, sizeof(LV2_Atom));
+}
+
+/**
+ Write the header of an atom:Object.
+
+ To complete the object, write a sequence of properties, always passing the
+ object as the @c parent parameter (or otherwise ensuring the size is updated
+ correctly).
+
+ For example:
+ @code
+ LV2_URID eg_Cat = map("http://example.org/Cat");
+ LV2_URID eg_name = map("http://example.org/name");
+
+ // Write object header
+ LV2_Atom* obj = (LV2_Atom*)lv2_atom_forge_object(forge, NULL, 0, eg_Cat);
+
+ // Write property: eg:name = "Hobbes"
+ lv2_atom_forge_property_head(forge, obj, eg_name, 0);
+ lv2_atom_forge_string(forge, obj, "Hobbes", strlen("Hobbes"));
+ @endcode
+*/
+static inline LV2_Atom_Object*
+lv2_atom_forge_object(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ LV2_URID id,
+ LV2_URID type)
+{
+ LV2_Atom_Object* out = (LV2_Atom_Object*)lv2_atom_forge_reserve(
+ forge, parent, sizeof(LV2_Atom_Object));
+ if (out) {
+ out->atom.type = forge->Object;
+ out->atom.size = sizeof(LV2_Atom_Object) - sizeof(LV2_Atom);
+ out->id = id;
+ out->type = type;
+ }
+ return out;
+}
+
+/**
+ Write the header for a property body (likely in an Object).
+ See lv2_atom_forge_object() documentation for an example.
+*/
+static inline LV2_Atom_Property_Body*
+lv2_atom_forge_property_head(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ LV2_URID key,
+ LV2_URID context)
+{
+ LV2_Atom_Property_Body* out = (LV2_Atom_Property_Body*)
+ lv2_atom_forge_reserve(forge, parent, 2 * sizeof(uint32_t));
+ if (out) {
+ out->key = key;
+ out->context = context;
+ }
+ return out;
+}
+
+/**
+ Write the header for a Sequence.
+ The size of the returned sequence will be 0, so passing it as the parent
+ parameter to other forge methods will do the right thing.
+*/
+static inline LV2_Atom_Sequence*
+lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t capacity)
+{
+ LV2_Atom_Sequence* out = (LV2_Atom_Sequence*)
+ lv2_atom_forge_reserve(forge, parent, sizeof(LV2_Atom_Sequence));
+ if (out) {
+ out->atom.type = forge->Sequence;
+ out->atom.size = sizeof(LV2_Atom_Sequence) - sizeof(LV2_Atom);
+ out->capacity = capacity;
+ out->pad = 0;
+ }
+ return out;
+}
+
+/**
+ Write the time stamp header of an Event (in a Sequence) in audio frames.
+ After this, call the appropriate forge method(s) to write the body, passing
+ the same @c parent parameter. Note the returned LV2_Event is NOT an Atom.
+*/
+static inline LV2_Atom_Event*
+lv2_atom_forge_audio_time(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ uint32_t frames,
+ uint32_t subframes)
+{
+ LV2_Atom_Event* out = (LV2_Atom_Event*)
+ lv2_atom_forge_reserve(forge, parent, 2 * sizeof(uint32_t));
+ if (out) {
+ out->time.audio.frames = frames;
+ out->time.audio.subframes = subframes;
+ }
+ return out;
+}
+
+/**
+ Write the time stamp header of an Event (in a Sequence) in beats.
+ After this, call the appropriate forge method(s) to write the body, passing
+ the same @c parent parameter. Note the returned LV2_Event is NOT an Atom.
+*/
+static inline LV2_Atom_Event*
+lv2_atom_forge_beat_time(LV2_Atom_Forge* forge,
+ LV2_Atom* parent,
+ double beats)
+{
+ LV2_Atom_Event* out = (LV2_Atom_Event*)
+ lv2_atom_forge_reserve(forge, parent, sizeof(double));
+ if (out) {
+ out->time.beats = beats;
+ }
+ return out;
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV2_ATOM_FORGE_H */
diff --git a/lv2/lv2plug.in/ns/ext/atom/manifest.ttl b/lv2/lv2plug.in/ns/ext/atom/manifest.ttl
new file mode 100644
index 0000000..37f0f58
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/manifest.ttl
@@ -0,0 +1,9 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://lv2plug.in/ns/ext/atom>
+ a lv2:Specification ;
+ lv2:minorVersion 0 ;
+ lv2:microVersion 4 ;
+ rdfs:seeAlso <atom.ttl> .
+
diff --git a/lv2/lv2plug.in/ns/ext/atom/waf b/lv2/lv2plug.in/ns/ext/atom/waf
new file mode 120000
index 0000000..5235032
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/waf
@@ -0,0 +1 @@
+../../../../../waf \ No newline at end of file
diff --git a/lv2/lv2plug.in/ns/ext/atom/wscript b/lv2/lv2plug.in/ns/ext/atom/wscript
new file mode 120000
index 0000000..7e2c01b
--- /dev/null
+++ b/lv2/lv2plug.in/ns/ext/atom/wscript
@@ -0,0 +1 @@
+../../../../../ext.wscript \ No newline at end of file