diff options
Diffstat (limited to 'lv2/atom/forge.h')
| -rw-r--r-- | lv2/atom/forge.h | 696 | 
1 files changed, 696 insertions, 0 deletions
diff --git a/lv2/atom/forge.h b/lv2/atom/forge.h new file mode 100644 index 0000000..5bcd816 --- /dev/null +++ b/lv2/atom/forge.h @@ -0,0 +1,696 @@ +/* +  Copyright 2008-2016 David Robillard <http://drobilla.net> + +  Permission to use, copy, modify, and/or distribute this software for any +  purpose with or without fee is hereby granted, provided that the above +  copyright notice and this permission notice appear in all copies. + +  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** +   @file forge.h An API for constructing LV2 atoms. + +   This file provides an API for constructing Atoms which makes it relatively +   simple to build nested atoms of arbitrary complexity without requiring +   dynamic memory allocation. + +   The API is based on successively appending the appropriate pieces to build a +   complete Atom.  The size of containers is automatically updated.  Functions +   that begin a container return (via their frame argument) a stack frame which +   must be popped when the container is finished. + +   All output is written to a user-provided buffer or sink function.  This +   makes it popssible to create create atoms on the stack, on the heap, in LV2 +   port buffers, in a ringbuffer, or elsewhere, all using the same API. + +   This entire API is realtime safe if used with a buffer or a realtime safe +   sink, except lv2_atom_forge_init() which is only realtime safe if the URI +   map function is. + +   Note these functions are all static inline, do not take their address. + +   This header is non-normative, it is provided for convenience. +*/ + +/** +   @defgroup forge Forge +   @ingroup atom +   @{ +*/ + +#ifndef LV2_ATOM_FORGE_H +#define LV2_ATOM_FORGE_H + +#include "lv2/atom/atom.h" +#include "lv2/atom/util.h" +#include "lv2/core/attributes.h" +#include "lv2/urid/urid.h" + +#include <assert.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Disable deprecation warnings for Blank and Resource +LV2_DISABLE_DEPRECATION_WARNINGS + +/** Handle for LV2_Atom_Forge_Sink. */ +typedef void* LV2_Atom_Forge_Sink_Handle; + +/** A reference to a chunk of written output. */ +typedef intptr_t LV2_Atom_Forge_Ref; + +/** Sink function for writing output.  See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom_Forge_Ref +(*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle, +                       const void*                buf, +                       uint32_t                   size); + +/** Function for resolving a reference.  See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom* +(*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle, +                             LV2_Atom_Forge_Ref         ref); + +/** A stack frame used for keeping track of nested Atom containers. */ +typedef struct _LV2_Atom_Forge_Frame { +	struct _LV2_Atom_Forge_Frame* parent; +	LV2_Atom_Forge_Ref            ref; +} LV2_Atom_Forge_Frame; + +/** A "forge" for creating atoms by appending to a buffer. */ +typedef struct { +	uint8_t* buf; +	uint32_t offset; +	uint32_t size; + +	LV2_Atom_Forge_Sink        sink; +	LV2_Atom_Forge_Deref_Func  deref; +	LV2_Atom_Forge_Sink_Handle handle; + +	LV2_Atom_Forge_Frame* stack; + +	LV2_URID Blank LV2_DEPRECATED; +	LV2_URID Bool; +	LV2_URID Chunk; +	LV2_URID Double; +	LV2_URID Float; +	LV2_URID Int; +	LV2_URID Long; +	LV2_URID Literal; +	LV2_URID Object; +	LV2_URID Path; +	LV2_URID Property; +	LV2_URID Resource LV2_DEPRECATED; +	LV2_URID Sequence; +	LV2_URID String; +	LV2_URID Tuple; +	LV2_URID URI; +	LV2_URID URID; +	LV2_URID Vector; +} LV2_Atom_Forge; + +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); + +/** +   Initialise `forge`. + +   URIs will be mapped using `map` and stored, a reference to `map` itself is +   not held. +*/ +static inline void +lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) +{ +	lv2_atom_forge_set_buffer(forge, NULL, 0); +	forge->Blank    = map->map(map->handle, LV2_ATOM__Blank); +	forge->Bool     = map->map(map->handle, LV2_ATOM__Bool); +	forge->Chunk    = map->map(map->handle, LV2_ATOM__Chunk); +	forge->Double   = map->map(map->handle, LV2_ATOM__Double); +	forge->Float    = map->map(map->handle, LV2_ATOM__Float); +	forge->Int      = map->map(map->handle, LV2_ATOM__Int); +	forge->Long     = map->map(map->handle, LV2_ATOM__Long); +	forge->Literal  = map->map(map->handle, LV2_ATOM__Literal); +	forge->Object   = map->map(map->handle, LV2_ATOM__Object); +	forge->Path     = map->map(map->handle, LV2_ATOM__Path); +	forge->Property = map->map(map->handle, LV2_ATOM__Property); +	forge->Resource = map->map(map->handle, LV2_ATOM__Resource); +	forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence); +	forge->String   = map->map(map->handle, LV2_ATOM__String); +	forge->Tuple    = map->map(map->handle, LV2_ATOM__Tuple); +	forge->URI      = map->map(map->handle, LV2_ATOM__URI); +	forge->URID     = map->map(map->handle, LV2_ATOM__URID); +	forge->Vector   = map->map(map->handle, LV2_ATOM__Vector); +} + +/** Access the Atom pointed to by a reference. */ +static inline LV2_Atom* +lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) +{ +	return forge->buf ? (LV2_Atom*)ref : forge->deref(forge->handle, ref); +} + +/** +   @name Object Stack +   @{ +*/ + +/** +   Push a stack frame. +   This is done automatically by container functions (which take a stack frame +   pointer), but may be called by the user to push the top level container when +   writing to an existing Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_push(LV2_Atom_Forge*       forge, +                    LV2_Atom_Forge_Frame* frame, +                    LV2_Atom_Forge_Ref    ref) +{ +	frame->parent = forge->stack; +	frame->ref    = ref; + +	if (ref) { +		forge->stack = frame; // Don't push, so walking the stack is always safe +	} + +	return ref; +} + +/** Pop a stack frame.  This must be called when a container is finished. */ +static inline void +lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ +	if (frame->ref) { +		// If frame has a valid ref, it must be the top of the stack +		assert(frame == forge->stack); +		forge->stack = frame->parent; +	} +	// Otherwise, frame was not pushed because of overflow, do nothing +} + +/** Return true iff the top of the stack has the given type. */ +static inline bool +lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) +{ +	return forge->stack && forge->stack->ref && +		(lv2_atom_forge_deref(forge, forge->stack->ref)->type == type); +} + +/** Return true iff `type` is an atom:Object. */ +static inline bool +lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type) +{ +	return (type == forge->Object || +	        type == forge->Blank || +	        type == forge->Resource); +} + +/** Return true iff `type` is an atom:Object with a blank ID. */ +static inline bool +lv2_atom_forge_is_blank(const LV2_Atom_Forge*       forge, +                        uint32_t                    type, +                        const LV2_Atom_Object_Body* body) +{ +	return (type == forge->Blank || +	        (type == forge->Object && body->id == 0)); +} + +/** +   @} +   @name Output Configuration +   @{ +*/ + +/** Set the output buffer where `forge` will write atoms. */ +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) +{ +	forge->buf    = buf; +	forge->size   = (uint32_t)size; +	forge->offset = 0; +	forge->deref  = NULL; +	forge->sink   = NULL; +	forge->handle = NULL; +	forge->stack  = NULL; +} + +/** +   Set the sink function where `forge` will write output. + +   The return value of forge functions is an LV2_Atom_Forge_Ref which is an +   integer type safe to use as a pointer but is otherwise opaque.  The sink +   function must return a ref that can be dereferenced to access as least +   sizeof(LV2_Atom) bytes of the written data, so sizes can be updated.  For +   ringbuffers, this should be possible as long as the size of the buffer is a +   multiple of sizeof(LV2_Atom), since atoms are always aligned. + +   Note that 0 is an invalid reference, so if you are using a buffer offset be +   sure to offset it such that 0 is never a valid reference.  You will get +   confusing errors otherwise. +*/ +static inline void +lv2_atom_forge_set_sink(LV2_Atom_Forge*            forge, +                        LV2_Atom_Forge_Sink        sink, +                        LV2_Atom_Forge_Deref_Func  deref, +                        LV2_Atom_Forge_Sink_Handle handle) +{ +	forge->buf    = NULL; +	forge->size   = forge->offset = 0; +	forge->deref  = deref; +	forge->sink   = sink; +	forge->handle = handle; +	forge->stack  = NULL; +} + +/** +   @} +   @name Low Level Output +   @{ +*/ + +/** +   Write raw output.  This is used internally, but is also useful for writing +   atom types not explicitly supported by the forge API.  Note the caller is +   responsible for ensuring the output is approriately padded. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ +	LV2_Atom_Forge_Ref out = 0; +	if (forge->sink) { +		out = forge->sink(forge->handle, data, size); +	} else { +		out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset; +		uint8_t* mem = forge->buf + forge->offset; +		if (forge->offset + size > forge->size) { +			return 0; +		} +		forge->offset += size; +		memcpy(mem, data, size); +	} +	for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { +		lv2_atom_forge_deref(forge, f->ref)->size += size; +	} +	return out; +} + +/** Pad output accordingly so next write is 64-bit aligned. */ +static inline void +lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) +{ +	const uint64_t pad      = 0; +	const uint32_t pad_size = lv2_atom_pad_size(written) - written; +	lv2_atom_forge_raw(forge, &pad, pad_size); +} + +/** Write raw output, padding to 64-bits as necessary. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ +	LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); +	if (out) { +		lv2_atom_forge_pad(forge, size); +	} +	return out; +} + +/** Write a null-terminated string body. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string_body(LV2_Atom_Forge* forge, +                           const char*     str, +                           uint32_t        len) +{ +	LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); +	if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { +		lv2_atom_forge_pad(forge, len + 1); +	} +	return out; +} + +/** +   @} +   @name Atom Output +   @{ +*/ + +/** Write an atom:Atom header. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type) +{ +	const LV2_Atom a = { size, type }; +	return lv2_atom_forge_raw(forge, &a, sizeof(a)); +} + +/** Write a primitive (fixed-size) atom. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a) +{ +	return (lv2_atom_forge_top_is(forge, forge->Vector) +	        ? lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size) +	        : lv2_atom_forge_write( +		        forge, a, (uint32_t)sizeof(LV2_Atom) + a->size)); +} + +/** Write an atom:Int. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val) +{ +	const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Long. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val) +{ +	const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Float. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_float(LV2_Atom_Forge* forge, float val) +{ +	const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Double. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_double(LV2_Atom_Forge* forge, double val) +{ +	const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:Bool. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val) +{ +	const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom:URID. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id) +{ +	const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id }; +	return lv2_atom_forge_primitive(forge, &a.atom); +} + +/** Write an atom compatible with atom:String.  Used internally. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, +                            uint32_t        type, +                            const char*     str, +                            uint32_t        len) +{ +	const LV2_Atom_String a   = { { len + 1, type } }; +	LV2_Atom_Forge_Ref    out = lv2_atom_forge_raw(forge, &a, sizeof(a)); +	if (out) { +		if (!lv2_atom_forge_string_body(forge, str, len)) { +			LV2_Atom* atom = lv2_atom_forge_deref(forge, out); +			atom->size = atom->type = 0; +			out = 0; +		} +	} +	return out; +} + +/** Write an atom:String.  Note that `str` need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) +{ +	return lv2_atom_forge_typed_string(forge, forge->String, str, len); +} + +/** +   Write an atom:URI.  Note that `uri` need not be NULL terminated. +   This does not map the URI, but writes the complete URI string.  To write +   a mapped URI, use lv2_atom_forge_urid(). +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) +{ +	return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); +} + +/** Write an atom:Path.  Note that `path` need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) +{ +	return lv2_atom_forge_typed_string(forge, forge->Path, path, len); +} + +/** Write an atom:Literal. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_literal(LV2_Atom_Forge* forge, +                       const char*     str, +                       uint32_t        len, +                       uint32_t        datatype, +                       uint32_t        lang) +{ +	const LV2_Atom_Literal a = { +		{ (uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1), +		  forge->Literal }, +		{ datatype, +		  lang } +	}; +	LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); +	if (out) { +		if (!lv2_atom_forge_string_body(forge, str, len)) { +			LV2_Atom* atom = lv2_atom_forge_deref(forge, out); +			atom->size = atom->type = 0; +			out = 0; +		} +	} +	return out; +} + +/** Start an atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector_head(LV2_Atom_Forge*       forge, +                           LV2_Atom_Forge_Frame* frame, +                           uint32_t              child_size, +                           uint32_t              child_type) +{ +	const LV2_Atom_Vector a = { +		{ sizeof(LV2_Atom_Vector_Body), forge->Vector }, +		{ child_size, child_type } +	}; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** Write a complete atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector(LV2_Atom_Forge* forge, +                      uint32_t        child_size, +                      uint32_t        child_type, +                      uint32_t        n_elems, +                      const void*     elems) +{ +	const LV2_Atom_Vector a = { +		{ (uint32_t)(sizeof(LV2_Atom_Vector_Body) + n_elems * child_size), +		  forge->Vector }, +		{ child_size, child_type } +	}; +	LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); +	if (out) { +		lv2_atom_forge_write(forge, elems, child_size * n_elems); +	} +	return out; +} + +/** +   Write the header of an atom:Tuple. + +   The passed frame will be initialised to represent this tuple.  To complete +   the tuple, write a sequence of atoms, then pop the frame with +   lv2_atom_forge_pop(). + +   For example: +   @code +   // Write tuple (1, 2.0) +   LV2_Atom_Forge_Frame frame; +   LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame); +   lv2_atom_forge_int32(forge, 1); +   lv2_atom_forge_float(forge, 2.0); +   lv2_atom_forge_pop(forge, &frame); +   @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ +	const LV2_Atom_Tuple a = { { 0, forge->Tuple } }; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** +   Write the header of an atom:Object. + +   The passed frame will be initialised to represent this object.  To complete +   the object, write a sequence of properties, then pop the frame with +   lv2_atom_forge_pop(). + +   For example: +   @code +   LV2_URID eg_Cat  = map("http://example.org/Cat"); +   LV2_URID eg_name = map("http://example.org/name"); + +   // Start object with type eg_Cat and blank ID +   LV2_Atom_Forge_Frame frame; +   lv2_atom_forge_object(forge, &frame, 0, eg_Cat); + +   // Append property eg:name = "Hobbes" +   lv2_atom_forge_key(forge, eg_name); +   lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes")); + +   // Finish object +   lv2_atom_forge_pop(forge, &frame); +   @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_object(LV2_Atom_Forge*       forge, +                      LV2_Atom_Forge_Frame* frame, +                      LV2_URID              id, +                      LV2_URID              otype) +{ +	const LV2_Atom_Object a = { +		{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object }, +		{ id, otype } +	}; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** +   The same as lv2_atom_forge_object(), but for object:Resource. + +   This function is deprecated and should not be used in new code. +   Use lv2_atom_forge_object() directly instead. +*/ +LV2_DEPRECATED +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_resource(LV2_Atom_Forge*       forge, +                        LV2_Atom_Forge_Frame* frame, +                        LV2_URID              id, +                        LV2_URID              otype) +{ +	const LV2_Atom_Object a = { +		{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource }, +		{ id, otype } +	}; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** +   The same as lv2_atom_forge_object(), but for object:Blank. + +   This function is deprecated and should not be used in new code. +   Use lv2_atom_forge_object() directly instead. +*/ +LV2_DEPRECATED +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_blank(LV2_Atom_Forge*       forge, +                     LV2_Atom_Forge_Frame* frame, +                     uint32_t              id, +                     LV2_URID              otype) +{ +	const LV2_Atom_Object a = { +		{ (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank }, +		{ id, otype } +	}; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** +   Write a property key in an Object, to be followed by the value. + +   See lv2_atom_forge_object() documentation for an example. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_key(LV2_Atom_Forge* forge, +                   LV2_URID        key) +{ +	const LV2_Atom_Property_Body a = { key, 0, { 0, 0 } }; +	return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); +} + +/** +   Write the header for a property body in an object, with context. + +   If you do not need the context, which is almost certainly the case, +   use the simpler lv2_atom_forge_key() instead. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_property_head(LV2_Atom_Forge* forge, +                             LV2_URID        key, +                             LV2_URID        context) +{ +	const LV2_Atom_Property_Body a = { key, context, { 0, 0 } }; +	return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); +} + +/** +   Write the header for a Sequence. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_sequence_head(LV2_Atom_Forge*       forge, +                             LV2_Atom_Forge_Frame* frame, +                             uint32_t              unit) +{ +	const LV2_Atom_Sequence a = { +		{ (uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence }, +		{ unit, 0 } +	}; +	return lv2_atom_forge_push( +		forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** +   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.  Note +   the returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) +{ +	return lv2_atom_forge_write(forge, &frames, sizeof(frames)); +} + +/** +   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.  Note the +   returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) +{ +	return lv2_atom_forge_write(forge, &beats, sizeof(beats)); +} + +/** +   @} +   @} +*/ + +LV2_RESTORE_WARNINGS + +#ifdef __cplusplus +}  /* extern "C" */ +#endif + +#endif  /* LV2_ATOM_FORGE_H */  |