aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-02-08 01:38:38 +0000
committerDavid Robillard <d@drobilla.net>2012-02-08 01:38:38 +0000
commit046c0a8bbffb572df1c6376d3bf525aac9b3a9fa (patch)
tree1299f3ed06b43031060673fefc32bc2f29587638
parent0ad020578277f8aade6b2df90385b911775ead18 (diff)
downloadlv2-046c0a8bbffb572df1c6376d3bf525aac9b3a9fa.tar.xz
Heavily revise atom extension into a release candidate.
-rw-r--r--Doxyfile5
-rw-r--r--ext.wscript59
-rw-r--r--lv2/ns/ext/atom/atom-buffer.h163
-rw-r--r--lv2/ns/ext/atom/atom-helpers.h306
-rw-r--r--lv2/ns/ext/atom/atom.h279
-rw-r--r--lv2/ns/ext/atom/atom.ttl455
-rw-r--r--lv2/ns/ext/atom/forge.h424
-rw-r--r--lv2/ns/ext/atom/manifest.ttl2
-rw-r--r--lv2/ns/ext/state/state.ttl13
-rw-r--r--plugins/eg-sampler.lv2/sampler.c112
-rw-r--r--plugins/eg-sampler.lv2/sampler.ttl9
-rw-r--r--plugins/eg-sampler.lv2/sampler_ui.c32
-rw-r--r--plugins/eg-sampler.lv2/uris.h10
-rw-r--r--wscript2
14 files changed, 1128 insertions, 743 deletions
diff --git a/Doxyfile b/Doxyfile
index 91582ec..3aa1c07 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -566,7 +566,6 @@ WARN_LOGFILE =
INPUT = \
../doc/mainpage.dox \
- ns/ext/atom/atom-buffer.h \
ns/ext/atom/atom-helpers.h \
ns/ext/atom/atom.h \
ns/ext/atom/forge.h \
@@ -722,13 +721,13 @@ STRIP_CODE_COMMENTS = YES
# then for each documented function all documented
# functions referencing it will be listed.
-REFERENCED_BY_RELATION = YES
+REFERENCED_BY_RELATION = NO
# If the REFERENCES_RELATION tag is set to YES
# then for each documented function all documented entities
# called/used by that function will be listed.
-REFERENCES_RELATION = YES
+REFERENCES_RELATION = NO
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
diff --git a/ext.wscript b/ext.wscript
index b7af3f5..dd46a5f 100644
--- a/ext.wscript
+++ b/ext.wscript
@@ -4,6 +4,7 @@ import os
import shutil
import sys
from waflib.extras import autowaf as autowaf
+from waflib.TaskGen import feature, before
import waflib.Scripting as Scripting
import waflib.Logs as Logs
import waflib.Options as Options
@@ -12,6 +13,19 @@ import waflib.Utils as Utils
info = None
+# A rule for making a link in the build directory to a source file
+def link(task):
+ func = os.symlink
+ if not func:
+ func = shutil.copy # Symlinks unavailable, make a copy
+
+ try:
+ os.remove(task.outputs[0].abspath()) # Remove old target
+ except:
+ pass # No old target, whatever
+
+ func(task.inputs[0].abspath(), task.outputs[0].abspath())
+
try:
# Read version information from lv2extinfo.py (in a release tarball)
import lv2extinfo
@@ -58,7 +72,10 @@ top = '.'
out = 'build'
def options(opt):
+ opt.load('compiler_c')
autowaf.set_options(opt)
+ opt.add_option('--test', action='store_true', default=False, dest='build_tests',
+ help="Build unit tests")
opt.add_option('--copy-headers', action='store_true', default=False,
dest='copy_headers',
help='Copy headers instead of linking to bundle')
@@ -72,7 +89,15 @@ def should_build(ctx):
info.MINOR > 0 and info.MICRO % 2 == 0)
def configure(conf):
+ try:
+ conf.load('compiler_c')
+ except:
+ Options.options.build_tests = False
+
+ conf.env['BUILD_TESTS'] = Options.options.build_tests
+ conf.env['COPY_HEADERS'] = Options.options.copy_headers
conf.env['EXPERIMENTAL'] = Options.options.experimental
+
if not should_build(conf):
return
@@ -80,9 +105,12 @@ def configure(conf):
conf.fatal(
'os.path.relpath missing, get Python 2.6 or use --copy-headers')
+ # Check for gcov library (for test coverage)
+ if conf.env['BUILD_TESTS']:
+ conf.check_cc(lib='gcov', define_name='HAVE_GCOV', mandatory=False)
+
autowaf.configure(conf)
autowaf.display_header('LV2 %s Configuration' % info.NAME)
- conf.env['COPY_HEADERS'] = Options.options.copy_headers
autowaf.display_msg(conf, 'LV2 bundle directory', conf.env['LV2DIR'])
autowaf.display_msg(conf, 'URI', info.URI)
autowaf.display_msg(conf, 'Version', VERSION)
@@ -108,6 +136,29 @@ def build(bld):
NAME = info.NAME,
VERSION = VERSION,
DESCRIPTION = info.SHORTDESC)
+
+ if bld.env['BUILD_TESTS'] and bld.path.find_node('%s-test.c' % info.NAME):
+ test_lib = []
+ test_cflags = ['']
+ if bld.is_defined('HAVE_GCOV'):
+ test_lib += ['gcov']
+ test_cflags += ['-fprofile-arcs', '-ftest-coverage']
+
+ # Copy headers to URI-style include paths in build directory
+ for i in bld.path.ant_glob('*.h'):
+ obj = bld(rule = link,
+ name = 'link',
+ cwd = 'build/lv2/%s/%s' % (include_base, info.NAME),
+ source = '%s' % i,
+ target = 'lv2/%s/%s/%s' % (include_base, info.NAME, i))
+
+ # Unit test program
+ obj = bld(features = 'c cprogram',
+ source = '%s-test.c' % info.NAME,
+ lib = test_lib,
+ target = '%s-test' % info.NAME,
+ install_path = '',
+ cflags = test_cflags)
# Install bundle
bld.install_files(bundle_dir,
@@ -121,6 +172,12 @@ def build(bld):
bld.symlink_as(os.path.join(include_dir, info.NAME),
os.path.relpath(bundle_dir, include_dir))
+def test(ctx):
+ autowaf.pre_test(ctx, APPNAME, dirs=['.'])
+ os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH')
+ autowaf.run_tests(ctx, APPNAME, ['%s-test' % info.NAME], dirs=['.'])
+ autowaf.post_test(ctx, APPNAME, dirs=['.'])
+
def write_news():
import rdflib
import textwrap
diff --git a/lv2/ns/ext/atom/atom-buffer.h b/lv2/ns/ext/atom/atom-buffer.h
deleted file mode 100644
index f4b90dd..0000000
--- a/lv2/ns/ext/atom/atom-buffer.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- Copyright 2008-2011 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-event-buffer.h Helper functions for atom:EventBuffer.
-
- Note that these functions are all static inline which basically means:
- do not take the address of these functions.
-*/
-
-#ifndef LV2_ATOM_EVENT_BUFFER_H
-#define LV2_ATOM_EVENT_BUFFER_H
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
-
-/**
- Initialize an existing atom buffer.
- All fields of @c buf are reset, except capacity which is unmodified.
-*/
-static inline void
-lv2_atom_buffer_reset(LV2_Atom_Buffer* buf)
-{
- buf->event_count = 0;
- buf->size = 0;
-}
-
-/**
- Allocate a new, empty atom buffer.
-*/
-static inline LV2_Atom_Buffer*
-lv2_atom_buffer_new(uint32_t capacity)
-{
- const uint32_t size = sizeof(LV2_Atom_Buffer) + capacity;
- LV2_Atom_Buffer* buf = (LV2_Atom_Buffer*)malloc(size);
- if (buf) {
- buf->data = (uint8_t*)(buf + 1);
- buf->capacity = capacity;
- lv2_atom_buffer_reset(buf);
- }
- return buf;
-}
-
-/**
- Free an atom buffer allocated with lv2_atom_buffer_new().
-*/
-static inline void
-lv2_atom_buffer_free(LV2_Atom_Buffer* buf)
-{
- free(buf);
-}
-
-/**
- An iterator over an LV2_Atom_Buffer.
-*/
-typedef struct {
- LV2_Atom_Buffer* buf;
- uint32_t offset;
-} LV2_Atom_Buffer_Iterator;
-
-/**
- Return an iterator to the beginning of @c buf.
-*/
-static inline LV2_Atom_Buffer_Iterator
-lv2_atom_buffer_begin(LV2_Atom_Buffer* buf)
-{
- const LV2_Atom_Buffer_Iterator i = { buf, 0 };
- return i;
-}
-
-/**
- Return true iff @c i points to a valid atom.
-*/
-static inline bool
-lv2_atom_buffer_is_valid(LV2_Atom_Buffer_Iterator i)
-{
- return i.offset < i.buf->size;
-}
-
-/**
- Return the iterator to the next element after @c i.
- @param i A valid iterator to an atom in a buffer.
-*/
-static inline LV2_Atom_Buffer_Iterator
-lv2_atom_buffer_next(LV2_Atom_Buffer_Iterator i)
-{
- if (!lv2_atom_buffer_is_valid(i)) {
- return i;
- }
- const LV2_Atom_Event* const ev = (LV2_Atom_Event*)(i.buf->data + i.offset);
- i.offset += lv2_atom_pad_size(sizeof(LV2_Atom_Event) + ev->body.size);
- return i;
-}
-
-/**
- Return a pointer to the atom currently pointed to by @c i.
-*/
-static inline LV2_Atom_Event*
-lv2_atom_buffer_get(LV2_Atom_Buffer_Iterator i)
-{
- if (!lv2_atom_buffer_is_valid(i)) {
- return NULL;
- }
- return (LV2_Atom_Event*)(i.buf->data + i.offset);
-}
-
-/**
- Write an atom to a buffer.
-
- The atom will be written at the location pointed to by @c i, which will be
- incremented to point to the location where the next atom should be written
- (which is likely now garbage). Thus, this function can be called repeatedly
- with a single @c i to write a sequence of atoms to the buffer.
-
- @return True if atom was written, otherwise false (buffer is full).
-*/
-static inline bool
-lv2_atom_buffer_write(LV2_Atom_Buffer_Iterator* i,
- uint32_t frames,
- uint32_t subframes,
- uint32_t type,
- uint32_t size,
- const uint8_t* data)
-{
- const uint32_t free_space = i->buf->capacity - i->buf->size;
- if (free_space < sizeof(LV2_Atom_Event) + size) {
- return false;
- }
-
- LV2_Atom_Event* const ev = (LV2_Atom_Event*)(i->buf->data + i->offset);
-
- ev->frames = frames;
- ev->subframes = subframes;
- ev->body.type = type;
- ev->body.size = size;
- memcpy((uint8_t*)ev + sizeof(LV2_Atom_Event), data, size);
- ++i->buf->event_count;
-
- size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + size);
- i->buf->size += size;
- i->offset += size;
-
- return true;
-}
-
-#endif /* LV2_ATOM_EVENT_BUFFER_H */
diff --git a/lv2/ns/ext/atom/atom-helpers.h b/lv2/ns/ext/atom/atom-helpers.h
index 4e51c89..6fff74b 100644
--- a/lv2/ns/ext/atom/atom-helpers.h
+++ b/lv2/ns/ext/atom/atom-helpers.h
@@ -1,5 +1,5 @@
/*
- Copyright 2008-2011 David Robillard <http://drobilla.net>
+ 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
@@ -17,153 +17,285 @@
/**
@file atom-helpers.h Helper functions for the LV2 Atom extension.
- These functions are provided for convenience only, use of them is not
- required for supporting atoms.
+ Note these functions are all static inline, do not take their address.
- Note that these functions are all static inline which basically means:
- do not take the address of these functions.
+ This header is non-normative, it is provided for convenience.
*/
#ifndef LV2_ATOM_HELPERS_H
#define LV2_ATOM_HELPERS_H
-#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
-typedef LV2_Atom_Property* LV2_Thing_Iter;
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
-/** Get an iterator pointing to @c prop in some LV2_Thing */
-static inline LV2_Thing_Iter
-lv2_thing_begin(const LV2_Thing* obj)
+/** Pad a size to 64 bits. */
+static inline uint32_t
+lv2_atom_pad_size(uint32_t size)
{
- return (LV2_Thing_Iter)(obj->properties);
+ return (size + 7) & (~7);
}
-/** Return true iff @c iter has reached the end of @c thing */
+/** Return true iff @p atom is null. */
static inline bool
-lv2_thing_iter_is_end(const LV2_Thing* obj, LV2_Thing_Iter iter)
+lv2_atom_is_null(const LV2_Atom* atom)
{
- return (uint8_t*)iter >= ((uint8_t*)obj + sizeof(LV2_Atom) + obj->size);
+ return !atom || (atom->type == 0 && atom->size == 0);
}
-/** Return true iff @c l points to the same property as @c r */
+/** Return true iff @p a is equal to @p b. */
static inline bool
-lv2_thing_iter_equals(const LV2_Thing_Iter l, const LV2_Thing_Iter r)
+lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b)
{
- return l == r;
+ 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 an iterator to the property following @c iter */
-static inline LV2_Thing_Iter
-lv2_thing_iter_next(const LV2_Thing_Iter iter)
+/** 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 (LV2_Thing_Iter)((uint8_t*)iter
- + sizeof(LV2_Atom_Property)
- + lv2_atom_pad_size(iter->value.size));
+ return (uint8_t*)i >= ((uint8_t*)seq + sizeof(LV2_Atom) + seq->atom.size);
}
-/** Return the property pointed to by @c iter */
-static inline LV2_Atom_Property*
-lv2_thing_iter_get(LV2_Thing_Iter iter)
+/** 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_Property*)iter;
+ 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 properties of an Thing.
- @param thing The thing to iterate over
+ 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.:
- <pre>
- LV2_THING_FOREACH(thing, i) {
- LV2_Atom_Property* prop = lv2_thing_iter_get(i);
- // Do things with prop here...
+ @code
+ LV2_SEQUENCE_FOREACH(sequence, i) {
+ LV2_Atom_Event* ev = lv2_sequence_iter_get(i);
+ // Do something with ev here...
}
- </pre>
+ @endcode
*/
-#define LV2_THING_FOREACH(thing, iter) \
- for (LV2_Thing_Iter (iter) = lv2_thing_begin(thing); \
- !lv2_thing_iter_is_end(thing, (iter)); \
- (iter) = lv2_thing_iter_next(iter))
+#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))
/**
- Append a Property body to an Atom that contains properties (e.g. atom:Thing).
- @param thing Pointer to the atom that contains the property to add. thing.size
- must be valid, but thing.type is ignored.
- @param key The key of the new property
- @param value_type The type of the new value
- @param value_size The size of the new value
- @param value_body Pointer to the new value's data
- @return a pointer to the new LV2_Atom_Property in @c body.
-
- This function will write the property body (not including an LV2_Thing
- header) at lv2_atom_pad_size(body + size). Thus, it can be used with any
- Atom type that contains headerless 32-bit aligned properties.
+ @}
+ @name Tuple Iterator
+ @{
*/
-static inline LV2_Atom_Property*
-lv2_thing_append(LV2_Thing* thing,
- uint32_t key,
- uint32_t value_type,
- uint32_t value_size,
- const void* value_body)
-{
- thing->size = lv2_atom_pad_size(thing->size);
- LV2_Atom_Property* prop = (LV2_Atom_Property*)(
- (uint8_t*)thing + sizeof(LV2_Atom) + thing->size);
- prop->key = key;
- prop->value.type = value_type;
- prop->value.size = value_size;
- memcpy(prop->value.body, value_body, value_size);
- thing->size += sizeof(LV2_Atom_Property) + value_size;
- return prop;
-}
-
-/** Return true iff @c atom is NULL */
+
+/** 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_atom_is_null(LV2_Atom* atom)
+lv2_tuple_is_end(const LV2_Atom_Tuple* tup, LV2_Atom_Tuple_Iter i)
{
- return !atom || (atom->type == 0 && atom->size == 0);
+ return (uint8_t*)i >= ((uint8_t*)tup + sizeof(LV2_Atom) + tup->atom.size);
}
-/** A single entry in an Thing query. */
+/** 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_Thing_Query;
+} LV2_Atom_Object_Query;
-static const LV2_Thing_Query LV2_THING_QUERY_END = { 0, NULL };
+static const LV2_Atom_Object_Query LV2_OBJECT_QUERY_END = { 0, NULL };
/**
- "Query" an thing, getting a pointer to the values for various keys.
+ Get an object's values for various keys.
- The value pointer of each item in @c query will be set to the location of
- the corresponding value in @c thing. Every value pointer in @c query MUST
- be initialised to NULL. This function reads @c thing in a single linear
- sweep. By allocating @c query on the stack, things can be "queried"
+ 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_thing_query(const LV2_Thing* thing, LV2_Thing_Query* query)
+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_Thing_Query* q = query; q->key; ++q)
+ for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
++n_queries;
+ }
- LV2_THING_FOREACH(thing, o) {
- const LV2_Atom_Property* prop = lv2_thing_iter_get(o);
- for (LV2_Thing_Query* q = query; q->key; ++q) {
+ 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)
+ if (++matches == n_queries) {
return matches;
+ }
break;
}
}
@@ -171,4 +303,8 @@ lv2_thing_query(const LV2_Thing* thing, LV2_Thing_Query* query)
return matches;
}
+/**
+ @}
+*/
+
#endif /* LV2_ATOM_HELPERS_H */
diff --git a/lv2/ns/ext/atom/atom.h b/lv2/ns/ext/atom/atom.h
index 569ab49..7a29dae 100644
--- a/lv2/ns/ext/atom/atom.h
+++ b/lv2/ns/ext/atom/atom.h
@@ -1,5 +1,5 @@
/*
- Copyright 2008-2011 David Robillard <http://drobilla.net>
+ 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
@@ -36,148 +36,152 @@
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];
+
/**
- An LV2 Atom.
+ 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)))
- An "Atom" is a generic chunk of memory with a given type and size.
- The type field defines how to interpret an atom.
+/** 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)
- All atoms are by definition Plain Old Data (POD) and may be safely copied
- (e.g. with memcpy) using the size field, except atoms with type 0. An atom
- with type 0 is a reference, and may only be used via the functions provided
- in LV2_Blob_Support (e.g. it MUST NOT be manually copied).
-*/
+/** 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. */
- uint8_t body[]; /**< Body of length @ref size bytes. */
+ uint32_t type; /**< Type of this atom (mapped URI). */
+ uint32_t size; /**< Size in bytes, not including type and size. */
} LV2_Atom;
-/**
- An atom:String.
- May be cast to 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 {
- uint32_t type; /**< Type of this atom (mapped URI). */
- uint32_t size; /**< Size in bytes, not including type and size. */
- uint8_t str[]; /**< Null-terminated string data in UTF-8 encoding. */
+ 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;
-/**
- An atom:Literal.
- May be cast to LV2_Atom.
-*/
+/** The header of an atom:Literal body. */
typedef struct {
- uint32_t type; /**< Type of this atom (mapped URI). */
- uint32_t size; /**< Size in bytes, not including type and size. */
uint32_t datatype; /**< The ID of the datatype of this literal. */
uint32_t lang; /**< The ID of the language of this literal. */
- uint8_t str[]; /**< Null-terminated string data in UTF-8 encoding. */
+} 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;
-/**
- An atom:URID or atom:BlankID.
- May be cast to LV2_Atom.
-*/
+/** The complete header of an atom:Tuple. */
typedef struct {
- uint32_t type; /**< Type of this atom (mapped URI). */
- uint32_t size; /**< Size in bytes, not including type and size. */
- uint32_t id; /**< URID (integer mapped URI) or blank node ID. */
-} LV2_Atom_URID;
+ LV2_Atom atom; /**< Atom header. */
+ /* Contents (a series of complete atoms) follow here. */
+} LV2_Atom_Tuple;
-/**
- An atom:Vector.
- May be cast to LV2_Atom.
-*/
+/** The complete header of an atom:Vector. */
typedef struct {
- uint32_t type; /**< Type of this atom (mapped URI). */
- uint32_t size; /**< Size in bytes, not including type and size. */
+ 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 */
- uint8_t elems[]; /**< Sequence of element bodies */
+ /* Contents (a series of packed atom bodies) follow here. */
} LV2_Atom_Vector;
-/**
- The body of an atom:Property.
- Note this type is not an LV2_Atom.
-*/
-typedef struct _LV2_Atom_Property {
- uint32_t key; /**< Key (predicate) (mapped URI). */
- LV2_Atom value; /**< Value (object) */
-} LV2_Atom_Property;
-
-/**
- An atom:Thing (Resource, Blank, or Message).
- May be cast to LV2_Atom.
-*/
+/** The header of an atom:Property body (e.g. in an atom:Object). */
typedef struct {
- uint32_t type; /**< Type of this atom (mapped URI). */
- uint32_t size; /**< Size in bytes, not including type and size. */
- uint32_t context; /**< ID of context graph, or 0 for default */
- uint32_t id; /**< URID (for Resource) or blank ID (for Blank) */
- uint8_t properties[]; /**< Sequence of LV2_Atom_Property */
-} LV2_Thing;
+ 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;
-/**
- An atom:Event, a timestamped Atom.
- Note this type is not an LV2_Atom, but contains an Atom as payload.
-*/
+/** The complete header of an atom:Property. */
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 body; /**< Event body. */
-} LV2_Atom_Event;
+ 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;
-/**
- An atom:Int32, a signed 32-bit integer.
- May be cast to LV2_Atom.
-*/
+/** The complete header of an atom:Object. */
typedef struct {
- uint32_t type;
- uint32_t size;
- int32_t value;
-} LV2_Atom_Int32;
+ 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;
-/**
- An atom:Int64, a signed 64-bit integer.
- May be cast to LV2_Atom.
-*/
+/** The complete header of an atom:Response. */
typedef struct {
- uint32_t type;
- uint32_t size;
- int64_t value;
-} LV2_Atom_Int64;
-
-/**
- An atom:Float, a 32-bit IEEE-754 floating point number.
- May be cast to LV2_Atom.
-*/
+ 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 type;
- uint32_t size;
- float value;
-} LV2_Atom_Float;
+ 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;
-/**
- An atom:Double, a 64-bit IEEE-754 floating point number.
- May be cast to LV2_Atom.
-*/
+/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */
typedef struct {
- uint32_t type;
- uint32_t size;
- double value;
-} LV2_Atom_Double;
+ /** 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 buffer of events (the contents of an atom:EventPort).
+ A sequence of events (time-stamped atoms).
- The host MAY elect to allocate buffers as a single chunk of POD by using
- this struct as a header much like LV2_Atom, or it may choose to point to
- a fragment of a buffer elsewhere. In either case, @ref data points to the
- start of the data contained in this buffer.
+ This is used as the contents of an atom:EventPort, but is a generic Atom
+ type which can be used anywhere.
- The buffer at @ref data contains a sequence of LV2_Atom_Event padded such
- that the start of each event is aligned to 64 bits, e.g.:
+ 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
| | | | | | | | |
@@ -186,60 +190,11 @@ typedef struct {
</pre>
*/
typedef struct {
-
- /**
- The contents of the event buffer. This may or may not reside in the
- same block of memory as this header, plugins must not assume either.
- The host guarantees this points to at least capacity bytes of allocated
- memory (though only size bytes of that are valid events).
- */
- uint8_t* data;
-
- /**
- The number of events in this buffer.
-
- INPUTS: The host must set this field to the number of events contained
- in the data buffer before calling run(). The plugin must not change
- this field.
-
- OUTPUTS: The plugin must set this field to the number of events it has
- written to the buffer before returning from run(). Any initial value
- should be ignored by the plugin.
- */
- uint32_t event_count;
-
- /**
- The capacity of the data buffer in bytes.
- This is set by the host and must not be changed by the plugin.
- The host is allowed to change this between run() calls.
- */
- uint32_t capacity;
-
- /**
- The size of the initial portion of the data buffer containing data.
-
- INPUTS: The host must set this field to the number of bytes used
- by all events it has written to the buffer (including headers)
- before calling the plugin's run().
- The plugin must not change this field.
-
- OUTPUTS: The plugin must set this field to the number of bytes
- used by all events it has written to the buffer (including headers)
- before returning from run().
- Any initial value should be ignored by the plugin.
- */
- uint32_t size;
-
-} LV2_Atom_Buffer;
-
-/**
- Pad a size to 64 bits.
-*/
-static inline uint32_t
-lv2_atom_pad_size(uint32_t size)
-{
- return (size + 7) & (~7);
-}
+ LV2_Atom atom; /**< Atom header. */
+ uint32_t capacity; /**< Maximum size of contents. */
+ uint32_t pad;
+ /* Contents (a series of events) follow here. */
+} LV2_Atom_Sequence;
#ifdef __cplusplus
} /* extern "C" */
diff --git a/lv2/ns/ext/atom/atom.ttl b/lv2/ns/ext/atom/atom.ttl
index c28b610..7d534d0 100644
--- a/lv2/ns/ext/atom/atom.ttl
+++ b/lv2/ns/ext/atom/atom.ttl
@@ -27,10 +27,11 @@
doap:name "LV2 Atom" ;
doap:shortdesc "A generic value container and several data types." ;
doap:license <http://opensource.org/licenses/isc> ;
- rdfs:seeAlso <atom-buffer.h> ;
+ rdfs:seeAlso <atom-helpers.h> ,
+ <forge.h> ;
doap:release [
- doap:revision "0.3" ;
- doap:created "2012-01-28"
+ doap:revision "0.4" ;
+ doap:created "2012-02-07"
] ;
doap:maintainer [
a foaf:Person ;
@@ -39,68 +40,118 @@
rdfs:seeAlso <http://drobilla.net/drobilla.rdf>
] ;
lv2:documentation """
-<p>This extension defines a generic format for a typed piece of data, called an
-lv2:Atom (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>).</p>
+<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>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.</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>Atoms (the start of the LV2_Atom header) MUST be 32-bit aligned.</p>
-
-<p>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: atom:ValuePort and atom:MessagePort, which both have the same
-buffer format but different semantics (with respect to how the run() callback
-interprets the Atom).</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 <code>body</code> of
-<code>size</code> bytes.</p>
+<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 <code>body</code>.</p>
+binary layout for their body.</p>
-<p>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 LV2_URI_Map_Feature::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
-(or copied if it does not have type 0).</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, actual specification of how references are used is left
-to another extension.</p>
+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 ;
@@ -128,7 +179,7 @@ atom:Literal
<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
+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
@@ -143,22 +194,26 @@ 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->type = map(expand("atom:Literal"));
- lit->size = 14;
- lit->datatype = 0;
- lit->lang = map("http://lexvo.org/id/term/en");
- memcpy(lit->str, "Hello", sizeof("Hello")); // Assumes enough space
+ 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->type = map(expand("atom:Literal"));
- lit->size = 64;
- lit->datatype = map("http://www.w3.org/2008/turtle#turtle");
- lit->lang = 0;
- memcpy(lit->str, ttl, strlen(ttl) + 1); // Assumes enough space
+ 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>
""" .
@@ -169,21 +224,7 @@ atom:URID
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 using the <a
-href="http://lv2plug.in/ns/ext/uri-map">URI Map</a> extension's
-LV2_URI_Map_Feature::uri_to_id() with <code>map = NULL</code>.</p>
-""" .
-
-atom:BlankID
- a rdfs:Class ;
- rdfs:subClassOf atom:Atom ;
- rdfs:label "Integer ID for a blank node" ;
- atom:cType "LV2_Atom_ID" ;
- lv2:documentation """
-<p>An unsigned 32-bit integer identifier for a blank node. A BlankID is only
-meaningful within a limited scope (e.g. the Atom in which it appears), and
-MUST NOT be used as a global identifier. In particular, a BlankID is NOT a
-URID, and can not be mapped to/from a URI.</p>
+<p>An unsigned 32-bit integer mapped from a URI (e.g. with LV2_URID_Map).</p>
""" .
atom:Vector
@@ -192,7 +233,7 @@ atom:Vector
rdfs:label "Vector" ;
atom:cType "LV2_Atom_Vector" ;
lv2:documentation """
-<p>A homogeneous sequence of atoms with equivalent type and size.</p>
+<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
@@ -221,125 +262,84 @@ atom:Tuple
rdfs:subClassOf atom:Atom ;
rdfs:label "Tuple" ;
lv2:documentation """
-<p>A sequence of lv2:Atom with varying <code>type</code>
-and <code>size</code>.</p>
+<p>A series of Atoms with varying <code>type</code> and <code>size</code>.</p>
-<p>The body of a Tuple is simply a sequence of complete atoms, each aligned to
-32 bits.</p>
+<p>The body of a Tuple is simply a series of complete atoms, each aligned to
+64 bits.</p>
""" .
-atom:Thing
+atom:Property
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
- rdfs:label "Thing" ;
- atom:cType "LV2_Thing" ;
+ rdfs:label "Property" ;
+ atom:cType "LV2_Atom_Property" ;
lv2:documentation """
-<p>Abstract base class for a "Thing", i.e. an atom:Atom with a number of
-properties. An LV2_Object is an unsigned 32-bit integer <code>context</code>
-and <code>id</code> followed by a sequence of LV2_Atom_Property .</p>
-
-<p>The <code>context</code> is mapped using the <a
-href="http://lv2plug.in/ns/ext/uri-map">URI Map</a> extension's
-LV2_URI_Map_Feature::uri_to_id() with <code>map = NULL</code>, and may be 0
-(the default context).</p>
-
-<p>Note this is an abstract class, i.e. no Atom can exist with <code>type =
-uri_to_id(atom:Thing)</code>. An Object is either an atom:Resource or an
-atom:Blank, but the <code>body</code> always has the same binary format,
-LV2_Object. Thus, both named and anonymous objects can be handled with common
-code using only a 64-bit header for both.</p>
+<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:Resource
+atom:Object
a rdfs:Class ;
- rdfs:subClassOf atom:Thing ;
- atom:cType "LV2_Thing" ;
+ rdfs:subClassOf atom:Atom ;
+ rdfs:label "Object" ;
+ atom:cType "LV2_Atom_Object" ;
lv2:documentation """
-<p>An atom:Thing where <code>id</code> is the URI of the resource mapped to an
-atom:URID.</p>
+<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:Blank
+atom:Resource
a rdfs:Class ;
- rdfs:subClassOf atom:Thing ;
- atom:cType "LV2_Thing" ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:label "Resource" ;
+ atom:cType "LV2_Atom_Object" ;
lv2:documentation """
-<p>An atom:Thing 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 a URID.</p>
+<p>An atom:Object where the <code>id</code> field is a URID, i.e. an Object
+with a URI.</p>
""" .
-atom:Message
+atom:Blank
a rdfs:Class ;
- rdfs:subClassOf atom:Thing ;
- atom:cType "LV2_Thing" ;
+ rdfs:subClassOf atom:Object ;
+ rdfs:label "Blank" ;
+ atom:cType "LV2_Atom_Object" ;
lv2:documentation """
-<p>A atom:Thing where <code>id</code> is a message type ID. Conceptually, a
-Message is identical to a Blank, but is a distinct type with a single type
-field to allow simple and fast dispatch by handling code.</p>
-
-<p>A Message may be serialised as a Blank by adding an rdf:type property with
-the value <code>id</code> unmapped to a URI.</p>
-""" .
+<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 for sample accurate
-transmission via LV2 ports. See struct LV2_Atom_Event.</p>
+<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:Bang
- a rdfs:Class ;
- rdfs:subClassOf atom:Atom ;
- rdfs:label "Bang (activity) (size = 0)" .
-
-atom:Number
+atom:Sequence
a rdfs:Class ;
rdfs:subClassOf atom:Atom ;
- rdfs:label "Number (abstract class)." .
-
-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:Bool
- a rdfs:Class ;
- rdfs:subClassOf atom:Atom ;
- rdfs:label "An atom:Int32 where 0 is false and all other values true" ;
- atom:cType "LV2_Atom_Int32" .
-
-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:blobSupport
- a lv2:Feature ;
- rdfs:label "Blob support" ;
+ rdfs:label "Sequence" ;
+ atom:cType "LV2_Atom_Sequence" ;
lv2:documentation """
-<p>Support for dynamically allocated blobs. If a host supports this feature,
-it MUST pass a LV2_Feature with <code>URI</code>
-http://lv2plug.in/ns/ext/atom#blobSupport and <code>data</code> pointing to a
-LV2_Blob_Support.</p>
+<p>A sequence of atom:Event, i.e. a series of time-stamped Atoms.</p>
""" .
atom:AtomPort
@@ -347,21 +347,20 @@ atom:AtomPort
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 32-bit aligned LV2_Atom immediately followed by
-<code>size</code> bytes of data.</p>
-
-<p>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 atom:ValuePort or atom:MessagePort).
-</p>
-
-<p>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>).</p>
+<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
@@ -369,67 +368,95 @@ atom:ValuePort
rdfs:subClassOf atom:AtomPort ;
rdfs:label "Value Port" ;
lv2:documentation """
-<p>An AtomPort that interprets its data as a persistent and time-independent
-"value".</p>
+
+<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 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>
+<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 can be thought of as purely functional ports: if a plugin
-callback has only value ports, then the plugin callback is a pure function.</p>
+
+<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" ;
- rdfs:comment """
-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.
-
-Intuitively, a MessagePort contains a "message" or "command" or "event"
-which is reacted to, NOT a "value" or "signal" (which is computed with).
-""" .
-
-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.
+ 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:EventPort
- a rdfs:Class ;
- rdfs:label "Event port" ;
- rdfs:subClassOf lv2:Port ;
+atom:bufferType
+ a rdf:Property ;
+ rdfs:domain atom:AtomPort ;
+ rdfs:label "buffer type" ;
lv2:documentation """
-<p>A port used for communicating time-stamped atoms in the audio context.
-Ports of this type are connected to an LV2_Atom_Buffer, which contains a flat
-time-stamped sequence of atom:Event.</p>
+<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>This port type is intended as a simpler and atom compatible successor to <a
-href="http://lv2plug.in/ns/ext/event#EventPort">ev:EventPort</a>.</p>
+<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:domain lv2:Port ;
- rdfs:range atom:Atom ;
rdfs:label "supports" ;
lv2:documentation """
-<p>Indicates that a Port supports a certain atom:Atom 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.</p>
+<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/ns/ext/atom/forge.h b/lv2/ns/ext/atom/forge.h
index e8f5d40..2790778 100644
--- a/lv2/ns/ext/atom/forge.h
+++ b/lv2/ns/ext/atom/forge.h
@@ -1,5 +1,5 @@
/*
- Copyright 2008-2011 David Robillard <http://drobilla.net>
+ 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
@@ -15,57 +15,427 @@
*/
/**
- @file forge.h Helper constructor functions for LV2 atoms.
+ @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 {
- uint32_t ID;
- uint32_t Message;
- uint32_t Property;
+ 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;
-static inline LV2_Atom_Forge*
-lv2_atom_forge_new(LV2_URID_Map* map)
+/** 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)
{
- LV2_Atom_Forge* forge = (LV2_Atom_Forge*)malloc(sizeof(LV2_Atom_Forge));
- forge->ID = map->map(map->handle, LV2_ATOM_URI "#ID");
- forge->Message = map->map(map->handle, LV2_ATOM_URI "#Message");
- forge->Property = map->map(map->handle, LV2_ATOM_URI "#Property");
- return forge;
+ 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_free(LV2_Atom_Forge* forge)
+lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)
{
- free(forge);
+ 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");
}
-static inline LV2_Atom_ID
-lv2_atom_forge_make_id(LV2_Atom_Forge* forge, uint32_t id)
+/**
+ 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)
{
- const LV2_Atom_ID atom = { forge->ID, sizeof(uint32_t), id };
- return atom;
+ 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;
}
-static inline void
-lv2_atom_forge_set_message(LV2_Atom_Forge* forge,
- LV2_Thing* msg,
- uint32_t id)
-{
- msg->type = forge->Message;
- msg->size = sizeof(LV2_Thing) - sizeof(LV2_Atom);
- msg->context = 0;
- msg->id = id;
+/**
+ 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
diff --git a/lv2/ns/ext/atom/manifest.ttl b/lv2/ns/ext/atom/manifest.ttl
index 20c974d..37f0f58 100644
--- a/lv2/ns/ext/atom/manifest.ttl
+++ b/lv2/ns/ext/atom/manifest.ttl
@@ -4,6 +4,6 @@
<http://lv2plug.in/ns/ext/atom>
a lv2:Specification ;
lv2:minorVersion 0 ;
- lv2:microVersion 3 ;
+ lv2:microVersion 4 ;
rdfs:seeAlso <atom.ttl> .
diff --git a/lv2/ns/ext/state/state.ttl b/lv2/ns/ext/state/state.ttl
index 84cda42..6c0656a 100644
--- a/lv2/ns/ext/state/state.ttl
+++ b/lv2/ns/ext/state/state.ttl
@@ -14,12 +14,20 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-@prefix state: <http://lv2plug.in/ns/ext/state#> .
+@prefix dcs: <http://ontologi.es/doap-changeset#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix state: <http://lv2plug.in/ns/ext/state#> .
+
+<http://drobilla.net/drobilla#me>
+ a foaf:Person ;
+ foaf:name "David Robillard" ;
+ foaf:homepage <http://drobilla.net/> ;
+ foaf:mbox <mailto:d@drobilla.net> ;
+ rdfs:seeAlso <http://drobilla.net/drobilla> .
<http://lv2plug.in/ns/ext/state>
a lv2:Specification ;
@@ -28,7 +36,8 @@
doap:license <http://opensource.org/licenses/isc> ;
doap:release [
doap:revision "0.5" ;
- doap:created "2012-01-29"
+ doap:created "2012-01-29" ;
+ dcs:blame <http://drobilla.net/drobilla#me>
] ;
doap:developer [
a foaf:Person ;
diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c
index 5584143..ae5434a 100644
--- a/plugins/eg-sampler.lv2/sampler.c
+++ b/plugins/eg-sampler.lv2/sampler.c
@@ -1,8 +1,8 @@
/*
LV2 Sampler Example Plugin
+ Copyright 2011-2012 David Robillard <d@drobilla.net>
Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
Copyright 2011 James Morris <jwm.art.net@gmail.com>
- Copyright 2011 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
@@ -24,11 +24,11 @@
incoming events) and also triggers their playback (based on incoming MIDI
note events). The sample must be monophonic.
- So that the runSampler() method stays real-time safe, the plugin creates a
- worker thread (worker_thread_main) that listens for file loading events. It
- loads everything in plugin->pending_samp and then signals the runSampler()
- that it's time to install it. runSampler() just has to swap pointers... so
- the change happens very fast and atomically.
+ So that the run() method stays real-time safe, the plugin creates a worker
+ thread (worker_thread_main) that listens for file loading events. It loads
+ everything in plugin->pending_samp and then signals the run() that it's time
+ to install it. run() just has to swap pointers... so the change happens
+ very fast and atomically.
*/
#include <assert.h>
@@ -41,7 +41,8 @@
#include <sndfile.h>
-#include "lv2/lv2plug.in/ns/ext/atom/atom-buffer.h"
+#include <semaphore.h>
+
#include "lv2/lv2plug.in/ns/ext/atom/atom-helpers.h"
#include "lv2/lv2plug.in/ns/ext/state/state.h"
#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
@@ -69,22 +70,21 @@ typedef struct {
LV2_URID_Map* map;
/* Sample */
- SampleFile* samp;
- SampleFile* pending_samp;
- pthread_mutex_t pending_samp_mutex; /**< Protects pending_samp */
- pthread_cond_t pending_samp_cond; /**< Signaling mechanism */
- int pending_sample_ready;
+ SampleFile* samp;
+ SampleFile* pending_samp;
+ sem_t signal;
+ int pending_sample_ready;
/* Ports */
- float* output_port;
- LV2_Atom_Buffer* event_port;
+ float* output_port;
+ LV2_Atom_Sequence* event_port;
/* URIs */
struct {
- LV2_URID midi_event;
- LV2_URID atom_message;
+ LV2_URID midi_Event;
+ LV2_URID atom_Object;
LV2_URID set_message;
- LV2_URID state_path;
+ LV2_URID state_Path;
LV2_URID filename_key;
} uris;
@@ -139,16 +139,16 @@ worker_thread_main(void* arg)
{
Sampler* plugin = (Sampler*)arg;
- pthread_mutex_lock(&plugin->pending_samp_mutex);
while (true) {
/* Wait for run() to signal that we need to load a sample */
- pthread_cond_wait(&plugin->pending_samp_cond,
- &plugin->pending_samp_mutex);
+ if (sem_wait(&plugin->signal)) {
+ fprintf(stderr, "Odd, sem_wait failed...\n");
+ continue;
+ }
/* Then load it */
handle_load_sample(plugin);
}
- pthread_mutex_unlock(&plugin->pending_samp_mutex);
return 0;
}
@@ -159,6 +159,7 @@ cleanup(LV2_Handle instance)
Sampler* plugin = (Sampler*)instance;
pthread_cancel(plugin->worker_thread);
pthread_join(plugin->worker_thread, 0);
+ sem_destroy(&plugin->signal);
free(plugin->samp->data);
free(plugin->pending_samp->data);
@@ -176,7 +177,7 @@ connect_port(LV2_Handle instance,
switch (port) {
case SAMPLER_CONTROL:
- plugin->event_port = (LV2_Atom_Buffer*)data;
+ plugin->event_port = (LV2_Atom_Sequence*)data;
break;
case SAMPLER_OUT:
plugin->output_port = (float*)data;
@@ -196,7 +197,7 @@ instantiate(const LV2_Descriptor* descriptor,
if (!plugin) {
return NULL;
}
-
+
plugin->samp = (SampleFile*)malloc(sizeof(SampleFile));
plugin->pending_samp = (SampleFile*)malloc(sizeof(SampleFile));
if (!plugin->samp || !plugin->pending_samp) {
@@ -207,15 +208,13 @@ instantiate(const LV2_Descriptor* descriptor,
memset(plugin->pending_samp, 0, sizeof(SampleFile));
memset(&plugin->uris, 0, sizeof(plugin->uris));
- /* Initialise mutexes and conditions for the worker thread */
- if (pthread_mutex_init(&plugin->pending_samp_mutex, 0)) {
- fprintf(stderr, "Could not initialize next_sample_mutex.\n");
- goto fail;
- }
- if (pthread_cond_init(&plugin->pending_samp_cond, 0)) {
- fprintf(stderr, "Could not initialize next_sample_waitcond.\n");
+ /* Create signal for waking up worker thread */
+ if (sem_init(&plugin->signal, 0, 0)) {
+ fprintf(stderr, "Could not initialize semaphore.\n");
goto fail;
}
+
+ /* Create worker thread */
if (pthread_create(&plugin->worker_thread, 0, worker_thread_main, plugin)) {
fprintf(stderr, "Could not initialize worker thread.\n");
goto fail;
@@ -225,13 +224,13 @@ instantiate(const LV2_Descriptor* descriptor,
for (int i = 0; features[i]; ++i) {
if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
plugin->map = (LV2_URID_Map*)features[i]->data;
- plugin->uris.midi_event = plugin->map->map(
+ plugin->uris.midi_Event = plugin->map->map(
plugin->map->handle, MIDI_EVENT_URI);
- plugin->uris.atom_message = plugin->map->map(
- plugin->map->handle, ATOM_MESSAGE_URI);
+ plugin->uris.atom_Object = plugin->map->map(
+ plugin->map->handle, ATOM_OBJECT_URI);
plugin->uris.set_message = plugin->map->map(
plugin->map->handle, SET_MESSAGE_URI);
- plugin->uris.state_path = plugin->map->map(
+ plugin->uris.state_Path = plugin->map->map(
plugin->map->handle, LV2_STATE_PATH_URI);
plugin->uris.filename_key = plugin->map->map(
plugin->map->handle, FILENAME_URI);
@@ -267,39 +266,35 @@ run(LV2_Handle instance,
float* output = plugin->output_port;
/* Read incoming events */
- for (LV2_Atom_Buffer_Iterator i = lv2_atom_buffer_begin(plugin->event_port);
- lv2_atom_buffer_is_valid(i);
- i = lv2_atom_buffer_next(i)) {
-
- LV2_Atom_Event* const ev = lv2_atom_buffer_get(i);
- if (ev->body.type == plugin->uris.midi_event) {
+ LV2_SEQUENCE_FOREACH(plugin->event_port, i) {
+ LV2_Atom_Event* const ev = lv2_sequence_iter_get(i);
+ if (ev->body.type == plugin->uris.midi_Event) {
uint8_t* const data = (uint8_t* const)(ev + 1);
if ((data[0] & 0xF0) == 0x90) {
- start_frame = ev->frames;
+ start_frame = ev->time.audio.frames;
plugin->frame = 0;
plugin->play = true;
}
- } else if (ev->body.type == plugin->uris.atom_message) {
- const LV2_Thing* msg = (LV2_Thing*)&ev->body;
- if (msg->id == plugin->uris.set_message) {
+ } else if (ev->body.type == plugin->uris.atom_Object) {
+ const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
+ if (obj->type == plugin->uris.set_message) {
const LV2_Atom* filename = NULL;
- LV2_Thing_Query q[] = {
+ LV2_Atom_Object_Query q[] = {
{ plugin->uris.filename_key, &filename },
- LV2_THING_QUERY_END
+ LV2_OBJECT_QUERY_END
};
- lv2_thing_query(msg, q);
+ lv2_object_get(obj, q);
if (filename) {
- memcpy(plugin->pending_samp->filepath,
- filename->body,
- filename->size);
- pthread_cond_signal(&plugin->pending_samp_cond);
-
+ char* str = (char*)LV2_ATOM_BODY(filename);
+ fprintf(stderr, "Request to load %s\n", str);
+ memcpy(plugin->pending_samp->filepath, str, filename->size);
+ sem_post(&plugin->signal);
} else {
fprintf(stderr, "Ignored set message with no filename\n");
}
} else {
- fprintf(stderr, "Unknown message type %d\n", msg->id);
+ fprintf(stderr, "Unknown message type %d\n", obj->id);
}
} else {
@@ -328,18 +323,13 @@ run(LV2_Handle instance,
}
/* Check if we have a sample pending */
- if (!plugin->play
- && plugin->pending_sample_ready
- && pthread_mutex_trylock(&plugin->pending_samp_mutex)) {
+ if (!plugin->play && plugin->pending_sample_ready) {
/* Install the new sample */
- SampleFile* tmp;
- tmp = plugin->samp;
+ SampleFile* tmp = plugin->samp;
plugin->samp = plugin->pending_samp;
plugin->pending_samp = tmp;
plugin->pending_sample_ready = 0;
free(plugin->pending_samp->data); // FIXME: non-realtime!
-
- pthread_mutex_unlock(&plugin->pending_samp_mutex);
}
/* Add zeros to end if sample not long enough (or not playing) */
@@ -376,7 +366,7 @@ save(LV2_Handle instance,
map_uri(plugin, FILENAME_URI),
apath,
strlen(plugin->samp->filepath) + 1,
- plugin->uris.state_path,
+ plugin->uris.state_Path,
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
free(apath);
diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl
index 54eb36b..7548396 100644
--- a/plugins/eg-sampler.lv2/sampler.ttl
+++ b/plugins/eg-sampler.lv2/sampler.ttl
@@ -25,13 +25,14 @@
a lv2:Plugin ;
doap:name "Example Sampler" ;
doap:license <http://opensource.org/licenses/isc> ;
- lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ;
- lv2:optionalFeature lv2:hardRtCapable ;
+ lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#map> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ;
ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ;
lv2:port [
a lv2:InputPort ,
- atom:EventPort ;
+ atom:MessagePort ;
+ atom:bufferType atom:Sequence ;
atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;
lv2:index 0 ;
lv2:symbol "control" ;
@@ -46,5 +47,5 @@
<http://lv2plug.in/plugins/eg-sampler#ui>
a ui:GtkUI ;
- lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ;
+ lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#map> ;
ui:binary <sampler_ui.so> .
diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c
index dcc808f..ea57cc1 100644
--- a/plugins/eg-sampler.lv2/sampler_ui.c
+++ b/plugins/eg-sampler.lv2/sampler_ui.c
@@ -34,8 +34,9 @@
#define SAMPLER_UI_URI "http://lv2plug.in/plugins/eg-sampler#ui"
typedef struct {
+ LV2_Atom_Forge forge;
+
LV2_URID_Map* map;
- LV2_Atom_Forge* forge;
LV2UI_Write_Function write;
LV2UI_Controller controller;
@@ -71,19 +72,20 @@ on_load_clicked(GtkWidget* widget,
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
const size_t filename_len = strlen(filename);
gtk_widget_destroy(dialog);
-
- uint8_t msg_buf[4096];
- LV2_Thing* msg = (LV2_Thing*)msg_buf;
- lv2_atom_forge_set_message(ui->forge, msg, uri_to_id(ui, SET_MESSAGE_URI));
- lv2_thing_append(msg,
- uri_to_id(ui, FILENAME_URI),
- uri_to_id(ui, NS_ATOM "String"),
- filename_len,
- filename);
-
- ui->write(ui->controller, 0, sizeof(LV2_Atom) + msg->size,
+
+#define OBJ_BUF_SIZE 1024
+ uint8_t obj_buf[OBJ_BUF_SIZE];
+ lv2_atom_forge_set_buffer(&ui->forge, obj_buf, OBJ_BUF_SIZE);
+
+ LV2_Atom* obj = (LV2_Atom*)lv2_atom_forge_object(
+ &ui->forge, NULL, 0, uri_to_id(ui, SET_MESSAGE_URI));
+ lv2_atom_forge_property_head(&ui->forge, obj,
+ uri_to_id(ui, FILENAME_URI), 0);
+ lv2_atom_forge_string(&ui->forge, obj, (uint8_t*)filename, filename_len);
+
+ ui->write(ui->controller, 0, sizeof(LV2_Atom) + obj->size,
uri_to_id(ui, NS_ATOM "atomTransfer"),
- msg);
+ obj);
g_free(filename);
}
@@ -98,7 +100,7 @@ instantiate(const LV2UI_Descriptor* descriptor,
const LV2_Feature* const* features)
{
SamplerUI* ui = (SamplerUI*)malloc(sizeof(SamplerUI));
- ui->map = NULL;
+ ui->map = NULL;
ui->write = write_function;
ui->controller = controller;
ui->button = NULL;
@@ -117,7 +119,7 @@ instantiate(const LV2UI_Descriptor* descriptor,
return NULL;
}
- ui->forge = lv2_atom_forge_new(ui->map);
+ lv2_atom_forge_init(&ui->forge, ui->map);
ui->button = gtk_button_new_with_label("Load Sample");
g_signal_connect(ui->button, "clicked",
diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h
index d4df237..5e968ed 100644
--- a/plugins/eg-sampler.lv2/uris.h
+++ b/plugins/eg-sampler.lv2/uris.h
@@ -18,8 +18,8 @@
#define NS_ATOM "http://lv2plug.in/ns/ext/atom#"
#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-#define SAMPLER_URI "http://lv2plug.in/plugins/eg-sampler"
-#define MIDI_EVENT_URI "http://lv2plug.in/ns/ext/midi#MidiEvent"
-#define FILENAME_URI SAMPLER_URI "#filename"
-#define ATOM_MESSAGE_URI "http://lv2plug.in/ns/ext/atom#Message"
-#define SET_MESSAGE_URI "http: //example.org/set"
+#define SAMPLER_URI "http://lv2plug.in/plugins/eg-sampler"
+#define MIDI_EVENT_URI "http://lv2plug.in/ns/ext/midi#MidiEvent"
+#define FILENAME_URI SAMPLER_URI "#filename"
+#define ATOM_OBJECT_URI NS_ATOM "Object"
+#define SET_MESSAGE_URI "http: //example.org/set"
diff --git a/wscript b/wscript
index 7dd8786..e173b16 100644
--- a/wscript
+++ b/wscript
@@ -26,6 +26,8 @@ def options(opt):
opt.load('compiler_cc')
opt.load('compiler_cxx')
autowaf.set_options(opt)
+ opt.add_option('--test', action='store_true', default=False, dest='build_tests',
+ help="Build unit tests")
opt.add_option('--experimental', action='store_true', default=False,
dest='experimental',
help='Install unreleased experimental extensions')