diff options
author | David Robillard <d@drobilla.net> | 2012-02-08 01:38:38 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2012-02-08 01:38:38 +0000 |
commit | 046c0a8bbffb572df1c6376d3bf525aac9b3a9fa (patch) | |
tree | 1299f3ed06b43031060673fefc32bc2f29587638 /lv2/ns/ext/atom/forge.h | |
parent | 0ad020578277f8aade6b2df90385b911775ead18 (diff) | |
download | lv2-046c0a8bbffb572df1c6376d3bf525aac9b3a9fa.tar.xz |
Heavily revise atom extension into a release candidate.
Diffstat (limited to 'lv2/ns/ext/atom/forge.h')
-rw-r--r-- | lv2/ns/ext/atom/forge.h | 424 |
1 files changed, 397 insertions, 27 deletions
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 |